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 "diagramscenecontroller.h"
27
28 #include "qmt/controller/namecontroller.h"
29 #include "qmt/controller/undocontroller.h"
30 #include "qmt/diagram_controller/dfactory.h"
31 #include "qmt/diagram_controller/diagramcontroller.h"
32 #include "qmt/diagram_controller/dselection.h"
33 #include "qmt/diagram/dannotation.h"
34 #include "qmt/diagram/dassociation.h"
35 #include "qmt/diagram/dboundary.h"
36 #include "qmt/diagram/dclass.h"
37 #include "qmt/diagram/dconnection.h"
38 #include "qmt/diagram/ditem.h"
39 #include "qmt/diagram/dpackage.h"
40 #include "qmt/diagram/drelation.h"
41 #include "qmt/diagram/dswimlane.h"
42 #include "qmt/diagram_ui/diagram_mime_types.h"
43 #include "qmt/model_controller/modelcontroller.h"
44 #include "qmt/model_controller/mselection.h"
45 #include "qmt/model_controller/mvoidvisitor.h"
46 #include "qmt/model/massociation.h"
47 #include "qmt/model/mcanvasdiagram.h"
48 #include "qmt/model/mclass.h"
49 #include "qmt/model/mcomponent.h"
50 #include "qmt/model/mconnection.h"
51 #include "qmt/model/mdependency.h"
52 #include "qmt/model/mdiagram.h"
53 #include "qmt/model/minheritance.h"
54 #include "qmt/model/mitem.h"
55 #include "qmt/model/mobject.h"
56 #include "qmt/model/mpackage.h"
57 #include "qmt/model/msourceexpansion.h"
58 #include "qmt/stereotype/customrelation.h"
59 #include "qmt/stereotype/stereotypecontroller.h"
60 #include "qmt/tasks/alignonrastervisitor.h"
61 #include "qmt/tasks/isceneinspector.h"
62 #include "qmt/tasks/voidelementtasks.h"
63
64 #include <QMenu>
65 #include <QFileInfo>
66 #include <QDir>
67 #include <QQueue>
68 #include <QPair>
69
70 #include <QtMath>
71
72 namespace qmt {
73
74 namespace {
75 VoidElementTasks dummyElementTasks;
76 }
77
78 class DiagramSceneController::AcceptRelationVisitor : public MVoidConstVisitor
79 {
80 public:
AcceptRelationVisitor(StereotypeController * stereotypeController,const MRelation * relation,RelationEnd relationEnd)81 AcceptRelationVisitor(StereotypeController *stereotypeController, const MRelation *relation,
82 RelationEnd relationEnd)
83 : m_stereotypeController(stereotypeController),
84 m_relation(relation),
85 m_relationEnd(relationEnd)
86 {
87 }
88
isAccepted() const89 bool isAccepted() const { return m_accepted; }
90
visitMObject(const MObject * object)91 void visitMObject(const MObject *object) override
92 {
93 Q_UNUSED(object)
94 if (auto connection = dynamic_cast<const MConnection *>(m_relation)) {
95 CustomRelation customRelation = m_stereotypeController->findCustomRelation(connection->customRelationId());
96 if (!customRelation.isNull()) {
97 QMT_ASSERT(customRelation.element() == CustomRelation::Element::Relation, return);
98 CustomRelation::End customEnd = m_relationEnd == EndA ? customRelation.endA() : customRelation.endB();
99 QStringList endItems = customEnd.endItems();
100 if (endItems.isEmpty())
101 endItems = customRelation.endItems();
102 QString stereotypeIconId = m_stereotypeController->findStereotypeIconId(StereotypeIcon::ElementItem, object->stereotypes());
103 if (stereotypeIconId.isEmpty() && !m_variety.isEmpty())
104 stereotypeIconId = m_stereotypeController->findStereotypeIconId(StereotypeIcon::ElementItem, {m_variety});
105 m_accepted = endItems.contains(stereotypeIconId);
106 }
107 }
108 if (!m_accepted)
109 m_accepted = dynamic_cast<const MDependency *>(m_relation) != nullptr;
110 }
111
visitMClass(const MClass * klass)112 void visitMClass(const MClass *klass) override
113 {
114 m_accepted = dynamic_cast<const MInheritance *>(m_relation) != nullptr
115 || dynamic_cast<const MAssociation *>(m_relation) != nullptr;
116 if (!m_accepted)
117 visitMObject(klass);
118 }
119
visitMItem(const MItem * item)120 void visitMItem(const MItem *item) override
121 {
122 m_variety = item->variety();
123 visitMObject(item);
124 }
125
126 private:
127 StereotypeController *m_stereotypeController = nullptr;
128 const MRelation *m_relation = nullptr;
129 RelationEnd m_relationEnd = EndA;
130 QString m_variety;
131 bool m_accepted = false;
132 };
133
DiagramSceneController(QObject * parent)134 DiagramSceneController::DiagramSceneController(QObject *parent)
135 : QObject(parent),
136 m_elementTasks(&dummyElementTasks)
137 {
138 }
139
~DiagramSceneController()140 DiagramSceneController::~DiagramSceneController()
141 {
142 }
143
setModelController(ModelController * modelController)144 void DiagramSceneController::setModelController(ModelController *modelController)
145 {
146 if (m_modelController == modelController)
147 return;
148 if (m_modelController) {
149 disconnect(m_modelController, nullptr, this, nullptr);
150 m_modelController = nullptr;
151 }
152 if (modelController)
153 m_modelController = modelController;
154 }
155
setDiagramController(DiagramController * diagramController)156 void DiagramSceneController::setDiagramController(DiagramController *diagramController)
157 {
158 if (m_diagramController == diagramController)
159 return;
160 if (m_diagramController) {
161 disconnect(m_diagramController, nullptr, this, nullptr);
162 m_diagramController = nullptr;
163 }
164 if (diagramController)
165 m_diagramController = diagramController;
166 }
167
setStereotypeController(StereotypeController * stereotypeController)168 void DiagramSceneController::setStereotypeController(StereotypeController *stereotypeController)
169 {
170 m_stereotypeController = stereotypeController;
171 }
172
setElementTasks(IElementTasks * elementTasks)173 void DiagramSceneController::setElementTasks(IElementTasks *elementTasks)
174 {
175 m_elementTasks = elementTasks;
176 }
177
setSceneInspector(ISceneInspector * sceneInspector)178 void DiagramSceneController::setSceneInspector(ISceneInspector *sceneInspector)
179 {
180 m_sceneInspector = sceneInspector;
181 }
182
deleteFromDiagram(const DSelection & dselection,MDiagram * diagram)183 void DiagramSceneController::deleteFromDiagram(const DSelection &dselection, MDiagram *diagram)
184 {
185 if (!dselection.isEmpty()) {
186 MSelection mselection;
187 DSelection remainingDselection;
188 foreach (const DSelection::Index &index, dselection.indices()) {
189 DElement *delement = m_diagramController->findElement(index.elementKey(), diagram);
190 QMT_ASSERT(delement, return);
191 if (delement->modelUid().isValid()) {
192 MElement *melement = m_modelController->findElement(delement->modelUid());
193 QMT_ASSERT(melement, return);
194 if (melement->owner())
195 mselection.append(melement->uid(), melement->owner()->uid());
196 } else {
197 remainingDselection.append(index);
198 }
199 }
200 if (!remainingDselection.isEmpty())
201 m_diagramController->deleteElements(remainingDselection, diagram);
202 if (!mselection.isEmpty())
203 m_modelController->deleteElements(mselection);
204 }
205 }
206
createDependency(DObject * endAObject,DObject * endBObject,const QList<QPointF> & intermediatePoints,MDiagram * diagram)207 void DiagramSceneController::createDependency(DObject *endAObject, DObject *endBObject,
208 const QList<QPointF> &intermediatePoints, MDiagram *diagram)
209 {
210 m_diagramController->undoController()->beginMergeSequence(tr("Create Dependency"));
211
212 MObject *endAModelObject = m_modelController->findObject<MObject>(endAObject->modelUid());
213 QMT_ASSERT(endAModelObject, return);
214 MObject *endBModelObject = m_modelController->findObject<MObject>(endBObject->modelUid());
215 QMT_ASSERT(endBModelObject, return);
216
217 if (endAModelObject == endBModelObject)
218 return;
219
220 auto modelDependency = new MDependency();
221 modelDependency->setEndAUid(endAModelObject->uid());
222 modelDependency->setEndBUid(endBModelObject->uid());
223 modelDependency->setDirection(MDependency::AToB);
224 m_modelController->addRelation(endAModelObject, modelDependency);
225
226 DRelation *relation = addRelation(modelDependency, intermediatePoints, diagram);
227
228 m_diagramController->undoController()->endMergeSequence();
229
230 if (relation)
231 emit newElementCreated(relation, diagram);
232 }
233
createInheritance(DClass * derivedClass,DClass * baseClass,const QList<QPointF> & intermediatePoints,MDiagram * diagram)234 void DiagramSceneController::createInheritance(DClass *derivedClass, DClass *baseClass,
235 const QList<QPointF> &intermediatePoints, MDiagram *diagram)
236 {
237 m_diagramController->undoController()->beginMergeSequence(tr("Create Inheritance"));
238
239 MClass *derivedModelClass = m_modelController->findObject<MClass>(derivedClass->modelUid());
240 QMT_ASSERT(derivedModelClass, return);
241 MClass *baseModelClass = m_modelController->findObject<MClass>(baseClass->modelUid());
242 QMT_ASSERT(baseModelClass, return);
243
244 if (derivedModelClass == baseModelClass)
245 return;
246
247 auto modelInheritance = new MInheritance();
248 modelInheritance->setDerived(derivedModelClass->uid());
249 modelInheritance->setBase(baseModelClass->uid());
250 m_modelController->addRelation(derivedModelClass, modelInheritance);
251
252 DRelation *relation = addRelation(modelInheritance, intermediatePoints, diagram);
253
254 m_diagramController->undoController()->endMergeSequence();
255
256 if (relation)
257 emit newElementCreated(relation, diagram);
258 }
259
createAssociation(DClass * endAClass,DClass * endBClass,const QList<QPointF> & intermediatePoints,MDiagram * diagram,std::function<void (MAssociation *,DAssociation *)> custom)260 void DiagramSceneController::createAssociation(DClass *endAClass, DClass *endBClass,
261 const QList<QPointF> &intermediatePoints, MDiagram *diagram,
262 std::function<void (MAssociation*, DAssociation*)> custom)
263 {
264 m_diagramController->undoController()->beginMergeSequence(tr("Create Association"));
265
266 MClass *endAModelObject = m_modelController->findObject<MClass>(endAClass->modelUid());
267 QMT_ASSERT(endAModelObject, return);
268 MClass *endBModelObject = m_modelController->findObject<MClass>(endBClass->modelUid());
269 QMT_ASSERT(endBModelObject, return);
270
271 // TODO allow self assignment with just one intermediate point and a nice round arrow
272 if (endAModelObject == endBModelObject && intermediatePoints.count() < 2)
273 return;
274
275 auto modelAssociation = new MAssociation();
276 modelAssociation->setEndAUid(endAModelObject->uid());
277 MAssociationEnd endA = modelAssociation->endA();
278 endA.setNavigable(true);
279 modelAssociation->setEndA(endA);
280 modelAssociation->setEndBUid(endBModelObject->uid());
281 m_modelController->addRelation(endAModelObject, modelAssociation);
282
283 DRelation *relation = addRelation(modelAssociation, intermediatePoints, diagram);
284 DAssociation *diagramAssociation = dynamic_cast<DAssociation *>(relation);
285 QMT_CHECK(diagramAssociation);
286
287 if (custom)
288 custom(modelAssociation, diagramAssociation);
289
290 m_diagramController->undoController()->endMergeSequence();
291
292 if (relation)
293 emit newElementCreated(relation, diagram);
294 }
295
createConnection(const QString & customRelationId,DObject * endAObject,DObject * endBObject,const QList<QPointF> & intermediatePoints,MDiagram * diagram,std::function<void (MConnection *,DConnection *)> custom)296 void DiagramSceneController::createConnection(const QString &customRelationId,
297 DObject *endAObject, DObject *endBObject,
298 const QList<QPointF> &intermediatePoints, MDiagram *diagram,
299 std::function<void (MConnection *, DConnection *)> custom)
300 {
301 m_diagramController->undoController()->beginMergeSequence(tr("Create Connection"));
302
303 MObject *endAModelObject = m_modelController->findObject<MObject>(endAObject->modelUid());
304 QMT_CHECK(endAModelObject);
305 MObject *endBModelObject = m_modelController->findObject<MObject>(endBObject->modelUid());
306 QMT_CHECK(endBModelObject);
307
308 // TODO allow self assignment with just one intermediate point and a nice round arrow
309 if (endAModelObject == endBModelObject && intermediatePoints.count() < 2)
310 return;
311
312 auto modelConnection = new MConnection();
313 modelConnection->setCustomRelationId(customRelationId);
314 modelConnection->setEndAUid(endAModelObject->uid());
315 MConnectionEnd endA = modelConnection->endA();
316 endA.setNavigable(true);
317 modelConnection->setEndA(endA);
318 modelConnection->setEndBUid(endBModelObject->uid());
319 m_modelController->addRelation(endAModelObject, modelConnection);
320
321 DRelation *relation = addRelation(modelConnection, intermediatePoints, diagram);
322 DConnection *diagramConnection = dynamic_cast<DConnection *>(relation);
323 QMT_CHECK(diagramConnection);
324
325 if (custom)
326 custom(modelConnection, diagramConnection);
327
328 m_diagramController->undoController()->endMergeSequence();
329
330 if (relation)
331 emit newElementCreated(relation, diagram);
332 }
333
relocateRelationEndA(DRelation * relation,DObject * targetObject)334 bool DiagramSceneController::relocateRelationEndA(DRelation *relation, DObject *targetObject)
335 {
336 return relocateRelationEnd(relation, targetObject, EndA, &MRelation::endAUid, &MRelation::setEndAUid);
337 }
338
relocateRelationEndB(DRelation * relation,DObject * targetObject)339 bool DiagramSceneController::relocateRelationEndB(DRelation *relation, DObject *targetObject)
340 {
341 return relocateRelationEnd(relation, targetObject, EndB, &MRelation::endBUid, &MRelation::setEndBUid);
342 }
343
isAddingAllowed(const Uid & modelElementKey,MDiagram * diagram)344 bool DiagramSceneController::isAddingAllowed(const Uid &modelElementKey, MDiagram *diagram)
345 {
346 MElement *modelElement = m_modelController->findElement(modelElementKey);
347 QMT_ASSERT(modelElement, return false);
348 if (m_diagramController->hasDelegate(modelElement, diagram))
349 return false;
350 if (auto modelRelation = dynamic_cast<MRelation *>(modelElement)) {
351 MObject *endAModelObject = m_modelController->findObject(modelRelation->endAUid());
352 QMT_ASSERT(endAModelObject, return false);
353 DObject *endADiagramObject = m_diagramController->findDelegate<DObject>(endAModelObject, diagram);
354 if (!endADiagramObject)
355 return false;
356
357 MObject *endBModelObject = m_modelController->findObject(modelRelation->endBUid());
358 QMT_ASSERT(endBModelObject, return false);
359 DObject *endBDiagramObject = m_diagramController->findDelegate<DObject>(endBModelObject, diagram);
360 if (!endBDiagramObject)
361 return false;
362 }
363 return true;
364 }
365
addExistingModelElement(const Uid & modelElementKey,const QPointF & pos,MDiagram * diagram)366 void DiagramSceneController::addExistingModelElement(const Uid &modelElementKey, const QPointF &pos, MDiagram *diagram)
367 {
368 DElement *element = addModelElement(modelElementKey, pos, diagram);
369 if (element)
370 emit elementAdded(element, diagram);
371 }
372
dropNewElement(const QString & newElementId,const QString & name,const QString & stereotype,DElement * topMostElementAtPos,const QPointF & pos,MDiagram * diagram,const QPoint & viewPos,const QSize & viewSize)373 void DiagramSceneController::dropNewElement(const QString &newElementId, const QString &name, const QString &stereotype,
374 DElement *topMostElementAtPos, const QPointF &pos, MDiagram *diagram,
375 const QPoint &viewPos, const QSize &viewSize)
376 {
377 if (newElementId == QLatin1String(ELEMENT_TYPE_ANNOTATION)) {
378 auto annotation = new DAnnotation();
379 annotation->setPos(pos - QPointF(10.0, 10.0));
380 m_diagramController->addElement(annotation, diagram);
381 alignOnRaster(annotation, diagram);
382 emit newElementCreated(annotation, diagram);
383 } else if (newElementId == QLatin1String(ELEMENT_TYPE_BOUNDARY)) {
384 auto boundary = new DBoundary();
385 boundary->setPos(pos);
386 m_diagramController->addElement(boundary, diagram);
387 alignOnRaster(boundary, diagram);
388 emit newElementCreated(boundary, diagram);
389 } else if (newElementId == QLatin1String(ELEMENT_TYPE_SWIMLANE)) {
390 auto swimlane = new DSwimlane();
391 qreal x = static_cast<qreal>(viewPos.x()) / viewSize.width();
392 qreal y = static_cast<qreal>(viewPos.y()) / viewSize.height();
393 bool horizontal = (y > x && (1-y) > x) || (y <= x && (1-y) <= x);
394 swimlane->setHorizontal(horizontal);
395 swimlane->setPos(horizontal ? pos.y() : pos.x());
396 m_diagramController->addElement(swimlane, diagram);
397 alignOnRaster(swimlane, diagram);
398 emit newElementCreated(swimlane, diagram);
399 } else {
400 MPackage *parentPackage = findSuitableParentPackage(topMostElementAtPos, diagram);
401 MObject *newObject = nullptr;
402 if (newElementId == QLatin1String(ELEMENT_TYPE_PACKAGE)) {
403 auto package = new MPackage();
404 if (!stereotype.isEmpty())
405 package->setStereotypes({stereotype});
406 newObject = package;
407 } else if (newElementId == QLatin1String(ELEMENT_TYPE_COMPONENT)) {
408 auto component = new MComponent();
409 if (!stereotype.isEmpty())
410 component->setStereotypes({stereotype});
411 newObject = component;
412 } else if (newElementId == QLatin1String(ELEMENT_TYPE_CLASS)) {
413 auto klass = new MClass();
414 if (!stereotype.isEmpty())
415 klass->setStereotypes({stereotype});
416 newObject = klass;
417 } else if (newElementId == QLatin1String(ELEMENT_TYPE_ITEM)) {
418 auto item = new MItem();
419 if (!stereotype.isEmpty()) {
420 item->setVariety(stereotype);
421 item->setVarietyEditable(false);
422 }
423 newObject = item;
424 }
425 if (newObject) {
426 newObject->setName(name);
427 dropNewModelElement(newObject, parentPackage, pos, diagram);
428 }
429 }
430 }
431
dropNewModelElement(MObject * modelObject,MPackage * parentPackage,const QPointF & pos,MDiagram * diagram)432 void DiagramSceneController::dropNewModelElement(MObject *modelObject, MPackage *parentPackage, const QPointF &pos,
433 MDiagram *diagram)
434 {
435 m_modelController->undoController()->beginMergeSequence(tr("Drop Element"));
436 m_modelController->addObject(parentPackage, modelObject);
437 DElement *element = addObject(modelObject, pos, diagram);
438 m_modelController->undoController()->endMergeSequence();
439 if (element)
440 emit newElementCreated(element, diagram);
441 }
442
addRelatedElements(const DSelection & selection,MDiagram * diagram)443 void DiagramSceneController::addRelatedElements(const DSelection &selection, MDiagram *diagram)
444 {
445 m_diagramController->undoController()->beginMergeSequence(tr("Add Related Element"));
446 foreach (const DSelection::Index &index, selection.indices()) {
447 DElement *delement = m_diagramController->findElement(index.elementKey(), diagram);
448 QMT_ASSERT(delement, return);
449 DObject *dobject = dynamic_cast<DObject *>(delement);
450 if (dobject && dobject->modelUid().isValid()) {
451 MObject *mobject = m_modelController->findElement<MObject>(delement->modelUid());
452 if (mobject) {
453 qreal dAngle = 360.0 / 11.5;
454 qreal dRadius = 100.0;
455 const QList<MRelation *> relations = m_modelController->findRelationsOfObject(mobject);
456 int count = 0;
457 for (MRelation *relation : relations) {
458 if (relation->endAUid() != mobject->uid() || relation->endBUid() != mobject->uid())
459 ++count;
460 }
461 if (count <= 12) {
462 dAngle = 360.0 / 12.0;
463 dRadius = 0.0;
464 }
465 qreal radius = 200.0;
466 qreal angle = 0.0;
467 for (MRelation *relation : relations) {
468 QPointF pos(dobject->pos());
469 pos += QPointF(radius * sin(angle / 180 * M_PI), -radius * cos(angle / 180 * M_PI));
470 bool added = false;
471 if (relation->endAUid() != mobject->uid())
472 added = addModelElement(relation->endAUid(), pos, diagram) != nullptr;
473 else if (relation->endBUid() != mobject->uid())
474 added = addModelElement(relation->endBUid(), pos, diagram) != nullptr;
475 if (added) {
476 radius += dRadius / (360.0 / dAngle);
477 angle += dAngle;
478 }
479 }
480 }
481 }
482 }
483 m_diagramController->undoController()->endMergeSequence();
484 }
485
findSuitableParentPackage(DElement * topmostDiagramElement,MDiagram * diagram)486 MPackage *DiagramSceneController::findSuitableParentPackage(DElement *topmostDiagramElement, MDiagram *diagram)
487 {
488 MPackage *parentPackage = nullptr;
489 if (auto diagramPackage = dynamic_cast<DPackage *>(topmostDiagramElement)) {
490 parentPackage = m_modelController->findObject<MPackage>(diagramPackage->modelUid());
491 } else if (auto diagramObject = dynamic_cast<DObject *>(topmostDiagramElement)) {
492 MObject *modelObject = m_modelController->findObject(diagramObject->modelUid());
493 if (modelObject)
494 parentPackage = dynamic_cast<MPackage *>(modelObject->owner());
495 }
496 if (!parentPackage && diagram)
497 parentPackage = dynamic_cast<MPackage *>(diagram->owner());
498 if (!parentPackage)
499 parentPackage = m_modelController->rootPackage();
500 return parentPackage;
501 }
502
findDiagramBySearchId(MPackage * package,const QString & diagramName)503 MDiagram *DiagramSceneController::findDiagramBySearchId(MPackage *package, const QString &diagramName)
504 {
505 QString diagramSearchId = NameController::calcElementNameSearchId(diagramName);
506 for (const Handle<MObject> &handle : package->children()) {
507 if (handle.hasTarget()) {
508 if (auto diagram = dynamic_cast<MDiagram *>(handle.target())) {
509 if (NameController::calcElementNameSearchId(diagram->name()) == diagramSearchId)
510 return diagram;
511 }
512 }
513 }
514 return nullptr;
515 }
516
517 namespace {
518
alignObjectLeft(DObject * object,DObject * otherObject)519 QPointF alignObjectLeft(DObject *object, DObject *otherObject)
520 {
521 qreal left = object->pos().x() + object->rect().left();
522 QPointF pos = otherObject->pos();
523 qreal otherLeft = pos.x() + otherObject->rect().left();
524 qreal delta = otherLeft - left;
525 pos.setX(pos.x() - delta);
526 return pos;
527 }
528
alignObjectRight(DObject * object,DObject * otherObject)529 QPointF alignObjectRight(DObject *object, DObject *otherObject)
530 {
531 qreal right = object->pos().x() + object->rect().right();
532 QPointF pos = otherObject->pos();
533 qreal otherRight = pos.x() + otherObject->rect().right();
534 qreal delta = otherRight - right;
535 pos.setX(pos.x() - delta);
536 return pos;
537 }
538
alignObjectHCenter(DObject * object,DObject * otherObject)539 QPointF alignObjectHCenter(DObject *object, DObject *otherObject)
540 {
541 qreal center = object->pos().x();
542 QPointF pos = otherObject->pos();
543 qreal otherCenter = pos.x();
544 qreal delta = otherCenter - center;
545 pos.setX(pos.x() - delta);
546 return pos;
547 }
548
alignObjectTop(DObject * object,DObject * otherObject)549 QPointF alignObjectTop(DObject *object, DObject *otherObject)
550 {
551 qreal top = object->pos().y() + object->rect().top();
552 QPointF pos = otherObject->pos();
553 qreal otherTop = pos.y() + otherObject->rect().top();
554 qreal delta = otherTop - top;
555 pos.setY(pos.y() - delta);
556 return pos;
557 }
558
alignObjectBottom(DObject * object,DObject * otherObject)559 QPointF alignObjectBottom(DObject *object, DObject *otherObject)
560 {
561 qreal bottom = object->pos().y() + object->rect().bottom();
562 QPointF pos = otherObject->pos();
563 qreal otherBottom = pos.y() + otherObject->rect().bottom();
564 qreal delta = otherBottom - bottom;
565 pos.setY(pos.y() - delta);
566 return pos;
567 }
568
alignObjectVCenter(DObject * object,DObject * otherObject)569 QPointF alignObjectVCenter(DObject *object, DObject *otherObject)
570 {
571 qreal center = object->pos().y();
572 QPointF pos = otherObject->pos();
573 qreal otherCenter = pos.y();
574 qreal delta = otherCenter - center;
575 pos.setY(pos.y() - delta);
576 return pos;
577 }
578
alignObjectWidth(DObject * object,const QSizeF & size)579 QRectF alignObjectWidth(DObject *object, const QSizeF &size)
580 {
581 QRectF rect = object->rect();
582 rect.setX(-size.width() / 2.0);
583 rect.setWidth(size.width());
584 return rect;
585 }
586
alignObjectHeight(DObject * object,const QSizeF & size)587 QRectF alignObjectHeight(DObject *object, const QSizeF &size)
588 {
589 QRectF rect = object->rect();
590 rect.setY(-size.height() / 2.0);
591 rect.setHeight(size.height());
592 return rect;
593 }
594
alignObjectSize(DObject * object,const QSizeF & size)595 QRectF alignObjectSize(DObject *object, const QSizeF &size)
596 {
597 Q_UNUSED(object)
598
599 QRectF rect;
600 rect.setX(-size.width() / 2.0);
601 rect.setY(-size.height() / 2.0);
602 rect.setWidth(size.width());
603 rect.setHeight(size.height());
604 return rect;
605 }
606
607 }
608
alignLeft(DObject * object,const DSelection & selection,MDiagram * diagram)609 void DiagramSceneController::alignLeft(DObject *object, const DSelection &selection, MDiagram *diagram)
610 {
611 alignPosition(object, selection, alignObjectLeft, diagram);
612 }
613
alignRight(DObject * object,const DSelection & selection,MDiagram * diagram)614 void DiagramSceneController::alignRight(DObject *object, const DSelection &selection, MDiagram *diagram)
615 {
616 alignPosition(object, selection, alignObjectRight, diagram);
617 }
618
alignHCenter(DObject * object,const DSelection & selection,MDiagram * diagram)619 void DiagramSceneController::alignHCenter(DObject *object, const DSelection &selection, MDiagram *diagram)
620 {
621 alignPosition(object, selection, alignObjectHCenter, diagram);
622 }
623
alignTop(DObject * object,const DSelection & selection,MDiagram * diagram)624 void DiagramSceneController::alignTop(DObject *object, const DSelection &selection, MDiagram *diagram)
625 {
626 alignPosition(object, selection, alignObjectTop, diagram);
627 }
628
alignBottom(DObject * object,const DSelection & selection,MDiagram * diagram)629 void DiagramSceneController::alignBottom(DObject *object, const DSelection &selection, MDiagram *diagram)
630 {
631 alignPosition(object, selection, alignObjectBottom, diagram);
632 }
633
alignVCenter(DObject * object,const DSelection & selection,MDiagram * diagram)634 void DiagramSceneController::alignVCenter(DObject *object, const DSelection &selection, MDiagram *diagram)
635 {
636 alignPosition(object, selection, alignObjectVCenter, diagram);
637 }
638
alignWidth(DObject * object,const DSelection & selection,const QSizeF & minimumSize,MDiagram * diagram)639 void DiagramSceneController::alignWidth(DObject *object, const DSelection &selection, const QSizeF &minimumSize,
640 MDiagram *diagram)
641 {
642 alignSize(object, selection, minimumSize, alignObjectWidth, diagram);
643 }
644
alignHeight(DObject * object,const DSelection & selection,const QSizeF & minimumSize,MDiagram * diagram)645 void DiagramSceneController::alignHeight(DObject *object, const DSelection &selection, const QSizeF &minimumSize,
646 MDiagram *diagram)
647 {
648 alignSize(object, selection, minimumSize, alignObjectHeight, diagram);
649 }
650
alignSize(DObject * object,const DSelection & selection,const QSizeF & minimumSize,MDiagram * diagram)651 void DiagramSceneController::alignSize(DObject *object, const DSelection &selection, const QSizeF &minimumSize,
652 MDiagram *diagram)
653 {
654 alignSize(object, selection, minimumSize, alignObjectSize, diagram);
655 }
656
alignHCenterDistance(const DSelection & selection,MDiagram * diagram)657 void DiagramSceneController::alignHCenterDistance(const DSelection &selection, MDiagram *diagram)
658 {
659 QList<DObject *> sortedObjects = collectObjects(selection, diagram);
660 if (sortedObjects.length() > 2) {
661 std::sort(sortedObjects.begin(), sortedObjects.end(), [](const DObject *lhs, const DObject *rhs) {
662 return lhs->pos().x() < rhs->pos().x();
663 });
664 int n = sortedObjects.length() - 1;
665 DObject *leftObject = sortedObjects.at(0);
666 DObject *rightObject = sortedObjects.at(n);
667 double distance = rightObject->pos().x() - leftObject->pos().x();
668 double step = distance / n;
669 double startX = leftObject->pos().x();
670 for (int i = 1; i < n; ++i) {
671 DObject *selectedObject = sortedObjects.at(i);
672 QPointF newPos = selectedObject->pos();
673 newPos.setX(startX + i * step);
674 if (newPos != selectedObject->pos()) {
675 m_diagramController->startUpdateElement(selectedObject, diagram, DiagramController::UpdateGeometry);
676 selectedObject->setPos(newPos);
677 m_diagramController->finishUpdateElement(selectedObject, diagram, false);
678 }
679 }
680 }
681 }
682
alignVCenterDistance(const DSelection & selection,MDiagram * diagram)683 void DiagramSceneController::alignVCenterDistance(const DSelection &selection, MDiagram *diagram)
684 {
685 QList<DObject *> sortedObjects = collectObjects(selection, diagram);
686 if (sortedObjects.length() > 2) {
687 std::sort(sortedObjects.begin(), sortedObjects.end(), [](const DObject *lhs, const DObject *rhs) {
688 return lhs->pos().y() < rhs->pos().y();
689 });
690 int n = sortedObjects.length() - 1;
691 DObject *topObject = sortedObjects.at(0);
692 DObject *bottomObject = sortedObjects.at(n);
693 double distance = bottomObject->pos().y() - topObject->pos().y();
694 double step = distance / n;
695 double startY = topObject->pos().y();
696 for (int i = 1; i < n; ++i) {
697 DObject *selectedObject = sortedObjects.at(i);
698 QPointF newPos = selectedObject->pos();
699 newPos.setY(startY + i * step);
700 if (newPos != selectedObject->pos()) {
701 m_diagramController->startUpdateElement(selectedObject, diagram, DiagramController::UpdateGeometry);
702 selectedObject->setPos(newPos);
703 m_diagramController->finishUpdateElement(selectedObject, diagram, false);
704 }
705 }
706 }
707 }
708
alignHBorderDistance(const DSelection & selection,MDiagram * diagram)709 void DiagramSceneController::alignHBorderDistance(const DSelection &selection, MDiagram *diagram)
710 {
711 QList<DObject *> sortedObjects = collectObjects(selection, diagram);
712 if (sortedObjects.length() > 2) {
713 std::sort(sortedObjects.begin(), sortedObjects.end(), [](const DObject *lhs, const DObject *rhs) {
714 return lhs->pos().x() < rhs->pos().x();
715 });
716 int n = sortedObjects.length() - 1;
717 DObject *leftObject = sortedObjects.at(0);
718 DObject *rightObject = sortedObjects.at(n);
719 double space = rightObject->pos().x() + rightObject->rect().left() - (leftObject->pos().x() + leftObject->rect().right());
720 for (int i = 1; i < n; ++i)
721 space -= sortedObjects.at(i)->rect().width();
722 double step = space / n;
723 double x = leftObject->pos().x() + leftObject->rect().right();
724 for (int i = 1 ; i < n; ++i) {
725 DObject *selectedObject = sortedObjects.at(i);
726 QPointF newPos = selectedObject->pos();
727 x += step;
728 newPos.setX(x - selectedObject->rect().left());
729 x += selectedObject->rect().width();
730 if (newPos != selectedObject->pos()) {
731 m_diagramController->startUpdateElement(selectedObject, diagram, DiagramController::UpdateGeometry);
732 selectedObject->setPos(newPos);
733 m_diagramController->finishUpdateElement(selectedObject, diagram, false);
734 }
735 }
736 }
737 }
738
alignVBorderDistance(const DSelection & selection,MDiagram * diagram)739 void DiagramSceneController::alignVBorderDistance(const DSelection &selection, MDiagram *diagram)
740 {
741 QList<DObject *> sortedObjects = collectObjects(selection, diagram);
742 if (sortedObjects.length() > 2) {
743 std::sort(sortedObjects.begin(), sortedObjects.end(), [](const DObject *lhs, const DObject *rhs) {
744 return lhs->pos().y() < rhs->pos().y();
745 });
746 int n = sortedObjects.length() - 1;
747 DObject *topObject = sortedObjects.at(0);
748 DObject *bottomObject = sortedObjects.at(n);
749 double space = bottomObject->pos().y() + bottomObject->rect().top() - (topObject->pos().y() + topObject->rect().bottom());
750 for (int i = 1; i < n; ++i)
751 space -= sortedObjects.at(i)->rect().height();
752 double step = space / n;
753 double y = topObject->pos().y() + topObject->rect().bottom();
754 for (int i = 1 ; i < n; ++i) {
755 DObject *selectedObject = sortedObjects.at(i);
756 QPointF newPos = selectedObject->pos();
757 y += step;
758 newPos.setY(y - selectedObject->rect().top());
759 y += selectedObject->rect().height();
760 if (newPos != selectedObject->pos()) {
761 m_diagramController->startUpdateElement(selectedObject, diagram, DiagramController::UpdateGeometry);
762 selectedObject->setPos(newPos);
763 m_diagramController->finishUpdateElement(selectedObject, diagram, false);
764 }
765 }
766 }
767 }
768
alignPosition(DObject * object,const DSelection & selection,QPointF (* aligner)(DObject *,DObject *),MDiagram * diagram)769 void DiagramSceneController::alignPosition(DObject *object, const DSelection &selection,
770 QPointF (*aligner)(DObject *, DObject *), MDiagram *diagram)
771 {
772 foreach (const DSelection::Index &index, selection.indices()) {
773 DElement *element = m_diagramController->findElement(index.elementKey(), diagram);
774 if (auto selectedObject = dynamic_cast<DObject *>(element)) {
775 if (selectedObject != object) {
776 QPointF newPos = aligner(object, selectedObject);
777 if (newPos != selectedObject->pos()) {
778 m_diagramController->startUpdateElement(selectedObject, diagram, DiagramController::UpdateGeometry);
779 selectedObject->setPos(newPos);
780 m_diagramController->finishUpdateElement(selectedObject, diagram, false);
781 }
782 }
783 }
784 }
785 }
786
alignSize(DObject * object,const DSelection & selection,const QSizeF & minimumSize,QRectF (* aligner)(DObject *,const QSizeF &),MDiagram * diagram)787 void DiagramSceneController::alignSize(DObject *object, const DSelection &selection, const QSizeF &minimumSize,
788 QRectF (*aligner)(DObject *, const QSizeF &), MDiagram *diagram)
789 {
790 QSizeF size;
791 if (object->rect().width() < minimumSize.width())
792 size.setWidth(minimumSize.width());
793 else
794 size.setWidth(object->rect().width());
795 if (object->rect().height() < minimumSize.height())
796 size.setHeight(minimumSize.height());
797 else
798 size.setHeight(object->rect().height());
799 foreach (const DSelection::Index &index, selection.indices()) {
800 DElement *element = m_diagramController->findElement(index.elementKey(), diagram);
801 if (auto selectedObject = dynamic_cast<DObject *>(element)) {
802 QRectF newRect = aligner(selectedObject, size);
803 if (newRect != selectedObject->rect()) {
804 m_diagramController->startUpdateElement(selectedObject, diagram, DiagramController::UpdateGeometry);
805 selectedObject->setAutoSized(false);
806 selectedObject->setRect(newRect);
807 m_diagramController->finishUpdateElement(selectedObject, diagram, false);
808 }
809 }
810 }
811 }
812
alignOnRaster(DElement * element,MDiagram * diagram)813 void DiagramSceneController::alignOnRaster(DElement *element, MDiagram *diagram)
814 {
815 AlignOnRasterVisitor visitor;
816 visitor.setDiagramController(m_diagramController);
817 visitor.setSceneInspector(m_sceneInspector);
818 visitor.setDiagram(diagram);
819 element->accept(&visitor);
820 }
821
collectObjects(const DSelection & selection,MDiagram * diagram)822 QList<DObject *> DiagramSceneController::collectObjects(const DSelection &selection, MDiagram *diagram)
823 {
824 QList<DObject *> list;
825 foreach (const DSelection::Index &index, selection.indices()) {
826 DObject *object = m_diagramController->findElement<DObject>(index.elementKey(), diagram);
827 if (object)
828 list.append(object);
829 }
830 return list;
831 }
832
addModelElement(const Uid & modelElementKey,const QPointF & pos,MDiagram * diagram)833 DElement *DiagramSceneController::addModelElement(const Uid &modelElementKey, const QPointF &pos, MDiagram *diagram)
834 {
835 DElement *element = nullptr;
836 if (MObject *modelObject = m_modelController->findObject(modelElementKey)) {
837 element = addObject(modelObject, pos, diagram);
838 } else if (MRelation *modelRelation = m_modelController->findRelation(modelElementKey)) {
839 element = addRelation(modelRelation, QList<QPointF>(), diagram);
840 } else {
841 QMT_CHECK(false);
842 }
843 return element;
844 }
845
addObject(MObject * modelObject,const QPointF & pos,MDiagram * diagram)846 DObject *DiagramSceneController::addObject(MObject *modelObject, const QPointF &pos, MDiagram *diagram)
847 {
848 QMT_ASSERT(modelObject, return nullptr);
849
850 if (m_diagramController->hasDelegate(modelObject, diagram))
851 return nullptr;
852
853 m_diagramController->undoController()->beginMergeSequence(tr("Add Element"));
854
855 DFactory factory;
856 modelObject->accept(&factory);
857 auto diagramObject = dynamic_cast<DObject *>(factory.product());
858 QMT_ASSERT(diagramObject, return nullptr);
859 diagramObject->setPos(pos);
860 m_diagramController->addElement(diagramObject, diagram);
861 alignOnRaster(diagramObject, diagram);
862
863 // add all relations between any other element on diagram and new element
864 foreach (DElement *delement, diagram->diagramElements()) {
865 if (delement != diagramObject) {
866 auto dobject = dynamic_cast<DObject *>(delement);
867 if (dobject) {
868 MObject *mobject = m_modelController->findObject(dobject->modelUid());
869 if (mobject) {
870 for (const Handle<MRelation> &handle : mobject->relations()) {
871 if (handle.hasTarget()
872 && ((handle.target()->endAUid() == modelObject->uid()
873 && handle.target()->endBUid() == mobject->uid())
874 || (handle.target()->endAUid() == mobject->uid()
875 && handle.target()->endBUid() == modelObject->uid()))) {
876 addRelation(handle.target(), QList<QPointF>(), diagram);
877 }
878 }
879 for (const Handle<MRelation> &handle : modelObject->relations()) {
880 if (handle.hasTarget()
881 && ((handle.target()->endAUid() == modelObject->uid()
882 && handle.target()->endBUid() == mobject->uid())
883 || (handle.target()->endAUid() == mobject->uid()
884 && handle.target()->endBUid() == modelObject->uid()))) {
885 addRelation(handle.target(), QList<QPointF>(), diagram);
886 }
887 }
888 }
889 }
890 }
891 }
892
893 // add all self relations
894 for (const Handle<MRelation> &handle : modelObject->relations()) {
895 if (handle.hasTarget ()
896 && handle.target()->endAUid() == modelObject->uid()
897 && handle.target()->endBUid() == modelObject->uid()) {
898 addRelation(handle.target(), QList<QPointF>(), diagram);
899 }
900 }
901
902 m_diagramController->undoController()->endMergeSequence();
903
904 return diagramObject;
905 }
906
addRelation(MRelation * modelRelation,const QList<QPointF> & intermediatePoints,MDiagram * diagram)907 DRelation *DiagramSceneController::addRelation(MRelation *modelRelation, const QList<QPointF> &intermediatePoints,
908 MDiagram *diagram)
909 {
910 QMT_ASSERT(modelRelation, return nullptr);
911
912 if (m_diagramController->hasDelegate(modelRelation, diagram))
913 return nullptr;
914
915 DFactory factory;
916 modelRelation->accept(&factory);
917 auto diagramRelation = dynamic_cast<DRelation *>(factory.product());
918 QMT_ASSERT(diagramRelation, return nullptr);
919
920 MObject *endAModelObject = m_modelController->findObject(modelRelation->endAUid());
921 QMT_ASSERT(endAModelObject, return nullptr);
922 DObject *endADiagramObject = m_diagramController->findDelegate<DObject>(endAModelObject, diagram);
923 QMT_ASSERT(endADiagramObject, return nullptr);
924 diagramRelation->setEndAUid(endADiagramObject->uid());
925
926 MObject *endBModelObject = m_modelController->findObject(modelRelation->endBUid());
927 QMT_ASSERT(endBModelObject, return nullptr);
928 DObject *endBDiagramObject = m_diagramController->findDelegate<DObject>(endBModelObject, diagram);
929 QMT_ASSERT(endBDiagramObject, return nullptr);
930 diagramRelation->setEndBUid(endBDiagramObject->uid());
931
932 QList<DRelation::IntermediatePoint> relationPoints;
933 if (endADiagramObject->uid() == endBDiagramObject->uid() && intermediatePoints.isEmpty()) {
934 // create some intermediate points for self-relation
935 QRectF rect = endADiagramObject->rect().translated(endADiagramObject->pos());
936 static const qreal EDGE_RADIUS = 30.0;
937 qreal w = rect.width() * 0.25;
938 if (w > EDGE_RADIUS)
939 w = EDGE_RADIUS;
940 qreal h = rect.height() * 0.25;
941 if (h > EDGE_RADIUS)
942 h = EDGE_RADIUS;
943 QPointF i1(rect.x() - EDGE_RADIUS, rect.bottom() - h);
944 QPointF i2(rect.x() - EDGE_RADIUS, rect.bottom() + EDGE_RADIUS);
945 QPointF i3(rect.x() + w, rect.bottom() + EDGE_RADIUS);
946 relationPoints.append(DRelation::IntermediatePoint(i1));
947 relationPoints.append(DRelation::IntermediatePoint(i2));
948 relationPoints.append(DRelation::IntermediatePoint(i3));
949 } else {
950 foreach (const QPointF &intermediatePoint, intermediatePoints)
951 relationPoints.append(DRelation::IntermediatePoint(intermediatePoint));
952 }
953 diagramRelation->setIntermediatePoints(relationPoints);
954
955 m_diagramController->addElement(diagramRelation, diagram);
956 alignOnRaster(diagramRelation, diagram);
957
958 return diagramRelation;
959 }
960
relocateRelationEnd(DRelation * relation,DObject * targetObject,RelationEnd relationEnd,Uid (MRelation::* endUid)()const,void (MRelation::* setEndUid)(const Uid &))961 bool DiagramSceneController::relocateRelationEnd(DRelation *relation, DObject *targetObject,
962 RelationEnd relationEnd,
963 Uid (MRelation::*endUid)() const,
964 void (MRelation::*setEndUid)(const Uid &))
965 {
966 QMT_ASSERT(relation, return false);
967 if (targetObject && targetObject->uid() != relation->endAUid()) {
968 MRelation *modelRelation = m_modelController->findRelation(relation->modelUid());
969 QMT_ASSERT(modelRelation, return false);
970 MObject *targetMObject = m_modelController->findObject(targetObject->modelUid());
971 QMT_ASSERT(targetMObject, return false);
972 AcceptRelationVisitor visitor(m_stereotypeController, modelRelation, relationEnd);
973 targetMObject->accept(&visitor);
974 if (visitor.isAccepted()) {
975 MObject *currentTargetMObject = m_modelController->findObject((modelRelation->*endUid)());
976 QMT_ASSERT(currentTargetMObject, return false);
977 m_modelController->undoController()->beginMergeSequence(tr("Relocate Relation"));
978 // move relation into new target if it was a child of the old target
979 if (currentTargetMObject == modelRelation->owner())
980 m_modelController->moveRelation(targetMObject, modelRelation);
981 // remove relation on all diagrams where the new targe element does not exist
982 foreach (MDiagram *diagram, m_diagramController->allDiagrams()) {
983 if (DElement *diagramRelation = m_diagramController->findDelegate(modelRelation, diagram)) {
984 if (!m_diagramController->findDelegate(targetMObject, diagram)) {
985 m_diagramController->removeElement(diagramRelation, diagram);
986 }
987 }
988 }
989 // update end of relation
990 m_modelController->startUpdateRelation(modelRelation);
991 (modelRelation->*setEndUid)(targetMObject->uid());
992 m_modelController->finishUpdateRelation(modelRelation, false);
993 m_modelController->undoController()->endMergeSequence();
994 return true;
995 }
996 }
997 return false;
998 }
999
1000 } // namespace qmt
1001