umlwidget.cpp 49.3 KB
Newer Older
1
2
3
4
5
6
/***************************************************************************
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
Andi Fischer's avatar
Andi Fischer committed
7
 *   copyright (C) 2002-2013                                               *
Ralf Habacker's avatar
Ralf Habacker committed
8
 *   Umbrello UML Modeller Authors <umbrello-devel@kde.org>                *
9
10
11
 ***************************************************************************/

#include "umlwidget.h"
12

13
// local includes
14
#include "associationwidget.h"
15
#include "classifier.h"
16
17
18
#include "classpropdlg.h"
#include "cmds.h"
#include "debug_utils.h"
19
#include "docwindow.h"
20
21
22
23
#include "floatingtextwidget.h"
#include "idchangelog.h"
#include "listpopupmenu.h"
#include "settingsdlg.h"
24
25
#include "uml.h"
#include "umldoc.h"
26
#include "umlobject.h"
27
#include "umlscene.h"
28
#include "umlview.h"
29
30
31
32
33
34
35
36
37
#include "uniqueid.h"

// kde includes
#include <kcolordialog.h>
#include <kfontdialog.h>
#include <klocale.h>
#include <kmessagebox.h>

// qt includes
38
#include <QColor>
39
40
#include <QPainter>
#include <QPointer>
41

Mohamed-Amine Bouchikhi's avatar
Mohamed-Amine Bouchikhi committed
42
using namespace Uml;
43

44
45
DEBUG_REGISTER_DISABLED(UMLWidget)

46
47
const QSizeF UMLWidget::DefaultMinimumSize(50, 20);
const QSizeF UMLWidget::DefaultMaximumSize(1000, 5000);
48

49
50
51
52
53
54
/**
 * Creates a UMLWidget object.
 *
 * @param scene The view to be displayed on.
 * @param o The UMLObject to represent.
 */
55
UMLWidget::UMLWidget(UMLScene * scene, WidgetType type, UMLObject * o)
56
  : WidgetBase(scene, type)
57
58
{
    init();
59
60
61
62
    m_umlObject = o;
    if (m_umlObject) {
        connect(m_umlObject, SIGNAL(modified()), this, SLOT(updateWidget()));
        m_nId = m_umlObject->id();
63
64
65
    }
}

66
67
68
69
70
71
72
/**
 * Creates a UMLWidget object.
 *
 * @param scene The view to be displayed on.
 * @param id The id of the widget.
 *  The default value (id_None) will prompt generation of a new ID.
 */
73
UMLWidget::UMLWidget(UMLScene *scene, WidgetType type, Uml::ID::Type id)
74
  : WidgetBase(scene, type)
75
76
{
    init();
Andi Fischer's avatar
Andi Fischer committed
77
    if (id == Uml::ID::None)
78
79
80
81
82
        m_nId = UniqueID::gen();
    else
        m_nId = id;
}

83
/**
84
 * Destructor.
85
 */
Andi Fischer's avatar
Andi Fischer committed
86
87
UMLWidget::~UMLWidget()
{
88
89
90
    cleanup();
}

91
92
93
/**
 * Assignment operator
 */
Andi Fischer's avatar
Andi Fischer committed
94
95
UMLWidget& UMLWidget::operator=(const UMLWidget & other)
{
96
97
98
    if (this == &other)
        return *this;

99
100
    WidgetBase::operator=(other);

101
    // assign members loaded/saved
102
103
104
    m_useFillColor = other.m_useFillColor;
    m_usesDiagramFillColor = other.m_usesDiagramFillColor;
    m_usesDiagramUseFillColor = other.m_usesDiagramUseFillColor;
105
    m_fillColor = other.m_fillColor;
106
    m_Assocs = other.m_Assocs;
107
    m_isInstance = other.m_isInstance;
108
    m_instanceName = other.m_instanceName;
109
110
    m_instanceName = other.m_instanceName;
    m_showStereotype = other.m_showStereotype;
111
112
    setX(other.x());
    setY(other.y());
113
    setRect(rect().x(), rect().y(), other.width(), other.height());
114
115

    // assign volatile (non-saved) members
116
    m_startMove = other.m_startMove;
117
    m_nPosX = other.m_nPosX;
118
    m_doc = other.m_doc;    //new
119
    m_resizable = other.m_resizable;
120
    for (unsigned i = 0; i < FT_INVALID; ++i)
121
        m_pFontMetrics[i] = other.m_pFontMetrics[i];
122
123
124
    m_activated = other.m_activated;
    m_ignoreSnapToGrid = other.m_ignoreSnapToGrid;
    m_ignoreSnapComponentSizeToGrid = other.m_ignoreSnapComponentSizeToGrid;
125
126
127
    return *this;
}

