umllistview.cpp 99.1 KB
Newer Older
Thibault Normand's avatar
Thibault Normand committed
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>                 *
Thibault Normand's avatar
Thibault Normand committed
9
10
11
12
13
14
15
16
 ***************************************************************************/

// own header
#include "umllistview.h"

// app includes
#include "actor.h"
#include "classifier.h"
17
#include "cmds.h"
18
#include "debug_utils.h"
19
#include "dialog_utils.h"
Thibault Normand's avatar
Thibault Normand committed
20
21
22
23
24
25
#include "package.h"
#include "folder.h"
#include "component.h"
#include "node.h"
#include "artifact.h"
#include "enum.h"
Sharan Rao's avatar
Added -    
Sharan Rao committed
26
#include "enumliteral.h"
Thibault Normand's avatar
Thibault Normand committed
27
#include "entity.h"
28
#include "category.h"
Thibault Normand's avatar
Thibault Normand committed
29
#include "docwindow.h"
30
#include "layoutgenerator.h"
31
#include "umllistviewpopupmenu.h"
Thibault Normand's avatar
Thibault Normand committed
32
33
34
35
#include "template.h"
#include "operation.h"
#include "attribute.h"
#include "entityattribute.h"
36
37
#include "instance.h"
#include "instanceattribute.h"
38
39
#include "uniqueconstraint.h"
#include "foreignkeyconstraint.h"
Sharan Rao's avatar
Sharan Rao committed
40
#include "checkconstraint.h"
Thibault Normand's avatar
Thibault Normand committed
41
#include "uml.h"
42
#include "umlclipboard.h"
Thibault Normand's avatar
Thibault Normand committed
43
44
45
#include "umldoc.h"
#include "umllistviewitemlist.h"
#include "umllistviewitem.h"
46
#include "umlscene.h"
47
#include "umlview.h"
Thibault Normand's avatar
Thibault Normand committed
48
49
50
#include "umlviewimageexporter.h"
#include "usecase.h"
#include "model_utils.h"
51
#include "models/diagramsmodel.h"
Oliver Kellogg's avatar
Oliver Kellogg committed
52
#include "optionstate.h"
Thibault Normand's avatar
Thibault Normand committed
53
#include "uniqueid.h"
54
55
#include "idchangelog.h"
#include "umldragdata.h"
56
#include "classpropertiesdialog.h"
57
58
59
60
61
62
63
#include "umlattributedialog.h"
#include "umlentityattributedialog.h"
#include "umloperationdialog.h"
#include "umltemplatedialog.h"
#include "umluniqueconstraintdialog.h"
#include "umlforeignkeyconstraintdialog.h"
#include "umlcheckconstraintdialog.h"
64
#include "object_factory.h"
Thibault Normand's avatar
Thibault Normand committed
65

66
// kde includes
67
#if QT_VERSION < 0x050000
68
#include <kfiledialog.h>
69
#endif
70
71
#include <KLocalizedString>
#include <KMessageBox>
72
#if QT_VERSION < 0x050000
73
#include <ktabwidget.h>
74
#endif
75
76

// qt includes
77
#include <QApplication>
78
#include <QDrag>
79
80
#include <QDropEvent>
#include <QEvent>
81
82
83
#if QT_VERSION >= 0x050000
#include <QFileDialog>
#endif
84
85
86
87
88
89
90
91
#include <QFocusEvent>
#include <QKeyEvent>
#include <QMouseEvent>
#include <QPointer>
#include <QRegExp>
#include <QPoint>
#include <QRect>
#include <QToolTip>
92

93
94
DEBUG_REGISTER(UMLListView)

95
96
97
98
99
/**
 * Constructs the tree view.
 *
 * @param parent   The parent to this.
 */
100
UMLListView::UMLListView(QWidget *parent)
Andi Fischer's avatar
Andi Fischer committed
101
  : QTreeWidget(parent),
102
103
    m_rv(0),
    m_datatypeFolder(0),
104
    m_settingsFolder(0),
Andi Fischer's avatar
Andi Fischer committed
105
    m_doc(UMLApp::app()->document()),
106
107
    m_bStartedCut(false),
    m_bStartedCopy(false),
108
109
    m_bCreatingChildObject(false),
    m_dragStartPosition(QPoint()),
110
    m_dragCopyData(0)
Thibault Normand's avatar
Thibault Normand committed
111
{
112
113
    // setup list view
    setAcceptDrops(true);
114
115
116
    //setDropVisualizer(false);
    //setItemsMovable(true);
    //setItemsRenameable(true);
117
    setSelectionMode(ExtendedSelection);
118
119
    setFocusPolicy(Qt::StrongFocus);
    setDragEnabled(true);
120
121
122
123
    //setColumnWidthMode(0, Manual);
    //setDefaultRenameAction(Accept);
    //setResizeMode(LastColumn);
    //header()->setClickEnabled(true);
Thibault Normand's avatar
Thibault Normand committed
124
    //add columns and initial items
125
    //addColumn(m_doc->name());
126
127
128
129
    setSortingEnabled(true);
    sortByColumn(0, Qt::AscendingOrder);

    setEditTriggers(QAbstractItemView::EditKeyPressed);
Thibault Normand's avatar
Thibault Normand committed
130

131
    for (int i = 0; i < Uml::ModelType::N_MODELTYPES; ++i) {
132
        m_lv[i] = 0;
133
134
    }

Thibault Normand's avatar
Thibault Normand committed
135
    //setup slots/signals
136
137
    connect(this, SIGNAL(itemCollapsed(QTreeWidgetItem*)), this, SLOT(slotCollapsed(QTreeWidgetItem*)));
    connect(this, SIGNAL(itemExpanded(QTreeWidgetItem*)), this, SLOT(slotExpanded(QTreeWidgetItem*)));
138
    connect(UMLApp::app(), SIGNAL(sigCutSuccessful()), this, SLOT(slotCutSuccessful()));
139
    connect(this, SIGNAL(itemSelectionChanged()), this, SLOT(slotItemSelectionChanged()));
Thibault Normand's avatar
Thibault Normand committed
140
141
}

