umlscene.cpp 123 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.                                   *
 *                                                                         *
Oliver Kellogg's avatar
Oliver Kellogg committed
7
 *   copyright (C) 2002-2014                                               *
Ralf Habacker's avatar
Ralf Habacker committed
8
 *   Umbrello UML Modeller Authors <umbrello-devel@kde.org>                *
9
10
 ***************************************************************************/

11
// own header
12
#include "umlscene.h"
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

// application specific includes
#include "activitywidget.h"
#include "actorwidget.h"
#include "artifactwidget.h"
#include "association.h"
#include "associationwidget.h"
#include "assocrules.h"
#include "attribute.h"
#include "boxwidget.h"
#include "classifier.h"
#include "classifierwidget.h"
#include "classoptionspage.h"
#include "cmds.h"
#include "componentwidget.h"
Ralf Habacker's avatar
Ralf Habacker committed
28
#include "datatype.h"
29
#include "diagram_utils.h"
30
#include "pinportbase.h"
31
32
#include "datatypewidget.h"
#include "debug_utils.h"
33
#include "dialog_utils.h"
34
35
36
37
38
39
40
41
42
#include "docwindow.h"
#include "entity.h"
#include "entitywidget.h"
#include "enumwidget.h"
#include "floatingtextwidget.h"
#include "folder.h"
#include "foreignkeyconstraint.h"
#include "forkjoinwidget.h"
#include "idchangelog.h"
43
#include "import_utils.h"
44
#include "layoutgenerator.h"
45
#include "layoutgrid.h"
46
47
48
49
50
51
52
53
54
55
56
57
58
#include "listpopupmenu.h"
#include "messagewidget.h"
#include "model_utils.h"
#include "notewidget.h"
#include "object_factory.h"
#include "objectnodewidget.h"
#include "objectwidget.h"
#include "package.h"
#include "packagewidget.h"
#include "pinwidget.h"
#include "seqlinewidget.h"
#include "signalwidget.h"
#include "statewidget.h"
59
#include "toolbarstate.h"
60
#include "toolbarstatefactory.h"
61
62
63
#include "uml.h"
#include "umldoc.h"
#include "umldragdata.h"
64
#include "umlfiledialog.h"
65
66
67
68
69
70
71
72
73
74
#include "umllistview.h"
#include "umllistviewitem.h"
#include "umlobject.h"
#include "umlobjectlist.h"
#include "umlrole.h"
#include "umlview.h"
#include "umlviewimageexporter.h"
#include "umlwidget.h"
#include "uniqueid.h"
#include "widget_factory.h"
75
76
#include "widget_utils.h"
#include "widgetlist_utils.h"
77
78

//kde include files
79
#if QT_VERSION < 0x050000
80
#include <kfiledialog.h>
81
#include <kio/netaccess.h>
82
#endif
83
#include <KMessageBox>
84
#include <kcursor.h>
85
#include <KLocalizedString>
86
87

// include files for Qt
88
#include <QColor>
89
90
91
#include <QPainter>
#include <QPixmap>
#include <QPrinter>
92
93
#include <QString>
#include <QStringList>
94

95
// system includes
96
#include <cmath>  // for ceil
97
98

// static members
99
const qreal UMLScene::defaultCanvasSize = 5000;
100
bool UMLScene::m_showDocumentationIndicator = false;
101
102
103

using namespace Uml;

104
105
DEBUG_REGISTER(UMLScene)

106
107
108
109
110
111
112
113
114
115
116
/**
 * The class UMLScenePrivate is intended to hold private
 * members/classes to reduce the size of the public class
 * and to speed up recompiling.
 * The migration to this class is not complete yet.
 */
class UMLScenePrivate {
public:
    UMLScenePrivate() {}
};

117
/**
118
 * Constructor.
119
120
 */
UMLScene::UMLScene(UMLFolder *parentFolder, UMLView *view)
121
  : QGraphicsScene(0, 0, defaultCanvasSize, defaultCanvasSize),
Andi Fischer's avatar
Andi Fischer committed
122
123
    m_nLocalID(Uml::ID::None),
    m_nID(Uml::ID::None),
124
125
126
    m_Type(Uml::DiagramType::Undefined),
    m_Name(QString()),
    m_Documentation(QString()),
127
    m_Options(Settings::optionState()),
128
129
130
131
    m_bUseSnapToGrid(false),
    m_bUseSnapComponentSizeToGrid(false),
    m_isOpen(true),
    m_nCollaborationId(0),
132
133
    m_bCreateObject(false),
    m_bDrawSelectedOnly(false),
134
    m_bPaste(false),
135
    m_bStartedCut(false),
136
    m_d(new UMLScenePrivate),
137
138
139
140
    m_view(view),
    m_pFolder(parentFolder),
    m_pIDChangesLog(0),
    m_isActivated(false),
141
    m_bPopupShowing(false),
142
    m_autoIncrementSequence(false)
