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