1 /*
2     SPDX-License-Identifier: GPL-2.0-or-later
3     SPDX-FileCopyrightText: 2002-2020 Umbrello UML Modeller Authors <umbrello-devel@kde.org>
4 */
5 
6 // own header
7 #include "umlscene.h"
8 
9 // application specific includes
10 #include "activitywidget.h"
11 #include "actorwidget.h"
12 #include "artifactwidget.h"
13 #include "association.h"
14 #include "associationwidget.h"
15 #include "assocrules.h"
16 #include "attribute.h"
17 #include "boxwidget.h"
18 #include "classifier.h"
19 #include "classifierwidget.h"
20 #include "classoptionspage.h"
21 #include "component.h"
22 #include "cmds.h"
23 #include "componentwidget.h"
24 #include "datatype.h"
25 #include "diagram_utils.h"
26 #include "pinportbase.h"
27 #include "datatypewidget.h"
28 #include "debug_utils.h"
29 #include "dialog_utils.h"
30 #include "docwindow.h"
31 #include "entity.h"
32 #include "entitywidget.h"
33 #include "enumwidget.h"
34 #include "floatingtextwidget.h"
35 #include "folder.h"
36 #include "foreignkeyconstraint.h"
37 #include "forkjoinwidget.h"
38 #include "idchangelog.h"
39 #include "interfacewidget.h"
40 #include "import_utils.h"
41 #include "layoutgenerator.h"
42 #include "layoutgrid.h"
43 #include "messagewidget.h"
44 #include "model_utils.h"
45 #include "notewidget.h"
46 #include "object_factory.h"
47 #include "objectnodewidget.h"
48 #include "objectwidget.h"
49 #include "package.h"
50 #include "packagewidget.h"
51 #include "pinwidget.h"
52 #include "portwidget.h"
53 #include "seqlinewidget.h"
54 #include "signalwidget.h"
55 #include "statewidget.h"
56 #include "toolbarstate.h"
57 #include "toolbarstatefactory.h"
58 #include "uml.h"
59 #include "umldoc.h"
60 #include "umldragdata.h"
61 #include "umlfiledialog.h"
62 #include "umllistview.h"
63 #include "umllistviewitem.h"
64 #include "umlobject.h"
65 #include "umlobjectlist.h"
66 #include "umlrole.h"
67 #include "umlscenepopupmenu.h"
68 #include "umlview.h"
69 #include "umlviewimageexporter.h"
70 #include "umlwidget.h"
71 #include "uniqueid.h"
72 #include "widget_factory.h"
73 #include "widget_utils.h"
74 #include "widgetlist_utils.h"
75 
76 //kde include files
77 #if QT_VERSION < 0x050000
78 #include <kfiledialog.h>
79 #include <kio/netaccess.h>
80 #endif
81 #include <KMessageBox>
82 #include <kcursor.h>
83 #include <KLocalizedString>
84 
85 // include files for Qt
86 #include <QColor>
87 #include <QLineF>
88 #include <QPainter>
89 #include <QPixmap>
90 #include <QPrinter>
91 #include <QString>
92 #include <QStringList>
93 #include <QXmlStreamWriter>
94 
95 // system includes
96 #include <cmath>  // for ceil
97 
98 // static members
99 const qreal UMLScene::defaultCanvasSize = 5000;
100 bool UMLScene::m_showDocumentationIndicator = false;
101 
102 using namespace Uml;
103 
104 DEBUG_REGISTER_DISABLED(UMLScene)
105 
106 /**
107  * The class UMLScenePrivate is intended to hold private
108  * members/classes to reduce the size of the public class
109  * and to speed up recompiling.
110  * The migration to this class is not complete yet.
111  */
112 class UMLScenePrivate {
113 public:
UMLScenePrivate(UMLScene * parent)114     UMLScenePrivate(UMLScene *parent)
115       : p(parent)
116       , toolBarState(nullptr)
117       , inMouseMoveEvent(false)
118     {
119         toolBarStateFactory = new ToolBarStateFactory;
120     }
121 
~UMLScenePrivate()122     ~UMLScenePrivate()
123     {
124         delete toolBarState;
125         delete toolBarStateFactory;
126     }
127     /**
128      * Check if there is a corresponding port widget
129      * for all UMLPort instances and add if not.
130      */
addMissingPorts()131     void addMissingPorts()
132     {
133         UMLWidgetList ports;
134         UMLWidgetList components;
135 
136         foreach(UMLWidget *w, p->widgetList()) {
137             if (w->isPortWidget())
138                 ports.append(w);
139             else if (w->isComponentWidget())
140                 components.append(w);
141         }
142 
143         foreach(UMLWidget *cw, components) {
144             UMLComponent *c = cw->umlObject()->asUMLComponent();
145             if (!c)
146                 continue;
147             // iterate through related ports for this component widget
148             foreach(UMLObject *o, c->containedObjects()) {
149                 UMLPort *up = o->asUMLPort();
150                 if (!up)
151                     continue;
152                 Uml::ID::Type id = o->id();
153                 bool found = false;
154                 foreach(UMLWidget *p, ports) {
155                     if (p->id() == id) {
156                         found = true;
157                         break;
158                     }
159                 }
160                 if (!found)
161                     new PortWidget(p, up, cw);
162             }
163         }
164     }
165 
166     /**
167      * Check if port are located equally on the border of a component
168      * and fix position if not.
169      */
fixPortPositions()170     void fixPortPositions()
171     {
172         foreach(UMLWidget *w, p->widgetList()) {
173             if (w->isPortWidget()) {
174                 QGraphicsItem *g = w->parentItem();
175                 ComponentWidget *c = dynamic_cast<ComponentWidget*>(g);
176                 Q_ASSERT(c);
177                 qreal w2 = w->width()/2;
178                 qreal h2 = w->height()/2;
179                 if (w->x() <= -w2 || w->y() <= -h2
180                         || w->x() >= c->width() - w2
181                         || w->y() >= c->height() - h2)
182                     continue;
183                 if (w->x() >= c->width() - 3 * w2) { // right
184                     w->setX(c->width() - w2);
185                 } else if (w->y() >= c->height() - 3 * h2) { // bottom
186                     w->setY(c->height() - h2);
187                 } else if (w->x() < 3 * w2) { // left
188                     w->setX(-w2);
189                 } else if (w->y() < 3 * h2) { // top
190                     w->setY(-h2);
191                 } else
192                     uWarning() << "unhandled widget position of" << w->name();
193             }
194         }
195     }
196 
197     /**
198      * Check if duplicated floating text labels are in the scene and remove them
199      */
removeDuplicatedFloatingTextInstances()200     void removeDuplicatedFloatingTextInstances()
201     {
202         UMLWidgetList labelsWithoutParents;
203         UMLWidgetList labelsWithParent;
204         uDebug() << "checking diagram" << p->name() << "id" << Uml::ID::toString(p->ID());
205 
206         foreach(UMLWidget *w, p->widgetList()) {
207             if (!w->isTextWidget())
208                 continue;
209             if (w->parentItem())
210                 labelsWithParent.append(w);
211             else
212                 labelsWithoutParents.append(w);
213         }
214         foreach(UMLWidget *w, labelsWithoutParents) {
215             foreach(UMLWidget *wp, labelsWithParent) {
216                 if (w->id() == wp->id() &&
217                         w->localID() == wp->localID() &&
218                         w->name() == wp->name()) {
219                     p->removeWidgetCmd(w);
220                     uDebug() << "removed duplicated text label" << w->name() << "id:" << Uml::ID::toString(w->id());
221                     break;
222                 }
223             }
224         }
225     }
226 
setToolBarChanged(WorkToolBar::ToolBar_Buttons button)227     void setToolBarChanged(WorkToolBar::ToolBar_Buttons button)
228     {
229         if (toolBarState)
230             toolBarState->cleanBeforeChange();
231         toolBarState = toolBarStateFactory->getState(button, p);
232         toolBarState->init();
233         p->setPaste(false);
234     }
235 
triggerToolBarButton(WorkToolBar::ToolBar_Buttons button)236     void triggerToolBarButton(WorkToolBar::ToolBar_Buttons button)
237     {
238         UMLApp::app()->workToolBar()->buttonChanged(button);
239         setToolBarChanged(button);
240         QGraphicsSceneMouseEvent event;
241         event.setScenePos(p->pos());
242         event.setButton(Qt::LeftButton);
243         toolBarState->mousePress(&event);
244         toolBarState->mouseRelease(&event);
245         p->connect(toolBarState, SIGNAL(finished()), UMLApp::app()->workToolBar(), SLOT(slotResetToolBar()));
246     }
247 
248     UMLScene *p;
249     ToolBarStateFactory *toolBarStateFactory;
250     ToolBarState *toolBarState;
251     QPointer<WidgetBase> widgetLink;
252     bool inMouseMoveEvent;
253 };
254 
255 /**
256  * Constructor.
257  */
UMLScene(UMLFolder * parentFolder,UMLView * view)258 UMLScene::UMLScene(UMLFolder *parentFolder, UMLView *view)
259   : QGraphicsScene(0, 0, defaultCanvasSize, defaultCanvasSize),
260     m_nLocalID(Uml::ID::None),
261     m_nID(Uml::ID::None),
262     m_Type(Uml::DiagramType::Undefined),
263     m_Name(QString()),
264     m_Documentation(QString()),
265     m_Options(Settings::optionState()),
266     m_bUseSnapToGrid(false),
267     m_bUseSnapComponentSizeToGrid(false),
268     m_isOpen(true),
269     m_nCollaborationId(0),
270     m_bCreateObject(false),
271     m_bDrawSelectedOnly(false),
272     m_bPaste(false),
273     m_bStartedCut(false),
274     m_d(new UMLScenePrivate(this)),
275     m_view(view),
276     m_pFolder(parentFolder),
277     m_pIDChangesLog(0),
278     m_isActivated(false),
279     m_bPopupShowing(false),
280     m_autoIncrementSequence(false)
281 {
282     m_PastePoint = QPointF(0, 0);
283 
284     m_pImageExporter = new UMLViewImageExporter(this);
285 
286     // setup signals
287     connect(UMLApp::app(), SIGNAL(sigCutSuccessful()),
288             this, SLOT(slotCutSuccessful()));
289     m_d->setToolBarChanged(WorkToolBar::tbb_Arrow);
290 
291     m_doc = UMLApp::app()->document();
292 
293 //    // settings for background
294 //    setBackgroundBrush(QColor(195, 195, 195));
295     m_layoutGrid = new LayoutGrid(this);
296 
297     // fix crash caused by Qt stale item issue see https://bugs.kde.org/show_bug.cgi?id=383592
298     setItemIndexMethod(NoIndex);
299 }
300 
301 /**
302  * Destructor.
303  */
~UMLScene()304 UMLScene::~UMLScene()
305 {
306     delete m_pImageExporter;
307     m_pImageExporter = 0;
308     delete m_pIDChangesLog;
309     m_pIDChangesLog = 0;
310 
311     // before we can delete the QCanvas, all widgets must be explicitly
312     // removed
313     // otherwise the implicit remove of the contained widgets will cause
314     // events which would demand a valid connected QCanvas
315     // ==> this causes umbrello to crash for some - larger?? - projects
316     // first avoid all events, which would cause some update actions
317     // on deletion of each removed widget
318     blockSignals(true);
319     removeAllWidgets();
320 
321     delete m_layoutGrid;
322     delete m_d;
323 }
324 
325 /**
326  * Return the UMLFolder in which this diagram lives.
327  */
folder() const328 UMLFolder* UMLScene::folder() const
329 {
330     return m_pFolder;
331 }
332 
333 /**
334  * Set the UMLFolder in which this diagram lives.
335  */
setFolder(UMLFolder * folder)336 void UMLScene::setFolder(UMLFolder *folder)
337 {
338     m_pFolder = folder;
339 }
340 
341 /**
342  * Returns the active view associated with this scene.
343  */
activeView() const344 UMLView* UMLScene::activeView() const
345 {
346     return m_view;
347 }
348 
349 /**
350  * Return the documentation of the diagram.
351  */
documentation() const352 QString UMLScene::documentation() const
353 {
354     return m_Documentation;
355 }
356 
357 /**
358  * Set the documentation of the diagram.
359  */
setDocumentation(const QString & doc)360 void UMLScene::setDocumentation(const QString &doc)
361 {
362     m_Documentation = doc;
363 }
364 
365 /**
366  * Return the state of the auto increment sequence
367  */
autoIncrementSequence() const368 bool UMLScene::autoIncrementSequence() const
369 {
370     return m_autoIncrementSequence;
371 }
372 
setAutoIncrementSequence(bool state)373 void UMLScene::setAutoIncrementSequence(bool state)
374 {
375     m_autoIncrementSequence = state;
376 }
377 
378 /**
379  * Return the next auto increment sequence value
380  */
autoIncrementSequenceValue()381 QString UMLScene::autoIncrementSequenceValue()
382 {
383     int sequenceNumber = 0;
384     if (isSequenceDiagram()) {
385         foreach (MessageWidget* message, messageList()) {
386             bool ok;
387             int value = message->sequenceNumber().toInt(&ok);
388             if (ok && value > sequenceNumber)
389                sequenceNumber = value;
390         }
391     }
392     else if (isCollaborationDiagram()) {
393         foreach (AssociationWidget* assoc, associationList()) {
394             bool ok;
395             int value = assoc->sequenceNumber().toInt(&ok);
396             if (ok && value > sequenceNumber)
397                sequenceNumber = value;
398         }
399     }
400     return QString::number(sequenceNumber + 1);
401 }
402 
403 /**
404  * Return the name of the diagram.
405  */
name() const406 QString UMLScene::name() const
407 {
408     return m_Name;
409 }
410 
411 /**
412  * Set the name of the diagram.
413  */
setName(const QString & name)414 void UMLScene::setName(const QString &name)
415 {
416     m_Name = name;
417 }
418 
419 /**
420  * Returns the type of the diagram.
421  */
type() const422 DiagramType::Enum UMLScene::type() const
423 {
424     return m_Type;
425 }
426 
427 /**
428  * Set the type of diagram.
429  */
setType(DiagramType::Enum type)430 void UMLScene::setType(DiagramType::Enum type)
431 {
432     m_Type = type;
433 }
434 
435 /**
436  * Returns the ID of the diagram.
437  */
ID() const438 Uml::ID::Type UMLScene::ID() const
439 {
440     return m_nID;
441 }
442 
443 /**
444  * Sets the ID of the diagram.
445  */
setID(Uml::ID::Type id)446 void UMLScene::setID(Uml::ID::Type id)
447 {
448     m_nID = id;
449 }
450 
451 /**
452  * Returns the position of the diagram.
453  */
pos() const454 QPointF UMLScene::pos() const
455 {
456     return m_pos;
457 }
458 
459 /**
460  * Sets the position of the diagram.
461  */
setPos(const QPointF & pos)462 void UMLScene::setPos(const QPointF &pos)
463 {
464     m_pos = pos;
465 }
466 
467 /**
468  * Returns the fill color to use.
469  */
fillColor() const470 const QColor& UMLScene::fillColor() const
471 {
472     return m_Options.uiState.fillColor;
473 }
474 
475 /**
476  * Set the background color.
477  *
478  * @param color  The color to use.
479  */
setFillColor(const QColor & color)480 void UMLScene::setFillColor(const QColor &color)
481 {
482     m_Options.uiState.fillColor = color;
483     emit sigFillColorChanged(ID());
484 }
485 
486 /**
487  * Returns the line color to use.
488  */
lineColor() const489 const QColor& UMLScene::lineColor() const
490 {
491     return m_Options.uiState.lineColor;
492 }
493 
494 /**
495  * Sets the line color.
496  *
497  * @param color  The color to use.
498  */
setLineColor(const QColor & color)499 void UMLScene::setLineColor(const QColor &color)
500 {
501     m_Options.uiState.lineColor = color;
502     emit sigLineColorChanged(ID());
503 }
504 
505 /**
506  * Returns the line width to use.
507  */
lineWidth() const508 uint UMLScene::lineWidth() const
509 {
510     return m_Options.uiState.lineWidth;
511 }
512 
513 /**
514  * Sets the line width.
515  *
516  * @param width  The width to use.
517  */
setLineWidth(uint width)518 void UMLScene::setLineWidth(uint width)
519 {
520     m_Options.uiState.lineWidth = width;
521     emit sigLineWidthChanged(ID());
522 }
523 
524 /**
525  * Returns the text color to use.
526  */
textColor() const527 const QColor& UMLScene::textColor() const
528 {
529     return m_Options.uiState.textColor;
530 }
531 
532 /**
533  * Sets the text color.
534  *
535  * @param color  The color to use.
536  */
setTextColor(const QColor & color)537 void UMLScene::setTextColor(const QColor& color)
538 {
539     m_Options.uiState.textColor = color;
540     emit sigTextColorChanged(ID());
541 }
542 
543 /**
544  * return grid dot color
545  *
546  * @return Color
547  */
gridDotColor() const548 const QColor& UMLScene::gridDotColor() const
549 {
550     return m_layoutGrid->gridDotColor();
551 }
552 
553 /**
554  * set grid dot color
555  *
556  * @param color grid dot color
557  */
setGridDotColor(const QColor & color)558 void UMLScene::setGridDotColor(const QColor& color)
559 {
560     m_Options.uiState.gridDotColor = color;
561     m_layoutGrid->setGridDotColor(color);
562 }
563 
564 /**
565  * Returns the options being used.
566  */
optionState()567 Settings::OptionState& UMLScene::optionState()
568 {
569     return m_Options;
570 }
571 
572 /**
573  * Sets the options to be used.
574  */
setOptionState(const Settings::OptionState & options)575 void UMLScene::setOptionState(const Settings::OptionState& options)
576 {
577     m_Options = options;
578     setBackgroundBrush(options.uiState.backgroundColor);
579     setGridDotColor(options.uiState.gridDotColor);
580 }
581 
582 /**
583  * Returns a reference to the association list.
584  */
associationList() const585 const AssociationWidgetList UMLScene::associationList() const
586 {
587     AssociationWidgetList result;
588     foreach(QGraphicsItem *item, items()) {
589         AssociationWidget *w = dynamic_cast<AssociationWidget*>(item);
590         if (w)
591             result.append(w);
592     }
593     return result;
594 }
595 
596 /**
597  * Returns a reference to the widget list.
598  */
widgetList() const599 const UMLWidgetList UMLScene::widgetList() const
600 {
601     UMLWidgetList result;
602     foreach(QGraphicsItem *item, items()) {
603         UMLWidget *w = dynamic_cast<UMLWidget*>(item);
604         if (w && !w->isMessageWidget() && !w->isAssociationWidget())
605             result.append(w);
606     }
607     return result;
608 }
609 
addWidgetCmd(UMLWidget * widget)610 void UMLScene::addWidgetCmd(UMLWidget* widget)
611 {
612     Q_ASSERT(0 != widget);
613     addItem(widget);
614 }
615 
addWidgetCmd(AssociationWidget * widget)616 void UMLScene::addWidgetCmd(AssociationWidget* widget)
617 {
618     Q_ASSERT(0 != widget);
619     addItem(widget);
620 }
621 
622 /**
623  * Returns a reference to the message list.
624  */
messageList() const625 const MessageWidgetList UMLScene::messageList() const
626 {
627     MessageWidgetList result;
628     foreach(QGraphicsItem *item, items()) {
629         MessageWidget *w = dynamic_cast<MessageWidget*>(item);
630         if (w)
631             result.append(w);
632     }
633     return result;
634 }
635 
636 /**
637  * Used for creating unique name of collaboration messages.
638  */
generateCollaborationId()639 int UMLScene::generateCollaborationId()
640 {
641     return ++m_nCollaborationId;
642 }
643 
644 /**
645  * Returns the open state.
646  * @return   when true diagram is shown to the user
647  */
isOpen() const648 bool UMLScene::isOpen() const
649 {
650     return m_isOpen;
651 }
652 
653 /**
654  * Sets the flag 'isOpen'.
655  * @param isOpen   flag indicating that the diagram is shown to the user
656  */
setIsOpen(bool isOpen)657 void UMLScene::setIsOpen(bool isOpen)
658 {
659     m_isOpen = isOpen;
660 }
661 
662 /**
663  * Contains the implementation for printing functionality.
664  */
print(QPrinter * pPrinter,QPainter & pPainter)665 void UMLScene::print(QPrinter *pPrinter, QPainter & pPainter)
666 {
667     bool isFooter = optionState().generalState.footerPrinting;
668 
669     // The printer will probably use a different font with different font metrics,
670     // force the widgets to update accordingly on paint
671     forceUpdateWidgetFontMetrics(&pPainter);
672 
673     QRectF source = diagramRect();
674     QRect paper = pPrinter->paperRect();
675     QRect page = pPrinter->pageRect();
676 
677     // use the painter font metrics, not the screen fm!
678     QFontMetrics fm = pPainter.fontMetrics();
679     int fontHeight  = fm.lineSpacing();
680 
681     if (paper == page) {
682         QSize margin = page.size() * 0.025;
683         page.adjust(margin.width(), margin.height(), -margin.width(), -margin.height());
684     }
685 
686     if (isFooter) {
687         int margin = 3 + 3 * fontHeight;
688         page.adjust(0, 0, 0, -margin);
689     }
690 
691     getDiagram(pPainter, QRectF(source), QRectF(page));
692 
693     //draw foot note
694     if (isFooter) {
695         page.adjust(0, 0, 0, fontHeight);
696         QString string = i18n("Diagram: %2 Page %1", 1, name());
697         QColor textColor(50, 50, 50);
698         pPainter.setPen(textColor);
699         pPainter.drawLine(page.left(), page.bottom()    , page.right(), page.bottom());
700         pPainter.drawText(page.left(), page.bottom() + 3, page.right(), 2*fontHeight, Qt::AlignLeft, string);
701     }
702 
703     // next painting will most probably be to a different device (i.e. the screen)
704     forceUpdateWidgetFontMetrics(0);
705 }
706 
707 /**
708  * Initialize and announce a newly created widget.
709  * Auxiliary to contentsMouseReleaseEvent().
710  */
setupNewWidget(UMLWidget * w,bool setPosition)711 void UMLScene::setupNewWidget(UMLWidget *w, bool setPosition)
712 {
713     if (setPosition &&
714         (!w->isPinWidget()) &&
715         (!w->isPortWidget()) &&
716         (!w->isObjectWidget())) {
717         // ObjectWidget's position is handled by the widget
718         w->setX(m_pos.x());
719         w->setY(m_pos.y());
720     }
721     w->setVisible(true);
722     w->activate();
723     w->setFontCmd(font());
724     w->slotFillColorChanged(ID());
725     w->slotTextColorChanged(ID());
726     w->slotLineWidthChanged(ID());
727     resizeSceneToItems();
728     m_doc->setModified();
729 
730     if (m_doc->loading()) {  // do not emit signals while loading
731         addWidgetCmd(w);
732         // w->activate();  // will be done by UMLDoc::activateAllViews() after loading
733     } else {
734         UMLApp::app()->executeCommand(new CmdCreateWidget(w));
735     }
736 }
737 
738 /**
739  * Return whether we are currently creating an object.
740  */
getCreateObject() const741 bool UMLScene::getCreateObject() const
742 {
743     return m_bCreateObject;
744 }
745 
746 /**
747  * Set whether we are currently creating an object.
748  */
setCreateObject(bool bCreate)749 void UMLScene::setCreateObject(bool bCreate)
750 {
751     m_bCreateObject = bCreate;
752 }
753 
754 /**
755  * Overrides the standard operation.
756  */
showEvent(QShowEvent *)757 void UMLScene::showEvent(QShowEvent* /*se*/)
758 {
759     connect(m_doc, SIGNAL(sigObjectCreated(UMLObject*)),
760             this, SLOT(slotObjectCreated(UMLObject*)));
761     connect(this, SIGNAL(sigAssociationRemoved(AssociationWidget*)),
762             UMLApp::app()->docWindow(), SLOT(slotAssociationRemoved(AssociationWidget*)));
763     connect(this, SIGNAL(sigWidgetRemoved(UMLWidget*)),
764             UMLApp::app()->docWindow(), SLOT(slotWidgetRemoved(UMLWidget*)));
765 }
766 
767 /**
768  * Overrides the standard operation.
769  */
hideEvent(QHideEvent *)770 void UMLScene::hideEvent(QHideEvent* /*he*/)
771 {
772     disconnect(m_doc, SIGNAL(sigObjectCreated(UMLObject*)), this, SLOT(slotObjectCreated(UMLObject*)));
773     disconnect(this, SIGNAL(sigAssociationRemoved(AssociationWidget*)),
774                UMLApp::app()->docWindow(), SLOT(slotAssociationRemoved(AssociationWidget*)));
775     disconnect(this, SIGNAL(sigWidgetRemoved(UMLWidget*)),
776                UMLApp::app()->docWindow(), SLOT(slotWidgetRemoved(UMLWidget*)));
777 }
778 
779 /**
780  * Changes the current tool to the selected tool.
781  * The current tool is cleaned and the selected tool initialized.
782  */
slotToolBarChanged(int c)783 void UMLScene::slotToolBarChanged(int c)
784 {
785     m_d->setToolBarChanged((WorkToolBar::ToolBar_Buttons)c);
786 }
787 
788 /**
789  * Slot called when an object is created.
790  * @param o   created UML object
791  */
slotObjectCreated(UMLObject * o)792 void UMLScene::slotObjectCreated(UMLObject* o)
793 {
794     DEBUG(DBG_SRC) << "scene=" << name() << " / object=" << o->name();
795     m_bPaste = false;
796     //check to see if we want the message
797     //may be wanted by someone else e.g. list view
798 
799     if (!m_bCreateObject) {
800         return;
801     }
802 
803     UMLWidget* newWidget = Widget_Factory::createWidget(this, o);
804 
805     if (!newWidget) {
806         return;
807     }
808 
809     setupNewWidget(newWidget);
810 
811     m_bCreateObject = false;
812 
813     if (Model_Utils::hasAssociations(o->baseType()))
814     {
815         createAutoAssociations(newWidget);
816         // We need to invoke createAutoAttributeAssociations()
817         // on all other widgets again because the newly created
818         // widget might saturate some latent attribute assocs.
819         createAutoAttributeAssociations2(newWidget);
820     }
821     resizeSceneToItems();
822 }
823 
824 /**
825  * Slot called when an object is removed.
826  * @param o   removed UML object
827  */
slotObjectRemoved(UMLObject * o)828 void UMLScene::slotObjectRemoved(UMLObject * o)
829 {
830     m_bPaste = false;
831     Uml::ID::Type id = o->id();
832 
833     foreach(UMLWidget* obj, widgetList()) {
834         if (obj->id() != id)
835             continue;
836         removeWidget(obj);
837         break;
838     }
839 }
840 
841 /**
842  * Override standard method.
843  */
dragEnterEvent(QGraphicsSceneDragDropEvent * e)844 void UMLScene::dragEnterEvent(QGraphicsSceneDragDropEvent *e)
845 {
846     UMLDragData::LvTypeAndID_List tidList;
847     if (!UMLDragData::getClip3TypeAndID(e->mimeData(), tidList)) {
848         DEBUG(DBG_SRC) << "UMLDragData::getClip3TypeAndID returned false";
849         return;
850     }
851     const DiagramType::Enum diagramType = type();
852 
853     bool bAccept = true;
854     for(UMLDragData::LvTypeAndID_List::const_iterator it = tidList.begin(); it != tidList.end(); it++) {
855         UMLListViewItem::ListViewType lvtype = (*it)->type;
856         Uml::ID::Type id = (*it)->id;
857 
858         UMLObject* temp = 0;
859         //if dragging diagram - might be a drag-to-note
860         if (Model_Utils::typeIsDiagram(lvtype)) {
861             break;
862         }
863         //can't drag anything onto state/activity diagrams
864         if (diagramType == DiagramType::State || diagramType == DiagramType::Activity) {
865             bAccept = false;
866             break;
867         }
868         //make sure can find UMLObject
869         if (!(temp = m_doc->findObjectById(id))) {
870             DEBUG(DBG_SRC) << "object " << Uml::ID::toString(id) << " not found";
871             bAccept = false;
872             break;
873         }
874         if (!Model_Utils::typeIsAllowedInDiagram(temp, this)) {
875             DEBUG(DBG_SRC) << temp->name() << " is not allowed in diagram type " << diagramType;
876             bAccept = false;
877             break;
878         }
879     }
880     if (bAccept) {
881         e->accept();
882     } else {
883         e->ignore();
884     }
885 }
886 
887 /**
888  * Override standard method.
889  */
dragMoveEvent(QGraphicsSceneDragDropEvent * e)890 void UMLScene::dragMoveEvent(QGraphicsSceneDragDropEvent* e)
891 {
892     e->accept();
893 }
894 
895 /**
896  * Override standard method.
897  */
dropEvent(QGraphicsSceneDragDropEvent * e)898 void UMLScene::dropEvent(QGraphicsSceneDragDropEvent *e)
899 {
900     UMLDragData::LvTypeAndID_List tidList;
901     if (!UMLDragData::getClip3TypeAndID(e->mimeData(), tidList)) {
902         DEBUG(DBG_SRC) << "UMLDragData::getClip3TypeAndID returned error";
903         return;
904     }
905     m_pos = e->scenePos();
906 
907     for(UMLDragData::LvTypeAndID_List::const_iterator it = tidList.begin(); it != tidList.end(); it++) {
908         UMLListViewItem::ListViewType lvtype = (*it)->type;
909         Uml::ID::Type id = (*it)->id;
910 
911         if (Model_Utils::typeIsDiagram(lvtype)) {
912             bool breakFlag = false;
913             UMLWidget* w = 0;
914             foreach(w, widgetList()) {
915                 if (w->isNoteWidget() && w->onWidget(e->scenePos())) {
916                     breakFlag = true;
917                     break;
918                 }
919             }
920             if (breakFlag) {
921                 NoteWidget *note = static_cast<NoteWidget*>(w);
922                 note->setDiagramLink(id);
923             }
924             continue;
925         }
926         UMLObject* o = m_doc->findObjectById(id);
927         if (!o) {
928             DEBUG(DBG_SRC) << "object id=" << Uml::ID::toString(id) << " not found";
929             continue;
930         }
931 
932         UMLWidget* newWidget = Widget_Factory::createWidget(this, o);
933         if (!newWidget) {
934             uWarning() << "could not create widget for uml object" << o->name();
935             continue;
936         }
937 
938         setupNewWidget(newWidget);
939         m_pos += QPointF(UMLWidget::DefaultMinimumSize.width(), UMLWidget::DefaultMinimumSize.height());
940         createAutoAssociations(newWidget);
941         createAutoAttributeAssociations2(newWidget);
942     }
943 }
944 
945 /**
946  * Overrides the standard operation.
947  * Calls the same method in the current tool bar state.
948  */
mouseMoveEvent(QGraphicsSceneMouseEvent * ome)949 void UMLScene::mouseMoveEvent(QGraphicsSceneMouseEvent* ome)
950 {
951     if (m_d->inMouseMoveEvent)
952         return;
953     m_d->inMouseMoveEvent = true;
954     m_d->toolBarState->mouseMove(ome);
955     m_d->inMouseMoveEvent = false;
956 }
957 
958 /**
959  * Override standard method.
960  * Calls the same method in the current tool bar state.
961  */
mousePressEvent(QGraphicsSceneMouseEvent * event)962 void UMLScene::mousePressEvent(QGraphicsSceneMouseEvent* event)
963 {
964     if (event->button() != Qt::LeftButton) {
965         event->ignore();
966         return;
967     }
968 
969     m_d->toolBarState->mousePress(event);
970 
971     // setup document
972     if (selectedItems().count() == 0)
973         UMLApp::app()->docWindow()->showDocumentation(this);
974     event->accept();
975 }
976 
977 /**
978  * Override standard method.
979  * Calls the same method in the current tool bar state.
980  */
mouseDoubleClickEvent(QGraphicsSceneMouseEvent * event)981 void UMLScene::mouseDoubleClickEvent(QGraphicsSceneMouseEvent* event)
982 {
983     if (!m_doc->loading())
984         m_d->toolBarState->mouseDoubleClick(event);
985     if (!event->isAccepted()) {
986         // show properties dialog of the scene
987         if (m_view->showPropertiesDialog() == true) {
988             m_doc->setModified();
989         }
990         event->accept();
991     }
992 }
993 
994 /**
995  * Overrides the standard operation.
996  * Calls the same method in the current tool bar state.
997  */
mouseReleaseEvent(QGraphicsSceneMouseEvent * ome)998 void UMLScene::mouseReleaseEvent(QGraphicsSceneMouseEvent* ome)
999 {
1000     m_d->toolBarState->mouseRelease(ome);
1001 }
1002 
1003 /**
1004  * Determine whether on a sequence diagram we have clicked on a line
1005  * of an Object.
1006  *
1007  * @return The widget owning the line which was clicked.
1008  *  Returns 0 if no line was clicked on.
1009  */
onWidgetLine(const QPointF & point) const1010 ObjectWidget * UMLScene::onWidgetLine(const QPointF &point) const
1011 {
1012     foreach(UMLWidget* obj, widgetList()) {
1013         ObjectWidget *ow = obj->asObjectWidget();
1014         if (ow == 0)
1015             continue;
1016         SeqLineWidget *pLine = ow->sequentialLine();
1017         if (pLine == 0) {
1018             uError() << "SeqLineWidget of " << ow->name()
1019             << " (id=" << Uml::ID::toString(ow->localID()) << ") is NULL";
1020             continue;
1021         }
1022         if (pLine->onWidget(point))
1023             return ow;
1024     }
1025     return 0;
1026 }
1027 
1028 /**
1029  * Determine whether on a sequence diagram we have clicked on
1030  * the destruction box of an Object.
1031  *
1032  * @return The widget owning the destruction box which was clicked.
1033  *  Returns 0 if no destruction box was clicked on.
1034  */
onWidgetDestructionBox(const QPointF & point) const1035 ObjectWidget * UMLScene::onWidgetDestructionBox(const QPointF &point) const
1036 {
1037     foreach(UMLWidget* obj, widgetList()) {
1038         ObjectWidget *ow = obj->asObjectWidget();
1039         if (ow == 0)
1040             continue;
1041         SeqLineWidget *pLine = ow->sequentialLine();
1042         if (pLine == 0) {
1043             uError() << "SeqLineWidget of " << ow->name()
1044                      << " (id=" << Uml::ID::toString(ow->localID()) << ") is NULL";
1045             continue;
1046         }
1047         if (pLine->onDestructionBox(point))
1048             return ow;
1049     }
1050     return 0;
1051 }
1052 
1053 /**
1054  * Return pointer to the first selected widget (for multi-selection)
1055  */
getFirstMultiSelectedWidget() const1056 UMLWidget* UMLScene::getFirstMultiSelectedWidget() const
1057 {
1058     if (selectedWidgets().size() == 0)
1059         return 0;
1060     return selectedWidgets().first();
1061 }
1062 
1063 /**
1064  * Checks the specified point against all widgets and returns the widget
1065  * for which the point is within its bounding box.
1066  * @param p Point in scene coordinates to search for
1067  * @return Returns the first widget of type UMLWidget returned by QGraphicsScene::items() for multiple matches
1068  * @return Returns NULL if the point is not inside any widget.
1069  */
widgetAt(const QPointF & p)1070 UMLWidget* UMLScene::widgetAt(const QPointF& p)
1071 {
1072     foreach(QGraphicsItem *item, items(p)) {
1073         UMLWidget *w = dynamic_cast<UMLWidget*>(item);
1074         if (w)
1075             return w;
1076     }
1077     return nullptr;
1078 }
1079 
1080 /**
1081  * Tests the given point against all associations and returns the
1082  * association widget for which the point is on the line.
1083  * Returns NULL if the point is not inside any association.
1084  * CHECK: This is the same method as in ToolBarState.
1085  */
associationAt(const QPointF & p)1086 AssociationWidget* UMLScene::associationAt(const QPointF& p)
1087 {
1088     foreach (AssociationWidget* association, associationList()) {
1089         if (association->onAssociation(p)) {
1090             return association;
1091         }
1092     }
1093     return 0;
1094 }
1095 
1096 /**
1097  * Tests the given point against all associations and returns the
1098  * association widget for which the point is on the line.
1099  * Returns NULL if the point is not inside any association.
1100  */
messageAt(const QPointF & p)1101 MessageWidget* UMLScene::messageAt(const QPointF& p)
1102 {
1103     foreach(MessageWidget *message, messageList()) {
1104         if (message->onWidget(p)) {
1105             return message;
1106         }
1107     }
1108     return 0;
1109 }
1110 
1111 /**
1112  * Sees if a message is relevant to the given widget.  If it does delete it.
1113  * @param w The widget to check messages against.
1114  */
checkMessages(ObjectWidget * w)1115 void UMLScene::checkMessages(ObjectWidget * w)
1116 {
1117     if (type() != DiagramType::Sequence) {
1118         return;
1119     }
1120 
1121     foreach(MessageWidget *obj, messageList()) {
1122         if (obj->hasObjectWidget(w)) {
1123             removeWidgetCmd(obj);
1124         }
1125     }
1126 }
1127 
1128 /**
1129  * Returns whether a widget is already on the diagram.
1130  *
1131  * @param id The id of the widget to check for.
1132  *
1133  * @return Returns pointer to the widget if it is on the diagram, NULL if not.
1134  */
widgetOnDiagram(Uml::ID::Type id)1135 UMLWidget* UMLScene::widgetOnDiagram(Uml::ID::Type id)
1136 {
1137     foreach(UMLWidget *obj, widgetList()) {
1138         if (!obj)
1139             continue;
1140         UMLWidget* w = obj->widgetWithID(id);
1141         if (w)
1142             return w;
1143     }
1144 
1145     foreach(UMLWidget *obj, messageList()) {
1146         // CHECK: Should MessageWidget reimplement widgetWithID() ?
1147         //       If yes then we should use obj->widgetWithID(id) here too.
1148         if (id == obj->id())
1149             return obj;
1150     }
1151 
1152     return 0;
1153 }
1154 
1155 /**
1156  * Returns whether a widget is already on the diagram.
1157  *
1158  * @param type The type of the widget to check for.
1159  *
1160  * @return Returns pointer to the widget if it is on the diagram, NULL if not.
1161  */
widgetOnDiagram(WidgetBase::WidgetType type)1162 UMLWidget* UMLScene::widgetOnDiagram(WidgetBase::WidgetType type)
1163 {
1164     foreach(UMLWidget *widget, widgetList()) {
1165         if (!widget)
1166             continue;
1167         if (widget->baseType() == type)
1168             return widget;
1169     }
1170     return nullptr;
1171 }
1172 
1173 /**
1174  * Finds a widget with the given ID.
1175  * Search both our UMLWidget AND MessageWidget lists.
1176  * @param id The ID of the widget to find.
1177  *
1178  * @return Returns the widget found, returns 0 if no widget found.
1179  */
findWidget(Uml::ID::Type id)1180 UMLWidget * UMLScene::findWidget(Uml::ID::Type id)
1181 {
1182     foreach(UMLWidget* obj, widgetList()) {
1183         if (!obj)
1184             continue;
1185         UMLWidget* w = obj->widgetWithID(id);
1186         if (w) {
1187             return w;
1188         }
1189     }
1190 
1191     foreach(UMLWidget* obj, messageList()) {
1192         // CHECK: Should MessageWidget reimplement widgetWithID() ?
1193         //       If yes then we should use obj->widgetWithID(id) here too.
1194         if (obj->localID() == id ||
1195             obj->id() == id)
1196             return obj;
1197     }
1198 
1199     return 0;
1200 }
1201 
1202 /**
1203  * Finds an association widget with the given ID.
1204  *
1205  * @param id The ID of the widget to find.
1206  *
1207  * @return Returns the widget found, returns 0 if no widget found.
1208  */
findAssocWidget(Uml::ID::Type id)1209 AssociationWidget * UMLScene::findAssocWidget(Uml::ID::Type id)
1210 {
1211     foreach(AssociationWidget* obj, associationList()) {
1212         UMLAssociation* umlassoc = obj->association();
1213         if (umlassoc && umlassoc->id() == id) {
1214             return obj;
1215         }
1216     }
1217     return 0;
1218 }
1219 
1220 /**
1221  * Finds an association widget with the given widgets and the given role B name.
1222  * Considers the following association types:
1223  *  at_Association, at_UniAssociation, at_Composition, at_Aggregation
1224  * This is used for seeking an attribute association.
1225  *
1226  * @param pWidgetA  Pointer to the UMLWidget of role A.
1227  * @param pWidgetB  Pointer to the UMLWidget of role B.
1228  * @param roleNameB Name at the B side of the association (the attribute name)
1229  *
1230  * @return Returns the widget found, returns 0 if no widget found.
1231  */
findAssocWidget(UMLWidget * pWidgetA,UMLWidget * pWidgetB,const QString & roleNameB)1232 AssociationWidget * UMLScene::findAssocWidget(UMLWidget *pWidgetA,
1233                                               UMLWidget *pWidgetB, const QString& roleNameB)
1234 {
1235     foreach(AssociationWidget* assoc, associationList()) {
1236         const Uml::AssociationType::Enum testType = assoc->associationType();
1237         if (testType != Uml::AssociationType::Association &&
1238                 testType != Uml::AssociationType::UniAssociation &&
1239                 testType != Uml::AssociationType::Composition &&
1240                 testType != Uml::AssociationType::Aggregation &&
1241                 testType != Uml::AssociationType::Relationship) {
1242             continue;
1243         }
1244 
1245         if (pWidgetA->id() == assoc->widgetIDForRole(Uml::RoleType::A) &&
1246                 pWidgetB->id() == assoc->widgetIDForRole(Uml::RoleType::B) &&
1247                 assoc->roleName(Uml::RoleType::B) == roleNameB) {
1248             return assoc;
1249         }
1250     }
1251     return 0;
1252 }
1253 
1254 /**
1255  * Finds an association widget with the given type and widgets.
1256  *
1257  * @param at  The AssociationType of the widget to find.
1258  * @param pWidgetA Pointer to the UMLWidget of role A.
1259  * @param pWidgetB Pointer to the UMLWidget of role B.
1260  *
1261  * @return Returns the widget found, returns 0 if no widget found.
1262  */
findAssocWidget(AssociationType::Enum at,UMLWidget * pWidgetA,UMLWidget * pWidgetB)1263 AssociationWidget * UMLScene::findAssocWidget(AssociationType::Enum at,
1264                                               UMLWidget *pWidgetA, UMLWidget *pWidgetB)
1265 {
1266     foreach(AssociationWidget* assoc, associationList()) {
1267         Uml::AssociationType::Enum testType = assoc->associationType();
1268         if (testType != at) {
1269             continue;
1270         }
1271 
1272         if (pWidgetA->id() == assoc->widgetIDForRole(Uml::RoleType::A) &&
1273                 pWidgetB->id() == assoc->widgetIDForRole(Uml::RoleType::B)) {
1274             return assoc;
1275         }
1276     }
1277     return 0;
1278 }
1279 
1280 /**
1281  * Remove a widget from view (undo command)
1282  *
1283  * @param o  The widget to remove.
1284  */
removeWidget(UMLWidget * o)1285 void UMLScene::removeWidget(UMLWidget * o)
1286 {
1287     UMLApp::app()->executeCommand(new CmdRemoveWidget(o));
1288 }
1289 
1290 /**
1291  * Remove a widget from view (undo command)
1292  *
1293  * @param o  The widget to remove.
1294  */
removeWidget(AssociationWidget * w)1295 void UMLScene::removeWidget(AssociationWidget* w)
1296 {
1297     UMLApp::app()->executeCommand(new CmdRemoveWidget(w));
1298 }
1299 
1300 /**
1301  * Remove a widget from view.
1302  *
1303  * @param o  The widget to remove.
1304  */
removeWidgetCmd(UMLWidget * o)1305 void UMLScene::removeWidgetCmd(UMLWidget * o)
1306 {
1307     if (!o)
1308         return;
1309 
1310     emit sigWidgetRemoved(o);
1311 
1312     removeAssociations(o);
1313 
1314     removeOwnedWidgets(o);
1315 
1316     WidgetBase::WidgetType t = o->baseType();
1317     if (type() == DiagramType::Sequence && t == WidgetBase::wt_Object) {
1318         checkMessages(static_cast<ObjectWidget*>(o));
1319     }
1320 
1321     o->cleanup();
1322     if (!UMLApp::app()->shuttingDown()) {
1323         /* Manipulating the Selected flag during shutdown may crash:
1324            UMLWidget::setSelectedFlag ->
1325              WidgetBase::setSelected ->
1326                 QGraphicsObjectWrapper::setSelected ->
1327                   QGraphicsItem::setSelected ->
1328                     QGraphicsObjectWrapper::itemChange ->
1329                       UMLWidget::setSelected ->
1330                         UMLApp::slotCopyChanged ->
1331                           UMLListView::selectedItemsCount ->
1332                             UMLListView::selectedItems ->
1333                               Crash somewhere in qobject_cast
1334                               (listview already destructed?)
1335          */
1336         o->setSelectedFlag(false);
1337     }
1338     disconnect(this, SIGNAL(sigFillColorChanged(Uml::ID::Type)), o, SLOT(slotFillColorChanged(Uml::ID::Type)));
1339     disconnect(this, SIGNAL(sigLineColorChanged(Uml::ID::Type)), o, SLOT(slotLineColorChanged(Uml::ID::Type)));
1340     disconnect(this, SIGNAL(sigTextColorChanged(Uml::ID::Type)), o, SLOT(slotTextColorChanged(Uml::ID::Type)));
1341     removeItem(o);
1342     o->deleteLater();
1343     m_doc->setModified(true);
1344 }
1345 
1346 /**
1347  * Remove all widgets that have given widget as owner.
1348  *
1349  * @param o The owner widget that will be removed.
1350  */
removeOwnedWidgets(UMLWidget * o)1351 void UMLScene::removeOwnedWidgets(UMLWidget* o)
1352 {
1353     foreach(QGraphicsItem* item, o->childItems()) {
1354         UMLWidget* widget = dynamic_cast<UMLWidget*>(item);
1355         if ((widget != 0) &&
1356             (widget->isPinWidget() ||
1357              widget->isPortWidget())) {
1358             removeWidgetCmd(widget);
1359         }
1360     }
1361 }
1362 
1363 /**
1364  * Returns background color
1365  */
backgroundColor() const1366 const QColor& UMLScene::backgroundColor() const
1367 {
1368     return backgroundBrush().color();
1369 }
1370 
1371 /**
1372  * Returns whether to use the fill/background color
1373  */
useFillColor() const1374 bool UMLScene::useFillColor() const
1375 {
1376     return m_Options.uiState.useFillColor;
1377 }
1378 
1379 /**
1380  * Sets whether to use the fill/background color
1381  */
setUseFillColor(bool ufc)1382 void UMLScene::setUseFillColor(bool ufc)
1383 {
1384     m_Options.uiState.useFillColor = ufc;
1385 }
1386 
1387 /**
1388  * Gets the smallest area to print.
1389  *
1390  * @return Returns the smallest area to print.
1391  */
diagramRect()1392 QRectF UMLScene::diagramRect()
1393 {
1394     return itemsBoundingRect();
1395 }
1396 
1397 /**
1398  * Returns a list of selected widgets
1399  * @return list of selected widgets based on class UMLWidget
1400  * @note This method returns widgets including message widgets, but no association widgets
1401  */
selectedWidgets() const1402 UMLWidgetList UMLScene::selectedWidgets() const
1403 {
1404     QList<QGraphicsItem *> items = selectedItems();
1405 
1406     UMLWidgetList widgets;
1407     foreach(QGraphicsItem *item, items) {
1408         UMLWidget *w = dynamic_cast<UMLWidget*>(item);
1409         if (w)
1410             widgets.append(w);
1411     }
1412     return widgets;
1413 }
1414 
1415 /**
1416  * Returns a list of selected association widgets
1417  * @return list of selected widgets based on class AssociationWidget
1418  */
selectedAssociationWidgets() const1419 AssociationWidgetList UMLScene::selectedAssociationWidgets() const
1420 {
1421     QList<QGraphicsItem *> items = selectedItems();
1422 
1423     AssociationWidgetList widgets;
1424     foreach(QGraphicsItem *item, items) {
1425         AssociationWidget *w = dynamic_cast<AssociationWidget*>(item);
1426         if (w)
1427             widgets.append(w);
1428     }
1429     return widgets;
1430 }
1431 
1432 /**
1433  * Returns a list of selected message widgets
1434  * @return list of selected widgets based on class MessageWidget
1435  */
selectedMessageWidgets() const1436 UMLWidgetList UMLScene::selectedMessageWidgets() const
1437 {
1438     QList<QGraphicsItem *> items = selectedItems();
1439 
1440     UMLWidgetList widgets;
1441     foreach(QGraphicsItem *item, items) {
1442         MessageWidget *w = dynamic_cast<MessageWidget*>(item);
1443         if (w) {
1444             widgets.append(w);
1445         } else {
1446             WidgetBase *wb = dynamic_cast<WidgetBase*>(item);
1447             QString name;
1448             if (wb)
1449                 name = wb->name();
1450             DEBUG(DBG_SRC) << name << " is not a MessageWidget";
1451         }
1452     }
1453     return widgets;
1454 }
1455 
1456 /**
1457  *  Clear the selected widgets list.
1458  */
clearSelected()1459 void UMLScene::clearSelected()
1460 {
1461     QList<QGraphicsItem *> items = selectedItems();
1462     foreach(QGraphicsItem *item, items) {
1463         WidgetBase *wb = dynamic_cast<WidgetBase*>(item);
1464         if (wb) {
1465             wb->setSelected(false);
1466         }
1467     }
1468     clearSelection();
1469     //m_doc->enableCutCopy(false);
1470 }
1471 
1472 /**
1473  * Move all the selected widgets by a relative X and Y offset.
1474  * TODO: Only used in UMLApp::handleCursorKeyReleaseEvent
1475  *
1476  * @param dX The distance to move horizontally.
1477  * @param dY The distance to move vertically.
1478  */
moveSelectedBy(qreal dX,qreal dY)1479 void UMLScene::moveSelectedBy(qreal dX, qreal dY)
1480 {
1481     // DEBUG(DBG_SRC) << "********** m_selectedList count=" << m_selectedList.count();
1482     foreach(UMLWidget *w, selectedWidgets()) {
1483         w->moveByLocal(dX, dY);
1484     }
1485 }
1486 
1487 /**
1488  * Set the useFillColor variable to all selected widgets
1489  *
1490  * @param useFC The state to set the widget to.
1491  */
selectionUseFillColor(bool useFC)1492 void UMLScene::selectionUseFillColor(bool useFC)
1493 {
1494     if (useFC) {
1495         UMLApp::app()->beginMacro(i18n("Use fill color"));
1496     } else {
1497         UMLApp::app()->beginMacro(i18n("No fill color"));
1498     }
1499 
1500     foreach(UMLWidget* widget, selectedWidgets()) {
1501         widget->setUseFillColor(useFC);
1502     }
1503 
1504     UMLApp::app()->endMacro();
1505 }
1506 
1507 /**
1508  * Set the font for all the currently selected items.
1509  */
selectionSetFont(const QFont & font)1510 void UMLScene::selectionSetFont(const QFont &font)
1511 {
1512     UMLApp::app()->beginMacro(i18n("Change font"));
1513 
1514     foreach(UMLWidget* temp, selectedWidgets()) {
1515         temp->setFont(font);
1516     }
1517 
1518     UMLApp::app()->endMacro();
1519 }
1520 
1521 /**
1522  * Set the line color for all the currently selected items.
1523  */
selectionSetLineColor(const QColor & color)1524 void UMLScene::selectionSetLineColor(const QColor &color)
1525 {
1526     UMLApp::app()->beginMacro(i18n("Change line color"));
1527 
1528     foreach(UMLWidget *temp, selectedWidgets()) {
1529         temp->setLineColor(color);
1530     }
1531     AssociationWidgetList assoclist = selectedAssocs();
1532     foreach(AssociationWidget *aw, assoclist) {
1533         aw->setLineColor(color);
1534     }
1535 
1536     UMLApp::app()->endMacro();
1537 }
1538 
1539 /**
1540  * Set the line width for all the currently selected items.
1541  */
selectionSetLineWidth(uint width)1542 void UMLScene::selectionSetLineWidth(uint width)
1543 {
1544     UMLApp::app()->beginMacro(i18n("Change line width"));
1545 
1546     foreach(UMLWidget* temp, selectedWidgets()) {
1547         temp->setLineWidth(width);
1548         temp->setUsesDiagramLineWidth(false);
1549     }
1550     AssociationWidgetList assoclist = selectedAssocs();
1551     foreach(AssociationWidget *aw, assoclist) {
1552         aw->setLineWidth(width);
1553         aw->setUsesDiagramLineWidth(false);
1554     }
1555 
1556     UMLApp::app()->endMacro();
1557 }
1558 
1559 /**
1560  * Set the fill color for all the currently selected items.
1561  */
selectionSetFillColor(const QColor & color)1562 void UMLScene::selectionSetFillColor(const QColor &color)
1563 {
1564     UMLApp::app()->beginMacro(i18n("Change fill color"));
1565 
1566     foreach(UMLWidget* widget, selectedWidgets()) {
1567         widget->setFillColor(color);
1568         widget->setUsesDiagramFillColor(false);
1569     }
1570 
1571     UMLApp::app()->endMacro();
1572 }
1573 
1574 /**
1575  * Set or unset the visual property (show ..) setting of all selected items.
1576  */
selectionSetVisualProperty(ClassifierWidget::VisualProperty property,bool value)1577 void UMLScene::selectionSetVisualProperty(ClassifierWidget::VisualProperty property, bool value)
1578 {
1579     UMLApp::app()->beginMacro(i18n("Change visual property"));
1580 
1581     foreach(UMLWidget *temp, selectedWidgets()) {
1582         ClassifierWidget *cw = temp->asClassifierWidget();
1583         cw->setVisualProperty(property, value);
1584     }
1585 
1586     UMLApp::app()->endMacro();
1587 }
1588 
1589 /**
1590  * Unselect child widgets when their owner is already selected.
1591  */
unselectChildrenOfSelectedWidgets()1592 void UMLScene::unselectChildrenOfSelectedWidgets()
1593 {
1594     foreach(UMLWidget* widget, selectedWidgets()) {
1595         if (widget->isPinWidget() ||
1596             widget->isPortWidget()) {
1597             foreach(UMLWidget* potentialParentWidget, selectedWidgets()) {
1598                 if (widget->parentItem() == potentialParentWidget) {
1599                     widget->setSelectedFlag(false);
1600                 }
1601             }
1602         }
1603     }
1604 }
1605 
1606 /**
1607  * Delete the selected widgets list and the widgets in it.
1608  */
deleteSelection()1609 void UMLScene::deleteSelection()
1610 {
1611     AssociationWidgetList selectedAssociations = selectedAssociationWidgets();
1612     int selectionCount = selectedWidgets().count() + selectedAssociations.count();
1613 
1614     if (selectionCount == 0)
1615         return;
1616 
1617     // check related associations
1618     bool hasAssociations = false;
1619     foreach(UMLWidget* widget, selectedWidgets()) {
1620         if (widget->isTextWidget() && widget->asFloatingTextWidget()->textRole() != Uml::TextRole::Floating) {
1621             continue;
1622         }
1623         if (widget->isMessageWidget() || widget->associationWidgetList().size() > 0)
1624             hasAssociations = true;
1625     }
1626 
1627     if (hasAssociations && !Dialog_Utils::askDeleteAssociation())
1628         return;
1629 
1630     UMLApp::app()->beginMacro(i18n("Delete widgets"));
1631 
1632     unselectChildrenOfSelectedWidgets();
1633 
1634     foreach(UMLWidget* widget, selectedWidgets()) {
1635         //  Don't delete text widget that are connect to associations as these will
1636         //  be cleaned up by the associations.
1637         if (widget->isTextWidget() &&
1638                 widget->asFloatingTextWidget()->textRole() != Uml::TextRole::Floating) {
1639             widget->setSelectedFlag(false);
1640             widget->hide();
1641         } else if (widget->isPortWidget()) {
1642             UMLObject *o = widget->umlObject();
1643             removeWidget(widget);
1644             if (o)
1645                 UMLApp::app()->executeCommand(new CmdRemoveUMLObject(o));
1646             // message widgets are handled later
1647         } else if (!widget->isMessageWidget()){
1648             removeWidget(widget);
1649         }
1650     }
1651 
1652     // Delete any selected associations.
1653     foreach(AssociationWidget* assocwidget, selectedAssociations) {
1654         removeWidget(assocwidget);
1655     }
1656 
1657     // we also have to remove selected messages from sequence diagrams
1658     foreach(UMLWidget* cur_msgWgt, selectedMessageWidgets()) {
1659         removeWidget(cur_msgWgt);
1660     }
1661 
1662     //make sure list empty - it should be anyway, just a check.
1663     clearSelected();
1664 
1665     UMLApp::app()->endMacro();
1666 }
1667 
1668 /**
1669  * resize selected widgets
1670  */
resizeSelection()1671 void UMLScene::resizeSelection()
1672 {
1673     int selectionCount = selectedWidgets().count();
1674 
1675     if (selectionCount > 1) {
1676         UMLApp::app()->beginMacro(i18n("Resize widgets"));
1677     }
1678 
1679     if (selectedCount() == 0)
1680         return;
1681     foreach(UMLWidget *w, selectedWidgets()) {
1682         w->resize();
1683     }
1684     m_doc->setModified();
1685 
1686     if (selectionCount > 1) {
1687         UMLApp::app()->endMacro();
1688     }
1689 }
1690 
1691 /**
1692  * Selects all widgets
1693  */
selectAll()1694 void UMLScene::selectAll()
1695 {
1696     selectWidgets(sceneRect().left(), sceneRect().top(), sceneRect().right(), sceneRect().bottom());
1697 }
1698 
1699 /**
1700  * Returns true if this diagram resides in an externalized folder.
1701  * CHECK: It is probably cleaner to move this to the UMLListViewItem.
1702  */
isSavedInSeparateFile()1703 bool UMLScene::isSavedInSeparateFile()
1704 {
1705     if (optionState().generalState.tabdiagrams) {
1706         // Umbrello currently does not support external folders
1707         // when tabbed diagrams are enabled.
1708         return false;
1709     }
1710     const QString msgPrefix(QLatin1String("UMLScene::isSavedInSeparateFile(") + name() + QLatin1String("): "));
1711     UMLListView *listView = UMLApp::app()->listView();
1712     UMLListViewItem *lvItem = listView->findItem(m_nID);
1713     if (lvItem == 0) {
1714         uError() << msgPrefix
1715                  << "listView->findUMLObject(this) returns false";
1716         return false;
1717     }
1718     UMLListViewItem *parentItem = dynamic_cast<UMLListViewItem*>(lvItem->parent());
1719     if (parentItem == 0) {
1720         uError() << msgPrefix
1721                  << "parent item in listview is not a UMLListViewItem (?)";
1722         return false;
1723     }
1724     const UMLListViewItem::ListViewType lvt = parentItem->type();
1725     if (! Model_Utils::typeIsFolder(lvt))
1726         return false;
1727     UMLFolder *modelFolder = parentItem->umlObject()->asUMLFolder();
1728     if (modelFolder == 0) {
1729         uError() << msgPrefix
1730                  << "parent model object is not a UMLFolder (?)";
1731         return false;
1732     }
1733     QString folderFile = modelFolder->folderFile();
1734     return !folderFile.isEmpty();
1735 }
1736 
collisions(const QPointF & p,int delta)1737 UMLSceneItemList UMLScene::collisions(const QPointF &p, int delta)
1738 {
1739     QPointF a = p-QPointF(delta, delta);
1740     QPointF b = p+QPointF(delta, delta);
1741     QList<QGraphicsItem *> list = items(QRectF(a, b));
1742     return list;
1743 }
1744 
1745 /**
1746  * Calls setSelected on the given UMLWidget and enters
1747  * it into the m_selectedList while making sure it is
1748  * there only once.
1749  */
makeSelected(UMLWidget * uw)1750 void UMLScene::makeSelected(UMLWidget* uw)
1751 {
1752     if (uw) {
1753         uw->setSelected(true);
1754     }
1755 }
1756 
1757 /**
1758  * Selects all the widgets of the given association widget.
1759  */
selectWidgetsOfAssoc(AssociationWidget * a)1760 void UMLScene::selectWidgetsOfAssoc(AssociationWidget * a)
1761 {
1762     if (a) {
1763         a->setSelected(true);
1764         //select the two widgets
1765         makeSelected(a->widgetForRole(Uml::RoleType::A));
1766         makeSelected(a->widgetForRole(Uml::RoleType::B));
1767         //select all the text
1768         makeSelected(a->multiplicityWidget(Uml::RoleType::A));
1769         makeSelected(a->multiplicityWidget(Uml::RoleType::B));
1770         makeSelected(a->roleWidget(Uml::RoleType::A));
1771         makeSelected(a->roleWidget(Uml::RoleType::B));
1772         makeSelected(a->changeabilityWidget(Uml::RoleType::A));
1773         makeSelected(a->changeabilityWidget(Uml::RoleType::B));
1774     }
1775 }
1776 
1777 /**
1778  * Selects all the widgets within an internally kept rectangle.
1779  */
selectWidgets(qreal px,qreal py,qreal qx,qreal qy)1780 void UMLScene::selectWidgets(qreal px, qreal py, qreal qx, qreal qy)
1781 {
1782     clearSelected();
1783 
1784     QRectF  rect;
1785     if (px <= qx) {
1786         rect.setLeft(px);
1787         rect.setRight(qx);
1788     } else {
1789         rect.setLeft(qx);
1790         rect.setRight(px);
1791     }
1792     if (py <= qy) {
1793         rect.setTop(py);
1794         rect.setBottom(qy);
1795     } else {
1796         rect.setTop(qy);
1797         rect.setBottom(py);
1798     }
1799 
1800     // Select UMLWidgets that fall within the selection rectangle
1801     foreach(UMLWidget* temp, widgetList()) {
1802         uIgnoreZeroPointer(temp);
1803         selectWidget(temp, &rect);
1804     }
1805 
1806     // Select messages that fall within the selection rectangle
1807     foreach(MessageWidget* temp, messageList()) {
1808         selectWidget(temp->asUMLWidget(), &rect);
1809     }
1810 
1811     // Select associations of selected widgets
1812     selectAssociations(true);
1813 
1814     // Automatically select all messages if two object widgets are selected
1815     foreach(MessageWidget *w, messageList()) {
1816         if (w->objectWidget(Uml::RoleType::A) &&
1817             w->objectWidget(Uml::RoleType::B) &&
1818             w->objectWidget(Uml::RoleType::A)->isSelected() &&
1819             w->objectWidget(Uml::RoleType::B)->isSelected()) {
1820             makeSelected(w);
1821         }
1822     }
1823 }
1824 
1825 /**
1826  * Select a single widget
1827  *
1828  * If QRectF* rect is provided, the selection is only made if the widget is
1829  * visible within the rectangle.
1830  */
selectWidget(UMLWidget * widget,QRectF * rect)1831 void UMLScene::selectWidget(UMLWidget* widget, QRectF* rect)
1832 {
1833     if (rect == 0) {
1834         makeSelected(widget);
1835         return;
1836     }
1837 
1838     int x = widget->x();
1839     int y = widget->y();
1840     int w = widget->width();
1841     int h = widget->height();
1842     QRectF  rect2(x, y, w, h);
1843 
1844     //see if any part of widget is in the rectangle
1845     if (!rect->intersects(rect2)) {
1846         return;
1847     }
1848 
1849     //if it is text that is part of an association then select the association
1850     //and the objects that are connected to it.
1851     if (widget->isTextWidget()) {
1852         FloatingTextWidget *ft = widget->asFloatingTextWidget();
1853         Uml::TextRole::Enum t = ft->textRole();
1854         LinkWidget *lw = ft->link();
1855         MessageWidget * mw = dynamic_cast<MessageWidget*>(lw);
1856         if (mw) {
1857             makeSelected(mw);
1858         } else if (t != Uml::TextRole::Floating) {
1859             AssociationWidget * a = dynamic_cast<AssociationWidget*>(lw);
1860             if (a)
1861                 selectWidgetsOfAssoc(a);
1862         }
1863     } else if (widget->isMessageWidget()) {
1864         MessageWidget *mw = widget->asMessageWidget();
1865         makeSelected(mw);
1866     }
1867     if (widget->isVisible()) {
1868         makeSelected(widget);
1869     }
1870 }
1871 
1872 /**
1873  * Selects all the widgets from a list.
1874  */
selectWidgets(UMLWidgetList & widgets)1875 void UMLScene::selectWidgets(UMLWidgetList &widgets)
1876 {
1877     foreach (UMLWidget* widget, widgets)
1878         makeSelected(widget);
1879 }
1880 
1881 /**
1882  * Returns the PNG picture of the paste operation.
1883  * @param diagram the class to store PNG picture of the paste operation.
1884  * @param rect the area of the diagram to copy
1885  */
getDiagram(QPixmap & diagram,const QRectF & rect)1886 void  UMLScene::getDiagram(QPixmap &diagram, const QRectF &rect)
1887 {
1888     DEBUG(DBG_SRC) << "rect=" << rect << ", pixmap=" << diagram.rect();
1889     QPainter painter(&diagram);
1890     painter.fillRect(0, 0, rect.width(), rect.height(), Qt::white);
1891     getDiagram(painter, rect);
1892 }
1893 
1894 /**
1895  * Paint diagram to the paint device
1896  * @param painter the QPainter to which the diagram is painted
1897  * @param source the area of the diagram to copy
1898  * @param target the rect where to paint into
1899  */
getDiagram(QPainter & painter,const QRectF & source,const QRectF & target)1900 void  UMLScene::getDiagram(QPainter &painter, const QRectF &source, const QRectF &target)
1901 {
1902     DEBUG(DBG_SRC) << "painter=" << painter.window() << ", source=" << source << ", target=" << target;
1903     //TODO unselecting and selecting later doesn't work now as the selection is
1904     //cleared in UMLSceneImageExporter. Check if the anything else than the
1905     //following is needed and, if it works, remove the clearSelected in
1906     //UMLSceneImageExporter and UMLSceneImageExporterModel
1907 
1908     UMLWidgetList selected = selectedWidgets();
1909     foreach(UMLWidget* widget, selected) {
1910         widget->setSelected(false);
1911     }
1912     AssociationWidgetList selectedAssociationsList = selectedAssocs();
1913 
1914     foreach(AssociationWidget* association, selectedAssociationsList) {
1915         association->setSelected(false);
1916     }
1917 
1918     // we don't want to get the grid
1919     bool showSnapGrid = isSnapGridVisible();
1920     setSnapGridVisible(false);
1921 
1922     const int sourceMargin = 1;
1923     QRectF alignedSource(source);
1924     alignedSource.adjust(-sourceMargin, -sourceMargin, sourceMargin, sourceMargin);
1925 
1926     uDebug() << "TODO: Check if this render method is identical to cavnas()->drawArea()";
1927     // [PORT]
1928     render(&painter, target, alignedSource, Qt::KeepAspectRatio);
1929 
1930     setSnapGridVisible(showSnapGrid);
1931 
1932     //select again
1933     foreach(UMLWidget* widget, selected) {
1934         widget->setSelected(true);
1935     }
1936     foreach(AssociationWidget* association, selectedAssociationsList) {
1937         association->setSelected(true);
1938     }
1939 }
1940 
1941 /**
1942  * Returns the imageExporter used to export the view.
1943  *
1944  * @return The imageExporter used to export the view.
1945  */
getImageExporter()1946 UMLViewImageExporter* UMLScene::getImageExporter()
1947 {
1948     return m_pImageExporter;
1949 }
1950 
1951 /**
1952  * makes this view the active view by asking the document to show us
1953  */
slotActivate()1954 void UMLScene::slotActivate()
1955 {
1956     m_doc->changeCurrentView(ID());
1957 }
1958 
1959 /**
1960  * Activate all the objects and associations after a load from the clipboard
1961  */
activate()1962 void UMLScene::activate()
1963 {
1964     //Activate Regular widgets then activate  messages
1965     foreach(UMLWidget* obj, widgetList()) {
1966         uIgnoreZeroPointer(obj);
1967         //If this UMLWidget is already activated or is a MessageWidget then skip it
1968         if (obj->isActivated() || obj->isMessageWidget()) {
1969             continue;
1970         }
1971 
1972        if (obj->activate()) {
1973            obj->setVisible(true);
1974        } else {
1975            removeItem(obj);
1976            delete obj;
1977        }
1978     }//end foreach
1979 
1980     //Activate Message widgets
1981     foreach(UMLWidget* obj, messageList()) {
1982         //If this MessageWidget is already activated then skip it
1983         if (obj->isActivated())
1984             continue;
1985 
1986         obj->activate(m_doc->changeLog());
1987         obj->setVisible(true);
1988 
1989     }//end foreach
1990 
1991     // Activate all association widgets
1992 
1993     foreach(AssociationWidget* aw, associationList()) {
1994         if (aw->activate()) {
1995             if (m_PastePoint.x() != 0) {
1996                 int x = m_PastePoint.x() - m_pos.x();
1997                 int y = m_PastePoint.y() - m_pos.y();
1998                 aw->moveEntireAssoc(x, y);
1999             }
2000         } else {
2001             removeWidgetCmd(aw);
2002             delete aw;
2003         }
2004     }
2005 }
2006 
2007 /**
2008  * Return the amount of widgets selected.
2009  *
2010  * @param filterText  When true, do NOT count floating text widgets that
2011  *                    belong to other widgets (i.e. only count TextRole::Floating.)
2012  *                    Default: Count all widgets.
2013  * @return  Number of widgets selected.
2014  */
selectedCount(bool filterText) const2015 int UMLScene::selectedCount(bool filterText) const
2016 {
2017     if (!filterText)
2018         return selectedWidgets().count();
2019     int counter = 0;
2020     foreach(UMLWidget* temp, selectedWidgets()) {
2021         if (temp->isTextWidget()) {
2022             const FloatingTextWidget *ft = static_cast<const FloatingTextWidget*>(temp);
2023             if (ft->textRole() == TextRole::Floating)
2024                 counter++;
2025         } else {
2026             counter++;
2027         }
2028     }
2029     return counter;
2030 }
2031 
2032 /**
2033  * Fills the List with all the selected widgets from the diagram
2034  * The list can be filled with all the selected widgets, or be filtered to prevent
2035  * text widgets other than tr_Floating to be append.
2036  *
2037  * @param filterText Don't append the text, unless their role is tr_Floating
2038  * @return           The UMLWidgetList to fill.
2039  */
selectedWidgetsExt(bool filterText)2040 UMLWidgetList UMLScene::selectedWidgetsExt(bool filterText /*= true*/)
2041 {
2042     UMLWidgetList widgetList;
2043 
2044     foreach(UMLWidget* widgt, selectedWidgets()) {
2045         if (filterText && widgt->isTextWidget()) {
2046             FloatingTextWidget *ft = widgt->asFloatingTextWidget();
2047             if (ft->textRole() == Uml::TextRole::Floating)
2048                 widgetList.append(widgt);
2049         } else {
2050             widgetList.append(widgt);
2051         }
2052     }
2053     return widgetList;
2054 }
2055 
2056 /**
2057  * Returns a list with all the selected associations from the diagram
2058  */
selectedAssocs()2059 AssociationWidgetList UMLScene::selectedAssocs()
2060 {
2061     AssociationWidgetList assocWidgetList;
2062 
2063     foreach(AssociationWidget* assocwidget, associationList()) {
2064         if (assocwidget->isSelected())
2065             assocWidgetList.append(assocwidget);
2066     }
2067     return assocWidgetList;
2068 }
2069 
2070 /**
2071  * Adds a floating text widget to the view
2072  */
addFloatingTextWidget(FloatingTextWidget * pWidget)2073 void UMLScene::addFloatingTextWidget(FloatingTextWidget* pWidget)
2074 {
2075     int wX = pWidget->x();
2076     int wY = pWidget->y();
2077     bool xIsOutOfRange = (wX < sceneRect().left() || wX > sceneRect().right());
2078     bool yIsOutOfRange = (wY < sceneRect().top() || wY > sceneRect().bottom());
2079     if (xIsOutOfRange || yIsOutOfRange) {
2080         QString name = pWidget->name();
2081         if (name.isEmpty()) {
2082             FloatingTextWidget *ft = pWidget->asFloatingTextWidget();
2083             if (ft)
2084                 name = ft->displayText();
2085         }
2086         DEBUG(DBG_SRC) << name << " type=" << pWidget->baseTypeStr() << ": position ("
2087                        << wX << "," << wY << ") is out of range";
2088         if (xIsOutOfRange) {
2089             pWidget->setX(0);
2090             wX = 0;
2091         }
2092         if (yIsOutOfRange) {
2093             pWidget->setY(0);
2094             wY = 0;
2095         }
2096     }
2097 
2098     addWidgetCmd(pWidget);
2099 }
2100 
2101 /**
2102  * Adds an association to the view from the given data.
2103  * Use this method when pasting.
2104  */
addAssociation(AssociationWidget * pAssoc,bool isPasteOperation)2105 bool UMLScene::addAssociation(AssociationWidget* pAssoc, bool isPasteOperation)
2106 {
2107     if (!pAssoc) {
2108         return false;
2109     }
2110     const Uml::AssociationType::Enum assocType = pAssoc->associationType();
2111 
2112     if (isPasteOperation) {
2113         IDChangeLog * log = m_doc->changeLog();
2114 
2115         if (!log) {
2116             return false;
2117         }
2118 
2119         Uml::ID::Type ida = Uml::ID::None, idb = Uml::ID::None;
2120         if (type() == DiagramType::Collaboration || type() == DiagramType::Sequence) {
2121             //check local log first
2122             ida = m_pIDChangesLog->findNewID(pAssoc->widgetIDForRole(Uml::RoleType::A));
2123             idb = m_pIDChangesLog->findNewID(pAssoc->widgetIDForRole(Uml::RoleType::B));
2124             //if either is still not found and assoc type is anchor
2125             //we are probably linking to a notewidet - else an error
2126             if (ida == Uml::ID::None && assocType == Uml::AssociationType::Anchor)
2127                 ida = log->findNewID(pAssoc->widgetIDForRole(Uml::RoleType::A));
2128             if (idb == Uml::ID::None && assocType == Uml::AssociationType::Anchor)
2129                 idb = log->findNewID(pAssoc->widgetIDForRole(Uml::RoleType::B));
2130         } else {
2131             Uml::ID::Type oldIdA = pAssoc->widgetIDForRole(Uml::RoleType::A);
2132             Uml::ID::Type oldIdB = pAssoc->widgetIDForRole(Uml::RoleType::B);
2133             ida = log->findNewID(oldIdA);
2134             if (ida == Uml::ID::None) {  // happens after a cut
2135                 if (oldIdA == Uml::ID::None) {
2136                     return false;
2137                 }
2138                 ida = oldIdA;
2139             }
2140             idb = log->findNewID(oldIdB);
2141             if (idb == Uml::ID::None) {  // happens after a cut
2142                 if (oldIdB == Uml::ID::None) {
2143                     return false;
2144                 }
2145                 idb = oldIdB;
2146             }
2147         }
2148         if (ida == Uml::ID::None || idb == Uml::ID::None) {
2149             return false;
2150         }
2151         // cant do this anymore.. may cause problem for pasting
2152         //      pAssoc->setWidgetID(ida, A);
2153         //      pAssoc->setWidgetID(idb, B);
2154         pAssoc->setWidgetForRole(findWidget(ida), Uml::RoleType::A);
2155         pAssoc->setWidgetForRole(findWidget(idb), Uml::RoleType::B);
2156     }
2157 
2158     UMLWidget * pWidgetA = findWidget(pAssoc->widgetIDForRole(Uml::RoleType::A));
2159     UMLWidget * pWidgetB = findWidget(pAssoc->widgetIDForRole(Uml::RoleType::B));
2160     //make sure valid widget ids
2161     if (!pWidgetA || !pWidgetB) {
2162         return false;
2163     }
2164 
2165     //make sure there isn't already the same assoc
2166 
2167     foreach(AssociationWidget* assocwidget, associationList()) {
2168         if (*pAssoc == *assocwidget)
2169             // this is nuts. Paste operation wants to know if 'true'
2170             // for duplicate, but loadFromXMI1 needs 'false' value
2171             return (isPasteOperation ? true : false);
2172     }
2173 
2174     addWidgetCmd(pAssoc);
2175 
2176     FloatingTextWidget *ft[5] = { pAssoc->nameWidget(),
2177                                   pAssoc->roleWidget(Uml::RoleType::A),
2178                                   pAssoc->roleWidget(Uml::RoleType::B),
2179                                   pAssoc->multiplicityWidget(Uml::RoleType::A),
2180                                   pAssoc->multiplicityWidget(Uml::RoleType::B)
2181     };
2182     for (int i = 0; i < 5; i++) {
2183         FloatingTextWidget *flotxt = ft[i];
2184         if (flotxt) {
2185             flotxt->updateGeometry();
2186             addFloatingTextWidget(flotxt);
2187         }
2188     }
2189 
2190     return true;
2191 }
2192 
2193 /**
2194  * Activate the view after a load a new file
2195  */
activateAfterLoad(bool bUseLog)2196 void UMLScene::activateAfterLoad(bool bUseLog)
2197 {
2198     if (m_isActivated) {
2199         return;
2200     }
2201     if (bUseLog) {
2202         beginPartialWidgetPaste();
2203     }
2204 
2205     //now activate them all
2206     activate();
2207 
2208     if (bUseLog) {
2209         endPartialWidgetPaste();
2210     }
2211     m_view->centerOn(0, 0);
2212     m_isActivated = true;
2213 }
2214 
beginPartialWidgetPaste()2215 void UMLScene::beginPartialWidgetPaste()
2216 {
2217     delete m_pIDChangesLog;
2218     m_pIDChangesLog = 0;
2219 
2220     m_pIDChangesLog = new IDChangeLog();
2221     m_bPaste = true;
2222 }
2223 
endPartialWidgetPaste()2224 void UMLScene::endPartialWidgetPaste()
2225 {
2226     delete m_pIDChangesLog;
2227     m_pIDChangesLog = 0;
2228 
2229     m_bPaste = false;
2230 }
2231 
2232 /**
2233  * Removes an AssociationWidget from a diagram
2234  * Physically deletes the AssociationWidget passed in.
2235  *
2236  * @param pAssoc  Pointer to the AssociationWidget.
2237  */
removeWidgetCmd(AssociationWidget * pAssoc)2238 void UMLScene::removeWidgetCmd(AssociationWidget* pAssoc)
2239 {
2240     if (!pAssoc)
2241         return;
2242 
2243     emit sigAssociationRemoved(pAssoc);
2244 
2245     pAssoc->cleanup();
2246     removeItem(pAssoc);
2247     pAssoc->deleteLater();
2248     m_doc->setModified();
2249 }
2250 
2251 /**
2252  * Removes an AssociationWidget from the association list
2253  * and removes the corresponding UMLAssociation from the current UMLDoc.
2254  */
removeAssocInViewAndDoc(AssociationWidget * a)2255 void UMLScene::removeAssocInViewAndDoc(AssociationWidget* a)
2256 {
2257     // For umbrello 1.2, UMLAssociations can only be removed in two ways:
2258     // 1. Right click on the assocwidget in the view and select Delete
2259     // 2. Go to the Class Properties page, select Associations, right click
2260     //    on the association and select Delete
2261     if (!a)
2262         return;
2263     if (a->associationType() == Uml::AssociationType::Containment) {
2264         UMLObject *objToBeMoved = a->widgetForRole(Uml::RoleType::B)->umlObject();
2265         if (objToBeMoved != 0) {
2266             UMLListView *lv = UMLApp::app()->listView();
2267             lv->moveObject(objToBeMoved->id(),
2268                            Model_Utils::convert_OT_LVT(objToBeMoved),
2269                            lv->theLogicalView());
2270             // UMLListView::moveObject() will delete the containment
2271             // AssociationWidget via UMLScene::updateContainment().
2272         } else {
2273             DEBUG(DBG_SRC) << "removeAssocInViewAndDoc(containment): "
2274                            << "objB is NULL";
2275         }
2276     } else {
2277         // Remove assoc in doc.
2278         m_doc->removeAssociation(a->association());
2279         // Remove assoc in view.
2280         removeWidgetCmd(a);
2281     }
2282 }
2283 
2284 /**
2285  * Removes all the associations related to Widget.
2286  *
2287  * @param widget  Pointer to the widget to remove.
2288  */
removeAssociations(UMLWidget * widget)2289 void UMLScene::removeAssociations(UMLWidget* widget)
2290 {
2291     foreach(AssociationWidget* assocwidget, associationList()) {
2292         if (assocwidget->containsAsEndpoint(widget)) {
2293             removeWidgetCmd(assocwidget);
2294         }
2295     }
2296 }
2297 
2298 /**
2299  * Sets each association as selected if the widgets it associates are selected
2300  *
2301  * @param bSelect  True to select, false for unselect
2302  */
selectAssociations(bool bSelect)2303 void UMLScene::selectAssociations(bool bSelect)
2304 {
2305     foreach(AssociationWidget* assocwidget, associationList()) {
2306         UMLWidget *widA = assocwidget->widgetForRole(Uml::RoleType::A);
2307         UMLWidget *widB = assocwidget->widgetForRole(Uml::RoleType::B);
2308         if (bSelect &&
2309                 widA && widA->isSelected() &&
2310                 widB && widB->isSelected()) {
2311             assocwidget->setSelected(true);
2312         } else {
2313             assocwidget->setSelected(false);
2314         }
2315     }
2316 }
2317 
2318 /**
2319  * Fills Associations with all the associations that includes a widget related to object
2320  */
getWidgetAssocs(UMLObject * Obj,AssociationWidgetList & Associations)2321 void UMLScene::getWidgetAssocs(UMLObject* Obj, AssociationWidgetList & Associations)
2322 {
2323     if (! Obj)
2324         return;
2325 
2326     foreach(AssociationWidget* assocwidget, associationList()) {
2327         if (assocwidget->widgetForRole(Uml::RoleType::A)->umlObject() == Obj ||
2328             assocwidget->widgetForRole(Uml::RoleType::B)->umlObject() == Obj)
2329             Associations.append(assocwidget);
2330     }
2331 
2332 }
2333 
2334 /**
2335  * Removes All the associations of the diagram
2336  */
removeAllAssociations()2337 void UMLScene::removeAllAssociations()
2338 {
2339     //Remove All association widgets
2340     foreach(AssociationWidget* assocwidget, associationList()) {
2341         removeWidgetCmd(assocwidget);
2342     }
2343 }
2344 
2345 /**
2346  * Removes All the widgets of the diagram
2347  */
removeAllWidgets()2348 void UMLScene::removeAllWidgets()
2349 {
2350     // Remove widgets.
2351     foreach(UMLWidget* temp, widgetList()) {
2352         uIgnoreZeroPointer(temp);
2353         // I had to take this condition back in, else umbrello
2354         // crashes on exit. Still to be analyzed.  --okellogg
2355         if (!(temp->isTextWidget() &&
2356               temp->asFloatingTextWidget()->textRole() != TextRole::Floating)) {
2357             removeWidgetCmd(temp);
2358         }
2359     }
2360 }
2361 
2362 /**
2363  * Refreshes containment association, i.e. removes possible old
2364  * containment and adds new containment association if applicable.
2365  *
2366  * @param self  Pointer to the contained object for which
2367  *   the association to the containing object is
2368  *   recomputed.
2369  */
updateContainment(UMLCanvasObject * self)2370 void UMLScene::updateContainment(UMLCanvasObject *self)
2371 {
2372     if (self == 0)
2373         return;
2374     // See if the object has a widget representation in this view.
2375     // While we're at it, also see if the new parent has a widget here.
2376     UMLWidget *selfWidget = 0, *newParentWidget = 0;
2377     UMLPackage *newParent = self->umlPackage();
2378     foreach(UMLWidget* w, widgetList()) {
2379         UMLObject *o = w->umlObject();
2380         if (o == self)
2381             selfWidget = w;
2382         else if (newParent != 0 && o == newParent)
2383             newParentWidget = w;
2384     }
2385     if (selfWidget == 0)
2386         return;
2387     // Remove possibly obsoleted containment association.
2388     foreach(AssociationWidget* a, associationList()) {
2389         if (a->associationType() != Uml::AssociationType::Containment)
2390             continue;
2391         // Container is at role A, containee at B.
2392         // We only look at association for which we are B.
2393         UMLWidget *wB = a->widgetForRole(Uml::RoleType::B);
2394         UMLObject *roleBObj = wB->umlObject();
2395         if (roleBObj != self)
2396             continue;
2397         UMLWidget *wA = a->widgetForRole(Uml::RoleType::A);
2398         UMLObject *roleAObj = wA->umlObject();
2399         if (roleAObj == newParent) {
2400             // Wow, all done. Great!
2401             return;
2402         }
2403         removeWidgetCmd(a);
2404         // It's okay to break out because there can only be a single
2405         // containing object.
2406         break;
2407     }
2408     if (newParentWidget == 0)
2409         return;
2410     // Create the new containment association.
2411     AssociationWidget *a = AssociationWidget::create
2412                              (this, newParentWidget,
2413                               Uml::AssociationType::Containment, selfWidget);
2414     addWidgetCmd(a);
2415 }
2416 
2417 /**
2418  * Creates automatically any Associations that the given @ref UMLWidget
2419  * may have on any diagram.  This method is used when you just add the UMLWidget
2420  * to a diagram.
2421  */
createAutoAssociations(UMLWidget * widget)2422 void UMLScene::createAutoAssociations(UMLWidget * widget)
2423 {
2424     if (widget == 0 ||
2425         (m_Type != Uml::DiagramType::Class &&
2426          m_Type != Uml::DiagramType::Object &&
2427          m_Type != Uml::DiagramType::Component &&
2428          m_Type != Uml::DiagramType::Deployment
2429          && m_Type != Uml::DiagramType::EntityRelationship))
2430         return;
2431     // Recipe:
2432     // If this widget has an underlying UMLCanvasObject then
2433     //   for each of the UMLCanvasObject's UMLAssociations
2434     //     if umlassoc's "other" role has a widget representation on this view then
2435     //       if the AssocWidget does not already exist then
2436     //         if the assoc type is permitted in the current diagram type then
2437     //           create the AssocWidget
2438     //         end if
2439     //       end if
2440     //     end if
2441     //   end loop
2442     //   Do createAutoAttributeAssociations()
2443     //   if this object is capable of containing nested objects then
2444     //     for each of the object's containedObjects
2445     //       if the containedObject has a widget representation on this view then
2446     //         if the containedWidget is not physically located inside this widget
2447     //           create the containment AssocWidget
2448     //         end if
2449     //       end if
2450     //     end loop
2451     //   end if
2452     //   if the UMLCanvasObject has a parentPackage then
2453     //     if the parentPackage has a widget representation on this view then
2454     //       create the containment AssocWidget
2455     //     end if
2456     //   end if
2457     // end if
2458     UMLObject *tmpUmlObj = widget->umlObject();
2459     if (tmpUmlObj == 0)
2460         return;
2461     UMLCanvasObject *umlObj = tmpUmlObj->asUMLCanvasObject();
2462     if (umlObj == 0)
2463         return;
2464     const UMLAssociationList& umlAssocs = umlObj->getAssociations();
2465 
2466     Uml::ID::Type myID = umlObj->id();
2467     foreach(UMLAssociation* assoc, umlAssocs) {
2468         UMLCanvasObject *other = 0;
2469         UMLObject *roleAObj = assoc->getObject(Uml::RoleType::A);
2470         if (roleAObj == 0) {
2471             DEBUG(DBG_SRC) << "roleA object is NULL at UMLAssoc "
2472                            << Uml::ID::toString(assoc->id());
2473             continue;
2474         }
2475         UMLObject *roleBObj = assoc->getObject(Uml::RoleType::B);
2476         if (roleBObj == 0) {
2477             DEBUG(DBG_SRC) << "roleB object is NULL at UMLAssoc "
2478                            << Uml::ID::toString(assoc->id());
2479             continue;
2480         }
2481         if (roleAObj->id() == myID) {
2482             other = roleBObj->asUMLCanvasObject();
2483         } else if (roleBObj->id() == myID) {
2484             other = roleAObj->asUMLCanvasObject();
2485         } else {
2486             DEBUG(DBG_SRC) << "Cannot find own object "
2487                            << Uml::ID::toString(myID) << " in UMLAssoc "
2488                            << Uml::ID::toString(assoc->id());
2489             continue;
2490         }
2491         // Now that we have determined the "other" UMLObject, seek it in
2492         // this view's UMLWidgets.
2493         if (!other) {
2494             continue;
2495         }
2496         Uml::ID::Type otherID = other->id();
2497 
2498         bool breakFlag = false;
2499         UMLWidget* pOtherWidget = 0;
2500         foreach(pOtherWidget, widgetList()) {
2501             if (pOtherWidget->id() == otherID) {
2502                 breakFlag = true;
2503                 break;
2504             }
2505         }
2506         if (!breakFlag)
2507             continue;
2508         // Both objects are represented in this view:
2509         // Assign widget roles as indicated by the UMLAssociation.
2510         UMLWidget *widgetA, *widgetB;
2511         if (myID == roleAObj->id()) {
2512             widgetA = widget;
2513             widgetB = pOtherWidget;
2514         } else {
2515             widgetA = pOtherWidget;
2516             widgetB = widget;
2517         }
2518         // Check that the assocwidget does not already exist.
2519         Uml::AssociationType::Enum assocType = assoc->getAssocType();
2520         AssociationWidget * assocwidget = findAssocWidget(assocType, widgetA, widgetB);
2521         if (assocwidget) {
2522             assocwidget->calculateEndingPoints();  // recompute assoc lines
2523             continue;
2524         }
2525         // Check that the assoc is allowed.
2526         if (!AssocRules::allowAssociation(assocType, widgetA, widgetB)) {
2527             DEBUG(DBG_SRC) << "not transferring assoc "
2528                            << "of type " << assocType;
2529             continue;
2530         }
2531 
2532         // Create the AssociationWidget.
2533         assocwidget = AssociationWidget::create(this);
2534         assocwidget->setWidgetForRole(widgetA, Uml::RoleType::A);
2535         assocwidget->setWidgetForRole(widgetB, Uml::RoleType::B);
2536         assocwidget->setAssociationType(assocType);
2537         assocwidget->setUMLObject(assoc);
2538         // Call calculateEndingPoints() before setting the FloatingTexts
2539         // because their positions are computed according to the
2540         // assocwidget line positions.
2541         assocwidget->calculateEndingPoints();
2542         assocwidget->syncToModel();
2543         assocwidget->setActivated(true);
2544         if (! addAssociation(assocwidget))
2545             delete assocwidget;
2546     }
2547 
2548     createAutoAttributeAssociations(widget);
2549 
2550     if (m_Type == Uml::DiagramType::EntityRelationship) {
2551         createAutoConstraintAssociations(widget);
2552     }
2553 
2554     // if this object is capable of containing nested objects then
2555     UMLObject::ObjectType t = umlObj->baseType();
2556     if (t == UMLObject::ot_Package || t == UMLObject::ot_Class ||
2557         t == UMLObject::ot_Interface || t == UMLObject::ot_Component) {
2558         // for each of the object's containedObjects
2559         UMLPackage *umlPkg = umlObj->asUMLPackage();
2560         UMLObjectList lst = umlPkg->containedObjects();
2561         foreach(UMLObject* obj,  lst) {
2562             uIgnoreZeroPointer(obj);
2563             // if the containedObject has a widget representation on this view then
2564             Uml::ID::Type id = obj->id();
2565             foreach(UMLWidget *w, widgetList()) {
2566                 uIgnoreZeroPointer(w);
2567                 if (w->id() != id)
2568                     continue;
2569                 // if the containedWidget is not physically located inside this widget
2570                 if (widget->rect().contains(w->rect()))
2571                     continue;
2572                 // create the containment AssocWidget
2573                 AssociationWidget *a = AssociationWidget::create(this, widget,
2574                         Uml::AssociationType::Containment, w);
2575                 a->calculateEndingPoints();
2576                 a->setActivated(true);
2577                 if (! addAssociation(a))
2578                     delete a;
2579             }
2580         }
2581     }
2582     // if the UMLCanvasObject has a parentPackage then
2583     UMLPackage *parent = umlObj->umlPackage();
2584     if (parent == 0)
2585         return;
2586     // if the parentPackage has a widget representation on this view then
2587     Uml::ID::Type pkgID = parent->id();
2588 
2589     bool breakFlag = false;
2590     UMLWidget* pWidget = 0;
2591     foreach(pWidget, widgetList()) {
2592         uIgnoreZeroPointer(pWidget);
2593         if (pWidget->id() == pkgID) {
2594             breakFlag = true;
2595             break;
2596         }
2597     }
2598     if (!breakFlag || pWidget->rect().contains(widget->rect()))
2599         return;
2600     // create the containment AssocWidget
2601     AssociationWidget *a = AssociationWidget::create(this, pWidget, Uml::AssociationType::Containment, widget);
2602     if (! addAssociation(a))
2603         delete a;
2604 }
2605 
2606 /**
2607  * If the m_Type of the given widget is WidgetBase::wt_Class then
2608  * iterate through the class' attributes and create an
2609  * association to each attribute type widget that is present
2610  * on the current diagram.
2611  */
createAutoAttributeAssociations(UMLWidget * widget)2612 void UMLScene::createAutoAttributeAssociations(UMLWidget *widget)
2613 {
2614     if (widget == 0 || m_Type != Uml::DiagramType::Class || !m_Options.classState.showAttribAssocs)
2615         return;
2616 
2617     // Pseudocode:
2618     //   if the underlying model object is really a UMLClassifier then
2619     //     for each of the UMLClassifier's UMLAttributes
2620     //       if the attribute type has a widget representation on this view then
2621     //         if the AssocWidget does not already exist then
2622     //           if the current diagram type permits compositions then
2623     //             create a composition AssocWidget
2624     //           end if
2625     //         end if
2626     //       end if
2627     //       if the attribute type is a Datatype then
2628     //         if the Datatype is a reference (pointer) type then
2629     //           if the referenced type has a widget representation on this view then
2630     //             if the AssocWidget does not already exist then
2631     //               if the current diagram type permits aggregations then
2632     //                 create an aggregation AssocWidget from the ClassifierWidget to the
2633     //                                                 widget of the referenced type
2634     //               end if
2635     //             end if
2636     //           end if
2637     //         end if
2638     //       end if
2639     //     end loop
2640     //   end if
2641     //
2642     // Implementation:
2643     UMLObject *tmpUmlObj = widget->umlObject();
2644     if (tmpUmlObj == 0)
2645         return;
2646     // if the underlying model object is really a UMLClassifier then
2647     if (tmpUmlObj->isUMLDatatype()) {
2648         UMLDatatype *dt = tmpUmlObj->asUMLDatatype();
2649         while (dt && dt->originType() != 0) {
2650             tmpUmlObj = dt->originType();
2651             if (!tmpUmlObj->isUMLDatatype())
2652                 break;
2653             dt = tmpUmlObj->asUMLDatatype();
2654         }
2655     }
2656     if (tmpUmlObj->baseType() != UMLObject::ot_Class)
2657         return;
2658     UMLClassifier * klass = tmpUmlObj->asUMLClassifier();
2659     // for each of the UMLClassifier's UMLAttributes
2660     UMLAttributeList attrList = klass->getAttributeList();
2661     foreach(UMLAttribute* attr, attrList) {
2662         createAutoAttributeAssociation(attr->getType(), attr, widget);
2663         /*
2664          * The following code from attachment 19935 of https://bugs.kde.org/140669
2665          * creates Aggregation/Composition to the template parameters.
2666          * The current solution uses Dependency instead, see handling of template
2667          * instantiation at Import_Utils::createUMLObject().
2668         UMLClassifierList templateList = attr->getTemplateParams();
2669         for (UMLClassifierListIt it(templateList); it.current(); ++it) {
2670             createAutoAttributeAssociation(it, attr, widget);
2671         }
2672          */
2673     }
2674 }
2675 
2676 /**
2677  * Create an association with the attribute attr associated with the UMLWidget
2678  * widget if the UMLClassifier type is present on the current diagram.
2679  */
createAutoAttributeAssociation(UMLClassifier * type,UMLAttribute * attr,UMLWidget * widget)2680 void UMLScene::createAutoAttributeAssociation(UMLClassifier *type, UMLAttribute *attr,
2681                                               UMLWidget *widget /*, UMLClassifier * klass*/)
2682 {
2683     if (type == 0) {
2684         // DEBUG(DBG_SRC) << klass->getName() << ": type is NULL for "
2685         //                << "attribute " << attr->getName();
2686         return;
2687     }
2688     Uml::AssociationType::Enum assocType = Uml::AssociationType::Composition;
2689     UMLWidget *w = findWidget(type->id());
2690     // if the attribute type has a widget representation on this view
2691     if (w) {
2692         AssociationWidget *a = findAssocWidget(widget, w, attr->name());
2693         if (a) {
2694             a->setAssociationType(assocType);
2695         } else if (AssocRules::allowAssociation(assocType, widget, w)) {
2696             // Create a composition AssocWidget, or, if the attribute type is
2697             // stereotyped <<CORBAInterface>>, create a UniAssociation widget.
2698             if (type->stereotype() == QLatin1String("CORBAInterface"))
2699                 assocType = Uml::AssociationType::UniAssociation;
2700             a = AssociationWidget::create(this, widget, assocType, w, attr);
2701             a->setVisibility(attr->visibility(), Uml::RoleType::B);
2702             /*
2703             if (assocType == Uml::AssociationType::Aggregation || assocType == Uml::AssociationType::UniAssociation)
2704             a->setMulti("0..1", Uml::RoleType::B);
2705             */
2706             a->setRoleName(attr->name(), Uml::RoleType::B);
2707             a->setActivated(true);
2708             if (! addAssociation(a))
2709                 delete a;
2710         }
2711     }
2712     // if the attribute type is a Datatype then
2713     if (type->isUMLDatatype()) {
2714         UMLDatatype *dt = type->asUMLDatatype();
2715         // if the Datatype is a reference (pointer) type
2716         if (dt && dt->isReference()) {
2717             UMLClassifier *c = dt->originType();
2718             UMLWidget *w = c ? findWidget(c->id()) : 0;
2719             // if the referenced type has a widget representation on this view
2720             if (w) {
2721                 Uml::AssociationType::Enum assocType = Uml::AssociationType::Aggregation;
2722                 AssociationWidget *a = findAssocWidget(widget, w, attr->name());
2723                 if (a) {
2724                     a->setAssociationType(assocType);
2725                 } else if (AssocRules::allowAssociation(assocType, widget, w)) {
2726                     // create an aggregation AssocWidget from the ClassifierWidget
2727                     // to the widget of the referenced type
2728                     a = AssociationWidget::create (this, widget, assocType, w, attr);
2729                     a->setVisibility(attr->visibility(), Uml::RoleType::B);
2730                     //a->setChangeability(true, Uml::RoleType::B);
2731                     a->setMultiplicity(QLatin1String("0..1"), Uml::RoleType::B);
2732                     a->setRoleName(attr->name(), Uml::RoleType::B);
2733                     a->setActivated(true);
2734                     if (! addAssociation(a))
2735                         delete a;
2736                 }
2737             }
2738         }
2739     }
2740 }
2741 
createAutoConstraintAssociations(UMLWidget * widget)2742 void UMLScene::createAutoConstraintAssociations(UMLWidget *widget)
2743 {
2744     if (widget == 0 || m_Type != Uml::DiagramType::EntityRelationship)
2745         return;
2746 
2747     // Pseudocode:
2748     //   if the underlying model object is really a UMLEntity then
2749     //     for each of the UMLEntity's UMLForeignKeyConstraint's
2750     //       if the attribute type has a widget representation on this view then
2751     //         if the AssocWidget does not already exist then
2752     //           if the current diagram type permits relationships then
2753     //             create a relationship AssocWidget
2754     //           end if
2755     //         end if
2756     //       end if
2757 
2758     UMLObject *tmpUmlObj = widget->umlObject();
2759     if (tmpUmlObj == 0)
2760         return;
2761     // check if the underlying model object is really a UMLEntity
2762     UMLCanvasObject *umlObj = tmpUmlObj->asUMLCanvasObject();
2763     if (umlObj == 0)
2764         return;
2765     // finished checking whether this widget has a UMLCanvas Object
2766 
2767     if (tmpUmlObj->baseType() != UMLObject::ot_Entity)
2768         return;
2769     UMLEntity *entity = tmpUmlObj->asUMLEntity();
2770 
2771     // for each of the UMLEntity's UMLForeignKeyConstraints
2772     UMLClassifierListItemList constrList = entity->getFilteredList(UMLObject::ot_ForeignKeyConstraint);
2773 
2774     foreach(UMLClassifierListItem* cli, constrList) {
2775         UMLEntityConstraint *eConstr = cli->asUMLEntityConstraint();
2776 
2777         UMLForeignKeyConstraint* fkc = eConstr->asUMLForeignKeyConstraint();
2778         if (fkc == 0) {
2779             return;
2780         }
2781 
2782         UMLEntity* refEntity = fkc->getReferencedEntity();
2783         if (refEntity == 0) {
2784             return;
2785         }
2786 
2787         createAutoConstraintAssociation(refEntity, fkc, widget);
2788     }
2789 }
2790 
createAutoConstraintAssociation(UMLEntity * refEntity,UMLForeignKeyConstraint * fkConstraint,UMLWidget * widget)2791 void UMLScene::createAutoConstraintAssociation(UMLEntity* refEntity, UMLForeignKeyConstraint* fkConstraint, UMLWidget* widget)
2792 {
2793     if (refEntity == 0) {
2794         return;
2795     }
2796 
2797     Uml::AssociationType::Enum assocType = Uml::AssociationType::Relationship;
2798     UMLWidget *w = findWidget(refEntity->id());
2799     AssociationWidget *aw = nullptr;
2800 
2801     if (w) {
2802         aw = findAssocWidget(assocType, w, widget);
2803         if (aw) {
2804             if (aw->roleWidget(Uml::RoleType::B))
2805                 aw->roleWidget(Uml::RoleType::B)->setName(fkConstraint->name());
2806             else
2807                 uError() << "could not find role widget B and therefore cannot rename constraint";
2808         // if the current diagram type permits relationships
2809         } else if (AssocRules::allowAssociation(assocType, w, widget)) {
2810             // for foreign key constraint, we need to create the association type Uml::AssociationType::Relationship.
2811             // The referenced entity is the "1" part (Role A) and the entity holding the relationship is the "many" part. (Role B)
2812             AssociationWidget *a = AssociationWidget::create(this, w, assocType, widget);
2813             a->setUMLObject(fkConstraint);
2814             //a->setVisibility(attr->getVisibility(), Uml::RoleType::B);
2815             a->setRoleName(fkConstraint->name(), Uml::RoleType::B);
2816             a->setActivated(true);
2817             if (! addAssociation(a))
2818                 delete a;
2819         }
2820     }
2821 }
2822 
createAutoAttributeAssociations2(UMLWidget * widget)2823 void UMLScene::createAutoAttributeAssociations2(UMLWidget *widget)
2824 {
2825     foreach(UMLWidget* w, widgetList()) {
2826         uIgnoreZeroPointer(w);
2827         if (w != widget) {
2828             createAutoAttributeAssociations(w);
2829 
2830             if (widget->umlObject() && widget->umlObject()->baseType() == UMLObject::ot_Entity)
2831                 createAutoConstraintAssociations(w);
2832         }
2833     }
2834 }
2835 
2836 /**
2837  * Find the maximum bounding rectangle of FloatingTextWidget widgets.
2838  * Auxiliary to copyAsImage().
2839  *
2840  * @param ft Pointer to the FloatingTextWidget widget to consider.
2841  * @param px  X coordinate of lower left corner. This value will be
2842  *            updated if the X coordinate of the lower left corner
2843  *            of ft is smaller than the px value passed in.
2844  * @param py  Y coordinate of lower left corner. This value will be
2845  *            updated if the Y coordinate of the lower left corner
2846  *            of ft is smaller than the py value passed in.
2847  * @param qx  X coordinate of upper right corner. This value will be
2848  *            updated if the X coordinate of the upper right corner
2849  *            of ft is larger than the qx value passed in.
2850  * @param qy  Y coordinate of upper right corner. This value will be
2851  *            updated if the Y coordinate of the upper right corner
2852  *            of ft is larger than the qy value passed in.
2853  */
findMaxBoundingRectangle(const FloatingTextWidget * ft,qreal & px,qreal & py,qreal & qx,qreal & qy)2854 void UMLScene::findMaxBoundingRectangle(const FloatingTextWidget* ft, qreal& px, qreal& py, qreal& qx, qreal& qy)
2855 {
2856     if (ft == 0 || !ft->isVisible())
2857         return;
2858 
2859     qreal x = ft->x();
2860     qreal y = ft->y();
2861     qreal x1 = x + ft->width() - 1;
2862     qreal y1 = y + ft->height() - 1;
2863 
2864     if (px == -1 || x < px)
2865         px = x;
2866     if (py == -1 || y < py)
2867         py = y;
2868     if (qx == -1 || x1 > qx)
2869         qx = x1;
2870     if (qy == -1 || y1 > qy)
2871         qy = y1;
2872 }
2873 
2874 /**
2875  * Returns the PNG picture of the paste operation.
2876  */
copyAsImage(QPixmap * & pix)2877 void UMLScene::copyAsImage(QPixmap*& pix)
2878 {
2879     //get the smallest rect holding the diagram
2880     QRectF rect = diagramRect();
2881     QPixmap diagram(rect.width(), rect.height());
2882 
2883     //only draw what is selected
2884     m_bDrawSelectedOnly = true;
2885     selectAssociations(true);
2886     getDiagram(diagram, rect);
2887 
2888     //now get the selection cut
2889     qreal px = -1, py = -1, qx = -1, qy = -1;
2890 
2891     //first get the smallest rect holding the widgets
2892     foreach(UMLWidget* temp, selectedWidgets()) {
2893         qreal x = temp->x();
2894         qreal y = temp->y();
2895         qreal x1 = x + temp->width() - 1;
2896         qreal y1 = y + temp->height() - 1;
2897         if (px == -1 || x < px) {
2898             px = x;
2899         }
2900         if (py == -1 || y < py) {
2901             py = y;
2902         }
2903         if (qx == -1 || x1 > qx) {
2904             qx = x1;
2905         }
2906         if (qy == -1 || y1 > qy) {
2907             qy = y1;
2908         }
2909     }
2910 
2911     //also take into account any text lines in assocs or messages
2912 
2913     //get each type of associations
2914     //This needs to be reimplemented to increase the rectangle
2915     //if a part of any association is not included
2916     foreach(AssociationWidget *a, associationList()) {
2917         if (! a->isSelected())
2918             continue;
2919         const FloatingTextWidget* multiA = a->multiplicityWidget(Uml::RoleType::A);
2920         const FloatingTextWidget* multiB = a->multiplicityWidget(Uml::RoleType::B);
2921         const FloatingTextWidget* roleA = a->roleWidget(Uml::RoleType::A);
2922         const FloatingTextWidget* roleB = a->roleWidget(Uml::RoleType::B);
2923         const FloatingTextWidget* changeA = a->changeabilityWidget(Uml::RoleType::A);
2924         const FloatingTextWidget* changeB = a->changeabilityWidget(Uml::RoleType::B);
2925         findMaxBoundingRectangle(multiA, px, py, qx, qy);
2926         findMaxBoundingRectangle(multiB, px, py, qx, qy);
2927         findMaxBoundingRectangle(roleA, px, py, qx, qy);
2928         findMaxBoundingRectangle(roleB, px, py, qx, qy);
2929         findMaxBoundingRectangle(changeA, px, py, qx, qy);
2930         findMaxBoundingRectangle(changeB, px, py, qx, qy);
2931     }//end foreach
2932 
2933     QRectF imageRect;  //area with respect to diagramRect()
2934     //i.e. all widgets on the scene.  Was previously with
2935     //respect to whole scene
2936 
2937     imageRect.setLeft(px - rect.left());
2938     imageRect.setTop(py - rect.top());
2939     imageRect.setRight(qx - rect.left());
2940     imageRect.setBottom(qy - rect.top());
2941 
2942     pix = new QPixmap(imageRect.width(), imageRect.height());
2943     QPainter output(pix);
2944     output.drawPixmap(QPoint(0, 0), diagram, imageRect);
2945     m_bDrawSelectedOnly = false;
2946 }
2947 
2948 /**
2949  * Reset the toolbar.
2950  */
resetToolbar()2951 void UMLScene::resetToolbar()
2952 {
2953     emit sigResetToolBar();
2954 }
2955 
triggerToolbarButton(WorkToolBar::ToolBar_Buttons button)2956 void UMLScene::triggerToolbarButton(WorkToolBar::ToolBar_Buttons button)
2957 {
2958     m_d->triggerToolBarButton(button);
2959 }
2960 
2961 /**
2962  * Event handler for context menu events.
2963  */
contextMenuEvent(QGraphicsSceneContextMenuEvent * event)2964 void UMLScene::contextMenuEvent(QGraphicsSceneContextMenuEvent* event)
2965 {
2966     QGraphicsScene::contextMenuEvent(event);
2967     if (!event->isAccepted()) {
2968         setPos(event->scenePos());
2969         UMLScenePopupMenu popup(m_view, this);
2970         QAction *triggered = popup.exec(event->screenPos());
2971         slotMenuSelection(triggered);
2972         event->accept();
2973     }
2974 }
2975 
2976 /**
2977  * Returns the status on whether in a paste state.
2978  *
2979  * @return Returns the status on whether in a paste state.
2980  */
getPaste() const2981 bool UMLScene::getPaste() const
2982 {
2983     return m_bPaste;
2984 }
2985 
2986 /**
2987  * Sets the status on whether in a paste state.
2988  */
setPaste(bool paste)2989 void UMLScene::setPaste(bool paste)
2990 {
2991     m_bPaste = paste;
2992 }
2993 
2994 /**
2995  * When a menu selection has been made on the menu
2996  * that this view created, this method gets called.
2997  */
slotMenuSelection(QAction * action)2998 void UMLScene::slotMenuSelection(QAction* action)
2999 {
3000     ListPopupMenu::MenuType sel = ListPopupMenu::typeFromAction(action);
3001     switch (sel) {
3002     case ListPopupMenu::mt_Undo:
3003         UMLApp::app()->undo();
3004         break;
3005 
3006     case ListPopupMenu::mt_Redo:
3007         UMLApp::app()->redo();
3008         break;
3009 
3010     case ListPopupMenu::mt_Clear:
3011         clearDiagram();
3012         break;
3013 
3014     case ListPopupMenu::mt_Export_Image:
3015         m_pImageExporter->exportView();
3016         break;
3017 
3018     case ListPopupMenu::mt_Apply_Layout:
3019     case ListPopupMenu::mt_Apply_Layout1:
3020     case ListPopupMenu::mt_Apply_Layout2:
3021     case ListPopupMenu::mt_Apply_Layout3:
3022     case ListPopupMenu::mt_Apply_Layout4:
3023     case ListPopupMenu::mt_Apply_Layout5:
3024     case ListPopupMenu::mt_Apply_Layout6:
3025     case ListPopupMenu::mt_Apply_Layout7:
3026     case ListPopupMenu::mt_Apply_Layout8:
3027     case ListPopupMenu::mt_Apply_Layout9:
3028         {
3029             QVariant value = ListPopupMenu::dataFromAction(ListPopupMenu::dt_ApplyLayout, action);
3030             applyLayout(value.toString());
3031         }
3032         break;
3033 
3034     case ListPopupMenu::mt_FloatText:
3035         {
3036             FloatingTextWidget* ft = new FloatingTextWidget(this);
3037             ft->showChangeTextDialog();
3038             //if no text entered delete
3039             if (!FloatingTextWidget::isTextValid(ft->text())) {
3040                 delete ft;
3041             } else {
3042                 ft->setID(UniqueID::gen());
3043                 setupNewWidget(ft);
3044             }
3045         }
3046         break;
3047 
3048     case ListPopupMenu::mt_UseCase:
3049         m_bCreateObject = true;
3050         Object_Factory::createUMLObject(UMLObject::ot_UseCase);
3051         break;
3052 
3053     case ListPopupMenu::mt_Actor:
3054         m_bCreateObject = true;
3055         Object_Factory::createUMLObject(UMLObject::ot_Actor);
3056         break;
3057 
3058     case ListPopupMenu::mt_Class:
3059     case ListPopupMenu::mt_Object:
3060         m_bCreateObject = true;
3061         Object_Factory::createUMLObject(UMLObject::ot_Class);
3062         break;
3063 
3064     case ListPopupMenu::mt_Package:
3065         m_bCreateObject = true;
3066         Object_Factory::createUMLObject(UMLObject::ot_Package);
3067         break;
3068 
3069     case ListPopupMenu::mt_Subsystem:
3070         m_bCreateObject = true;
3071         Object_Factory::createUMLObject(UMLObject::ot_SubSystem);
3072        break;
3073 
3074     case ListPopupMenu::mt_Component:
3075         m_bCreateObject = true;
3076         Object_Factory::createUMLObject(UMLObject::ot_Component);
3077         break;
3078 
3079     case ListPopupMenu::mt_Node:
3080         m_bCreateObject = true;
3081         Object_Factory::createUMLObject(UMLObject::ot_Node);
3082         break;
3083 
3084     case ListPopupMenu::mt_Artifact:
3085         m_bCreateObject = true;
3086         Object_Factory::createUMLObject(UMLObject::ot_Artifact);
3087         break;
3088 
3089     case ListPopupMenu::mt_Interface:
3090     case ListPopupMenu::mt_InterfaceComponent:
3091         m_bCreateObject = true;
3092         Object_Factory::createUMLObject(UMLObject::ot_Interface);
3093         break;
3094 
3095     case ListPopupMenu::mt_Enum:
3096         m_bCreateObject = true;
3097         Object_Factory::createUMLObject(UMLObject::ot_Enum);
3098         break;
3099 
3100     case ListPopupMenu::mt_Entity:
3101         m_bCreateObject = true;
3102         Object_Factory::createUMLObject(UMLObject::ot_Entity);
3103         break;
3104 
3105     case ListPopupMenu::mt_Category:
3106         m_bCreateObject = true;
3107         Object_Factory::createUMLObject(UMLObject::ot_Category);
3108         break;
3109 
3110     case ListPopupMenu::mt_Datatype:
3111         m_bCreateObject = true;
3112         Object_Factory::createUMLObject(UMLObject::ot_Datatype);
3113         break;
3114 
3115     case ListPopupMenu::mt_Instance:
3116         m_bCreateObject = true;
3117         Object_Factory::createUMLObject(UMLObject::ot_Instance);
3118         break;
3119 
3120     case ListPopupMenu::mt_Note: {
3121         m_bCreateObject = true;
3122         UMLWidget* widget = new NoteWidget(this);
3123         addItem(widget);
3124         widget->setPos(pos());
3125         widget->setSize(100, 40);
3126         widget->showPropertiesDialog();
3127         QSizeF size = widget->minimumSize();
3128         widget->setSize(size);
3129         break;
3130     }
3131 
3132     case ListPopupMenu::mt_Cut:
3133         //FIXME make this work for diagram's right click menu
3134         if (selectedWidgets().count() &&
3135             UMLApp::app()->editCutCopy(true)) {
3136             deleteSelection();
3137             m_doc->setModified(true);
3138         }
3139         break;
3140 
3141     case ListPopupMenu::mt_Copy:
3142         //FIXME make this work for diagram's right click menu
3143         selectedWidgets().count() && UMLApp::app()->editCutCopy(true);
3144         break;
3145 
3146     case ListPopupMenu::mt_Paste:
3147         m_PastePoint = m_pos;
3148         m_pos.setX(2000);
3149         m_pos.setY(2000);
3150         UMLApp::app()->slotEditPaste();
3151 
3152         m_PastePoint.setX(0);
3153         m_PastePoint.setY(0);
3154         break;
3155 
3156     case ListPopupMenu::mt_Initial_State:
3157         {
3158             StateWidget* state = new StateWidget(this, StateWidget::Initial);
3159             setupNewWidget(state);
3160         }
3161         break;
3162 
3163     case ListPopupMenu::mt_End_State:
3164         {
3165             StateWidget* state = new StateWidget(this, StateWidget::End);
3166             setupNewWidget(state);
3167         }
3168         break;
3169 
3170     case ListPopupMenu::mt_Junction:
3171         {
3172             StateWidget* state = new StateWidget(this, StateWidget::Junction);
3173             setupNewWidget(state);
3174         }
3175         break;
3176 
3177     case ListPopupMenu::mt_DeepHistory:
3178         {
3179             StateWidget* state = new StateWidget(this, StateWidget::DeepHistory);
3180             setupNewWidget(state);
3181         }
3182         break;
3183 
3184     case ListPopupMenu::mt_ShallowHistory:
3185         {
3186             StateWidget* state = new StateWidget(this, StateWidget::ShallowHistory);
3187             setupNewWidget(state);
3188         }
3189         break;
3190 
3191     case ListPopupMenu::mt_Choice:
3192         {
3193             StateWidget* state = new StateWidget(this, StateWidget::Choice);
3194             setupNewWidget(state);
3195         }
3196         break;
3197 
3198     case ListPopupMenu::mt_StateFork:
3199         {
3200             StateWidget* state = new StateWidget(this, StateWidget::Fork);
3201             setupNewWidget(state);
3202         }
3203         break;
3204 
3205     case ListPopupMenu::mt_StateJoin:
3206         {
3207             StateWidget* state = new StateWidget(this, StateWidget::Join);
3208             setupNewWidget(state);
3209         }
3210         break;
3211 
3212     case ListPopupMenu::mt_State:
3213         {
3214             QString name = Widget_Utils::defaultWidgetName(WidgetBase::WidgetType::wt_State);
3215             bool ok = Dialog_Utils::askNewName(WidgetBase::WidgetType::wt_State, name);
3216             if (ok) {
3217                 StateWidget* state = new StateWidget(this);
3218                 state->setName(name);
3219                 setupNewWidget(state);
3220             }
3221         }
3222         break;
3223 
3224     case ListPopupMenu::mt_CombinedState:
3225         {
3226             QString name = Widget_Utils::defaultWidgetName(WidgetBase::WidgetType::wt_State);
3227             bool ok;
3228             do {
3229                 if (!Diagram_Utils::isUniqueDiagramName(Uml::DiagramType::State, name))
3230                     name.append(QLatin1String("_1"));
3231                 ok = Dialog_Utils::askNewName(WidgetBase::WidgetType::wt_State, name);
3232             } while(ok && !Diagram_Utils::isUniqueDiagramName(Uml::DiagramType::State, name));
3233             if (ok) {
3234                 StateWidget* state = new StateWidget(this);
3235                 state->setName(name);
3236                 setupNewWidget(state);
3237                 Uml::CmdCreateDiagram* d = new Uml::CmdCreateDiagram(m_doc, Uml::DiagramType::State, name);
3238                 UMLApp::app()->executeCommand(d);
3239                 state->setDiagramLink(d->view()->umlScene()->ID());
3240                 d->view()->umlScene()->setWidgetLink(state);
3241                 state->setStateType(StateWidget::Combined);
3242             }
3243         }
3244         break;
3245 
3246     case ListPopupMenu::mt_ReturnToClass:
3247     case ListPopupMenu::mt_ReturnToCombinedState:
3248         if (widgetLink()) {
3249             UMLApp::app()->document()->changeCurrentView(widgetLink()->umlScene()->ID());
3250             widgetLink()->update();
3251             widgetLink()->umlScene()->update();
3252             setWidgetLink(nullptr);
3253         }
3254         break;
3255 
3256     case ListPopupMenu::mt_Initial_Activity:
3257         {
3258             ActivityWidget* activity = new ActivityWidget(this, ActivityWidget::Initial);
3259             setupNewWidget(activity);
3260         }
3261         break;
3262 
3263     case ListPopupMenu::mt_End_Activity:
3264         {
3265             ActivityWidget* activity = new ActivityWidget(this, ActivityWidget::End);
3266             setupNewWidget(activity);
3267         }
3268         break;
3269 
3270     case ListPopupMenu::mt_Branch:
3271         {
3272             ActivityWidget* activity = new ActivityWidget(this, ActivityWidget::Branch);
3273             setupNewWidget(activity);
3274         }
3275         break;
3276 
3277     case ListPopupMenu::mt_Activity:
3278         {
3279             QString name;
3280             bool ok = Dialog_Utils::askDefaultNewName(WidgetBase::wt_Activity, name);
3281             if (ok) {
3282                 ActivityWidget* activity = new ActivityWidget(this, ActivityWidget::Normal);
3283                 activity->setName(name);
3284                 setupNewWidget(activity);
3285             }
3286         }
3287         break;
3288 
3289     case ListPopupMenu::mt_SnapToGrid:
3290         toggleSnapToGrid();
3291         m_doc->setModified();
3292         break;
3293 
3294     case ListPopupMenu::mt_SnapComponentSizeToGrid:
3295         toggleSnapComponentSizeToGrid();
3296         m_doc->setModified();
3297         break;
3298 
3299     case ListPopupMenu::mt_ShowSnapGrid:
3300         toggleShowGrid();
3301         m_doc->setModified();
3302         break;
3303 
3304     case ListPopupMenu::mt_ShowDocumentationIndicator:
3305         setShowDocumentationIndicator(!isShowDocumentationIndicator());
3306         update();
3307         break;
3308 
3309     case ListPopupMenu::mt_Properties:
3310         if (m_view->showPropertiesDialog() == true)
3311             m_doc->setModified();
3312         break;
3313 
3314     case ListPopupMenu::mt_Delete:
3315         m_doc->removeDiagram(ID());
3316         break;
3317 
3318     case ListPopupMenu::mt_Rename:
3319         {
3320             QString newName = name();
3321             bool ok = Dialog_Utils::askName(i18n("Enter Diagram Name"),
3322                                             i18n("Enter the new name of the diagram:"),
3323                                             newName);
3324             if (ok) {
3325                 setName(newName);
3326                 m_doc->signalDiagramRenamed(activeView());
3327             }
3328         }
3329         break;
3330 
3331     case ListPopupMenu::mt_Import_from_File:
3332     {
3333         QPointer<UMLFileDialog> dialog = new UMLFileDialog(QUrl(), QString(), UMLApp::app());
3334         dialog->exec();
3335         QUrl url = dialog->selectedUrl();
3336         if (!url.isEmpty())
3337             if (!Diagram_Utils::importGraph(url.toLocalFile(), this))
3338                 UMLApp::app()->slotStatusMsg(i18n("Failed to import from file."));
3339         break;
3340     }
3341 
3342     case ListPopupMenu::mt_MessageCreation:
3343         m_d->triggerToolBarButton(WorkToolBar::tbb_Seq_Message_Creation);
3344         break;
3345 
3346     case ListPopupMenu::mt_MessageDestroy:
3347         m_d->triggerToolBarButton(WorkToolBar::tbb_Seq_Message_Destroy);
3348         break;
3349 
3350     case ListPopupMenu::mt_MessageSynchronous:
3351         m_d->triggerToolBarButton(WorkToolBar::tbb_Seq_Message_Synchronous);
3352         break;
3353 
3354     case ListPopupMenu::mt_MessageAsynchronous:
3355         m_d->triggerToolBarButton(WorkToolBar::tbb_Seq_Message_Asynchronous);
3356         break;
3357 
3358     case ListPopupMenu::mt_MessageFound:
3359         m_d->triggerToolBarButton(WorkToolBar::tbb_Seq_Message_Found);
3360         break;
3361 
3362     case ListPopupMenu::mt_MessageLost:
3363         m_d->triggerToolBarButton(WorkToolBar::tbb_Seq_Message_Lost);
3364         break;
3365 
3366     default:
3367         uWarning() << "unknown ListPopupMenu::MenuType " << ListPopupMenu::toString(sel);
3368         break;
3369     }
3370 }
3371 
3372 /**
3373  * Connects to the signal that @ref UMLApp emits when a cut operation
3374  * is successful.
3375  * If the view or a child started the operation the flag m_bStartedCut will
3376  * be set and we can carry out any operation that is needed, like deleting the selected
3377  * widgets for the cut operation.
3378  */
slotCutSuccessful()3379 void UMLScene::slotCutSuccessful()
3380 {
3381     if (m_bStartedCut) {
3382         deleteSelection();
3383         m_bStartedCut = false;
3384     }
3385 }
3386 
3387 /**
3388  * Called by menu when to show the instance of the view.
3389  */
slotShowView()3390 void UMLScene::slotShowView()
3391 {
3392     m_doc->changeCurrentView(ID());
3393 }
3394 
3395 /**
3396  * Returns the offset point at which to place the paste from clipboard.
3397  * Just add the amount to your co-ords.
3398  * Only call this straight after the event, the value won't stay valid.
3399  * Should only be called by Assoc widgets at the moment. no one else needs it.
3400  */
getPastePoint()3401 QPointF UMLScene::getPastePoint()
3402 {
3403     QPointF point = m_PastePoint;
3404     point.setX(point.x() - m_pos.x());
3405     point.setY(point.y() - m_pos.y());
3406     return point;
3407 }
3408 
3409 /**
3410  * Reset the paste point.
3411  */
resetPastePoint()3412 void UMLScene::resetPastePoint()
3413 {
3414     m_PastePoint = m_pos;
3415 }
3416 
3417 /**
3418  * Called by the view or any of its children when they start a cut
3419  * operation.
3420  */
setStartedCut()3421 void UMLScene::setStartedCut()
3422 {
3423     m_bStartedCut = true;
3424 }
3425 
3426 /**
3427  * Returns the font to use
3428  */
font() const3429 QFont UMLScene::font() const
3430 {
3431     return m_Options.uiState.font;
3432 }
3433 
3434 /**
3435  * Sets the font for the view and optionally all the widgets on the view.
3436  */
setFont(QFont font,bool changeAllWidgets)3437 void UMLScene::setFont(QFont font, bool changeAllWidgets /* = false */)
3438 {
3439     m_Options.uiState.font = font;
3440     if (!changeAllWidgets)
3441         return;
3442     foreach(UMLWidget* w, widgetList()) {
3443         uIgnoreZeroPointer(w);
3444         w->setFont(font);
3445     }
3446 }
3447 
3448 /**
3449  * Sets some options for all the @ref ClassifierWidget on the view.
3450  */
setClassWidgetOptions(ClassOptionsPage * page)3451 void UMLScene::setClassWidgetOptions(ClassOptionsPage * page)
3452 {
3453     foreach(UMLWidget* pWidget, widgetList()) {
3454         uIgnoreZeroPointer(pWidget);
3455         WidgetBase::WidgetType wt = pWidget->baseType();
3456         if (wt == WidgetBase::wt_Class) {
3457             page->setWidget(pWidget->asClassifierWidget());
3458             page->apply();
3459         } else if (wt == WidgetBase::wt_Interface) {
3460             page->setWidget(pWidget->asInterfaceWidget());
3461             page->apply();
3462         }
3463     }
3464 }
3465 
3466 /**
3467  * Returns the type of the selected widget or widgets.
3468  *
3469  * If multiple widgets of different types are selected. WidgetType::UMLWidget
3470  * is returned.
3471  */
getUniqueSelectionType()3472 WidgetBase::WidgetType UMLScene::getUniqueSelectionType()
3473 {
3474     if (selectedWidgets().isEmpty()) {
3475         return WidgetBase::wt_UMLWidget;
3476     }
3477 
3478     // Get the first item and its base type
3479     UMLWidget * pTemp = (UMLWidget *) selectedWidgets().first();
3480     WidgetBase::WidgetType tmpType = pTemp->baseType();
3481 
3482     // Check all selected items, if they have the same BaseType
3483     foreach(pTemp, selectedWidgets()) {
3484         if (pTemp->baseType() != tmpType) {
3485             return WidgetBase::wt_UMLWidget;
3486         }
3487     }
3488 
3489     return tmpType;
3490 }
3491 
3492 /**
3493  * Asks for confirmation and clears everything on the diagram.
3494  * Called from menus.
3495  */
clearDiagram()3496 void UMLScene::clearDiagram()
3497 {
3498     if (Dialog_Utils::askDeleteDiagram()) {
3499         removeAllWidgets();
3500     }
3501 }
3502 
3503 /**
3504  * Apply an automatic layout.
3505  */
applyLayout(const QString & variant)3506 void UMLScene::applyLayout(const QString &variant)
3507 {
3508     DEBUG(DBG_SRC) << "layout = " << variant;
3509     LayoutGenerator r;
3510     r.generate(this, variant);
3511     r.apply(this);
3512     resizeSceneToItems();
3513     UMLApp::app()->slotZoomFit();
3514 }
3515 
3516 /**
3517  * Changes snap to grid boolean.
3518  * Called from menus.
3519  */
toggleSnapToGrid()3520 void UMLScene::toggleSnapToGrid()
3521 {
3522     setSnapToGrid(!snapToGrid());
3523 }
3524 
3525 /**
3526  * Changes snap to grid for component size boolean.
3527  * Called from menus.
3528  */
toggleSnapComponentSizeToGrid()3529 void UMLScene::toggleSnapComponentSizeToGrid()
3530 {
3531     setSnapComponentSizeToGrid(!snapComponentSizeToGrid());
3532 }
3533 
3534 /**
3535  * Changes show grid boolean.
3536  * Called from menus.
3537  */
toggleShowGrid()3538 void UMLScene::toggleShowGrid()
3539 {
3540     setSnapGridVisible(!isSnapGridVisible());
3541 }
3542 
3543 /**
3544  * Return whether to use snap to grid.
3545  */
snapToGrid() const3546 bool UMLScene::snapToGrid() const
3547 {
3548     return m_bUseSnapToGrid;
3549 }
3550 
3551 /**
3552  *  Sets whether to snap to grid.
3553  */
setSnapToGrid(bool bSnap)3554 void UMLScene::setSnapToGrid(bool bSnap)
3555 {
3556     m_bUseSnapToGrid = bSnap;
3557     emit sigSnapToGridToggled(snapToGrid());
3558 }
3559 
3560 /**
3561  * Return whether to use snap to grid for component size.
3562  */
snapComponentSizeToGrid() const3563 bool UMLScene::snapComponentSizeToGrid() const
3564 {
3565     return m_bUseSnapComponentSizeToGrid;
3566 }
3567 
3568 /**
3569  * Sets whether to snap to grid for component size.
3570  */
setSnapComponentSizeToGrid(bool bSnap)3571 void UMLScene::setSnapComponentSizeToGrid(bool bSnap)
3572 {
3573     m_bUseSnapComponentSizeToGrid = bSnap;
3574     updateComponentSizes();
3575     emit sigSnapComponentSizeToGridToggled(snapComponentSizeToGrid());
3576 }
3577 
3578 /**
3579  * Returns the x grid size.
3580  */
snapX() const3581 int UMLScene::snapX() const
3582 {
3583     return m_layoutGrid->gridSpacingX();
3584 }
3585 
3586 /**
3587  * Returns the y grid size.
3588  */
snapY() const3589 int UMLScene::snapY() const
3590 {
3591     return m_layoutGrid->gridSpacingY();
3592 }
3593 
3594 /**
3595  * Sets the grid size in x and y.
3596  */
setSnapSpacing(int x,int y)3597 void UMLScene::setSnapSpacing(int x, int y)
3598 {
3599     m_layoutGrid->setGridSpacing(x, y);
3600 }
3601 
3602 /**
3603  * Returns the input coordinate with possible grid-snap applied.
3604  */
snappedX(qreal _x)3605 qreal UMLScene::snappedX(qreal _x)
3606 {
3607     if (snapToGrid()) {
3608         int x = (int)_x;
3609         int gridX = snapX();
3610         int modX = x % gridX;
3611         x -= modX;
3612         if (modX >= gridX / 2)
3613             x += gridX;
3614         return x;
3615     }
3616     else
3617         return _x;
3618 }
3619 
3620 /**
3621  * Returns the input coordinate with possible grid-snap applied.
3622  */
snappedY(qreal _y)3623 qreal UMLScene::snappedY(qreal _y)
3624 {
3625     if (snapToGrid()) {
3626         int y = (int)_y;
3627         int gridY = snapY();
3628         int modY = y % gridY;
3629         y -= modY;
3630         if (modY >= gridY / 2)
3631             y += gridY;
3632         return y;
3633     }
3634     else
3635         return _y;
3636 }
3637 
3638 /**
3639  *  Returns whether to show snap grid or not.
3640  */
isSnapGridVisible() const3641 bool UMLScene::isSnapGridVisible() const
3642 {
3643     return m_layoutGrid->isVisible();
3644 }
3645 
3646 /**
3647  * Sets whether to show snap grid.
3648  */
setSnapGridVisible(bool bShow)3649 void UMLScene::setSnapGridVisible(bool bShow)
3650 {
3651     m_layoutGrid->setVisible(bShow);
3652     emit sigShowGridToggled(bShow);
3653 }
3654 
3655 /**
3656  *  Returns whether to show documentation indicator.
3657  */
isShowDocumentationIndicator() const3658 bool UMLScene::isShowDocumentationIndicator() const
3659 {
3660     return m_showDocumentationIndicator;
3661 }
3662 
3663 /**
3664  *  sets whether to show documentation indicator.
3665  */
setShowDocumentationIndicator(bool bShow)3666 void UMLScene::setShowDocumentationIndicator(bool bShow)
3667 {
3668     m_showDocumentationIndicator = bShow;
3669 }
3670 
3671 /**
3672  * Returns whether to show operation signatures.
3673  */
showOpSig() const3674 bool UMLScene::showOpSig() const
3675 {
3676     return m_Options.classState.showOpSig;
3677 }
3678 
3679 /**
3680  * Sets whether to show operation signatures.
3681  */
setShowOpSig(bool bShowOpSig)3682 void UMLScene::setShowOpSig(bool bShowOpSig)
3683 {
3684     m_Options.classState.showOpSig = bShowOpSig;
3685 }
3686 
3687 /**
3688  * Changes the zoom to the currently set level (now loaded from file)
3689  * Called from UMLApp::slotUpdateViews()
3690  */
fileLoaded()3691 void UMLScene::fileLoaded()
3692 {
3693     m_view->setZoom(m_view->zoom());
3694     resizeSceneToItems();
3695 }
3696 
3697 /**
3698  * Sets the size of the scene to just fit on all the items
3699  */
resizeSceneToItems()3700 void UMLScene::resizeSceneToItems()
3701 {
3702     // let QGraphicsScene handle scene size by itself
3703     setSceneRect(itemsBoundingRect());
3704 }
3705 
3706 /**
3707  * Updates the size of all components in this view.
3708  */
updateComponentSizes()3709 void UMLScene::updateComponentSizes()
3710 {
3711     // update sizes of all components
3712     foreach(UMLWidget *obj, widgetList()) {
3713         uIgnoreZeroPointer(obj);
3714         obj->updateGeometry();
3715     }
3716 }
3717 
3718 /**
3719  * Force the widget font metrics to be updated next time
3720  * the widgets are drawn.
3721  * This is necessary because the widget size might depend on the
3722  * font metrics and the font metrics might change for different
3723  * QPainter, i.e. font metrics for Display font and Printer font are
3724  * usually different.
3725  * Call this when you change the QPainter.
3726  */
forceUpdateWidgetFontMetrics(QPainter * painter)3727 void UMLScene::forceUpdateWidgetFontMetrics(QPainter * painter)
3728 {
3729     foreach(UMLWidget *obj, widgetList()) {
3730         uIgnoreZeroPointer(obj);
3731         obj->forceUpdateFontMetrics(painter);
3732     }
3733 }
3734 
3735 /**
3736  * Overrides standard method from QGraphicsScene drawing the background.
3737  */
drawBackground(QPainter * painter,const QRectF & rect)3738 void UMLScene::drawBackground(QPainter *painter, const QRectF &rect)
3739 {
3740     QGraphicsScene::drawBackground(painter, rect);
3741     m_layoutGrid->paint(painter, rect);
3742     // debug info
3743     if (Tracer::instance()->isEnabled(QLatin1String(metaObject()->className()))) {
3744         painter->setPen(Qt::green);
3745         painter->drawRect(sceneRect());
3746         QVector<QLineF> origin;
3747         origin << QLineF(QPointF(-5,0), QPointF(5,0))
3748               << QLineF(QPointF(0,-5), QPointF(0,5));
3749         painter->drawLines(origin);
3750         painter->setPen(Qt::blue);
3751         painter->drawRect(itemsBoundingRect());
3752         QVector<QLineF> lines;
3753         lines << QLineF(m_pos + QPointF(-5,0), m_pos + QPointF(5,0))
3754               << QLineF(m_pos + QPointF(0,-5), m_pos + QPointF(0,5));
3755         painter->setPen(Qt::gray);
3756         painter->drawLines(lines);
3757     }
3758 }
3759 
3760 /**
3761  * Creates the "diagram" tag and fills it with the contents of the diagram.
3762  */
saveToXMI1(QXmlStreamWriter & writer)3763 void UMLScene::saveToXMI1(QXmlStreamWriter& writer)
3764 {
3765     resizeSceneToItems();
3766     writer.writeStartElement(QLatin1String("diagram"));
3767     writer.writeAttribute(QLatin1String("xmi.id"), Uml::ID::toString(m_nID));
3768     writer.writeAttribute(QLatin1String("name"), name());
3769     writer.writeAttribute(QLatin1String("type"), QString::number(m_Type));
3770     writer.writeAttribute(QLatin1String("documentation"), m_Documentation);
3771     //option state
3772     m_Options.saveToXMI1(writer);
3773     //misc
3774     writer.writeAttribute(QLatin1String("localid"), Uml::ID::toString(m_nLocalID));
3775     writer.writeAttribute(QLatin1String("showgrid"), QString::number(m_layoutGrid->isVisible()));
3776     writer.writeAttribute(QLatin1String("snapgrid"), QString::number(m_bUseSnapToGrid));
3777     writer.writeAttribute(QLatin1String("snapcsgrid"), QString::number(m_bUseSnapComponentSizeToGrid));
3778     writer.writeAttribute(QLatin1String("snapx"), QString::number(m_layoutGrid->gridSpacingX()));
3779     writer.writeAttribute(QLatin1String("snapy"), QString::number(m_layoutGrid->gridSpacingY()));
3780     // FIXME: move to UMLView
3781     writer.writeAttribute(QLatin1String("zoom"), QString::number(activeView()->zoom()));
3782     writer.writeAttribute(QLatin1String("canvasheight"), QString::number(height()));
3783     writer.writeAttribute(QLatin1String("canvaswidth"), QString::number(width()));
3784     writer.writeAttribute(QLatin1String("isopen"), QString::number(isOpen()));
3785     if (isSequenceDiagram() || isCollaborationDiagram())
3786         writer.writeAttribute(QLatin1String("autoincrementsequence"), QString::number(autoIncrementSequence()));
3787 
3788     //now save all the widgets
3789     writer.writeStartElement(QLatin1String("widgets"));
3790     foreach(UMLWidget *widget, widgetList()) {
3791         uIgnoreZeroPointer(widget);
3792         // do not save floating text widgets having a parent widget; they are saved as part of the parent
3793         if (widget->isTextWidget() && widget->parentItem())
3794             continue;
3795         // Having an exception is bad I know, but gotta work with
3796         // system we are given.
3797         // We DON'T want to record any text widgets which are belonging
3798         // to associations as they are recorded later in the "associations"
3799         // section when each owning association is dumped. -b.t.
3800         if ((!widget->isTextWidget() &&
3801              !widget->isFloatingDashLineWidget()) ||
3802              (widget->asFloatingTextWidget() && widget->asFloatingTextWidget()->link() == 0))
3803             widget->saveToXMI1(writer);
3804     }
3805     writer.writeEndElement();            // widgets
3806     //now save the message widgets
3807     writer.writeStartElement(QLatin1String("messages"));
3808     foreach(UMLWidget* widget, messageList()) {
3809         widget->saveToXMI1(writer);
3810     }
3811     writer.writeEndElement();            // messages
3812     //now save the associations
3813     writer.writeStartElement(QLatin1String("associations"));
3814     if (associationList().count()) {
3815         // We guard against (associationList().count() == 0) because
3816         // this code could be reached as follows:
3817         //  ^  UMLScene::saveToXMI1()
3818         //  ^  UMLDoc::saveToXMI1()
3819         //  ^  UMLDoc::addToUndoStack()
3820         //  ^  UMLDoc::setModified()
3821         //  ^  UMLDoc::createDiagram()
3822         //  ^  UMLDoc::newDocument()
3823         //  ^  UMLApp::newDocument()
3824         //  ^  main()
3825         //
3826         AssociationWidget * assoc = 0;
3827         foreach(assoc, associationList()) {
3828             assoc->saveToXMI1(writer);
3829         }
3830     }
3831     writer.writeEndElement();            // associations
3832     writer.writeEndElement();  // diagram
3833 }
3834 
3835 /**
3836  * Loads the "diagram" tag.
3837  */
loadFromXMI1(QDomElement & qElement)3838 bool UMLScene::loadFromXMI1(QDomElement & qElement)
3839 {
3840     QString id = qElement.attribute(QLatin1String("xmi.id"), QLatin1String("-1"));
3841     m_nID = Uml::ID::fromString(id);
3842     if (m_nID == Uml::ID::None)
3843         return false;
3844     setName(qElement.attribute(QLatin1String("name")));
3845     QString type = qElement.attribute(QLatin1String("type"), QLatin1String("0"));
3846     m_Documentation = qElement.attribute(QLatin1String("documentation"));
3847     QString localid = qElement.attribute(QLatin1String("localid"), QLatin1String("0"));
3848     // option state
3849     m_Options.loadFromXMI1(qElement);
3850     setBackgroundBrush(m_Options.uiState.backgroundColor);
3851     setGridDotColor(m_Options.uiState.gridDotColor);
3852     //misc
3853     QString showgrid = qElement.attribute(QLatin1String("showgrid"), QLatin1String("0"));
3854     m_layoutGrid->setVisible((bool)showgrid.toInt());
3855 
3856     QString snapgrid = qElement.attribute(QLatin1String("snapgrid"), QLatin1String("0"));
3857     m_bUseSnapToGrid = (bool)snapgrid.toInt();
3858 
3859     QString snapcsgrid = qElement.attribute(QLatin1String("snapcsgrid"), QLatin1String("0"));
3860     m_bUseSnapComponentSizeToGrid = (bool)snapcsgrid.toInt();
3861 
3862     QString snapx = qElement.attribute(QLatin1String("snapx"), QLatin1String("10"));
3863     QString snapy = qElement.attribute(QLatin1String("snapy"), QLatin1String("10"));
3864     m_layoutGrid->setGridSpacing(snapx.toInt(), snapy.toInt());
3865 
3866     QString zoom = qElement.attribute(QLatin1String("zoom"), QLatin1String("100"));
3867     activeView()->setZoom(zoom.toInt());
3868     resizeSceneToItems();
3869 
3870     QString isOpen = qElement.attribute(QLatin1String("isopen"), QLatin1String("1"));
3871     m_isOpen = (bool)isOpen.toInt();
3872 
3873     int nType = type.toInt();
3874     if (nType == -1 || nType >= 400) {
3875         // Pre 1.5.5 numeric values
3876         // Values of "type" were changed in 1.5.5 to merge with Settings::Diagram
3877         switch (nType) {
3878         case 400:
3879             m_Type = Uml::DiagramType::UseCase;
3880             break;
3881         case 401:
3882             m_Type = Uml::DiagramType::Collaboration;
3883             break;
3884         case 402:
3885             m_Type = Uml::DiagramType::Class;
3886             break;
3887         case 403:
3888             m_Type = Uml::DiagramType::Sequence;
3889             break;
3890         case 404:
3891             m_Type = Uml::DiagramType::State;
3892             break;
3893         case 405:
3894             m_Type = Uml::DiagramType::Activity;
3895             break;
3896         case 406:
3897             m_Type = Uml::DiagramType::Component;
3898             break;
3899         case 407:
3900             m_Type = Uml::DiagramType::Deployment;
3901             break;
3902         case 408:
3903             m_Type = Uml::DiagramType::EntityRelationship;
3904             break;
3905         case 409:
3906             m_Type = Uml::DiagramType::Object;
3907             break;
3908         default:
3909             m_Type = Uml::DiagramType::Undefined;
3910             break;
3911         }
3912     } else {
3913         m_Type = Uml::DiagramType::fromInt(nType);
3914     }
3915     m_nLocalID = Uml::ID::fromString(localid);
3916 
3917     if (m_Type == Uml::DiagramType::Sequence ||
3918         m_Type == Uml::DiagramType::Collaboration) {
3919         QString autoIncrementSequence = qElement.attribute(QLatin1String("autoincrementsequence"),
3920                                                            QLatin1String("0"));
3921         m_autoIncrementSequence = (bool)autoIncrementSequence.toInt();
3922     }
3923 
3924     QDomNode node = qElement.firstChild();
3925     bool widgetsLoaded = false, messagesLoaded = false, associationsLoaded = false;
3926     while (!node.isNull()) {
3927         QDomElement element = node.toElement();
3928         if (!element.isNull()) {
3929             if (element.tagName() == QLatin1String("widgets"))
3930                 widgetsLoaded = loadWidgetsFromXMI(element);
3931             else if (element.tagName() == QLatin1String("messages"))
3932                 messagesLoaded = loadMessagesFromXMI(element);
3933             else if (element.tagName() == QLatin1String("associations"))
3934                 associationsLoaded = loadAssociationsFromXMI(element);
3935         }
3936         node = node.nextSibling();
3937     }
3938 
3939     if (!widgetsLoaded) {
3940         uWarning() << "failed UMLScene load on widgets";
3941         return false;
3942     }
3943     if (!messagesLoaded) {
3944         uWarning() << "failed UMLScene load on messages";
3945         return false;
3946     }
3947     if (!associationsLoaded) {
3948         uWarning() << "failed UMLScene load on associations";
3949         return false;
3950     }
3951 
3952     if (this->isComponentDiagram()) {
3953         m_d->addMissingPorts();
3954         m_d->fixPortPositions();
3955     }
3956     m_d->removeDuplicatedFloatingTextInstances();
3957     return true;
3958 }
3959 
loadWidgetsFromXMI(QDomElement & qElement)3960 bool UMLScene::loadWidgetsFromXMI(QDomElement & qElement)
3961 {
3962     UMLWidget* widget = 0;
3963     QDomNode node = qElement.firstChild();
3964     QDomElement widgetElement = node.toElement();
3965     while (!widgetElement.isNull()) {
3966         widget = loadWidgetFromXMI(widgetElement);
3967         if (widget) {
3968             addWidgetCmd(widget);
3969             widget->clipSize();
3970             // In the interest of best-effort loading, in case of a
3971             // (widget == 0) we still go on.
3972             // The individual widget's loadFromXMI1 method should
3973             // already have generated an error message to tell the
3974             // user that something went wrong.
3975         }
3976         node = widgetElement.nextSibling();
3977         widgetElement = node.toElement();
3978     }
3979 
3980     return true;
3981 }
3982 
3983 /**
3984  * Loads a "widget" element from XMI, used by loadFromXMI1() and the clipboard.
3985  */
loadWidgetFromXMI(QDomElement & widgetElement)3986 UMLWidget* UMLScene::loadWidgetFromXMI(QDomElement& widgetElement)
3987 {
3988     if (!m_doc) {
3989         uWarning() << "m_doc is NULL";
3990         return 0L;
3991     }
3992 
3993     QString tag  = widgetElement.tagName();
3994     QString idstr  = widgetElement.attribute(QLatin1String("xmi.id"), QLatin1String("-1"));
3995     UMLWidget* widget = Widget_Factory::makeWidgetFromXMI(tag, idstr, this);
3996 
3997     if (widget == 0)
3998         return 0;
3999     if (!widget->loadFromXMI1(widgetElement)) {
4000         widget->cleanup();
4001         delete widget;
4002         return 0;
4003     }
4004     return widget;
4005 }
4006 
loadMessagesFromXMI(QDomElement & qElement)4007 bool UMLScene::loadMessagesFromXMI(QDomElement & qElement)
4008 {
4009     MessageWidget * message = 0;
4010     QDomNode node = qElement.firstChild();
4011     QDomElement messageElement = node.toElement();
4012     while (!messageElement.isNull()) {
4013         QString tag = messageElement.tagName();
4014         DEBUG(DBG_SRC) << "tag = " << tag;
4015         if (tag == QLatin1String("messagewidget") ||
4016             tag == QLatin1String("UML:MessageWidget")) {   // for bkwd compatibility
4017             message = new MessageWidget(this, SequenceMessage::Asynchronous,
4018                                         Uml::ID::Reserved);
4019             if (!message->loadFromXMI1(messageElement)) {
4020                 delete message;
4021                 return false;
4022             }
4023             addWidgetCmd(message);
4024             FloatingTextWidget *ft = message->floatingTextWidget();
4025             if (!ft && message->sequenceMessageType() != SequenceMessage::Creation)
4026                 DEBUG(DBG_SRC) << "floating text is NULL for message " << Uml::ID::toString(message->id());
4027         }
4028         node = messageElement.nextSibling();
4029         messageElement = node.toElement();
4030     }
4031     return true;
4032 }
4033 
loadAssociationsFromXMI(QDomElement & qElement)4034 bool UMLScene::loadAssociationsFromXMI(QDomElement & qElement)
4035 {
4036     QDomNode node = qElement.firstChild();
4037     QDomElement assocElement = node.toElement();
4038     int countr = 0;
4039     while (!assocElement.isNull()) {
4040         QString tag = assocElement.tagName();
4041         if (tag == QLatin1String("assocwidget") ||
4042             tag == QLatin1String("UML:AssocWidget")) {  // for bkwd compatibility
4043             countr++;
4044             AssociationWidget *assoc = AssociationWidget::create(this);
4045             if (!assoc->loadFromXMI1(assocElement)) {
4046                 uError() << "could not loadFromXMI1 association widget:"
4047                          << assoc << ", bad XMI file? Deleting from UMLScene.";
4048                 delete assoc;
4049                 /* return false;
4050                    Returning false here is a little harsh when the
4051                    rest of the diagram might load okay.
4052                  */
4053             } else {
4054                 assoc->clipSize();
4055                 if (!addAssociation(assoc, false)) {
4056                     uError() << "Could not addAssociation(" << assoc << ") to UMLScene, deleting.";
4057                     delete assoc;
4058                     //return false; // soften error.. may not be that bad
4059                 }
4060             }
4061         }
4062         node = assocElement.nextSibling();
4063         assocElement = node.toElement();
4064     }
4065     return true;
4066 }
4067 
4068 /**
4069  * Add an object to the application, and update the view.
4070  */
addObject(UMLObject * object)4071 void UMLScene::addObject(UMLObject *object)
4072 {
4073     m_bCreateObject = true;
4074     if (m_doc->addUMLObject(object))
4075         m_doc->signalUMLObjectCreated(object);  // m_bCreateObject is reset by slotObjectCreated()
4076     else
4077         m_bCreateObject = false;
4078 }
4079 
loadUisDiagramPresentation(QDomElement & qElement)4080 bool UMLScene::loadUisDiagramPresentation(QDomElement & qElement)
4081 {
4082     for (QDomNode node = qElement.firstChild(); !node.isNull(); node = node.nextSibling()) {
4083         QDomElement elem = node.toElement();
4084         QString tag = elem.tagName();
4085         if (! UMLDoc::tagEq(tag, QLatin1String("Presentation"))) {
4086             uError() << "ignoring unknown UisDiagramPresentation tag " << tag;
4087             continue;
4088         }
4089         QDomNode n = elem.firstChild();
4090         QDomElement e = n.toElement();
4091         QString idStr;
4092         int x = 0, y = 0, w = 0, h = 0;
4093         while (!e.isNull()) {
4094             tag = e.tagName();
4095             DEBUG(DBG_SRC) << "Presentation: tag = " << tag;
4096             if (UMLDoc::tagEq(tag, QLatin1String("Presentation.geometry"))) {
4097                 QDomNode gnode = e.firstChild();
4098                 QDomElement gelem = gnode.toElement();
4099                 QString csv = gelem.text();
4100                 QStringList dim = csv.split(QLatin1Char(','));
4101                 x = dim[0].toInt();
4102                 y = dim[1].toInt();
4103                 w = dim[2].toInt();
4104                 h = dim[3].toInt();
4105             } else if (UMLDoc::tagEq(tag, QLatin1String("Presentation.style"))) {
4106                 // TBD
4107             } else if (UMLDoc::tagEq(tag, QLatin1String("Presentation.model"))) {
4108                 QDomNode mnode = e.firstChild();
4109                 QDomElement melem = mnode.toElement();
4110                 idStr = melem.attribute(QLatin1String("xmi.idref"));
4111             } else {
4112                 DEBUG(DBG_SRC) << "ignoring tag " << tag;
4113             }
4114             n = n.nextSibling();
4115             e = n.toElement();
4116         }
4117         Uml::ID::Type id = Uml::ID::fromString(idStr);
4118         UMLObject *o = m_doc->findObjectById(id);
4119         if (o == 0) {
4120             uError() << "Cannot find object for id " << idStr;
4121         } else {
4122             UMLObject::ObjectType ot = o->baseType();
4123             DEBUG(DBG_SRC) << "Create widget for model object of type " << UMLObject::toString(ot);
4124             UMLWidget *widget = 0;
4125             switch (ot) {
4126             case UMLObject::ot_Class:
4127                 widget = new ClassifierWidget(this, o->asUMLClassifier());
4128                 break;
4129             case UMLObject::ot_Association: {
4130                 UMLAssociation *umla = o->asUMLAssociation();
4131                 Uml::AssociationType::Enum at = umla->getAssocType();
4132                 UMLObject* objA = umla->getObject(Uml::RoleType::A);
4133                 UMLObject* objB = umla->getObject(Uml::RoleType::B);
4134                 if (objA == 0 || objB == 0) {
4135                     uError() << "intern err 1";
4136                     return false;
4137                 }
4138                 UMLWidget *wA = findWidget(objA->id());
4139                 UMLWidget *wB = findWidget(objB->id());
4140                 if (wA != 0 && wB != 0) {
4141                     AssociationWidget *aw =
4142                         AssociationWidget::create(this, wA, at, wB, umla);
4143                     aw->syncToModel();
4144                     addWidgetCmd(aw);
4145                 } else {
4146                     uError() << "cannot create assocwidget from (" ; //<< wA << ", " << wB << ")";
4147                 }
4148                 break;
4149             }
4150             case UMLObject::ot_Role: {
4151                 //UMLRole *robj = o->asUMLRole();
4152                 //UMLAssociation *umla = robj->getParentAssociation();
4153                 // @todo properly display role names.
4154                 //       For now, in order to get the role names displayed
4155                 //       simply delete the participating diagram objects
4156                 //       and drag them from the list view to the diagram.
4157                 break;
4158             }
4159             default:
4160                 uError() << "Cannot create widget of type " << ot;
4161             }
4162             if (widget) {
4163                 DEBUG(DBG_SRC) << "Widget: x=" << x << ", y=" << y
4164                                << ", w=" << w << ", h=" << h;
4165                 widget->setX(x);
4166                 widget->setY(y);
4167                 widget->setSize(w, h);
4168                 addWidgetCmd(widget);
4169             }
4170         }
4171     }
4172     return true;
4173 }
4174 
4175 /**
4176  * Loads the "UISDiagram" tag of Unisys.IntegratePlus.2 generated files.
4177  */
loadUISDiagram(QDomElement & qElement)4178 bool UMLScene::loadUISDiagram(QDomElement & qElement)
4179 {
4180     QString idStr = qElement.attribute(QLatin1String("xmi.id"));
4181     if (idStr.isEmpty())
4182         return false;
4183     m_nID = Uml::ID::fromString(idStr);
4184     UMLListViewItem *ulvi = 0;
4185     for (QDomNode node = qElement.firstChild(); !node.isNull(); node = node.nextSibling()) {
4186         if (node.isComment())
4187             continue;
4188         QDomElement elem = node.toElement();
4189         QString tag = elem.tagName();
4190         if (tag == QLatin1String("uisDiagramName")) {
4191             setName(elem.text());
4192             if (ulvi)
4193                 ulvi->setText(name());
4194         } else if (tag == QLatin1String("uisDiagramStyle")) {
4195             QString diagramStyle = elem.text();
4196             if (diagramStyle != QLatin1String("ClassDiagram")) {
4197                 uError() << "diagram style " << diagramStyle << " is not yet implemented";
4198                 continue;
4199             }
4200             m_doc->setMainViewID(m_nID);
4201             m_Type = Uml::DiagramType::Class;
4202             UMLListView *lv = UMLApp::app()->listView();
4203             ulvi = new UMLListViewItem(lv->theLogicalView(), name(),
4204                                        UMLListViewItem::lvt_Class_Diagram, m_nID);
4205         } else if (tag == QLatin1String("uisDiagramPresentation")) {
4206             loadUisDiagramPresentation(elem);
4207         } else if (tag != QLatin1String("uisToolName")) {
4208             DEBUG(DBG_SRC) << "ignoring tag " << tag;
4209         }
4210     }
4211     return true;
4212 }
4213 
4214 /**
4215  * Left Alignment
4216  */
alignLeft()4217 void UMLScene::alignLeft()
4218 {
4219     UMLWidgetList widgetList = selectedWidgetsExt();
4220     if (widgetList.isEmpty())
4221         return;
4222 
4223     qreal smallestX = WidgetList_Utils::getSmallestX(widgetList);
4224 
4225     foreach(UMLWidget *widget, widgetList) {
4226         widget->setX(smallestX);
4227         widget->adjustAssocs(widget->x(), widget->y());
4228     }
4229     //TODO: Push stored cmds to stack.
4230 }
4231 
4232 /**
4233  * Right Alignment
4234  */
alignRight()4235 void UMLScene::alignRight()
4236 {
4237     UMLWidgetList widgetList = selectedWidgetsExt();
4238     if (widgetList.isEmpty())
4239         return;
4240     qreal biggestX = WidgetList_Utils::getBiggestX(widgetList);
4241 
4242     foreach(UMLWidget *widget, widgetList) {
4243         widget->setX(biggestX - widget->width());
4244         widget->adjustAssocs(widget->x(), widget->y());
4245     }
4246     //TODO: Push stored cmds to stack.
4247 }
4248 
4249 /**
4250  * Top Alignment
4251  */
alignTop()4252 void UMLScene::alignTop()
4253 {
4254     UMLWidgetList widgetList = selectedWidgetsExt();
4255     if (widgetList.isEmpty())
4256         return;
4257 
4258     qreal smallestY = WidgetList_Utils::getSmallestY(widgetList);
4259 
4260     foreach(UMLWidget *widget, widgetList) {
4261         widget->setY(smallestY);
4262         widget->adjustAssocs(widget->x(), widget->y());
4263     }
4264     //TODO: Push stored cmds to stack.
4265 }
4266 
4267 /**
4268  * Bottom Alignment
4269  */
alignBottom()4270 void UMLScene::alignBottom()
4271 {
4272     UMLWidgetList widgetList = selectedWidgetsExt();
4273     if (widgetList.isEmpty())
4274         return;
4275     qreal biggestY = WidgetList_Utils::getBiggestY(widgetList);
4276 
4277     foreach(UMLWidget *widget, widgetList) {
4278         widget->setY(biggestY - widget->height());
4279         widget->adjustAssocs(widget->x(), widget->y());
4280     }
4281     //TODO: Push stored cmds to stack.
4282 }
4283 
4284 /**
4285  * Vertical Middle Alignment
4286  */
alignVerticalMiddle()4287 void UMLScene::alignVerticalMiddle()
4288 {
4289     UMLWidgetList widgetList = selectedWidgetsExt();
4290     if (widgetList.isEmpty())
4291         return;
4292 
4293     qreal smallestY = WidgetList_Utils::getSmallestY(widgetList);
4294     qreal biggestY = WidgetList_Utils::getBiggestY(widgetList);
4295     qreal middle = int((biggestY - smallestY) / 2) + smallestY;
4296 
4297     foreach(UMLWidget *widget, widgetList) {
4298         widget->setY(middle - widget->height() / 2);
4299         widget->adjustAssocs(widget->x(), widget->y());
4300     }
4301 
4302     AssociationWidgetList assocList = selectedAssocs();
4303     if (!assocList.isEmpty()) {
4304         foreach (AssociationWidget *widget, assocList) {
4305             widget->setYEntireAssoc(middle);
4306         }
4307     }
4308 
4309     //TODO: Push stored cmds to stack.
4310 }
4311 
4312 /**
4313  * Horizontal Middle Alignment
4314  */
alignHorizontalMiddle()4315 void UMLScene::alignHorizontalMiddle()
4316 {
4317     UMLWidgetList widgetList = selectedWidgetsExt();
4318     if (widgetList.isEmpty())
4319         return;
4320 
4321     qreal smallestX = WidgetList_Utils::getSmallestX(widgetList);
4322     qreal biggestX = WidgetList_Utils::getBiggestX(widgetList);
4323     qreal middle = int((biggestX - smallestX) / 2) + smallestX;
4324 
4325     foreach(UMLWidget *widget, widgetList) {
4326         widget->setX(middle - widget->width() / 2);
4327         widget->adjustAssocs(widget->x(), widget->y());
4328     }
4329 
4330     AssociationWidgetList assocList = selectedAssocs();
4331     if (!assocList.isEmpty()) {
4332         foreach (AssociationWidget *widget, assocList) {
4333             widget->setXEntireAssoc(middle);
4334         }
4335     }
4336 
4337     //TODO: Push stored cmds to stack.
4338 }
4339 
4340 /**
4341  * Vertical Distribute Alignment
4342  */
alignVerticalDistribute()4343 void UMLScene::alignVerticalDistribute()
4344 {
4345     UMLWidgetList widgetList = selectedWidgetsExt();
4346     if (widgetList.isEmpty())
4347         return;
4348 
4349     qreal smallestY = WidgetList_Utils::getSmallestY(widgetList);
4350     qreal biggestY = WidgetList_Utils::getBiggestY(widgetList);
4351     qreal heightsSum = WidgetList_Utils::getHeightsSum(widgetList);
4352     qreal distance = int(((biggestY - smallestY) - heightsSum) / (widgetList.count() - 1.0) + 0.5);
4353 
4354     qSort(widgetList.begin(), widgetList.end(), Widget_Utils::hasSmallerY);
4355 
4356     int i = 1;
4357     UMLWidget* widgetPrev = 0;
4358     foreach(UMLWidget *widget, widgetList) {
4359         if (i == 1) {
4360             widgetPrev = widget;
4361         } else {
4362             widget->setY(widgetPrev->y() + widgetPrev->height() + distance);
4363             widget->adjustAssocs(widget->x(), widget->y());
4364             widgetPrev = widget;
4365         }
4366         i++;
4367     }
4368     //TODO: Push stored cmds to stack.
4369 }
4370 
4371 /**
4372  * Horizontal Distribute Alignment
4373  */
alignHorizontalDistribute()4374 void UMLScene::alignHorizontalDistribute()
4375 {
4376     UMLWidgetList widgetList = selectedWidgetsExt();
4377     if (widgetList.isEmpty())
4378         return;
4379 
4380     qreal smallestX = WidgetList_Utils::getSmallestX(widgetList);
4381     qreal biggestX = WidgetList_Utils::getBiggestX(widgetList);
4382     qreal widthsSum = WidgetList_Utils::getWidthsSum(widgetList);
4383     qreal distance = int(((biggestX - smallestX) - widthsSum) / (widgetList.count() - 1.0) + 0.5);
4384 
4385     qSort(widgetList.begin(), widgetList.end(), Widget_Utils::hasSmallerX);
4386 
4387     int i = 1;
4388     UMLWidget* widgetPrev = 0;
4389     foreach(UMLWidget *widget,  widgetList) {
4390         if (i == 1) {
4391             widgetPrev = widget;
4392         } else {
4393             widget->setX(widgetPrev->x() + widgetPrev->width() + distance);
4394             widget->adjustAssocs(widget->x(), widget->y());
4395             widgetPrev = widget;
4396         }
4397         i++;
4398     }
4399     //TODO: Push stored cmds to stack.
4400 }
4401 
4402 /**
4403  * Overloading operator for debugging output.
4404  */
operator <<(QDebug dbg,UMLScene * item)4405 QDebug operator<<(QDebug dbg, UMLScene *item)
4406 {
4407     dbg.nospace() << "UMLScene: " << item->name()
4408                   << " / type=" << DiagramType::toString(item->type())
4409                   << " / id=" << Uml::ID::toString(item->ID())
4410                   << " / isOpen=" << item->isOpen();
4411     return dbg.space();
4412 }
4413 
setWidgetLink(WidgetBase * w)4414 void UMLScene::setWidgetLink(WidgetBase *w)
4415 {
4416     m_d->widgetLink = w;
4417 }
4418 
widgetLink()4419 WidgetBase *UMLScene::widgetLink()
4420 {
4421     return m_d->widgetLink;
4422 }
4423