143
{
144
    m_PastePoint = QPointF(0, 0);
145

146
    m_pImageExporter = new UMLViewImageExporter(this);
147

148
    // setup signals
149
150
    connect(UMLApp::app(), SIGNAL(sigCutSuccessful()),
            this, SLOT(slotCutSuccessful()));
151
152
153
154
    // Create the ToolBarState factory. This class is not a singleton, because it
    // needs a pointer to this object.
    m_pToolBarStateFactory = new ToolBarStateFactory();
    m_pToolBarState = m_pToolBarStateFactory->getState(WorkToolBar::tbb_Arrow, this);
155
156
157

    m_doc = UMLApp::app()->document();

158
159
//    // settings for background
//    setBackgroundBrush(QColor(195, 195, 195));
Ralf Habacker's avatar
Ralf Habacker committed
160
    m_layoutGrid = new LayoutGrid(this);
161
162
163

    // fix crash caused by Qt stale item issue see https://bugs.kde.org/show_bug.cgi?id=383592
    setItemIndexMethod(NoIndex);
164
165
166
}

/**
167
 * Destructor.
168
169
170
171
 */
UMLScene::~UMLScene()
{
    delete m_pImageExporter;
Andi Fischer's avatar
Andi Fischer committed
172
173
174
    m_pImageExporter = 0;
    delete m_pIDChangesLog;
    m_pIDChangesLog = 0;
175
176
177
178
179
180
181
182
183
184

    // before we can delete the QCanvas, all widgets must be explicitly
    // removed
    // otherwise the implicit remove of the contained widgets will cause
    // events which would demand a valid connected QCanvas
    // ==> this causes umbrello to crash for some - larger?? - projects
    // first avoid all events, which would cause some update actions
    // on deletion of each removed widget
    blockSignals(true);
    removeAllWidgets();
185
186
187

    delete m_pToolBarStateFactory;
    m_pToolBarStateFactory = 0;
188
    delete m_layoutGrid;
189
    delete m_d;
190
191
}

192
193
194
/**
 * Return the UMLFolder in which this diagram lives.
 */
195
UMLFolder* UMLScene::folder() const
196
197
198
199
200
201
202
203
204
205
206
207
{
    return m_pFolder;
}

/**
 * Set the UMLFolder in which this diagram lives.
 */
void UMLScene::setFolder(UMLFolder *folder)
{
    m_pFolder = folder;
}

208
209
210
211
/**
 * Returns the active view associated with this scene.
 */
UMLView* UMLScene::activeView() const
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
{
    return m_view;
}

/**
 * Return the documentation of the diagram.
 */
QString UMLScene::documentation() const
{
    return m_Documentation;
}

/**
 * Set the documentation of the diagram.
 */
void UMLScene::setDocumentation(const QString &doc)
{
    m_Documentation = doc;
}

232
233
234
235
236
237
238
239
240
241
242
243
244
/**
 * Return the state of the auto increment sequence
 */
bool UMLScene::autoIncrementSequence() const
{
    return m_autoIncrementSequence;
}

void UMLScene::setAutoIncrementSequence(bool state)
{
    m_autoIncrementSequence = state;
}

245
246
247
248
/**
 * Return the next auto increment sequence value
 */
QString UMLScene::autoIncrementSequenceValue()
249
{
250
    int sequenceNumber = 0;
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
    if (type() == Uml::DiagramType::Sequence) {
        foreach (MessageWidget* message, messageList()) {
            bool ok;
            int value = message->sequenceNumber().toInt(&ok);
            if (ok && value > sequenceNumber)
               sequenceNumber = value;
        }
    }
    else if (type() == Uml::DiagramType::Collaboration) {
        foreach (AssociationWidget* assoc, associationList()) {
            bool ok;
            int value = assoc->sequenceNumber().toInt(&ok);
            if (ok && value > sequenceNumber)
               sequenceNumber = value;
        }
266
267
    }
    return QString::number(sequenceNumber + 1);
268
269
}

270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
/**
 * Return the name of the diagram.
 */
QString UMLScene::name() const
{
    return m_Name;
}

/**
 * Set the name of the diagram.
 */
void UMLScene::setName(const QString &name)
{
    m_Name = name;
}

286
287
288
/**
 * Returns the type of the diagram.
 */
Andi Fischer's avatar
Andi Fischer committed
289
DiagramType::Enum UMLScene::type() const
290
291
292
293
294
295
296
{
    return m_Type;
}

/**
 * Set the type of diagram.
 */
Andi Fischer's avatar
Andi Fischer committed
297
void UMLScene::setType(DiagramType::Enum type)
298
299
300
301
302
303
304
{
    m_Type = type;
}

/**
 * Returns the ID of the diagram.
 */
Andi Fischer's avatar
Andi Fischer committed
305
Uml::ID::Type UMLScene::ID() const
306
307
308
309
310
311
312
{
    return m_nID;
}

/**
 * Sets the ID of the diagram.
 */
Andi Fischer's avatar
Andi Fischer committed
313
void UMLScene::setID(Uml::ID::Type id)
314
315
316
317
318
319
320
{
    m_nID = id;
}

/**
 * Returns the position of the diagram.
 */
321
QPointF UMLScene::pos() const
322
323
324
325
326
327
328
{
    return m_Pos;
}

/**
 * Sets the position of the diagram.
 */
329
void UMLScene::setPos(const QPointF &pos)
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
{
    m_Pos = pos;
}

/**
 * Returns the fill color to use.
 */
const QColor& UMLScene::fillColor() const
{
    return m_Options.uiState.fillColor;
}

/**
 * Set the background color.
 *
 * @param color  The color to use.
 */