128
129
130
/**
 * Overload '==' operator
 */
131
bool UMLWidget::operator==(const UMLWidget& other) const
Andi Fischer's avatar
Andi Fischer committed
132
133
{
    if (this == &other)
134
135
        return true;

136
    if (m_baseType != other.m_baseType) {
137
138
139
        return false;
    }

140
    if (id() != other.id())
141
142
143
144
145
146
147
148
149
150
151
        return false;

    /* Testing the associations is already an exaggeration, no?
       The type and ID should uniquely identify an UMLWidget.
     */
    if (m_Assocs.count() != other.m_Assocs.count()) {
        return false;
    }

    // if(getBaseType() != wt_Text) // DON'T do this for floatingtext widgets, an infinite loop will result
    // {
Andi Fischer's avatar
Andi Fischer committed
152
153
    AssociationWidgetListIt assoc_it(m_Assocs);
    AssociationWidgetListIt assoc_it2(other.m_Assocs);
154
    AssociationWidget * assoc = 0, *assoc2 = 0;
Andi Fischer's avatar
Andi Fischer committed
155
    while (assoc_it.hasNext() &&  assoc_it2.hasNext()) {
156
157
158
        assoc = assoc_it.next();
        assoc2 = assoc_it2.next();

Andi Fischer's avatar
Andi Fischer committed
159
        if (!(*assoc == *assoc2)) {
160
161
162
163
164
165
166
167
            return false;
        }
    }
    // }
    return true;
    // NOTE:  In the comparison tests we are going to do, we don't need these values.
    // They will actually stop things functioning correctly so if you change these, be aware of that.
    /*
168
    if(m_useFillColor != other.m_useFillColor)
169
170
171
172
173
174
175
176
177
178
        return false;
    if(m_nId != other.m_nId)
        return false;
    if(m_nX  != other.m_nX)
        return false;
    if(m_nY != other.m_nY)
        return false;
     */
}

179
180
181
/**
 * Compute the minimum possible width and height.
 *
182
 * @return QSizeF(mininum_width, minimum_height)
183
 */
184
QSizeF UMLWidget::minimumSize()
185
186
187
188
189
190
191
192
193
194
{
    return m_minimumSize;
}

/**
 * This method is used to set the minimum size variable for this
 * widget.
 *
 * @param newSize The size being set as minimum.
 */
195
void UMLWidget::setMinimumSize(const QSizeF& newSize)
196
197
198
199
200
201
202
203
204
{
    m_minimumSize = newSize;
}

/**
 * Compute the maximum possible width and height.
 *
 * @return maximum size
 */
205
QSizeF UMLWidget::maximumSize()
206
207
208
209
210
211
212
213
214
215
{
    return m_maximumSize;
}

/**
 * This method is used to set the maximum size variable for this
 * widget.
 *
 * @param newSize The size being set as maximum.
 */
216
void UMLWidget::setMaximumSize(const QSizeF& newSize)
217
218
219
220
{
    m_maximumSize = newSize;
}

221
222
223
224
225
226
227
228
/**
 * Event handler for context menu events.
 */
void UMLWidget::contextMenuEvent(QGraphicsSceneContextMenuEvent *event)
{
    WidgetBase::contextMenuEvent(event);
}

229
/**
230
231
232
233
234
235
236
237
238
239
240
 * Moves the widget to a new position using the difference between the
 * current position and the new position.
 * This method doesn't adjust associations. It only moves the widget.
 *
 * It can be overridden to constrain movement only in one axis even when
 * the user isn't constraining the movement with shift or control buttons, for example.
 * The movement policy set here is applied whenever the widget is moved, being it
 * moving it explicitly, or as a part of a selection but not receiving directly the
 * mouse events.
 *
 * Default behaviour is move the widget to the new position using the diffs.
241
 * @see constrainMovementForAllWidgets
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
 *
 * @param diffX The difference between current X position and new X position.
 * @param diffY The difference between current Y position and new Y position.
 */
void UMLWidget::moveWidgetBy(qreal diffX, qreal diffY)
{
    setX(x() + diffX);
    setY(y() + diffY);
}

