umlscene.cpp 124 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

// 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"
26
#include "component.h"
27
28
#include "cmds.h"
#include "componentwidget.h"
Ralf Habacker's avatar
Ralf Habacker committed
29
#include "datatype.h"
30
#include "diagram_utils.h"
31
#include "pinportbase.h"
32
33
#include "datatypewidget.h"
#include "debug_utils.h"
34
#include "dialog_utils.h"
35
36
37
38
39
40
41
42
43
#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"
44
#include "import_utils.h"
45
#include "layoutgenerator.h"
46
#include "layoutgrid.h"
47
48
49
50
51
52
53
54
55
#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"
56
#include "portwidget.h"
57
58
59
#include "seqlinewidget.h"
#include "signalwidget.h"
#include "statewidget.h"
60
#include "toolbarstate.h"
61
#include "toolbarstatefactory.h"
62
63
64
#include "uml.h"
#include "umldoc.h"
#include "umldragdata.h"
65
#include "umlfiledialog.h"
66
67
68
69
70
#include "umllistview.h"
#include "umllistviewitem.h"
#include "umlobject.h"
#include "umlobjectlist.h"
#include "umlrole.h"
71
#include "umlscenepopupmenu.h"
72
73
74
75
76
#include "umlview.h"
#include "umlviewimageexporter.h"
#include "umlwidget.h"
#include "uniqueid.h"
#include "widget_factory.h"
77
78
#include "widget_utils.h"
#include "widgetlist_utils.h"
79
80

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

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

97
// system includes
98
#include <cmath>  // for ceil
99
100

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

using namespace Uml;

106
107
DEBUG_REGISTER(UMLScene)

108
109
110
111
112
113
114
115
/**
 * 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:
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
    UMLScenePrivate(UMLScene *parent)
    : p(parent)
    {
    }

    /**
     * Check if there is a corresponding port widget
     * for all UMLPort instances and add if not.
     */
    void fixupPorts()
    {
        UMLWidgetList ports;
        UMLWidgetList components;

        foreach(UMLWidget *w, p->widgetList()) {
            if (w->isPortWidget())
                ports.append(w);
            else if (w->isComponentWidget())
                components.append(w);
        }

        foreach(UMLWidget *cw, components) {
            UMLComponent *c = cw->umlObject()->asUMLComponent();
            if (!c)
                continue;
            // iterate through related ports for this component widget
            foreach(UMLObject *o, c->containedObjects()) {
                UMLPort *up = o->asUMLPort();
                if (!up)
                    continue;
                Uml::ID::Type id = o->id();
                bool found = false;
                foreach(UMLWidget *p, ports) {
                    if (p->id() == id) {
                        found = true;
                        break;
                    }
                }
                if (!found)
                    new PortWidget(p, up, cw);
            }
        }
    }
    UMLScene *p;
160
161
};

162
/**
163
 * Constructor.
164
165
 */
UMLScene::UMLScene(UMLFolder *parentFolder, UMLView *view)
166
  : QGraphicsScene(0, 0, defaultCanvasSize, defaultCanvasSize),
Andi Fischer's avatar
Andi Fischer committed
167
168
    m_nLocalID(Uml::ID::None),
    m_nID(Uml::ID::None),
169
170
171
    m_Type(Uml::DiagramType::Undefined),
    m_Name(QString()),
    m_Documentation(QString()),
172
    m_Options(Settings::optionState()),
173
174
175
176
    m_bUseSnapToGrid(false),
    m_bUseSnapComponentSizeToGrid(false),
    m_isOpen(true),
    m_nCollaborationId(0),
177
178
    m_bCreateObject(false),
    m_bDrawSelectedOnly(false),
179
    m_bPaste(false),
180
    m_bStartedCut(false),
181
    m_d(new UMLScenePrivate(this)),
182
183
184
185
    m_view(view),
    m_pFolder(parentFolder),
    m_pIDChangesLog(0),
    m_isActivated(false),
186
    m_bPopupShowing(false),
187
    m_autoIncrementSequence(false)