142
/**
143
 * Standard destructor.
144
 */
145
146
UMLListView::~UMLListView()
{
Ralf Habacker's avatar
Ralf Habacker committed
147
    clean(); // m_lv
Ralf Habacker's avatar
Ralf Habacker committed
148
    delete m_datatypeFolder;
149
    delete m_settingsFolder;
Ralf Habacker's avatar
Ralf Habacker committed
150
    delete m_rv;
151
}
Thibault Normand's avatar
Thibault Normand committed
152

153
154
155
156
157
/**
 * Sets the title.
 * @param column   column in which to write
 * @param text     the text to write
 */
158
159
void UMLListView::setTitle(int column, const QString &text)
{
160
    headerItem()->setText(column, text);
161
162
}

163
/**
164
 * Handler for item selection changed signals.
165
 */
166
167
void UMLListView::slotItemSelectionChanged()
{
168
169
    UMLListViewItem* currItem = static_cast<UMLListViewItem*>(currentItem());
    if (currItem && currItem->isSelected()) {
170
        DEBUG(DBG_SRC) << "UMLListView selection changed to" << currItem->text(0);
171
172
173
174
175
176
177
178
179
180
        // Update current view to selected object's view
        if (Model_Utils::typeIsDiagram(currItem->type())) {
            // If the user navigates to a diagram, load the diagram just like what
            // would happen when clicking on it (includes saving/showing the documentation)
            m_doc->changeCurrentView(currItem->ID());
        } else {
            // If the user navigates to any other item, save the current object's
            // documentation and show selected object's documentation
            UMLApp::app()->docWindow()->showDocumentation(currItem->umlObject(), true);
        }
181
    }
182
183
}

184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
/**
 * Event handler for the tool tip event.
 * Works only for operations to show the signature.
 */
bool UMLListView::event(QEvent *e)
{
    if (e->type() == QEvent::ToolTip) {
        QHelpEvent *helpEvent = static_cast<QHelpEvent *>(e);
        UMLListViewItem * item = static_cast<UMLListViewItem*>(itemAt(helpEvent->pos()));
        if (item) {
            QToolTip::showText(helpEvent->globalPos(), item->toolTip());
        } else {
            QToolTip::hideText();
            e->ignore();
        }
        return true;
    }
    return QTreeWidget::event(e);
}

/**
 * Handler for mouse press events.
 * @param me   the mouse event
 */
208
void UMLListView::mousePressEvent(QMouseEvent *me)
209
{
210
    UMLView *currentView = UMLApp::app()->currentView();
211
212
213
214
    Q_ASSERT(currentView);
    UMLScene *scene = currentView->umlScene();
    Q_ASSERT(scene);
    scene->clearSelected();
215
    if (me->modifiers() != Qt::ShiftModifier)
Thibault Normand's avatar
Thibault Normand committed
216
        clearSelection();
217
218

    // Get the UMLListViewItem at the point where the mouse pointer was pressed
219
220
    UMLListViewItem * item = static_cast<UMLListViewItem*>(itemAt(me->pos()));
    if (item) {
221
        DEBUG(DBG_SRC) << "QMouseEvent on" << UMLListViewItem::toString(item->type());
222
223
    }
    else {
224
        DEBUG(DBG_SRC) << "QMouseEvent on empty space";
225
    }
226

227
    const Qt::MouseButton button = me->button();
Thibault Normand's avatar
Thibault Normand committed
228
229

    if (!item || (button != Qt::RightButton && button != Qt::LeftButton)) {
230
        UMLApp::app()->docWindow()->updateDocumentation(true);
Thibault Normand's avatar
Thibault Normand committed
231
232
233
234
        return;
    }

    if (button == Qt::LeftButton) {
235
        UMLObject *o = item->umlObject();
Thibault Normand's avatar
Thibault Normand committed
236
        if (o)
237
            UMLApp::app()->docWindow()->showDocumentation(o, false);
Thibault Normand's avatar
Thibault Normand committed
238
        else
239
            UMLApp::app()->docWindow()->updateDocumentation(true);
240
241

        m_dragStartPosition = me->pos();
Thibault Normand's avatar
Thibault Normand committed
242
    }
243

244
    QTreeWidget::mousePressEvent(me);
245
246
}

247
248
249
250
/**
 * Handler for mouse move events.
 * @param me   the mouse event
 */
251
void UMLListView::mouseMoveEvent(QMouseEvent* me)
252
{
Oliver Kellogg's avatar
Oliver Kellogg committed
253
254
    if (!(me->buttons() & Qt::LeftButton)) {
        DEBUG(DBG_SRC) << "not LeftButton (no action)";
255
        return;
Oliver Kellogg's avatar
Oliver Kellogg committed
256
    }
257
    if ((me->pos() - m_dragStartPosition).manhattanLength()
Oliver Kellogg's avatar
Oliver Kellogg committed
258
259
            < QApplication::startDragDistance()) {
        DEBUG(DBG_SRC) << "pos change since dragStart is below startDragDistance threshold (no action)";
260
        return;
Oliver Kellogg's avatar
Oliver Kellogg committed
261
    }
262

Oliver Kellogg's avatar
Oliver Kellogg committed
263
    DEBUG(DBG_SRC) << "initiating drag";
264
265
266
267
268
269
270
271
272
273
274
275

    // Store a copy of selected list items in case the user
    // will ctrl-drag (basically just copy/paste) an item
    //
    // The QDrag mime data is used for moving items onto the diagram
    // or internally in the tree view
    UMLClipboard clipboard;
    if ((m_dragCopyData = clipboard.copy(false)) == 0) {
        // This should never happen, this is just like using ctrl+c on the list view item
        uError() << "Unable to obtain mime data for copy-drag operation";
    }

276
277
    QDrag* drag = new QDrag(this);
    drag->setMimeData(getDragData());
278
    drag->exec(Qt::MoveAction | Qt::CopyAction, Qt::MoveAction);
Thibault Normand's avatar
Thibault Normand committed
279
280
}