/**
 * Modifies the value of the diffX and diffY variables used to move the widgets.
 *
 * It can be overridden to constrain movement of all the selected widgets only in one
 * axis even when the user isn't constraining the movement with shift or control
 * buttons, for example.
 * The difference with moveWidgetBy is that the diff positions used here are
 * applied to all the selected widgets instead of only to m_widget, and that
 * moveWidgetBy, in fact, moves the widget, and here simply the diff positions
 * are modified.
 *
 * Default behaviour is do nothing.
 * @see moveWidgetBy
 *
 * @param diffX The difference between current X position and new X position.
 * @param diffY The difference between current Y position and new Y position.
 */
void UMLWidget::constrainMovementForAllWidgets(qreal &diffX, qreal &diffY)
{
    Q_UNUSED(diffX) Q_UNUSED(diffY)
}

274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
/**
 * Bring the widget at the pressed position to the foreground.
 */
void UMLWidget::toForeground()
{
    QRectF rect = QRectF(scenePos(), QSizeF(width(), height()));
    QList<QGraphicsItem*> items = scene()->items(rect, Qt::IntersectsItemShape, Qt::DescendingOrder);
    DEBUG(DBG_SRC) << "items at " << rect << " = " << items.count();
    if (items.count() > 1) {
        foreach(QGraphicsItem* i, items) {
            UMLWidget* w = dynamic_cast<UMLWidget*>(i);
            if (w) {
                DEBUG(DBG_SRC) << "item=" << w->name() << " with zValue=" << w->zValue();
                if (w->name() != name()) {
                    if (w->zValue() >= zValue()) {
                        setZValue(w->zValue() + 1.0);
                        DEBUG(DBG_SRC) << "bring to foreground with zValue: " << zValue();
                    }
                }
            }
        }
    }
    else {
        setZValue(0.0);
    }
    DEBUG(DBG_SRC) << "zValue is " << zValue();
}

302
/**
303
304
305
 * Handles a mouse press event.
 * It'll select the widget (or mark it to be deselected) and prepare it to
 * be moved or resized. Go on reading for more info about this.
306
 *
307
308
309
310
311
312
313
314
315
316
317
 * Widget values and message bar status are saved.
 *
 * If shift or control buttons are pressed, we're in move area no matter
 * where the button was pressed in the widget. Moreover, if the widget
 * wasn't already selected, it's added to the selection. If already selected,
 * it's marked to be deselected when releasing the button (provided it isn't
 * moved).
 * Also, if the widget is already selected with other widgets but shift nor
 * control buttons are pressed, we're in move area. If finally we don't move
 * the widget, it's selected and the other widgets deselected when releasing
 * the left button.
318
 *
319
320
321
322
323
324
325
326
 * If shift nor control buttons are pressed, we're facing a single selection.
 * Depending on the position of the cursor, we're in move or in resize area.
 * If the widget wasn't selected (both when there are no widgets selected, or
 * when there're other widgets selected but not the one receiving the press
 * event) it's selected and the others deselected, if any. If already selected,
 * it's marked to be deselected when releasing the button (provided it wasn't
 * moved or resized).
 *
327
 * @param event The QGraphicsSceneMouseEvent event.
328
329
330
 */
void UMLWidget::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
331
    if (event->button() != Qt::LeftButton) {
332
333
334
        event->ignore();
        return;
    }
335
336
337
338
339
    event->accept();
    DEBUG(DBG_SRC) << "widget = " << name() << " / type = " << baseTypeStr();

    toForeground();

340
341
    m_startMovePostion = pos();
    m_startResizeSize = QSizeF(width(), height());
342
343
344

    // saving the values of the widget
    m_pressOffset = event->scenePos() - pos();
345
    DEBUG(DBG_SRC) << "press offset=" << m_pressOffset;
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366

    m_oldStatusBarMsg = UMLApp::app()->statusBarMsg();

    if (event->modifiers() == Qt::ShiftModifier || event->modifiers() == Qt::ControlModifier) {
        m_shiftPressed = true;

        if (event->button() == Qt::LeftButton) {
            m_inMoveArea = true;
        }

        if (!isSelected()) {
            selectMultiple(event);
        }
        return;
    }

    m_shiftPressed = false;

    int count = m_scene->selectedCount(true);
    if (event->button() == Qt::LeftButton) {
        if (isSelected() && count > 1) {
367
            // single selection is made in release event if the widget wasn't moved
368
369
370
371
372
373
374
375
376
377
378
379
380
381
            m_inMoveArea = true;
            m_oldPos = pos();
            return;
        }

        if (isInResizeArea(event)) {
            m_inResizeArea = true;
            m_oldW = width();
            m_oldH = height();
        } else {
            m_inMoveArea = true;
        }
    }

382
    // if widget wasn't selected, or it was selected but with other widgets also selected
