1 /*******************************************************************
2
3 Part of the Fritzing project - http://fritzing.org
4 Copyright (c) 2007-2014 Fachhochschule Potsdam - http://fh-potsdam.de
5
6 Fritzing is free software: you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
10
11 Fritzing is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with Fritzing. If not, see <http://www.gnu.org/licenses/>.
18
19 ********************************************************************
20
21 $Revision: 6980 $:
22 $Author: irascibl@gmail.com $:
23 $Date: 2013-04-22 01:45:43 +0200 (Mo, 22. Apr 2013) $
24
25 ********************************************************************/
26
27 #include "note.h"
28 #include "../debugdialog.h"
29 #include "../sketch/infographicsview.h"
30 #include "../model/modelpart.h"
31 #include "../utils/resizehandle.h"
32 #include "../utils/textutils.h"
33 #include "../utils/graphicsutils.h"
34 #include "../fsvgrenderer.h"
35
36 #include <QTextFrame>
37 #include <QTextLayout>
38 #include <QTextFrameFormat>
39 #include <QApplication>
40 #include <QTextDocumentFragment>
41 #include <QTimer>
42 #include <QFormLayout>
43 #include <QGroupBox>
44 #include <QLineEdit>
45 #include <QDialogButtonBox>
46 #include <QPushButton>
47
48 // TODO:
49 // ** search for ModelPart:: and fix up
50 // check which menu items don't apply
51 // ** selection
52 // ** delete
53 // ** move
54 // ** undo delete + text
55 // ** resize
56 // ** undo resize
57 // anchor
58 // ** undo change text
59 // ** undo selection
60 // ** undo move
61 // ** layers and z order
62 // ** hide and show layer
63 // ** save and load
64 // format: bold, italic, size (small normal large huge), color?,
65 // undo format
66 // heads-up controls
67 // copy/paste
68 // ** z-order manipulation
69 // hover
70 // ** multiple selection
71 // ** icon in taskbar (why does it show up as text until you update it?)
72
73 const int Note::emptyMinWidth = 40;
74 const int Note::emptyMinHeight = 25;
75 const int Note::initialMinWidth = 140;
76 const int Note::initialMinHeight = 45;
77 const int borderWidth = 7;
78 const int TriangleOffset = 7;
79
80 const double InactiveOpacity = 0.5;
81
82 QString Note::initialTextString;
83
84 QRegExp UrlTag("<a.*href=[\"']([^\"]+[.\\s]*)[\"'].*>");
85
86 ///////////////////////////////////////
87
findA(QDomElement element,QList<QDomElement> & aElements)88 void findA(QDomElement element, QList<QDomElement> & aElements)
89 {
90 if (element.tagName().compare("a", Qt::CaseInsensitive) == 0) {
91 aElements.append(element);
92 return;
93 }
94
95 QDomElement c = element.firstChildElement();
96 while (!c.isNull()) {
97 findA(c, aElements);
98
99 c = c.nextSiblingElement();
100 }
101 }
102
103
addText(const QString & text,bool inUrl)104 QString addText(const QString & text, bool inUrl)
105 {
106 if (text.isEmpty()) return "";
107
108 return QString("<tspan fill='%1' >%2</tspan>\n")
109 .arg(inUrl ? "#0000ff" : "#000000")
110 .arg(TextUtils::convertExtendedChars(TextUtils::escapeAnd(text)))
111 ;
112 }
113
114 ///////////////////////////////////////
115
116 class NoteGraphicsTextItem : public QGraphicsTextItem
117 {
118 public:
119 NoteGraphicsTextItem(QGraphicsItem * parent = NULL);
120
121 protected:
122 void focusInEvent(QFocusEvent *);
123 void focusOutEvent(QFocusEvent *);
124 };
125
NoteGraphicsTextItem(QGraphicsItem * parent)126 NoteGraphicsTextItem::NoteGraphicsTextItem(QGraphicsItem * parent) : QGraphicsTextItem(parent)
127 {
128 const QTextFrameFormat format = document()->rootFrame()->frameFormat();
129 QTextFrameFormat altFormat(format);
130 altFormat.setMargin(0); // so document never thinks a mouse click is a move event
131 document()->rootFrame()->setFrameFormat(altFormat);
132 }
133
focusInEvent(QFocusEvent * event)134 void NoteGraphicsTextItem::focusInEvent(QFocusEvent * event) {
135 InfoGraphicsView * igv = InfoGraphicsView::getInfoGraphicsView(this);
136 if (igv != NULL) {
137 igv->setNoteFocus(this, true);
138 }
139 QApplication::instance()->installEventFilter((Note *) this->parentItem());
140 QGraphicsTextItem::focusInEvent(event);
141 DebugDialog::debug("note focus in");
142 }
143
focusOutEvent(QFocusEvent * event)144 void NoteGraphicsTextItem::focusOutEvent(QFocusEvent * event) {
145 InfoGraphicsView * igv = InfoGraphicsView::getInfoGraphicsView(this);
146 if (igv != NULL) {
147 igv->setNoteFocus(this, false);
148 }
149 QApplication::instance()->removeEventFilter((Note *) this->parentItem());
150 QGraphicsTextItem::focusOutEvent(event);
151 DebugDialog::debug("note focus out");
152 }
153
154 //////////////////////////////////////////
155
LinkDialog(QWidget * parent)156 LinkDialog::LinkDialog(QWidget *parent) : QDialog(parent)
157 {
158 this->setWindowTitle(QObject::tr("Edit link"));
159
160 QVBoxLayout * vLayout = new QVBoxLayout(this);
161
162 QGroupBox * formGroupBox = new QGroupBox(this);
163
164 QFormLayout * formLayout = new QFormLayout();
165
166 m_urlEdit = new QLineEdit(this);
167 m_urlEdit->setFixedHeight(25);
168 m_urlEdit->setFixedWidth(200);
169 formLayout->addRow(tr("url:"), m_urlEdit );
170
171 m_textEdit = new QLineEdit(this);
172 m_textEdit->setFixedHeight(25);
173 m_textEdit->setFixedWidth(200);
174 formLayout->addRow(tr("text:"), m_textEdit );
175
176 formGroupBox->setLayout(formLayout);
177
178 vLayout->addWidget(formGroupBox);
179
180 QDialogButtonBox * buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
181 buttonBox->button(QDialogButtonBox::Cancel)->setText(tr("Cancel"));
182 buttonBox->button(QDialogButtonBox::Ok)->setText(tr("OK"));
183
184 connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept()));
185 connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject()));
186
187 vLayout->addWidget(buttonBox);
188
189 this->setLayout(vLayout);
190 }
191
~LinkDialog()192 LinkDialog::~LinkDialog() {
193 }
194
setText(const QString & text)195 void LinkDialog::setText(const QString & text) {
196 m_textEdit->setText(text);
197 }
198
setUrl(const QString & url)199 void LinkDialog::setUrl(const QString & url) {
200 m_urlEdit->setText(url);
201 }
202
text()203 QString LinkDialog::text() {
204 return m_textEdit->text();
205 }
206
url()207 QString LinkDialog::url() {
208 return m_urlEdit->text();
209 }
210
211 /////////////////////////////////////////////
212
Note(ModelPart * modelPart,ViewLayer::ViewID viewID,const ViewGeometry & viewGeometry,long id,QMenu * itemMenu)213 Note::Note( ModelPart * modelPart, ViewLayer::ViewID viewID, const ViewGeometry & viewGeometry, long id, QMenu* itemMenu)
214 : ItemBase(modelPart, viewID, viewGeometry, id, itemMenu)
215 {
216 m_charsAdded = 0;
217 if (initialTextString.isEmpty()) {
218 initialTextString = tr("[write your note here]");
219 }
220
221 m_inResize = NULL;
222 this->setCursor(Qt::ArrowCursor);
223
224 setFlag(QGraphicsItem::ItemIsSelectable, true);
225
226 if (viewGeometry.rect().width() == 0 || viewGeometry.rect().height() == 0) {
227 m_rect.setRect(0, 0, Note::initialMinWidth, Note::initialMinHeight);
228 }
229 else {
230 m_rect.setRect(0, 0, viewGeometry.rect().width(), viewGeometry.rect().height());
231 }
232 m_pen.setWidth(borderWidth);
233 m_pen.setCosmetic(false);
234 m_pen.setBrush(QColor(0xfa, 0xbc, 0x4f));
235
236 m_brush.setColor(QColor(0xff, 0xe9, 0xc8));
237 m_brush.setStyle(Qt::SolidPattern);
238
239 setPos(m_viewGeometry.loc());
240
241 QPixmap pixmap(":/resources/images/icons/noteResizeGrip.png");
242 m_resizeGrip = new ResizeHandle(pixmap, Qt::SizeFDiagCursor, false, this);
243 connect(m_resizeGrip, SIGNAL(mousePressSignal(QGraphicsSceneMouseEvent *, ResizeHandle *)), this, SLOT(handleMousePressSlot(QGraphicsSceneMouseEvent *, ResizeHandle *)));
244 connect(m_resizeGrip, SIGNAL(mouseMoveSignal(QGraphicsSceneMouseEvent *, ResizeHandle *)), this, SLOT(handleMouseMoveSlot(QGraphicsSceneMouseEvent *, ResizeHandle *)));
245 connect(m_resizeGrip, SIGNAL(mouseReleaseSignal(QGraphicsSceneMouseEvent *, ResizeHandle *)), this, SLOT(handleMouseReleaseSlot(QGraphicsSceneMouseEvent *, ResizeHandle *)));
246 //connect(m_resizeGrip, SIGNAL(zoomChangedSignal(double)), this, SLOT(handleZoomChangedSlot(double)));
247
248 m_graphicsTextItem = new NoteGraphicsTextItem();
249 QFont font("Droid Sans", 9, QFont::Normal);
250 m_graphicsTextItem->setFont(font);
251 m_graphicsTextItem->document()->setDefaultFont(font);
252 m_graphicsTextItem->setParentItem(this);
253 m_graphicsTextItem->setVisible(true);
254 m_graphicsTextItem->setPlainText(initialTextString);
255 m_graphicsTextItem->setTextInteractionFlags(Qt::TextEditorInteraction | Qt::LinksAccessibleByMouse | Qt::LinksAccessibleByKeyboard);
256 m_graphicsTextItem->setCursor(Qt::IBeamCursor);
257 m_graphicsTextItem->setOpenExternalLinks(true);
258
259
260 connectSlots();
261
262 positionGrip();
263
264 setAcceptHoverEvents(true);
265 }
266
saveGeometry()267 void Note::saveGeometry() {
268 m_viewGeometry.setRect(boundingRect());
269 m_viewGeometry.setLoc(this->pos());
270 m_viewGeometry.setSelected(this->isSelected());
271 m_viewGeometry.setZ(this->zValue());
272 }
273
itemMoved()274 bool Note::itemMoved() {
275 return (this->pos() != m_viewGeometry.loc());
276 }
277
saveInstanceLocation(QXmlStreamWriter & streamWriter)278 void Note::saveInstanceLocation(QXmlStreamWriter & streamWriter) {
279 QRectF rect = m_viewGeometry.rect();
280 QPointF loc = m_viewGeometry.loc();
281 streamWriter.writeAttribute("x", QString::number(loc.x()));
282 streamWriter.writeAttribute("y", QString::number(loc.y()));
283 streamWriter.writeAttribute("width", QString::number(rect.width()));
284 streamWriter.writeAttribute("height", QString::number(rect.height()));
285 }
286
moveItem(ViewGeometry & viewGeometry)287 void Note::moveItem(ViewGeometry & viewGeometry) {
288 this->setPos(viewGeometry.loc());
289 }
290
findConnectorsUnder()291 void Note::findConnectorsUnder() {
292 }
293
paint(QPainter * painter,const QStyleOptionGraphicsItem * option,QWidget * widget)294 void Note::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
295 {
296 Q_UNUSED(widget);
297
298 if (m_hidden) return;
299
300 if (m_inactive) {
301 painter->save();
302 painter->setOpacity(InactiveOpacity);
303 }
304
305 painter->setPen(Qt::NoPen);
306 painter->setBrush(m_brush);
307 QPolygonF poly;
308 poly.append(m_rect.topLeft());
309 poly.append(m_rect.topRight() + QPointF(-TriangleOffset, 0));
310 poly.append(m_rect.topRight() + QPointF(0, TriangleOffset));
311 poly.append(m_rect.bottomRight());
312 poly.append(m_rect.bottomLeft());
313 painter->drawPolygon(poly);
314
315 painter->setBrush(m_pen.brush());
316 poly.clear();
317 poly.append(m_rect.topRight() + QPointF(-TriangleOffset, 0));
318 poly.append(m_rect.topRight() + QPointF(0, TriangleOffset));
319 poly.append(m_rect.topRight() + QPointF(-TriangleOffset, TriangleOffset));
320 painter->drawPolygon(poly);
321
322 painter->setPen(m_pen);
323 poly.clear();
324 poly.append(QPointF(0, m_rect.bottom() - borderWidth / 2.0));
325 poly.append(QPointF(m_rect.right(), m_rect.bottom() - borderWidth / 2.0));
326 painter->drawPolygon(poly);
327
328 if (option->state & QStyle::State_Selected) {
329 GraphicsUtils::qt_graphicsItem_highlightSelected(painter, option, boundingRect(), QPainterPath());
330 }
331
332 if (m_inactive) {
333 painter->restore();
334 }
335 }
336
boundingRect() const337 QRectF Note::boundingRect() const
338 {
339 return m_rect;
340 }
341
shape() const342 QPainterPath Note::shape() const
343 {
344 QPainterPath path;
345 path.addRect(boundingRect());
346 return path;
347 }
348
positionGrip()349 void Note::positionGrip() {
350 QSizeF gripSize = m_resizeGrip->boundingRect().size();
351 QSizeF sz = this->boundingRect().size();
352 QPointF p(sz.width() - gripSize.width(), sz.height() - gripSize.height());
353 m_resizeGrip->setPos(p);
354 m_graphicsTextItem->setPos(TriangleOffset / 2, TriangleOffset / 2);
355 m_graphicsTextItem->setTextWidth(sz.width() - TriangleOffset);
356 }
357
mousePressEvent(QGraphicsSceneMouseEvent * event)358 void Note::mousePressEvent(QGraphicsSceneMouseEvent * event) {
359 InfoGraphicsView *infographics = InfoGraphicsView::getInfoGraphicsView(this);
360 if (infographics != NULL && infographics->spaceBarIsPressed()) {
361 m_spaceBarWasPressed = true;
362 event->ignore();
363 return;
364 }
365
366 m_spaceBarWasPressed = false;
367 m_inResize = NULL;
368 ItemBase::mousePressEvent(event);
369 }
370
mouseMoveEvent(QGraphicsSceneMouseEvent * event)371 void Note::mouseMoveEvent(QGraphicsSceneMouseEvent * event) {
372 if (m_spaceBarWasPressed) {
373 event->ignore();
374 return;
375 }
376
377 ItemBase::mouseMoveEvent(event);
378 }
379
mouseReleaseEvent(QGraphicsSceneMouseEvent * event)380 void Note::mouseReleaseEvent(QGraphicsSceneMouseEvent * event) {
381 if (m_spaceBarWasPressed) {
382 event->ignore();
383 return;
384 }
385
386 ItemBase::mouseReleaseEvent(event);
387 }
388
contentsChangeSlot(int position,int charsRemoved,int charsAdded)389 void Note::contentsChangeSlot(int position, int charsRemoved, int charsAdded) {
390 Q_UNUSED(charsRemoved);
391
392 m_charsAdded = charsAdded;
393 m_charsPosition = position;
394 }
395
forceFormat(int position,int charsAdded)396 void Note::forceFormat(int position, int charsAdded) {
397 disconnectSlots();
398 QTextCursor textCursor = m_graphicsTextItem->textCursor();
399
400 QTextCharFormat f;
401 QFont font("Droid Sans", 9, QFont::Normal);
402
403 f.setFont(font);
404 f.setFontFamily("Droid Sans");
405 f.setFontPointSize(9);
406
407 int cc = m_graphicsTextItem->document()->characterCount();
408 textCursor.setPosition(position, QTextCursor::MoveAnchor);
409 if (position + charsAdded >= cc) {
410 charsAdded--;
411 }
412 textCursor.setPosition(position + charsAdded, QTextCursor::KeepAnchor);
413
414 //textCursor.setCharFormat(f);
415 textCursor.mergeCharFormat(f);
416 //DebugDialog::debug(QString("setting font tc:%1,%2 params:%3,%4")
417 //.arg(textCursor.anchor()).arg(textCursor.position())
418 //.arg(position).arg(position + charsAdded));
419
420 /*
421 textCursor = m_graphicsTextItem->textCursor();
422 for (int i = 0; i < charsAdded; i++) {
423 textCursor.setPosition(position + i, QTextCursor::MoveAnchor);
424 f = textCursor.charFormat();
425 DebugDialog::debug(QString("1format %1 %2 %3").arg(f.fontPointSize()).arg(f.fontFamily()).arg(f.fontWeight()));
426 //f.setFont(font);
427 //f.setFontPointSize(9);
428 //f.setFontWeight(QFont::Normal);
429 //textCursor.setCharFormat(f);
430 //QTextCharFormat g = textCursor.charFormat();
431 //DebugDialog::debug(QString("2format %1 %2 %3").arg(g.fontPointSize()).arg(g.fontFamily()).arg(g.fontWeight()));
432 }
433 */
434
435 connectSlots();
436 }
437
contentsChangedSlot()438 void Note::contentsChangedSlot() {
439
440 //DebugDialog::debug(QString("contents changed ") + m_graphicsTextItem->document()->toPlainText());
441 if (m_charsAdded > 0) {
442 forceFormat(m_charsPosition, m_charsAdded);
443 }
444
445 InfoGraphicsView *infoGraphicsView = InfoGraphicsView::getInfoGraphicsView(this);
446 if (infoGraphicsView != NULL) {
447 QString oldText;
448 if (m_modelPart) {
449 oldText = m_modelPart->instanceText();
450 }
451
452 QSizeF oldSize = m_rect.size();
453 QSizeF newSize = oldSize;
454 checkSize(newSize);
455
456 infoGraphicsView->noteChanged(this, oldText, m_graphicsTextItem->document()->toHtml(), oldSize, newSize);
457 }
458 if (m_modelPart) {
459 m_modelPart->setInstanceText(m_graphicsTextItem->document()->toHtml());
460 }
461 }
462
checkSize(QSizeF & newSize)463 void Note::checkSize(QSizeF & newSize) {
464 QSizeF gripSize = m_resizeGrip->boundingRect().size();
465 QSizeF size = m_graphicsTextItem->document()->size();
466 if (size.height() + gripSize.height() + gripSize.height() > m_rect.height()) {
467 prepareGeometryChange();
468 m_rect.setHeight(size.height() + gripSize.height() + gripSize.height());
469 newSize.setHeight(m_rect.height());
470 positionGrip();
471 this->update();
472 }
473 }
474
disconnectSlots()475 void Note::disconnectSlots() {
476 disconnect(m_graphicsTextItem->document(), SIGNAL(contentsChanged()),
477 this, SLOT(contentsChangedSlot()));
478 disconnect(m_graphicsTextItem->document(), SIGNAL(contentsChange(int, int, int)),
479 this, SLOT(contentsChangeSlot(int, int, int)));
480 }
481
connectSlots()482 void Note::connectSlots() {
483 connect(m_graphicsTextItem->document(), SIGNAL(contentsChanged()),
484 this, SLOT(contentsChangedSlot()), Qt::DirectConnection);
485 connect(m_graphicsTextItem->document(), SIGNAL(contentsChange(int, int, int)),
486 this, SLOT(contentsChangeSlot(int, int, int)), Qt::DirectConnection);
487 }
488
setText(const QString & text,bool check)489 void Note::setText(const QString & text, bool check) {
490 // disconnect the signal so it doesn't fire recursively
491 disconnectSlots();
492 QString oldText = text;
493 m_graphicsTextItem->document()->setHtml(text);
494 connectSlots();
495
496 if (check) {
497 QSizeF newSize;
498 checkSize(newSize);
499 forceFormat(0, m_graphicsTextItem->document()->characterCount());
500 }
501 }
502
text()503 QString Note::text() {
504 return m_graphicsTextItem->document()->toHtml();
505 }
506
setSize(const QSizeF & size)507 void Note::setSize(const QSizeF & size)
508 {
509 prepareGeometryChange();
510 m_rect.setWidth(size.width());
511 m_rect.setHeight(size.height());
512 positionGrip();
513 }
514
setHidden(bool hide)515 void Note::setHidden(bool hide)
516 {
517 ItemBase::setHidden(hide);
518 m_graphicsTextItem->setVisible(!hide);
519 m_resizeGrip->setVisible(!hide);
520 }
521
setInactive(bool inactivate)522 void Note::setInactive(bool inactivate)
523 {
524 ItemBase::setInactive(inactivate);
525 }
526
eventFilter(QObject * object,QEvent * event)527 bool Note::eventFilter(QObject * object, QEvent * event)
528 {
529 if (event->type() == QEvent::Shortcut || event->type() == QEvent::ShortcutOverride)
530 {
531 if (!object->inherits("QGraphicsView"))
532 {
533 event->accept();
534 return true;
535 }
536 }
537
538 if (event->type() == QEvent::KeyPress) {
539 QKeyEvent * kevent = static_cast<QKeyEvent *>(event);
540 if (kevent->matches(QKeySequence::Bold)) {
541 QTextCursor textCursor = m_graphicsTextItem->textCursor();
542 QTextCharFormat cf = textCursor.charFormat();
543 bool isBold = cf.fontWeight() == QFont::Bold;
544 QTextCharFormat textCharFormat;
545 textCharFormat.setFontWeight(isBold ? QFont::Normal : QFont::Bold);
546 textCursor.mergeCharFormat(textCharFormat);
547 event->accept();
548 return true;
549 }
550 if (kevent->matches(QKeySequence::Italic)) {
551 QTextCursor textCursor = m_graphicsTextItem->textCursor();
552 QTextCharFormat cf = textCursor.charFormat();
553 QTextCharFormat textCharFormat;
554 textCharFormat.setFontItalic(!cf.fontItalic());
555 textCursor.mergeCharFormat(textCharFormat);
556 event->accept();
557 return true;
558 }
559 if ((kevent->key() == Qt::Key_L) && (kevent->modifiers() & Qt::ControlModifier)) {
560 QTimer::singleShot(75, this, SLOT(linkDialog()));
561 event->accept();
562 return true;
563
564 }
565 }
566 return false;
567 }
568
linkDialog()569 void Note::linkDialog() {
570 QTextCursor textCursor = m_graphicsTextItem->textCursor();
571 bool gotUrl = false;
572 if (textCursor.anchor() == textCursor.selectionStart()) {
573 // the selection returns empty since we're between characters
574 // so select one character forward or one character backward
575 // to see whether we're in a url
576 int wasAnchor = textCursor.anchor();
577 bool atEnd = textCursor.atEnd();
578 bool atStart = textCursor.atStart();
579 if (!atStart) {
580 textCursor.setPosition(wasAnchor - 1, QTextCursor::KeepAnchor);
581 QString html = textCursor.selection().toHtml();
582 if (UrlTag.indexIn(html) >= 0) {
583 gotUrl = true;
584 }
585 }
586 if (!gotUrl && !atEnd) {
587 textCursor.setPosition(wasAnchor + 1, QTextCursor::KeepAnchor);
588 QString html = textCursor.selection().toHtml();
589 if (UrlTag.indexIn(html) >= 0) {
590 gotUrl = true;
591 }
592 }
593 textCursor.setPosition(wasAnchor, QTextCursor::MoveAnchor);
594 }
595 else {
596 QString html = textCursor.selection().toHtml();
597 DebugDialog::debug(html);
598 if (UrlTag.indexIn(html) >= 0) {
599 gotUrl = true;
600 }
601 }
602
603 LinkDialog ld;
604 QString originalText;
605 QString originalUrl;
606 if (gotUrl) {
607 originalUrl = UrlTag.cap(1);
608 ld.setUrl(originalUrl);
609 QString html = m_graphicsTextItem->toHtml();
610
611 // assumes html is in xml form
612 QString errorStr;
613 int errorLine;
614 int errorColumn;
615
616 QDomDocument domDocument;
617 if (!domDocument.setContent(html, &errorStr, &errorLine, &errorColumn)) {
618 return;
619 }
620
621 QDomElement root = domDocument.documentElement();
622 if (root.isNull()) {
623 return;
624 }
625
626 if (root.tagName() != "html") {
627 return;
628 }
629
630 DebugDialog::debug(html);
631 QList<QDomElement> aElements;
632 findA(root, aElements);
633 foreach (QDomElement a, aElements) {
634 // TODO: if multiple hrefs point to the same url this will only find the first one
635 QString href = a.attribute("href");
636 if (href.isEmpty()) {
637 href = a.attribute("HREF");
638 }
639 if (href.compare(originalUrl) == 0) {
640 QString text;
641 if (TextUtils::findText(a, text)) {
642 ld.setText(text);
643 break;
644 }
645 else {
646 return;
647 }
648 }
649 }
650 }
651 int result = ld.exec();
652 if (result == QDialog::Accepted) {
653 if (gotUrl) {
654 int from = 0;
655 while (true) {
656 QTextCursor cursor = m_graphicsTextItem->document()->find(originalText, from);
657 if (cursor.isNull()) {
658 // TODO: tell the user
659 return;
660 }
661
662 QString html = cursor.selection().toHtml();
663 if (html.contains(originalUrl)) {
664 cursor.insertHtml(QString("<a href=\"%1\">%2</a>").arg(ld.url()).arg(ld.text()));
665 break;
666 }
667
668 from = cursor.selectionEnd();
669 }
670 }
671 else {
672 textCursor.insertHtml(QString("<a href=\"%1\">%2</a>").arg(ld.url()).arg(ld.text()));
673 }
674 }
675 }
676
handleZoomChangedSlot(double scale)677 void Note::handleZoomChangedSlot(double scale) {
678 Q_UNUSED(scale);
679 positionGrip();
680 }
681
handleMousePressSlot(QGraphicsSceneMouseEvent * event,ResizeHandle * resizeHandle)682 void Note::handleMousePressSlot(QGraphicsSceneMouseEvent * event, ResizeHandle * resizeHandle) {
683 if (m_spaceBarWasPressed) return;
684
685 saveGeometry();
686
687 QSizeF sz = this->boundingRect().size();
688 resizeHandle->setResizeOffset(this->pos() + QPointF(sz.width(), sz.height()) - event->scenePos());
689
690 m_inResize = resizeHandle;
691 }
692
handleMouseMoveSlot(QGraphicsSceneMouseEvent * event,ResizeHandle * resizeHandle)693 void Note::handleMouseMoveSlot(QGraphicsSceneMouseEvent * event, ResizeHandle * resizeHandle) {
694 Q_UNUSED(resizeHandle);
695
696 if (!m_inResize) return;
697
698 double minWidth = emptyMinWidth;
699 double minHeight = emptyMinHeight;
700 QSizeF gripSize = m_resizeGrip->boundingRect().size();
701 QSizeF minSize = m_graphicsTextItem->document()->size() + gripSize + gripSize;
702 if (minSize.height() > minHeight) minHeight = minSize.height();
703
704 QRectF rect = boundingRect();
705 rect.moveTopLeft(this->pos());
706
707 double oldX1 = rect.x();
708 double oldY1 = rect.y();
709 double newX = event->scenePos().x() + m_inResize->resizeOffset().x();
710 double newY = event->scenePos().y() + m_inResize->resizeOffset().y();
711 QRectF newR;
712
713 if (newX - oldX1 < minWidth) {
714 newX = oldX1 + minWidth;
715 }
716 if (newY - oldY1 < minHeight) {
717 newY = oldY1 + minHeight;
718 }
719 newR.setRect(0, 0, newX - oldX1, newY - oldY1);
720
721 prepareGeometryChange();
722 m_rect = newR;
723 positionGrip();
724 }
725
handleMouseReleaseSlot(QGraphicsSceneMouseEvent * event,ResizeHandle * resizeHandle)726 void Note::handleMouseReleaseSlot(QGraphicsSceneMouseEvent * event, ResizeHandle * resizeHandle) {
727 Q_UNUSED(resizeHandle);
728 Q_UNUSED(event);
729
730 if (!m_inResize) return;
731
732 m_inResize = NULL;
733 InfoGraphicsView *infoGraphicsView = InfoGraphicsView::getInfoGraphicsView(this);
734 if (infoGraphicsView != NULL) {
735 infoGraphicsView->noteSizeChanged(this, m_viewGeometry.rect().size(), m_rect.size());
736 }
737 }
738
hasPartLabel()739 bool Note::hasPartLabel() {
740
741 return false;
742 }
743
stickyEnabled()744 bool Note::stickyEnabled() {
745 return false;
746 }
747
hasPartNumberProperty()748 bool Note::hasPartNumberProperty()
749 {
750 return false;
751 }
752
rotationAllowed()753 bool Note::rotationAllowed() {
754 return false;
755 }
756
rotation45Allowed()757 bool Note::rotation45Allowed() {
758 return false;
759 }
760
retrieveSvg(ViewLayer::ViewLayerID viewLayerID,QHash<QString,QString> & svgHash,bool blackOnly,double dpi,double & factor)761 QString Note::retrieveSvg(ViewLayer::ViewLayerID viewLayerID, QHash<QString, QString> & svgHash, bool blackOnly, double dpi, double & factor)
762 {
763 Q_UNUSED(svgHash);
764 factor = 1;
765
766 switch (viewLayerID) {
767 case ViewLayer::BreadboardNote:
768 if (viewID() != ViewLayer::BreadboardView) return "";
769 break;
770 case ViewLayer::PcbNote:
771 if (viewID() != ViewLayer::PCBView) return "";
772 break;
773 case ViewLayer::SchematicNote:
774 if (viewID() != ViewLayer::SchematicView) return "";
775 break;
776 default:
777 return "";
778 }
779
780 QString svg = "<g>";
781
782 QString penColor = blackOnly ? "#000000" : m_pen.color().name();
783 double penWidth = m_pen.widthF() * dpi / GraphicsUtils::SVGDPI;
784 QString brushColor = blackOnly ? "none" : m_brush.color().name();
785 svg += QString("<rect x='%1' y='%2' width='%3' height='%4' fill='%5' stroke='%6' stroke-width='%7' />")
786 .arg(penWidth / 2)
787 .arg(penWidth / 2)
788 .arg((m_rect.width() * dpi / GraphicsUtils::SVGDPI) - penWidth)
789 .arg((m_rect.height() * dpi / GraphicsUtils::SVGDPI) - penWidth)
790 .arg(brushColor)
791 .arg(penColor)
792 .arg(penWidth)
793 ;
794
795 QTextCursor textCursor = m_graphicsTextItem->textCursor();
796 QSizeF gripSize = m_resizeGrip->boundingRect().size();
797 double docLeft = gripSize.width();
798 double docTop = gripSize.height();
799 for (QTextBlock block = m_graphicsTextItem->document()->begin(); block.isValid(); block = block.next()) {
800 QTextLayout * layout = block.layout();
801 double left = block.blockFormat().leftMargin() + docLeft + layout->position().x();
802 double top = block.blockFormat().topMargin() + docTop + layout->position().y();
803 for (int i = 0; i < layout->lineCount(); i++) {
804 QTextLine line = layout->lineAt(i);
805 QRectF r = line.rect();
806 int start = line.textStart();
807 int count = line.textLength();
808
809 QString soFar;
810
811 svg += QString("<text x='%1' y='%2' font-family='%3' stroke='none' fill='#000000' text-anchor='left' font-size='%4' >\n")
812 .arg((left + r.left()) * dpi / GraphicsUtils::SVGDPI)
813 .arg((top + r.top() + line.ascent()) * dpi / GraphicsUtils::SVGDPI)
814 .arg("Droid Sans")
815 .arg(line.ascent() * dpi / GraphicsUtils::SVGDPI)
816 ;
817
818
819 bool inUrl = false;
820 for (int i = 0; i < count; i++) {
821 textCursor.setPosition(i + start + block.position(), QTextCursor::MoveAnchor);
822 textCursor.setPosition(i + start + block.position() + 1, QTextCursor::KeepAnchor);
823 QString html = textCursor.selection().toHtml();
824 if (UrlTag.indexIn(html) >= 0) {
825 if (inUrl) {
826 soFar += block.text().mid(start + i, 1);
827 continue;
828 }
829
830 svg += addText(soFar, false);
831 soFar = block.text().mid(start + i, 1);
832 inUrl = true;
833 }
834 else {
835 if (!inUrl) {
836 soFar += block.text().mid(start + i, 1);
837 continue;
838 }
839
840 svg += addText(soFar, !blackOnly);
841 soFar = block.text().mid(start + i, 1);
842 inUrl = false;
843 }
844 }
845 svg += addText(soFar, inUrl && !blackOnly);
846 svg += "</text>";
847 }
848 }
849
850
851 svg += "</g>";
852
853
854 return svg;
855 }
856
useViewIDForPixmap(ViewLayer::ViewID,bool)857 ViewLayer::ViewID Note::useViewIDForPixmap(ViewLayer::ViewID, bool)
858 {
859 return ViewLayer::IconView;
860 }
861
addedToScene(bool temporary)862 void Note::addedToScene(bool temporary)
863 {
864 positionGrip();
865
866 return ItemBase::addedToScene(temporary);
867 }
868