void UMLScene::setFillColor(const QColor &color)
{
    m_Options.uiState.fillColor = color;
Ralf Habacker's avatar
Ralf Habacker committed
350
    emit sigFillColorChanged(ID());
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
}

/**
 * Returns the line color to use.
 */
const QColor& UMLScene::lineColor() const
{
    return m_Options.uiState.lineColor;
}

/**
 * Sets the line color.
 *
 * @param color  The color to use.
 */
void UMLScene::setLineColor(const QColor &color)
{
    m_Options.uiState.lineColor = color;
Ralf Habacker's avatar
Ralf Habacker committed
369
    emit sigLineColorChanged(ID());
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
}

/**
 * Returns the line width to use.
 */
uint UMLScene::lineWidth() const
{
    return m_Options.uiState.lineWidth;
}

/**
 * Sets the line width.
 *
 * @param width  The width to use.
 */
void UMLScene::setLineWidth(uint width)
{
    m_Options.uiState.lineWidth = width;
Ralf Habacker's avatar
Ralf Habacker committed
388
    emit sigLineWidthChanged(ID());
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
}

/**
 * Returns the text color to use.
 */
const QColor& UMLScene::textColor() const
{
    return m_Options.uiState.textColor;
}

/**
 * Sets the text color.
 *
 * @param color  The color to use.
 */
404
void UMLScene::setTextColor(const QColor& color)
405
406
{
    m_Options.uiState.textColor = color;
Ralf Habacker's avatar
Ralf Habacker committed
407
    emit sigTextColorChanged(ID());
408
409
410
411
412
413
414
415
416
}

/**
 * return grid dot color
 *
 * @return Color
 */
const QColor& UMLScene::gridDotColor() const
{
417
    return m_layoutGrid->gridDotColor();
418
419
420
421
422
423
424
425
426
}

/**
 * set grid dot color
 *
 * @param color grid dot color
 */
void UMLScene::setGridDotColor(const QColor& color)
{
427
    m_Options.uiState.gridDotColor = color;
428
    m_layoutGrid->setGridDotColor(color);
429
430
431
432
433
}

/**
 * Returns the options being used.
 */
434
Settings::OptionState& UMLScene::optionState()
435
436
437
438
439
440
441
{
    return m_Options;
}

/**
 * Sets the options to be used.
 */
442
void UMLScene::setOptionState(const Settings::OptionState& options)
443
444
{
    m_Options = options;
445
446
    setBackgroundBrush(options.uiState.backgroundColor);
    setGridDotColor(options.uiState.gridDotColor);
447
448
449
450
451
}

/**
 * Returns a reference to the association list.
 */
452
const AssociationWidgetList UMLScene::associationList() const
453
{
454
455
456
457
458
459
460
    AssociationWidgetList result;
    foreach(QGraphicsItem *item, items()) {
        AssociationWidget *w = dynamic_cast<AssociationWidget*>(item);
        if (w)
            result.append(w);
    }
    return result;
461
462
463
464
465
}

/**
 * Returns a reference to the widget list.
 */
466
const UMLWidgetList UMLScene::widgetList() const
467
{
468
469
470
471
472
473
474
475
476
477
478
479
480
    UMLWidgetList result;
    foreach(QGraphicsItem *item, items()) {
        UMLWidget *w = dynamic_cast<UMLWidget*>(item);
        if (w && !w->isMessageWidget() && !w->isAssociationWidget())
            result.append(w);
    }
    return result;
}

void UMLScene::addWidgetCmd(UMLWidget* widget)
{
    Q_ASSERT(0 != widget);
    addItem(widget);
481
482
}

483
void UMLScene::addWidgetCmd(AssociationWidget* widget)
484
485
{
    Q_ASSERT(0 != widget);
486
    addItem(widget);
487
488
}

489
490
491
/**
 * Returns a reference to the message list.
 */
492
const MessageWidgetList UMLScene::messageList() const
493
{
494
495
496
497
498
499
500
    MessageWidgetList result;
    foreach(QGraphicsItem *item, items()) {
        MessageWidget *w = dynamic_cast<MessageWidget*>(item);
        if (w)
            result.append(w);
    }
    return result;
501
502
}

503
504
505
506
507
508
509
510
/**
 * Used for creating unique name of collaboration messages.
 */
int UMLScene::generateCollaborationId()
{
    return ++m_nCollaborationId;
}

511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
/**
 * Returns the open state.
 * @return   when true diagram is shown to the user
 */
bool UMLScene::isOpen() const
{
    return m_isOpen;
}

/**
 * Sets the flag 'isOpen'.
 * @param isOpen   flag indicating that the diagram is shown to the user
 */
void UMLScene::setIsOpen(bool isOpen)
{
    m_isOpen = isOpen;
}

529
/**
530
 * Contains the implementation for printing functionality.
531
532
533
 */