383
384
385
    if (!isSelected() || count > 1) {
        selectSingle(event);
    }
386
387
}

388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
/**
 * Handles a mouse move event.
 * It resizes or moves the widget, depending on where the cursor is pressed
 * on the widget. Go on reading for more info about this.
 *
 * If resizing, the widget is resized using UMLWidget::resizeWidget (where specific
 * widget resize constrain can be applied), and then the associations are
 * adjusted.
 * The resizing can be constrained also to an specific axis using control
 * and shift buttons. If on or another is pressed, it's constrained to X axis.
 * If both are pressed, it's constrained to Y axis.
 *
 * If not resizing, the widget is being moved. If the move is being started,
 * the selection bounds are set (which includes updating the list of selected
 * widgets).
 * The difference between the previous position of the selection and the new
 * one is got (taking in account the selection bounds so widgets don't go
 * beyond the scene limits). Then, it's constrained to X or Y axis depending
 * on shift and control buttons.
 * A further constrain is made using constrainMovementForAllWidgets (for example,
 * if the widget that receives the event can only be moved in Y axis, with this
 * method the movement of all the widgets in the selection can be constrained to
 * be moved only in Y axis).
 * Then, all the selected widgets are moved using moveWidgetBy (where specific
 * widget movement constrain can be applied) and, if an specific amount of time
 * passed from the last move event, the associations are also updated (they're
 * not updated always to be easy on the CPU). Finally, the scene is resized,
 * and selection bounds updated.
 *
417
 * @param event The QGraphicsSceneMouseEvent event.
418
 */
419
void UMLWidget::mouseMoveEvent(QGraphicsSceneMouseEvent* event)
420
421
{
    if (m_inResizeArea) {
422
        resize(event);
423
424
425
426
427
428
429
430
431
432
433
434
435
        return;
    }

    if (!m_moved) {
        UMLApp::app()->document()->writeToStatusBar(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."));

        m_moved = true;
        //Maybe needed by AssociationWidget
        m_startMove = true;

        setSelectionBounds();
    }

436
    QPointF position = event->scenePos() - m_pressOffset;
437
438
439
    qreal diffX = position.x() - x();
    qreal diffY = position.y() - y();

440
    if ((event->modifiers() & Qt::ShiftModifier) && (event->modifiers() & Qt::ControlModifier)) {
441
442
        // move only in Y axis
        diffX = 0;
443
    } else if ((event->modifiers() & Qt::ShiftModifier) || (event->modifiers() & Qt::ControlModifier)) {
444
445
446
447
448
449
450
451
452
453
454
        // move only in X axis
        diffY = 0;
    }

    constrainMovementForAllWidgets(diffX, diffY);

    // nothing to move
    if (diffX == 0 && diffY == 0) {
        return;
    }

455
    QPointF delta = event->scenePos() - event->lastScenePos();
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
    adjustUnselectedAssocs(delta.x(), delta.y());

    DEBUG(DBG_SRC) << "diffX=" << diffX << " / diffY=" << diffY;
    foreach(UMLWidget* widget, m_selectedWidgetsList) {
        widget->moveWidgetBy(diffX, diffY);
    }

    // Move any selected associations.
    foreach(AssociationWidget* aw, m_scene->selectedAssocs()) {
        if (aw->isSelected()) {
            aw->moveEntireAssoc(diffX, diffY);
        }
    }

    umlScene()->resizeSceneToItems();
    slotSnapToGrid();
}

474
/**
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
 * Handles a mouse release event.
 * It selects or deselects the widget and cancels or confirms the move or
 * resize. Go on reading for more info about this.
 * No matter which tool is selected, Z position of widget is updated.
 *
 * Middle button release resets the selection.
 * Left button release, if it wasn't moved nor resized, selects the widget
 * and deselect the others if it wasn't selected and there were other widgets
 * selected. If the widget was marked to be deselected, deselects it.
 * If it was moved or resized, the document is set to modified if position
 * or size changed. Also, if moved, all the associations are adjusted because
 * the timer could have prevented the adjustment in the last move event before
 * the release.
 * If mouse was pressed in resize area, cursor is set again to normal cursor
 * Right button release if right button was pressed shows the pop up menu for
 * the widget.
 * If left button was pressed, it cancels the move or resize with a mouse move
 * event at the same position than the cursor was when pressed. Another left
 * button release is also sent.
494
 *
495
 * @param event The QGraphicsSceneMouseEvent event.
496
 */
497
void UMLWidget::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
498
{
499
500
    if (!m_moved && !m_resized) {
        if (!m_shiftPressed && (m_scene->selectedCount(true) > 1)) {
501
            selectSingle(event);
502
        } else if (!isSelected()) {
503
            deselect(event);
504
505
506
507
        }
    } else {
        // Commands
        if (m_moved) {
508
509
510
            foreach(UMLWidget* widget, m_selectedWidgetsList) {
                UMLApp::app()->executeCommand(new Uml::CmdMoveWidget(widget));
            }
511
512
513
514
515
            m_moved = false;
        } else {
            UMLApp::app()->executeCommand(new Uml::CmdResizeWidget(this));
            m_resized = false;
        }
516

517
518
519
520
        if ((m_inMoveArea && wasPositionChanged()) ||
                (m_inResizeArea && wasSizeChanged())) {
            umlDoc()->setModified(true);
        }
521

522
        UMLApp::app()->document()->writeToStatusBar(m_oldStatusBarMsg);
523
524
    }

525
526
527
    if (m_inResizeArea) {
        m_inResizeArea = false;
        m_scene->activeView()->setCursor(Qt::ArrowCursor);
528
    } else {
529
        m_inMoveArea = false;
530
    }
531
532
533
534
535
536
537
}

/**
 * Event handler for mouse double click events.
 * @param event the QGraphicsSceneMouseEvent event.
 */
void UMLWidget::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event)
Andi Fischer's avatar
Andi Fischer committed
538
{
539
    if (event->button() == Qt::LeftButton) {
540
        DEBUG(DBG_SRC) << "widget = " << name() << " / type = " << baseTypeStr();
541
542
543
544
545
546
547
548
549
550
        switch(baseType()) {
        case WidgetBase::wt_Message:  // will be handled in its class
            QGraphicsItem::mouseDoubleClickEvent(event);
            break;
        default:
            showPropertiesDialog();
            event->accept();
            break;
        }
    }
551
552
}

