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('<', "<");
212 annotateAsStr.replace('>', ">");
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