281
282
283
284
/**
 * Handler for mouse release event.
 * @param me   the mouse event
 */
285
void UMLListView::mouseReleaseEvent(QMouseEvent *me)
286
{
Thibault Normand's avatar
Thibault Normand committed
287
    if (me->button() != Qt::LeftButton) {
288
        QTreeWidget::mouseReleaseEvent(me);
Thibault Normand's avatar
Thibault Normand committed
289
290
        return;
    }
291
    UMLListViewItem *item = static_cast<UMLListViewItem*>(itemAt(me->pos()));
292
    if (item == 0 || !Model_Utils::typeIsDiagram(item->type())) {
293
        QTreeWidget::mouseReleaseEvent(me);
Thibault Normand's avatar
Thibault Normand committed
294
295
296
297
        return;
    }
    // Switch to diagram on mouse release - not on mouse press
    // because the user might intend a drag-to-note.
Ralf Habacker's avatar
Ralf Habacker committed
298
    m_doc->changeCurrentView(item->ID());
299
    UMLView *view = m_doc->findView(item->ID());
300
    if (view && view->umlScene())
301
        UMLApp::app()->docWindow()->showDocumentation(view->umlScene(), false);
302
    QTreeWidget::mouseReleaseEvent(me);
Thibault Normand's avatar
Thibault Normand committed
303
304
}

305
306
307
308
/**
 * Handler for key press events.
 * @param ke   the key event
 */
309
310
void UMLListView::keyPressEvent(QKeyEvent *ke)
{
311
312
313
314
315
316
317
318
319
320
    QTreeWidget::keyPressEvent(ke); // let parent handle it
    const int k = ke->key();
    if (k == Qt::Key_Delete || k == Qt::Key_Backspace) {
        slotDeleteSelectedItems();
    } else if (k == Qt::Key_F3) {
        // prelimary support for layout generator
        LayoutGenerator r;
        if (!r.generate(UMLApp::app()->currentView()->umlScene()))
            return;
        r.apply(UMLApp::app()->currentView()->umlScene());
Thibault Normand's avatar
Thibault Normand committed
321
322
323
    }
}

324
/**
325
 * Called when a right mouse button menu has an item selected.
326
 * @param action   the selected action
327
 * @param position the position of the menu on the diagram (only used for multi selection "Show")
328
 */
