1 /**
2  * UGENE - Integrated Bioinformatics Tools.
3  * Copyright (C) 2008-2021 UniPro <ugene@unipro.ru>
4  * http://ugene.net
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2
9  * of the License, or (at your option) any later version.
10  *
11  * This program 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 this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19  * MA 02110-1301, USA.
20  */
21 
22 #include "ItemViewStyle.h"
23 
24 #include <QAbstractTextDocumentLayout>
25 #include <QBitmap>
26 #include <QColorDialog>
27 #include <QDomDocument>
28 #include <QFontDialog>
29 #include <QGraphicsSceneMouseEvent>
30 #include <QGraphicsSimpleTextItem>
31 #include <QGraphicsView>
32 #include <QPainter>
33 #include <QRadialGradient>
34 #include <QStyleOptionGraphicsItem>
35 #include <QTextDocument>
36 #include <QtMath>
37 
38 #include <U2Core/QVariantUtils.h>
39 
40 #include <U2Lang/ActorModel.h>
41 #include <U2Lang/WorkflowSettings.h>
42 
43 #include "WorkflowViewController.h"
44 #include "WorkflowViewItems.h"
45 
46 namespace U2 {
47 
48 const StyleId ItemStyles::SIMPLE = "simple";
49 const StyleId ItemStyles::EXTENDED = "ext";
50 
51 #define BGC QString("-bgc")
52 #define FONT QString("-font")
53 
54 const QColor ITEM_WITH_ENABLED_BREAKPOINT_BORDER_COLOR = QColor(178, 34, 34);
55 const QColor ITEM_WITH_DISABLED_BREAKPOINT_BORDER_COLOR = QColor(184, 134, 11);
56 
ItemViewStyle(WorkflowProcessItem * p,const QString & id)57 ItemViewStyle::ItemViewStyle(WorkflowProcessItem *p, const QString &id)
58     : QGraphicsObject(p),
59       defFont(WorkflowSettings::defaultFont()), id(id) {
60     setVisible(false);
61     bgColorAction = new QAction(tr("Background color"), this);
62     connect(bgColorAction, SIGNAL(triggered()), SLOT(selectBGColor()));
63 
64     fontAction = new QAction(tr("Font"), this);
65     connect(fontAction, SIGNAL(triggered()), SLOT(selectFont()));
66 }
67 
selectBGColor()68 void ItemViewStyle::selectBGColor() {
69     QColor res = QColorDialog::getColor(bgColor, owner->scene()->views().first());
70     if (res.isValid()) {
71         bgColor = res;
72         WorkflowScene *sc = qobject_cast<WorkflowScene *>(owner->scene());
73         if (sc != nullptr) {
74             sc->setModified(true);
75         }
76     }
77 }
78 
selectFont()79 void ItemViewStyle::selectFont() {
80     bool ok;
81     QFont res = QFontDialog::getFont(&ok, defFont, owner->scene()->views().first(), tr("Characters Font"), QFontDialog::DontUseNativeDialog);
82     if (ok) {
83         defFont = res;
84         WorkflowScene *sc = qobject_cast<WorkflowScene *>(owner->scene());
85         if (sc != nullptr) {
86             sc->setModified(true);
87         }
88     }
89 }
90 
saveState(QDomElement & el) const91 void ItemViewStyle::saveState(QDomElement &el) const {
92     if (bgColor != defaultColor())
93         el.setAttribute(id + BGC, QVariantUtils::var2String(bgColor));
94     if (defFont != QFont())
95         el.setAttribute(id + FONT, defFont.toString());
96 }
97 
loadState(QDomElement & el)98 void ItemViewStyle::loadState(QDomElement &el) {
99     if (el.hasAttribute(id + BGC)) {
100         QColor bgc = QVariantUtils::String2Var(el.attribute(id + BGC)).value<QColor>();
101         if (bgc.isValid()) {
102             bgColor = bgc;
103         }
104     }
105     if (el.hasAttribute(id + FONT)) {
106         defFont.fromString(el.attribute(id + FONT));
107     }
108 }
109 
SimpleProcStyle(WorkflowProcessItem * pit)110 SimpleProcStyle::SimpleProcStyle(WorkflowProcessItem *pit)
111     : ItemViewStyle(pit, ItemStyles::SIMPLE) {
112     owner = (pit);
113     owner->connect(owner->getProcess(), SIGNAL(si_labelChanged()), SLOT(sl_update()));
114     bgColor = defaultColor();
115 }
116 
defaultColor() const117 QColor SimpleProcStyle::defaultColor() const {
118     /*QColor ret(Qt::darkCyan);
119     ret.setAlpha(200);*/
120     QColor ret = WorkflowSettings::getBGColor();
121     return ret;
122 }
123 
boundingRect(void) const124 QRectF SimpleProcStyle::boundingRect(void) const {
125     // extra space added for clean antialiased painting
126     return QRectF(-R - 2, -R - 2, R * 2 + 4, R * 2 + 4);
127 }
128 
shape() const129 QPainterPath SimpleProcStyle::shape() const {
130     QPainterPath contour;
131     contour.addEllipse(QPointF(0, 0), R, R);
132     return contour;
133 }
134 
paint(QPainter * painter,const QStyleOptionGraphicsItem * option,QWidget * widget)135 void SimpleProcStyle::paint(QPainter *painter,
136                             const QStyleOptionGraphicsItem *option,
137                             QWidget *widget) {
138     Q_UNUSED(option);
139     Q_UNUSED(widget);
140     // painter->fillRect(boundingRect(), QBrush(Qt::magenta, Qt::Dense6Pattern));
141     painter->setRenderHint(QPainter::Antialiasing);
142     QPainterPath contour;
143     contour.addEllipse(QPointF(0, 0), R, R);
144 
145     QPen pen;
146     if (owner->isBreakpointInserted()) {
147         const QColor borderColor = (owner->isBreakpointEnabled()) ? ITEM_WITH_ENABLED_BREAKPOINT_BORDER_COLOR : ITEM_WITH_DISABLED_BREAKPOINT_BORDER_COLOR;
148         pen.setColor(borderColor);
149     }
150     if (owner->isSelected()) {
151         pen.setWidthF(2);
152         pen.setStyle(Qt::DashLine);
153     }
154     painter->setPen(pen);
155 
156     QRadialGradient rg(R / 2, -R / 2, R * 2);
157     rg.setColorAt(1, bgColor);
158     rg.setColorAt(0, QColor(Qt::white));
159     QBrush procBrush(rg);
160     painter->drawPath(contour);
161     painter->fillPath(contour, procBrush);
162 
163     painter->save();
164     QTextDocument d;
165     d.setDefaultFont(defFont);
166     d.setHtml("<center>" + owner->getProcess()->getLabel().toHtmlEscaped() + "</center>");
167     d.setTextWidth(R * 2);
168     // d.setDefaultTextOption(QTextOption(Qt::AlignHCenter));
169     painter->translate(-d.size().width() / 2, -d.size().height() / 2);
170     // painter->translate(-R, -R);
171     d.drawContents(painter, QRectF(0, 0, 2 * R, 2 * R));
172     painter->restore();
173 }
174 
175 // QPainterPath shape () const;
176 
177 #define MARGIN 5
178 
ExtendedProcStyle(WorkflowProcessItem * pit)179 ExtendedProcStyle::ExtendedProcStyle(WorkflowProcessItem *pit)
180     : ItemViewStyle(pit, ItemStyles::EXTENDED),
181       autoResize(true), resizing(NoResize) {
182     owner = (pit);
183     Actor *process = pit->getProcess();
184 
185     doc = process->getDescription();
186     if (doc) {
187         owner->connect(doc, SIGNAL(contentsChanged()), SLOT(sl_update()));
188     } else {
189         doc = new QTextDocument(pit);
190         doc->setHtml(QString("<center><b>%1</b></center><hr>%2<br>aLSKDJALSK LASDJ LASKD LASJD ALSKDJ XCKLJSLC Jas dlkjsdf sdlkjsdlfj sdlkfjlsdkfjs dlkfjsdlkfjsld flsdkjflsd kfjlsdkfj lsdkfjlsd flskfjsldkfjsldf jsdlkfjsdlkfjsdlfkjsdlfj")
191                          .arg(process->getLabel())
192                          .arg(process->getProto()->getDocumentation()));
193     }
194     owner->connect(fontAction, SIGNAL(triggered()), SLOT(sl_update()));
195     desc = new DescriptionItem(this);
196     refresh();
197 
198     resizeModeAction = new QAction(tr("Auto-resize to text"), this);
199     resizeModeAction->setCheckable(true);
200     resizeModeAction->setChecked(autoResize);
201     connect(resizeModeAction, SIGNAL(toggled(bool)), SLOT(setAutoResizeEnabled(bool)));
202 
203     bgColor = defaultColor();
204 }
205 
defaultColor() const206 QColor ExtendedProcStyle::defaultColor() const {
207     return WorkflowSettings::getBGColor();
208 }
209 
210 #define MINW 2 * R
211 #define MAXW 6 * R
212 
refresh()213 void ExtendedProcStyle::refresh() {
214     doc->setDefaultFont(defFont);
215     if (autoResize) {
216         bool snap2grid = WorkflowSettings::snap2Grid();
217         qreal w, h;
218         int cycle = 0;
219         do {
220             QSizeF docFrame = doc->size();
221             w = docFrame.width() + MARGIN * 2;
222             if (snap2grid) {
223                 w = roundUp(w, GRID_STEP);
224                 doc->setTextWidth(w - MARGIN * 2);
225                 docFrame = doc->size();
226             }
227             h = qMax(2 * R, docFrame.height()) + MARGIN * 2;
228             if (snap2grid) {
229                 h = roundUp(h, GRID_STEP);
230             }
231             // printf("ideal=%f, actual=%f\n",doc->idealWidth(),w);
232 
233             // try to improve docFrame proportions
234             if (++cycle > 2) {
235                 break;
236             }
237             if ((h / w < 0.6 && w > (MINW + MAXW) / 2)  // doc is disproportionately wide
238                 || (h / w > 1.6 && w < MAXW)  // doc is disproportionately long and can be widen
239                 || (w < MINW || w > MAXW)) {  // width is out of bounds
240                 doc->setTextWidth(qBound(MINW, (qreal)(h / 1.6), MAXW - MARGIN * 2));
241             }
242         } while (true);
243 
244         bounds = QRectF(-R, -R, w, h);
245     } else {
246         // bounds.setSize(bounds.size().expandedTo(doc->size() + QSizeF(MARGIN*2,MARGIN*2)));
247     }
248     desc->setDocument(doc);
249 }
250 
shape() const251 QPainterPath ExtendedProcStyle::shape() const {
252     QPainterPath contour;
253     contour.addRoundedRect(bounds, MARGIN, MARGIN);
254     return contour;
255 }
256 
paint(QPainter * painter,const QStyleOptionGraphicsItem * option,QWidget *)257 void ExtendedProcStyle::paint(QPainter *painter,
258                               const QStyleOptionGraphicsItem *option,
259                               QWidget *) {
260     if (owner->isSelected()) {
261         ((QStyleOptionGraphicsItem *)option)->state |= QStyle::State_Selected;
262     }
263     bgColor.setAlpha(64);
264     QRectF tb = boundingRect();
265     painter->fillRect(tb, QBrush(bgColor));
266 
267     painter->setRenderHint(QPainter::Antialiasing);
268 
269     if (doc->pageCount() > 1) {
270         QPointF tp = bounds.bottomRight();
271 
272         // draw a page corner
273         // QPen pen;
274         // pen.setWidthF(1.2);
275         // painter->setPen(pen);
276         // qreal len = 6;
277         // painter->drawLine(tp.x() - len, tp.y() - len, tp.x(), tp.y() - len);
278         // painter->drawLine(tp.x() - len, tp.y() - len, tp.x() - len, tp.y());
279 
280         // draw 3 dots at corner
281         QPointF dt(tp.x() - 7, tp.y() - 5);
282         QPainterPath dot;
283         dot.addEllipse(dt, 1.2, 1.2);
284         dot.addEllipse(dt - QPointF(4, 0), 1.2, 1.2);
285         dot.addEllipse(dt - QPointF(8, 0), 1.2, 1.2);
286         painter->fillPath(dot, QBrush(QColor(0x33, 0x33, 0x33)));
287     }
288 
289     QPen pen;
290     pen.setWidthF(1.3);
291     if (owner->isSelected()) {
292         pen.setStyle(Qt::DashLine);
293     }
294     if (owner->isBreakpointInserted()) {
295         const QColor borderColor = (owner->isBreakpointEnabled()) ? ITEM_WITH_ENABLED_BREAKPOINT_BORDER_COLOR : ITEM_WITH_DISABLED_BREAKPOINT_BORDER_COLOR;
296         pen.setColor(borderColor);
297     }
298 
299     painter->setPen(pen);
300     painter->drawRoundedRect(tb, MARGIN, MARGIN);
301 }
302 
303 #define RESIZE_AREA 4
sceneEventFilter(QGraphicsItem * watched,QEvent * event)304 bool ExtendedProcStyle::sceneEventFilter(QGraphicsItem *watched, QEvent *event) {
305     assert(watched == owner);
306     Q_UNUSED(watched);
307 
308     bool ret = false;
309 
310     switch (event->type()) {
311         case QEvent::GraphicsSceneHoverEnter:
312         case QEvent::GraphicsSceneHoverMove: {
313             QGraphicsSceneHoverEvent *he = dynamic_cast<QGraphicsSceneHoverEvent *>(event);
314             ret = updateCursor(he->pos());
315         } break;
316         case QEvent::GraphicsSceneMouseRelease:
317         case QEvent::GraphicsSceneHoverLeave:
318             if (event->type() == QEvent::GraphicsSceneMouseRelease) {
319                 desc->mouseReleaseEvent(dynamic_cast<QGraphicsSceneMouseEvent *>(event));
320             }
321             if (resizing) {
322                 owner->unsetCursor();
323             }
324             resizing = NoResize;
325             break;
326         case QEvent::GraphicsSceneMouseMove:
327             if (resizing && event->spontaneous()) {
328                 QGraphicsSceneMouseEvent *me = (dynamic_cast<QGraphicsSceneMouseEvent *>(event));
329                 WorkflowSettings::setSnap2Grid(false);
330                 QPointF newPos;
331                 if ((me->buttons() & Qt::LeftButton)) {
332                     ret = true;
333                     QRectF b2 = bounds;
334                     QPointF p = me->pos();
335                     QPointF p2 = p - me->lastPos();
336 
337                     if (resizing & RightResize &&  // border is either "pulled" or "pushed" by mouse pointer
338                                                    // in the latter case pointer should be close to the border
339                         ((p2.x() < 0 && b2.right() > p.x()) || (p2.x() > 0 && b2.right() < p.x()) || (qAbs(b2.right() - p.x()) < RESIZE_AREA))) {
340                         qreal rb = b2.right() + p2.x();
341                         b2.setRight(rb);
342 
343                         owner->updatePorts();
344 
345                         if (b2.width() < MARGIN * 2 + R) {
346                             return true;
347                         }
348                     }
349 
350                     if (resizing & LeftResize && ((p2.x() < 0 && b2.left() > p.x()) || (p2.x() > 0 && b2.left() < p.x()) || (qAbs(b2.left() - p.x()) < RESIZE_AREA))) {
351                         b2.setWidth(b2.width() - p2.x());
352                         newPos = owner->scenePos();
353                         newPos.setX(newPos.x() - (b2.width() - bounds.width()));
354 
355                         if (b2.width() < MARGIN * 2 + R) {
356                             return true;
357                         }
358 
359                         setFixedBounds(b2);
360                         owner->setPos(newPos);
361                     }
362 
363                     if (resizing & TopResize &&
364                         ((p2.y() < 0 && b2.top() > p.y()) || (p2.y() > 0 && b2.top() < p.y()) || (qAbs(b2.top() - p.y()) < RESIZE_AREA))) {
365                         b2.setHeight(b2.height() - p2.y());
366 
367                         newPos = owner->scenePos();
368                         newPos.setY(newPos.y() - (b2.height() - bounds.height()));
369 
370                         qreal minHeight = R + MARGIN * 2;
371 
372                         WorkflowScene *sc = qobject_cast<WorkflowScene *>(owner->scene());
373                         if (b2.height() < minHeight || newPos.y() < sc->sceneRect().top()) {
374                             return true;
375                         }
376 
377                         setFixedBounds(b2);
378                         owner->setPos(newPos);
379                     }
380 
381                     if (resizing & BottomResize && ((p2.y() < 0 && b2.bottom() > p.y()) || (p2.y() > 0 && b2.bottom() < p.y()) || (qAbs(b2.bottom() - p.y()) < RESIZE_AREA))) {
382                         b2.setBottom(b2.bottom() + p2.y());
383                         owner->updatePorts();
384                     }
385                     // qreal minHeight = qMax(doc->size().height(), R) + MARGIN*2;
386                     qreal minHeight = R + MARGIN * 2;
387                     if (b2.height() < minHeight) {
388                         b2.setHeight(minHeight);
389                     }
390 
391                     setFixedBounds(b2);
392 
393                     WorkflowScene *sc = qobject_cast<WorkflowScene *>(owner->scene());
394                     if (sc != nullptr) {
395                         sc->setModified(true);
396                     }
397                 }
398             }
399             break;
400         /*case QEvent::GraphicsSceneMousePress:
401         mousePressEvent(static_cast<QGraphicsSceneMouseEvent *>(event));
402         break;
403         case QEvent::GraphicsSceneMouseDoubleClick:
404         mouseDoubleClickEvent(static_cast<QGraphicsSceneMouseEvent *>(event));
405         break;
406         case QEvent::GraphicsSceneWheel:
407         wheelEvent(static_cast<QGraphicsSceneWheelEvent *>(event));
408         break;
409         case QEvent::KeyPress:
410         keyPressEvent(static_cast<QKeyEvent *>(event));
411         break;
412         case QEvent::KeyRelease:
413         keyReleaseEvent(static_cast<QKeyEvent *>(event));
414         break;
415         case QEvent::InputMethod:
416         inputMethodEvent(static_cast<QInputMethodEvent *>(event));
417         break;*/
418         default:
419             return false;
420     }
421 
422     return ret;
423 }
424 
updateCursor(const QPointF & p)425 bool ExtendedProcStyle::updateCursor(const QPointF &p) {
426     resizing = NoResize;
427     qreal dx = qAbs(bounds.right() - p.x());
428     qreal dy = qAbs(bounds.bottom() - p.y());
429     if (dx < RESIZE_AREA) {
430         resizing |= RightResize;
431     }
432     if (dx > (bounds.width() - RESIZE_AREA)) {
433         resizing |= LeftResize;
434     }
435     if (dy < RESIZE_AREA) {
436         resizing |= BottomResize;
437     }
438     if (dy > (bounds.height() - RESIZE_AREA)) {
439         resizing |= TopResize;
440     }
441 
442     switch (resizing) {
443         case NoResize:
444             owner->unsetCursor();
445             break;
446         case RightResize:
447         case LeftResize:
448             owner->setCursor(Qt::SizeHorCursor);
449             break;
450         case BottomResize:
451         case TopResize:
452             owner->setCursor(Qt::SizeVerCursor);
453             break;
454         case RBResize:
455         case LTResize:
456             owner->setCursor(Qt::SizeFDiagCursor);
457             break;
458         case LBResize:
459         case RTResize:
460             owner->setCursor(Qt::SizeBDiagCursor);
461             break;
462     }
463     return resizing != NoResize;
464 }
465 
setFixedBounds(const QRectF & b)466 void ExtendedProcStyle::setFixedBounds(const QRectF &b) {
467     doc->setPageSize(b.size() - QSizeF(MARGIN * 2, MARGIN * 2));
468     if (bounds != b) {
469         bounds = b;
470         owner->prepareUpdate();
471 
472         foreach (WorkflowPortItem *pit, owner->getPortItems()) {
473             pit->adaptOwnerShape();
474         }
475     }
476     owner->update();
477     resizeModeAction->setChecked(false);
478 }
479 
setAutoResizeEnabled(bool b)480 void ExtendedProcStyle::setAutoResizeEnabled(bool b) {
481     autoResize = b;
482     if (autoResize) {
483         doc->setPageSize(QSizeF(-1, -1));
484         owner->sl_update();
485     }
486 }
487 
getContextMenuActions() const488 QList<QAction *> ExtendedProcStyle::getContextMenuActions() const {
489     QList<QAction *> ret;
490     ret << resizeModeAction << bgColorAction << fontAction;
491     return ret;
492 }
493 
494 //#define ARM QString("arm")
495 #define BOUNDS QString("bounds")
496 
saveState(QDomElement & el) const497 void ExtendedProcStyle::saveState(QDomElement &el) const {
498     // el.setAttribute(ARM, autoResize);
499     if (!autoResize) {
500         el.setAttribute(BOUNDS, QVariantUtils::var2String(bounds));
501     }
502     ItemViewStyle::saveState(el);
503 }
504 
loadState(QDomElement & el)505 void ExtendedProcStyle::loadState(QDomElement &el) {
506     if (el.hasAttribute(BOUNDS)) {
507         QRectF b = QVariantUtils::String2Var(el.attribute(BOUNDS)).toRectF();
508         if (!b.isNull()) {
509             setFixedBounds(b);
510         }
511     }
512     ItemViewStyle::loadState(el);
513 }
514 
linkHovered(const QString & url)515 void ExtendedProcStyle::linkHovered(const QString &url) {
516     if (url.isEmpty()) {
517         owner->unsetCursor();
518     } else {
519         owner->setCursor(Qt::PointingHandCursor);
520     }
521 }
522 
HintItem(const QString & text,QGraphicsItem * parent)523 HintItem::HintItem(const QString &text, QGraphicsItem *parent)
524     : QGraphicsTextItem(text, parent), dragging(false) {
525     setFlag(QGraphicsItem::ItemIsSelectable);
526 #if (QT_VERSION >= QT_VERSION_CHECK(4, 6, 0))
527     setFlag(QGraphicsItem::ItemSendsGeometryChanges, true);
528 #endif
529     document()->setDefaultTextOption(QTextOption(Qt::AlignCenter));
530     setTextWidth(qMin(3 * R, document()->idealWidth()));
531     QRectF tb = boundingRect();
532     setPos(-tb.width() / 2, -tb.height() - 3);
533     setDefaultTextColor(QColor(Qt::gray).darker());
534     QFont f = font();
535     f.setWeight(QFont::Light);
536     setFont(f);
537 }
538 
itemChange(GraphicsItemChange change,const QVariant & value)539 QVariant HintItem::itemChange(GraphicsItemChange change, const QVariant &value) {
540     if (change == ItemSelectedChange && value.toBool()) {
541         parentItem()->setSelected(true);
542         return false;
543     }
544     if (change == ItemPositionChange) {
545         QPointF newPos = value.toPointF();
546         if (scene()) {
547             QRectF bound = boundingRect();
548             QRectF sceneRect = scene()->sceneRect();
549             // scene topLeft in parent coords
550             QPointF tl = mapToParent(mapFromScene(sceneRect.topLeft()));
551             sceneRect.moveTopLeft(tl);
552 
553             qreal x0 = sceneRect.left() - bound.left();
554             qreal x1 = sceneRect.left() + sceneRect.width() - bound.right();
555             qreal y0 = sceneRect.top() - bound.top();
556             qreal y1 = sceneRect.top() + sceneRect.height() - bound.bottom();
557 
558             newPos.setX(qBound(x0, newPos.x(), x1));
559             newPos.setY(qBound(y0, newPos.y(), y1));
560         }
561         return newPos;
562     }
563     if (change == ItemPositionHasChanged) {
564         parentItem()->update();
565         if (scene()) {
566             foreach (QGraphicsView *v, scene()->views()) {
567                 v->ensureVisible(this, 0, 0);
568             }
569         }
570     }
571     return QGraphicsItem::itemChange(change, value);
572 }
573 
mouseMoveEvent(QGraphicsSceneMouseEvent * event)574 void HintItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event) {
575     if (event->buttons() & Qt::LeftButton) {
576         if (!dragging) {
577             initPos = pos();
578             dragging = true;
579         }
580 
581         QPointF delta = event->screenPos() - event->buttonDownScreenPos(Qt::LeftButton);
582         setPos(initPos + delta);
583     } else {
584         event->ignore();
585     }
586 }
587 
mouseReleaseEvent(QGraphicsSceneMouseEvent * event)588 void HintItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) {
589     dragging = false;
590     QGraphicsTextItem::mouseReleaseEvent(event);
591 }
592 
DescriptionItem(ExtendedProcStyle * p)593 DescriptionItem::DescriptionItem(ExtendedProcStyle *p)
594     : QGraphicsTextItem(p) {
595     setPos(-R + MARGIN, -R + MARGIN);
596     setTextInteractionFlags(Qt::LinksAccessibleByMouse | Qt::LinksAccessibleByKeyboard);
597     p->connect(this, SIGNAL(linkActivated(const QString &)), SIGNAL(linkActivated(const QString &)));
598     p->connect(this, SIGNAL(linkHovered(const QString &)), SLOT(linkHovered(const QString &)));
599 }
600 
boundingRect() const601 QRectF DescriptionItem::boundingRect() const {
602     QRectF bounds = parentItem()->boundingRect();
603     bounds.setBottomRight(bounds.bottomRight() - QPointF(MARGIN, MARGIN));
604     bounds.translate(R - MARGIN, R - MARGIN);
605     return bounds;
606 }
607 
paint(QPainter * painter,const QStyleOptionGraphicsItem * option,QWidget * widget)608 void DescriptionItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) {
609     QStyleOptionGraphicsItem deselectedOption = *option;
610     deselectedOption.state &= ~(QStyle::State_Selected | QStyle::State_HasFocus);
611     QGraphicsTextItem::paint(painter, &deselectedOption, widget);
612 }
613 
mouseReleaseEvent(QGraphicsSceneMouseEvent * event)614 void DescriptionItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) {
615     event->setPos(mapFromParent(event->pos()));
616     QGraphicsTextItem::mouseReleaseEvent(event);
617 }
618 
sceneEvent(QEvent * event)619 bool DescriptionItem::sceneEvent(QEvent *event) {
620     switch (event->type()) {
621         case QEvent::GraphicsSceneHoverMove:
622         case QEvent::GraphicsSceneHoverEnter: {
623             ExtendedProcStyle *owner = qgraphicsitem_cast<ExtendedProcStyle *>(parentItem());
624             if (owner->resizing) {
625                 QGraphicsSceneHoverEvent *he = static_cast<QGraphicsSceneHoverEvent *>(event);
626                 const QPointF &p = mapToParent(he->pos());
627                 owner->updateCursor(p);
628             }
629         } break;
630         default:
631             break;
632     }
633     return QGraphicsTextItem::sceneEvent(event);
634 }
635 
contextMenuEvent(QGraphicsSceneContextMenuEvent * event)636 void DescriptionItem::contextMenuEvent(QGraphicsSceneContextMenuEvent *event) {
637     QAbstractTextDocumentLayout *layout = document()->documentLayout();
638     const QString &href = layout->anchorAt(event->pos());
639 
640     if (href.isEmpty()) {
641         event->ignore();
642         return;
643     }
644 
645     ItemViewStyle *style = qgraphicsitem_cast<ItemViewStyle *>(parentItem());
646     WorkflowProcessItem const *procItem = style->getOwner();
647     Actor *actor = procItem->getProcess();
648     WorkflowScene *ws = procItem->getWorkflowScene();
649     ws->setupLinkCtxMenu(href, actor, event->screenPos());
650 }
651 
652 }  // namespace U2
653