188
{
189
    m_PastePoint = QPointF(0, 0);
190

191
    m_pImageExporter = new UMLViewImageExporter(this);
192

193
    // setup signals
194
195
    connect(UMLApp::app(), SIGNAL(sigCutSuccessful()),
            this, SLOT(slotCutSuccessful()));
196
197
198
199
    // 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);
200
201
202

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

203
204
//    // settings for background
//    setBackgroundBrush(QColor(195, 195, 195));
Ralf Habacker's avatar
Ralf Habacker committed
205
    m_layoutGrid = new LayoutGrid(this);
206
207
208

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

/**
212
 * Destructor.
213
214
215
216
 */
UMLScene::~UMLScene()
{
    delete m_pImageExporter;
Andi Fischer's avatar
Andi Fischer committed
217
218
219
    m_pImageExporter = 0;
    delete m_pIDChangesLog;
    m_pIDChangesLog = 0;
220
221
222
223
224
225
226
227
228
229

    // 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();
230
231
232

    delete m_pToolBarStateFactory;
    m_pToolBarStateFactory = 0;
233
    delete m_layoutGrid;
234
    delete m_d;
235
236
}

237
238
239
/**
 * Return the UMLFolder in which this diagram lives.
 */
240
UMLFolder* UMLScene::folder() const
241
242
243
244
245
246
247
248
249
250
251
252
{
    return m_pFolder;
}

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

253
254
255
256
/**
 * Returns the active view associated with this scene.
 */
UMLView* UMLScene::activeView() const
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
{
    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;
}

277
278
279
280
281
282
283
284
285
286
287
288
289
/**
 * Return the state of the auto increment sequence
 */
bool UMLScene::autoIncrementSequence() const
{
    return m_autoIncrementSequence;
}

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

290
291
292
293
/**
 * Return the next auto increment sequence value
 */
QString UMLScene::autoIncrementSequenceValue()
294
{
295
    int sequenceNumber = 0;
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
    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;
        }
311
312
    }
    return QString::number(sequenceNumber + 1);
313
314
}

315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
/**
 * 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;
}

331
332
333
/**
 * Returns the type of the diagram.
 */
Andi Fischer's avatar
Andi Fischer committed
334
DiagramType::Enum UMLScene::type() const
335
336
337
338
339
340
341
{
    return m_Type;
}

/**
 * Set the type of diagram.
 */
Andi Fischer's avatar
Andi Fischer committed
342
void UMLScene::setType(DiagramType::Enum type)
343
344
345
346
347
348
349
{
    m_Type = type;
}

/**
 * Returns the ID of the diagram.
 */
Andi Fischer's avatar
Andi Fischer committed
350
Uml::ID::Type UMLScene::ID() const
351
352
353
354
355
356
357
{
    return m_nID;
}

/**
 * Sets the ID of the diagram.
 */
Andi Fischer's avatar
Andi Fischer committed
358
void UMLScene::setID(Uml::ID::Type id)
359
360
361
362
363
364
365
{
    m_nID = id;
}

/**
 * Returns the position of the diagram.
 */
366
QPointF UMLScene::pos() const
367
368
369
370
371
372
373
{
    return m_Pos;
}

/**
 * Sets the position of the diagram.
 */
374
void UMLScene::setPos(const QPointF &pos)
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
{
    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
395
    emit sigFillColorChanged(ID());
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
}

/**
 * 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
414
    emit sigLineColorChanged(ID());
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
}

/**
 * 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
433
    emit sigLineWidthChanged(ID());
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
}

/**
 * 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.
 */
449
void UMLScene::setTextColor(const QColor& color)
450
451
{
    m_Options.uiState.textColor = color;
Ralf Habacker's avatar
Ralf Habacker committed
452
    emit sigTextColorChanged(ID());
453
454
455
456
457
458
459
460
461
}

/**
 * return grid dot color
 *
 * @return Color
 */
const QColor& UMLScene::gridDotColor() const
{
462
    return m_layoutGrid->gridDotColor();
463
464
465
466
467
468
469
470
471
}

/**
 * set grid dot color
 *
 * @param color grid dot color
 */
void UMLScene::setGridDotColor(const QColor& color)
{
472
    m_Options.uiState.gridDotColor = color;
473
    m_layoutGrid->setGridDotColor(color);
474
475
476
477
478
}

/**
 * Returns the options being used.
 */