void UMLScene::print(QPrinter *pPrinter, QPainter & pPainter)
{
534
    bool isFooter = optionState().generalState.footerPrinting;
535
536
537
538
539

    // The printer will probably use a different font with different font metrics,
    // force the widgets to update accordingly on paint
    forceUpdateWidgetFontMetrics(&pPainter);

540
    QRectF source = diagramRect();
Ralf Habacker's avatar
Ralf Habacker committed
541
    QRect paper = pPrinter->paperRect();
542
543
544
545
546
    QRect page = pPrinter->pageRect();

    // use the painter font metrics, not the screen fm!
    QFontMetrics fm = pPainter.fontMetrics(); 
    int fontHeight  = fm.lineSpacing();
547

Ralf Habacker's avatar
Ralf Habacker committed
548
549
550
551
552
    if (paper == page) {
        QSize margin = page.size() * 0.025;
        page.adjust(margin.width(), margin.height(), -margin.width(), -margin.height());
    }

553
    if (isFooter) {
554
        int margin = 3 + 3 * fontHeight;
Ralf Habacker's avatar
Ralf Habacker committed
555
        page.adjust(0, 0, 0, -margin);
556
557
    }

558
    getDiagram(pPainter, QRectF(source), QRectF(page));
559

560
    //draw foot note
561
    if (isFooter) {
Ralf Habacker's avatar
Ralf Habacker committed
562
        page.adjust(0, 0, 0, fontHeight);
563
564
565
        QString string = i18n("Diagram: %2 Page %1", 1, name());
        QColor textColor(50, 50, 50);
        pPainter.setPen(textColor);
566
567
        pPainter.drawLine(page.left(), page.bottom()    , page.right(), page.bottom());
        pPainter.drawText(page.left(), page.bottom() + 3, page.right(), 2*fontHeight, Qt::AlignLeft, string);
568
569
570
571
572
573
574
575
576
577
    }

    // next painting will most probably be to a different device (i.e. the screen)
    forceUpdateWidgetFontMetrics(0);
}

/**
 * Initialize and announce a newly created widget.
 * Auxiliary to contentsMouseReleaseEvent().
 */
578
void UMLScene::setupNewWidget(UMLWidget *w, bool setPosition)
579
{
580
581
582
583
584
585
586
    if (setPosition &&
        (!w->isPinWidget()) &&
        (!w->isPortWidget()) &&
        (!w->isObjectWidget())) {
        // ObjectWidget's position is handled by the widget
        w->setX(m_Pos.x());
        w->setY(m_Pos.y());
587
    }
588
    w->setVisible(true);
589
    w->activate();
590
    w->setFontCmd(font());
Ralf Habacker's avatar
Ralf Habacker committed
591
592
593
    w->slotFillColorChanged(ID());
    w->slotTextColorChanged(ID());
    w->slotLineWidthChanged(ID());
594
    resizeSceneToItems();
595
596
    m_doc->setModified();

597
    if (m_doc->loading()) {  // do not emit signals while loading
598
        addWidgetCmd(w);
599
600
601
602
        // w->activate();  // will be done by UMLDoc::activateAllViews() after loading
    } else {
        UMLApp::app()->executeCommand(new CmdCreateWidget(w));
    }
603
604
}

605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
/**
 * Return whether we are currently creating an object.
 */
bool UMLScene::getCreateObject() const
{
    return m_bCreateObject;
}

/**
 * Set whether we are currently creating an object.
 */
void UMLScene::setCreateObject(bool bCreate)
{
    m_bCreateObject = bCreate;
}

621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
/**
 * Overrides the standard operation.
 */
void UMLScene::showEvent(QShowEvent* /*se*/)
{
    connect(m_doc, SIGNAL(sigObjectCreated(UMLObject*)),
            this, SLOT(slotObjectCreated(UMLObject*)));
    connect(this, SIGNAL(sigAssociationRemoved(AssociationWidget*)),
            UMLApp::app()->docWindow(), SLOT(slotAssociationRemoved(AssociationWidget*)));
    connect(this, SIGNAL(sigWidgetRemoved(UMLWidget*)),
            UMLApp::app()->docWindow(), SLOT(slotWidgetRemoved(UMLWidget*)));
}

/**
 * Overrides the standard operation.
 */
void UMLScene::hideEvent(QHideEvent* /*he*/)
{
    disconnect(m_doc, SIGNAL(sigObjectCreated(UMLObject*)), this, SLOT(slotObjectCreated(UMLObject*)));
    disconnect(this, SIGNAL(sigAssociationRemoved(AssociationWidget*)),
               UMLApp::app()->docWindow(), SLOT(slotAssociationRemoved(AssociationWidget*)));
    disconnect(this, SIGNAL(sigWidgetRemoved(UMLWidget*)),
               UMLApp::app()->docWindow(), SLOT(slotWidgetRemoved(UMLWidget*)));
644
645
646
647
648
649
650
651
652
653
654
}

/**
 * Changes the current tool to the selected tool.
 * The current tool is cleaned and the selected tool initialized.
 */
void UMLScene::slotToolBarChanged(int c)
{
    m_pToolBarState->cleanBeforeChange();
    m_pToolBarState = m_pToolBarStateFactory->getState((WorkToolBar::ToolBar_Buttons)c, this);
    m_pToolBarState->init();
655

656
    m_bPaste = false;
657
658
}

Andi Fischer's avatar
Andi Fischer committed
659
660
661
662
/**
 * Slot called when an object is created.
 * @param o   created UML object
 */