553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
/**
 * Return the start position of the move action.
 * @return   point where the move began
 */
QPointF UMLWidget::startMovePosition() const
{
    return m_startMovePostion;
}

/**
 * Return the start size of the resize action.
 * @return   size where the resize began
 */
QSizeF UMLWidget::startResizeSize() const
{
    return m_startResizeSize;
}

571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
/**
 * Resizes the widget.
 * It's called from resize, after the values are constrained and before
 * the associations are adjusted.
 *
 * Default behaviour is resize the widget using the new size values.
 * @see resize
 *
 * @param newW   The new width for the widget.
 * @param newH   The new height for the widget.
 */
void UMLWidget::resizeWidget(qreal newW, qreal newH)
{
    setSize(newW, newH);
}

587
588
589
/**
 * When a widget changes this slot captures that signal.
 */
590
591
void UMLWidget::updateWidget()
{
592
    updateGeometry();
593
    switch (m_baseType) {
594
    case WidgetBase::wt_Class:
595
        m_scene->createAutoAttributeAssociations(this);
Andi Fischer's avatar
Andi Fischer committed
596
        break;
597
    case WidgetBase::wt_Entity:
598
        m_scene->createAutoConstraintAssociations(this);
Andi Fischer's avatar
Andi Fischer committed
599
600
601
        break;
    default:
        break;
602
    }
603

Andi Fischer's avatar
Andi Fischer committed
604
    if (isVisible())
605
606
607
        update();
}

608
609
/**
 * Apply possible constraints to the given candidate width and height.
610
611
 * The default implementation limits input values to the bounds returned
 * by minimumSize()/maximumSize().
612
613
614
615
 *
 * @param width  input value, may be modified by the constraint
 * @param height input value, may be modified by the constraint
 */
616
void UMLWidget::constrain(qreal& width, qreal& height)
Andi Fischer's avatar
Andi Fischer committed
617
{
618
    QSizeF minSize = minimumSize();
619
620
621
622
    if (width < minSize.width())
        width = minSize.width();
    if (height < minSize.height())
        height = minSize.height();
623
    QSizeF maxSize = maximumSize();
624
625
626
627
    if (width > maxSize.width())
        width = maxSize.width();
    if (height > maxSize.height())
        height = maxSize.height();
628
629

    if (fixedAspectRatio()) {
630
        QSizeF size = rect().size();
631
        float aspectRatio = size.width() > 0 ? (float)size.height()/size.width() : 1;
632
633
        height = width * aspectRatio;
    }
634
635
}

636
637
638
/**
 * Initializes key attributes of the class.
 */
