1 /*
2     SPDX-License-Identifier: GPL-2.0-or-later
3     SPDX-FileCopyrightText: 2002-2021 Umbrello UML Modeller Authors <umbrello-devel@kde.org>
4 */
5 
6 #include "umlwidget.h"
7 
8 // local includes
9 #include "artifact.h"
10 #include "artifactwidget.h"
11 #include "activitywidget.h"
12 #include "actor.h"
13 #include "actorwidget.h"
14 #include "associationwidget.h"
15 #include "classifier.h"
16 #include "classpropertiesdialog.h"
17 #include "cmds.h"
18 #include "component.h"
19 #include "componentwidget.h"
20 #include "debug_utils.h"
21 #include "dialog_utils.h"
22 #include "docwindow.h"
23 #include "floatingtextwidget.h"
24 #include "forkjoinwidget.h"
25 #include "interfacewidget.h"
26 #include "notewidget.h"
27 #include "messagewidget.h"
28 #include "objectwidget.h"
29 #include "object_factory.h"
30 #include "idchangelog.h"
31 #include "menus/listpopupmenu.h"
32 #include "objectnodewidget.h"
33 #include "pinwidget.h"
34 #include "port.h"
35 #include "portwidget.h"
36 #include "regionwidget.h"
37 #include "signalwidget.h"
38 #include "settingsdialog.h"
39 #include "statewidget.h"
40 #include "stereotype.h"
41 #include "uml.h"
42 #include "umldoc.h"
43 #include "umllistview.h"
44 #include "umlobject.h"
45 #include "umlscene.h"
46 #include "umlview.h"
47 #include "usecase.h"
48 #include "usecasewidget.h"
49 #include "uniqueid.h"
50 #include "widget_factory.h"
51 #include "widget_utils.h"
52 
53 // kde includes
54 #include <KLocalizedString>
55 #include <KMessageBox>
56 
57 // qt includes
58 #include <QApplication>
59 #include <QColor>
60 #include <QPainter>
61 #include <QPointer>
62 #include <QXmlStreamWriter>
63 
64 using namespace Uml;
65 
66 DEBUG_REGISTER_DISABLED(UMLWidget)
67 
68 #define I18N_NEXT_RELEASE(a,b) QString(QLatin1String(a)).arg(b))
69 
70 const QSizeF UMLWidget::DefaultMinimumSize(50, 20);
71 const QSizeF UMLWidget::DefaultMaximumSize(1000, 5000);
72 const int UMLWidget::defaultMargin = 5;
73 const int UMLWidget::selectionMarkerSize = 4;
74 const int UMLWidget::resizeMarkerLineCount = 3;
75 
76 
77 /**
78  * Creates a UMLWidget object.
79  *
80  * @param scene The view to be displayed on.
81  * @param type  The WidgetType to construct.
82  *              This must be set to the appropriate value by the constructors of inheriting classes.
83  * @param o The UMLObject to represent.
84  * @note Although a pointer to the scene is required, the widget is not added to the scene by default.
85  */
UMLWidget(UMLScene * scene,WidgetType type,UMLObject * o)86 UMLWidget::UMLWidget(UMLScene * scene, WidgetType type, UMLObject * o)
87   : WidgetBase(scene, type, o ? o->id() : Uml::ID::None)
88   , DiagramProxyWidget(this)
89 {
90     init();
91     m_umlObject = o;
92     if (m_umlObject) {
93         // TODO: calling WidgetBase::setUMLObject does not add this connection
94         connect(m_umlObject, SIGNAL(modified()), this, SLOT(updateWidget()));
95     }
96 }
97 
98 /**
99  * Creates a UMLWidget object.
100  *
101  * @param scene The view to be displayed on.
102  * @param type  The WidgetType to construct.
103  *              This must be set to the appropriate value by the constructors of inheriting classes.
104  * @param id The id of the widget.
105  *  The default value (id_None) will prompt generation of a new ID.
106  */
UMLWidget(UMLScene * scene,WidgetType type,Uml::ID::Type id)107 UMLWidget::UMLWidget(UMLScene *scene, WidgetType type, Uml::ID::Type id)
108   : WidgetBase(scene, type, id)
109   , DiagramProxyWidget(this)
110 {
111     init();
112 }
113 
114 /**
115  * Destructor.
116  */
~UMLWidget()117 UMLWidget::~UMLWidget()
118 {
119     cleanup();
120 }
121 
122 /**
123  * Assignment operator
124  */
operator =(const UMLWidget & other)125 UMLWidget& UMLWidget::operator=(const UMLWidget & other)
126 {
127     if (this == &other)
128         return *this;
129 
130     WidgetBase::operator=(other);
131     DiagramProxyWidget::operator=(other);
132 
133     // assign members loaded/saved
134     m_useFillColor = other.m_useFillColor;
135     m_usesDiagramFillColor = other.m_usesDiagramFillColor;
136     m_usesDiagramUseFillColor = other.m_usesDiagramUseFillColor;
137     m_fillColor = other.m_fillColor;
138     m_Assocs = other.m_Assocs;
139     m_isInstance = other.m_isInstance;
140     m_instanceName = other.m_instanceName;
141     m_instanceName = other.m_instanceName;
142     m_showStereotype = other.m_showStereotype;
143     setX(other.x());
144     setY(other.y());
145     setRect(rect().x(), rect().y(), other.width(), other.height());
146 
147     // assign volatile (non-saved) members
148     m_startMove = other.m_startMove;
149     m_nPosX = other.m_nPosX;
150     m_doc = other.m_doc;    //new
151     m_resizable = other.m_resizable;
152     for (unsigned i = 0; i < FT_INVALID; ++i)
153         m_pFontMetrics[i] = other.m_pFontMetrics[i];
154     m_activated = other.m_activated;
155     m_ignoreSnapToGrid = other.m_ignoreSnapToGrid;
156     m_ignoreSnapComponentSizeToGrid = other.m_ignoreSnapComponentSizeToGrid;
157     return *this;
158 }
159 
160 /**
161  * Overload '==' operator
162  */
operator ==(const UMLWidget & other) const163 bool UMLWidget::operator==(const UMLWidget& other) const
164 {
165     if (this == &other)
166         return true;
167 
168     if (baseType() != other.baseType()) {
169         return false;
170     }
171 
172     if (id() != other.id())
173         return false;
174 
175     /* Testing the associations is already an exaggeration, no?
176        The type and ID should uniquely identify a UMLWidget.
177      */
178     if (m_Assocs.count() != other.m_Assocs.count()) {
179         return false;
180     }
181 
182     // if(getBaseType() != wt_Text) // DON'T do this for floatingtext widgets, an infinite loop will result
183     // {
184     AssociationWidgetListIt assoc_it(m_Assocs);
185     AssociationWidgetListIt assoc_it2(other.m_Assocs);
186     AssociationWidget * assoc = 0, *assoc2 = 0;
187     while (assoc_it.hasNext() &&  assoc_it2.hasNext()) {
188         assoc = assoc_it.next();
189         assoc2 = assoc_it2.next();
190 
191         if (!(*assoc == *assoc2)) {
192             return false;
193         }
194     }
195     // }
196     return true;
197     // NOTE:  In the comparison tests we are going to do, we don't need these values.
198     // They will actually stop things functioning correctly so if you change these, be aware of that.
199     /*
200     if(m_useFillColor != other.m_useFillColor)
201         return false;
202     if(m_nId != other.m_nId)
203         return false;
204     if(m_nX  != other.m_nX)
205         return false;
206     if(m_nY != other.m_nY)
207         return false;
208      */
209 }
210 
211 /**
212  * Compute the minimum possible width and height.
213  *
214  * @return QSizeF(mininum_width, minimum_height)
215  */
minimumSize() const216 QSizeF UMLWidget::minimumSize() const
217 {
218     return m_minimumSize;
219 }
220 
221 /**
222  * This method is used to set the minimum size variable for this
223  * widget.
224  *
225  * @param newSize The size being set as minimum.
226  */
setMinimumSize(const QSizeF & newSize)227 void UMLWidget::setMinimumSize(const QSizeF& newSize)
228 {
229     m_minimumSize = newSize;
230 }
231 
232 /**
233  * Compute the maximum possible width and height.
234  *
235  * @return maximum size
236  */
maximumSize()237 QSizeF UMLWidget::maximumSize()
238 {
239     return m_maximumSize;
240 }
241 
242 /**
243  * This method is used to set the maximum size variable for this
244  * widget.
245  *
246  * @param newSize The size being set as maximum.
247  */
setMaximumSize(const QSizeF & newSize)248 void UMLWidget::setMaximumSize(const QSizeF& newSize)
249 {
250     m_maximumSize = newSize;
251 }
252 
253 /**
254  * Event handler for context menu events.
255  */
contextMenuEvent(QGraphicsSceneContextMenuEvent * event)256 void UMLWidget::contextMenuEvent(QGraphicsSceneContextMenuEvent *event)
257 {
258     WidgetBase::contextMenuEvent(event);
259 }
260 
261 /**
262  * Moves the widget to a new position using the difference between the
263  * current position and the new position.
264  * This method doesn't adjust associations. It only moves the widget.
265  *
266  * It can be overridden to constrain movement only in one axis even when
267  * the user isn't constraining the movement with shift or control buttons, for example.
268  * The movement policy set here is applied whenever the widget is moved, being it
269  * moving it explicitly, or as a part of a selection but not receiving directly the
270  * mouse events.
271  *
272  * Default behaviour is move the widget to the new position using the diffs.
273  * @see constrainMovementForAllWidgets
274  *
275  * @param diffX The difference between current X position and new X position.
276  * @param diffY The difference between current Y position and new Y position.
277  */
moveWidgetBy(qreal diffX,qreal diffY)278 void UMLWidget::moveWidgetBy(qreal diffX, qreal diffY)
279 {
280     setX(x() + diffX);
281     setY(y() + diffY);
282 }
283 
284 /**
285  * Modifies the value of the diffX and diffY variables used to move the widgets.
286  *
287  * It can be overridden to constrain movement of all the selected widgets only in one
288  * axis even when the user isn't constraining the movement with shift or control
289  * buttons, for example.
290  * The difference with moveWidgetBy is that the diff positions used here are
291  * applied to all the selected widgets instead of only to m_widget, and that
292  * moveWidgetBy, in fact, moves the widget, and here simply the diff positions
293  * are modified.
294  *
295  * Default behaviour is do nothing.
296  * @see moveWidgetBy
297  *
298  * @param diffX The difference between current X position and new X position.
299  * @param diffY The difference between current Y position and new Y position.
300  */
constrainMovementForAllWidgets(qreal & diffX,qreal & diffY)301 void UMLWidget::constrainMovementForAllWidgets(qreal &diffX, qreal &diffY)
302 {
303     Q_UNUSED(diffX) Q_UNUSED(diffY)
304 }
305 
306 /**
307  * Bring the widget at the pressed position to the foreground.
308  */
toForeground()309 void UMLWidget::toForeground()
310 {
311     QRectF rect = QRectF(scenePos(), QSizeF(width(), height()));
312     QList<QGraphicsItem*> items = scene()->items(rect, Qt::IntersectsItemShape, Qt::DescendingOrder);
313     DEBUG(DBG_SRC) << "items at " << rect << " = " << items.count();
314     if (items.count() > 1) {
315         foreach(QGraphicsItem* i, items) {
316             UMLWidget* w = dynamic_cast<UMLWidget*>(i);
317             if (w) {
318                 DEBUG(DBG_SRC) << "item=" << w->name() << " with zValue=" << w->zValue();
319                 if (w->name() != name()) {
320                     if (w->zValue() >= zValue()) {
321                         setZValue(w->zValue() + 1.0);
322                         DEBUG(DBG_SRC) << "bring to foreground with zValue: " << zValue();
323                     }
324                 }
325             }
326         }
327     }
328     else {
329         setZValue(0.0);
330     }
331     DEBUG(DBG_SRC) << "zValue is " << zValue();
332 }
333 
334 /**
335  * Handles a mouse press event.
336  * It'll select the widget (or mark it to be deselected) and prepare it to
337  * be moved or resized. Go on reading for more info about this.
338  *
339  * Widget values and message bar status are saved.
340  *
341  * If shift or control buttons are pressed, we're in move area no matter
342  * where the button was pressed in the widget. Moreover, if the widget
343  * wasn't already selected, it's added to the selection. If already selected,
344  * it's marked to be deselected when releasing the button (provided it isn't
345  * moved).
346  * Also, if the widget is already selected with other widgets but shift nor
347  * control buttons are pressed, we're in move area. If finally we don't move
348  * the widget, it's selected and the other widgets deselected when releasing
349  * the left button.
350  *
351  * If shift nor control buttons are pressed, we're facing a single selection.
352  * Depending on the position of the cursor, we're in move or in resize area.
353  * If the widget wasn't selected (both when there are no widgets selected, or
354  * when there're other widgets selected but not the one receiving the press
355  * event) it's selected and the others deselected, if any. If already selected,
356  * it's marked to be deselected when releasing the button (provided it wasn't
357  * moved or resized).
358  *
359  * @param event The QGraphicsSceneMouseEvent event.
360  */
mousePressEvent(QGraphicsSceneMouseEvent * event)361 void UMLWidget::mousePressEvent(QGraphicsSceneMouseEvent *event)
362 {
363     if (event->button() != Qt::LeftButton) {
364         event->ignore();
365         return;
366     }
367     DEBUG(DBG_SRC) << "widget = " << name() << " / type = " << baseTypeStr()
368                    << " event->scenePos = " << event->scenePos()
369                    << " pos = " << pos();
370     /*
371     if (! onWidget(event->scenePos())) {
372         DEBUG(DBG_SRC) << name() << " event->scenePos onWidget = false, ignoring event";
373         event->ignore();
374         return;
375     } */
376     event->accept();
377     DEBUG(DBG_SRC) << "widget = " << name() << " / type = " << baseTypeStr();
378 
379     toForeground();
380 
381     m_startMovePostion = pos();
382     m_startResizeSize = QSizeF(width(), height());
383 
384     // saving the values of the widget
385     m_pressOffset = event->scenePos() - pos();
386     DEBUG(DBG_SRC) << "press offset=" << m_pressOffset;
387 
388     m_oldStatusBarMsg = UMLApp::app()->statusBarMsg();
389 
390     if (event->modifiers() == Qt::ShiftModifier || event->modifiers() == Qt::ControlModifier) {
391         m_shiftPressed = true;
392 
393         if (event->button() == Qt::LeftButton) {
394             m_inMoveArea = true;
395         }
396 
397         if (!isSelected()) {
398             selectMultiple(event);
399         }
400         return;
401     }
402 
403     m_shiftPressed = false;
404 
405     int count = m_scene->selectedCount();
406     if (event->button() == Qt::LeftButton) {
407         if (isSelected() && count > 1) {
408             // single selection is made in release event if the widget wasn't moved
409             m_inMoveArea = true;
410             m_oldPos = pos();
411             return;
412         }
413 
414         if (isInResizeArea(event)) {
415             m_inResizeArea = true;
416             m_oldW = width();
417             m_oldH = height();
418         } else {
419             m_inMoveArea = true;
420         }
421     }
422 
423     // if widget wasn't selected, or it was selected but with other widgets also selected
424     if (!isSelected() || count > 1) {
425         selectSingle(event);
426     }
427 }
428 
429 /**
430  * Handles a mouse move event.
431  * It resizes or moves the widget, depending on where the cursor is pressed
432  * on the widget. Go on reading for more info about this.
433  *
434  * If resizing, the widget is resized using UMLWidget::resizeWidget (where specific
435  * widget resize constraint can be applied), and then the associations are
436  * adjusted.
437  * The resizing can be constrained also to a specific axis using control
438  * and shift buttons. If one or another is pressed, it's constrained to X axis.
439  * If both are pressed, it's constrained to Y axis.
440  *
441  * If not resizing, the widget is being moved. If the move is being started,
442  * the selection bounds are set (which includes updating the list of selected
443  * widgets).
444  * The difference between the previous position of the selection and the new
445  * one is calculated (taking in account the selection bounds so widgets don't go
446  * beyond the scene limits). Then, it's constrained to X or Y axis depending
447  * on shift and control buttons.
448  * A further constraint is made using constrainMovementForAllWidgets (for example,
449  * if the widget that receives the event can only be moved in Y axis, with this
450  * method the movement of all the widgets in the selection can be constrained to
451  * be moved only in Y axis).
452  * Then, all the selected widgets are moved using moveWidgetBy (where specific
453  * widget movement constraint can be applied) and, if a certain amount of time
454  * passed from the last move event, the associations are also updated (they're
455  * not updated always to be easy on the CPU). Finally, the scene is resized,
456  * and selection bounds updated.
457  *
458  * @param event The QGraphicsSceneMouseEvent event.
459  */
mouseMoveEvent(QGraphicsSceneMouseEvent * event)460 void UMLWidget::mouseMoveEvent(QGraphicsSceneMouseEvent* event)
461 {
462     if (m_inResizeArea) {
463         resize(event);
464         return;
465     }
466 
467     if (!m_moved) {
468         UMLApp::app()->document()->writeToStatusBar
469           (i18n("Hold shift or ctrl to move in X axis. Hold shift and control to move in Y axis. Right button click to cancel move."));
470 
471         m_moved = true;
472         //Maybe needed by AssociationWidget
473         m_startMove = true;
474 
475         setSelectionBounds();
476     }
477 
478     QPointF position = event->scenePos() - m_pressOffset;
479     qreal diffX = position.x() - x();
480     qreal diffY = position.y() - y();
481 
482     if ((event->modifiers() & Qt::ShiftModifier) && (event->modifiers() & Qt::ControlModifier)) {
483         // move only in Y axis
484         diffX = 0;
485     } else if ((event->modifiers() & Qt::ShiftModifier) || (event->modifiers() & Qt::ControlModifier)) {
486         // move only in X axis
487         diffY = 0;
488     }
489 
490     constrainMovementForAllWidgets(diffX, diffY);
491 
492     // nothing to move
493     if (diffX == 0 && diffY == 0) {
494         return;
495     }
496 
497     QPointF delta = event->scenePos() - event->lastScenePos();
498 
499     DEBUG(DBG_SRC) << "diffX=" << diffX << " / diffY=" << diffY;
500     foreach(UMLWidget* widget, umlScene()->selectedWidgets()) {
501         if ((widget->parentItem() == 0) || (!widget->parentItem()->isSelected())) {
502             widget->moveWidgetBy(diffX, diffY);
503             widget->adjustUnselectedAssocs(delta.x(), delta.y());
504             widget->slotSnapToGrid();
505         }
506     }
507 
508     // Move any selected associations.
509     foreach(AssociationWidget* aw, m_scene->selectedAssocs()) {
510         if (aw->isSelected()) {
511             aw->moveEntireAssoc(diffX, diffY);
512         }
513     }
514 
515     umlScene()->resizeSceneToItems();
516 }
517 
518 /**
519  * Handles a mouse release event.
520  * It selects or deselects the widget and cancels or confirms the move or
521  * resize. Go on reading for more info about this.
522  * No matter which tool is selected, Z position of widget is updated.
523  *
524  * Middle button release resets the selection.
525  * Left button release, if it wasn't moved nor resized, selects the widget
526  * and deselect the others if it wasn't selected and there were other widgets
527  * selected. If the widget was marked to be deselected, deselects it.
528  * If it was moved or resized, the document is set to modified if position
529  * or size changed. Also, if moved, all the associations are adjusted because
530  * the timer could have prevented the adjustment in the last move event before
531  * the release.
532  * If mouse was pressed in resize area, cursor is set again to normal cursor
533  * Right button release if right button was pressed shows the pop up menu for
534  * the widget.
535  * If left button was pressed, it cancels the move or resize with a mouse move
536  * event at the same position than the cursor was when pressed. Another left
537  * button release is also sent.
538  *
539  * @param event The QGraphicsSceneMouseEvent event.
540  */
mouseReleaseEvent(QGraphicsSceneMouseEvent * event)541 void UMLWidget::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
542 {
543     if (!m_moved && !m_resized) {
544         if (!m_shiftPressed && (m_scene->selectedCount() > 1)) {
545             selectSingle(event);
546         } else if (!isSelected()) {
547             deselect(event);
548         }
549     } else {
550         // Commands
551         if (m_moved) {
552             int selectionCount = umlScene()->selectedWidgets().count();
553             if (selectionCount > 1) {
554                 UMLApp::app()->beginMacro(i18n("Move widgets"));
555             }
556             foreach(UMLWidget* widget, umlScene()->selectedWidgets()) {
557                 UMLApp::app()->executeCommand(new Uml::CmdMoveWidget(widget));
558             }
559             if (selectionCount > 1) {
560                 UMLApp::app()->endMacro();
561             }
562             m_moved = false;
563         } else {
564             UMLApp::app()->executeCommand(new Uml::CmdResizeWidget(this));
565             m_autoResize = false;
566             m_resized = false;
567             deselect(event);
568         }
569 
570         if ((m_inMoveArea && wasPositionChanged()) ||
571                 (m_inResizeArea && wasSizeChanged())) {
572             umlDoc()->setModified(true);
573             umlScene()->invalidate();
574         }
575         umlScene()->resizeSceneToItems();
576 
577         UMLApp::app()->document()->writeToStatusBar(m_oldStatusBarMsg);
578     }
579 
580     if (m_inResizeArea) {
581         m_inResizeArea = false;
582         m_scene->activeView()->setCursor(Qt::ArrowCursor);
583     } else {
584         m_inMoveArea = false;
585     }
586     m_startMove = false;
587 }
588 
589 /**
590  * Event handler for mouse double click events.
591  * @param event the QGraphicsSceneMouseEvent event.
592  */
mouseDoubleClickEvent(QGraphicsSceneMouseEvent * event)593 void UMLWidget::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event)
594 {
595     if (event->button() == Qt::LeftButton) {
596         DEBUG(DBG_SRC) << "widget = " << name() << " / type = " << baseTypeStr();
597         showPropertiesDialog();
598         event->accept();
599     }
600 }
601 
602 /**
603  * Return the start position of the move action.
604  * @return   point where the move began
605  */
startMovePosition() const606 QPointF UMLWidget::startMovePosition() const
607 {
608     return m_startMovePostion;
609 }
610 
611 /**
612  * Set the start position of the move action.
613  * @param position point where the move began
614  */
setStartMovePosition(const QPointF & position)615 void UMLWidget::setStartMovePosition(const QPointF &position)
616 {
617     m_startMovePostion = position;
618 }
619 
620 /**
621  * Return the start size of the resize action.
622  * @return   size where the resize began
623  */
startResizeSize() const624 QSizeF UMLWidget::startResizeSize() const
625 {
626     return m_startResizeSize;
627 }
628 
629 /**
630  * Resizes the widget.
631  * It's called from resize, after the values are constrained and before
632  * the associations are adjusted.
633  *
634  * Default behaviour is resize the widget using the new size values.
635  * @see resize
636  *
637  * @param newW   The new width for the widget.
638  * @param newH   The new height for the widget.
639  */
resizeWidget(qreal newW,qreal newH)640 void UMLWidget::resizeWidget(qreal newW, qreal newH)
641 {
642     setSize(newW, newH);
643 }
644 
645 /**
646  * Notify child widget about parent resizes.
647  * Child widgets can override this function to move when their parent is resized.
648  */
notifyParentResize()649 void UMLWidget::notifyParentResize()
650 {
651 }
652 
653 
654 /**
655  * When a widget changes this slot captures that signal.
656  */
updateWidget()657 void UMLWidget::updateWidget()
658 {
659     updateGeometry();
660     switch (baseType()) {
661     case WidgetBase::wt_Class:
662         m_scene->createAutoAttributeAssociations(this);
663         break;
664     case WidgetBase::wt_Entity:
665         m_scene->createAutoConstraintAssociations(this);
666         break;
667     default:
668         break;
669     }
670 
671     if (isVisible())
672         update();
673 }
674 
675 /**
676  * Apply possible constraints to the given candidate width and height.
677  * The default implementation limits input values to the bounds returned
678  * by minimumSize()/maximumSize().
679  *
680  * @param width  input value, may be modified by the constraint
681  * @param height input value, may be modified by the constraint
682  */
constrain(qreal & width,qreal & height)683 void UMLWidget::constrain(qreal& width, qreal& height)
684 {
685     QSizeF minSize = minimumSize();
686     if (width < minSize.width())
687         width = minSize.width();
688     if (height < minSize.height())
689         height = minSize.height();
690     QSizeF maxSize = maximumSize();
691     if (width > maxSize.width())
692         width = maxSize.width();
693     if (height > maxSize.height())
694         height = maxSize.height();
695 
696     if (fixedAspectRatio()) {
697         QSizeF size = rect().size();
698         float aspectRatio = size.width() > 0 ? (float)size.height()/size.width() : 1;
699         height = width * aspectRatio;
700     }
701 }
702 
703 /**
704  * Initializes key attributes of the class.
705  */
init()706 void UMLWidget::init()
707 {
708     m_isInstance = false;
709     setMinimumSize(DefaultMinimumSize);
710     setMaximumSize(DefaultMaximumSize);
711 
712     m_font = QApplication::font();
713     for (int i = (int)FT_INVALID - 1; i >= 0; --i) {
714         FontType fontType = (FontType)i;
715         setupFontType(m_font, fontType);
716         m_pFontMetrics[fontType] = new QFontMetrics(m_font);
717     }
718 
719     if (m_scene) {
720         m_useFillColor = true;
721         m_usesDiagramFillColor = true;
722         m_usesDiagramUseFillColor = true;
723         const Settings::OptionState& optionState = m_scene->optionState();
724         m_fillColor = optionState.uiState.fillColor;
725         m_showStereotype = optionState.classState.showStereoType;
726     } else {
727         uError() << "SERIOUS PROBLEM - m_scene is NULL";
728         m_useFillColor = false;
729         m_usesDiagramFillColor = false;
730         m_usesDiagramUseFillColor = false;
731         m_showStereotype = Uml::ShowStereoType::None;
732     }
733 
734     m_resizable = true;
735     m_fixedAspectRatio = false;
736 
737     m_startMove = false;
738     m_activated = false;
739     m_ignoreSnapToGrid = false;
740     m_ignoreSnapComponentSizeToGrid = false;
741     m_doc = UMLApp::app()->document();
742     m_nPosX = 0;
743     connect(m_scene, SIGNAL(sigFillColorChanged(Uml::ID::Type)), this, SLOT(slotFillColorChanged(Uml::ID::Type)));
744     connect(m_scene, SIGNAL(sigLineColorChanged(Uml::ID::Type)), this, SLOT(slotLineColorChanged(Uml::ID::Type)));
745     connect(m_scene, SIGNAL(sigTextColorChanged(Uml::ID::Type)), this, SLOT(slotTextColorChanged(Uml::ID::Type)));
746     connect(m_scene, SIGNAL(sigLineWidthChanged(Uml::ID::Type)), this, SLOT(slotLineWidthChanged(Uml::ID::Type)));
747 
748     m_umlObject = 0;
749 
750     m_oldPos = QPointF();
751     m_pressOffset = QPointF();
752     m_oldW = 0;
753     m_oldH = 0;
754 
755     m_shiftPressed = false;
756     m_inMoveArea = false;
757     m_inResizeArea = false;
758     m_moved = false;
759     m_resized = false;
760 
761     // propagate line color set by base class constructor
762     // which does not call the virtual methods from this class.
763     setLineColor(lineColor());
764 
765     setZValue(2.0);  // default for most widgets
766 }
767 
768 /**
769  * This is usually called synchronously after menu.exec() and \a
770  * trigger's parent is always the ListPopupMenu which can be used to
771  * get the type of action of \a trigger.
772  *
773  * @note Subclasses can reimplement to handle specific actions and
774  *       leave the rest to WidgetBase::slotMenuSelection.
775  */
slotMenuSelection(QAction * trigger)776 void UMLWidget::slotMenuSelection(QAction *trigger)
777 {
778     if (!trigger) {
779         return;
780     }
781 
782     ListPopupMenu::MenuType sel = ListPopupMenu::typeFromAction(trigger);
783     switch (sel) {
784     case ListPopupMenu::mt_Resize:
785         umlScene()->resizeSelection();
786         break;
787 
788     case ListPopupMenu::mt_AutoResize:
789         setAutoResize(trigger->isChecked());
790         updateGeometry();
791         break;
792 
793     case ListPopupMenu::mt_Rename_Object: {
794         QString name = m_instanceName;
795         bool ok = Dialog_Utils::askName(i18n("Rename Object"),
796                                         i18n("Enter object name:"),
797                                         name);
798         if (ok) {
799             m_instanceName = name;
800             updateGeometry();
801             moveEvent(0);
802             update();
803             UMLApp::app()->document()->setModified(true);
804         }
805         break;
806     }
807 
808     case ListPopupMenu::mt_FloatText: {
809         FloatingTextWidget* ft = new FloatingTextWidget(umlScene());
810         ft->showChangeTextDialog();
811         //if no text entered delete
812         if (!FloatingTextWidget::isTextValid(ft->text())) {
813             delete ft;
814         } else {
815             ft->setID(UniqueID::gen());
816             addWidget(ft, false);
817         }
818         break;
819     }
820 
821     case ListPopupMenu::mt_Actor: {
822         UMLActor *actor = new UMLActor;
823         UMLWidget *widget = new ActorWidget(umlScene(), actor);
824         addConnectedWidget(widget, Uml::AssociationType::Association);
825         break;
826     }
827 
828     case ListPopupMenu::mt_Artifact: {
829         UMLArtifact *a = new UMLArtifact();
830         ArtifactWidget *widget = new ArtifactWidget(umlScene(), a);
831         addConnectedWidget(widget, Uml::AssociationType::Association);
832         break;
833     }
834 
835     case ListPopupMenu::mt_Component: {
836         UMLComponent *c = new UMLComponent();
837         ComponentWidget *widget = new ComponentWidget(umlScene(), c);
838         addConnectedWidget(widget, Uml::AssociationType::Association, SetupSize);
839         break;
840     }
841 
842     case ListPopupMenu::mt_Hide_Destruction_Box: {
843         ObjectWidget *w = asObjectWidget();
844         if (w)
845             w->setShowDestruction(false);
846         break;
847     }
848 
849     case ListPopupMenu::mt_Show_Destruction_Box: {
850         ObjectWidget *w = asObjectWidget();
851         if (w)
852             w->setShowDestruction(true);
853         break;
854     }
855 
856     case ListPopupMenu::mt_Interface: {
857         UMLPackage* component = umlObject()->asUMLPackage();
858         QString name = Model_Utils::uniqObjectName(UMLObject::ot_Interface, component);
859         if (Dialog_Utils::askNewName(WidgetBase::wt_Interface, name)) {
860             UMLClassifier *c = new UMLClassifier();
861             c->setBaseType(UMLObject::ot_Interface);
862             ClassifierWidget *widget = new ClassifierWidget(umlScene(), c);
863             addConnectedWidget(widget, Uml::AssociationType::Association);
864         }
865         break;
866     }
867 
868     case ListPopupMenu::mt_InterfaceComponent:
869     case ListPopupMenu::mt_InterfaceProvided: {
870         UMLObject *o = Object_Factory::createUMLObject(UMLObject::ot_Interface);
871         InterfaceWidget *w = new InterfaceWidget(umlScene(), o->asUMLClassifier());
872         w->setDrawAsCircle(true);
873         addConnectedWidget(w, Uml::AssociationType::Association, SetupSize);
874         break;
875     }
876 
877     case ListPopupMenu::mt_InterfaceRequired: {
878         UMLObject *o = Object_Factory::createUMLObject(UMLObject::ot_Interface);
879         InterfaceWidget *w = new InterfaceWidget(umlScene(), o->asUMLClassifier());
880         w->setDrawAsCircle(true);
881         addConnectedWidget(w, Uml::AssociationType::Association, SetupSize | SwitchDirection);
882         break;
883     }
884 
885     case ListPopupMenu::mt_Note: {
886         NoteWidget *widget = new NoteWidget(umlScene());
887         addConnectedWidget(widget, Uml::AssociationType::Anchor);
888         break;
889     }
890 
891     case ListPopupMenu::mt_Port: {
892         // TODO: merge with ToolbarStateOneWidget::setWidget()
893         UMLPackage* component = umlObject()->asUMLPackage();
894         QString name = Model_Utils::uniqObjectName(UMLObject::ot_Port, component);
895         if (Dialog_Utils::askNewName(WidgetBase::wt_Port, name)) {
896             UMLPort *port = Object_Factory::createUMLObject(UMLObject::ot_Port, name, component)->asUMLPort();
897             UMLWidget *umlWidget = Widget_Factory::createWidget(umlScene(), port);
898             umlWidget->setParentItem(this);
899             QPointF p = mapFromScene(umlScene()->pos());
900             umlWidget->setPos(p);
901             umlScene()->setupNewWidget(umlWidget, false);
902 
903         }
904         break;
905     }
906 
907     case ListPopupMenu::mt_UseCase: {
908         UMLUseCase *useCase = new UMLUseCase;
909         UMLWidget *widget = new UseCaseWidget(umlScene(), useCase);
910         addConnectedWidget(widget, Uml::AssociationType::Association);
911         break;
912     }
913 
914     case ListPopupMenu::mt_MessageCreation:
915     case ListPopupMenu::mt_MessageDestroy:
916     case ListPopupMenu::mt_MessageSynchronous:
917 //        MessageWidget *widget = new MessageWidget(umlScene(), this);
918 //        addConnectedWidget(widget, Uml::AssociationType::Coll_Mesg_Sync);
919     case ListPopupMenu::mt_MessageAsynchronous:
920     case ListPopupMenu::mt_MessageFound:
921     case ListPopupMenu::mt_MessageLost:
922         break;
923 
924     // activity diagrams
925     case ListPopupMenu::mt_Accept_Signal:
926         addConnectedWidget(new SignalWidget(umlScene(), SignalWidget::Accept), Uml::AssociationType::Activity, NoOption);
927         break;
928     case ListPopupMenu::mt_Accept_Time_Event:
929         addConnectedWidget(new SignalWidget(umlScene(), SignalWidget::Time), Uml::AssociationType::Activity, NoOption);
930         break;
931     case ListPopupMenu::mt_Activity:
932         addConnectedWidget(new ActivityWidget(umlScene(), ActivityWidget::Normal), Uml::AssociationType::Activity, NoOption);
933         break;
934     case ListPopupMenu::mt_Activity_Transition:
935         addConnectedWidget(new ActivityWidget(umlScene(), ActivityWidget::Final), Uml::AssociationType::Activity, NoOption);
936         break;
937     case ListPopupMenu::mt_Branch:
938         addConnectedWidget(new ActivityWidget(umlScene(), ActivityWidget::Branch), Uml::AssociationType::Activity, NoOption);
939         break;
940     case ListPopupMenu::mt_Exception:
941         umlScene()->triggerToolbarButton(WorkToolBar::tbb_Exception);
942         break;
943     case ListPopupMenu::mt_Final_Activity:
944         addConnectedWidget(new ActivityWidget(umlScene(), ActivityWidget::Final), Uml::AssociationType::Activity, NoOption);
945         break;
946     case ListPopupMenu::mt_Fork:
947         addConnectedWidget(new ForkJoinWidget(umlScene()), Uml::AssociationType::Activity, NoOption);
948         break;
949     case ListPopupMenu::mt_End_Activity:
950         addConnectedWidget(new ActivityWidget(umlScene(), ActivityWidget::End), Uml::AssociationType::Activity, NoOption);
951         break;
952     case ListPopupMenu::mt_Initial_Activity:
953         addConnectedWidget(new ActivityWidget(umlScene(), ActivityWidget::Initial), Uml::AssociationType::Activity, NoOption);
954         break;
955     case ListPopupMenu::mt_Invoke_Activity:
956         addConnectedWidget(new ActivityWidget(umlScene(), ActivityWidget::Invok), Uml::AssociationType::Activity, NoOption);
957         break;
958     case ListPopupMenu::mt_Object_Node:
959         addConnectedWidget(new ObjectNodeWidget(umlScene(), ObjectNodeWidget::Data), Uml::AssociationType::Activity, NoOption);
960         break;
961     case ListPopupMenu::mt_Pin:
962         umlScene()->triggerToolbarButton(WorkToolBar::tbb_Pin);
963         break;
964     case ListPopupMenu::mt_Param_Activity:
965         addConnectedWidget(new ActivityWidget(umlScene(), ActivityWidget::Param), Uml::AssociationType::Activity, NoOption);
966         break;
967     case ListPopupMenu::mt_PrePostCondition:
968         addConnectedWidget(new NoteWidget(umlScene(), NoteWidget::Normal), Uml::AssociationType::Activity, NoOption);
969         break;
970     case ListPopupMenu::mt_Region:
971         addConnectedWidget(new RegionWidget(umlScene()), Uml::AssociationType::Activity, NoOption);
972         break;
973     case ListPopupMenu::mt_Send_Signal:
974         addConnectedWidget(new SignalWidget(umlScene(), SignalWidget::Send), Uml::AssociationType::Activity, NoOption);
975         break;
976 
977   // state diagrams
978     case ListPopupMenu::mt_Choice:
979         addConnectedWidget(new StateWidget(umlScene(), StateWidget::Choice), Uml::AssociationType::State, NoOption);
980         break;
981     case ListPopupMenu::mt_DeepHistory:
982         addConnectedWidget(new StateWidget(umlScene(), StateWidget::DeepHistory), Uml::AssociationType::State, NoOption);
983         break;
984     case ListPopupMenu::mt_End_State:
985         addConnectedWidget(new StateWidget(umlScene(), StateWidget::End), Uml::AssociationType::State, NoOption);
986         break;
987     case ListPopupMenu::mt_Junction:
988         addConnectedWidget(new StateWidget(umlScene(), StateWidget::Junction), Uml::AssociationType::State, NoOption);
989         break;
990     case ListPopupMenu::mt_ShallowHistory:
991         addConnectedWidget(new StateWidget(umlScene(), StateWidget::ShallowHistory), Uml::AssociationType::State, NoOption);
992         break;
993     case ListPopupMenu::mt_State:
994         addConnectedWidget(new StateWidget(umlScene(), StateWidget::Normal), Uml::AssociationType::State, ShowProperties);
995         break;
996     case ListPopupMenu::mt_StateFork:
997         addConnectedWidget(new StateWidget(umlScene(), StateWidget::Fork), Uml::AssociationType::State, NoOption);
998         break;
999     case ListPopupMenu::mt_StateJoin:
1000         addConnectedWidget(new StateWidget(umlScene(), StateWidget::Join), Uml::AssociationType::State, NoOption);
1001         break;
1002     case ListPopupMenu::mt_StateTransition:
1003         umlScene()->triggerToolbarButton(WorkToolBar::tbb_State_Transition);
1004         break;
1005     default:
1006         WidgetBase::slotMenuSelection(trigger);
1007         break;
1008     }
1009 }
1010 
1011 /**
1012  * Captures when another widget moves if this widget is linked to it.
1013  * @see sigWidgetMoved
1014  *
1015  * @param id The id of object behind the widget.
1016  */
slotWidgetMoved(Uml::ID::Type)1017 void UMLWidget::slotWidgetMoved(Uml::ID::Type /*id*/)
1018 {
1019 }
1020 
1021 /**
1022  * Captures a color change signal.
1023  *
1024  * @param viewID  The id of the UMLScene behind the widget.
1025  */
slotFillColorChanged(Uml::ID::Type viewID)1026 void UMLWidget::slotFillColorChanged(Uml::ID::Type viewID)
1027 {
1028     //only change if on the diagram concerned
1029     if (m_scene->ID() != viewID) {
1030         return;
1031     }
1032     if (m_usesDiagramFillColor) {
1033         WidgetBase::setFillColor(m_scene->fillColor());
1034     }
1035     if (m_usesDiagramUseFillColor) {
1036         WidgetBase::setUseFillColor(m_scene->useFillColor());
1037     }
1038     update();
1039 }
1040 
1041 /**
1042  * Captures a text color change signal.
1043  *
1044  * @param viewID  The id of the UMLScene behind the widget.
1045  */
slotTextColorChanged(Uml::ID::Type viewID)1046 void UMLWidget::slotTextColorChanged(Uml::ID::Type viewID)
1047 {
1048     //only change if on the diagram concerned
1049     if (m_scene->ID() != viewID)
1050         return;
1051     WidgetBase::setTextColor(m_scene->textColor());
1052     update();
1053 }
1054 
1055 
1056 /**
1057  * Captures a line color change signal.
1058  *
1059  * @param viewID  The id of the UMLScene behind the widget.
1060  */
slotLineColorChanged(Uml::ID::Type viewID)1061 void UMLWidget::slotLineColorChanged(Uml::ID::Type viewID)
1062 {
1063     //only change if on the diagram concerned
1064     if (m_scene->ID() != viewID)
1065         return;
1066 
1067     if (m_usesDiagramLineColor) {
1068         WidgetBase::setLineColor(m_scene->lineColor());
1069     }
1070     update();
1071 }
1072 
1073 /**
1074  * Captures a linewidth change signal.
1075  *
1076  * @param viewID  The id of the UMLScene behind the widget.
1077  */
slotLineWidthChanged(Uml::ID::Type viewID)1078 void UMLWidget::slotLineWidthChanged(Uml::ID::Type viewID)
1079 {
1080     //only change if on the diagram concerned
1081     if (m_scene->ID() != viewID) {
1082         return;
1083     }
1084     if (m_usesDiagramLineWidth) {
1085         WidgetBase::setLineWidth(m_scene->lineWidth());
1086     }
1087     update();
1088 }
1089 
1090 /**
1091  * Set the status of using fill color (undo action)
1092  *
1093  * @param fc the status of using fill color.
1094  */
setUseFillColor(bool fc)1095 void UMLWidget::setUseFillColor(bool fc)
1096 {
1097     if (useFillColor() != fc) {
1098         UMLApp::app()->executeCommand(new CmdChangeUseFillColor(this, fc));
1099     }
1100 }
1101 
1102 /**
1103  * Set the status of using fill color.
1104  *
1105  * @param fc the status of using fill color.
1106  */
setUseFillColorCmd(bool fc)1107 void UMLWidget::setUseFillColorCmd(bool fc)
1108 {
1109     WidgetBase::setUseFillColor(fc);
1110     update();
1111 }
1112 
1113 /**
1114  * Overrides the method from WidgetBase.
1115  */
setTextColorCmd(const QColor & color)1116 void UMLWidget::setTextColorCmd(const QColor &color)
1117 {
1118     WidgetBase::setTextColor(color);
1119     update();
1120 }
1121 
1122 /**
1123  * Overrides the method from WidgetBase.
1124  */
setTextColor(const QColor & color)1125 void UMLWidget::setTextColor(const QColor &color)
1126 {
1127     if (textColor() != color) {
1128         UMLApp::app()->executeCommand(new CmdChangeTextColor(this, color));
1129         update();
1130     }
1131 }
1132 
1133 /**
1134  * Overrides the method from WidgetBase.
1135  */
setLineColorCmd(const QColor & color)1136 void UMLWidget::setLineColorCmd(const QColor &color)
1137 {
1138     WidgetBase::setLineColor(color);
1139     update();
1140 }
1141 
1142 /**
1143  * Overrides the method from WidgetBase.
1144  */
setLineColor(const QColor & color)1145 void UMLWidget::setLineColor(const QColor &color)
1146 {
1147     if (lineColor() != color) {
1148         UMLApp::app()->executeCommand(new CmdChangeLineColor(this, color));
1149     }
1150 }
1151 
1152 /**
1153  * Overrides the method from WidgetBase, execute CmdChangeLineWidth
1154  */
setLineWidth(uint width)1155 void UMLWidget::setLineWidth(uint width)
1156 {
1157     if (lineWidth() != width) {
1158         UMLApp::app()->executeCommand(new CmdChangeLineWidth(this, width));
1159     }
1160 }
1161 
1162 /**
1163  * Overrides the method from WidgetBase.
1164  */
setLineWidthCmd(uint width)1165 void UMLWidget::setLineWidthCmd(uint width)
1166 {
1167     WidgetBase::setLineWidth(width);
1168     update();
1169 }
1170 
1171 /**
1172  * Sets the background fill color
1173  *
1174  * @param color the new fill color
1175  */
setFillColor(const QColor & color)1176 void UMLWidget::setFillColor(const QColor &color)
1177 {
1178     if (fillColor() != color) {
1179         UMLApp::app()->executeCommand(new CmdChangeFillColor(this, color));
1180     }
1181 }
1182 
1183 /**
1184  * Sets the background fill color
1185  *
1186  * @param color the new fill color
1187  */
setFillColorCmd(const QColor & color)1188 void UMLWidget::setFillColorCmd(const QColor &color)
1189 {
1190     WidgetBase::setFillColor(color);
1191     update();
1192 }
1193 
1194 /**
1195  * Reimplemented from class WidgetBase
1196  *
1197  * @param ChangeLog
1198  * @return  true for success
1199  */
activate(IDChangeLog * changeLog)1200 bool UMLWidget::activate(IDChangeLog* changeLog)
1201 {
1202     if (!WidgetBase::activate(changeLog))
1203         return false;
1204     DiagramProxyWidget::activate(changeLog);
1205 
1206     setFontCmd(m_font);
1207     setSize(width(), height());
1208     m_activated = true;
1209     updateGeometry();
1210     if (m_scene->getPaste()) {
1211         FloatingTextWidget * ft = 0;
1212         QPointF point = m_scene->getPastePoint();
1213         int x = point.x() + this->x();
1214         int y = point.y() + this->y();
1215         if (m_scene->isSequenceDiagram()) {
1216             switch (baseType()) {
1217             case WidgetBase::wt_Object:
1218             case WidgetBase::wt_Precondition :
1219                 setY(this->y());
1220                 setX(x);
1221                 break;
1222 
1223             case WidgetBase::wt_Message:
1224                 setY(this->y());
1225                 setX(x);
1226                 break;
1227 
1228             case WidgetBase::wt_Text:
1229                 ft = static_cast<FloatingTextWidget *>(this);
1230                 if (ft->textRole() == Uml::TextRole::Seq_Message) {
1231                     setX(x);
1232                     setY(this->y());
1233                 } else {
1234                     setX(this->x());
1235                     setY(this->y());
1236                 }
1237                 break;
1238 
1239             default:
1240                 setY(y);
1241                 break;
1242             }//end switch base type
1243         }//end if sequence
1244         else {
1245             setX(x);
1246             setY(y);
1247         }
1248     }//end if pastepoint
1249     else {
1250         setX(this->x());
1251         setY(this->y());
1252     }
1253     if (m_scene->getPaste())
1254         m_scene->createAutoAssociations(this);
1255     updateGeometry();
1256     return true;
1257 }
1258 
1259 /**
1260  * Returns true if the Activate method has been called for this instance
1261  *
1262  * @return The activate status.
1263  */
isActivated() const1264 bool UMLWidget::isActivated() const
1265 {
1266     return m_activated;
1267 }
1268 
1269 /**
1270  * Set the m_activated flag of a widget but does not perform the Activate method
1271  *
1272  * @param active  Status of activation is to be set.
1273  */
setActivated(bool active)1274 void UMLWidget::setActivated(bool active /*=true*/)
1275 {
1276     m_activated = active;
1277 }
1278 
1279 /**
1280  * Reimplemented from class WidgetBase
1281  */
addAssoc(AssociationWidget * pAssoc)1282 void UMLWidget::addAssoc(AssociationWidget* pAssoc)
1283 {
1284     if (pAssoc && !associationWidgetList().contains(pAssoc)) {
1285         associationWidgetList().append(pAssoc);
1286     }
1287 }
1288 
1289 /**
1290  *  Returns the list of associations connected to this widget.
1291  */
associationWidgetList() const1292 AssociationWidgetList &UMLWidget::associationWidgetList() const
1293 {
1294     m_Assocs.removeAll(0);
1295     return m_Assocs;
1296 }
1297 
1298 /**
1299  * Reimplemented from class WidgetBase
1300  */
removeAssoc(AssociationWidget * pAssoc)1301 void UMLWidget::removeAssoc(AssociationWidget* pAssoc)
1302 {
1303     if (pAssoc) {
1304         associationWidgetList().removeAll(pAssoc);
1305     }
1306 
1307     if (changesShape()) {
1308         updateGeometry();
1309     }
1310 }
1311 
1312 /**
1313  * Adjusts associations with the given co-ordinates
1314  *
1315  * @param dx  The amount by which the widget moved in X direction.
1316  * @param dy  The amount by which the widget moved in Y direction.
1317  */
adjustAssocs(qreal dx,qreal dy)1318 void UMLWidget::adjustAssocs(qreal dx, qreal dy)
1319 {
1320     qDebug() << this;
1321     // don't adjust Assocs on file load, as
1322     // the original positions, which are stored in XMI
1323     // should be reproduced exactly
1324     // (don't try to reposition assocs as long
1325     //   as file is only partly loaded -> reposition
1326     //   could be misguided)
1327     /// @todo avoid trigger of this event during load
1328     if (m_doc->loading()) {
1329         // don't recalculate the assocs during load of XMI
1330         // -> return immediately without action
1331         return;
1332     }
1333 
1334     foreach(AssociationWidget* assocwidget, associationWidgetList()) {
1335         assocwidget->saveIdealTextPositions();
1336     }
1337 
1338     foreach(AssociationWidget* assocwidget, associationWidgetList()) {
1339         assocwidget->widgetMoved(this, dx, dy);
1340     }
1341 }
1342 
1343 /**
1344  * Adjusts all unselected associations with the given co-ordinates
1345  *
1346  * @param dx  The amount by which the widget moved in X direction.
1347  * @param dy  The amount by which the widget moved in Y direction.
1348  */
adjustUnselectedAssocs(qreal dx,qreal dy)1349 void UMLWidget::adjustUnselectedAssocs(qreal dx, qreal dy)
1350 {
1351     foreach(AssociationWidget* assocwidget, associationWidgetList()) {
1352         if (!assocwidget->isSelected())
1353             assocwidget->saveIdealTextPositions();
1354     }
1355 
1356     foreach(AssociationWidget* assocwidget, associationWidgetList()) {
1357         if (!assocwidget->isSelected()) {
1358             assocwidget->widgetMoved(this, dx, dy);
1359         }
1360     }
1361 }
1362 
1363 /**
1364  * Show a properties dialog for a UMLWidget.
1365  */
showPropertiesDialog()1366 bool UMLWidget::showPropertiesDialog()
1367 {
1368     bool result = false;
1369     // will already be selected so make sure docWindow updates the doc
1370     // back it the widget
1371     UMLApp::app()->docWindow()->updateDocumentation(false);
1372     QPointer<ClassPropertiesDialog> dlg = new ClassPropertiesDialog((QWidget*)UMLApp::app(), this);
1373 
1374     if (dlg->exec()) {
1375         UMLApp::app()->docWindow()->showDocumentation(umlObject(), true);
1376         m_doc->setModified(true);
1377         result = true;
1378     }
1379     dlg->close(); //wipe from memory
1380     delete dlg;
1381     return result;
1382 }
1383 
1384 /**
1385  * Move the widget by an X and Y offset relative to
1386  * the current position.
1387  */
moveByLocal(qreal dx,qreal dy)1388 void UMLWidget::moveByLocal(qreal dx, qreal dy)
1389 {
1390     qreal newX = x() + dx;
1391     qreal newY = y() + dy;
1392     setX(newX);
1393     setY(newY);
1394     adjustAssocs(dx, dy);
1395 }
1396 
1397 /**
1398  * Set the pen.
1399  */
setPenFromSettings(QPainter & p)1400 void UMLWidget::setPenFromSettings(QPainter & p)
1401 {
1402     p.setPen(QPen(m_lineColor, m_lineWidth));
1403 }
1404 
1405 /**
1406  * Set the pen.
1407  */
setPenFromSettings(QPainter * p)1408 void UMLWidget::setPenFromSettings(QPainter *p)
1409 {
1410     p->setPen(QPen(m_lineColor, m_lineWidth));
1411 }
1412 
1413 /**
1414  * Returns the cursor to be shown when resizing the widget.
1415  * Default cursor is KCursor::sizeFDiagCursor().
1416  *
1417  * @return The cursor to be shown when resizing the widget.
1418  */
resizeCursor() const1419 QCursor UMLWidget::resizeCursor() const
1420 {
1421     return Qt::SizeFDiagCursor;
1422 }
1423 
1424 /**
1425  * Checks if the mouse is in resize area (right bottom corner), and sets
1426  * the cursor depending on that.
1427  * The cursor used when resizing is gotten from resizeCursor().
1428  *
1429  * @param me The QMouseEVent to check.
1430  * @return true if the mouse is in resize area, false otherwise.
1431  */
isInResizeArea(QGraphicsSceneMouseEvent * me)1432 bool UMLWidget::isInResizeArea(QGraphicsSceneMouseEvent *me)
1433 {
1434     qreal m = 10.0;
1435     const qreal w = width();
1436     const qreal h = height();
1437 
1438     // If the widget itself is very small then make the resize area small, too.
1439     // Reason: Else it becomes impossible to do a move instead of resize.
1440     if (w - m < m || h - m < m) {
1441         m = 2.0;
1442     }
1443 
1444     if (m_resizable &&
1445             me->scenePos().x() >= (x() + w - m) &&
1446             me->scenePos().y() >= (y() + h - m)) {
1447         m_scene->activeView()->setCursor(resizeCursor());
1448         return true;
1449     } else {
1450         m_scene->activeView()->setCursor(Qt::ArrowCursor);
1451         return false;
1452     }
1453 }
1454 
1455 /**
1456  * calculate content related size of widget.
1457  *
1458  * @return calculated widget size
1459  */
calculateSize(bool withExtensions) const1460 QSizeF UMLWidget::calculateSize(bool withExtensions /* = true */) const
1461 {
1462     Q_UNUSED(withExtensions)
1463     const QFontMetrics &fm = getFontMetrics(UMLWidget::FT_NORMAL);
1464     const int fontHeight = fm.lineSpacing();
1465     if (m_umlObject) {
1466         qreal width = 0, height = defaultMargin;
1467         if (!m_umlObject->stereotype().isEmpty()) {
1468             height += fontHeight;
1469             const QFontMetrics &bfm = UMLWidget::getFontMetrics(UMLWidget::FT_BOLD);
1470             const int stereoWidth = bfm.size(0, m_umlObject->stereotype(true)).width();
1471             if (stereoWidth > width)
1472                 width = stereoWidth;
1473         }
1474         height += fontHeight;
1475         const QFontMetrics &bfm = UMLWidget::getFontMetrics(UMLWidget::FT_BOLD);
1476         const int nameWidth = bfm.size(0, m_umlObject->name()).width();
1477         if (nameWidth > width)
1478             width = nameWidth;
1479         return QSizeF(width + 2*defaultMargin, height);
1480     }
1481     else
1482         return QSizeF(width(), height());
1483 }
1484 
1485 /**
1486  * Resize widget to minimum size.
1487  */
resize()1488 void UMLWidget::resize()
1489 {
1490     qreal oldW = width();
1491     qreal oldH = height();
1492     // @TODO minimumSize() do not work in all cases, we need a dedicated autoResize() method
1493     QSizeF size = minimumSize();
1494     setSize(size.width(), size.height());
1495     DEBUG(DBG_SRC) << "size=" << size;
1496     adjustAssocs(size.width()-oldW, size.height()-oldH);
1497 }
1498 
1499 /**
1500  * Resizes the widget and adjusts the associations.
1501  * It's called when a mouse move event happens and the cursor was
1502  * in resize area when pressed.
1503  * Resizing can be constrained to an specific axis using control and shift buttons.
1504  *
1505  * @param me The QGraphicsSceneMouseEvent to get the values from.
1506  */
resize(QGraphicsSceneMouseEvent * me)1507 void UMLWidget::resize(QGraphicsSceneMouseEvent *me)
1508 {
1509     QString msgX = i18n("Hold shift or control to move in X axis.");
1510     QString msgY = i18n("Hold shift and control to move in Y axis.");
1511     QString msg;
1512     if (isMessageWidget())
1513         msg = msgY;
1514     else if (isObjectWidget())
1515         msg = msgX;
1516     else
1517         msg = QString(QLatin1String("%1 %2")).arg(msgX, msgY);
1518     UMLApp::app()->document()->writeToStatusBar(msg);
1519 
1520     m_resized = true;
1521 
1522     qreal newW = m_oldW + me->scenePos().x() - x() - m_pressOffset.x();
1523     qreal newH = m_oldH + me->scenePos().y() - y() - m_pressOffset.y();
1524 
1525     if ((me->modifiers() & Qt::ShiftModifier) && (me->modifiers() & Qt::ControlModifier)) {
1526         //Move in Y axis
1527         newW = m_oldW;
1528     } else if ((me->modifiers() & Qt::ShiftModifier) || (me->modifiers() & Qt::ControlModifier)) {
1529         //Move in X axis
1530         newH = m_oldH;
1531     }
1532 
1533     constrain(newW, newH);
1534     resizeWidget(newW, newH);
1535     DEBUG(DBG_SRC) << "event=" << me->scenePos() << "/ pos=" << pos() << " / newW=" << newW << " / newH=" << newH;
1536     QPointF delta = me->scenePos() - me->lastScenePos();
1537     adjustAssocs(delta.x(), delta.y());
1538 
1539     m_scene->resizeSceneToItems();
1540 }
1541 
1542 /**
1543  * Checks if the size of the widget changed respect to the size that
1544  * it had when press event was fired.
1545  *
1546  * @return true if was resized, false otherwise.
1547  */
wasSizeChanged()1548 bool UMLWidget::wasSizeChanged()
1549 {
1550     return m_oldW != width() || m_oldH != height();
1551 }
1552 
1553 /**
1554  * Checks if the position of the widget changed respect to the position that
1555  * it had when press event was fired.
1556  *
1557  * @return true if was moved, false otherwise.
1558  */
wasPositionChanged()1559 bool UMLWidget::wasPositionChanged()
1560 {
1561     return m_oldPos != pos();
1562 }
1563 
1564 /**
1565  * Fills m_selectedWidgetsList and sets the selection bounds ((m_min/m_max)X/Y attributes).
1566  */
setSelectionBounds()1567 void UMLWidget::setSelectionBounds()
1568 {
1569 }
1570 
setSelectedFlag(bool _select)1571 void UMLWidget::setSelectedFlag(bool _select)
1572 {
1573     WidgetBase::setSelected(_select);
1574 }
1575 
1576 /**
1577  * Sets the state of whether the widget is selected.
1578  *
1579  * @param _select The state of whether the widget is selected.
1580  */
setSelected(bool _select)1581 void UMLWidget::setSelected(bool _select)
1582 {
1583     const WidgetBase::WidgetType wt = baseType();
1584     if (_select) {
1585         if (m_scene->selectedCount() == 0) {
1586             if (widgetHasUMLObject(wt)) {
1587                 UMLApp::app()->docWindow()->showDocumentation(m_umlObject, false);
1588             } else {
1589                 UMLApp::app()->docWindow()->showDocumentation(this, false);
1590             }
1591         }//end if
1592         /* if (wt != wt_Text && wt != wt_Box) {
1593             setZ(9);//keep text on top and boxes behind so don't touch Z value
1594         } */
1595     } else {
1596         /* if (wt != wt_Text && wt != wt_Box) {
1597             setZ(m_origZ);
1598         } */
1599         if (isSelected())
1600             UMLApp::app()->docWindow()->updateDocumentation(true);
1601     }
1602 
1603     WidgetBase::setSelected(_select);
1604 
1605     update();
1606 
1607     // selection changed, we have to make sure the copy and paste items
1608     // are correctly enabled/disabled
1609     UMLApp::app()->slotCopyChanged();
1610 
1611     // select in tree view as done for diagrams
1612     if (_select) {
1613         UMLListViewItem * item = UMLApp::app()->listView()->findItem(id());
1614         if (item)
1615             UMLApp::app()->listView()->setCurrentItem(item);
1616         else
1617             UMLApp::app()->listView()->clearSelection();
1618     }
1619 }
1620 
1621 /**
1622  * Selects the widget and clears the other selected widgets, if any.
1623  *
1624  * @param me The QGraphicsSceneMouseEvent which made the selection.
1625  */
selectSingle(QGraphicsSceneMouseEvent * me)1626 void UMLWidget::selectSingle(QGraphicsSceneMouseEvent *me)
1627 {
1628     m_scene->clearSelected();
1629 
1630     // Adds the widget to the selected widgets list, but as it has been cleared
1631     // only the current widget is selected.
1632     selectMultiple(me);
1633 }
1634 
1635 /**
1636  * Selects the widget and adds it to the list of selected widgets.
1637  *
1638  * @param me The QGraphicsSceneMouseEvent which made the selection.
1639  */
selectMultiple(QGraphicsSceneMouseEvent * me)1640 void UMLWidget::selectMultiple(QGraphicsSceneMouseEvent *me)
1641 {
1642     Q_UNUSED(me);
1643 
1644     setSelected(true);
1645 }
1646 
1647 /**
1648  * Deselects the widget and removes it from the list of selected widgets.
1649  *
1650  * @param me The QGraphicsSceneMouseEvent which made the selection.
1651  */
deselect(QGraphicsSceneMouseEvent * me)1652 void UMLWidget::deselect(QGraphicsSceneMouseEvent *me)
1653 {
1654     Q_UNUSED(me);
1655 
1656     setSelected(false);
1657 }
1658 
1659 /**
1660  * Clears the selection, resets the toolbar and deselects the widget.
1661  */
1662 //void UMLWidget::resetSelection()
1663 //{
1664 //    m_scene->clearSelected();
1665 //    m_scene->resetToolbar();
1666 //    setSelected(false);
1667 //}
1668 
1669 /**
1670  * Sets the view the widget is on.
1671  *
1672  * @param scene  The UMLScene the widget is on.
1673  */
setScene(UMLScene * scene)1674 void UMLWidget::setScene(UMLScene *scene)
1675 {
1676     //remove signals from old view - was probably 0 anyway
1677     disconnect(m_scene, SIGNAL(sigFillColorChanged(Uml::ID::Type)), this, SLOT(slotFillColorChanged(Uml::ID::Type)));
1678     disconnect(m_scene, SIGNAL(sigTextColorChanged(Uml::ID::Type)), this, SLOT(slotTextColorChanged(Uml::ID::Type)));
1679     disconnect(m_scene, SIGNAL(sigLineWidthChanged(Uml::ID::Type)), this, SLOT(slotLineWidthChanged(Uml::ID::Type)));
1680     m_scene = scene;
1681     connect(m_scene, SIGNAL(sigFillColorChanged(Uml::ID::Type)), this, SLOT(slotFillColorChanged(Uml::ID::Type)));
1682     connect(m_scene, SIGNAL(sigTextColorChanged(Uml::ID::Type)), this, SLOT(slotTextColorChanged(Uml::ID::Type)));
1683     connect(m_scene, SIGNAL(sigLineWidthChanged(Uml::ID::Type)), this, SLOT(slotLineWidthChanged(Uml::ID::Type)));
1684 }
1685 
1686 /**
1687  * Gets the x-coordinate.
1688  * Currently, the only class that reimplements this method is PinPortBase.
1689  *
1690  * @return The x-coordinate.
1691  */
getX() const1692 qreal UMLWidget::getX() const
1693 {
1694     return QGraphicsObjectWrapper::x();
1695 }
1696 
1697 /**
1698  * Gets the y-coordinate.
1699  * Currently, the only class that reimplements this method is PinPortBase.
1700  *
1701  * @return The y-coordinate.
1702  */
getY() const1703 qreal UMLWidget::getY() const
1704 {
1705     return QGraphicsObjectWrapper::y();
1706 }
1707 
1708 /**
1709  * Gets the position.
1710  * Currently, the only class that reimplements this method is PinPortBase.
1711  *
1712  * @return The QGraphicsObject position.
1713  */
getPos() const1714 QPointF UMLWidget::getPos() const
1715 {
1716     return QGraphicsObjectWrapper::pos();
1717 }
1718 
1719 /**
1720  * Sets the x-coordinate.
1721  * Currently, the only class that reimplements this method is
1722  * ObjectWidget.
1723  *
1724  * @param x The x-coordinate to be set.
1725  */
setX(qreal x)1726 void UMLWidget::setX(qreal x)
1727 {
1728     QGraphicsObjectWrapper::setX(x);
1729 }
1730 
1731 /**
1732  * Sets the y-coordinate.
1733  * Currently, the only class that reimplements this method is
1734  * ObjectWidget.
1735  *
1736  * @param y The y-coordinate to be set.
1737  */
setY(qreal y)1738 void UMLWidget::setY(qreal y)
1739 {
1740     QGraphicsObjectWrapper::setY(y);
1741 }
1742 
1743 /**
1744  * Used to cleanup any other widget it may need to delete.
1745  * Used by child classes.  This should be called before deleting a widget of a diagram.
1746  */
cleanup()1747 void UMLWidget::cleanup()
1748 {
1749 }
1750 
1751 /**
1752  * Tells the widget to snap to grid.
1753  * Will use the grid settings of the @ref UMLView it belongs to.
1754  */
slotSnapToGrid()1755 void UMLWidget::slotSnapToGrid()
1756 {
1757     if (!m_ignoreSnapToGrid) {
1758         qreal newX = m_scene->snappedX(x());
1759         setX(newX);
1760         qreal newY = m_scene->snappedY(y());
1761         setY(newY);
1762     }
1763 }
1764 
1765 /**
1766  * Set m_ignoreSnapToGrid.
1767  */
setIgnoreSnapToGrid(bool to)1768 void UMLWidget::setIgnoreSnapToGrid(bool to)
1769 {
1770     m_ignoreSnapToGrid = to;
1771 }
1772 
1773 /**
1774  * Return the value of m_ignoreSnapToGrid.
1775  */
getIgnoreSnapToGrid() const1776 bool UMLWidget::getIgnoreSnapToGrid() const
1777 {
1778     return m_ignoreSnapToGrid;
1779 }
1780 
1781 /**
1782  * Sets the size.
1783  * If m_scene->snapComponentSizeToGrid() is true then
1784  * set the next larger size that snaps to the grid.
1785  */
setSize(qreal width,qreal height)1786 void UMLWidget::setSize(qreal width, qreal height)
1787 {
1788     // snap to the next larger size that is a multiple of the grid
1789     if (!m_ignoreSnapComponentSizeToGrid && m_scene
1790             && m_scene->snapComponentSizeToGrid()) {
1791         // integer divisions
1792         int numX = width / m_scene->snapX();
1793         int numY = height / m_scene->snapY();
1794         // snap to the next larger valid value
1795         if (width > numX * m_scene->snapX())
1796             width = (numX + 1) * m_scene->snapX();
1797         if (height > numY * m_scene->snapY())
1798             height = (numY + 1) * m_scene->snapY();
1799     }
1800 
1801     const QRectF newRect(rect().x(), rect().y(), width, height);
1802     setRect(newRect);
1803     foreach(QGraphicsItem* child, childItems()) {
1804         UMLWidget* umlChild = static_cast<UMLWidget*>(child);
1805         umlChild->notifyParentResize();
1806     }
1807 }
1808 
1809 /**
1810  * Sets the size with another size.
1811  */
setSize(const QSizeF & size)1812 void UMLWidget::setSize(const QSizeF& size)
1813 {
1814     setSize(size.width(), size.height());
1815 }
1816 
1817 /**
1818  * Update the size of this widget.
1819  *
1820  * @param withAssocs true - update associations too
1821  */
updateGeometry(bool withAssocs)1822 void UMLWidget::updateGeometry(bool withAssocs)
1823 {
1824     if (m_doc->loading()) {
1825         return;
1826     }
1827     if (!m_autoResize)
1828         return;
1829     qreal oldW = width();
1830     qreal oldH = height();
1831     QSizeF size = calculateSize();
1832     qreal clipWidth = size.width();
1833     qreal clipHeight = size.height();
1834     constrain(clipWidth, clipHeight);
1835     setSize(clipWidth, clipHeight);
1836     slotSnapToGrid();
1837     if (withAssocs)
1838         adjustAssocs(size.width()-oldW, size.height()-oldH);
1839     update();
1840 }
1841 
1842 /**
1843  * clip the size of this widget against the
1844  * minimal and maximal limits.
1845  */
clipSize()1846 void UMLWidget::clipSize()
1847 {
1848     qreal clipWidth = width();
1849     qreal clipHeight = height();
1850     constrain(clipWidth, clipHeight);
1851     setSize(clipWidth, clipHeight);
1852 }
1853 
1854 /**
1855  * Template Method, override this to set the default font metric.
1856  */
setDefaultFontMetrics(QFont & font,UMLWidget::FontType fontType)1857 void UMLWidget::setDefaultFontMetrics(QFont &font, UMLWidget::FontType fontType)
1858 {
1859     setupFontType(font, fontType);
1860     setFontMetrics(fontType, QFontMetrics(font));
1861 }
1862 
setupFontType(QFont & font,UMLWidget::FontType fontType)1863 void UMLWidget::setupFontType(QFont &font, UMLWidget::FontType fontType)
1864 {
1865     switch (fontType) {
1866     case FT_NORMAL:
1867         font.setBold(false);
1868         font.setItalic(false);
1869         font.setUnderline(false);
1870         break;
1871     case FT_BOLD:
1872         font.setBold(true);
1873         font.setItalic(false);
1874         font.setUnderline(false);
1875         break;
1876     case FT_ITALIC:
1877         font.setBold(false);
1878         font.setItalic(true);
1879         font.setUnderline(false);
1880         break;
1881     case FT_UNDERLINE:
1882         font.setBold(false);
1883         font.setItalic(false);
1884         font.setUnderline(true);
1885         break;
1886     case FT_BOLD_ITALIC:
1887         font.setBold(true);
1888         font.setItalic(true);
1889         font.setUnderline(false);
1890         break;
1891     case FT_BOLD_UNDERLINE:
1892         font.setBold(true);
1893         font.setItalic(false);
1894         font.setUnderline(true);
1895         break;
1896     case FT_ITALIC_UNDERLINE:
1897         font.setBold(false);
1898         font.setItalic(true);
1899         font.setUnderline(true);
1900         break;
1901     case FT_BOLD_ITALIC_UNDERLINE:
1902         font.setBold(true);
1903         font.setItalic(true);
1904         font.setUnderline(true);
1905         break;
1906     default: return;
1907     }
1908 }
1909 
paint(QPainter * painter,const QStyleOptionGraphicsItem * option,QWidget * widget)1910 void UMLWidget::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
1911 {
1912     Q_UNUSED(option);
1913     Q_UNUSED(widget);
1914 
1915     if (option->state & QStyle::State_Selected) {
1916         const qreal w = width();
1917         const qreal h = height();
1918         const qreal s = selectionMarkerSize;
1919         QBrush brush(Qt::blue);
1920         painter->fillRect(0, 0, s,  s, brush);
1921         painter->fillRect(0, 0 + h - s, s, s, brush);
1922         painter->fillRect(0 + w - s, 0, s, s, brush);
1923 
1924         // Draw the resize anchor in the lower right corner.
1925         // Don't draw it if the widget is so small that the
1926         // resize anchor would cover up most of the widget.
1927         if (m_resizable && w >= s+8 && h >= s+8) {
1928             brush.setColor(Qt::red);
1929             const int bottom = 0 + h;
1930             int horSide = w;   // horizontal side default: right side
1931             if (baseType() == wt_Message) {
1932                MessageWidget *msg = asMessageWidget();
1933                int x1 = msg->objectWidget(Uml::RoleType::A)->x();
1934                int x2 = msg->objectWidget(Uml::RoleType::B)->x();
1935                if (x1 > x2) {
1936                    // On messages running right to left we use the left side for
1937                    // placing the resize anchor because the message's execution
1938                    // specification as at the left in this case.  Furthermore,
1939                    // the right side may be covered up by another message's
1940                    // execution specification.
1941                    horSide = 17;  // execution box width
1942                }
1943             }
1944             painter->drawLine(horSide - s, 0 + h - 1, 0 + w - 1, 0 + h - s);
1945             painter->drawLine(horSide - (s*2), bottom - 1, horSide - 1, bottom - (s*2));
1946             painter->drawLine(horSide - (s*3), bottom - 1, horSide - 1, bottom - (s*3));
1947         } else {
1948             painter->fillRect(0 + w - s, 0 + h - s, s, s, brush);
1949         }
1950         // debug info
1951         if (Tracer::instance()->isEnabled(QLatin1String(metaObject()->className()))) {
1952             QPen p(Qt::green);
1953             p.setWidthF(1.0);
1954             painter->setPen(p);
1955             painter->setBrush(Qt::NoBrush);
1956             painter->drawPath(shape());
1957             painter->setPen(Qt::blue);
1958             painter->drawRect(boundingRect());
1959             // origin
1960             painter->drawLine(-10, 0, 10, 0);
1961             painter->drawLine(0, -10, 0, 10);
1962         }
1963     }
1964 
1965     if (umlScene()->isShowDocumentationIndicator() && hasDocumentation()) {
1966         const qreal h = height();
1967         const qreal d = 8;
1968         QPolygonF p;
1969         p << QPointF(0, h - d) << QPointF(d, h) << QPointF(0, h);
1970         painter->setPen(Qt::blue);
1971         painter->setBrush(Qt::red);
1972         painter->drawPolygon(p);
1973     }
1974 }
1975 
1976 /**
1977  * Template Method, override this to set the default font metric.
1978  */
setDefaultFontMetrics(QFont & font,UMLWidget::FontType fontType,QPainter & painter)1979 void UMLWidget::setDefaultFontMetrics(QFont &font, UMLWidget::FontType fontType, QPainter &painter)
1980 {
1981     setupFontType(font, fontType);
1982     painter.setFont(font);
1983     setFontMetrics(fontType, painter.fontMetrics());
1984 }
1985 
1986 /**
1987  * Returns the font metric used by this object for Text
1988  * which uses bold/italic fonts.
1989  */
getFontMetrics(UMLWidget::FontType fontType) const1990 QFontMetrics &UMLWidget::getFontMetrics(UMLWidget::FontType fontType) const
1991 {
1992     return *m_pFontMetrics[fontType];
1993 }
1994 
1995 /**
1996  * Set the font metric to use.
1997  */
setFontMetrics(UMLWidget::FontType fontType,QFontMetrics fm)1998 void UMLWidget::setFontMetrics(UMLWidget::FontType fontType, QFontMetrics fm)
1999 {
2000     delete m_pFontMetrics[fontType];
2001     m_pFontMetrics[fontType] = new QFontMetrics(fm);
2002 }
2003 
2004 /**
2005  * Sets the font the widget is to use.
2006  *
2007  * @param font Font to be set.
2008  */
setFont(const QFont & font)2009 void UMLWidget::setFont(const QFont &font)
2010 {
2011     QFont newFont = font;
2012     forceUpdateFontMetrics(newFont, 0);
2013 
2014     if (m_font != newFont) {
2015         UMLApp::app()->executeCommand(new CmdChangeFont(this, font));
2016     }
2017 }
2018 
2019 /**
2020  * Sets the font the widget is to use.
2021  *
2022  * @param font Font to be set.
2023  */
setFontCmd(const QFont & font)2024 void UMLWidget::setFontCmd(const QFont &font)
2025 {
2026     WidgetBase::setFont(font);
2027     forceUpdateFontMetrics(0);
2028     if (m_doc->loading())
2029         return;
2030     update();
2031 }
2032 
2033 /**
2034  * Updates font metrics for widgets current m_font
2035  */
forceUpdateFontMetrics(QPainter * painter)2036 void UMLWidget::forceUpdateFontMetrics(QPainter *painter)
2037 {
2038     forceUpdateFontMetrics(m_font, painter);
2039 }
2040 
2041 /**
2042  * @note For performance Reasons, only FontMetrics for already used
2043  *  font types are updated. Not yet used font types will not get a font metric
2044  *  and will get the same font metric as if painter was zero.
2045  *  This behaviour is acceptable, because diagrams will always be shown on Display
2046  *  first before a special painter like a printer device is used.
2047  */
forceUpdateFontMetrics(QFont & font,QPainter * painter)2048 void UMLWidget::forceUpdateFontMetrics(QFont& font, QPainter *painter)
2049 {
2050     if (painter == 0) {
2051         for (int i = (int)FT_INVALID - 1; i >= 0; --i) {
2052             if (m_pFontMetrics[(UMLWidget::FontType)i] != 0)
2053                 setDefaultFontMetrics(font, (UMLWidget::FontType)i);
2054         }
2055     } else {
2056         for (int i2 = (int)FT_INVALID - 1; i2 >= 0; --i2) {
2057             if (m_pFontMetrics[(UMLWidget::FontType)i2] != 0)
2058                 setDefaultFontMetrics(font, (UMLWidget::FontType)i2, *painter);
2059         }
2060     }
2061     if (m_doc->loading())
2062         return;
2063     // calculate the size, based on the new font metric
2064     updateGeometry();
2065 }
2066 
2067 /**
2068  * Set the status of whether to show Stereotype.
2069  *
2070  * @param flag   Value of type Uml::ShowStereoType::Enum
2071  */
setShowStereotype(Uml::ShowStereoType::Enum flag)2072 void UMLWidget::setShowStereotype(Uml::ShowStereoType::Enum flag)
2073 {
2074     m_showStereotype = flag;
2075     updateGeometry();
2076     update();
2077 }
2078 
2079 /**
2080  * Return stereotype concrete attributes concatenated into single string
2081  * with the attribute name given before each value and delimited by "{"
2082  * at start and "}" at end.
2083  * Example:
2084  * For a stereotype with attribute 'foo' of type Double and attribute 'bar'
2085  * of type String and concrete values 1.0 for 'foo' and "hello" for 'bar',
2086  * the result is:  {foo=1.0,bar="hello"}
2087  */
tags() const2088 QString UMLWidget::tags() const
2089 {
2090     if (m_umlObject == 0)
2091         return QString();
2092     UMLStereotype *s = m_umlObject->umlStereotype();
2093     if (s == 0)
2094         return QString();
2095     UMLStereotype::AttributeDefs adefs = s->getAttributeDefs();
2096     if (adefs.isEmpty())
2097         return QString();
2098     const QStringList& umlTags = m_umlObject->tags();
2099     QString taggedValues(QLatin1String("{"));
2100     for (int i = 0; i < adefs.size(); i++) {
2101         UMLStereotype::AttributeDef ad = adefs.at(i);
2102         taggedValues.append(ad.name);
2103         taggedValues.append(QLatin1String("="));
2104         QString value = ad.defaultVal;
2105         if (i < umlTags.size()) {
2106             QString umlTag = umlTags.at(i);
2107             if (!umlTag.isEmpty())
2108                 value = umlTag;
2109         }
2110         if (ad.type == Uml::PrimitiveTypes::String)
2111             value = QLatin1String("\"") + value + QLatin1String("\"");
2112         taggedValues.append(value);
2113         if (i < adefs.size() - 1)
2114             taggedValues.append(QLatin1String(","));
2115      }
2116      taggedValues.append(QLatin1String("}"));
2117      return taggedValues;
2118 }
2119 
2120 /**
2121  * Returns the status of whether to show Stereotype.
2122  *
2123  * @return  True if stereotype is shown.
2124  */
showStereotype() const2125 Uml::ShowStereoType::Enum UMLWidget::showStereotype() const
2126 {
2127     return m_showStereotype;
2128 }
2129 
2130 /**
2131  * Overrides the standard operation.
2132  *
2133  * @param me The move event.
2134  */
moveEvent(QGraphicsSceneMouseEvent * me)2135 void UMLWidget::moveEvent(QGraphicsSceneMouseEvent* me)
2136 {
2137   Q_UNUSED(me)
2138 }
2139 
saveToXMI1(QXmlStreamWriter & writer)2140 void UMLWidget::saveToXMI1(QXmlStreamWriter& writer)
2141 {
2142     /*
2143       When calling this from child classes bear in mind that the call
2144       must precede terminated XML subelements.
2145       Type must be set in the child class.
2146     */
2147     WidgetBase::saveToXMI1(writer);
2148     DiagramProxyWidget::saveToXMI1(writer);
2149 
2150     qreal dpiScale = UMLApp::app()->document()->dpiScale();
2151     writer.writeAttribute(QLatin1String("x"), QString::number(x() / dpiScale));
2152     writer.writeAttribute(QLatin1String("y"), QString::number(y() / dpiScale));
2153     writer.writeAttribute(QLatin1String("width"), QString::number(width() / dpiScale));
2154     writer.writeAttribute(QLatin1String("height"), QString::number(height() / dpiScale));
2155 
2156     writer.writeAttribute(QLatin1String("isinstance"), QString::number(m_isInstance));
2157     if (!m_instanceName.isEmpty())
2158         writer.writeAttribute(QLatin1String("instancename"), m_instanceName);
2159     if (m_showStereotype)
2160         writer.writeAttribute(QLatin1String("showstereotype"), QString::number(m_showStereotype));
2161 }
2162 
loadFromXMI1(QDomElement & qElement)2163 bool UMLWidget::loadFromXMI1(QDomElement & qElement)
2164 {
2165     WidgetBase::loadFromXMI1(qElement);
2166     DiagramProxyWidget::loadFromXMI1(qElement);
2167     QString x = qElement.attribute(QLatin1String("x"), QLatin1String("0"));
2168     QString y = qElement.attribute(QLatin1String("y"), QLatin1String("0"));
2169     QString h = qElement.attribute(QLatin1String("height"), QLatin1String("0"));
2170     QString w = qElement.attribute(QLatin1String("width"), QLatin1String("0"));
2171     qreal dpiScale = UMLApp::app()->document()->dpiScale();
2172     setSize(toDoubleFromAnyLocale(w) * dpiScale,
2173             toDoubleFromAnyLocale(h) * dpiScale);
2174     setX(toDoubleFromAnyLocale(x) * dpiScale);
2175     setY(toDoubleFromAnyLocale(y) * dpiScale);
2176 
2177     QString isinstance = qElement.attribute(QLatin1String("isinstance"), QLatin1String("0"));
2178     m_isInstance = (bool)isinstance.toInt();
2179     m_instanceName = qElement.attribute(QLatin1String("instancename"));
2180     QString showstereo = qElement.attribute(QLatin1String("showstereotype"), QLatin1String("0"));
2181     m_showStereotype = (Uml::ShowStereoType::Enum)showstereo.toInt();
2182 
2183     return true;
2184 }
2185 
2186 /**
2187  * Adds a widget to the diagram, which is connected to the current widget
2188  * @param widget widget instance to add to diagram
2189  * @param type association type
2190  * @param options widget options
2191  */
addConnectedWidget(UMLWidget * widget,Uml::AssociationType::Enum type,AddWidgetOptions options)2192 void UMLWidget::addConnectedWidget(UMLWidget *widget, Uml::AssociationType::Enum type, AddWidgetOptions options)
2193 {
2194     QString name = Widget_Utils::defaultWidgetName(widget->baseType());
2195     widget->setName(name);
2196     if (options & ShowProperties) {
2197         if (!widget->showPropertiesDialog()) {
2198             delete widget;
2199             return;
2200         }
2201     }
2202 
2203     umlScene()->addItem(widget);
2204     widget->setX(x() + rect().width() + 100);
2205     widget->setY(y());
2206     if (options & SetupSize) {
2207         widget->setSize(100, 40);
2208         QSizeF size = widget->minimumSize();
2209         widget->setSize(size);
2210     }
2211     AssociationWidget* assoc = options & SwitchDirection ? AssociationWidget::create(umlScene(), widget, type, this)
2212                                                     : AssociationWidget::create(umlScene(), this, type, widget);
2213     umlScene()->addAssociation(assoc);
2214     umlScene()->clearSelected();
2215     umlScene()->selectWidget(widget);
2216 
2217     UMLApp::app()->beginMacro(I18N_NEXT_RELEASE("Adding connected '%1'", widget->baseTypeStrWithoutPrefix());
2218     UMLApp::app()->executeCommand(new CmdCreateWidget(widget));
2219     UMLApp::app()->executeCommand(new CmdCreateWidget(assoc));
2220     UMLApp::app()->endMacro();
2221     m_doc->setModified();
2222 }
2223 
2224 /**
2225  * Adds a widget to the diagram, which is connected to the current widget
2226  * @param widget widget instance to add to diagram
2227  * @param showProperties whether to show properties of the widget
2228  */
2229 void UMLWidget::addWidget(UMLWidget *widget, bool showProperties)
2230 {
2231     umlScene()->addItem(widget);
2232     widget->setX(x() + rect().width() + 100);
2233     widget->setY(y());
2234     widget->setSize(100, 40);
2235     if (showProperties)
2236         widget->showPropertiesDialog();
2237     QSizeF size = widget->minimumSize();
2238     widget->setSize(size);
2239 }
2240