663
664
void UMLScene::slotObjectCreated(UMLObject* o)
{
665
    DEBUG(DBG_SRC) << "scene=" << name() << " / object=" << o->name();
666
667
668
669
670
671
672
673
674
675
    m_bPaste = false;
    //check to see if we want the message
    //may be wanted by someone else e.g. list view

    if (!m_bCreateObject) {
        return;
    }

    UMLWidget* newWidget = Widget_Factory::createWidget(this, o);

676
    if (!newWidget) {
677
        return;
678
    }
679

680
    setupNewWidget(newWidget);
681
682

    m_bCreateObject = false;
683
684
685
686
687
688
689
690
691
692
693
694
695
696

    switch (o->baseType()) {
        case UMLObject::ot_Actor:
        case UMLObject::ot_UseCase:
        case UMLObject::ot_Class:
        case UMLObject::ot_Package:
        case UMLObject::ot_Component:
        case UMLObject::ot_Node:
        case UMLObject::ot_Artifact:
        case UMLObject::ot_Interface:
        case UMLObject::ot_Enum:
        case UMLObject::ot_Entity:
        case UMLObject::ot_Datatype:
        case UMLObject::ot_Category:
697
        case UMLObject::ot_Instance:
698
699
700
701
            createAutoAssociations(newWidget);
            // We need to invoke createAutoAttributeAssociations()
            // on all other widgets again because the newly created
            // widget might saturate some latent attribute assocs.
702
            createAutoAttributeAssociations2(newWidget);
703
704
705
706
            break;
        default:
            break;
    }
707
    resizeSceneToItems();
708
709
}

Andi Fischer's avatar
Andi Fischer committed
710
711
712
713
/**
 * Slot called when an object is removed.
 * @param o   removed UML object
 */
714
715
716
void UMLScene::slotObjectRemoved(UMLObject * o)
{
    m_bPaste = false;
Andi Fischer's avatar
Andi Fischer committed
717
    Uml::ID::Type id = o->id();
718

719
    foreach(UMLWidget* obj, widgetList()) {
720
721
722
723
724
725
726
727
728
729
        if (obj->id() != id)
            continue;
        removeWidget(obj);
        break;
    }
}

/**
 * Override standard method.
 */
730
void UMLScene::dragEnterEvent(QGraphicsSceneDragDropEvent *e)
731
732
733
{
    UMLDragData::LvTypeAndID_List tidList;
    if (!UMLDragData::getClip3TypeAndID(e->mimeData(), tidList)) {
Oliver Kellogg's avatar
Oliver Kellogg committed
734
        DEBUG(DBG_SRC) << "UMLDragData::getClip3TypeAndID returned false";
735
736
737
738
739
740
741
742
743
744
        return;
    }
    UMLDragData::LvTypeAndID_It tidIt(tidList);
    UMLDragData::LvTypeAndID * tid;
    if (!tidIt.hasNext()) {
        DEBUG(DBG_SRC) << "UMLDragData::getClip3TypeAndID returned empty list";
        return;
    }
    tid = tidIt.next();
    UMLListViewItem::ListViewType lvtype = tid->type;
Andi Fischer's avatar
Andi Fischer committed
745
    Uml::ID::Type id = tid->id;
746

Andi Fischer's avatar
Andi Fischer committed
747
    DiagramType::Enum diagramType = type();
748
749
750
751
752
753
754
755

    UMLObject* temp = 0;
    //if dragging diagram - might be a drag-to-note
    if (Model_Utils::typeIsDiagram(lvtype)) {
        e->accept();
        return;
    }
    //can't drag anything onto state/activity diagrams
Andi Fischer's avatar
Andi Fischer committed
756
    if (diagramType == DiagramType::State || diagramType == DiagramType::Activity) {
757
758
759
760
761
        e->ignore();
        return;
    }
    //make sure can find UMLObject
    if (!(temp = m_doc->findObjectById(id))) {
762
        DEBUG(DBG_SRC) << "object " << Uml::ID::toString(id) << " not found";
763
764
765
        e->ignore();
        return;
    }
766
    bool bAccept = Model_Utils::typeIsAllowedInDiagram(temp, this);
767
768
769
770
771
772
773
774
775
776
    if (bAccept) {
        e->accept();
    } else {
        e->ignore();
    }
}

/**
 * Override standard method.
 */
777
void UMLScene::dragMoveEvent(QGraphicsSceneDragDropEvent* e)
778
779
780
781
782
783
784
{
    e->accept();
}

/**
 * Override standard method.
 */
785
void UMLScene::dropEvent(QGraphicsSceneDragDropEvent *e)
786
787
788
{
    UMLDragData::LvTypeAndID_List tidList;
    if (!UMLDragData::getClip3TypeAndID(e->mimeData(), tidList)) {
789
        DEBUG(DBG_SRC) << "UMLDragData::getClip3TypeAndID returned error";
790
791
792
793
794
795
796
797
798
799
        return;
    }
    UMLDragData::LvTypeAndID_It tidIt(tidList);
    UMLDragData::LvTypeAndID * tid;
    if (!tidIt.hasNext()) {
        DEBUG(DBG_SRC) << "UMLDragData::getClip3TypeAndID returned empty list";
        return;
    }
    tid = tidIt.next();
    UMLListViewItem::ListViewType lvtype = tid->type;
Andi Fischer's avatar
Andi Fischer committed
800
    Uml::ID::Type id = tid->id;
801
802
803
804

    if (Model_Utils::typeIsDiagram(lvtype)) {
        bool breakFlag = false;
        UMLWidget* w = 0;
805
        foreach(w, widgetList()) {
806
            if (w->isNoteWidget() && w->onWidget(e->scenePos())) {
807
808
809
810
811
812
813
814
815
816
817
818
                breakFlag = true;
                break;
            }
        }
        if (breakFlag) {
            NoteWidget *note = static_cast<NoteWidget*>(w);
            note->setDiagramLink(id);
        }
        return;
    }
    UMLObject* o = m_doc->findObjectById(id);
    if (!o) {
819
        DEBUG(DBG_SRC) << "object id=" << Uml::ID::toString(id) << " not found";
820
821
        return;
    }
822

823
    m_Pos = e->scenePos();
824

825
826
827
828
    UMLWidget* newWidget = Widget_Factory::createWidget(this, o);
    if (!newWidget) {
        return;
    }
829

830
    setupNewWidget(newWidget);
831
832
    createAutoAssociations(newWidget);
    createAutoAttributeAssociations2(newWidget);
833
834
}