Andi Fischer's avatar
Andi Fischer committed
639
640
void UMLWidget::init()
{
Andi Fischer's avatar
Andi Fischer committed
641
    m_nId = Uml::ID::None;
642
    m_isInstance = false;
643
644
    setMinimumSize(DefaultMinimumSize);
    setMaximumSize(DefaultMaximumSize);
645
    
646
    for (int i = 0; i < (int)FT_INVALID; ++i) {
647
        m_pFontMetrics[(UMLWidget::FontType)i] = 0;
648
    }
649
    
650
    if (m_scene) {
651
652
653
        m_useFillColor = true;
        m_usesDiagramFillColor = true;
        m_usesDiagramUseFillColor = true;
654
        const Settings::OptionState& optionState = m_scene->optionState();
655
        m_fillColor = optionState.uiState.fillColor;
656
        m_showStereotype = optionState.classState.showStereoType;
657
    } else {
658
        uError() << "SERIOUS PROBLEM - m_scene is NULL";
659
660
661
        m_useFillColor = false;
        m_usesDiagramFillColor = false;
        m_usesDiagramUseFillColor = false;
662
        m_showStereotype = false;
663
664
    }

665
    m_resizable = true;
666
    m_fixedAspectRatio = false;
667

668
669
670
671
    m_startMove = false;
    m_activated = false;
    m_ignoreSnapToGrid = false;
    m_ignoreSnapComponentSizeToGrid = false;
672
    m_doc = UMLApp::app()->document();
673
    m_nPosX = 0;
674
    connect(m_scene, SIGNAL(sigClearAllSelected()), this, SLOT(slotClearAllSelected()));
675

Andi Fischer's avatar
Andi Fischer committed
676
677
678
679
    connect(m_scene, SIGNAL(sigFillColorChanged(Uml::ID::Type)), this, SLOT(slotFillColorChanged(Uml::ID::Type)));
    connect(m_scene, SIGNAL(sigLineColorChanged(Uml::ID::Type)), this, SLOT(slotLineColorChanged(Uml::ID::Type)));
    connect(m_scene, SIGNAL(sigTextColorChanged(Uml::ID::Type)), this, SLOT(slotTextColorChanged(Uml::ID::Type)));
    connect(m_scene, SIGNAL(sigLineWidthChanged(Uml::ID::Type)), this, SLOT(slotLineWidthChanged(Uml::ID::Type)));
680

681
    m_umlObject = 0;
682
683
684
685
686
687
688
689
690
691
692
693

    m_oldPos = QPointF();
    m_pressOffset = QPointF();
    m_oldW = 0;
    m_oldH = 0;

    m_shiftPressed = false;
    m_inMoveArea = false;
    m_inResizeArea = false;
    m_moved = false;
    m_resized = false;

694
    setZValue(2.0);  // default for most widgets
695
696
}

697
/**
698
699
700
 * This is usually called synchronously after menu.exec() and \a
 * trigger's parent is always the ListPopupMenu which can be used to
 * get the type of action of \a trigger.
701
 *
702
703
 * @note Subclasses can reimplement to handle specific actions and
 *       leave the rest to WidgetBase::slotMenuSelection.
704
 */
705
void UMLWidget::slotMenuSelection(QAction *trigger)
Andi Fischer's avatar
Andi Fischer committed
706
{
707
708
709
    if (!trigger) {
        return;
    }
710

711
    ListPopupMenu::MenuType sel = ListPopupMenu::typeFromAction(trigger);
Andi Fischer's avatar
Andi Fischer committed
712
    switch (sel) {
713
    case ListPopupMenu::mt_Delete:
714
        umlScene()->removeWidget(this);
715
        break;
716

717
    case ListPopupMenu::mt_Resize:
Ralf Habacker's avatar
Ralf Habacker committed
718
        umlScene()->resizeSelection();
719
720
        break;

721
    default:
722
723
        WidgetBase::slotMenuSelection(trigger);
        break;
724
725
726
    }
}

727
/**
728
729
 * Captures when another widget moves if this widget is linked to it.
 * @see sigWidgetMoved
730
731
732
 *
 * @param id The id of object behind the widget.
 */
Andi Fischer's avatar
Andi Fischer committed
733
void UMLWidget::slotWidgetMoved(Uml::ID::Type /*id*/)
Andi Fischer's avatar
Andi Fischer committed
734
735
{
}
736

737
738
739
740
741
/**
 * Captures a color change signal.
 *
 * @param sceneID The id of the object behind the widget.
 */