479
Settings::OptionState& UMLScene::optionState()
480
481
482
483
484
485
486
{
    return m_Options;
}

/**
 * Sets the options to be used.
 */
487
void UMLScene::setOptionState(const Settings::OptionState& options)
488
489
{
    m_Options = options;
490
491
    setBackgroundBrush(options.uiState.backgroundColor);
    setGridDotColor(options.uiState.gridDotColor);
492
493
494
495
496
}

/**
 * Returns a reference to the association list.
 */
497
const AssociationWidgetList UMLScene::associationList() const
498
{
499
500
501
502
503
504
505
    AssociationWidgetList result;
    foreach(QGraphicsItem *item, items()) {
        AssociationWidget *w = dynamic_cast<AssociationWidget*>(item);
        if (w)
            result.append(w);
    }
    return result;
506
507
508
509
510
}

/**
 * Returns a reference to the widget list.
 */
511
const UMLWidgetList UMLScene::widgetList() const
512
{
513
514
515
516
517
518
519
520
521
522
523
524
525
    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);
526
527
}

528
void UMLScene::addWidgetCmd(AssociationWidget* widget)
529
530
{
    Q_ASSERT(0 != widget);
531
    addItem(widget);
532
533
}

534
535
536
/**
 * Returns a reference to the message list.
 */
537
const MessageWidgetList UMLScene::messageList() const
538
{
539
540
541
542
543
544
545
    MessageWidgetList result;
    foreach(QGraphicsItem *item, items()) {
        MessageWidget *w = dynamic_cast<MessageWidget*>(item);
        if (w)
            result.append(w);
    }
    return result;
546
547
}

548
549
550
551
552
553
554
555
/**
 * Used for creating unique name of collaboration messages.
 */
int UMLScene::generateCollaborationId()
{
    return ++m_nCollaborationId;
}

556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
/**
 * 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;
}

574
/**
575
 * Contains the implementation for printing functionality.
576
577
578
 */
void UMLScene::print(QPrinter *pPrinter, QPainter & pPainter)
{
579
    bool isFooter = optionState().generalState.footerPrinting;
580
581
582
583
584

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

585
    QRectF source = diagramRect();
Ralf Habacker's avatar
Ralf Habacker committed
586
    QRect paper = pPrinter->paperRect();
587
588
589
590
591
    QRect page = pPrinter->pageRect();

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

Ralf Habacker's avatar
Ralf Habacker committed
593
594
595
596
597
    if (paper == page) {
        QSize margin = page.size() * 0.025;
        page.adjust(margin.width(), margin.height(), -margin.width(), -margin.height());
    }

598
    if (isFooter) {
599
        int margin = 3 + 3 * fontHeight;
Ralf Habacker's avatar
Ralf Habacker committed
600
        page.adjust(0, 0, 0, -margin);
601
602
    }

603
    getDiagram(pPainter, QRectF(source), QRectF(page));
604

605
    //draw foot note
606
    if (isFooter) {
Ralf Habacker's avatar
Ralf Habacker committed
607
        page.adjust(0, 0, 0, fontHeight);
608
609
610
        QString string = i18n("Diagram: %2 Page %1", 1, name());
        QColor textColor(50, 50, 50);
        pPainter.setPen(textColor);
611
612
        pPainter.drawLine(page.left(), page.bottom()    , page.right(), page.bottom());
        pPainter.drawText(page.left(), page.bottom() + 3, page.right(), 2*fontHeight, Qt::AlignLeft, string);
613
614
615
616
617
618
619
620
621
622
    }

    // 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().
 */
623
void UMLScene::setupNewWidget(UMLWidget *w, bool setPosition)
624
{
625
626
627
628
629
630
631
    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());
632
    }
633
    w->setVisible(true);
634
    w->activate();
635
    w->setFontCmd(font());
Ralf Habacker's avatar
Ralf Habacker committed
636
637
638
    w->slotFillColorChanged(ID());
    w->slotTextColorChanged(ID());
    w->slotLineWidthChanged(ID());
639
    resizeSceneToItems();
640
641
    m_doc->setModified();