329
void UMLListView::slotMenuSelection(QAction* action, const QPoint &position)
330
{
331
332
    UMLListViewItem * currItem = static_cast<UMLListViewItem*>(currentItem());
    if (!currItem) {
333
        DEBUG(DBG_SRC) << "Invoked without currently selectedItem!";
Thibault Normand's avatar
Thibault Normand committed
334
335
        return;
    }
336
    UMLListViewItem::ListViewType lvt = currItem->type();
337
    ListPopupMenu::MenuType menuType = ListPopupMenu::typeFromAction(action);
Thibault Normand's avatar
Thibault Normand committed
338
339

    switch (menuType) {
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
    case ListPopupMenu::mt_Activity_Diagram:
        addNewItem(currItem, UMLListViewItem::lvt_Activity_Diagram);
        break;

    case ListPopupMenu::mt_Class_Diagram:
        addNewItem(currItem, UMLListViewItem::lvt_Class_Diagram);
        break;

    case ListPopupMenu::mt_Collaboration_Diagram:
        addNewItem(currItem, UMLListViewItem::lvt_Collaboration_Diagram);
        break;

    case ListPopupMenu::mt_Component_Diagram:
        addNewItem(currItem, UMLListViewItem::lvt_Component_Diagram);
        break;

    case ListPopupMenu::mt_Deployment_Diagram:
        addNewItem(currItem, UMLListViewItem::lvt_Deployment_Diagram);
        break;

    case ListPopupMenu::mt_EntityRelationship_Diagram:
        addNewItem(currItem, UMLListViewItem::lvt_EntityRelationship_Diagram);
        break;

    case ListPopupMenu::mt_Sequence_Diagram:
        addNewItem(currItem, UMLListViewItem::lvt_Sequence_Diagram);
        break;

    case ListPopupMenu::mt_State_Diagram:
        addNewItem(currItem, UMLListViewItem::lvt_State_Diagram);
        break;

    case ListPopupMenu::mt_UseCase_Diagram:
        addNewItem(currItem, UMLListViewItem::lvt_UseCase_Diagram);
        break;

Thibault Normand's avatar
Thibault Normand committed
376
    case ListPopupMenu::mt_Class:
377
        addNewItem(currItem, UMLListViewItem::lvt_Class);
Thibault Normand's avatar
Thibault Normand committed
378
379
380
        break;

    case ListPopupMenu::mt_Package:
381
        addNewItem(currItem, UMLListViewItem::lvt_Package);
Thibault Normand's avatar
Thibault Normand committed
382
383
384
        break;

    case ListPopupMenu::mt_Subsystem:
385
        addNewItem(currItem, UMLListViewItem::lvt_Subsystem);
Thibault Normand's avatar
Thibault Normand committed
386
387
388
        break;

    case ListPopupMenu::mt_Component:
389
        addNewItem(currItem, UMLListViewItem::lvt_Component);
Thibault Normand's avatar
Thibault Normand committed
390
391
        break;

Oliver Kellogg's avatar
Oliver Kellogg committed
392
393
394
395
396
    case ListPopupMenu::mt_Port:
        if (Settings::optionState().generalState.uml2)
            addNewItem(currItem, UMLListViewItem::lvt_Port);
        break;

Thibault Normand's avatar
Thibault Normand committed
397
    case ListPopupMenu::mt_Node:
398
        addNewItem(currItem, UMLListViewItem::lvt_Node);
Thibault Normand's avatar
Thibault Normand committed
399
400
401
        break;

    case ListPopupMenu::mt_Artifact:
402
        addNewItem(currItem, UMLListViewItem::lvt_Artifact);
Thibault Normand's avatar
Thibault Normand committed
403
404
405
        break;

    case ListPopupMenu::mt_Interface:
406
        addNewItem(currItem, UMLListViewItem::lvt_Interface);
Thibault Normand's avatar
Thibault Normand committed
407
408
409
        break;

    case ListPopupMenu::mt_Enum:
410
        addNewItem(currItem, UMLListViewItem::lvt_Enum);
Thibault Normand's avatar
Thibault Normand committed
411
412
        break;

Sharan Rao's avatar
Added -    
Sharan Rao committed
413
    case ListPopupMenu::mt_EnumLiteral:
414
        addNewItem(currItem, UMLListViewItem::lvt_EnumLiteral);
Sharan Rao's avatar
Added -    
Sharan Rao committed
415
416
        break;

Thibault Normand's avatar
Thibault Normand committed
417
    case ListPopupMenu::mt_Template:
418
        addNewItem(currItem, UMLListViewItem::lvt_Template);
Thibault Normand's avatar
Thibault Normand committed
419
420
421
        break;

    case ListPopupMenu::mt_Entity:
422
        addNewItem(currItem, UMLListViewItem::lvt_Entity);
Thibault Normand's avatar
Thibault Normand committed
423
424
        break;

425
    case ListPopupMenu::mt_Category:
426
        addNewItem(currItem, UMLListViewItem::lvt_Category);
427
428
        break;

429
430
    case ListPopupMenu::mt_DisjointSpecialisation:
        {
431
            UMLCategory* catObj = currItem->umlObject()->asUMLCategory();
432
433
            catObj->setType(UMLCategory::ct_Disjoint_Specialisation);
        }
434
        break;
Sharan Rao's avatar
Sharan Rao committed
435

436
437
    case ListPopupMenu::mt_OverlappingSpecialisation:
        {
438
            UMLCategory* catObj = currItem->umlObject()->asUMLCategory();
439
440
            catObj->setType(UMLCategory::ct_Overlapping_Specialisation);
        }
441
        break;
Sharan Rao's avatar
Sharan Rao committed
442

443
444
    case ListPopupMenu::mt_Union:
        {
445
            UMLCategory* catObj = currItem->umlObject()->asUMLCategory();
446
447
            catObj->setType(UMLCategory::ct_Union);
        }
448
        break;
Sharan Rao's avatar
Sharan Rao committed
449

Thibault Normand's avatar
Thibault Normand committed
450
    case ListPopupMenu::mt_Datatype:
451
        addNewItem(currItem, UMLListViewItem::lvt_Datatype);
Thibault Normand's avatar
Thibault Normand committed
452
453
454
        break;

    case ListPopupMenu::mt_Actor:
455
        addNewItem(currItem, UMLListViewItem::lvt_Actor);
Thibault Normand's avatar
Thibault Normand committed
456
457
458
        break;

    case ListPopupMenu::mt_UseCase:
459
        addNewItem(currItem, UMLListViewItem::lvt_UseCase);
Thibault Normand's avatar
Thibault Normand committed
460
461
462
        break;

    case ListPopupMenu::mt_Attribute:
463
        addNewItem(currItem, UMLListViewItem::lvt_Attribute);
Thibault Normand's avatar
Thibault Normand committed
464
465
466
        break;

    case ListPopupMenu::mt_EntityAttribute:
467
        addNewItem(currItem, UMLListViewItem::lvt_EntityAttribute);
Thibault Normand's avatar
Thibault Normand committed
468
        break;
469
470
471
    case ListPopupMenu::mt_InstanceAttribute:
        addNewItem(currItem, UMLListViewItem::lvt_InstanteAttribute);
        break;
Thibault Normand's avatar
Thibault Normand committed
472
    case ListPopupMenu::mt_Operation:
473
        addNewItem(currItem, UMLListViewItem::lvt_Operation);
Thibault Normand's avatar
Thibault Normand committed
474
475
        break;

476
    case ListPopupMenu::mt_UniqueConstraint:
477
        addNewItem(currItem, UMLListViewItem::lvt_UniqueConstraint);
478
479
480
        break;

    case ListPopupMenu::mt_PrimaryKeyConstraint:
481
        addNewItem(currItem, UMLListViewItem::lvt_PrimaryKeyConstraint);
482
483
484
        break;

    case ListPopupMenu::mt_ForeignKeyConstraint:
485
        addNewItem(currItem, UMLListViewItem::lvt_ForeignKeyConstraint);
486
487
        break;

Sharan Rao's avatar
Sharan Rao committed
488
    case ListPopupMenu::mt_CheckConstraint:
489
        addNewItem(currItem, UMLListViewItem::lvt_CheckConstraint);
Sharan Rao's avatar
Sharan Rao committed
490
491
        break;

492
493
494
495
496
497
498
499
    case ListPopupMenu::mt_Import_Class:
        UMLApp::app()->slotImportClass();
        break;

    case ListPopupMenu::mt_Import_Project:
        UMLApp::app()->slotImportProject();
        break;

Thibault Normand's avatar
Thibault Normand committed
500
    case ListPopupMenu::mt_Expand_All:
501
        expandAll(currItem);
Thibault Normand's avatar
Thibault Normand committed
502
503
504
        break;

    case ListPopupMenu::mt_Collapse_All:
505
        collapseAll(currItem);
Thibault Normand's avatar
Thibault Normand committed
506
507
508
        break;

    case ListPopupMenu::mt_Export_Image:
509
510
511
512
513
514
515
516
517
518
519
520
521
522
        {
            const Uml::ID::Type id = currItem->ID();
            UMLView *view = m_doc->findView(id);
            if (view) {
                if (view->umlScene())
                    view->umlScene()->getImageExporter()->exportView();
                else
                    uError() << "ListPopupMenu::mt_Export_Image: view " << Uml::ID::toString(id)
                             << " umlScene() is NULL";
            } else {
                uError() << "ListPopupMenu::mt_Export_Image: m_doc->findView("
                         << Uml::ID::toString(id) << " returns NULL";
            }
        }
Thibault Normand's avatar
Thibault Normand committed
523
524
        break;

525
526
527
    case ListPopupMenu::mt_Externalize_Folder:
        {
            UMLListViewItem *current = static_cast<UMLListViewItem*>(currentItem());
528
            UMLFolder *modelFolder = current->umlObject()->asUMLFolder();
529
530
531
532
533
            if (modelFolder == 0) {
                uError() << "modelFolder is 0";
                return;
            }
            // configure & show the file dialog
534
535
536
537
#if QT_VERSION >= 0x050000
            const QString rootDir(m_doc->url().adjusted(QUrl::RemoveFilename).path());
            QPointer<QFileDialog> fileDialog = new QFileDialog(this, i18n("Externalize Folder"), rootDir, QLatin1String("*.xml"));
#else
538
            const QString rootDir(m_doc->url().directory());
Oliver Kellogg's avatar
Oliver Kellogg committed
539
            QPointer<KFileDialog> fileDialog = new KFileDialog(rootDir, QLatin1String("*.xml"), this);
540
541
            fileDialog->setCaption(i18n("Externalize Folder"));
            fileDialog->setOperationMode(KFileDialog::Other);
542
#endif
543
544
            // set a sensible default filename
            QString defaultFilename = current->text(0).toLower();
Oliver Kellogg's avatar
Oliver Kellogg committed
545
546
            defaultFilename.replace(QRegExp(QLatin1String("\\W+")), QLatin1String("_"));
            defaultFilename.append(QLatin1String(".xml"));  // default extension
547
548
549
550
551
552
553
#if QT_VERSION >= 0x050000
            fileDialog->selectFile(defaultFilename);
            QList<QUrl> selURL;
            if (fileDialog->exec() == QDialog::Accepted) {
                selURL = fileDialog->selectedUrls();
            }
#else
554
555
556
557
558
            fileDialog->setSelection(defaultFilename);
            KUrl selURL;
            if (fileDialog->exec() == QDialog::Accepted) {
                selURL = fileDialog->selectedUrl();
            }
559
#endif
560
561
562
            delete fileDialog;
            if (selURL.isEmpty())
                return;
563
564
565
#if QT_VERSION >= 0x050000
            QString path = selURL[0].toLocalFile();
#else
566
            QString path = selURL.toLocalFile();
567
#endif
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
            QString fileName = path;
            if (fileName.startsWith(rootDir)) {
                fileName.remove(rootDir);
            } else {
                KMessageBox::error(
                    0,
                    i18n("Folder %1 must be relative to the main model directory, %2.", path, rootDir),
                    i18n("Path Error"));
                return;
            }
            QFile file(path);
            // Warn if file exists.
            if (file.exists()) {
                KMessageBox::error(
                    0,
                    i18n("File %1 already exists!\nThe existing file will be overwritten.", fileName),
                    i18n("File Exist"));
            }
            // Test if file is writable.
            if (file.open(QIODevice::WriteOnly)) {
                file.close();
            } else {
                KMessageBox::error(
                    0,
                    i18n("There was a problem saving file: %1", fileName),
                    i18n("Save Error"));
                return;
            }
            modelFolder->setFolderFile(fileName);
            // Recompute text of the folder
            QString folderText = current->text(0);
Oliver Kellogg's avatar
Oliver Kellogg committed
599
600
            folderText.remove(QRegExp(QLatin1String("\\s*\\(.*$")));
            folderText.append(QLatin1String(" (") + fileName + QLatin1Char(')'));
601
602
            current->setText(folderText);
            break;
603
        }
Thibault Normand's avatar
Thibault Normand committed
604

605
606
607
    case ListPopupMenu::mt_Internalize_Folder:
        {
            UMLListViewItem *current = static_cast<UMLListViewItem*>(currentItem());
608
            UMLFolder *modelFolder = current->umlObject()->asUMLFolder();
609
610
611
612
613
614
615
            if (modelFolder == 0) {
                uError() << "modelFolder is 0";
                return;
            }
            modelFolder->setFolderFile(QString());
            // Recompute text of the folder
            QString folderText = current->text(0);
Oliver Kellogg's avatar
Oliver Kellogg committed
616
            folderText.remove(QRegExp(QLatin1String("\\s*\\(.*$")));
617
618
            current->setText(folderText);
            break;
619
620
        }

621
622
    case ListPopupMenu::mt_Model:
        {
623
624
625
626
            QString name = m_doc->name();
            bool ok = Dialog_Utils::askName(i18n("Enter Model Name"),
                                            i18n("Enter the new name of the model:"),
                                            name);
627
628
629
630
631
            if (ok) {
                setTitle(0, name);
                m_doc->setName(name);
            }
            break;
Thibault Normand's avatar
Thibault Normand committed
632
633
634
        }

    case ListPopupMenu::mt_Rename:
635
        edit(currentIndex());
Thibault Normand's avatar
Thibault Normand committed
636
637
638
        break;

    case ListPopupMenu::mt_Delete:
639
        deleteItem(currItem);
Thibault Normand's avatar
Thibault Normand committed
640
641
        break;

642
643
644
645
646
647
648
649
    case ListPopupMenu::mt_Show:
        // first check if we are on a diagram
        if (!Model_Utils::typeIsDiagram(lvt)) {
            UMLObject * object = currItem->umlObject();
            if (!object) {
                uError() << "UMLObjet of ... is null! Doing nothing.";
                return;
            }
650
651
            QList<UMLWidget*> findResults;

652
653
654
655
656
            if (Model_Utils::typeIsCanvasWidget(lvt)) {
                UMLViewList views = m_doc->viewIterator();
                foreach (UMLView *view, views) {
                    foreach(UMLWidget *widget, view->umlScene()->widgetList()) {
                        if (object == widget->umlObject()) {
657
                            findResults.append(widget);
658
659
660
661
                        }
                    }
                }
            }
662
663
664
665
666
667
668
669
670
671
672

            if (findResults.size() == 0)  {
                break;
            }

            UMLWidget *selectedResult = 0;

            if (findResults.size() > 1) {
                QMenu menu(this);
                int i = 0;
                foreach(UMLWidget *w, findResults) {
Oliver Kellogg's avatar
Oliver Kellogg committed
673
                    QAction *action = menu.addAction(w->umlScene()->name() + QLatin1Char(':') + w->name());
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
                    action->setData(i++);
                }
                QAction *action = menu.exec(position);
                if (action) {
                    selectedResult = findResults.at(action->data().toInt());
                }
            } else {
                selectedResult = findResults.first();
            }

            if (!selectedResult) {
                break;
            }

            UMLView *view = selectedResult->umlScene()->activeView();
            selectedResult->umlScene()->setIsOpen(true);
            view->setZoom(100);
            if (UMLApp::app()->currentView() != view) {
                UMLApp::app()->setCurrentView(view, false);
            }
694
            view->centerOn(selectedResult->scenePos());
695
            selectedResult->setSelected(true);
696
697
698
        }
        break;

Thibault Normand's avatar
Thibault Normand committed
699
    case ListPopupMenu::mt_Properties:
700
        if (Model_Utils::typeIsProperties(lvt)) {
701
            UMLApp::app()->slotPrefs(Model_Utils::convert_LVT_PT(lvt));
702
703
704
            return;
        }
        else if (Model_Utils::typeIsDiagram(lvt)) {
Ralf Habacker's avatar
Ralf Habacker committed
705
            UMLView * pView = m_doc->findView(currItem->ID());
706
            if (pView) {
707
                UMLApp::app()->docWindow()->updateDocumentation(false);
708
                pView->showPropertiesDialog();
709
                UMLApp::app()->docWindow()->showDocumentation(pView->umlScene(), true);
Thibault Normand's avatar
Thibault Normand committed
710
711
712
713
            }
            return;
        }

714
715
716
717
718
719
        { // ok, we are on another object, so find out on which one
            UMLObject * object = currItem->umlObject();
            if (!object) {
                uError() << "UMLObject of ... is null! Doing nothing.";
                return;
            }
720
            object->showPropertiesDialog();
Thibault Normand's avatar
Thibault Normand committed
721
722
723
724
        }
        break;

    case ListPopupMenu::mt_Logical_Folder:
725
        addNewItem(currItem, UMLListViewItem::lvt_Logical_Folder);
Thibault Normand's avatar
Thibault Normand committed
726
727
728
        break;

    case ListPopupMenu::mt_UseCase_Folder:
729
        addNewItem(currItem, UMLListViewItem::lvt_UseCase_Folder);
Thibault Normand's avatar
Thibault Normand committed
730
731
732
        break;

    case ListPopupMenu::mt_Component_Folder:
733
        addNewItem(currItem, UMLListViewItem::lvt_Component_Folder);
Thibault Normand's avatar
Thibault Normand committed
734
735
736
        break;

    case ListPopupMenu::mt_Deployment_Folder:
737
        addNewItem(currItem, UMLListViewItem::lvt_Deployment_Folder);
Thibault Normand's avatar
Thibault Normand committed
738
739
740
        break;

    case ListPopupMenu::mt_EntityRelationship_Folder:
741
        addNewItem(currItem, UMLListViewItem::lvt_EntityRelationship_Folder);
Thibault Normand's avatar
Thibault Normand committed
742
743
744
745
746
        break;

    case ListPopupMenu::mt_Cut:
        m_bStartedCut = true;
        m_bStartedCopy = false;
747
        UMLApp::app()->slotEditCut();
Thibault Normand's avatar
Thibault Normand committed
748
749
750
751
752
        break;

    case ListPopupMenu::mt_Copy:
        m_bStartedCut = false;
        m_bStartedCopy = true;
753
        UMLApp::app()->slotEditCopy();
Thibault Normand's avatar
Thibault Normand committed
754
755
756
        break;

    case ListPopupMenu::mt_Paste:
757
        UMLApp::app()->slotEditPaste();
Thibault Normand's avatar
Thibault Normand committed
758
759
        break;

760
761
762
763
764
    case ListPopupMenu::mt_Clone:
        UMLApp::app()->slotEditCopy();
        UMLApp::app()->slotEditPaste();
        break;

765
766
    case ListPopupMenu::mt_ChangeToClass:
        {
767
            UMLClassifier* o = currItem->umlObject()->asUMLClassifier();
768
769
770
771
772
773
            o->setStereotypeCmd(QString());
            currItem->updateObject();
        }
        break;
    case ListPopupMenu::mt_ChangeToPackage:
        {
774
            UMLClassifier* o = currItem->umlObject()->asUMLClassifier();
775
776
777
778
779
780
            o->setStereotypeCmd(QString());
            o->setBaseType(UMLObject::ot_Package);
            currItem->updateObject();
        }
        break;

781
782
783
784
785
786
787
    case ListPopupMenu::mt_Undefined:
        // We got signalled for a menu action, but that menu action was not
        // defined in ListPopupMenu. This is the case for "create diagram"
        // actions which are defined and handled in UMLApp. This is fine,
        // ignore them without warning.
        break;

788
    default:
789
790
        uError() << "unknown type" << menuType;

791
        break;
792

Thibault Normand's avatar
Thibault Normand committed
793
794
795
    }//end switch
}