Andi Fischer's avatar
Andi Fischer committed
742
void UMLWidget::slotFillColorChanged(Uml::ID::Type viewID)
Andi Fischer's avatar
Andi Fischer committed
743
{
744
    //only change if on the diagram concerned
Ralf Habacker's avatar
Ralf Habacker committed
745
    if (m_scene->ID() != viewID) {
746
747
        return;
    }
748
    if (m_usesDiagramFillColor) {
749
        WidgetBase::setFillColor(m_scene->fillColor());
750
    }
751
    if (m_usesDiagramUseFillColor) {
752
        WidgetBase::setUseFillColor(m_scene->useFillColor());
753
754
755
756
    }
    update();
}

757
758
759
760
761
/**
 * Captures a text color change signal.
 *
 * @param sceneID The id of the object behind the widget.
 */
Andi Fischer's avatar
Andi Fischer committed
762
void UMLWidget::slotTextColorChanged(Uml::ID::Type viewID)
763
764
{
    //only change if on the diagram concerned
Ralf Habacker's avatar
Ralf Habacker committed
765
    if (m_scene->ID() != viewID)
766
        return;
767
    WidgetBase::setTextColor(m_scene->textColor());
768
769
770
    update();
}

771
772
773
774
775
776

/**
 * Captures a line color change signal.
 *
 * @param sceneID The id of the object behind the widget.
 */
Andi Fischer's avatar
Andi Fischer committed
777
void UMLWidget::slotLineColorChanged(Uml::ID::Type viewID)
778
779
{
    //only change if on the diagram concerned
Ralf Habacker's avatar
Ralf Habacker committed
780
    if (m_scene->ID() != viewID)
781
782
783
        return;

    if (m_usesDiagramLineColor) {
784
        WidgetBase::setLineColor(m_scene->lineColor());
785
786
787
788
    }
    update();
}

789
790
791
792
793
/**
 * Captures a linewidth change signal.
 *
 * @param sceneID The id of the object behind the widget.
 */
Andi Fischer's avatar
Andi Fischer committed
794
void UMLWidget::slotLineWidthChanged(Uml::ID::Type viewID)
Andi Fischer's avatar
Andi Fischer committed
795
{
796
    //only change if on the diagram concerned
Ralf Habacker's avatar
Ralf Habacker committed
797
    if (m_scene->ID() != viewID) {
798
799
        return;
    }
800
    if (m_usesDiagramLineWidth) {
801
        WidgetBase::setLineWidth(m_scene->lineWidth());
802
803
804
805
    }
    update();
}

806
807
808
809
810
/**
 * Set the status of using fill color.
 *
 * @param fc the status of using fill color.
 */
811
void UMLWidget::setUseFillColor(bool fc)
Andi Fischer's avatar
Andi Fischer committed
812
{
813
    WidgetBase::setUseFillColor(fc);
814
815
816
    update();
}

817
818
819
820
/**
 * Overrides the method from WidgetBase.
 */
void UMLWidget::setTextColorcmd(const QColor &color)
821
{
822
    WidgetBase::setTextColor(color);
823
824
825
    update();
}

826
827
828
829
/**
 * Overrides the method from WidgetBase.
 */
void UMLWidget::setTextColor(const QColor &color)
830
{
831
    UMLApp::app()->executeCommand(new CmdChangeTextColor(this, color));
832
833
834
    update();
}

835
836
837
838
/**
 * Overrides the method from WidgetBase.
 */
void UMLWidget::setLineColorcmd(const QColor &color)
Andi Fischer's avatar
Andi Fischer committed
839
{
840
    WidgetBase::setLineColor(color);
841
842
843
    update();
}

844
845
846
847
/**
 * Overrides the method from WidgetBase.
 */
void UMLWidget::setLineColor(const QColor &color)
Andi Fischer's avatar
Andi Fischer committed
848
{
849
    UMLApp::app()->executeCommand(new CmdChangeLineColor(this, color));
Mohamed-Amine Bouchikhi's avatar
Mohamed-Amine Bouchikhi committed
850
851
}

852
853
854
/**
 * Overrides the method from WidgetBase.
 */
Andi Fischer's avatar
Andi Fischer committed
855
856
void UMLWidget::setLineWidth(uint width)
{
857
858
859
860
    WidgetBase::setLineWidth(width);
    update();
}

861
862
863
864
865
/**
 * Sets the background fill color
 *
 * @param color the new fill color
 */
866
void UMLWidget::setFillColor(const QColor &color)
Andi Fischer's avatar
Andi Fischer committed
867
{
868
    UMLApp::app()->executeCommand(new CmdChangeFillColor(this, color));
Pierre Pettera's avatar
Pierre Pettera committed
869
870
}