642
    if (m_doc->loading()) {  // do not emit signals while loading
643
        addWidgetCmd(w);
644
645
646
647
        // w->activate();  // will be done by UMLDoc::activateAllViews() after loading
    } else {
        UMLApp::app()->executeCommand(new CmdCreateWidget(w));
    }
648
649
}

650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
/**
 * 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;
}

666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
/**
 * 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*)));
689
690
691
692
693
694
695
696
697
698
699
}

/**
 * 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();
700

701
    m_bPaste = false;
702
703
}

Andi Fischer's avatar
Andi Fischer committed
704
705
706
707
/**
 * Slot called when an object is created.
 * @param o   created UML object
 */
708
709
void UMLScene::slotObjectCreated(UMLObject* o)
{
710
    DEBUG(DBG_SRC) << "scene=" << name() << " / object=" << o->name();
711
712
713
714
715
716
717
718
719
720
    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);

721
    if (!newWidget) {
722
        return;
723
    }
724

725
    setupNewWidget(newWidget);
726
727

    m_bCreateObject = false;
728

729
730
731
732
733
734
735
    if (Model_Utils::hasAssociations(o->baseType()))
    {
        createAutoAssociations(newWidget);
        // We need to invoke createAutoAttributeAssociations()
        // on all other widgets again because the newly created
        // widget might saturate some latent attribute assocs.
        createAutoAttributeAssociations2(newWidget);
736
    }
737
    resizeSceneToItems();
738
739
}

Andi Fischer's avatar
Andi Fischer committed
740
741
742
743
/**
 * Slot called when an object is removed.
 * @param o   removed UML object
 */
744
745
746
void UMLScene::slotObjectRemoved(UMLObject * o)
{
    m_bPaste = false;
Andi Fischer's avatar
Andi Fischer committed
747
    Uml::ID::Type id = o->id();
748

749
    foreach(UMLWidget* obj, widgetList()) {
750
751
752
753
754
755
756
757
758
759
        if (obj->id() != id)
            continue;
        removeWidget(obj);
        break;
    }
}

/**
 * Override standard method.
 */
760
void UMLScene::dragEnterEvent(QGraphicsSceneDragDropEvent *e)
761
762
763
{
    UMLDragData::LvTypeAndID_List tidList;
    if (!UMLDragData::getClip3TypeAndID(e->mimeData(), tidList)) {
Oliver Kellogg's avatar
Oliver Kellogg committed
764
        DEBUG(DBG_SRC) << "UMLDragData::getClip3TypeAndID returned false";
765
766
        return;
    }
767
768
769
    for(UMLDragData::LvTypeAndID_List::const_iterator it = tidList.begin(); it != tidList.end(); it++) {
        UMLListViewItem::ListViewType lvtype = (*it)->type;
        Uml::ID::Type id = (*it)->id;
770

771
        DiagramType::Enum diagramType = type();
772

773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
        UMLObject* temp = 0;
        //if dragging diagram - might be a drag-to-note
        if (Model_Utils::typeIsDiagram(lvtype)) {
            e->accept();
            continue;
        }
        //can't drag anything onto state/activity diagrams
        if (diagramType == DiagramType::State || diagramType == DiagramType::Activity) {
            e->ignore();
            continue;
        }
        //make sure can find UMLObject
        if (!(temp = m_doc->findObjectById(id))) {
            DEBUG(DBG_SRC) << "object " << Uml::ID::toString(id) << " not found";
            e->ignore();
            continue;
        }
        bool bAccept = Model_Utils::typeIsAllowedInDiagram(temp, this);
        if (bAccept) {
            e->accept();
        } else {
            e->ignore();
        }
796
797
798
799
800
801
    }
}

/**
 * Override standard method.
 */
802
void UMLScene::dragMoveEvent(QGraphicsSceneDragDropEvent* e)
803
804
805
806
807
808
809
{
    e->accept();
}

/**
 * Override standard method.
 */