796
797
798
799
800
801
802
803
804
805
806
807
/**
 * Find the parent folder for a diagram.
 * If the currently selected item in the list view is a folder
 * then that folder is returned as the parent.
 *
 * @param dt   The Diagram_Type of the diagram.
 *             The type will only be used if there is no currently
 *             selected item, or if the current item is not a folder.
 *             In that case the root folder which is suitable for the
 *             Diagram_Type is returned.
 * @return  Pointer to the parent UMLListViewItem for the diagram.
 */
Andi Fischer's avatar
Andi Fischer committed
808
UMLListViewItem *UMLListView::findFolderForDiagram(Uml::DiagramType::Enum dt)
809
{
Thibault Normand's avatar
Thibault Normand committed
810
    UMLListViewItem *p = static_cast<UMLListViewItem*>(currentItem());
811
812
    if (p && Model_Utils::typeIsFolder(p->type())
            && !Model_Utils::typeIsRootView(p->type())) {
Thibault Normand's avatar
Thibault Normand committed
813
814
815
        return p;
    }
    switch (dt) {
816
817
    case Uml::DiagramType::UseCase:
        p = m_lv[Uml::ModelType::UseCase];
818
        break;
819
820
    case Uml::DiagramType::Component:
        p = m_lv[Uml::ModelType::Component];
821
        break;
822
823
    case Uml::DiagramType::Deployment:
        p = m_lv[Uml::ModelType::Deployment];
824
        break;
825
826
    case Uml::DiagramType::EntityRelationship:
        p = m_lv[Uml::ModelType::EntityRelationship];
827
828
        break;
    default:
829
        p = m_lv[Uml::ModelType::Logical];
830
        break;
Thibault Normand's avatar
Thibault Normand committed
831
832
833
834
    }
    return p;
}