835
836
837
838
/**
 * Overrides the standard operation.
 * Calls the same method in the current tool bar state.
 */
839
void UMLScene::mouseMoveEvent(QGraphicsSceneMouseEvent* ome)
840
841
842
843
844
845
846
847
{
    m_pToolBarState->mouseMove(ome);
}

/**
 * Override standard method.
 * Calls the same method in the current tool bar state.
 */
848
void UMLScene::mousePressEvent(QGraphicsSceneMouseEvent* event)
849
{
850
    if (event->button() != Qt::LeftButton) {
851
852
853
854
        event->ignore();
        return;
    }

855
856
857
858
859
860
861
862
863
864
    m_pToolBarState->mousePress(event);

    //TODO should be managed by widgets when are selected. Right now also has some
    //problems, such as clicking on a widget, and clicking to move that widget shows
    //documentation of the diagram instead of keeping the widget documentation.
    //When should diagram documentation be shown? When clicking on an empty
    //space in the diagram with arrow tool?
    UMLWidget* widget = widgetAt(event->scenePos());
    if (widget) {
        DEBUG(DBG_SRC) << "widget = " << widget->name() << " / type = " << widget->baseTypeStr();
865
        UMLApp::app()->docWindow()->showDocumentation(widget);
866
        event->accept();
867
868
869
870
    }
    else {
        AssociationWidget* association = associationAt(event->scenePos());
        if (association) {
871
            DEBUG(DBG_SRC) << "association widget = " << association->name() << " / type = " << association->baseTypeStr();
872
            // the following is done in AssociationWidget::setSelected()
873
            // UMLApp::app()->docWindow()->showDocumentation(association, true);
874
            // event->accept();
875
876
        }
        //:TODO: else if (clicking on other elements with documentation) {
877
        //:TODO: UMLApp::app()->docWindow()->showDocumentation(umlObject, true);
878
879
        else {
            // clicking on an empty space in the diagram with arrow tool
880
            UMLApp::app()->docWindow()->showDocumentation(this);
881
882
883
884
885
886
887
888
889
            event->accept();
        }
    }
}

/**
 * Override standard method.
 * Calls the same method in the current tool bar state.
 */
890
void UMLScene::mouseDoubleClickEvent(QGraphicsSceneMouseEvent* event)
891
{
892
893
    if (!m_doc->loading())
        m_pToolBarState->mouseDoubleClick(event);
894
    if (!event->isAccepted()) {
895
        // show properties dialog of the scene
896
        if (m_view->showPropertiesDialog() == true) {
897
898
            m_doc->setModified();
        }
899
        event->accept();
900
901
902
903
904
905
906
    }
}

/**
 * Overrides the standard operation.
 * Calls the same method in the current tool bar state.
 */
907
void UMLScene::mouseReleaseEvent(QGraphicsSceneMouseEvent* ome)
908
909
910
911
{
    m_pToolBarState->mouseRelease(ome);
}

912
913
914
915
/**
 * Determine whether on a sequence diagram we have clicked on a line
 * of an Object.
 *
916
 * @return The widget owning the line which was clicked.
917
918
 *  Returns 0 if no line was clicked on.
 */
919
ObjectWidget * UMLScene::onWidgetLine(const QPointF &point) const
920
{
921
    foreach(UMLWidget* obj, widgetList()) {
922
        ObjectWidget *ow = obj->asObjectWidget();
923
        if (ow == 0)
924
            continue;
925
        SeqLineWidget *pLine = ow->sequentialLine();
926
        if (pLine == 0) {
927
            uError() << "SeqLineWidget of " << ow->name()
928
            << " (id=" << Uml::ID::toString(ow->localID()) << ") is NULL";
929
930
931
932
933
934
935
936
937
938
939
940
            continue;
        }
        if (pLine->onWidget(point))
            return ow;
    }
    return 0;
}

/**
 * Determine whether on a sequence diagram we have clicked on
 * the destruction box of an Object.
 *
941
 * @return The widget owning the destruction box which was clicked.
942
943
 *  Returns 0 if no destruction box was clicked on.
 */
944
ObjectWidget * UMLScene::onWidgetDestructionBox(const QPointF &point) const
945
{
946
    foreach(UMLWidget* obj, widgetList()) {
947
        ObjectWidget *ow = obj->asObjectWidget();
948
        if (ow == 0)
949
            continue;
950
        SeqLineWidget *pLine = ow->sequentialLine();
951
        if (pLine == 0) {
952
            uError() << "SeqLineWidget of " << ow->name()
953
                     << " (id=" << Uml::ID::toString(ow->localID()) << ") is NULL";
954
955
956
957
958
959
960
961
            continue;
        }
        if (pLine->onDestructionBox(point))
            return ow;
    }
    return 0;
}