810
void UMLScene::dropEvent(QGraphicsSceneDragDropEvent *e)
811
812
813
{
    UMLDragData::LvTypeAndID_List tidList;
    if (!UMLDragData::getClip3TypeAndID(e->mimeData(), tidList)) {
814
        DEBUG(DBG_SRC) << "UMLDragData::getClip3TypeAndID returned error";
815
816
        return;
    }
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
    m_Pos = e->scenePos();

    for(UMLDragData::LvTypeAndID_List::const_iterator it = tidList.begin(); it != tidList.end(); it++) {
        UMLListViewItem::ListViewType lvtype = (*it)->type;
        Uml::ID::Type id = (*it)->id;

        if (Model_Utils::typeIsDiagram(lvtype)) {
            bool breakFlag = false;
            UMLWidget* w = 0;
            foreach(w, widgetList()) {
                if (w->isNoteWidget() && w->onWidget(e->scenePos())) {
                    breakFlag = true;
                    break;
                }
            }
            if (breakFlag) {
                NoteWidget *note = static_cast<NoteWidget*>(w);
                note->setDiagramLink(id);
835
            }
836
            continue;
837
        }
838
839
840
841
        UMLObject* o = m_doc->findObjectById(id);
        if (!o) {
            DEBUG(DBG_SRC) << "object id=" << Uml::ID::toString(id) << " not found";
            continue;
842
        }
843

844
845
846
847
848
        UMLWidget* newWidget = Widget_Factory::createWidget(this, o);
        if (!newWidget) {
            uWarning() << "could not create widget for uml object" << o->name();
            continue;
        }
849

850
851
852
853
        setupNewWidget(newWidget);
        m_Pos += QPointF(UMLWidget::DefaultMinimumSize.width(), UMLWidget::DefaultMinimumSize.height());
        createAutoAssociations(newWidget);
        createAutoAttributeAssociations2(newWidget);
854
    }
855
856
}

857
858
859
860
/**
 * Overrides the standard operation.
 * Calls the same method in the current tool bar state.
 */
861
void UMLScene::mouseMoveEvent(QGraphicsSceneMouseEvent* ome)
862
863
864
865
866
867
868
869
{
    m_pToolBarState->mouseMove(ome);
}

/**
 * Override standard method.
 * Calls the same method in the current tool bar state.
 */
870
void UMLScene::mousePressEvent(QGraphicsSceneMouseEvent* event)
871
{
872
    if (event->button() != Qt::LeftButton) {
873
874
875
876
        event->ignore();
        return;
    }

877
878
879
880
881
882
883
884
885
886
    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();
887
        UMLApp::app()->docWindow()->showDocumentation(widget);
888
        event->accept();
889
890
891
892
    }
    else {
        AssociationWidget* association = associationAt(event->scenePos());
        if (association) {
893
            DEBUG(DBG_SRC) << "association widget = " << association->name() << " / type = " << association->baseTypeStr();
894
            // the following is done in AssociationWidget::setSelected()
895
            // UMLApp::app()->docWindow()->showDocumentation(association, true);
896
            // event->accept();
897
898
        }
        //:TODO: else if (clicking on other elements with documentation) {
899
        //:TODO: UMLApp::app()->docWindow()->showDocumentation(umlObject, true);
900
901
        else {
            // clicking on an empty space in the diagram with arrow tool
902
            UMLApp::app()->docWindow()->showDocumentation(this);
903
904
905
906
907
908
909
910
911
            event->accept();
        }
    }
}

/**
 * Override standard method.
 * Calls the same method in the current tool bar state.
 */
912
void UMLScene::mouseDoubleClickEvent(QGraphicsSceneMouseEvent* event)
913
{
914
915
    if (!m_doc->loading())
        m_pToolBarState->mouseDoubleClick(event);
916
    if (!event->isAccepted()) {
917
        // show properties dialog of the scene
918
        if (m_view->showPropertiesDialog() == true) {
919
920
            m_doc->setModified();
        }
921
        event->accept();
922
923
924
925
926
927
928
    }
}

/**
 * Overrides the standard operation.
 * Calls the same method in the current tool bar state.
 */
929
void UMLScene::mouseReleaseEvent(QGraphicsSceneMouseEvent* ome)
930
931
932
933
{
    m_pToolBarState->mouseRelease(ome);
}

934
935
936
937
/**
 * Determine whether on a sequence diagram we have clicked on a line
 * of an Object.
 *
938
 * @return The widget owning the line which was clicked.
939
940
 *  Returns 0 if no line was clicked on.
 */