871
872
873
874
875
/**
 * Sets the background fill color
 *
 * @param color the new fill color
 */
876
void UMLWidget::setFillColorcmd(const QColor &color)
Andi Fischer's avatar
Andi Fischer committed
877
{
878
    WidgetBase::setFillColor(color);
879
880
881
    update();
}

882
883
884
885
886
887
/**
 * Activate the object after serializing it from a QDataStream
 *
 * @param ChangeLog
 * @return  true for success
 */
Andi Fischer's avatar
Andi Fischer committed
888
889
bool UMLWidget::activate(IDChangeLog* /*ChangeLog  = 0 */)
{
890
891
892
    if (widgetHasUMLObject(m_baseType) && m_umlObject == NULL) {
        m_umlObject = m_doc->findObjectById(m_nId);
        if (m_umlObject == NULL) {
893
            uError() << "cannot find UMLObject with id=" << Uml::ID::toString(m_nId);
894
895
896
            return false;
        }
    }
897
    setFont(m_font);
898
    setSize(width(), height());
899
    m_activated = true;
900
    updateGeometry();
901
    if (m_scene->getPaste()) {
902
        FloatingTextWidget * ft = 0;
903
        QPointF point = m_scene->getPastePoint();
904
905
        int x = point.x() + this->x();
        int y = point.y() + this->y();
906
        if (m_scene->type() == Uml::DiagramType::Sequence) {
907
            switch (baseType()) {
908
909
            case WidgetBase::wt_Object:
            case WidgetBase::wt_Precondition :
910
                setY(this->y());
Andi Fischer's avatar
Andi Fischer committed
911
                setX(x);
912
                break;
913

914
            case WidgetBase::wt_Message:
915
                setY(this->y());
Andi Fischer's avatar
Andi Fischer committed
916
                setX(x);
917
918
                break;

919
            case WidgetBase::wt_Text:
Andi Fischer's avatar
Andi Fischer committed
920
                ft = static_cast<FloatingTextWidget *>(this);
921
                if (ft->textRole() == Uml::TextRole::Seq_Message) {
Andi Fischer's avatar
Andi Fischer committed
922
                    setX(x);
923
                    setY(this->y());
924
                } else {
925
926
                    setX(this->x());
                    setY(this->y());
927
928
929
930
                }
                break;

            default:
Andi Fischer's avatar
Andi Fischer committed
931
                setY(y);
932
933
934
935
                break;
            }//end switch base type
        }//end if sequence
        else {
Andi Fischer's avatar
Andi Fischer committed
936
937
            setX(x);
            setY(y);
938
939
940
        }
    }//end if pastepoint
    else {
941
942
        setX(this->x());
        setY(this->y());
943
    }
944
945
    if (m_scene->getPaste())
        m_scene->createAutoAssociations(this);
946
    updateGeometry();
947
    return true;
948
949
}

950
951
952
953
954
/**
 * Returns true if the Activate method has been called for this instance
 *
 * @return The activate status.
 */
955
bool UMLWidget::isActivated() const
Andi Fischer's avatar
Andi Fischer committed
956
{
957
    return m_activated;
958
959
}

960
961
962
963
964
/**
 * Set the m_activated flag of a widget but does not perform the Activate method
 *
 * @param Active Status of activation is to be set.
 */
965
void UMLWidget::setActivated(bool active /*=true*/)
Andi Fischer's avatar
Andi Fischer committed
966
{
967
    m_activated = active;
968
969
}

970
971
972
973
/**
 * Adds an already created association to the list of
 * associations that include this UMLWidget
 */
Andi Fischer's avatar
Andi Fischer committed
974
975
void UMLWidget::addAssoc(AssociationWidget* pAssoc)
{
976
977
978
979
980
    if (pAssoc && !m_Assocs.contains(pAssoc)) {
        m_Assocs.append(pAssoc);
    }
}

981
982
983
984
/**
 * Removes an already created association from the list of
 * associations that include this UMLWidget
 */
Andi Fischer's avatar
Andi Fischer committed
985
986
987
void UMLWidget::removeAssoc(AssociationWidget* pAssoc)
{
    if (pAssoc) {
988
        m_Assocs.removeAll(pAssoc);
989
990
991
    }
}

992
993
994
995
996
997
/**
 * Adjusts associations with the given co-ordinates
 *
 * @param x The x-coordinate.
 * @param y The y-coordinate.
 */
998
void UMLWidget::adjustAssocs(qreal dx, qreal dy)
999
1000
{
    // don't adjust Assocs on file load, as