835
/**
836
 * Creates a new item to represent a new diagram.
837
838
 * @param id the id of the new diagram
 */
Andi Fischer's avatar
Andi Fischer committed
839
void UMLListView::slotDiagramCreated(Uml::ID::Type id)
840
{
Oliver Kellogg's avatar
Oliver Kellogg committed
841
842
    if (findItem(id)) {
        uDebug() << "list view item " << Uml::ID::toString(id) << " already exists";
Thibault Normand's avatar
Thibault Normand committed
843
        return;
844
    }
845
    UMLView *v = m_doc->findView(id);
846
847
848
    if (v) {
        UMLScene *scene = v->umlScene();
        if (scene) {
Andi Fischer's avatar
Andi Fischer committed
849
            const Uml::DiagramType::Enum dt = scene->type();
Oliver Kellogg's avatar
Oliver Kellogg committed
850
            UMLListViewItem* p = findUMLObject(scene->folder());
851
            UMLListViewItem* item = new UMLListViewItem(p, scene->name(), Model_Utils::convert_DT_LVT(dt), id);
852
            item->setSelected(true);
853
            UMLApp::app()->docWindow()->showDocumentation(scene, false);
854
        }
855
856
    } else {
        uError() << "UmlDoc::findView(" << Uml::ID::toString(id) << ") returns NULL";
857
    }
Thibault Normand's avatar
Thibault Normand committed
858
859
}