941
ObjectWidget * UMLScene::onWidgetLine(const QPointF &point) const
942
{
943
    foreach(UMLWidget* obj, widgetList()) {
944
        ObjectWidget *ow = obj->asObjectWidget();
945
        if (ow == 0)
946
            continue;
947
        SeqLineWidget *pLine = ow->sequentialLine();
948
        if (pLine == 0) {
949
            uError() << "SeqLineWidget of " << ow->name()
950
            << " (id=" << Uml::ID::toString(ow->localID()) << ") is NULL";
951
952
953
954
955
956
957
958
959
960
961
962
            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.
 *
963
 * @return The widget owning the destruction box which was clicked.
964
965
 *  Returns 0 if no destruction box was clicked on.
 */
966
ObjectWidget * UMLScene::onWidgetDestructionBox(const QPointF &point) const
967
{
968
    foreach(UMLWidget* obj, widgetList()) {
969
        ObjectWidget *ow = obj->asObjectWidget();
970
        if (ow == 0)
971
            continue;
972
        SeqLineWidget *pLine = ow->sequentialLine();
973
        if (pLine == 0) {
974
            uError() << "SeqLineWidget of " << ow->name()
975
                     << " (id=" << Uml::ID::toString(ow->localID()) << ") is NULL";
976
977
978
979
980
981
982
983
            continue;
        }
        if (pLine->onDestructionBox(point))
            return ow;
    }
    return 0;
}

984
985
986
987
988
/**
 * Return pointer to the first selected widget (for multi-selection)
 */
UMLWidget* UMLScene::getFirstMultiSelectedWidget() const
{
989
    if (selectedWidgets().size() == 0)
Ralf Habacker's avatar
Ralf Habacker committed
990
        return 0;
991
    return selectedWidgets().first();
992
993
}

994
995
996
997
998
/**
 * 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.
999
 * TODO: What about using QGraphicsScene::items(...)?
1000
 */
1001
UMLWidget* UMLScene::widgetAt(const QPointF& p)
1002
{
1003
    return dynamic_cast<UMLWidget*>(itemAt(p));
1004
1005
}

1006
1007
/**
 * Tests the given point against all associations and returns the
1008
 * association widget for which the point is on the line.
1009
 * Returns NULL if the point is not inside any association.
1010
 * CHECK: This is the same method as in ToolBarState.
1011
 */
1012
AssociationWidget* UMLScene::associationAt(const QPointF& p)
1013
{
1014
1015
1016
1017
1018
1019
    foreach (AssociationWidget* association, associationList()) {
        if (association->onAssociation(p)) {
            return association;
        }
    }
    return 0;
1020
1021
}

1022
1023
1024
1025
1026
/**
 * 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.
 */
1027
MessageWidget* UMLScene::messageAt(const QPointF& p)
1028
1029
1030
1031
1032
1033
1034
1035
1036
{
    foreach(MessageWidget *message, messageList()) {
        if (message->onWidget(p)) {
            return message;
        }
    }
    return 0;
}

1037
1038
1039
1040
1041
1042
/**
 * 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
1043
    if (type() != DiagramType::Sequence) {
1044
        return;
Andi Fischer's avatar
Andi Fischer committed
1045
    }
1046

1047
    foreach(MessageWidget *obj, messageList()) {
1048
        if (obj->hasObjectWidget(w)) {
1049
            removeWidgetCmd(obj);
1050
        }
1051
1052
1053
1054
1055
1056
1057
1058
    }
}

/**
 * Returns whether a widget is already on the diagram.
 *
 * @param id The id of the widget to check for.
 *
1059
 * @return Returns pointer to the widget if it is on the diagram, NULL if not.
1060
 */
1061
UMLWidget* UMLScene::widgetOnDiagram(Uml::ID::Type id)
1062
{
1063
    foreach(UMLWidget *obj, widgetList()) {
Ralf Habacker's avatar
Ralf Habacker committed
1064
1065
        if (!obj)
            continue;
1066
1067
1068
        UMLWidget* w = obj->widgetWithID(id);
        if (w)
            return w;
1069
1070
    }

1071
    foreach(UMLWidget *obj, messageList()) {
1072
1073
        // CHECK: Should MessageWidget reimplement widgetWithID() ?
        //       If yes then we should use obj->widgetWithID(id) here too.
1074
        if (id == obj->id())
1075
            return obj;
1076
1077
    }

1078
    return 0;
1079
1080
1081
1082
}

/**
 * Finds a widget with the given ID.
Andi Fischer's avatar
Andi Fischer committed
1083
 * Search both our UMLWidget AND MessageWidget lists.
1084
1085
1086
1087
 * @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
1088
UMLWidget * UMLScene::findWidget(Uml::ID::Type id)
1089
{
1090
    foreach(UMLWidget* obj, widgetList()) {
Ralf Habacker's avatar
Ralf Habacker committed
1091
1092
        if (!obj)
            continue;
1093
1094
1095
        UMLWidget* w = obj->widgetWithID(id);
        if (w) {
            return w;
1096
1097
1098
        }
    }

1099
    foreach(UMLWidget* obj, messageList()) {
1100
1101
1102
1103
        // CHECK: Should MessageWidget reimplement widgetWithID() ?
        //       If yes then we should use obj->widgetWithID(id) here too.
        if (obj->localID() == id ||
            obj->id() == id)
1104
1105
1106
            return obj;
    }

1107
1108
1109
    return 0;
}

1110
1111
1112
1113
1114
1115
1116
/**
 * 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
1117
AssociationWidget * UMLScene::findAssocWidget(Uml::ID::Type id)
1118
{
1119
    foreach(AssociationWidget* obj, associationList()) {
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
        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,
1141
                                              UMLWidget *pWidgetB, const QString& roleNameB)
1142
{
1143
    foreach(AssociationWidget* assoc, associationList()) {
1144
        const Uml::AssociationType::Enum testType = assoc->associationType();
1145
1146
1147
1148
        if (testType != Uml::AssociationType::Association &&
                testType != Uml::AssociationType::UniAssociation &&
                testType != Uml::AssociationType::Composition &&
                testType != Uml::AssociationType::Aggregation &&
1149
                testType != Uml::AssociationType::Relationship) {
1150
            continue;
1151
1152
        }

1153
1154
1155
        if (pWidgetA->id() == assoc->widgetIDForRole(Uml::RoleType::A) &&
                pWidgetB->id() == assoc->widgetIDForRole(Uml::RoleType::B) &&
                assoc->roleName(Uml::RoleType::B) == roleNameB) {
1156
            return assoc;
1157
        }
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
    }
    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.
 */
1171
AssociationWidget * UMLScene::findAssocWidget(AssociationType::Enum at,
1172
                                              UMLWidget *pWidgetA, UMLWidget *pWidgetB)
1173
{
1174
    foreach(AssociationWidget* assoc, associationList()) {
1175
        Uml::AssociationType::Enum testType = assoc->associationType();
1176
        if (testType != at) {
1177
            continue;
1178
1179
        }

1180
1181
        if (pWidgetA->id() == assoc->widgetIDForRole(Uml::RoleType::A) &&
                pWidgetB->id() == assoc->widgetIDForRole(Uml::RoleType::B)) {
1182
            return assoc;
1183
        }
1184
1185
1186
1187
1188
    }
    return 0;
}

/**
1189
 * Remove a widget from view (undo command)
1190
1191
1192
1193
 *
 * @param o  The widget to remove.
 */
void UMLScene::removeWidget(UMLWidget * o)
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
{
    UMLApp::app()->executeCommand(new CmdRemoveWidget(o));
}

/**
 * Remove a widget from view.
 *
 * @param o  The widget to remove.
 */
void UMLScene::removeWidgetCmd(UMLWidget * o)
1204
1205
1206
1207
1208
1209
1210
1211
{
    if (!o)
        return;

    emit sigWidgetRemoved(o);

    removeAssociations(o);

1212
1213
    removeOwnedWidgets(o);

1214
    WidgetBase::WidgetType t = o->baseType();
Andi Fischer's avatar
Andi Fischer committed
1215
    if (type() == DiagramType::Sequence && t == WidgetBase::wt_Object) {
1216
        checkMessages(static_cast<ObjectWidget*>(o));
Andi Fischer's avatar
Andi Fischer committed
1217
    }
1218
1219

    o->cleanup();