1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 Jochen Becher
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of Qt Creator.
7 **
8 ** Commercial License Usage
9 ** Licensees holding valid commercial Qt licenses may use this file in
10 ** accordance with the commercial license agreement provided with the
11 ** Software or, alternatively, in accordance with the terms contained in
12 ** a written agreement between you and The Qt Company. For licensing terms
13 ** and conditions see https://www.qt.io/terms-conditions. For further
14 ** information use the contact form at https://www.qt.io/contact-us.
15 **
16 ** GNU General Public License Usage
17 ** Alternatively, this file may be used under the terms of the GNU
18 ** General Public License version 3 as published by the Free Software
19 ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
20 ** included in the packaging of this file. Please review the following
21 ** information to ensure the GNU General Public License requirements will
22 ** be met: https://www.gnu.org/licenses/gpl-3.0.html.
23 **
24 ****************************************************************************/
25
26 #include "diagramscenemodel.h"
27
28 #include "diagramgraphicsscene.h"
29 #include "diagramsceneconstants.h"
30 #include "diagramscenemodelitemvisitors.h"
31 #include "latchcontroller.h"
32 #include "capabilities/moveable.h"
33 #include "capabilities/resizable.h"
34 #include "capabilities/selectable.h"
35 #include "capabilities/editable.h"
36
37 #include "qmt/diagram/dobject.h"
38 #include "qmt/diagram/drelation.h"
39 #include "qmt/diagram/dswimlane.h"
40 #include "qmt/diagram_controller/diagramcontroller.h"
41 #include "qmt/diagram_controller/dselection.h"
42 #include "qmt/diagram_scene/items/objectitem.h"
43 #include "qmt/diagram_scene/items/swimlaneitem.h"
44 #include "qmt/model/mdiagram.h"
45 #include "qmt/model/mobject.h"
46 #include "qmt/model/mpackage.h"
47 #include "qmt/model_controller/modelcontroller.h"
48 #include "qmt/stereotype/stereotypecontroller.h"
49 #include "qmt/style/stylecontroller.h"
50 #include "qmt/tasks/diagramscenecontroller.h"
51 #include "qmt/tasks/ielementtasks.h"
52
53 #include <utils/algorithm.h>
54
55 #include <QSet>
56 #include <QGraphicsItem>
57 #include <QGraphicsSceneMouseEvent>
58 #include <QGraphicsView>
59 #include <QApplication>
60 #include <QClipboard>
61 #include <QMimeData>
62
63 #include <QBuffer>
64 #include <QPdfWriter>
65 #include <QFile>
66
67 #ifndef QT_NO_SVG
68 #include <QtSvg/QSvgGenerator>
69 #endif
70
71 namespace qmt {
72
73 class DiagramSceneModel::OriginItem : public QGraphicsItem
74 {
75 public:
OriginItem(QGraphicsItem * parent=nullptr)76 explicit OriginItem(QGraphicsItem *parent = nullptr)
77 : QGraphicsItem(parent)
78 {
79 }
80
boundingRect() const81 QRectF boundingRect() const final
82 {
83 return QRectF(0.0, 0.0, 20.0, 20.0);
84 }
85
paint(QPainter * painter,const QStyleOptionGraphicsItem * option,QWidget * widget)86 void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) final
87 {
88 Q_UNUSED(option)
89 Q_UNUSED(widget)
90
91 QPen pen(QBrush(Qt::lightGray), 1.0, Qt::DotLine);
92 painter->setPen(pen);
93 painter->drawLine(QLineF(0.0, 0.0, 20.0, 0.0));
94 painter->drawLine(QLineF(0.0, 0.0, 0.0, 20.0));
95 }
96 };
97
98 class DiagramSceneModel::SelectionStatus {
99 public:
100 QSet<QGraphicsItem *> m_selectedItems;
101 QSet<QGraphicsItem *> m_secondarySelectedItems;
102 QGraphicsItem *m_focusItem = nullptr;
103 IEditable *m_editItem = nullptr;
104 bool m_exportSelectedElements = false;
105 QRectF m_sceneBoundingRect;
106 };
107
DiagramSceneModel(QObject * parent)108 DiagramSceneModel::DiagramSceneModel(QObject *parent)
109 : QObject(parent),
110 m_graphicsScene(new DiagramGraphicsScene(this)),
111 m_latchController(new LatchController(this)),
112 m_originItem(new OriginItem())
113 {
114 m_latchController->setDiagramSceneModel(this);
115 connect(m_graphicsScene, &QGraphicsScene::selectionChanged,
116 this, &DiagramSceneModel::onSelectionChanged);
117
118 // add one item at origin to force scene rect to include origin always
119 m_graphicsScene->addItem(m_originItem);
120
121 m_latchController->addToGraphicsScene(m_graphicsScene);
122 }
123
~DiagramSceneModel()124 DiagramSceneModel::~DiagramSceneModel()
125 {
126 QMT_CHECK(m_busyState == NotBusy);
127 m_latchController->removeFromGraphicsScene(m_graphicsScene);
128 disconnect();
129 if (m_diagramController)
130 disconnect(m_diagramController, nullptr, this, nullptr);
131 m_graphicsScene->deleteLater();
132 }
133
setDiagramController(DiagramController * diagramController)134 void DiagramSceneModel::setDiagramController(DiagramController *diagramController)
135 {
136 if (m_diagramController == diagramController)
137 return;
138 if (m_diagramController) {
139 disconnect(m_diagramController, nullptr, this, nullptr);
140 m_diagramController = nullptr;
141 }
142 m_diagramController = diagramController;
143 if (diagramController) {
144 connect(m_diagramController, &DiagramController::beginResetAllDiagrams,
145 this, &DiagramSceneModel::onBeginResetAllDiagrams);
146 connect(m_diagramController, &DiagramController::endResetAllDiagrams,
147 this, &DiagramSceneModel::onEndResetAllDiagrams);
148 connect(m_diagramController, &DiagramController::beginResetDiagram,
149 this, &DiagramSceneModel::onBeginResetDiagram);
150 connect(m_diagramController, &DiagramController::endResetDiagram,
151 this, &DiagramSceneModel::onEndResetDiagram);
152 connect(m_diagramController, &DiagramController::beginUpdateElement,
153 this, &DiagramSceneModel::onBeginUpdateElement);
154 connect(m_diagramController, &DiagramController::endUpdateElement,
155 this, &DiagramSceneModel::onEndUpdateElement);
156 connect(m_diagramController, &DiagramController::beginInsertElement,
157 this, &DiagramSceneModel::onBeginInsertElement);
158 connect(m_diagramController, &DiagramController::endInsertElement,
159 this, &DiagramSceneModel::onEndInsertElement);
160 connect(m_diagramController, &DiagramController::beginRemoveElement,
161 this, &DiagramSceneModel::onBeginRemoveElement);
162 connect(m_diagramController, &DiagramController::endRemoveElement,
163 this, &DiagramSceneModel::onEndRemoveElement);
164 }
165 }
166
setDiagramSceneController(DiagramSceneController * diagramSceneController)167 void DiagramSceneModel::setDiagramSceneController(DiagramSceneController *diagramSceneController)
168 {
169 m_diagramSceneController = diagramSceneController;
170 }
171
setStyleController(StyleController * styleController)172 void DiagramSceneModel::setStyleController(StyleController *styleController)
173 {
174 m_styleController = styleController;
175 }
176
setStereotypeController(StereotypeController * stereotypeController)177 void DiagramSceneModel::setStereotypeController(StereotypeController *stereotypeController)
178 {
179 m_stereotypeController = stereotypeController;
180 }
181
setDiagram(MDiagram * diagram)182 void DiagramSceneModel::setDiagram(MDiagram *diagram)
183 {
184 if (m_diagram != diagram) {
185 onBeginResetDiagram(diagram);
186 m_diagram = diagram;
187 onEndResetDiagram(diagram);
188 }
189 }
190
graphicsScene() const191 QGraphicsScene *DiagramSceneModel::graphicsScene() const
192 {
193 return m_graphicsScene;
194 }
195
sceneRect() const196 QRectF DiagramSceneModel::sceneRect() const
197 {
198 return m_sceneRect;
199 }
200
hasSelection() const201 bool DiagramSceneModel::hasSelection() const
202 {
203 return !m_graphicsScene->selectedItems().isEmpty();
204 }
205
hasMultiObjectsSelection() const206 bool DiagramSceneModel::hasMultiObjectsSelection() const
207 {
208 int count = 0;
209 foreach (QGraphicsItem *item, m_graphicsScene->selectedItems()) {
210 DElement *element = m_itemToElementMap.value(item);
211 QMT_CHECK(element);
212 if (dynamic_cast<DObject *>(element)) {
213 ++count;
214 if (count > 1)
215 return true;
216 }
217 }
218 return false;
219 }
220
selectedElements() const221 DSelection DiagramSceneModel::selectedElements() const
222 {
223 DSelection selection;
224 foreach (QGraphicsItem *item, m_graphicsScene->selectedItems()) {
225 DElement *element = m_itemToElementMap.value(item);
226 QMT_ASSERT(element, return selection);
227 selection.append(element->uid(), m_diagram->uid());
228 }
229 return selection;
230 }
231
findTopmostElement(const QPointF & scenePos) const232 DElement *DiagramSceneModel::findTopmostElement(const QPointF &scenePos) const
233 {
234 // fetch affected items from scene in correct drawing order to find topmost element
235 QList<QGraphicsItem *> items = m_graphicsScene->items(scenePos);
236 foreach (QGraphicsItem *item, items) {
237 if (m_graphicsItems.contains(item))
238 return m_itemToElementMap.value(item);
239 }
240 return nullptr;
241 }
242
findTopmostObject(const QPointF & scenePos) const243 DObject *DiagramSceneModel::findTopmostObject(const QPointF &scenePos) const
244 {
245 ObjectItem *item = findTopmostObjectItem(scenePos);
246 if (!item)
247 return nullptr;
248 return item->object();
249 }
250
findTopmostObjectItem(const QPointF & scenePos) const251 ObjectItem *DiagramSceneModel::findTopmostObjectItem(const QPointF &scenePos) const
252 {
253 // fetch affected items from scene in correct drawing order to find topmost element
254 const QList<QGraphicsItem *> items = m_graphicsScene->items(scenePos);
255 for (QGraphicsItem *item : qAsConst(items)) {
256 if (m_graphicsItems.contains(item)) {
257 DObject *object = dynamic_cast<DObject *>(m_itemToElementMap.value(item));
258 if (object)
259 return dynamic_cast<ObjectItem *>(item);
260 }
261 }
262 return nullptr;
263 }
264
graphicsItem(DElement * element) const265 QGraphicsItem *DiagramSceneModel::graphicsItem(DElement *element) const
266 {
267 return m_elementToItemMap.value(element);
268 }
269
graphicsItem(const Uid & uid) const270 QGraphicsItem *DiagramSceneModel::graphicsItem(const Uid &uid) const
271 {
272 return m_elementToItemMap.value(m_diagramController->findElement(uid, m_diagram));
273 }
274
isSelectedItem(QGraphicsItem * item) const275 bool DiagramSceneModel::isSelectedItem(QGraphicsItem *item) const
276 {
277 return m_selectedItems.contains(item);
278 }
279
element(QGraphicsItem * item) const280 DElement *DiagramSceneModel::element(QGraphicsItem *item) const
281 {
282 return m_itemToElementMap.value(item);
283 }
284
isElementEditable(const DElement * element) const285 bool DiagramSceneModel::isElementEditable(const DElement *element) const
286 {
287 auto editable = dynamic_cast<IEditable *>(m_elementToItemMap.value(element));
288 return editable && editable->isEditable();
289 }
290
isInFrontOf(const QGraphicsItem * frontItem,const QGraphicsItem * backItem)291 bool DiagramSceneModel::isInFrontOf(const QGraphicsItem *frontItem, const QGraphicsItem *backItem)
292 {
293 QMT_ASSERT(frontItem, return false);
294 QMT_ASSERT(backItem, return false);
295
296 // shortcut for usual case of root items
297 if (!frontItem->parentItem() && !backItem->parentItem()) {
298 foreach (const QGraphicsItem *item, m_graphicsScene->items()) {
299 if (item == frontItem)
300 return true;
301 else if (item == backItem)
302 return false;
303 }
304 QMT_CHECK(false);
305 return false;
306 }
307
308 // collect all anchestors of front item
309 QList<const QGraphicsItem *> frontStack;
310 const QGraphicsItem *iterator = frontItem;
311 while (iterator) {
312 frontStack.append(iterator);
313 iterator = iterator->parentItem();
314 }
315
316 // collect all anchestors of back item
317 QList<const QGraphicsItem *> backStack;
318 iterator = backItem;
319 while (iterator) {
320 backStack.append(iterator);
321 iterator = iterator->parentItem();
322 }
323
324 // search lowest common anchestor
325 int frontIndex = frontStack.size() - 1;
326 int backIndex = backStack.size() - 1;
327 while (frontIndex >= 0 && backIndex >= 0 && frontStack.at(frontIndex) == backStack.at(backIndex)) {
328 --frontIndex;
329 --backIndex;
330 }
331
332 if (frontIndex < 0 && backIndex < 0) {
333 QMT_CHECK(frontItem == backItem);
334 return false;
335 } else if (frontIndex < 0) {
336 // front item is higher in hierarchy and thus behind back item
337 return false;
338 } else if (backIndex < 0) {
339 // back item is higher in hierarchy and thus in behind front item
340 return true;
341 } else {
342 frontItem = frontStack.at(frontIndex);
343 backItem = backStack.at(backIndex);
344 QMT_CHECK(frontItem != backItem);
345
346 if (frontItem->zValue() != backItem->zValue()) {
347 return frontItem->zValue() > backItem->zValue();
348 } else {
349 QList<QGraphicsItem *> children;
350 if (frontIndex + 1 < frontStack.size())
351 children = frontStack.at(frontIndex + 1)->childItems();
352 else
353 children = m_graphicsScene->items(Qt::AscendingOrder);
354 foreach (const QGraphicsItem *item, children) {
355 if (item == frontItem)
356 return false;
357 else if (item == backItem)
358 return true;
359 }
360 QMT_CHECK(false);
361 return false;
362 }
363 }
364 }
365
selectAllElements()366 void DiagramSceneModel::selectAllElements()
367 {
368 foreach (QGraphicsItem *item, m_graphicsItems)
369 item->setSelected(true);
370 }
371
selectElement(DElement * element)372 void DiagramSceneModel::selectElement(DElement *element)
373 {
374 QGraphicsItem *selectItem = m_elementToItemMap.value(element);
375 foreach (QGraphicsItem *item, m_selectedItems) {
376 if (item != selectItem)
377 item->setSelected(false);
378 }
379 if (selectItem)
380 selectItem->setSelected(true);
381 }
382
editElement(DElement * element)383 void DiagramSceneModel::editElement(DElement *element)
384 {
385 auto editable = dynamic_cast<IEditable *>(m_elementToItemMap.value(element));
386 if (editable && editable->isEditable())
387 editable->edit();
388 }
389
copyToClipboard()390 void DiagramSceneModel::copyToClipboard()
391 {
392 SelectionStatus status;
393 saveSelectionStatusBeforeExport(!(m_selectedItems.isEmpty() && m_secondarySelectedItems.isEmpty()), &status);
394
395 auto mimeData = new QMimeData();
396 // Create the image with the size of the shrunk scene
397 const int scaleFactor = 1;
398 const int border = 5;
399 const int baseDpi = 75;
400 const int dotsPerMeter = 10000 * baseDpi / 254;
401 QSize imageSize = status.m_sceneBoundingRect.size().toSize();
402 imageSize += QSize(2 * border, 2 * border);
403 imageSize *= scaleFactor;
404 QImage image(imageSize, QImage::Format_ARGB32);
405 image.setDotsPerMeterX(dotsPerMeter * scaleFactor);
406 image.setDotsPerMeterY(dotsPerMeter * scaleFactor);
407 image.fill(Qt::white);
408 QPainter painter;
409 painter.begin(&image);
410 painter.setRenderHint(QPainter::Antialiasing);
411 m_graphicsScene->render(&painter,
412 QRectF(border, border,
413 painter.device()->width() - 2 * border,
414 painter.device()->height() - 2 * border),
415 status.m_sceneBoundingRect);
416 painter.end();
417 mimeData->setImageData(image);
418 QApplication::clipboard()->setMimeData(mimeData, QClipboard::Clipboard);
419
420 restoreSelectedStatusAfterExport(status);
421 }
422
exportImage(const QString & fileName,bool selectedElements)423 bool DiagramSceneModel::exportImage(const QString &fileName, bool selectedElements)
424 {
425 SelectionStatus status;
426 saveSelectionStatusBeforeExport(selectedElements, &status);
427
428 // Create the image with the size of the shrunk scene
429 const int scaleFactor = 1;
430 const int border = 5;
431 const int baseDpi = 75;
432 const int dotsPerMeter = 10000 * baseDpi / 254;
433
434 QSize imageSize = status.m_sceneBoundingRect.size().toSize();
435 imageSize += QSize(2 * border, 2 * border);
436 imageSize *= scaleFactor;
437
438 QImage image(imageSize, QImage::Format_ARGB32);
439 image.setDotsPerMeterX(dotsPerMeter * scaleFactor);
440 image.setDotsPerMeterY(dotsPerMeter * scaleFactor);
441 image.fill(Qt::white);
442
443 QPainter painter;
444 painter.begin(&image);
445 painter.setRenderHint(QPainter::Antialiasing);
446 m_graphicsScene->render(&painter,
447 QRectF(border, border,
448 painter.device()->width() - 2 * border,
449 painter.device()->height() - 2 * border),
450 status.m_sceneBoundingRect);
451 painter.end();
452
453 bool success = image.save(fileName);
454
455 restoreSelectedStatusAfterExport(status);
456
457 return success;
458 }
459
exportPdf(const QString & fileName,bool selectedElements)460 bool DiagramSceneModel::exportPdf(const QString &fileName, bool selectedElements)
461 {
462 SelectionStatus status;
463 saveSelectionStatusBeforeExport(selectedElements, &status);
464
465 const double scaleFactor = 1.0;
466 const double border = 5;
467 const double baseDpi = 100;
468 const double dotsPerMm = 25.4 / baseDpi;
469
470 QSizeF pageSize = status.m_sceneBoundingRect.size();
471 pageSize += QSizeF(2.0 * border, 2.0 * border);
472 pageSize *= scaleFactor;
473 pageSize *= dotsPerMm;
474
475 QPdfWriter pdfWriter(fileName);
476 pdfWriter.setPageSize(QPageSize(pageSize, QPageSize::Millimeter));
477
478 QPainter pdfPainter;
479 pdfPainter.begin(&pdfWriter);
480 m_graphicsScene->render(&pdfPainter,
481 QRectF(border, border,
482 pdfPainter.device()->width() - 2 * border,
483 pdfPainter.device()->height() - 2 * border),
484 status.m_sceneBoundingRect);
485 pdfPainter.end();
486
487 restoreSelectedStatusAfterExport(status);
488
489 return true;
490 }
491
exportSvg(const QString & fileName,bool selectedElements)492 bool DiagramSceneModel::exportSvg(const QString &fileName, bool selectedElements)
493 {
494 #ifndef QT_NO_SVG
495 SelectionStatus status;
496 saveSelectionStatusBeforeExport(selectedElements, &status);
497
498 const double border = 5;
499
500 QSvgGenerator svgGenerator;
501 svgGenerator.setFileName(fileName);
502 QSize svgSceneSize = status.m_sceneBoundingRect.size().toSize();
503 svgGenerator.setSize(svgSceneSize);
504 svgGenerator.setViewBox(QRect(QPoint(0,0), svgSceneSize));
505 QPainter svgPainter;
506 svgPainter.begin(&svgGenerator);
507 svgPainter.setRenderHint(QPainter::Antialiasing);
508 m_graphicsScene->render(&svgPainter,
509 QRectF(border, border,
510 svgPainter.device()->width() - 2 * border,
511 svgPainter.device()->height() - 2 * border),
512 status.m_sceneBoundingRect);
513 svgPainter.end();
514
515 restoreSelectedStatusAfterExport(status);
516
517 return true;
518 #else // QT_NO_SVG
519 Q_UNUSED(fileName)
520 Q_UNUSED(selectedElements)
521 return false;
522 #endif // QT_NO_SVG
523 }
524
selectItem(QGraphicsItem * item,bool multiSelect)525 void DiagramSceneModel::selectItem(QGraphicsItem *item, bool multiSelect)
526 {
527 if (!multiSelect) {
528 if (!item->isSelected()) {
529 foreach (QGraphicsItem *selectedItem, m_selectedItems) {
530 if (selectedItem != item)
531 selectedItem->setSelected(false);
532 }
533 item->setSelected(true);
534 }
535 } else {
536 item->setSelected(!item->isSelected());
537 }
538 }
539
moveSelectedItems(QGraphicsItem * grabbedItem,const QPointF & delta)540 void DiagramSceneModel::moveSelectedItems(QGraphicsItem *grabbedItem, const QPointF &delta)
541 {
542 Q_UNUSED(grabbedItem)
543
544 if (delta != QPointF(0.0, 0.0)) {
545 foreach (QGraphicsItem *item, m_selectedItems) {
546 if (auto moveable = dynamic_cast<IMoveable *>(item))
547 moveable->moveDelta(delta);
548 }
549 foreach (QGraphicsItem *item, m_secondarySelectedItems) {
550 if (auto moveable = dynamic_cast<IMoveable *>(item))
551 moveable->moveDelta(delta);
552 }
553 }
554 }
555
alignSelectedItemsPositionOnRaster()556 void DiagramSceneModel::alignSelectedItemsPositionOnRaster()
557 {
558 foreach (QGraphicsItem *item, m_selectedItems) {
559 if (auto moveable = dynamic_cast<IMoveable *>(item))
560 moveable->alignItemPositionToRaster(RASTER_WIDTH, RASTER_HEIGHT);
561 }
562 foreach (QGraphicsItem *item, m_secondarySelectedItems) {
563 if (auto moveable = dynamic_cast<IMoveable *>(item))
564 moveable->alignItemPositionToRaster(RASTER_WIDTH, RASTER_HEIGHT);
565 }
566 }
567
onDoubleClickedItem(QGraphicsItem * item)568 void DiagramSceneModel::onDoubleClickedItem(QGraphicsItem *item)
569 {
570 DElement *element = m_itemToElementMap.value(item);
571 if (item)
572 m_diagramSceneController->elementTasks()->openElement(element, m_diagram);
573 }
574
collectCollidingObjectItems(const QGraphicsItem * item,CollidingMode collidingMode) const575 QList<QGraphicsItem *> DiagramSceneModel::collectCollidingObjectItems(const QGraphicsItem *item,
576 CollidingMode collidingMode) const
577 {
578 QList<QGraphicsItem *> collidingItems;
579
580 auto resizable = dynamic_cast<const IResizable *>(item);
581 if (!resizable)
582 return collidingItems;
583 QRectF rect = resizable->rect();
584 rect.translate(resizable->pos());
585
586 switch (collidingMode) {
587 case CollidingInnerItems:
588 foreach (QGraphicsItem *candidate, m_graphicsItems) {
589 if (auto candidateResizable = dynamic_cast<const IResizable *>(candidate)) {
590 QRectF candidateRect = candidateResizable->rect();
591 candidateRect.translate(candidateResizable->pos());
592 if (candidateRect.left() >= rect.left() && candidateRect.right() <= rect.right()
593 && candidateRect.top() >= rect.top() && candidateRect.bottom() <= rect.bottom()) {
594 collidingItems.append(candidate);
595 }
596 }
597 }
598 break;
599 case CollidingItems:
600 foreach (QGraphicsItem *candidate, m_graphicsItems) {
601 if (auto candidateResizable = dynamic_cast<const IResizable *>(candidate)) {
602 QRectF candidateRect = candidateResizable->rect();
603 candidateRect.translate(candidateResizable->pos());
604 if (candidateRect.left() <= rect.right() && candidateRect.right() >= rect.left()
605 && candidateRect.top() <= rect.bottom() && candidateRect.bottom() >= rect.top()) {
606 collidingItems.append(candidate);
607 }
608 }
609 }
610 break;
611 case CollidingOuterItems:
612 foreach (QGraphicsItem *candidate, m_graphicsItems) {
613 if (auto candidateResizable = dynamic_cast<const IResizable *>(candidate)) {
614 QRectF candidateRect = candidateResizable->rect();
615 candidateRect.translate(candidateResizable->pos());
616 if (candidateRect.left() <= rect.left() && candidateRect.right() >= rect.right()
617 && candidateRect.top() <= rect.top() && candidateRect.bottom() >= rect.bottom()) {
618 collidingItems.append(candidate);
619 }
620 }
621 }
622 break;
623 }
624 return collidingItems;
625 }
626
sceneActivated()627 void DiagramSceneModel::sceneActivated()
628 {
629 emit diagramSceneActivated(m_diagram);
630 }
631
keyPressEvent(QKeyEvent * event)632 void DiagramSceneModel::keyPressEvent(QKeyEvent *event)
633 {
634 m_latchController->keyPressEventLatching(event);
635 }
636
keyReleaseEvent(QKeyEvent * event)637 void DiagramSceneModel::keyReleaseEvent(QKeyEvent *event)
638 {
639 m_latchController->keyReleaseEventLatching(event);
640 }
641
mousePressEvent(QGraphicsSceneMouseEvent * event)642 void DiagramSceneModel::mousePressEvent(QGraphicsSceneMouseEvent *event)
643 {
644 updateFocusItem(Utils::toSet(m_graphicsScene->selectedItems()));
645 m_latchController->mousePressEventLatching(event);
646 mousePressEventReparenting(event);
647 }
648
mousePressEventReparenting(QGraphicsSceneMouseEvent * event)649 void DiagramSceneModel::mousePressEventReparenting(QGraphicsSceneMouseEvent *event)
650 {
651 // TODO add keyboard event handler to change cursor also on modifier change without move
652 mouseMoveEventReparenting(event);
653 }
654
mouseMoveEvent(QGraphicsSceneMouseEvent * event)655 void DiagramSceneModel::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
656 {
657 m_latchController->mouseMoveEventLatching(event);
658 mouseMoveEventReparenting(event);
659 }
660
mouseMoveEventReparenting(QGraphicsSceneMouseEvent * event)661 void DiagramSceneModel::mouseMoveEventReparenting(QGraphicsSceneMouseEvent *event)
662 {
663 if (event->modifiers() & Qt::AltModifier) {
664 // TODO show move cursor only if elements can be moved to underlaying element
665 foreach (QGraphicsView *view, m_graphicsScene->views()) {
666 // TODO find a better cursor that signals "move to this package"
667 view->setCursor(QCursor(Qt::OpenHandCursor));
668 }
669 } else {
670 foreach (QGraphicsView *view, m_graphicsScene->views())
671 view->unsetCursor();
672 }
673 }
674
mouseReleaseEvent(QGraphicsSceneMouseEvent * event)675 void DiagramSceneModel::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
676 {
677 m_latchController->mouseReleaseEventLatching(event);
678 mouseReleaseEventReparenting(event);
679 }
680
mouseReleaseEventReparenting(QGraphicsSceneMouseEvent * event)681 void DiagramSceneModel::mouseReleaseEventReparenting(QGraphicsSceneMouseEvent *event)
682 {
683 if (event->modifiers() & Qt::AltModifier) {
684 ModelController *modelController = diagramController()->modelController();
685 MPackage *newOwner = nullptr;
686 QSet<QGraphicsItem *> selectedItemSet = Utils::toSet(m_graphicsScene->selectedItems());
687 QList<QGraphicsItem *> itemsUnderMouse = m_graphicsScene->items(event->scenePos());
688 foreach (QGraphicsItem *item, itemsUnderMouse) {
689 if (!selectedItemSet.contains(item)) {
690 // the item may be any graphics item not matching to a DElement
691 DElement *element = m_itemToElementMap.value(item);
692 if (element && element->modelUid().isValid())
693 newOwner = modelController->findElement<MPackage>(element->modelUid());
694 }
695 if (newOwner)
696 break;
697 }
698 if (newOwner) {
699 foreach (QGraphicsItem *item, m_graphicsScene->selectedItems()) {
700 DElement *element = m_itemToElementMap.value(item);
701 QMT_ASSERT(element, return);
702 if (element->modelUid().isValid()) {
703 MObject *modelObject = modelController->findObject(element->modelUid());
704 if (modelObject) {
705 if (newOwner != modelObject->owner())
706 modelController->moveObject(newOwner, modelObject);
707 }
708 }
709 }
710 }
711 }
712 foreach (QGraphicsView *view, m_graphicsScene->views())
713 view->unsetCursor();
714 }
715
onBeginResetAllDiagrams()716 void DiagramSceneModel::onBeginResetAllDiagrams()
717 {
718 onBeginResetDiagram(m_diagram);
719 }
720
onEndResetAllDiagrams()721 void DiagramSceneModel::onEndResetAllDiagrams()
722 {
723 onEndResetDiagram(m_diagram);
724 }
725
onBeginResetDiagram(const MDiagram * diagram)726 void DiagramSceneModel::onBeginResetDiagram(const MDiagram *diagram)
727 {
728 QMT_CHECK(m_busyState == NotBusy);
729 m_busyState = ResetDiagram;
730 if (diagram == m_diagram)
731 clearGraphicsScene();
732 }
733
onEndResetDiagram(const MDiagram * diagram)734 void DiagramSceneModel::onEndResetDiagram(const MDiagram *diagram)
735 {
736 QMT_CHECK(m_busyState == ResetDiagram);
737 if (diagram == m_diagram) {
738 QMT_CHECK(m_graphicsItems.size() == 0);
739 // create all items and update graphics item from element initially
740 foreach (DElement *element, diagram->diagramElements()) {
741 QGraphicsItem *item = createGraphicsItem(element);
742 m_graphicsItems.append(item);
743 updateGraphicsItem(item, element);
744 }
745 // invalidate scene
746 m_graphicsScene->invalidate();
747 // update graphics items again so every item gets a correct list of colliding items
748 foreach (DElement *element, diagram->diagramElements())
749 updateGraphicsItem(m_elementToItemMap.value(element), element);
750 recalcSceneRectSize();
751 }
752 m_busyState = NotBusy;
753 }
754
onBeginUpdateElement(int row,const MDiagram * diagram)755 void DiagramSceneModel::onBeginUpdateElement(int row, const MDiagram *diagram)
756 {
757 Q_UNUSED(row)
758 Q_UNUSED(diagram)
759 QMT_CHECK(m_busyState == NotBusy);
760 m_busyState = UpdateElement;
761
762 }
763
onEndUpdateElement(int row,const MDiagram * diagram)764 void DiagramSceneModel::onEndUpdateElement(int row, const MDiagram *diagram)
765 {
766 QMT_CHECK(m_busyState == UpdateElement);
767 if (diagram == m_diagram) {
768 QGraphicsItem *item = m_graphicsItems.at(row);
769 updateGraphicsItem(item, diagram->diagramElements().at(row));
770 // TODO update all relations and their other end's (e.g. class name may have changed)
771 recalcSceneRectSize();
772 }
773 m_busyState = NotBusy;
774 }
775
onBeginInsertElement(int row,const MDiagram * diagram)776 void DiagramSceneModel::onBeginInsertElement(int row, const MDiagram *diagram)
777 {
778 Q_UNUSED(row)
779 Q_UNUSED(diagram)
780 QMT_CHECK(m_busyState == NotBusy);
781 m_busyState = InsertElement;
782 }
783
onEndInsertElement(int row,const MDiagram * diagram)784 void DiagramSceneModel::onEndInsertElement(int row, const MDiagram *diagram)
785 {
786 QMT_CHECK(m_busyState == InsertElement);
787 QGraphicsItem *item = nullptr;
788 if (diagram == m_diagram) {
789 DElement *element = diagram->diagramElements().at(row);
790 item = createGraphicsItem(element);
791 m_graphicsItems.insert(row, item);
792 updateGraphicsItem(item, element);
793 m_graphicsScene->invalidate();
794 updateGraphicsItem(item, element);
795 // if element is relation update ends
796 if (DRelation *dRelation = dynamic_cast<DRelation *>(element)) {
797 DElement *dEnd = m_diagramController->findElement(dRelation->endAUid(), diagram);
798 if (dEnd)
799 updateGraphicsItem(graphicsItem(dEnd), dEnd);
800 dEnd = m_diagramController->findElement(dRelation->endBUid(), diagram);
801 if (dEnd)
802 updateGraphicsItem(graphicsItem(dEnd), dEnd);
803 }
804 recalcSceneRectSize();
805 }
806 m_busyState = NotBusy;
807 }
808
onBeginRemoveElement(int row,const MDiagram * diagram)809 void DiagramSceneModel::onBeginRemoveElement(int row, const MDiagram *diagram)
810 {
811 QMT_CHECK(m_busyState == NotBusy);
812 if (diagram == m_diagram) {
813 // if element is relation store end's uid
814 m_relationEndsUid.clear();
815 if (DRelation *relation = dynamic_cast<DRelation *>(diagram->diagramElements().at(row))) {
816 m_relationEndsUid.append(relation->endAUid());
817 m_relationEndsUid.append(relation->endBUid());
818 }
819 QGraphicsItem *item = m_graphicsItems.takeAt(row);
820 deleteGraphicsItem(item, diagram->diagramElements().at(row));
821 recalcSceneRectSize();
822 }
823 m_busyState = RemoveElement;
824 }
825
onEndRemoveElement(int row,const MDiagram * diagram)826 void DiagramSceneModel::onEndRemoveElement(int row, const MDiagram *diagram)
827 {
828 Q_UNUSED(row)
829 Q_UNUSED(diagram)
830 QMT_CHECK(m_busyState == RemoveElement);
831 // update elements from store (see above)
832 for (const Uid &end_uid : qAsConst(m_relationEndsUid)) {
833 DElement *dEnd = m_diagramController->findElement(end_uid, diagram);
834 if (dEnd)
835 updateGraphicsItem(graphicsItem(dEnd), dEnd);
836 }
837 m_busyState = NotBusy;
838 }
839
onSelectionChanged()840 void DiagramSceneModel::onSelectionChanged()
841 {
842 bool selectionChanged = false;
843
844 // collect and update all primary selected items (selected by user)
845 QSet<QGraphicsItem *> newSelectedItems = Utils::toSet(m_graphicsScene->selectedItems());
846 updateFocusItem(newSelectedItems);
847 foreach (QGraphicsItem *item, m_selectedItems) {
848 if (!newSelectedItems.contains(item)) {
849 DElement *element = m_itemToElementMap.value(item);
850 updateGraphicsItem(item, element);
851 selectionChanged = true;
852 }
853 }
854 foreach (QGraphicsItem *item, newSelectedItems) {
855 if (!m_selectedItems.contains(item)) {
856 DElement *element = m_itemToElementMap.value(item);
857 updateGraphicsItem(item, element);
858 selectionChanged = true;
859 }
860 }
861 m_selectedItems = newSelectedItems;
862
863 // collect and update all secondary selected items
864 QSet<QGraphicsItem *> newSecondarySelectedItems;
865
866 // select all contained objects secondarily
867 foreach (QGraphicsItem *selectedItem, m_selectedItems) {
868 foreach (QGraphicsItem *item, collectCollidingObjectItems(selectedItem, CollidingInnerItems)) {
869 if (!item->isSelected() && dynamic_cast<ISelectable *>(item)
870 && item->collidesWithItem(selectedItem, Qt::ContainsItemBoundingRect)
871 && isInFrontOf(item, selectedItem)) {
872 QMT_CHECK(!m_selectedItems.contains(item));
873 newSecondarySelectedItems.insert(item);
874 }
875 }
876 }
877
878 // select more items secondarily
879 for (QGraphicsItem *selectedItem : qAsConst(m_selectedItems)) {
880 if (auto selectable = dynamic_cast<ISelectable *>(selectedItem)) {
881 QRectF boundary = selectable->getSecondarySelectionBoundary();
882 if (!boundary.isEmpty()) {
883 for (QGraphicsItem *item : qAsConst(m_graphicsItems)) {
884 if (auto secondarySelectable = dynamic_cast<ISelectable *>(item)) {
885 if (!item->isSelected() && !secondarySelectable->isSecondarySelected()) {
886 secondarySelectable->setBoundarySelected(boundary, true);
887 QMT_CHECK(!m_selectedItems.contains(item));
888 QMT_CHECK(!m_secondarySelectedItems.contains(item));
889 if (secondarySelectable->isSecondarySelected())
890 newSecondarySelectedItems.insert(item);
891 }
892 }
893 }
894 }
895 }
896 }
897
898
899 // select all relations where both ends are primary or secondary selected
900 foreach (DElement *element, m_diagram->diagramElements()) {
901 auto relation = dynamic_cast<DRelation *>(element);
902 if (relation) {
903 QGraphicsItem *relationItem = m_elementToItemMap.value(relation);
904 QMT_ASSERT(relationItem, return);
905 DObject *endAObject = m_diagramController->findElement<DObject>(relation->endAUid(), m_diagram);
906 QMT_ASSERT(endAObject, return);
907 QGraphicsItem *endAItem = m_elementToItemMap.value(endAObject);
908 QMT_ASSERT(endAItem, return);
909 DObject *endBObject = m_diagramController->findElement<DObject>(relation->endBUid(), m_diagram);
910 QMT_ASSERT(endBObject, return);
911 QGraphicsItem *endBItem = m_elementToItemMap.value(endBObject);
912 QMT_ASSERT(endBItem, return);
913 if (relationItem && !relationItem->isSelected()
914 && (m_selectedItems.contains(endAItem) || newSecondarySelectedItems.contains(endAItem))
915 && (m_selectedItems.contains(endBItem) || newSecondarySelectedItems.contains(endBItem))) {
916 QMT_CHECK(!m_selectedItems.contains(relationItem));
917 newSecondarySelectedItems.insert(relationItem);
918 }
919 }
920 }
921
922 foreach (QGraphicsItem *item, m_secondarySelectedItems) {
923 if (!newSecondarySelectedItems.contains(item)) {
924 auto selectable = dynamic_cast<ISelectable *>(item);
925 QMT_ASSERT(selectable, return);
926 selectable->setSecondarySelected(false);
927 selectionChanged = true;
928 }
929 }
930 foreach (QGraphicsItem *item, newSecondarySelectedItems) {
931 if (!m_secondarySelectedItems.contains(item)) {
932 auto selectable = dynamic_cast<ISelectable *>(item);
933 QMT_ASSERT(selectable, return);
934 selectable->setSecondarySelected(true);
935 selectionChanged = true;
936 }
937 }
938 m_secondarySelectedItems = newSecondarySelectedItems;
939
940 QMT_CHECK((m_selectedItems & m_secondarySelectedItems).isEmpty());
941
942 if (selectionChanged) {
943 m_diagramController->breakUndoChain();
944 emit selectionHasChanged(m_diagram);
945 }
946 }
947
clearGraphicsScene()948 void DiagramSceneModel::clearGraphicsScene()
949 {
950 m_graphicsScene->clearSelection();
951 m_graphicsItems.clear();
952 m_itemToElementMap.clear();
953 m_elementToItemMap.clear();
954 m_selectedItems.clear();
955 m_secondarySelectedItems.clear();
956 m_focusItem = nullptr;
957 // save extra items from being deleted
958 removeExtraSceneItems();
959 m_graphicsScene->clear();
960 addExtraSceneItems();
961 }
962
removeExtraSceneItems()963 void DiagramSceneModel::removeExtraSceneItems()
964 {
965 m_latchController->removeFromGraphicsScene(m_graphicsScene);
966 m_graphicsScene->removeItem(m_originItem);
967 }
968
addExtraSceneItems()969 void DiagramSceneModel::addExtraSceneItems()
970 {
971 m_graphicsScene->addItem(m_originItem);
972 m_latchController->addToGraphicsScene(m_graphicsScene);
973 }
974
saveSelectionStatusBeforeExport(bool exportSelectedElements,DiagramSceneModel::SelectionStatus * status)975 void DiagramSceneModel::saveSelectionStatusBeforeExport(bool exportSelectedElements, DiagramSceneModel::SelectionStatus *status)
976 {
977 status->m_selectedItems = m_selectedItems;
978 status->m_secondarySelectedItems = m_secondarySelectedItems;
979 status->m_focusItem = m_focusItem;
980 status->m_exportSelectedElements = exportSelectedElements;
981
982 // Selections would also render to the clipboard
983 m_graphicsScene->clearSelection();
984 foreach (QGraphicsItem *item, m_graphicsItems) {
985 if (IEditable *editItem = dynamic_cast<IEditable *>(item)) {
986 if (editItem->isEditing()) {
987 status->m_editItem = editItem;
988 editItem->finishEdit();
989 }
990 }
991 }
992 removeExtraSceneItems();
993
994 foreach (QGraphicsItem *item, m_graphicsItems) {
995 if (!exportSelectedElements
996 || status->m_selectedItems.contains(item)
997 || status->m_secondarySelectedItems.contains(item)) {
998 // TODO introduce interface for calculating export boundary
999 if (SwimlaneItem *swimlane = dynamic_cast<SwimlaneItem *>(item)) {
1000 QRectF boundary = item->mapRectToScene(swimlane->boundingRect());
1001 if (swimlane->swimlane()->isHorizontal()) {
1002 boundary.setLeft(status->m_sceneBoundingRect.left());
1003 boundary.setRight(status->m_sceneBoundingRect.right());
1004 } else {
1005 boundary.setTop(status->m_sceneBoundingRect.top());
1006 boundary.setBottom(status->m_sceneBoundingRect.bottom());
1007 }
1008 status->m_sceneBoundingRect |= boundary;
1009 } else {
1010 status->m_sceneBoundingRect |= item->mapRectToScene(item->boundingRect());
1011 }
1012 } else {
1013 item->hide();
1014 }
1015 }
1016 }
1017
restoreSelectedStatusAfterExport(const DiagramSceneModel::SelectionStatus & status)1018 void DiagramSceneModel::restoreSelectedStatusAfterExport(const DiagramSceneModel::SelectionStatus &status)
1019 {
1020 if (status.m_exportSelectedElements) {
1021 // TODO once an annotation item had focus the call to show() will give it focus again. Bug in Qt?
1022 foreach (QGraphicsItem *item, m_graphicsItems)
1023 item->show();
1024 }
1025
1026 addExtraSceneItems();
1027
1028 foreach (QGraphicsItem *item, status.m_selectedItems)
1029 item->setSelected(true);
1030
1031 // reset focus item
1032 if (status.m_focusItem) {
1033 ISelectable *selectable = dynamic_cast<ISelectable *>(status.m_focusItem);
1034 if (selectable) {
1035 selectable->setFocusSelected(true);
1036 m_focusItem = status.m_focusItem;
1037 }
1038 }
1039
1040 // reset edit item
1041 if (status.m_editItem)
1042 status.m_editItem->edit();
1043 }
1044
recalcSceneRectSize()1045 void DiagramSceneModel::recalcSceneRectSize()
1046 {
1047 QRectF sceneRect = m_originItem->mapRectToScene(m_originItem->boundingRect());
1048 for (QGraphicsItem *item : qAsConst(m_graphicsItems)) {
1049 // TODO use an interface to update sceneRect by item
1050 if (!dynamic_cast<SwimlaneItem *>(item))
1051 sceneRect |= item->mapRectToScene(item->boundingRect());
1052 }
1053 m_sceneRect = sceneRect;
1054 emit sceneRectChanged(sceneRect);
1055 }
1056
createGraphicsItem(DElement * element)1057 QGraphicsItem *DiagramSceneModel::createGraphicsItem(DElement *element)
1058 {
1059 QMT_ASSERT(element, return nullptr);
1060 QMT_CHECK(!m_elementToItemMap.contains(element));
1061
1062 CreationVisitor visitor(this);
1063 element->accept(&visitor);
1064 QGraphicsItem *item = visitor.createdGraphicsItem();
1065 m_itemToElementMap.insert(item, element);
1066 m_elementToItemMap.insert(element, item);
1067 m_graphicsScene->addItem(item);
1068 return item;
1069 }
1070
updateGraphicsItem(QGraphicsItem * item,DElement * element)1071 void DiagramSceneModel::updateGraphicsItem(QGraphicsItem *item, DElement *element)
1072 {
1073 QMT_ASSERT(item, return);
1074 QMT_ASSERT(element, return);
1075
1076 UpdateVisitor visitor(item, this);
1077 element->accept(&visitor);
1078 }
1079
deleteGraphicsItem(QGraphicsItem * item,DElement * element)1080 void DiagramSceneModel::deleteGraphicsItem(QGraphicsItem *item, DElement *element)
1081 {
1082 QMT_CHECK(m_elementToItemMap.contains(element));
1083 QMT_CHECK(m_itemToElementMap.contains(item));
1084 if (item == m_focusItem)
1085 unsetFocusItem();
1086 m_graphicsScene->removeItem(item);
1087 m_elementToItemMap.remove(element);
1088 m_itemToElementMap.remove(item);
1089 m_selectedItems.remove(item);
1090 m_secondarySelectedItems.remove(item);
1091 delete item;
1092 }
1093
updateFocusItem(const QSet<QGraphicsItem * > & selectedItems)1094 void DiagramSceneModel::updateFocusItem(const QSet<QGraphicsItem *> &selectedItems)
1095 {
1096 QGraphicsItem *mouseGrabberItem = m_graphicsScene->mouseGrabberItem();
1097 QGraphicsItem *focusItem = nullptr;
1098 ISelectable *selectable = nullptr;
1099
1100 if (mouseGrabberItem && selectedItems.contains(mouseGrabberItem)) {
1101 selectable = dynamic_cast<ISelectable *>(mouseGrabberItem);
1102 if (selectable)
1103 focusItem = mouseGrabberItem;
1104 }
1105 if (focusItem && focusItem != m_focusItem) {
1106 unsetFocusItem();
1107 selectable->setFocusSelected(true);
1108 m_focusItem = focusItem;
1109 } else if (m_focusItem && !selectedItems.contains(m_focusItem)) {
1110 unsetFocusItem();
1111 }
1112 }
1113
unsetFocusItem()1114 void DiagramSceneModel::unsetFocusItem()
1115 {
1116 if (m_focusItem) {
1117 if (auto oldSelectable = dynamic_cast<ISelectable *>(m_focusItem))
1118 oldSelectable->setFocusSelected(false);
1119 else
1120 QMT_CHECK(false);
1121 m_focusItem = nullptr;
1122 }
1123 }
1124
1125 } // namespace qmt
1126