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 "WorkflowViewItems.h"
23
24 #include <QBitmap>
25 #include <QDomElement>
26 #include <QGraphicsItem>
27 #include <QGraphicsSceneMouseEvent>
28 #include <QGraphicsSimpleTextItem>
29 #include <QGraphicsTextItem>
30 #include <QGraphicsView>
31 #include <QPainter>
32 #include <QRadialGradient>
33 #include <QStyleOptionGraphicsItem>
34 #include <QTextDocument>
35 #include <QtMath>
36
37 #include <U2Core/Log.h>
38 #include <U2Core/QVariantUtils.h>
39
40 #include <U2Lang/ActorModel.h>
41 #include <U2Lang/ActorPrototypeRegistry.h>
42 #include <U2Lang/IntegralBus.h>
43 #include <U2Lang/IntegralBusModel.h>
44 #include <U2Lang/WorkflowRunTask.h>
45 #include <U2Lang/WorkflowSettings.h>
46 #include <U2Lang/WorkflowUtils.h>
47
48 #include "ItemViewStyle.h"
49 #include "WorkflowEditor.h"
50 #include "WorkflowViewController.h"
51
52 namespace U2 {
53
WorkflowProcessItem(Actor * prc)54 WorkflowProcessItem::WorkflowProcessItem(Actor *prc)
55 : process(prc), hasBreakpoint(false), highlighting(nullptr) /*, inspectionItem(NULL)*/ {
56 setToolTip(process->getProto()->getDocumentation());
57 setFlag(QGraphicsItem::ItemIsSelectable, true);
58 setFlag(QGraphicsItem::ItemIsMovable, true);
59 #if (QT_VERSION >= QT_VERSION_CHECK(4, 6, 0))
60 setFlag(QGraphicsItem::ItemSendsGeometryChanges, true);
61 #endif
62 setAcceptHoverEvents(true);
63
64 styles[ItemStyles::SIMPLE] = new SimpleProcStyle(this);
65 styles[ItemStyles::EXTENDED] = new ExtendedProcStyle(this);
66 currentStyle = getStyleByIdSafe(WorkflowSettings::defaultStyle());
67 currentStyle->setVisible(true);
68 createPorts();
69 connect(prc, SIGNAL(si_descriptionChanged()), SLOT(sl_descriptionChanged()));
70 }
71
~WorkflowProcessItem()72 WorkflowProcessItem::~WorkflowProcessItem() {
73 qDeleteAll(styles.values());
74 qDeleteAll(ports);
75 delete highlighting;
76 }
77
getStyleByIdSafe(StyleId id) const78 ItemViewStyle *WorkflowProcessItem::getStyleByIdSafe(StyleId id) const {
79 if (!styles.contains(id)) {
80 uiLog.trace(QString("Unknown workflow item style: %1").arg(id));
81 id = ItemStyles::EXTENDED;
82 }
83 ItemViewStyle *result = styles.value(id);
84 assert(result != nullptr);
85 return result;
86 }
87
getStyleById(const StyleId & id) const88 ItemViewStyle *WorkflowProcessItem::getStyleById(const StyleId &id) const {
89 return styles.value(id);
90 }
91
containsStyle(const StyleId & id) const92 bool WorkflowProcessItem::containsStyle(const StyleId &id) const {
93 return styles.contains(id);
94 }
95
getPort(const QString & id) const96 WorkflowPortItem *WorkflowProcessItem::getPort(const QString &id) const {
97 foreach (WorkflowPortItem *pit, ports) {
98 if (pit->getPort()->getId() == id) {
99 return pit;
100 }
101 }
102 return nullptr;
103 }
104
createPorts()105 void WorkflowProcessItem::createPorts() {
106 assert(ports.isEmpty());
107
108 int num = process->getInputPorts().size() + 1;
109 qreal pie = 180 / num;
110 int i = 1;
111 QGraphicsScene *sc = scene();
112 foreach (Port *port, process->getInputPorts()) {
113 WorkflowPortItem *pit = new WorkflowPortItem(this, port);
114 connect(port, SIGNAL(si_enabledChanged(bool)), pit, SLOT(sl_onVisibleChanged(bool)));
115 ports << pit;
116 pit->setOrientation(90 + pie * i++);
117 if (sc) {
118 sc->addItem(pit);
119 }
120 pit->sl_onVisibleChanged(port->isEnabled());
121 }
122 num = process->getOutputPorts().size() + 1;
123 pie = 180 / num;
124 i = 1;
125 foreach (Port *port, process->getOutputPorts()) {
126 WorkflowPortItem *pit = new WorkflowPortItem(this, port);
127 connect(port, SIGNAL(si_enabledChanged(bool)), pit, SLOT(sl_onVisibleChanged(bool)));
128 ports << pit;
129 pit->setOrientation(270 + pie * i++);
130 if (sc) {
131 sc->addItem(pit);
132 }
133 pit->sl_onVisibleChanged(port->isEnabled());
134 }
135 }
136
sl_update()137 void WorkflowProcessItem::sl_update() {
138 prepareGeometryChange();
139 currentStyle->refresh();
140 foreach (WorkflowPortItem *pit, ports) {
141 pit->adaptOwnerShape();
142 }
143 update();
144 }
145
sl_descriptionChanged()146 void WorkflowProcessItem::sl_descriptionChanged() {
147 setToolTip(process->getProto()->getDocumentation());
148 }
149
setStyle(StyleId s)150 void WorkflowProcessItem::setStyle(StyleId s) {
151 prepareGeometryChange();
152 currentStyle->setVisible(false);
153 currentStyle = getStyleByIdSafe(s);
154 currentStyle->setVisible(true);
155 currentStyle->refresh();
156 foreach (WorkflowPortItem *pit, ports) {
157 pit->setStyle(s);
158 }
159 assert(currentStyle);
160 update();
161 }
162
boundingRect(void) const163 QRectF WorkflowProcessItem::boundingRect(void) const {
164 QRectF brect = currentStyle->boundingRect();
165 brect.setTop(brect.top() - QFontMetrics(QFont()).height() * 2 - 2);
166 return brect;
167 }
168
portsBoundingRect() const169 QRectF WorkflowProcessItem::portsBoundingRect() const {
170 QRectF rect; // null rect
171 foreach (WorkflowPortItem *p, getPortItems()) {
172 QRectF pBound = p->boundingRect();
173 QPointF pCenter = pBound.center();
174 pCenter = p->mapToItem(this, pCenter);
175 pBound.moveCenter(pCenter);
176 rect |= pBound;
177 }
178 return rect;
179 }
180
shape() const181 QPainterPath WorkflowProcessItem::shape() const {
182 return currentStyle->shape();
183 }
184
paint(QPainter * painter,const QStyleOptionGraphicsItem *,QWidget *)185 void WorkflowProcessItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *) {
186 WorkflowAbstractRunner *rt = getWorkflowScene()->getRunner();
187 if (rt) {
188 //{WorkerWaiting, WorkerReady, WorkerRunning, WorkerDone};
189 static QColor rsColors[5] = {QColor(234, 143, 7), "#04AA04", "#AA0404", "#0404AA", "#9B30FF"};
190 //{QColor(234,143,7),QColor(Qt::red),QColor(Qt::green),QColor(0,0,255)};
191 static QString rsNames[5] = {("Waiting"), ("Ready"), ("Running"), ("Done"), ("Paused")};
192
193 const QList<WorkerState> rsList = rt->getState(this->process);
194 WorkerState state = WorkerDone;
195 if (rsList.contains(WorkerRunning)) {
196 state = WorkerRunning;
197 } else if (rsList.contains(WorkerReady)) {
198 state = WorkerReady;
199 } else if (rsList.contains(WorkerWaiting)) {
200 state = WorkerWaiting;
201 } else if (rsList.contains(WorkerPaused)) {
202 state = WorkerPaused;
203 }
204
205 QString stateName = rsNames[state];
206 QColor scolor = rsColors[state];
207 painter->setPen(scolor);
208 QRectF brect = boundingRect();
209 qreal fh = QFontMetrics(QFont()).height();
210
211 if (rsList.size() == 1) {
212 brect.setTop(brect.top() + 2 + fh);
213 }
214
215 painter->drawRoundedRect(brect, 5, 5);
216 painter->drawText(brect, Qt::AlignHCenter, stateName);
217 if (rsList.size() == 1) {
218 } else {
219 uint vals[4] = {0, 0, 0, 0};
220 for (int i = rsList.size(); i > 0;) {
221 vals[rsList.at(--i)]++;
222 }
223
224 // draw progress bar
225 if (state != WorkerDone) {
226 QRectF textRect(brect.topLeft() + QPointF(0, fh - 1), QSizeF(brect.width(), 3));
227 textRect.setLeft(brect.left() + 1);
228 textRect.setRight(brect.right() - 1);
229 QColor fc = /*rsColors[WorkerDone];//*/ QColor(0, 80, 222);
230 fc.setAlpha(90);
231 painter->setPen(fc);
232 painter->drawRect(textRect);
233 qreal done = (qreal)vals[WorkerDone] / rsList.size();
234 QBrush brush(fc);
235 // QLinearGradient lg(textRect.topLeft(),textRect.topRight());
236 // lg.setColorAt(0, fc);
237 // lg.setColorAt(done, fc.lighter(128));
238 // lg.setColorAt(done + 1./rsList.size(), QColor(Qt::white));
239 // lg.setColorAt(1, QColor(Qt::white));
240 // brush = QBrush(lg);
241 textRect.setRight(textRect.left() + textRect.width() * done);
242 painter->fillRect(textRect, brush);
243 }
244
245 // draw extended text
246 painter->save();
247 QTextDocument d;
248 d.setHtml("<center><font size='-1'>" + QString("<font color='%1'>%2/</font>"
249 " <!--font color='%3'>%4/</font-->"
250 " <font color='%5'>%6/</font>"
251 " <font color='%7'>%8/</font>"
252 " <font color='black'>%9</font>")
253 .arg(rsColors[WorkerWaiting].name())
254 .arg(vals[WorkerWaiting])
255 .arg(rsColors[WorkerReady].name())
256 .arg(vals[WorkerReady])
257 .arg(rsColors[WorkerRunning].name())
258 .arg(vals[WorkerRunning])
259 .arg(rsColors[WorkerDone].name())
260 .arg(vals[WorkerDone])
261 .arg(rsList.size()) +
262 "</font></center>");
263 // d.setTextWidth(brect.width());
264 painter->translate(brect.center().x() - d.idealWidth() / 2, brect.top() + fh);
265 d.drawContents(painter /*, brect*/);
266 painter->restore();
267
268 // qreal unit = brect.width()/rsList.size();
269 // qreal step = 3;
270 // qreal w = 10;
271 // QPointF base(brect.topLeft());
272 // base.ry() += 5;
273
274 // for (int i = 3; i>=0; i--) {
275 // // base.ry() -= step;
276 // // painter->setPen(rsColors[i]); painter->drawLine(base, base + QPointF(unit*vals[i],0));
277 // QRectF rt(base, base + QPointF(unit*vals[i],w)); base = rt.topRight();
278 // painter->setPen(rsColors[i]);
279 // QColor fc = rsColors[i]; fc.setAlpha(128);
280 // painter->fillRect(rt, QBrush(fc));
281 // painter->drawText(rt, Qt::AlignJustify|Qt::AlignVCenter, rsNames[i]);
282 // }
283 }
284 }
285 }
286
updatePorts()287 void WorkflowProcessItem::updatePorts() {
288 foreach (WorkflowPortItem *pit, ports) {
289 pit->setPos(pos());
290 foreach (WorkflowBusItem *bit, pit->getDataFlows()) {
291 bit->updatePos();
292 }
293 }
294 }
295
itemChange(GraphicsItemChange change,const QVariant & value)296 QVariant WorkflowProcessItem::itemChange(GraphicsItemChange change, const QVariant &value) {
297 switch (change) {
298 case ItemSelectedHasChanged: {
299 currentStyle->update();
300 } break;
301 case ItemZValueHasChanged: {
302 qreal z = value.value<qreal>();
303 foreach (WorkflowPortItem *pit, ports) {
304 pit->setZValue(z);
305 }
306 } break;
307 case ItemPositionChange: {
308 // value is the new position.
309 QPointF newPos = value.toPointF();
310 if (scene() && pos() != QPointF(0, 0)) {
311 QRectF bound = boundingRect() | childrenBoundingRect() | portsBoundingRect();
312 QRectF sceneRect = scene()->sceneRect();
313
314 qreal x0 = sceneRect.left() - bound.left();
315 qreal x1 = sceneRect.left() + sceneRect.width() - bound.right() - 10; // extra space for scroll bars
316 qreal y0 = sceneRect.top() - bound.top();
317 qreal y1 = sceneRect.top() + sceneRect.height() - bound.bottom() - 10;
318
319 newPos.setX(qBound(x0, newPos.x(), x1));
320 newPos.setY(qBound(y0, newPos.y(), y1));
321 }
322 if (WorkflowSettings::snap2Grid()) {
323 newPos.setX(round(newPos.x(), GRID_STEP));
324 newPos.setY(round(newPos.y(), GRID_STEP));
325 }
326 return newPos;
327 /*foreach(WorkflowPortItem* pit, ports) {
328 foreach(WorkflowBusItem*bit, pit->getDataFlows()) {
329 bit->prepareGeometryChange();
330 }
331 }*/
332 } break;
333 case ItemPositionHasChanged: {
334 updatePorts();
335
336 WorkflowScene *sc = qobject_cast<WorkflowScene *>(scene());
337 if (sc != nullptr) {
338 if (!sc->views().isEmpty()) {
339 foreach (QGraphicsView *view, sc->views()) {
340 QRectF itemRect = boundingRect() | childrenBoundingRect();
341 // ports are not the child items atm
342 // unite with their bounds
343 itemRect |= portsBoundingRect();
344 QPointF itemCenter = itemRect.center();
345 itemCenter = mapToScene(itemCenter);
346 itemRect.moveCenter(itemCenter);
347 view->ensureVisible(itemRect, 0, 0);
348 }
349 }
350 sc->setModified(true);
351 }
352 if (scene()) {
353 scene()->update();
354 }
355 } break;
356 case ItemSceneHasChanged: {
357 WorkflowScene *ws = getWorkflowScene();
358 if (ws) {
359 ItemViewStyle *viewStyle = styles.value(ItemStyles::EXTENDED);
360 ExtendedProcStyle *extStyle = qgraphicsitem_cast<ExtendedProcStyle *>(viewStyle);
361 assert(extStyle);
362 WorkflowView *view = ws->getController();
363 if (view) {
364 connect(extStyle, SIGNAL(linkActivated(const QString &)), view->getPropertyEditor(), SLOT(sl_linkActivated(const QString &)));
365 }
366
367 foreach (WorkflowPortItem *pit, ports) {
368 ws->addItem(pit);
369 }
370 }
371 } break;
372 case ItemSceneChange:
373 if ((value.value<QGraphicsScene *>()) == nullptr) {
374 foreach (WorkflowPortItem *pit, ports) {
375 scene()->removeItem(pit);
376 }
377 // scene()->removeItem(inspectionItem);
378 // delete inspectionItem;
379 }
380 break;
381 /* case ItemSelectedChange:
382 if (NULL != inspectionItem) {
383 inspectionItem->setPermanent(!inspectionItem->isPermanent());
384 if (!inspectionItem->isPermanent()) {
385 inspectionItem->eraseFromScene();
386 }
387 }
388 break;*/
389 default:
390 break;
391 }
392 return QGraphicsItem::itemChange(change, value);
393 }
394
sceneEvent(QEvent * event)395 bool WorkflowProcessItem::sceneEvent(QEvent *event) {
396 if (currentStyle->sceneEventFilter(this, event)) {
397 return true;
398 }
399 return QGraphicsItem::sceneEvent(event);
400 }
401
getContextMenuActions() const402 QList<QAction *> WorkflowProcessItem::getContextMenuActions() const {
403 return currentStyle->getContextMenuActions();
404 }
405
saveState(QDomElement & el) const406 void WorkflowProcessItem::saveState(QDomElement &el) const {
407 el.setAttribute("pos", QVariantUtils::var2String(pos()));
408 el.setAttribute("style", styles.key(currentStyle));
409 foreach (ItemViewStyle *style, styles) {
410 QDomElement stel = el.ownerDocument().createElement(style->getId());
411 style->saveState(stel);
412 if (stel.hasAttributes() || stel.hasChildNodes()) {
413 el.appendChild(stel);
414 }
415 }
416 }
417
loadState(QDomElement & el)418 void WorkflowProcessItem::loadState(QDomElement &el) {
419 const QString posS = el.attribute("pos");
420 const QPointF pos = QVariantUtils::String2Var(posS).toPointF();
421 assert(!pos.isNull());
422 setPos(pos);
423
424 foreach (ItemViewStyle *style, styles) {
425 QDomElement stel = el.elementsByTagName(style->getId()).item(0).toElement();
426 if (stel.isNull())
427 continue;
428 style->loadState(stel);
429 }
430 QString key = el.attribute("style");
431 if (styles.contains(key)) {
432 setStyle(key);
433 }
434 }
435
mouseMoveEvent(QGraphicsSceneMouseEvent * event)436 void WorkflowProcessItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event) {
437 if (event->buttons() & Qt::LeftButton) {
438 if (initialPositions.isEmpty()) {
439 if (isSelected()) {
440 QList<QGraphicsItem *> selectedItems = scene()->selectedItems();
441 foreach (QGraphicsItem *item, selectedItems) {
442 if (item->type() == WorkflowProcessItemType) {
443 initialPositions[item] = item->scenePos();
444 }
445 }
446 } else {
447 initialPositions[this] = scenePos();
448 }
449 }
450
451 // Find the active view.
452 QGraphicsView *view = 0;
453 if (event->widget()) {
454 view = qobject_cast<QGraphicsView *>(event->widget()->parentWidget());
455 }
456
457 for (int i = 0, n = initialPositions.keys().size(); i < n; i++) {
458 QGraphicsItem *item = initialPositions.keys().at(i);
459
460 QPointF currentParentPos = view->mapToScene(view->mapFromGlobal(event->screenPos()));
461 QPointF buttonDownParentPos = view->mapToScene(view->mapFromGlobal(event->buttonDownScreenPos(Qt::LeftButton)));
462
463 item->setPos(initialPositions.value(item) + currentParentPos - buttonDownParentPos);
464 }
465 } else {
466 event->ignore();
467 }
468 }
469
mouseReleaseEvent(QGraphicsSceneMouseEvent * event)470 void WorkflowProcessItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) {
471 initialPositions.clear();
472 QGraphicsItem::mouseReleaseEvent(event);
473 }
474
toggleBreakpoint()475 void WorkflowProcessItem::toggleBreakpoint() {
476 hasBreakpoint = !hasBreakpoint;
477 if (!hasBreakpoint) {
478 hasEnabledBreakpoint = false;
479 } else {
480 if (nullptr == highlighting) {
481 highlighting = new WorkflowHighlightItem(this);
482 }
483 hasEnabledBreakpoint = true;
484 }
485 }
486
toggleBreakpointState()487 void WorkflowProcessItem::toggleBreakpointState() {
488 Q_ASSERT(hasBreakpoint);
489 hasEnabledBreakpoint = !hasEnabledBreakpoint;
490 }
491
isBreakpointInserted()492 bool WorkflowProcessItem::isBreakpointInserted() {
493 return hasBreakpoint;
494 }
495
isBreakpointEnabled()496 bool WorkflowProcessItem::isBreakpointEnabled() {
497 return hasEnabledBreakpoint;
498 }
499
highlightItem()500 void WorkflowProcessItem::highlightItem() {
501 highlighting->replay();
502 }
503
504 ///////////// PIO /////////////
505
findNearbyBindingCandidate(const QPointF & pos) const506 WorkflowPortItem *WorkflowPortItem::findNearbyBindingCandidate(const QPointF &pos) const {
507 QPainterPath neighbourhood;
508 neighbourhood.addEllipse(pos, R / 2, R / 2);
509 // QRectF neighbourhood(pos.x() - R/2, pos.y() + R/2, R, R);
510 WorkflowPortItem *candidate = nullptr;
511 qreal distance = R * 2;
512 foreach (QGraphicsItem *it, scene()->items(neighbourhood, Qt::IntersectsItemBoundingRect)) {
513 WorkflowPortItem *next = qgraphicsitem_cast<WorkflowPortItem *>(it);
514 if (next) {
515 if (bindCandidates.contains(next)) {
516 QLineF l(pos, next->headToScene());
517 qreal len = l.length();
518 if (distance > len) {
519 distance = len;
520 candidate = next;
521 }
522 }
523 }
524 }
525 return candidate;
526 }
527
528 // static const QCursor portRotationCursor = QCursor(QBitmap(":workflow_designer/images/rot_cur.png")); //FIXME
529 static const int portRotationModifier = Qt::AltModifier;
530 static const int bl = (int)A / 4;
531
WorkflowPortItem(WorkflowProcessItem * owner,Port * p)532 WorkflowPortItem::WorkflowPortItem(WorkflowProcessItem *owner, Port *p)
533 : /*StyledItem(owner), */ currentStyle(owner->getStyle()), port(p), owner(owner), orientation(0), dragging(false), rotating(false),
534 sticky(false), highlight(false), mouseMoveIsBeingProcessed(false) {
535 setFlags(ItemIsSelectable | ItemIsFocusable);
536 setAcceptHoverEvents(true);
537 QString tt = p->isInput() ? "Input port (" : "Output port (";
538 tt += p->getDocumentation();
539 tt += ").\nDrag it to connect to other process/port."
540 "\nHold Alt key while dragging to change port orientation";
541 setToolTip(tt);
542
543 setPos(owner->pos());
544 setZValue(owner->zValue());
545 }
546
~WorkflowPortItem()547 WorkflowPortItem::~WorkflowPortItem() {
548 assert(flows.isEmpty());
549 }
550
setStyle(StyleId s)551 void WorkflowPortItem::setStyle(StyleId s) {
552 Q_UNUSED(s);
553 currentStyle = owner->getStyle();
554 adaptOwnerShape();
555 }
556
adaptOwnerShape()557 void WorkflowPortItem::adaptOwnerShape() {
558 setOrientation(orientation);
559 }
560
setOrientation(qreal angle)561 void WorkflowPortItem::setOrientation(qreal angle) {
562 qreal oldOrientation = orientation;
563 orientation = angle;
564
565 if (ItemStyles::SIMPLE == currentStyle) {
566 angle = -angle;
567 qreal x = R * qCos(angle * 2 * M_PI / 360);
568 qreal y = R * qSin(angle * 2 * M_PI / 360);
569
570 resetTransform();
571 setTransform(QTransform::fromTranslate(x, y), true);
572 setRotation(angle);
573 } else { // EXTENDED STYLE
574 resetTransform();
575 QRectF rec = owner->boundingRect();
576 QPolygonF pol(owner->shape().toFillPolygon());
577 qreal radius = qMax(rec.width(), rec.height()) * 2;
578 QLineF centerLine(0, 0, radius, 0);
579 assert(pol.containsPoint(centerLine.p1(), Qt::WindingFill));
580 assert(!pol.containsPoint(centerLine.p2(), Qt::WindingFill));
581 centerLine.setAngle(angle);
582 QPointF p1 = pol.first();
583 QPointF p2;
584 QLineF polyLine;
585 QPointF intersectPoint;
586 for (int i = 1; i < pol.count(); ++i) {
587 p2 = pol.at(i);
588 polyLine = QLineF(p1, p2);
589 if (QLineF::BoundedIntersection == polyLine.intersect(centerLine, &intersectPoint)) {
590 break;
591 }
592 p1 = p2;
593 }
594
595 setTransform(QTransform::fromTranslate(intersectPoint.x(), intersectPoint.y()), true);
596 qreal norm = polyLine.normalVector().angle();
597 qreal df = qAbs(norm - angle);
598 if (df > 90 && df < 270) {
599 norm += 180;
600 }
601 setRotation(-norm);
602 }
603 if (oldOrientation != orientation) {
604 WorkflowScene *sc = qobject_cast<WorkflowScene *>(owner->scene());
605 if (sc != nullptr) {
606 sc->setModified(true);
607 sc->update();
608 }
609 }
610 }
611
getDataFlow(const WorkflowPortItem * otherPit) const612 WorkflowBusItem *WorkflowPortItem::getDataFlow(const WorkflowPortItem *otherPit) const {
613 foreach (WorkflowBusItem *dit, flows) {
614 if ((port->isInput() ? dit->getOutPort() : dit->getInPort()) == otherPit) {
615 return dit;
616 }
617 }
618 return nullptr;
619 }
620
checkTypes(Port * p1,Port * p2)621 static bool checkTypes(Port *p1, Port *p2) {
622 Port *ip = p1->isInput() ? p1 : p2;
623 Port *op = p1->isInput() ? p2 : p1;
624 DataTypePtr idt = ip->getType();
625 DataTypePtr odt = op->getType();
626 if (idt->isSingle() && odt->isMap()) {
627 foreach (Descriptor d, odt->getAllDescriptors()) {
628 if (idt == odt->getDatatypeByDescriptor(d))
629 return true;
630 }
631 }
632 if (idt->isMap() && odt->isMap()) {
633 if (idt->getDatatypesMap().isEmpty()) {
634 ActorPrototype *proto = ip->owner()->getProto();
635 return proto->isAllowsEmptyPorts();
636 }
637 foreach (Descriptor d1, idt->getAllDescriptors()) {
638 foreach (Descriptor d2, odt->getAllDescriptors()) {
639 if (idt->getDatatypeByDescriptor(d1) == odt->getDatatypeByDescriptor(d2))
640 return true;
641 }
642 }
643 }
644 return odt == idt;
645 }
646
checkBindCandidate(const QGraphicsItem * it) const647 WorkflowPortItem *WorkflowPortItem::checkBindCandidate(const QGraphicsItem *it) const {
648 switch (it->type()) {
649 case WorkflowProcessItemType: {
650 const WorkflowProcessItem *receiver = static_cast<const WorkflowProcessItem *>(it);
651 // try best matches first
652 foreach (WorkflowPortItem *otherPit, receiver->getPortItems()) {
653 if (port->canBind(otherPit->getPort()) && checkTypes(port, otherPit->getPort())) {
654 return otherPit;
655 }
656 }
657 // take first free port
658 foreach (WorkflowPortItem *otherPit, receiver->getPortItems()) {
659 if (port->canBind(otherPit->getPort())) {
660 return otherPit;
661 }
662 }
663 } break;
664 case WorkflowPortItemType: {
665 WorkflowPortItem *otherPit = (WorkflowPortItem *)(it);
666 if (port->canBind(otherPit->getPort())) {
667 return otherPit;
668 }
669 } break;
670 }
671 return nullptr;
672 }
673
removeDataFlow(WorkflowBusItem * flow)674 void WorkflowPortItem::removeDataFlow(WorkflowBusItem *flow) {
675 assert(flows.contains(flow));
676 flows.removeOne(flow);
677 assert(!flows.contains(flow));
678 }
679
addDataFlow(WorkflowBusItem * flow)680 void WorkflowPortItem::addDataFlow(WorkflowBusItem *flow) {
681 flows << flow;
682 }
683
head(const QGraphicsItem * item) const684 QPointF WorkflowPortItem::head(const QGraphicsItem *item) const {
685 return mapToItem(item, A / 2 + bl, 0);
686 }
687
headToScene() const688 QPointF WorkflowPortItem::headToScene() const {
689 return mapToScene(A, 0);
690 }
691
arrow(const QGraphicsItem * item) const692 QLineF WorkflowPortItem::arrow(const QGraphicsItem *item) const {
693 return QLineF(mapToItem(item, 0, 0), mapToItem(item, A, 0));
694 }
695
boundingRect(void) const696 QRectF WorkflowPortItem::boundingRect(void) const {
697 QRectF rect(0, -A, A + A / 2, 2 * A);
698 if (dragging) {
699 rect |= QRectF(QPointF(A, 0), dragPoint);
700 // FIXME arrow tip inclusion
701 }
702 return rect;
703 }
704
drawArrow(QPainter * painter,const QPen & pen,const QPointF & p1,const QPointF & p2)705 static void drawArrow(QPainter *painter, const QPen &pen, const QPointF &p1, const QPointF &p2) {
706 painter->setPen(pen);
707 QLineF l(p1, p2);
708 painter->drawLine(l);
709 // draw arrow tip
710 painter->save();
711 painter->translate(p2);
712 painter->rotate(-l.angle());
713 QRectF rf(-3 * A, -A / 2, A * 1.5, A);
714 QPainterPath tip(QPointF(0, 0));
715 tip.arcTo(rf, -50, 100);
716 tip.closeSubpath();
717 painter->fillPath(tip, QBrush(pen.color()));
718 painter->restore();
719 }
720
getCandidates(WorkflowPortItem * port)721 static QList<WorkflowPortItem *> getCandidates(WorkflowPortItem *port) {
722 QList<WorkflowPortItem *> l;
723 foreach (QGraphicsItem *it, port->scene()->items()) {
724 if (it->type() == WorkflowPortItemType) {
725 WorkflowPortItem *next = qgraphicsitem_cast<WorkflowPortItem *>(it);
726 if (port->getPort()->canBind(next->getPort()) && checkTypes(next->getPort(), port->getPort()) && !WorkflowUtils::isPathExist(port->getPort(), next->getPort())) {
727 l.append(next);
728 }
729 }
730 }
731 return l;
732 }
733
paint(QPainter * painter,const QStyleOptionGraphicsItem * option,QWidget * widget)734 void WorkflowPortItem::paint(QPainter *painter,
735 const QStyleOptionGraphicsItem *option,
736 QWidget *widget) {
737 Q_UNUSED(widget);
738 QPointF p1(A / 2 + bl, 0);
739 QColor greenLight(0, 0x99, 0x33, 128);
740 QColor stickyLight(0, 0x77, 0x33);
741
742 if (highlight) {
743 QPen pen;
744 pen.setColor(greenLight);
745 painter->setPen(pen);
746 }
747
748 // painter->fillRect(boundingRect(), QBrush(Qt::magenta, Qt::Dense4Pattern));
749 painter->setRenderHint(QPainter::Antialiasing);
750 painter->drawLine(0, 0, bl, 0);
751
752 if (port->isInput()) {
753 if (highlight) {
754 QPainterPath path;
755 path.addEllipse(bl, -A / 2, A, A);
756 painter->fillPath(path, QBrush(greenLight));
757 } else {
758 painter->drawArc(QRectF(bl, -A / 2, A, A), 90 * 16, 180 * 16);
759 }
760 } else {
761 if (highlight) {
762 QPainterPath path;
763 path.addEllipse(p1, A / 2, A / 2);
764 painter->fillPath(path, QBrush(greenLight));
765 } else {
766 painter->drawEllipse(p1, A / 2, A / 2);
767 }
768 }
769 if (0 && port->getWidth() == 0) {
770 // draw a hint
771 painter->save();
772 QPen pen;
773 // pen.setWidthF(2);
774 pen.setStyle(Qt::DashLine);
775 painter->setPen(pen);
776 QPointF hc(R, 0);
777 drawArrow(painter, pen, hc, p1);
778
779 painter->translate(R, 0);
780 painter->rotate(orientation);
781 QRectF approx(-10, -10, 200, 100);
782 QRectF htb = painter->boundingRect(approx, Qt::AlignCenter, port->getDisplayName());
783 // painter->drawRoundedRect(htb, 30, 30, Qt::RelativeSize);
784
785 painter->drawRoundedRect(htb, 30, 30, Qt::RelativeSize);
786 painter->drawText(approx, Qt::AlignCenter, port->getDisplayName());
787 painter->setPen(Qt::red);
788 painter->drawPoint(0, 0);
789 painter->restore();
790 }
791 if (dragging) {
792 QPen pen;
793 // pen.setWidthF(3);
794 pen.setStyle(Qt::DotLine);
795 if (sticky) {
796 pen.setColor(stickyLight);
797 }
798 // put drag point inside of the scene rect
799 QPointF pp = dragPoint;
800 QRectF scRect = scene()->sceneRect();
801 QList<QLineF> sceneEdges;
802 sceneEdges << QLineF(scRect.topLeft(), scRect.topRight())
803 << QLineF(scRect.topRight(), scRect.bottomRight())
804 << QLineF(scRect.bottomLeft(), scRect.bottomRight())
805 << QLineF(scRect.topLeft(), scRect.bottomLeft());
806 QLineF arr(mapToScene(dragPoint), mapToScene(p1));
807 QPointF crossPt;
808 foreach (QLineF scEdge, sceneEdges) {
809 if (scEdge.intersect(arr, &crossPt) == QLineF::BoundedIntersection) {
810 pp = mapFromScene(crossPt);
811 break;
812 }
813 }
814 //////////////////////////////////////////////////////////////////////////
815 if (port->isInput())
816 drawArrow(painter, pen, pp, p1);
817 else
818 drawArrow(painter, pen, p1, pp);
819 } else if (option->state & QStyle::State_Selected) {
820 QPen pen;
821 // pen.setWidthF(2);
822 pen.setStyle(Qt::DotLine);
823 painter->setPen(pen);
824 painter->drawRoundedRect(boundingRect(), 30, 30, Qt::RelativeSize);
825 }
826 }
827
focusOutEvent(QFocusEvent *)828 void WorkflowPortItem::focusOutEvent(QFocusEvent * /*event*/) {
829 if (dragging) {
830 dragging = false;
831 scene()->update();
832 }
833 }
834
835 namespace {
836
837 class ScopedFlagFlipper {
838 public:
ScopedFlagFlipper(bool & flag)839 ScopedFlagFlipper(bool &flag)
840 : flag(flag) {
841 flag = true;
842 }
843
~ScopedFlagFlipper()844 ~ScopedFlagFlipper() {
845 flag = false;
846 }
847
848 private:
849 bool &flag;
850 };
851
852 } // namespace
853
mouseMoveEvent(QGraphicsSceneMouseEvent * event)854 void WorkflowPortItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event) {
855 CHECK(!mouseMoveIsBeingProcessed, );
856
857 ScopedFlagFlipper guard(mouseMoveIsBeingProcessed);
858 Q_UNUSED(guard);
859 if (!dragging && !rotating && (event->buttons() & Qt::LeftButton) && !dragPoint.isNull()) {
860 // log.debug("port grabbed mouse");
861 if ((event->pos().toPoint() - dragPoint.toPoint()).manhattanLength() < 10)
862 return;
863 event->accept();
864 // grabMouse();
865 if (event->modifiers() & portRotationModifier) {
866 rotating = true;
867 // setCursor(portRotationCursor);
868 setCursor(QCursor(QPixmap(":workflow_designer/images/rot_cur.png")));
869 } else {
870 dragging = true;
871 setCursor(Qt::ClosedHandCursor);
872 bindCandidates = getCandidates(this);
873 foreach (WorkflowPortItem *it, bindCandidates) {
874 it->setHighlight(true);
875 it->update(it->boundingRect());
876 }
877 }
878 }
879
880 sticky = false;
881
882 if (!dragging && !rotating) {
883 return;
884 }
885 event->accept();
886 prepareGeometryChange();
887 if (rotating) {
888 qreal angle = QLineF(owner->pos(), event->scenePos()).angle();
889 setOrientation(angle);
890 }
891 if (dragging) {
892 foreach (QGraphicsView *v, scene()->views()) {
893 QRectF r(0, 0, 5, 5);
894 r.moveCenter(mapToScene(dragPoint));
895 v->ensureVisible(r, 0, 0);
896 }
897 dragPoint += event->pos() - event->lastPos();
898
899 WorkflowPortItem *preferable = findNearbyBindingCandidate(event->scenePos());
900 if (preferable) {
901 dragPoint = preferable->head(this);
902 sticky = true;
903 }
904 }
905 }
906
mousePressEvent(QGraphicsSceneMouseEvent * event)907 void WorkflowPortItem::mousePressEvent(QGraphicsSceneMouseEvent *event) {
908 dragPoint = QPointF();
909 if ((event->buttons() & Qt::LeftButton) && !getWorkflowScene()->isLocked()) {
910 dragPoint = event->pos();
911 if ((event->modifiers() & portRotationModifier) && !dragging) {
912 rotating = true;
913 setCursor(QCursor(QPixmap(":workflow_designer/images/rot_cur.png")));
914 } else {
915 setCursor(Qt::ClosedHandCursor);
916 }
917 } else {
918 QGraphicsItem::mousePressEvent(event);
919 }
920 }
921
mouseReleaseEvent(QGraphicsSceneMouseEvent * event)922 void WorkflowPortItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) {
923 ungrabMouse();
924 unsetCursor();
925 QGraphicsItem::mouseReleaseEvent(event);
926 rotating = false;
927 if (dragging && (event->button() == Qt::LeftButton)) {
928 event->accept();
929 QList<QGraphicsItem *> li = scene()->items(/*event->scenePos()*/ mapToScene(dragPoint));
930 // bool done = false;
931 WorkflowPortItem *otherPit = nullptr;
932 foreach (QGraphicsItem *it, li) {
933 WorkflowView *ctl = getWorkflowScene()->getController();
934 if (ctl) {
935 otherPit = checkBindCandidate(it);
936 WorkflowBusItem *dit;
937 if (otherPit && (dit = ctl->tryBind(this, otherPit))) {
938 scene()->clearSelection();
939 IntegralBusPort *bp = qobject_cast<IntegralBusPort *>(dit->getInPort()->getPort());
940 if (bp) {
941 bp->setupBusMap();
942 }
943 dit->getInPort()->setSelected(true);
944 break;
945 }
946 }
947 }
948 prepareGeometryChange();
949 dragging = false;
950 dragPoint = QPointF();
951 foreach (WorkflowPortItem *it, bindCandidates) {
952 it->setHighlight(false);
953 }
954 scene()->update();
955 bindCandidates.clear();
956 }
957 }
958
hoverEnterEvent(QGraphicsSceneHoverEvent * event)959 void WorkflowPortItem::hoverEnterEvent(QGraphicsSceneHoverEvent *event) {
960 if (getWorkflowScene()->isLocked()) {
961 return;
962 }
963 setCursor((event->modifiers() & portRotationModifier) ? QCursor(QPixmap(":workflow_designer/images/rot_cur.png")) : QCursor(Qt::OpenHandCursor));
964 }
965
hoverLeaveEvent(QGraphicsSceneHoverEvent *)966 void WorkflowPortItem::hoverLeaveEvent(QGraphicsSceneHoverEvent *) {
967 unsetCursor();
968 }
969
itemChange(GraphicsItemChange change,const QVariant & value)970 QVariant WorkflowPortItem::itemChange(GraphicsItemChange change, const QVariant &value) {
971 if (change == ItemPositionChange || change == ItemTransformChange) {
972 foreach (WorkflowBusItem *dit, flows) {
973 dit->prepareGeometryChange();
974 }
975 } else if (change == ItemPositionHasChanged || change == ItemTransformHasChanged) {
976 foreach (WorkflowBusItem *dit, flows) {
977 // TODO correct update
978 // dit->update(dit->boundingRect());
979 dit->updatePos();
980 }
981 } else if (change == ItemSceneChange && (value.value<QGraphicsScene *>()) == nullptr) {
982 foreach (WorkflowBusItem *dit, flows) {
983 scene()->removeItem(dit);
984 delete dit;
985 }
986 }
987
988 return QGraphicsItem::itemChange(change, value);
989 }
990
sl_onVisibleChanged(bool isVisible)991 void WorkflowPortItem::sl_onVisibleChanged(bool isVisible) {
992 setVisible(isVisible);
993 if (false == isVisible) {
994 foreach (WorkflowBusItem *flow, flows) {
995 WorkflowScene *ws = getWorkflowScene();
996 if (nullptr != ws) {
997 WorkflowView *view = ws->getController();
998 view->removeBusItem(flow);
999 }
1000 }
1001 }
1002 }
1003
1004 ////////////////// Flow //////////////
1005
WorkflowBusItem(WorkflowPortItem * p1,WorkflowPortItem * p2,Link * link)1006 WorkflowBusItem::WorkflowBusItem(WorkflowPortItem *p1, WorkflowPortItem *p2, Link *link)
1007 : bus(link) {
1008 if (p1->getPort()->isInput()) {
1009 assert(!p2->getPort()->isInput());
1010 dst = p1;
1011 src = p2;
1012 } else {
1013 assert(p2->getPort()->isInput());
1014 dst = p2;
1015 src = p1;
1016 }
1017
1018 setAcceptHoverEvents(true);
1019 setFlag(QGraphicsItem::ItemIsSelectable, true);
1020 setZValue(-1000);
1021
1022 this->text = new HintItem(src->getPort()->getDisplayName(), this);
1023 connect(dst->getPort(), SIGNAL(bindingChanged()), this, SLOT(sl_update()));
1024 }
1025
~WorkflowBusItem()1026 WorkflowBusItem::~WorkflowBusItem() {
1027 assert(bus == nullptr);
1028 }
1029
updatePos()1030 void WorkflowBusItem::updatePos() {
1031 QPointF p1 = dst->headToScene();
1032 QPointF p2 = src->headToScene();
1033
1034 setPos((p1.x() + p2.x()) / 2, (p1.y() + p2.y()) / 2);
1035 }
1036
itemChange(GraphicsItemChange change,const QVariant & value)1037 QVariant WorkflowBusItem::itemChange(GraphicsItemChange change, const QVariant &value) {
1038 if (change == ItemSceneChange && (value.value<QGraphicsScene *>()) == nullptr) {
1039 dst->removeDataFlow(this);
1040 src->removeDataFlow(this);
1041 disconnect(dst->getPort(), SIGNAL(bindingChanged()), this, SLOT(sl_update()));
1042
1043 WorkflowView *ctl = getWorkflowScene()->getController();
1044 if (nullptr != ctl) {
1045 ctl->onBusRemoved(bus);
1046 } else {
1047 delete bus;
1048 }
1049 bus = nullptr;
1050 }
1051
1052 return QGraphicsItem::itemChange(change, value);
1053 }
1054
hoverEnterEvent(QGraphicsSceneHoverEvent * event)1055 void WorkflowBusItem::hoverEnterEvent(QGraphicsSceneHoverEvent *event) {
1056 Q_UNUSED(event);
1057 setCursor(QCursor(Qt::PointingHandCursor));
1058 }
hoverLeaveEvent(QGraphicsSceneHoverEvent * event)1059 void WorkflowBusItem::hoverLeaveEvent(QGraphicsSceneHoverEvent *event) {
1060 Q_UNUSED(event);
1061 unsetCursor();
1062 }
1063
boundingRect(void) const1064 QRectF WorkflowBusItem::boundingRect(void) const {
1065 QRectF brect(mapFromItem(dst, dst->boundingRect()).boundingRect() | mapFromItem(src, src->boundingRect()).boundingRect());
1066 QRectF trect(text->boundingRect().translated(text->pos()));
1067 if (/*getWorkflowScene()->getRunner()*/ true) {
1068 trect.setTop(trect.top() - trect.height());
1069 }
1070 return brect | trect;
1071 }
1072
shape() const1073 QPainterPath WorkflowBusItem::shape() const {
1074 QPainterPath p;
1075 QPointF p1 = dst->head(this);
1076 QPointF p2 = src->head(this);
1077 QLineF direct(p2, p1);
1078 QLineF n = direct.normalVector();
1079 n.setLength(A / 2);
1080 p.moveTo(n.p2());
1081 p.lineTo(n.translated(p1 - p2).p2());
1082 QLineF rn(n.p2(), n.p1());
1083 rn.translate(n.p1() - n.p2());
1084 p.lineTo(rn.translated(p1 - p2).p2());
1085 p.lineTo(rn.p2());
1086 p.closeSubpath();
1087 p.addRect(text->boundingRect().translated(text->pos()));
1088 return p;
1089 }
1090
paint(QPainter * painter,const QStyleOptionGraphicsItem * option,QWidget * widget)1091 void WorkflowBusItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) {
1092 Q_UNUSED(widget);
1093 painter->setRenderHint(QPainter::Antialiasing);
1094 QColor baseColor(0x66, 0x66, 0x66);
1095 painter->setPen(baseColor);
1096 // painter->fillRect(boundingRect(), QBrush(Qt::blue));
1097 QPointF p1 = dst->head(this);
1098 QPointF p2 = src->head(this);
1099
1100 QPainterPath path;
1101 path.addEllipse(p2, A / 2 - 2, A / 2 - 2);
1102 path.addEllipse(p1, A / 2 - 2, A / 2 - 2);
1103 painter->fillPath(path, QBrush(baseColor));
1104
1105 QPen pen = painter->pen();
1106 if (option->state & QStyle::State_Selected) {
1107 pen.setWidthF(1.5);
1108 pen.setStyle(Qt::DashLine);
1109 }
1110 if (!validate()) {
1111 pen.setColor(Qt::red);
1112 }
1113
1114 drawArrow(painter, pen, p2, p1);
1115 // update();
1116
1117 painter->setRenderHint(QPainter::NonCosmeticDefaultPen);
1118 QColor yc = QColor(Qt::yellow).lighter();
1119 yc.setAlpha(127);
1120 QRectF textRec = text->boundingRect().translated(text->pos());
1121 painter->fillRect(textRec, QBrush(yc));
1122 painter->drawRect(textRec);
1123
1124 WorkflowAbstractRunner *rt = getWorkflowScene()->getRunner();
1125 if (rt) {
1126 int msgsInQueue = rt->getMsgNum(this->bus);
1127 int passed = rt->getMsgPassed(this->bus);
1128
1129 QString rts = QString("%1 in queue, %2 passed").arg(msgsInQueue).arg(passed);
1130 QRectF rtb = textRec.translated(0, -QFontMetricsF(QFont()).height());
1131 qreal shift = (QFontMetricsF(QFont()).width(rts) - rtb.width()) / 2;
1132 rtb.setLeft(rtb.left() - shift);
1133 rtb.setRight(rtb.right() + shift);
1134 painter->drawText(rtb, Qt::AlignHCenter, rts);
1135 if (msgsInQueue == 0) {
1136 return;
1137 }
1138 qreal dx = (p2.x() - p1.x()) / msgsInQueue;
1139 qreal dy = (p2.y() - p1.y()) / msgsInQueue;
1140 QPointF dp(dx, dy);
1141 QColor c1("#AA0404");
1142 painter->setPen(c1);
1143 c1.setAlphaF(0.8);
1144 QColor c2(Qt::white);
1145 c2.setAlphaF(0.8);
1146 while (msgsInQueue-- > 0) {
1147 QPainterPath p;
1148 p.addEllipse(p1, 3, 3);
1149 QRadialGradient rg(p1 + QPointF(1., -1.), 3);
1150
1151 rg.setColorAt(1, c1);
1152 rg.setColorAt(0, c2);
1153 QBrush br(rg);
1154
1155 painter->fillPath(p, br);
1156 // painter->drawEllipse(p1, 3,3);
1157 p1 += dp;
1158 }
1159 }
1160 }
1161
sl_update()1162 void WorkflowBusItem::sl_update() {
1163 update();
1164 }
1165
validate()1166 bool WorkflowBusItem::validate() {
1167 NotificationsList lst;
1168 return dst->getPort()->validate(lst);
1169 }
1170
saveState(QDomElement & el) const1171 void WorkflowBusItem::saveState(QDomElement &el) const {
1172 el.setAttribute("hint-pos", QVariantUtils::var2String(text->pos()));
1173 }
loadState(QDomElement & el)1174 void WorkflowBusItem::loadState(QDomElement &el) {
1175 if (el.hasAttribute("hint-pos")) {
1176 QPointF pos = QVariantUtils::String2Var(el.attribute("hint-pos")).toPointF();
1177 if (!pos.isNull()) {
1178 text->setPos(pos);
1179 }
1180 }
1181 }
1182
getWorkflowScene() const1183 WorkflowScene *StyledItem::getWorkflowScene() const {
1184 return qobject_cast<WorkflowScene *>(scene());
1185 }
1186
1187 ///////////////// WorkflowHighlightItem /////////////////////////////////////////////////////////
1188
1189 const quint8 INIT_ANIMATION_STEPS_NUMBER = 50;
1190 const qreal HALVED_MAXIMUM_SIZE_RELATION_TO_PROCESS_ITEM_SIZE = 0.15;
1191 const QColor BORDER_COLOR = QColor(205, 133, 63);
1192 const QPointF INIT_POSITION = QPointF(0.0, 0.0);
1193
WorkflowHighlightItem(WorkflowProcessItem * owner)1194 WorkflowHighlightItem::WorkflowHighlightItem(WorkflowProcessItem *owner)
1195 : StyledItem(owner), countOfAnimationStepsLeft(0) {
1196 setPos(INIT_POSITION);
1197 setZValue(owner->zValue());
1198 setVisible(false);
1199 }
1200
boundingRect() const1201 QRectF WorkflowHighlightItem::boundingRect() const {
1202 WorkflowProcessItem *owner = dynamic_cast<WorkflowProcessItem *>(parentItem());
1203 const QRectF parentBoundary = owner->getStyleById(owner->getStyle())->boundingRect();
1204 const qreal sizeFactor = HALVED_MAXIMUM_SIZE_RELATION_TO_PROCESS_ITEM_SIZE * countOfAnimationStepsLeft / INIT_ANIMATION_STEPS_NUMBER;
1205
1206 return parentBoundary.adjusted(-parentBoundary.width() * sizeFactor, -parentBoundary.height() * sizeFactor, parentBoundary.width() * sizeFactor, parentBoundary.height() * sizeFactor);
1207 }
1208
paint(QPainter * painter,const QStyleOptionGraphicsItem *,QWidget *)1209 void WorkflowHighlightItem::paint(QPainter *painter, const QStyleOptionGraphicsItem * /*option*/, QWidget * /*widget*/) {
1210 if (0 != countOfAnimationStepsLeft) {
1211 painter->setPen(BORDER_COLOR);
1212 painter->drawRoundedRect(boundingRect(), 5, 5);
1213 prepareGeometryChange();
1214 --countOfAnimationStepsLeft;
1215 if (0 == countOfAnimationStepsLeft) {
1216 setVisible(false);
1217 }
1218 }
1219 }
1220
replay()1221 void WorkflowHighlightItem::replay() {
1222 countOfAnimationStepsLeft = INIT_ANIMATION_STEPS_NUMBER;
1223 setVisible(true);
1224 update();
1225 }
1226
1227 } // namespace U2
1228