962
963
964
965
966
/**
 * Return pointer to the first selected widget (for multi-selection)
 */
UMLWidget* UMLScene::getFirstMultiSelectedWidget() const
{
967
    if (selectedWidgets().size() == 0)
Ralf Habacker's avatar
Ralf Habacker committed
968
        return 0;
969
    return selectedWidgets().first();
970
971
}

972
973
974
975
976
/**
 * Tests the given point against all widgets and returns the
 * widget for which the point is within its bounding rectangle.
 * In case of multiple matches, returns the smallest widget.
 * Returns NULL if the point is not inside any widget.
977
 * TODO: What about using QGraphicsScene::items(...)?
978
 */
979
UMLWidget* UMLScene::widgetAt(const QPointF& p)
980
{
981
    return dynamic_cast<UMLWidget*>(itemAt(p));
982
983
}

984
985
/**
 * Tests the given point against all associations and returns the
986
 * association widget for which the point is on the line.
987
 * Returns NULL if the point is not inside any association.
988
 * CHECK: This is the same method as in ToolBarState.
989
 */
990
AssociationWidget* UMLScene::associationAt(const QPointF& p)
991
{
992
993
994
995
996
997
    foreach (AssociationWidget* association, associationList()) {
        if (association->onAssociation(p)) {
            return association;
        }
    }
    return 0;
998
999
}

1000
1001
1002
1003
1004
/**
 * Tests the given point against all associations and returns the
 * association widget for which the point is on the line.
 * Returns NULL if the point is not inside any association.
 */
1005
MessageWidget* UMLScene::messageAt(const QPointF& p)
1006
1007
1008
1009
1010
1011
1012
1013
1014
{
    foreach(MessageWidget *message, messageList()) {
        if (message->onWidget(p)) {
            return message;
        }
    }
    return 0;
}

1015
1016
1017
1018
1019
1020
/**
 * Sees if a message is relevant to the given widget.  If it does delete it.
 * @param w The widget to check messages against.
 */
void UMLScene::checkMessages(ObjectWidget * w)
{
Andi Fischer's avatar
Andi Fischer committed
1021
    if (type() != DiagramType::Sequence) {
1022
        return;
Andi Fischer's avatar
Andi Fischer committed
1023
    }
1024

1025
    foreach(MessageWidget *obj, messageList()) {
1026
        if (obj->hasObjectWidget(w)) {
1027
            removeWidgetCmd(obj);
1028
        }
1029
1030
1031
1032
1033
1034
1035
1036
    }
}

/**
 * Returns whether a widget is already on the diagram.
 *
 * @param id The id of the widget to check for.
 *
1037
 * @return Returns pointer to the widget if it is on the diagram, NULL if not.
1038
 */
1039
UMLWidget* UMLScene::widgetOnDiagram(Uml::ID::Type id)
1040
{
1041
    foreach(UMLWidget *obj, widgetList()) {
Ralf Habacker's avatar
Ralf Habacker committed
1042
1043
        if (!obj)
            continue;
1044
1045
1046
        UMLWidget* w = obj->widgetWithID(id);
        if (w)
            return w;
1047
1048
    }

1049
    foreach(UMLWidget *obj, messageList()) {
1050
1051
        // CHECK: Should MessageWidget reimplement widgetWithID() ?
        //       If yes then we should use obj->widgetWithID(id) here too.
1052
        if (id == obj->id())
1053
            return obj;
1054
1055
    }

1056
    return 0;
1057
1058
1059
1060
}

/**
 * Finds a widget with the given ID.
Andi Fischer's avatar
Andi Fischer committed
1061
 * Search both our UMLWidget AND MessageWidget lists.
1062
1063
1064
1065
 * @param id The ID of the widget to find.
 *
 * @return Returns the widget found, returns 0 if no widget found.
 */
Andi Fischer's avatar
Andi Fischer committed
1066
UMLWidget * UMLScene::findWidget(Uml::ID::Type id)
1067
{
1068
    foreach(UMLWidget* obj, widgetList()) {
Ralf Habacker's avatar
Ralf Habacker committed
1069
1070
        if (!obj)
            continue;
1071
1072
1073
        UMLWidget* w = obj->widgetWithID(id);
        if (w) {
            return w;
1074
1075
1076
        }
    }

1077
    foreach(UMLWidget* obj, messageList()) {
1078
1079
1080
1081
        // CHECK: Should MessageWidget reimplement widgetWithID() ?
        //       If yes then we should use obj->widgetWithID(id) here too.
        if (obj->localID() == id ||
            obj->id() == id)
1082
1083
1084
            return obj;
    }

1085
1086
1087
    return 0;
}

1088
1089
1090
1091
1092
1093
1094
/**
 * Finds an association widget with the given ID.
 *
 * @param id The ID of the widget to find.
 *
 * @return Returns the widget found, returns 0 if no widget found.
 */
Andi Fischer's avatar
Andi Fischer committed
1095
AssociationWidget * UMLScene::findAssocWidget(Uml::ID::Type id)
1096
{
1097
    foreach(AssociationWidget* obj, associationList()) {
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
        UMLAssociation* umlassoc = obj->association();
        if (umlassoc && umlassoc->id() == id) {
            return obj;
        }
    }
    return 0;
}