860
/**
861
 * Determine the parent ListViewItem given an UMLObject.
862
 *
863
864
865
 * @param object   Pointer to the UMLObject for which to look up the parent.
 * @return    Pointer to the parent UMLListViewItem chosen.
 *            Returns NULL on error (no parent could be determined.)
866
 */
867
868
UMLListViewItem* UMLListView::determineParentItem(UMLObject* object) const
{
869
    UMLListViewItem* parentItem = 0;
Oliver Kellogg's avatar
Oliver Kellogg committed
870
    UMLPackage*      pkg = 0;
Thibault Normand's avatar
Thibault Normand committed
871
    UMLListViewItem* current = (UMLListViewItem*) currentItem();
872
    UMLListViewItem::ListViewType lvt = UMLListViewItem::lvt_Unknown;
Thibault Normand's avatar
Thibault Normand committed
873
    if (current)
874
        lvt = current->type();
875
    UMLObject::ObjectType t = object->baseType();
Thibault Normand's avatar
Thibault Normand committed
876
877

    switch (t) {
878
879
880
881
882
    case UMLObject::ot_Attribute:
    case UMLObject::ot_Operation:
    case UMLObject::ot_Template:
    case UMLObject::ot_EnumLiteral:
    case UMLObject::ot_EntityAttribute:
883
    case UMLObject::ot_InstanceAttribute:
884
885
886
    case UMLObject::ot_UniqueConstraint:
    case UMLObject::ot_ForeignKeyConstraint:
    case UMLObject::ot_CheckConstraint:
Thibault Normand's avatar
Thibault Normand committed
887
        //this will be handled by childObjectAdded
888
        return 0;
Thibault Normand's avatar
Thibault Normand committed
889
        break;
890
891
892
    case UMLObject::ot_Association:
    case UMLObject::ot_Role:
    case UMLObject::ot_Stereotype:
893
        return 0;  // currently no representation in list view
Thibault Normand's avatar
Thibault Normand committed
894
        break;
Oliver Kellogg's avatar
Oliver Kellogg committed
895
896
    default:
        pkg = object->umlPackage();
897
898
        if (pkg) {
            UMLListViewItem* pkgItem = findUMLObject(pkg);
899
            if (pkgItem == 0)
900
                uError() << "could not find parent package " << pkg->name();
901
902
            else
                parentItem = pkgItem;
903
904
905
906
907
        } else if ((lvt == UMLListViewItem::lvt_UseCase_Folder &&
                    (t == UMLObject::ot_Actor || t == UMLObject::ot_UseCase))
                   || (lvt == UMLListViewItem::lvt_Component_Folder && t == UMLObject::ot_Component)
                   || (lvt == UMLListViewItem::lvt_Deployment_Folder && t == UMLObject::ot_Node)
                   || (lvt == UMLListViewItem::lvt_EntityRelationship_Folder && t == UMLObject::ot_Entity)) {
908
            parentItem = current;
909
        } else if (t == UMLObject::ot_Datatype) {
910
911
            parentItem = m_datatypeFolder;
        } else {
Andi Fischer's avatar
Andi Fischer committed
912
            Uml::ModelType::Enum guess = Model_Utils::guessContainer(object);
913
            parentItem = m_lv[guess];
Thibault Normand's avatar
Thibault Normand committed
914
        }
Oliver Kellogg's avatar
Oliver Kellogg committed
915
        break;
Thibault Normand's avatar
Thibault Normand committed
916
917
918
919
    }
    return parentItem;
}

