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