/**
 * Finds an association widget with the given widgets and the given role B name.
 * Considers the following association types:
 *  at_Association, at_UniAssociation, at_Composition, at_Aggregation
 * This is used for seeking an attribute association.
 *
 * @param pWidgetA  Pointer to the UMLWidget of role A.
 * @param pWidgetB  Pointer to the UMLWidget of role B.
 * @param roleNameB Name at the B side of the association (the attribute name)
 *
 * @return Returns the widget found, returns 0 if no widget found.
 */
AssociationWidget * UMLScene::findAssocWidget(UMLWidget *pWidgetA,
1119
                                              UMLWidget *pWidgetB, const QString& roleNameB)
1120
{
1121
    foreach(AssociationWidget* assoc, associationList()) {
1122
        const Uml::AssociationType::Enum testType = assoc->associationType();
1123
1124
1125
1126
        if (testType != Uml::AssociationType::Association &&
                testType != Uml::AssociationType::UniAssociation &&
                testType != Uml::AssociationType::Composition &&
                testType != Uml::AssociationType::Aggregation &&
1127
                testType != Uml::AssociationType::Relationship) {
1128
            continue;
1129
1130
        }

1131
1132
1133
        if (pWidgetA->id() == assoc->widgetIDForRole(Uml::RoleType::A) &&
                pWidgetB->id() == assoc->widgetIDForRole(Uml::RoleType::B) &&
                assoc->roleName(Uml::RoleType::B) == roleNameB) {
1134
            return assoc;
1135
        }
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
    }
    return 0;
}

/**
 * Finds an association widget with the given type and widgets.
 *
 * @param at  The AssociationType of the widget to find.
 * @param pWidgetA Pointer to the UMLWidget of role A.
 * @param pWidgetB Pointer to the UMLWidget of role B.
 *
 * @return Returns the widget found, returns 0 if no widget found.
 */
1149
AssociationWidget * UMLScene::findAssocWidget(AssociationType::Enum at,
1150
                                              UMLWidget *pWidgetA, UMLWidget *pWidgetB)
1151
{
1152
    foreach(AssociationWidget* assoc, associationList()) {
1153
        Uml::AssociationType::Enum testType = assoc->associationType();
1154
        if (testType != at) {
1155
            continue;
1156
1157
        }

1158
1159
        if (pWidgetA->id() == assoc->widgetIDForRole(Uml::RoleType::A) &&
                pWidgetB->id() == assoc->widgetIDForRole(Uml::RoleType::B)) {
1160
            return assoc;
1161
        }
1162
1163
1164
1165
1166
    }
    return 0;
}

/**
1167
 * Remove a widget from view (undo command)
1168
1169
1170
1171
 *
 * @param o  The widget to remove.
 */
void UMLScene::removeWidget(UMLWidget * o)
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
{
    UMLApp::app()->executeCommand(new CmdRemoveWidget(o));
}

/**
 * Remove a widget from view.
 *
 * @param o  The widget to remove.
 */
void UMLScene::removeWidgetCmd(UMLWidget * o)
1182
1183
1184
1185
1186
1187
1188
1189
{
    if (!o)
        return;

    emit sigWidgetRemoved(o);

    removeAssociations(o);

1190
1191
    removeOwnedWidgets(o);

1192
    WidgetBase::WidgetType t = o->baseType();
Andi Fischer's avatar
Andi Fischer committed
1193
    if (type() == DiagramType::Sequence && t == WidgetBase::wt_Object) {
1194
        checkMessages(static_cast<ObjectWidget*>(o));
Andi Fischer's avatar
Andi Fischer committed
1195
    }
1196
1197

    o->cleanup();
1198
    o->setSelectedFlag(false);
Andi Fischer's avatar
Andi Fischer committed
1199
1200
1201
    disconnect(this, SIGNAL(sigFillColorChanged(Uml::ID::Type)), o, SLOT(slotFillColorChanged(Uml::ID::Type)));
    disconnect(this, SIGNAL(sigLineColorChanged(Uml::ID::Type)), o, SLOT(slotLineColorChanged(Uml::ID::Type)));
    disconnect(this, SIGNAL(sigTextColorChanged(Uml::ID::Type)), o, SLOT(slotTextColorChanged(Uml::ID::Type)));
1202
    removeItem(o);
1203
    o->deleteLater();
1204
    m_doc->setModified(true);
1205
1206
}

1207
1208
1209
1210
1211
1212
1213
/**
 * Remove all widgets that have given widget as owner.
 *
 * @param o The owner widget that will be removed.
 */
void UMLScene::removeOwnedWidgets(UMLWidget* o)
{
1214
1215
    foreach(QGraphicsItem* item, o->childItems()) {
        UMLWidget* widget = dynamic_cast<UMLWidget*>(item);
1216
1217
1218
        if ((widget != 0) &&
            (widget->isPinWidget() ||
             widget->isPortWidget())) {
1219
            removeWidgetCmd(widget);
1220
1221
1222
1223
        }
    }
}

1224
1225
1226
1227
1228
/**
 * Returns background color
 */
const QColor& UMLScene::backgroundColor() const
{
1229
    return backgroundBrush().color();
1230
1231
}

1232
1233
1234
1235
1236
1237
1238