920
/**
921
 * Return true if the given ObjectType permits child items.
922
923
924
 * A "child item" is anything that qualifies as a UMLClassifierListItem,
 * e.g. operations and attributes of classifiers.
 */
925
bool UMLListView::mayHaveChildItems(UMLObject::ObjectType type)
926
{
Thibault Normand's avatar
Thibault Normand committed
927
928
    bool retval = false;
    switch (type) {
929
930
    case UMLObject::ot_Class:
    case UMLObject::ot_Interface:
931
    case UMLObject::ot_Instance:
932
933
    case UMLObject::ot_Enum:
    case UMLObject::ot_Entity:  // CHECK: more?
934
935
936
937
        retval = true;
        break;
    default:
        break;
Thibault Normand's avatar
Thibault Normand committed
938
939
940
941
    }
    return retval;
}

942
/**
943
 * Creates a new list view item and connects the appropriate signals/slots.
944
945
 * @param object the newly created object
 */
946
947
void UMLListView::slotObjectCreated(UMLObject* object)
{
Thibault Normand's avatar
Thibault Normand committed
948
949
950
    if (m_bCreatingChildObject) {
        // @todo eliminate futile signal traffic
        // e.g. we get here thru various indirections from
Ralf Habacker's avatar
Ralf Habacker committed
951
        // ClassifierListPage::slot{Up, Down}Clicked()
Thibault Normand's avatar
Thibault Normand committed
952
953
        return;
    }
954
955
956
957

    if (object->baseType() == UMLObject::ot_Association)
        return;

Thibault Normand's avatar
Thibault Normand committed
958
    UMLListViewItem* newItem = findUMLObject(object);
959

Thibault Normand's avatar
Thibault Normand committed
960
    if (newItem) {
961
962
        DEBUG(DBG_SRC) << object->name() << ", type=" << newItem->type()
                       << ", id=" << Uml::ID::toString(object->id())
963
                       << ": item already exists.";
Andi Fischer's avatar
Andi Fischer committed
964
        Icon_Utils::IconType icon = Model_Utils::convert_LVT_IT(newItem->type());
Thibault Normand's avatar
Thibault Normand committed
965
966
967
        newItem->setIcon(icon);
        return;
    }
Oliver Kellogg's avatar
Oliver Kellogg committed
968
969
970
971
972
973
974
975
976
977
    UMLListViewItem* parentItem = 0;
    UMLPackage *p = object->umlPackage();
    if (p) {
        parentItem = findUMLObject(p);
        if (parentItem == 0)
            parentItem = determineParentItem(object);
    } else {
        uWarning() << object->name() << " : umlPackage not set on object";
        parentItem = determineParentItem(object);
    }
978
    if (parentItem == 0)
Thibault Normand's avatar
Thibault Normand committed
979
        return;
980
    UMLObject::ObjectType type = object->baseType();
Thibault Normand's avatar
Thibault Normand committed
981
982

    connectNewObjectsSlots(object);
983
    const UMLListViewItem::ListViewType lvt = Model_Utils::convert_OT_LVT(object);
984
    QString name = object->name();
985
    if (type == UMLObject::ot_Folder) {
986
<