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 "QueryViewItems.h"
23 
24 #include <QApplication>
25 #include <QGraphicsScene>
26 #include <QGraphicsSceneMouseEvent>
27 #include <QGraphicsView>
28 #include <QInputDialog>
29 #include <QMenu>
30 #include <QPainter>
31 #include <QStyleOptionGraphicsItem>
32 #include <QTextDocument>
33 
34 #include <U2Core/Log.h>
35 #include <U2Core/QVariantUtils.h>
36 #include <U2Core/global.h>
37 
38 #include "QueryPalette.h"
39 #include "QueryViewController.h"
40 
41 #define ANNOTATION_MIN_SIZE GRID_STEP
42 #define ANNOTATION_MAX_SIZE 4 * GRID_STEP
43 #define MARGIN 4
44 #define ARR_W 15
45 
46 namespace U2 {
47 
48 /************************************************************************/
49 /* Annotation Item                                                      */
50 /************************************************************************/
51 
round(qreal val,int step)52 inline qreal round(qreal val, int step) {
53     if (0 > val) {
54         step *= -1;
55     }
56     int tmp = int(val) + step / 2;
57     tmp -= tmp % step;
58     return qreal(tmp);
59 }
60 
QDElement(QDSchemeUnit * _unit)61 QDElement::QDElement(QDSchemeUnit *_unit)
62     : highlighted(false), unit(_unit), font(QFont()), bound(0, 0, 3 * ANNOTATION_MIN_SIZE, ANNOTATION_MIN_SIZE),
63       dragging(false), extendedHeight(ANNOTATION_MIN_SIZE), itemResizeFlags(0) {
64     setFlag(QGraphicsItem::ItemIsSelectable, true);
65 #if (QT_VERSION >= QT_VERSION_CHECK(4, 6, 0))
66     setFlag(QGraphicsItem::ItemSendsGeometryChanges, true);
67 #endif
68     setAcceptHoverEvents(true);
69     setZValue(1);
70 
71     doc = new QTextDocument(this);
72 
73     QDParameters *params = unit->getActor()->getParameters();
74     connect(params, SIGNAL(si_modified()), SLOT(sl_refresh()));
75     connect(unit->getActor(), SIGNAL(si_strandChanged(QDStrandOption)), SLOT(sl_refresh()));
76 
77     itemDescription = new QDElementDescription(this);
78     itemDescription->setDocument(doc);
79 
80     const QString &s = getHeaderString();
81     const QFont &f = itemDescription->font();
82     QFontMetrics fm(f);
83     if (getActor()->hasAnyDirection()) {
84         bound.setWidth(fm.width(s) + 2 * ARR_W);
85     } else {
86         bound.setWidth(fm.width(s) + ARR_W);
87     }
88 }
89 
updateDescription()90 void QDElement::updateDescription() {
91     if (getActor()->hasAnyDirection()) {
92         itemDescription->setTextWidth(bound.width() - 2 * ARR_W);
93     } else {
94         itemDescription->setTextWidth(bound.width() - ARR_W);
95     }
96     itemDescription->setHeight(bound.height() - MARGIN);
97     itemDescription->document()->setPageSize(itemDescription->boundingRect().size());
98 }
99 
rememberSize()100 void QDElement::rememberSize() {
101     QueryScene *qs = qobject_cast<QueryScene *>(scene());
102     assert(qs);
103     if (!qs->showActorDesc()) {
104         extendedHeight = bound.height();
105     }
106 }
107 
adaptSize()108 void QDElement::adaptSize() {
109     QueryScene *qs = qobject_cast<QueryScene *>(scene());
110     assert(qs);
111     prepareGeometryChange();
112     if (qs->showActorDesc()) {
113         qreal raY = scenePos().y() + bound.height();
114         QRectF requiredArea(QPointF(scenePos().x(), raY), QSizeF(bound.width(), extendedHeight - bound.height()));
115         int requiredRowsNum = requiredArea.height() / GRID_STEP;
116         for (int i = 0; i < requiredRowsNum; i++) {
117             QRectF reqRowFragment(requiredArea.left(), requiredArea.top() + i * GRID_STEP, requiredArea.width(), GRID_STEP);
118             QPainterPath rowPath;
119             rowPath.addRect(reqRowFragment);
120             QList<QGraphicsItem *> containedItems = qs->items(rowPath, Qt::IntersectsItemShape);
121             QList<QGraphicsItem *> containedUnits;
122             foreach (QGraphicsItem *it, containedItems) {
123                 if (it->type() == QDElementType) {
124                     containedUnits.append(it);
125                 }
126             }
127             if (containedUnits.size() > 0) {
128                 int insertAt = qs->getRow(this) + i + 1;
129                 qs->insertRow(insertAt);
130             }
131         }
132         bound.setHeight(extendedHeight);
133     } else {
134         bound.setHeight(ANNOTATION_MIN_SIZE);
135     }
136 
137     doc->setPageSize(bound.size() - QSizeF(MARGIN * 2, MARGIN * 2));
138     updateDescription();
139 
140     if (bound.bottom() + scenePos().y() < qs->annotationsArea().bottom()) {
141         qs->sl_adaptRowsNumber();
142     } else if (bound.bottom() + scenePos().y() > qs->annotationsArea().bottom()) {
143         int prevRowsNum = qs->getRowsNumber();
144         qreal bottomEdge = scenePos().y() + boundingRect().height();
145         int reqRowNum = (bottomEdge - qs->annotationsArea().top()) / GRID_STEP;
146         int rowNum = qMax(prevRowsNum, reqRowNum);
147         qs->setRowsNumber(rowNum);
148     }
149 }
150 
loadState(QDElementStatement * el)151 void QDElement::loadState(QDElementStatement *el) {
152     assert(el->getType() == Element);
153     const QString &geometryStr = el->getAttribute(QDElementStatement::GEOMETRY_ATTR_NAME);
154     QStringList attrs = geometryStr.split(',');
155     const QString &xPosStr = attrs.at(0);
156     const QString &yPosStr = attrs.at(1);
157     QPointF pos(xPosStr.toInt(), yPosStr.toInt());
158     assert(!pos.isNull());
159     const QString &wS = attrs.at(2);
160     const QString &hS = attrs.at(3);
161     QSizeF size(wS.toInt(), hS.toInt());
162     bound.setSize(size);
163     setPos(pos);
164     doc->setPageSize(bound.size() - QSizeF(MARGIN * 2, MARGIN * 2));
165     const QString &eHS = attrs.at(4);
166     extendedHeight = eHS.toInt();
167 }
168 
saveState(QDElementStatement * el) const169 void QDElement::saveState(QDElementStatement *el) const {
170     assert(el->getType() == Element);
171     QGraphicsScene *s = scene();
172     QueryScene *qs = qobject_cast<QueryScene *>(s);
173     assert(qs);
174     qreal extHeight = extendedHeight;
175     if (qs->showActorDesc()) {
176         extHeight = bound.height();
177     }
178     QString geomStr = QString("%1,%2,%3,%4,%5")
179                           .arg(scenePos().x())
180                           .arg(scenePos().y())
181                           .arg(boundingRect().width())
182                           .arg(boundingRect().height())
183                           .arg(extHeight);
184     el->setAttribute(QDElementStatement::GEOMETRY_ATTR_NAME, geomStr);
185 }
186 
sl_refresh()187 void QDElement::sl_refresh() {
188     QString baseHtml = "<center>" + getHeaderString();
189     QueryScene *qs = qobject_cast<QueryScene *>(scene());
190     if (qs && !qs->showActorDesc()) {
191         doc->setHtml(baseHtml);
192     } else {
193         QString infoStr = unit->getActor()->getText();
194         doc->setHtml(QString("%1<hr>%2")
195                          .arg(baseHtml)
196                          .arg(infoStr));
197     }
198     update();
199 
200     if (getActor()->hasAnyDirection() || getActor()->hasBackwardDirection()) {
201         itemDescription->setPos(ARR_W, 0);
202     } else {
203         itemDescription->setPos(0, 0);
204     }
205     updateDescription();
206 }
207 
getHeaderString() const208 QString QDElement::getHeaderString() const {
209     QString res;
210     QString annotateAsStr = unit->getActor()->annotateAs();
211     annotateAsStr.replace('<', "&lt;");
212     annotateAsStr.replace('>', "&gt;");
213     QueryScene *qs = qobject_cast<QueryScene *>(scene());
214     QDActorParameters *params = unit->getActor()->getParameters();
215     if (getActor()->getSchemeUnits().size() == 1) {
216         res = QString("<b>%1 (\"%3\")</b>")
217                   .arg(params->getLabel())
218                   .arg(annotateAsStr);
219     } else {
220         res = QString("<b>%1.%2 (\"%3\")</b>")
221                   .arg(params->getLabel())
222                   .arg(unit->getId())
223                   .arg(annotateAsStr);
224     }
225     if (qs && qs->showActorOrder()) {
226         int serialNumber = qs->getScheme()->getActors().indexOf(getActor()) + 1;
227         res += QString("<b> Order: %1</b>").arg(serialNumber);
228     }
229     return res;
230 }
231 
isLinkedWith(QDElement *,QDDistanceType)232 bool QDElement::isLinkedWith(QDElement * /*other*/, QDDistanceType /*kind*/) {
233     /*foreach(Footnote* linkIt, links) {
234         if(other->links.contains(linkIt) && linkIt->kind()==kind) {
235             return true;
236         }
237     }*/
238     return false;
239 }
240 
getOutcomeFootnotes() const241 QList<Footnote *> QDElement::getOutcomeFootnotes() const {
242     QList<Footnote *> res;
243     foreach (Footnote *fn, links) {
244         if (fn->getSrc() == this) {
245             res.append(fn);
246         }
247     }
248     return res;
249 }
250 
getIncomeFootnotes() const251 QList<Footnote *> QDElement::getIncomeFootnotes() const {
252     QList<Footnote *> res;
253     foreach (Footnote *fn, links) {
254         if (fn->getDst() == this) {
255             res.append(fn);
256         }
257     }
258     return res;
259 }
260 
261 #define CORNER_DIAM 6
paint(QPainter * painter,const QStyleOptionGraphicsItem * option,QWidget * widget)262 void QDElement::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) {
263     Q_UNUSED(option);
264     Q_UNUSED(widget);
265     QPen pen;
266     if (isSelected()) {
267         pen.setStyle(Qt::DashLine);
268         painter->setPen(pen);
269     }
270     if (highlighted) {
271         pen.setWidth(2);
272         painter->setPen(pen);
273     }
274 
275     painter->setRenderHint(QPainter::Antialiasing);
276     painter->setBrush(getActor()->defaultColor());
277 
278     qreal w = boundingRect().width();
279     qreal h = boundingRect().height();
280     QPainterPath path;
281 
282     if (!getActor()->hasAnyDirection()) {
283         path.moveTo(QPointF(CORNER_DIAM / 2.0, 0));
284         path.lineTo(w - ARR_W, 0);
285         path.lineTo(w, h / 2);
286         path.lineTo(w - ARR_W, h);
287         path.lineTo(CORNER_DIAM / 2.0, h);
288         path.arcTo(0, h - CORNER_DIAM, CORNER_DIAM, CORNER_DIAM, -90, -90);
289         path.lineTo(0, CORNER_DIAM / 2);
290         path.arcTo(0, 0, CORNER_DIAM, CORNER_DIAM, 180, -90);
291         if (getActor()->hasBackwardDirection()) {
292             painter->rotate(180);
293             painter->translate(-w, -h);
294         }
295     } else {
296         path.moveTo(0, h / 2);
297         path.lineTo(ARR_W, 0);
298         path.lineTo(w - ARR_W, 0);
299         path.lineTo(w, h / 2);
300         path.lineTo(w - ARR_W, h);
301         path.lineTo(ARR_W, h);
302         path.lineTo(0, h / 2);
303         path.lineTo(ARR_W, 0);
304     }
305 
306     painter->fillPath(path, painter->brush());
307     painter->drawPath(path);
308 }
309 
getRightConnector()310 QPointF QDElement::getRightConnector() {
311     return mapToScene(QPointF(boundingRect().right(), (boundingRect().top() + boundingRect().bottom()) / 2));
312 }
313 
getLeftConnector()314 QPointF QDElement::getLeftConnector() {
315     return mapToScene(QPointF(boundingRect().left(), (boundingRect().top() + boundingRect().bottom()) / 2));
316 }
317 
mouseMoveEvent(QGraphicsSceneMouseEvent * event)318 void QDElement::mouseMoveEvent(QGraphicsSceneMouseEvent *event) {
319     if (event->buttons() & Qt::LeftButton) {
320         foreach (Footnote *link, links) {
321             link->draging = true;
322         }
323 
324         if (!dragging) {
325             dragPoint = event->pos();
326             dragging = true;
327         }
328 
329         QPointF newPos = scenePos();
330         const QPointF &mousePos = event->pos();
331         const QPointF &p = mousePos - dragPoint;
332         newPos.rx() += p.x();
333         if (qAbs(p.y()) >= GRID_STEP / 2) {
334             newPos.ry() += p.y();
335         }
336         setPos(newPos);
337     } else {
338         QGraphicsItem::mouseMoveEvent(event);
339     }
340 }
341 
mouseReleaseEvent(QGraphicsSceneMouseEvent * event)342 void QDElement::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) {
343     foreach (Footnote *link, links) {
344         link->draging = false;
345         link->update();
346     }
347     dragging = false;
348     QGraphicsItem::mouseReleaseEvent(event);
349 }
350 
contextMenuEvent(QGraphicsSceneContextMenuEvent * event)351 void QDElement::contextMenuEvent(QGraphicsSceneContextMenuEvent *event) {
352     QueryScene *qs = qobject_cast<QueryScene *>(scene());
353     assert(qs);
354     QDScheme *scheme = qs->getScheme();
355     const QList<QDActor *> &actors = scheme->getActors();
356     int serialNum = actors.indexOf(getActor());
357 
358     QMenu menu;
359     QMenu *orderMenu = new QMenu(tr("Set order"), &menu);
360     for (int i = 0; i < actors.size(); i++) {
361         QDActor *actor = actors.at(i);
362         QAction *a = orderMenu->addAction(QString("%1 %2")
363                                               .arg(i + 1)
364                                               .arg(actor->getParameters()->getLabel()));
365         a->setCheckable(true);
366         a->setChecked(false);
367         if (i == serialNum) {
368             a->setChecked(true);
369         }
370         a->setData(qVariantFromValue(i));
371     }
372 
373     QAction *removeFromGroupAction = nullptr;
374     QAction *addToGroupAction = nullptr;
375     if (!scheme->getActorGroups().isEmpty()) {
376         if (!scheme->getActorGroup(getActor()).isEmpty()) {
377             removeFromGroupAction = menu.addAction(tr("Remove from group"));
378         } else {
379             addToGroupAction = menu.addAction(tr("Add to group"));
380         }
381     }
382     menu.addSeparator();
383     menu.addMenu(orderMenu);
384     menu.addSeparator();
385     QAction *upAction = menu.addAction(tr("Up"));
386     QAction *downAction = menu.addAction(tr("Down"));
387     QAction *action = menu.exec(event->screenPos());
388 
389     if (!action) {
390         return;
391     }
392 
393     if (action == addToGroupAction) {
394         bool ok;
395 
396         QString sel = QInputDialog::getItem(nullptr,
397                                             tr("Add '%1' to group").arg(getActor()->getParameters()->getLabel()),
398                                             tr("Group:"),
399                                             scheme->getActorGroups(),
400                                             0,
401                                             false,
402                                             &ok);
403 
404         if (!ok) {
405             return;
406         }
407 
408         scheme->addActorToGroup(getActor(), sel);
409         qs->getViewController()->switchToGroupsTab();
410         return;
411     }
412 
413     if (action == removeFromGroupAction) {
414         scheme->removeActorFromGroup(getActor());
415         qs->getViewController()->switchToGroupsTab();
416         return;
417     }
418 
419     int newSerialNum = serialNum;
420     if (action == upAction) {
421         newSerialNum = serialNum + 1;
422     } else if (action == downAction) {
423         newSerialNum = serialNum - 1;
424     } else {
425         newSerialNum = action->data().toInt();
426     }
427 
428     int from = 0;
429     int to = 0;
430     if (serialNum < newSerialNum) {
431         from = qMax(serialNum, 0);
432         to = qMin(newSerialNum, actors.size() - 1);
433     } else {
434         from = qMax(newSerialNum, 0);
435         to = qMin(serialNum, actors.size() - 1);
436     }
437     scheme->setOrder(getActor(), newSerialNum);
438     for (int i = from; i <= to; i++) {
439         QDActor *a = actors.at(i);
440         const QList<QDSchemeUnit *> &suList = a->getSchemeUnits();
441         foreach (QDSchemeUnit *su, suList) {
442             foreach (QGraphicsItem *it, qs->getElements()) {
443                 QDElement *uv = qgraphicsitem_cast<QDElement *>(it);
444                 assert(uv);
445                 if (uv->getSchemeUnit() == su) {
446                     uv->sl_refresh();
447                 }
448             }
449         }
450     }
451 }
452 
453 // resize processing
454 #define EDGE_WIDTH 4
sceneEvent(QEvent * event)455 bool QDElement::sceneEvent(QEvent *event) {
456     switch (event->type()) {
457         case QEvent::GraphicsSceneHoverEnter:
458         case QEvent::GraphicsSceneHoverMove: {
459             itemResizeFlags = 0;
460             QGraphicsSceneHoverEvent *he = static_cast<QGraphicsSceneHoverEvent *>(event);
461             QPointF p = he->pos();
462             int dxRight = qAbs(boundingRect().right() - p.x());
463             int dxLeft = qAbs(boundingRect().left() - p.x());
464             int dyBottom = qAbs(boundingRect().bottom() - p.y());
465             int dyTop = qAbs(boundingRect().top() - p.y());
466             if (dxRight < EDGE_WIDTH) {
467                 itemResizeFlags |= ResizeRight;
468                 setCursor(Qt::SizeHorCursor);
469             } else if (dxLeft < EDGE_WIDTH) {
470                 itemResizeFlags |= ResizeLeft;
471                 setCursor(Qt::SizeHorCursor);
472             }
473             if (dyBottom < EDGE_WIDTH) {
474                 itemResizeFlags |= ResizeBottom;
475                 setCursor(Qt::SizeVerCursor);
476             } else if (dyTop < EDGE_WIDTH) {
477                 itemResizeFlags |= ResizeTop;
478                 setCursor(Qt::SizeVerCursor);
479             }
480 
481             if ((itemResizeFlags & ResizeBottom && itemResizeFlags & ResizeRight) ||
482                 (itemResizeFlags & ResizeTop && itemResizeFlags & ResizeLeft)) {
483                 setCursor(Qt::SizeFDiagCursor);
484             } else if ((itemResizeFlags & ResizeTop && itemResizeFlags & ResizeRight) ||
485                        (itemResizeFlags & ResizeBottom && itemResizeFlags & ResizeLeft)) {
486                 setCursor(Qt::SizeBDiagCursor);
487             }
488             if (!itemResizeFlags && cursor().shape() != Qt::PointingHandCursor) {
489                 unsetCursor();
490             }
491             break;
492         }
493         case QEvent::GraphicsSceneHoverLeave:
494         case QEvent::GraphicsSceneMouseRelease:
495             itemResizeFlags = 0;
496             break;
497         case QEvent::GraphicsSceneMouseMove: {
498             if (itemResizeFlags) {
499                 QueryScene *qs = qobject_cast<QueryScene *>(scene());
500                 QGraphicsSceneMouseEvent *me = static_cast<QGraphicsSceneMouseEvent *>(event);
501                 if (me->buttons() & Qt::LeftButton) {
502                     QPointF p = me->pos();
503                     p.setY(round(p.y(), GRID_STEP));
504 
505                     QRectF newBound(bound);
506 
507                     if (itemResizeFlags & ResizeRight) {
508                         newBound.setRight(p.x());
509                     } else if (itemResizeFlags & ResizeLeft && me->scenePos().x() > 0) {
510                         newBound.setWidth(newBound.width() - p.x());
511                     }
512                     if (itemResizeFlags & ResizeTop && (0 <= (scenePos().y() - GRID_STEP) || 0 <= p.y())) {
513                         newBound.setHeight(newBound.height() - p.y() + newBound.top());
514                     } else if (itemResizeFlags & ResizeBottom) {
515                         newBound.setBottom(p.y());
516                     }
517 
518                     // keep minimum size
519                     if (newBound.width() < ANNOTATION_MIN_SIZE * 2 || newBound.height() < ANNOTATION_MIN_SIZE) {
520                         break;
521                     }
522                     // remove empty rows if any
523                     if (newBound.bottom() + scenePos().y() < qs->annotationsArea().bottom()) {
524                         qs->sl_adaptRowsNumber();
525                     }
526                     // expand rows area if necessary
527                     if (newBound.bottom() + scenePos().y() > qs->annotationsArea().bottom()) {
528                         int prevRowsNum = qs->getRowsNumber();
529                         qreal bottomEdge = scenePos().y() + boundingRect().height();
530                         int reqRowNum = (bottomEdge - qs->annotationsArea().top()) / GRID_STEP;
531                         int rowNum = qMax(prevRowsNum, reqRowNum);
532                         qs->setRowsNumber(rowNum);
533                     }
534 
535                     // find new position for left|top resize
536                     QPointF newPos = scenePos();
537                     if (itemResizeFlags & ResizeTop) {
538                         qreal deltaY = newBound.height() - bound.height();
539                         newPos.setY(newPos.y() - deltaY);
540                     }
541                     if (itemResizeFlags & ResizeLeft) {
542                         newPos.setX(newPos.x() + p.x());
543                     }
544 
545                     // check for collisions
546                     QRectF rect = newBound;
547                     rect.moveTopLeft(newPos);
548                     QPainterPath path;
549                     path.addRect(rect);
550                     QList<QGraphicsItem *> items = scene()->items(path, Qt::IntersectsItemShape);
551                     items.removeAll(this);
552                     foreach (QGraphicsItem *item, items) {
553                         if (item->type() != QDElementType) {
554                             items.removeAll(item);
555                         }
556                     }
557                     if (!items.isEmpty()) {
558                         break;
559                     }
560                     // check if affected footnotes have positive length
561                     if (itemResizeFlags & ResizeRight) {
562                         foreach (Footnote *fn, links) {
563                             if (fn->getDst() == this) {
564                                 if (fn->getDistType() == S2E || fn->getDistType() == E2E) {
565                                     if (newPos.x() + newBound.width() - fn->getSrcPoint().x() <= 0) {
566                                         return true;
567                                     }
568                                 }
569                             } else {
570                                 if (fn->getDistType() == E2S || fn->getDistType() == E2E) {
571                                     if (fn->getDstPoint().x() - newPos.x() - newBound.width() <= 0) {
572                                         return true;
573                                     }
574                                 }
575                             }
576                         }
577                     }
578                     if (itemResizeFlags & ResizeLeft) {
579                         foreach (Footnote *fn, links) {
580                             if (fn->getDst() == this) {
581                                 if (fn->getDistType() == S2S || fn->getDistType() == E2S) {
582                                     if (newPos.x() - fn->getSrcPoint().x() <= 0) {
583                                         return true;
584                                     }
585                                 }
586                             } else {
587                                 if (fn->getDistType() == S2S || fn->getDistType() == S2E) {
588                                     if (fn->getDstPoint().x() - newPos.x() <= 0) {
589                                         return true;
590                                     }
591                                 }
592                             }
593                         }
594                     }
595 
596                     bound.setRect(newBound.x(), newBound.y(), newBound.width(), newBound.height());
597 
598                     if (itemResizeFlags & (ResizeTop | ResizeLeft)) {
599                         if (newPos != scenePos()) {
600                             setPos(newPos);
601                         }
602                     }
603 
604                     // update
605                     updateDescription();
606                     updateFootnotes();
607                     qs->setModified(true);
608                     qs->update();
609                 }
610             }
611         } break;
612         default:;
613     }
614 
615     if (itemResizeFlags) {
616         return true;
617     } else {
618         return QGraphicsItem::sceneEvent(event);
619     }
620 }
621 
itemChange(GraphicsItemChange change,const QVariant & value)622 QVariant QDElement::itemChange(GraphicsItemChange change, const QVariant &value) {
623     switch (change) {
624         case ItemPositionChange: {
625             // value is the new position
626             QPointF newPos = value.toPointF();
627             QueryScene *qs = qobject_cast<QueryScene *>(scene());
628             if (qs == nullptr) {
629                 return newPos;
630             }
631             // Adjust position for item to fit in row
632             qreal start = qs->annotationsArea().top();
633             newPos.setY(round(newPos.y() - start, GRID_STEP) + start);
634             QRectF rect = qs->annotationsArea();
635             rect.setHeight(rect.height() - boundingRect().height());
636             rect.setWidth(rect.width() - boundingRect().width());
637             if (!rect.contains(newPos)) {
638                 if (newPos.y() > rect.bottom()) {
639                     int prevRowsNum = qs->getRowsNumber();
640                     qreal bottomEdge = newPos.y() + boundingRect().height();
641                     int reqRowNum = (bottomEdge - qs->annotationsArea().top()) / GRID_STEP;
642                     int rowNum = qMax(prevRowsNum, reqRowNum);
643                     qs->setRowsNumber(rowNum);
644                 }
645                 // Keep the item inside the annotation area
646                 newPos.setX(qBound(rect.left(), newPos.x(), rect.left() + QueryScene::MAX_SCENE_SIZE.width()));
647                 newPos.setY(qMax(newPos.y(), rect.top()));
648             }
649 
650             // prevent collision
651             QRectF itemRect = boundingRect();
652             const QPointF &topLeft = mapToScene(itemRect.topLeft());
653             itemRect.moveTopLeft(topLeft);
654             itemRect.translate(newPos - scenePos());
655             QPainterPath path;
656             path.addRect(itemRect);
657             QList<QGraphicsItem *> items = scene()->items(path, Qt::IntersectsItemShape);
658             items.removeAll(this);
659             foreach (QGraphicsItem *item, items) {
660                 if (item->type() != QDElementType) {
661                     items.removeAll(item);
662                 }
663             }
664             const QPointF &oldPos = scenePos();
665             if (!items.isEmpty()) {
666                 return oldPos;
667             }
668             // check if links have positive length
669             foreach (Footnote *fn, links) {
670                 if (fn->from == this) {
671                     // future connector position
672                     QPointF left = fn->getSrcPoint();
673                     left += newPos - scenePos();
674                     QPointF right = fn->getDstPoint();
675                     if (right.x() - left.x() <= 0) {
676                         return oldPos;
677                     }
678                 } else {
679                     QPointF right = fn->getDstPoint();
680                     right += newPos - scenePos();
681                     QPointF left = fn->getSrcPoint();
682                     if (right.x() - left.x() <= 0) {
683                         return oldPos;
684                     }
685                 }
686             }
687             return newPos;
688         } break;
689         case ItemPositionHasChanged: {
690             QueryScene *qs = qobject_cast<QueryScene *>(scene());
691             if (qs == nullptr) {
692                 return QGraphicsItem::itemChange(change, value);
693             }
694             qs->sl_adaptRowsNumber();
695 
696             QRectF rect = qs->sceneRect();
697             qreal rightEdge = mapRectToScene(boundingRect()).right();
698             qreal min = rect.left() + QueryScene::DEFAULT_SCENE_SIZE.width();
699             qreal max = rect.left() + QueryScene::MAX_SCENE_SIZE.width();
700             rightEdge = qBound(min, rightEdge, max);
701             if (rightEdge > rect.right()) {
702                 rect.setRight(rightEdge);
703                 qs->setSceneRect(rect);
704             }
705 
706             updateFootnotes();
707             qs->setModified(true);
708         } break;
709         case ItemSceneChange:
710             if ((value.value<QGraphicsScene *>()) == nullptr) {
711                 foreach (Footnote *fn, links) {
712                     scene()->removeItem(fn);
713                     delete fn;
714                 }
715             }
716             break;
717         case ItemSceneHasChanged:
718             if ((value.value<QGraphicsScene *>()) != nullptr) {
719                 sl_refresh();
720                 adaptSize();
721                 QueryScene *qs = qobject_cast<QueryScene *>(scene());
722                 QueryViewController *view = qs->getViewController();
723                 if (view) {
724                     connect(itemDescription, SIGNAL(linkActivated(const QString &)), view, SLOT(sl_selectEditorCell(const QString &)));
725                     connect(itemDescription, SIGNAL(linkHovered(const QString &)), SLOT(sl_onHoverLink(const QString &)));
726                 }
727             }
728             break;
729         default:
730             break;
731     }
732     return QGraphicsItem::itemChange(change, value);
733 }
734 
updateFootnotes()735 void QDElement::updateFootnotes() {
736     QueryScene *qs = qobject_cast<QueryScene *>(scene());
737     QGraphicsView *view = qs->views().at(0);
738     assert(view);
739     QGraphicsView::ViewportUpdateMode mode = view->viewportUpdateMode();
740     view->setViewportUpdateMode(QGraphicsView::NoViewportUpdate);
741     foreach (Footnote *fn, links) {
742         fn->updatePos();
743     }
744     view->setViewportUpdateMode(mode);
745 }
746 
sl_onHoverLink(const QString & link)747 void QDElement::sl_onHoverLink(const QString &link) {
748     if (link.isEmpty()) {
749         unsetCursor();
750     } else {
751         setCursor(Qt::PointingHandCursor);
752     }
753 }
754 
755 /************************************************************************/
756 /* QDElementDescription                                                 */
757 /************************************************************************/
758 
QDElementDescription(QGraphicsItem * parent)759 QDElementDescription::QDElementDescription(QGraphicsItem *parent /* =NULL */)
760     : QGraphicsTextItem(parent) {
761     setAcceptHoverEvents(true);
762     setTextInteractionFlags(Qt::LinksAccessibleByMouse | Qt::LinksAccessibleByKeyboard);
763 }
764 
boundingRect() const765 QRectF QDElementDescription::boundingRect() const {
766     QRectF bound = QGraphicsTextItem::boundingRect();
767     bound.setHeight(height);
768     return bound;
769 }
770 
paint(QPainter * painter,const QStyleOptionGraphicsItem * option,QWidget * widget)771 void QDElementDescription::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) {
772     QStyleOptionGraphicsItem deselectedOption = *option;
773     deselectedOption.state &= ~(QStyle::State_Selected | QStyle::State_HasFocus);
774     QGraphicsTextItem::paint(painter, &deselectedOption, widget);
775 }
776 
777 // subscribe to mouse release event (link activation in QGraphicsTextItem) by accepting mouse press
mousePressEvent(QGraphicsSceneMouseEvent * event)778 void QDElementDescription::mousePressEvent(QGraphicsSceneMouseEvent *event) {
779     QGraphicsTextItem::mousePressEvent(event);
780     event->accept();
781 }
782 
sceneEvent(QEvent * event)783 bool QDElementDescription::sceneEvent(QEvent *event) {
784     bool res = QGraphicsTextItem::sceneEvent(event);
785     switch (event->type()) {
786         case QEvent::GraphicsSceneHoverEnter:
787         case QEvent::GraphicsSceneHoverMove:
788         case QEvent::GraphicsSceneMouseRelease:
789         case QEvent::GraphicsSceneMouseMove:
790         case QEvent::GraphicsSceneMousePress: {
791             QGraphicsSceneMouseEvent *me = static_cast<QGraphicsSceneMouseEvent *>(event);
792             QDElement *parent = qgraphicsitem_cast<QDElement *>(parentItem());
793             assert(parent);
794             me->setPos(mapToParent(me->pos()));
795             res = parent->sceneEvent(me);
796         } break;
797         default:
798             break;
799     }
800     return res;
801 }
802 
803 /************************************************************************/
804 /* Footnote Item                                                        */
805 /************************************************************************/
806 
Footnote(QDElement * _from,QDElement * _to,QDDistanceType _distType,QDConstraint * parent,const QFont & _font)807 Footnote::Footnote(QDElement *_from, QDElement *_to, QDDistanceType _distType, QDConstraint *parent, const QFont &_font)
808     : from(_from), to(_to), distType(_distType), constraint(parent), font(_font), draging(false) {
809 #if (QT_VERSION >= QT_VERSION_CHECK(4, 6, 0))
810     setFlag(QGraphicsItem::ItemSendsGeometryChanges, true);
811 #endif
812     connect(constraint->getParameters(), SIGNAL(si_modified()), SLOT(sl_update()));
813     init();
814 }
815 
init()816 void Footnote::init() {
817     setFlag(QGraphicsItem::ItemIsSelectable, true);
818     from->links << this;
819     to->links << this;
820 
821     QPen refPen(Qt::black);
822     refPen.setStyle(Qt::DotLine);
823     leftRef = new QGraphicsLineItem;
824     rightRef = new QGraphicsLineItem;
825     leftRef->setPen(refPen);
826     leftRef->setZValue(-1);
827     rightRef->setPen(refPen);
828     rightRef->setZValue(-1);
829 
830     sl_update();
831 }
832 
~Footnote()833 Footnote::~Footnote() {
834 }
835 
sl_update()836 void Footnote::sl_update() {
837     update();
838 }
839 
840 #define FOOTNOTE_MARGIN 6
updatePos()841 void Footnote::updatePos() {
842     qreal xPos = getSrcPoint().x();
843     int step = boundingRect().height() + FOOTNOTE_MARGIN;
844     QueryScene *qs = qobject_cast<QueryScene *>(scene());
845     assert(qs);
846     const QRectF &area = qs->footnotesArea();
847     // look for vacant position
848     int y;
849     for (y = area.top() + FOOTNOTE_MARGIN; y < area.bottom(); y += step) {
850         QRectF bound = boundingRect();
851         bound.translate(QPointF(xPos, y));
852 
853         QList<QGraphicsItem *> overlapingItems = qs->items(bound, Qt::IntersectsItemBoundingRect);
854         overlapingItems.removeAll(this);
855         foreach (QGraphicsItem *item, overlapingItems) {
856             if (item->type() != FootnoteItemType) {
857                 overlapingItems.removeAll(item);
858             }
859         }
860 
861         if (overlapingItems.isEmpty()) {
862             setPos(xPos, y);
863             updateLines(QPointF(xPos, y));
864             return;
865         }
866     }
867     y += step;
868     // assert(qs->footnotesArea().contains(QPointF(xPos,y)));
869     setPos(xPos, y);
870     updateLines(QPointF(xPos, y));
871 }
872 
updateLines(const QPointF & p)873 void Footnote::updateLines(const QPointF &p) {
874     QPointF p3 = getSrcPoint();
875     QLineF rightLine(p3, p);
876 
877     QPointF p1 = getDstPoint();
878     QPointF p2 = p + QPointF(p1.x() - p3.x(), 0);
879     QLineF leftLine(p1, p2);
880 
881     leftRef->setLine(leftLine);
882     rightRef->setLine(rightLine);
883 }
884 
getText() const885 QString Footnote::getText() const {
886     return constraint->getText(from->getSchemeUnit(), to->getSchemeUnit());
887 }
888 
itemChange(GraphicsItemChange change,const QVariant & value)889 QVariant Footnote::itemChange(GraphicsItemChange change, const QVariant &value) {
890     if (change == ItemSceneHasChanged) {
891         if (scene()) {
892             scene()->addItem(leftRef);
893             scene()->addItem(rightRef);
894         }
895     } else if (change == ItemSceneChange) {
896         if ((value.value<QGraphicsScene *>()) == nullptr) {
897             scene()->removeItem(leftRef);
898             scene()->removeItem(rightRef);
899             delete leftRef;
900             delete rightRef;
901             from->links.removeAll(this);
902             to->links.removeAll(this);
903         }
904     } else if (change == ItemPositionHasChanged) {
905         updateLines(scenePos());
906         leftRef->update();
907         rightRef->update();
908     }
909     return QGraphicsItem::itemChange(change, value);
910 }
911 
912 #define ARROW_DELTA 2
913 #define ARROW_WIDTH 4
boundingRect() const914 QRectF Footnote::boundingRect() const {
915     const QString &text = getText();
916     QFontMetricsF fm(font);
917     // fm.boundingRect().width() and fm.width() provide different values
918     QRectF textBound(0, 0, fm.width(text), fm.height());
919     textBound.moveTop(ARROW_DELTA);
920 
921     qreal arrW = getDstPoint().x() - getSrcPoint().x();
922     QRectF arrowBound(0, -ARROW_DELTA, arrW, 2 * ARROW_DELTA);
923     return textBound | arrowBound;
924 }
925 
paint(QPainter * painter,const QStyleOptionGraphicsItem * option,QWidget * widget)926 void Footnote::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) {
927     Q_UNUSED(option);
928     Q_UNUSED(widget);
929     painter->fillRect(boundingRect(), Qt::white);
930     qreal arrW = getDstPoint().x() - getSrcPoint().x();
931     QPen pen(Qt::black);
932     if (isSelected()) {
933         pen.setStyle(Qt::DashLine);
934     }
935     if (draging) {
936         pen.setColor(Qt::green);
937     }
938     QDDistanceConstraint *dc = static_cast<QDDistanceConstraint *>(constraint);
939     if (dc && dc->getMin() > dc->getMax()) {
940         pen.setColor(Qt::red);
941     }
942     painter->setPen(pen);
943     painter->drawLine(0, 0, arrW, 0);
944     // draw arrow endings
945     painter->drawLine(0, 0, ARROW_WIDTH, ARROW_DELTA);
946     painter->drawLine(0, 0, ARROW_WIDTH, -ARROW_DELTA);
947     painter->drawLine(arrW, 0, arrW - ARROW_WIDTH, ARROW_DELTA);
948     painter->drawLine(arrW, 0, arrW - ARROW_WIDTH, -ARROW_DELTA);
949 
950     // draw text
951     const QString &text = getText();
952     QFontMetrics fm(font);
953     QRectF textBound(0, 0, fm.width(text), fm.height());
954     textBound.moveTop(ARROW_DELTA);
955     QPointF c(boundingRect().center().x(), textBound.center().y());
956     textBound.moveCenter(c);
957     painter->drawText(textBound, text);
958 }
959 
getSrcPoint() const960 QPointF Footnote::getSrcPoint() const {
961     switch (distType) {
962         case E2S:
963         case E2E:
964             return from->getRightConnector();
965         case S2E:
966         case S2S:
967             return from->getLeftConnector();
968     }
969     assert(false);
970     return QPointF(0, 0);
971 }
972 
getDstPoint() const973 QPointF Footnote::getDstPoint() const {
974     switch (distType) {
975         case E2S:
976         case S2S:
977             return to->getLeftConnector();
978         case S2E:
979         case E2E:
980             return to->getRightConnector();
981     }
982     assert(false);
983     return QPointF(0, 0);
984 }
985 
adjacent(QDElement * uv) const986 QDElement *Footnote::adjacent(QDElement *uv) const {
987     if (uv == from) {
988         return to;
989     }
990     if (uv == to) {
991         return from;
992     }
993     return nullptr;
994 }
995 
996 /************************************************************************/
997 /* QDLabelItem                                                          */
998 /************************************************************************/
999 
QDLabelItem(const QString & text)1000 QDLabelItem::QDLabelItem(const QString &text)
1001     : QGraphicsTextItem(text) {
1002     setTextInteractionFlags(Qt::TextEditorInteraction);
1003 }
1004 
keyPressEvent(QKeyEvent * event)1005 void QDLabelItem::keyPressEvent(QKeyEvent *event) {
1006     if (event->key() == Qt::Key_Return || event->key() == Qt::Key_Enter || event->key() == Qt::Key_Space) {
1007         setSelected(false);
1008         return;
1009     }
1010     QGraphicsTextItem::keyPressEvent(event);
1011 }
1012 
focusOutEvent(QFocusEvent * event)1013 void QDLabelItem::focusOutEvent(QFocusEvent *event) {
1014     setTextInteractionFlags(Qt::NoTextInteraction);
1015     QGraphicsTextItem::focusOutEvent(event);
1016     emit si_editingFinished();
1017 }
1018 
mousePressEvent(QGraphicsSceneMouseEvent * event)1019 void QDLabelItem::mousePressEvent(QGraphicsSceneMouseEvent *event) {
1020     setTextInteractionFlags(Qt::TextEditorInteraction);
1021     QGraphicsTextItem::mousePressEvent(event);
1022 }
1023 
1024 /************************************************************************/
1025 /* QDDescriptionItem                                                    */
1026 /************************************************************************/
1027 #define TEXT_MARGINS 5
QDDescriptionItem(const QString & text)1028 QDDescriptionItem::QDDescriptionItem(const QString &text)
1029     : QGraphicsTextItem(text), resize(0) {
1030     setTextInteractionFlags(Qt::TextEditorInteraction);
1031     setAcceptHoverEvents(true);
1032     setFlag(QGraphicsItem::ItemIsSelectable, true);
1033 }
1034 
boundingRect() const1035 QRectF QDDescriptionItem::boundingRect() const {
1036     QRectF bound = QGraphicsTextItem::boundingRect();
1037     const QPointF &cp = bound.center();
1038     bound.setWidth(bound.width() + TEXT_MARGINS * 2);
1039     bound.setHeight(bound.height() + TEXT_MARGINS * 2);
1040     bound.moveCenter(cp);
1041     return bound;
1042 }
1043 
shape() const1044 QPainterPath QDDescriptionItem::shape() const {
1045     QRectF bound = boundingRect();
1046     QPainterPath path;
1047     path.addRect(bound);
1048     return path;
1049 }
1050 
paint(QPainter * painter,const QStyleOptionGraphicsItem * option,QWidget * widget)1051 void QDDescriptionItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) {
1052     Q_UNUSED(option);
1053     Q_UNUSED(widget);
1054     QRectF bound = boundingRect();
1055     bound.setHeight(bound.height() - 1);
1056     // bound.setLeft(bound.left()+1);
1057     bound.setWidth(bound.width() - 1);
1058     if (!hasFocus()) {
1059         painter->drawRect(bound);
1060     }
1061     QGraphicsTextItem::paint(painter, option, widget);
1062 }
1063 
sceneEvent(QEvent * event)1064 bool QDDescriptionItem::sceneEvent(QEvent *event) {
1065     switch (event->type()) {
1066         case QEvent::GraphicsSceneHoverEnter:
1067         case QEvent::GraphicsSceneHoverMove: {
1068             QGraphicsSceneHoverEvent *he = static_cast<QGraphicsSceneHoverEvent *>(event);
1069             QPointF p = he->pos();
1070             qreal dxRight = qAbs(boundingRect().right() - p.x());
1071             qreal dxLeft = qAbs(boundingRect().left() - p.x());
1072             if (p.y() >= boundingRect().top() && p.y() <= boundingRect().bottom()) {
1073                 if (dxRight < EDGE_WIDTH) {
1074                     setCursor(Qt::SizeHorCursor);
1075                     resize = QDElement::ResizeRight;
1076                 } else if (dxLeft < EDGE_WIDTH) {
1077                     setCursor(Qt::SizeHorCursor);
1078                     resize = QDElement::ResizeLeft;
1079                 } else {
1080                     unsetCursor();
1081                     resize = 0;
1082                 }
1083             }
1084             break;
1085         }
1086         case QEvent::GraphicsSceneHoverLeave:
1087         case QEvent::GraphicsSceneMouseRelease:
1088             unsetCursor();
1089             resize = 0;
1090             break;
1091         case QEvent::GraphicsSceneMouseMove: {
1092             if (resize) {
1093                 // QueryScene* qs = qobject_cast<QueryScene*>(scene());
1094                 QGraphicsSceneMouseEvent *me = static_cast<QGraphicsSceneMouseEvent *>(event);
1095                 if (me->buttons() & Qt::LeftButton) {
1096                     QPointF p = me->pos();
1097                     QPointF oldPos = me->lastPos();
1098                     if (resize == QDElement::ResizeRight) {
1099                         setTextWidth(textWidth() + p.x() - oldPos.x());
1100                     } else if (resize == QDElement::ResizeLeft) {
1101                         QPointF newPos = scenePos();
1102                         newPos.setX(me->scenePos().x());
1103                         setTextWidth(textWidth() - newPos.x() + scenePos().x());
1104                         setPos(newPos);
1105                     }
1106                 }
1107             }
1108         } break;
1109         default:;
1110     }
1111     return QGraphicsTextItem::sceneEvent(event);
1112 }
1113 
mousePressEvent(QGraphicsSceneMouseEvent * event)1114 void QDDescriptionItem::mousePressEvent(QGraphicsSceneMouseEvent *event) {
1115     setTextInteractionFlags(Qt::TextEditorInteraction);
1116     QGraphicsTextItem::mousePressEvent(event);
1117 }
1118 
focusOutEvent(QFocusEvent * event)1119 void QDDescriptionItem::focusOutEvent(QFocusEvent *event) {
1120     setTextInteractionFlags(Qt::NoTextInteraction);
1121     QGraphicsTextItem::focusOutEvent(event);
1122 }
1123 
1124 /************************************************************************/
1125 /* Ruler                                                                */
1126 /************************************************************************/
1127 #define NOTCH_SIZE 6
1128 #define QD_RULER_MARGINS 5
1129 #define MIN_LEN_TO_DRAW 20
1130 
boundingRect() const1131 QRectF QDRulerItem::boundingRect() const {
1132     QRectF bound(leftPos, 0, rightPos, QD_RULER_MARGINS * 2 + NOTCH_SIZE);
1133     QRectF resBound = bound.united(txtBound());
1134     resBound.setHeight(resBound.height() + QD_RULER_MARGINS);
1135     return resBound;
1136 }
1137 
txtBound() const1138 QRectF QDRulerItem::txtBound() const {
1139     QFontMetricsF fm(font);
1140     QRectF txtBound(0, 0, fm.width(text), fm.height());
1141     qreal txtX = leftPos + (rightPos - leftPos) / 2;
1142     qreal txtY = QD_RULER_MARGINS + NOTCH_SIZE + txtBound.height() / 2;
1143     txtBound.moveCenter(QPointF(txtX, txtY));
1144     return txtBound;
1145 }
1146 
paint(QPainter * painter,const QStyleOptionGraphicsItem *,QWidget *)1147 void QDRulerItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *) {
1148     if (rightPos - leftPos < MIN_LEN_TO_DRAW) {
1149         return;
1150     }
1151     painter->drawLine(leftPos, NOTCH_SIZE / 2 + QD_RULER_MARGINS, rightPos, NOTCH_SIZE / 2 + QD_RULER_MARGINS);
1152     painter->drawLine(leftPos, QD_RULER_MARGINS, leftPos, NOTCH_SIZE + QD_RULER_MARGINS);
1153     painter->drawLine(rightPos, QD_RULER_MARGINS, rightPos, NOTCH_SIZE + QD_RULER_MARGINS);
1154 
1155     painter->setFont(font);
1156     painter->drawText(txtBound(), text);
1157 }
1158 
itemChange(GraphicsItemChange change,const QVariant & value)1159 QVariant QDRulerItem::itemChange(GraphicsItemChange change, const QVariant &value) {
1160     if (change == ItemSceneHasChanged) {
1161         if (scene()) {
1162             connect(scene(), SIGNAL(changed(const QList<QRectF> &)), SLOT(sl_updateGeometry()));
1163         }
1164     }
1165     return QGraphicsItem::itemChange(change, value);
1166 }
1167 
sl_updateGeometry()1168 void QDRulerItem::sl_updateGeometry() {
1169     QueryScene *qs = qobject_cast<QueryScene *>(scene());
1170     assert(qs);
1171     QList<QDElement *> items;
1172     foreach (QGraphicsItem *it, qs->getElements()) {
1173         QDElement *uv = qgraphicsitem_cast<QDElement *>(it);
1174         assert(uv);
1175         items.append(uv);
1176     }
1177     if (items.isEmpty()) {
1178         leftPos = 0;
1179         rightPos = 0;
1180     } else {
1181         leftPos = items.first()->scenePos().x();
1182         rightPos = items.first()->scenePos().x() + items.first()->boundingRect().width();
1183         foreach (QDElement *item, items) {
1184             qreal curLeft = item->scenePos().x();
1185             qreal curRight = curLeft + item->boundingRect().right();
1186             if (curLeft < leftPos) {
1187                 leftPos = curLeft;
1188             }
1189             if (curRight > rightPos) {
1190                 rightPos = curRight;
1191             }
1192         }
1193     }
1194 }
1195 
sl_updateText()1196 void QDRulerItem::sl_updateText() {
1197     int minDist = 0;
1198     int maxDist = 0;
1199     QueryScene *qs = qobject_cast<QueryScene *>(scene());
1200     assert(qs);
1201     QDScheme *scheme = qs->getScheme();
1202     QList<QDSchemeUnit *> units;
1203     foreach (QDActor *a, scheme->getActors()) {
1204         units << a->getSchemeUnits();
1205     }
1206     for (int i = 0, n = units.size(); i < n - 1; i++) {
1207         for (int j = i + 1; j < n; j++) {
1208             QDSchemeUnit *src = units.at(i);
1209             QDSchemeUnit *dst = units.at(j);
1210             QList<QDPath *> paths = scheme->findPaths(src, dst);
1211             foreach (QDPath *path, paths) {
1212                 QDDistanceConstraint *dc = path->toConstraint();
1213                 if (!dc) {
1214                     text = tr("N/A");
1215                     update();
1216                     return;
1217                 }
1218                 int curMinDist = dc->getMin();
1219                 int curMaxDist = dc->getMax();
1220                 QDSchemeUnit *dcSrc = dc->getSource();
1221                 QDSchemeUnit *dcDst = dc->getDestination();
1222                 if (dc->distanceType() == S2S) {
1223                     curMinDist += dcDst->getActor()->getMinResultLen();
1224                     curMaxDist += dcDst->getActor()->getMaxResultLen();
1225                 }
1226                 if (dc->distanceType() == E2E) {
1227                     curMinDist += dcSrc->getActor()->getMinResultLen();
1228                     curMaxDist += dcSrc->getActor()->getMaxResultLen();
1229                 }
1230                 if (dc->distanceType() == E2S) {
1231                     curMinDist += dcSrc->getActor()->getMinResultLen();
1232                     curMaxDist += dcSrc->getActor()->getMaxResultLen();
1233                     curMinDist += dcDst->getActor()->getMinResultLen();
1234                     curMaxDist += dcDst->getActor()->getMaxResultLen();
1235                 }
1236                 minDist = qMax(minDist, curMinDist);
1237                 maxDist = qMax(maxDist, curMaxDist);
1238             }
1239         }
1240     }
1241     foreach (QDSchemeUnit *su, units) {
1242         minDist = qMax(su->getActor()->getMinResultLen(), minDist);
1243         maxDist = qMax(su->getActor()->getMaxResultLen(), maxDist);
1244     }
1245     if (minDist == maxDist) {
1246         text = QString("%1 bp").arg(minDist);
1247     } else {
1248         text = QString("%1..%2 bp").arg(minDist).arg(maxDist);
1249     }
1250     update();
1251 }
1252 
1253 }  // namespace U2
1254