1 /*
2     SPDX-License-Identifier: GPL-2.0-or-later
3     SPDX-FileCopyrightText: 2002-2021 Umbrello UML Modeller Authors <umbrello-devel@kde.org>
4 */
5 
6 // own header
7 #include "umldoc.h"
8 
9 // app includes
10 #include "debug_utils.h"
11 #include "uniqueid.h"
12 #include "association.h"
13 #include "package.h"
14 #include "folder.h"
15 #include "codegenerator.h"
16 #include "classifier.h"
17 #include "dialog_utils.h"
18 #include "enum.h"
19 #include "entity.h"
20 #include "docwindow.h"
21 #include "operation.h"
22 #include "attribute.h"
23 #include "template.h"
24 #include "enumliteral.h"
25 #include "stereotype.h"
26 #include "datatype.h"
27 #include "classifierlistitem.h"
28 #include "object_factory.h"
29 #include "import_argo.h"
30 #include "import_rose.h"
31 #include "model_utils.h"
32 #include "uml.h"
33 #include "umllistview.h"
34 #include "umllistviewitem.h"
35 #include "umlview.h"
36 #include "entityconstraint.h"
37 #include "idchangelog.h"
38 #include "umllistviewpopupmenu.h"
39 #include "cmds.h"
40 #include "diagramprintpage.h"
41 #include "umlscene.h"
42 #include "version.h"
43 #include "worktoolbar.h"
44 #include "models/diagramsmodel.h"
45 #include "models/objectsmodel.h"
46 #include "models/stereotypesmodel.h"
47 
48 // kde includes
49 #include <kio/job.h>
50 #if QT_VERSION < 0x050000
51 #include <kio/netaccess.h>
52 #endif
53 #if QT_VERSION >= 0x050000
54 #include <KJobWidgets>
55 #endif
56 #include <KLocalizedString>
57 #include <KMessageBox>
58 #if QT_VERSION < 0x050000
59 #include <kmimetype.h>
60 #endif
61 #include <ktar.h>
62 #if QT_VERSION < 0x050000
63 #include <ktempdir.h>
64 #include <ktabwidget.h>
65 #include <ktemporaryfile.h>
66 #endif
67 
68 // qt includes
69 #include <QApplication>
70 #include <QBuffer>
71 #include <QDateTime>
72 #include <QDesktopWidget>
73 #include <QDir>
74 #include <QDomDocument>
75 #include <QDomElement>
76 #include <QListWidget>
77 #if QT_VERSION >= 0x050000
78 #include <QMimeDatabase>
79 #endif
80 #include <QPainter>
81 #include <QPrinter>
82 #include <QRegExp>
83 #if QT_VERSION >= 0x050000
84 #include <QTemporaryDir>
85 #include <QTemporaryFile>
86 #endif
87 #include <QTextStream>
88 #include <QTimer>
89 #include <QXmlStreamWriter>
90 
91 DEBUG_REGISTER(UMLDoc)
92 
93 class UMLDoc::Private
94 {
95 public:
96     UMLDoc *parent;
97     QStringList errors; ///< holds loading errors
98 
Private(UMLDoc * p)99     Private(UMLDoc *p) : parent(p) {}
100 };
101 
102 /**
103  * Constructor for the fileclass of the application.
104  */
UMLDoc()105 UMLDoc::UMLDoc()
106   : m_d(new Private(this)),
107     m_datatypeRoot(0),
108     m_stereoList(UMLStereotypeList()),
109     m_Name(i18n("UML Model")),
110     m_modelID("m1"),
111     m_count(0),
112     m_modified(false),
113 #if QT_VERSION >= 0x050000
114     m_doc_url(QUrl()),
115 #else
116     m_doc_url(KUrl()),
117 #endif
118     m_pChangeLog(0),
119     m_bLoading(false),
120     m_importing(false),
121     m_Doc(QString()),
122     m_pAutoSaveTimer(0),
123     m_nViewID(Uml::ID::None),
124     m_bTypesAreResolved(true),
125     m_pCurrentRoot(0),
126     m_bClosing(false),
127     m_diagramsModel(new DiagramsModel),
128     m_objectsModel(new ObjectsModel),
129     m_stereotypesModel(new StereotypesModel(m_stereoList)),
130     m_resolution(0.0)
131 {
132     for (int i = 0; i < Uml::ModelType::N_MODELTYPES; ++i)
133         m_root[i] = 0;
134 }
135 
136 /**
137  * Initialize the UMLDoc.
138  * To be called after the constructor, before anything else.
139  */
init()140 void UMLDoc::init()
141 {
142     // Initialize predefined folders.
143     const char* nativeRootName[Uml::ModelType::N_MODELTYPES] = {
144         "Logical View",
145         "Use Case View",
146         "Component View",
147         "Deployment View",
148         "Entity Relationship Model"
149     };
150     const QString localizedRootName[Uml::ModelType::N_MODELTYPES] = {
151         i18n("Logical View"),
152         i18n("Use Case View"),
153         i18n("Component View"),
154         i18n("Deployment View"),
155         i18n("Entity Relationship Model")
156     };
157     for (int i = 0; i < Uml::ModelType::N_MODELTYPES; ++i) {
158         const QString rootName = QString::fromLatin1(nativeRootName[i]);
159         QString id = rootName;
160         id.replace(QLatin1Char(' '), QLatin1Char('_'));
161         m_root[i] = new UMLFolder(rootName, Uml::ID::fromString(id));
162         m_root[i]->setLocalName(localizedRootName[i]);
163     }
164     createDatatypeFolder();
165 
166     // Connect signals.
167     UMLApp * pApp = UMLApp::app();
168     connect(this, SIGNAL(sigDiagramCreated(Uml::ID::Type)), pApp, SLOT(slotUpdateViews()));
169     connect(this, SIGNAL(sigDiagramRemoved(Uml::ID::Type)), pApp, SLOT(slotUpdateViews()));
170     connect(this, SIGNAL(sigDiagramRenamed(Uml::ID::Type)), pApp, SLOT(slotUpdateViews()));
171     connect(this, SIGNAL(sigCurrentViewChanged()),          pApp, SLOT(slotCurrentViewChanged()));
172 }
173 
174 /**
175  * Create the datatype folder and add it to the logical folder.
176  */
createDatatypeFolder()177 void UMLDoc::createDatatypeFolder()
178 {
179     delete m_datatypeRoot;
180     m_datatypeRoot = new UMLFolder(QLatin1String("Datatypes"), "Datatypes");
181     m_datatypeRoot->setLocalName(i18n("Datatypes"));
182     m_datatypeRoot->setUMLPackage(m_root[Uml::ModelType::Logical]);
183     Q_ASSERT(m_root[Uml::ModelType::Logical]);
184     m_root[Uml::ModelType::Logical]->addObject(m_datatypeRoot);
185 }
186 
187 /**
188  * Destructor for the fileclass of the application.
189  */
~UMLDoc()190 UMLDoc::~UMLDoc()
191 {
192     UMLApp * pApp = UMLApp::app();
193     disconnect(this, SIGNAL(sigDiagramCreated(Uml::ID::Type)), pApp, SLOT(slotUpdateViews()));
194     disconnect(this, SIGNAL(sigDiagramRemoved(Uml::ID::Type)), pApp, SLOT(slotUpdateViews()));
195     disconnect(this, SIGNAL(sigDiagramRenamed(Uml::ID::Type)), pApp, SLOT(slotUpdateViews()));
196     disconnect(this, SIGNAL(sigCurrentViewChanged()),          pApp, SLOT(slotCurrentViewChanged()));
197 
198     disconnect(m_pAutoSaveTimer, SIGNAL(timeout()), this, SLOT(slotAutoSave()));
199     delete m_pAutoSaveTimer;
200 
201     m_root[Uml::ModelType::Logical]->removeObject(m_datatypeRoot);
202     delete m_datatypeRoot;
203 
204     for (int i = 0; i < Uml::ModelType::N_MODELTYPES; ++i) {
205         delete m_root[i];
206     }
207     delete m_pChangeLog;
208     qDeleteAll(m_stereoList);
209     delete m_stereotypesModel;
210     delete m_diagramsModel;
211     delete m_objectsModel;
212     delete m_d;
213 }
214 
215 /**
216  * Adds a view to the document which represents the document
217  * contents. Usually this is your main view.
218  *
219  * @param view   Pointer to the UMLView to add.
220  */
addView(UMLView * view)221 void UMLDoc::addView(UMLView *view)
222 {
223     if (view == 0) {
224         uError() << "argument is NULL";
225         return;
226     }
227     UMLFolder *f = view->umlScene()->folder();
228     if (f == 0) {
229         uError() << "view folder is not set";
230         return;
231     }
232     DEBUG(DBG_SRC) << view->umlScene()->name() << " to folder " << *f << " (" << f->name() << ")";
233     f->addView(view);
234     m_diagramsModel->addDiagram(view);
235 
236     UMLApp * pApp = UMLApp::app();
237     if (pApp->listView()) {
238         connect(this, SIGNAL(sigObjectRemoved(UMLObject*)), view->umlScene(), SLOT(slotObjectRemoved(UMLObject*)));
239     }
240 
241     if (!m_bLoading || pApp->currentView() == 0) {
242         pApp->setCurrentView(view);
243     }
244     if (!m_bLoading) {
245         view->show();
246         emit sigDiagramChanged(view->umlScene()->type());
247     }
248 
249     pApp->setDiagramMenuItemsState(true);
250     pApp->slotUpdateViews();
251 }
252 
253 /**
254  * Removes a view from the list of currently connected views.
255  *
256  * @param view             Pointer to the UMLView to remove.
257  * @param enforceCurrentView   Switch to determine if we have a current view or not.
258  *                         Most of the time, we DO want this, except when exiting the program.
259  */
removeView(UMLView * view,bool enforceCurrentView)260 void UMLDoc::removeView(UMLView *view, bool enforceCurrentView)
261 {
262     if (!view) {
263         uError() << "UMLDoc::removeView(UMLView *view) called with view = 0";
264         return;
265     }
266     DEBUG(DBG_SRC) << "<" << view->umlScene()->name() << ">";
267     if (UMLApp::app()->listView()) {
268         disconnect(this, SIGNAL(sigObjectRemoved(UMLObject*)),
269                    view->umlScene(), SLOT(slotObjectRemoved(UMLObject*)));
270     }
271     view->hide();
272     UMLFolder *f = view->umlScene()->folder();
273     if (f == 0) {
274         uError() << view->umlScene()->name() << ": view->getFolder() returns NULL";
275         return;
276     }
277     m_diagramsModel->removeDiagram(view);
278     f->removeView(view);
279     UMLView *currentView = UMLApp::app()->currentView();
280     if (currentView == view) {
281         UMLApp::app()->setCurrentView(0);
282         UMLViewList viewList;
283         m_root[Uml::ModelType::Logical]->appendViews(viewList);
284         UMLView* firstView = 0;
285         if (!viewList.isEmpty()) {
286             firstView =  viewList.first();
287         }
288 
289         if (!firstView && enforceCurrentView) {  //create a diagram
290             QString name = createDiagramName(Uml::DiagramType::Class, false);
291             createDiagram(m_root[Uml::ModelType::Logical], Uml::DiagramType::Class, name);
292             qApp->processEvents();
293             m_root[Uml::ModelType::Logical]->appendViews(viewList);
294             firstView = viewList.first();
295         }
296 
297         if (firstView) {
298             changeCurrentView(firstView->umlScene()->ID());
299             UMLApp::app()->setDiagramMenuItemsState(true);
300         }
301     }
302     delete view;
303 }
304 
305 /**
306  * Sets the URL of the document.
307  *
308  * @param url   The KUrl to set.
309  */
310 #if QT_VERSION >= 0x050000
setUrl(const QUrl & url)311 void UMLDoc::setUrl(const QUrl &url)
312 #else
313 void UMLDoc::setUrl(const KUrl &url)
314 #endif
315 {
316     m_doc_url = url;
317 }
318 
319 /**
320  * Returns the KUrl of the document.
321  *
322  * @return  The KUrl of this UMLDoc.
323  */
324 #if QT_VERSION >= 0x050000
url() const325 const QUrl& UMLDoc::url() const
326 #else
327 const KUrl& UMLDoc::url() const
328 #endif
329 {
330     return m_doc_url;
331 }
332 
333 /**
334  * Sets the URL of the document to "Untitled".
335  */
setUrlUntitled()336 void UMLDoc::setUrlUntitled()
337 {
338 #if QT_VERSION >= 0x050000
339     m_doc_url.setUrl(m_doc_url.toString(QUrl::RemoveFilename) + i18n("Untitled"));
340 #else
341     m_doc_url.setFileName(i18n("Untitled"));
342 #endif
343 }
344 
345 /**
346  * "save modified" - Asks the user for saving if the document
347  * is modified.
348  *
349  * @return  True if document can be closed.
350  */
saveModified()351 bool UMLDoc::saveModified()
352 {
353     bool completed(true);
354     if (!m_modified) {
355         return completed;
356     }
357 
358     UMLApp *win = UMLApp::app();
359     int want_save = KMessageBox::warningYesNoCancel(win,
360                                      i18n("The current file has been modified.\nDo you want to save it?"),
361                                      i18nc("warning message", "Warning"),
362                                      KStandardGuiItem::save(), KStandardGuiItem::discard());
363     switch(want_save) {
364     case KMessageBox::Yes:
365         if (m_doc_url.fileName() == i18n("Untitled")) {
366             if (win->slotFileSaveAs()) {
367                 closeDocument();
368                 completed=true;
369             } else {
370                 completed=false;
371             }
372         } else {
373             saveDocument(url());
374             closeDocument();
375             completed=true;
376         }
377         break;
378 
379     case KMessageBox::No:
380         setModified(false);
381         closeDocument();
382         completed=true;
383         break;
384 
385     case KMessageBox::Cancel:
386         completed=false;
387         break;
388 
389     default:
390         completed=false;
391         break;
392     }
393     return completed;
394 }
395 
396 /**
397  * Closes the current document.
398  */
closeDocument()399 void UMLDoc::closeDocument()
400 {
401     m_bClosing = true;
402     UMLApp::app()->setGenerator(Uml::ProgrammingLanguage::Reserved);  // delete the codegen
403     m_Doc = QString();
404     DocWindow* dw = UMLApp::app()->docWindow();
405     if (dw) {
406         dw->reset();
407     }
408     UMLApp::app()->logWindow()->clear();
409 
410     UMLListView *listView = UMLApp::app()->listView();
411     if (listView) {
412         listView->clean();
413         // store old setting - for restore of last setting
414         bool m_bLoading_old = m_bLoading;
415         m_bLoading = true; // This is to prevent document becoming modified.
416         // For reference, here is an example of a call sequence that would
417         // otherwise result in futile addToUndoStack() calls:
418         //  removeAllViews()  =>
419         //   UMLView::removeAllAssociations()  =>
420         //    UMLView::removeAssoc()  =>
421         //     UMLDoc::setModified(true, true)  =>
422         //      addToUndoStack().
423         removeAllViews();
424         m_bLoading = m_bLoading_old;
425         // Remove all objects from the predefined folders.
426         // @fixme With advanced code generation enabled, this crashes.
427         removeAllObjects();
428 
429         // Remove any stereotypes.
430         if (stereotypes().count() > 0) {
431             foreach(UMLStereotype *s, stereotypes()) {
432                 m_stereotypesModel->removeStereotype(s);
433                 delete s;
434             }
435             m_stereoList.clear();
436         }
437 
438         // Restore the datatype folder, it has been deleted above.
439         createDatatypeFolder();
440         // this creates too many items, only Logical View should be created
441         listView->init();
442     }
443     m_bClosing = false;
444 }
445 
446 /**
447  * Initializes the document generally.
448  *
449  * @return  True if operation successful.
450  */
newDocument()451 bool UMLDoc::newDocument()
452 {
453     bool state = UMLApp::app()->document()->loading();
454     UMLApp::app()->document()->setLoading(true);
455 
456     closeDocument();
457     UMLApp::app()->setCurrentView(0);
458     setUrlUntitled();
459     setResolution(qApp->desktop()->logicalDpiX());
460     //see if we need to start with a new diagram
461     Settings::OptionState optionState = Settings::optionState();
462     Uml::DiagramType::Enum dt = optionState.generalState.diagram;
463     Uml::ModelType::Enum mt = Model_Utils::convert_DT_MT(dt);
464     if (mt == Uml::ModelType::N_MODELTYPES) {  // don't allow no diagram
465         dt = Uml::DiagramType::Class;
466         mt = Uml::ModelType::Logical;
467     }
468     QString name = createDiagramName(dt, false);
469     createDiagram(m_root[mt], dt, name);
470 
471     UMLApp::app()->initGenerator();
472 
473     setModified(false);
474     initSaveTimer();
475 
476     UMLApp::app()->enableUndoAction(false);
477     UMLApp::app()->clearUndoStack();
478 
479     UMLApp::app()->document()->setLoading(state);
480     return true;
481 }
482 
483 /**
484  * Loads the document by filename and format and emits the
485  * updateViews() signal.
486  *
487  * @param url      The filename in KUrl format.
488  * @param format   The format (optional.)
489  * @return  True if operation successful.
490  */
491 #if QT_VERSION >= 0x050000
openDocument(const QUrl & url,const char * format)492 bool UMLDoc::openDocument(const QUrl& url, const char* format /* =0 */)
493 #else
494 bool UMLDoc::openDocument(const KUrl& url, const char* format /* =0 */)
495 #endif
496 {
497     Q_UNUSED(format);
498     if (url.fileName().length() == 0) {
499         newDocument();
500         return false;
501     }
502 
503     m_doc_url = url;
504     closeDocument();
505     setResolution(0.0);
506     // IMPORTANT: set m_bLoading to true
507     // _AFTER_ the call of UMLDoc::closeDocument()
508     // as it sets m_bLoading to false after it was temporarily
509     // changed to true to block recording of changes in redo-buffer
510     m_bLoading = true;
511     m_d->errors.clear();
512 #if QT_VERSION >= 0x050000
513     QTemporaryFile tmpfile;
514     tmpfile.open();
515     QUrl dest(QUrl::fromLocalFile(tmpfile.fileName()));
516     DEBUG(DBG_SRC) << "UMLDoc::openDocument: copy from " << url << " to " << dest << ".";
517     KIO::FileCopyJob *job = KIO::file_copy(url, dest, -1, KIO::Overwrite);
518     KJobWidgets::setWindow(job, UMLApp::app());
519     job->exec();
520     QFile file(tmpfile.fileName());
521     if (job->error() || !file.exists()) {
522         if (!file.exists())
523             DEBUG(DBG_SRC) << "UMLDoc::openDocument: temporary file <" << file.fileName() << "> failed!";
524         if (job->error())
525            DEBUG(DBG_SRC) << "UMLDoc::openDocument: " << job->errorString();
526         KMessageBox::error(0, i18n("The file <%1> does not exist.", url.toString()), i18n("Load Error"));
527         setUrlUntitled();
528         m_bLoading = false;
529         newDocument();
530         return false;
531     }
532 #else
533     QString tmpfile;
534     KIO::NetAccess::download(url, tmpfile, UMLApp::app());
535 
536     QFile file(tmpfile);
537     if (!file.exists()) {
538         KMessageBox::error(0, i18n("The file %1 does not exist.", url.pathOrUrl()), i18n("Load Error"));
539         setUrlUntitled();
540         m_bLoading = false;
541         newDocument();
542         return false;
543     }
544 #endif
545     // status of XMI loading
546     bool status = false;
547 
548     // check if the xmi file is a compressed archive like tar.bzip2 or tar.gz
549     QString filetype = m_doc_url.fileName();
550     QString mimetype;
551     if (filetype.endsWith(QLatin1String(".tgz")) || filetype.endsWith(QLatin1String(".tar.gz"))) {
552         mimetype = QLatin1String("application/x-gzip");
553     } else if (filetype.endsWith(QLatin1String(".tar.bz2"))) {
554         mimetype = QLatin1String("application/x-bzip");
555     }
556 
557     if (mimetype.isEmpty() == false) {
558 #if QT_VERSION >= 0x050000
559         KTar archive(file.fileName(), mimetype);
560 #else
561         KTar archive(tmpfile, mimetype);
562 #endif
563         if (archive.open(QIODevice::ReadOnly) == false) {
564 #if QT_VERSION >= 0x050000
565             KMessageBox::error(0, i18n("The file %1 seems to be corrupted.", url.toString()), i18n("Load Error"));
566 #else
567             KMessageBox::error(0, i18n("The file %1 seems to be corrupted.", url.pathOrUrl()), i18n("Load Error"));
568             KIO::NetAccess::removeTempFile(tmpfile);
569 #endif
570             setUrlUntitled();
571             m_bLoading = false;
572             newDocument();
573             return false;
574         }
575 
576         // get the root directory and all entries in
577         const KArchiveDirectory * rootDir = archive.directory();
578         const QStringList entries = rootDir->entries();
579         QString entryMimeType;
580         bool foundXMI = false;
581         QStringList::ConstIterator it;
582         QStringList::ConstIterator end(entries.end());
583 
584         // now go through all entries till we find an xmi file
585         for (it = entries.begin(); it != end; ++it) {
586             // only check files, we do not go in subdirectories
587             if (rootDir->entry(*it)->isFile() == true) {
588                 // we found a file, check the mimetype
589 #if QT_VERSION >= 0x050000
590                 QMimeDatabase db;
591                 entryMimeType = db.mimeTypeForFile(*it, QMimeDatabase::MatchExtension).name();
592 #else
593                 entryMimeType = KMimeType::findByPath(*it, 0, true)->name();
594 #endif
595                 if (entryMimeType == QLatin1String("application/x-uml")) {
596                     foundXMI = true;
597                     break;
598                 }
599             }
600         }
601 
602         // if we found an XMI file, we have to extract it to a temporary file
603         if (foundXMI == true) {
604 #if QT_VERSION >= 0x050000
605             QTemporaryDir tmp_dir;
606 #else
607             KTempDir tmp_dir;
608 #endif
609             KArchiveEntry * entry;
610             KArchiveFile * fileEntry;
611 
612             // try to cast the file entry in the archive to an archive entry
613             entry = const_cast<KArchiveEntry*>(rootDir->entry(*it));
614             if (entry == 0) {
615 #if QT_VERSION >= 0x050000
616                 KMessageBox::error(0, i18n("There was no XMI file found in the compressed file %1.", url.toString()),
617                                    i18n("Load Error"));
618 #else
619                 KMessageBox::error(0, i18n("There was no XMI file found in the compressed file %1.", url.pathOrUrl()),
620                                    i18n("Load Error"));
621                 KIO::NetAccess::removeTempFile(tmpfile);
622 #endif
623                 setUrlUntitled();
624                 m_bLoading = false;
625                 newDocument();
626                 return false;
627             }
628 
629             // now try to cast the archive entry to a file entry, so that we can
630             // extract the file
631             fileEntry = dynamic_cast<KArchiveFile*>(entry);
632             if (fileEntry == 0) {
633 #if QT_VERSION >= 0x050000
634                 KMessageBox::error(0, i18n("There was no XMI file found in the compressed file %1.", url.toString()),
635                                    i18n("Load Error"));
636 #else
637                 KMessageBox::error(0, i18n("There was no XMI file found in the compressed file %1.", url.pathOrUrl()),
638                                    i18n("Load Error"));
639                 KIO::NetAccess::removeTempFile(tmpfile);
640 #endif
641                 setUrlUntitled();
642                 m_bLoading = false;
643                 newDocument();
644                 return false;
645             }
646 
647             // now we can extract the file to the temporary directory
648 #if QT_VERSION >= 0x050000
649             fileEntry->copyTo(tmp_dir.path() + QLatin1Char('/'));
650 
651             // now open the extracted file for reading
652             QFile xmi_file(tmp_dir.path() + QLatin1Char('/') + *it);
653 #else
654             fileEntry->copyTo(tmp_dir.name());
655 
656             // now open the extracted file for reading
657             QFile xmi_file(tmp_dir.name() + *it);
658 #endif
659             if(!xmi_file.open(QIODevice::ReadOnly)) {
660 #if QT_VERSION >= 0x050000
661                 KMessageBox::error(0, i18n("There was a problem loading the extracted file: %1", url.toString()),
662                                    i18n("Load Error"));
663 #else
664                 KMessageBox::error(0, i18n("There was a problem loading the extracted file: %1", url.pathOrUrl()),
665                                    i18n("Load Error"));
666                 KIO::NetAccess::removeTempFile(tmpfile);
667 #endif
668                 setUrlUntitled();
669                 m_bLoading = false;
670                 newDocument();
671                 return false;
672             }
673             m_bTypesAreResolved = false;
674             status = loadFromXMI1(xmi_file, ENC_UNKNOWN);
675 
676             // close the extracted file and the temporary directory
677             xmi_file.close();
678         } else {
679 #if QT_VERSION >= 0x050000
680                 KMessageBox::error(0, i18n("There was no XMI file found in the compressed file %1.", url.toString()),
681                                    i18n("Load Error"));
682 #else
683             KMessageBox::error(0, i18n("There was no XMI file found in the compressed file %1.", url.pathOrUrl()),
684                                i18n("Load Error"));
685             KIO::NetAccess::removeTempFile(tmpfile);
686 #endif
687             setUrlUntitled();
688             m_bLoading = false;
689             newDocument();
690             return false;
691         }
692 
693         archive.close();
694     } else {
695         // no, it seems to be an ordinary file
696         if (!file.open(QIODevice::ReadOnly)) {
697 #if QT_VERSION >= 0x050000
698             KMessageBox::error(0, i18n("There was a problem loading file: %1", url.toString()),
699                                i18n("Load Error"));
700 #else
701             KMessageBox::error(0, i18n("There was a problem loading file: %1", url.pathOrUrl()),
702                                i18n("Load Error"));
703             KIO::NetAccess::removeTempFile(tmpfile);
704 #endif
705             setUrlUntitled();
706             m_bLoading = false;
707             newDocument();
708             return false;
709         }
710         if (filetype.endsWith(QLatin1String(".mdl"))) {
711             setUrlUntitled();
712             m_bTypesAreResolved = false;
713             status = Import_Rose::loadFromMDL(file);
714             if (status) {
715                 if (UMLApp::app()->currentView() == 0) {
716                     QString name = createDiagramName(Uml::DiagramType::Class, false);
717                     createDiagram(m_root[Uml::ModelType::Logical], Uml::DiagramType::Class, name);
718                     setCurrentRoot(Uml::ModelType::Logical);
719                 }
720             }
721         }
722         else if (filetype.endsWith(QLatin1String(".zargo"))) {
723             setUrlUntitled();
724             status = Import_Argo::loadFromZArgoFile(file);
725         }
726         else {
727             m_bTypesAreResolved = false;
728             status = loadFromXMI1(file, ENC_UNKNOWN);
729         }
730     }
731 
732     if (file.isOpen())
733         file.close();
734 #if QT_VERSION < 0x050000
735     KIO::NetAccess::removeTempFile(tmpfile);
736 #endif
737     m_bLoading = false;
738     m_bTypesAreResolved = true;
739     if (!status) {
740 #if QT_VERSION >= 0x050000
741         QString msg = i18n("There was a problem loading file: %1", url.toString());
742 #else
743         QString msg = i18n("There was a problem loading file: %1", url.pathOrUrl());
744 #endif
745         if (m_d->errors.size() > 0)
746             msg += QLatin1String("<br/>") + i18n("Reason: %1", m_d->errors.join(QLatin1String("<br/>")));
747         KMessageBox::error(nullptr, msg, i18n("Load Error"));
748         newDocument();
749         return false;
750     }
751     setModified(false);
752     initSaveTimer();
753 
754     UMLApp::app()->enableUndoAction(false);
755     UMLApp::app()->clearUndoStack();
756     // for compatibility
757     addDefaultStereotypes();
758 
759     return true;
760 }
761 
762 /**
763  * Saves the document using the given filename and format.
764  *
765  * @param url      The filename in KUrl format.
766  * @param format   The format (optional.)
767  * @return  True if operation successful.
768  */
769 #if QT_VERSION >= 0x050000
saveDocument(const QUrl & url,const char * format)770 bool UMLDoc::saveDocument(const QUrl& url, const char * format)
771 #else
772 bool UMLDoc::saveDocument(const KUrl& url, const char * format)
773 #endif
774 {
775     Q_UNUSED(format);
776     m_doc_url = url;
777     bool uploaded = true;
778 
779     // first, we have to find out which format to use
780 #if QT_VERSION >= 0x050000
781     QString strFileName = url.path();
782 #else
783     QString strFileName = url.path(KUrl::RemoveTrailingSlash);
784 #endif
785     QFileInfo fileInfo(strFileName);
786     QString fileExt = fileInfo.completeSuffix();
787     QString fileFormat = QLatin1String("xmi");
788     if (fileExt == QLatin1String("xmi") || fileExt == QLatin1String("bak.xmi")) {
789         fileFormat = QLatin1String("xmi");
790     } else if (fileExt == QLatin1String("xmi.tgz") || fileExt == QLatin1String("bak.xmi.tgz")) {
791         fileFormat = QLatin1String("tgz");
792     } else if (fileExt == QLatin1String("xmi.tar.bz2") || fileExt == QLatin1String("bak.xmi.tar.bz2")) {
793         fileFormat = QLatin1String("bz2");
794     } else {
795         fileFormat = QLatin1String("xmi");
796     }
797 
798     initSaveTimer();
799 
800     if (fileFormat == QLatin1String("tgz") || fileFormat == QLatin1String("bz2")) {
801         KTar * archive;
802 #if QT_VERSION >= 0x050000
803         QTemporaryFile tmp_tgz_file;
804 #else
805         KTemporaryFile tmp_tgz_file;
806 #endif
807         tmp_tgz_file.setAutoRemove(false);
808         tmp_tgz_file.open();
809 
810         // first we have to check if we are saving to a local or remote file
811         if (url.isLocalFile()) {
812             if (fileFormat == QLatin1String("tgz")) {  // check tgz or bzip
813                 archive = new KTar(url.toLocalFile(), QLatin1String("application/x-gzip"));
814             } else {
815                 archive = new KTar(url.toLocalFile(), QLatin1String("application/x-bzip"));
816             }
817         } else {
818             if (fileFormat == QLatin1String("tgz")) {  // check tgz or bzip2
819                 archive = new KTar(tmp_tgz_file.fileName(), QLatin1String("application/x-gzip"));
820             } else {
821                 archive = new KTar(tmp_tgz_file.fileName(), QLatin1String("application/x-bzip"));
822             }
823         }
824 
825         // now check if we can write to the file
826         if (archive->open(QIODevice::WriteOnly) == false) {
827             uError() << "could not open" << archive->fileName();
828 #if QT_VERSION >= 0x050000
829             KMessageBox::error(0, i18n("There was a problem saving: %1", url.url(QUrl::PreferLocalFile)), i18n("Save Error"));
830 #else
831             KMessageBox::error(0, i18n("There was a problem saving file: %1", url.pathOrUrl()), i18n("Save Error"));
832 #endif
833             delete archive;
834             return false;
835         }
836 
837         // we have to create a temporary xmi file
838         // we will add this file later to the archive
839 #if QT_VERSION >= 0x050000
840         QTemporaryFile tmp_xmi_file;
841 #else
842         KTemporaryFile tmp_xmi_file;
843 #endif
844         tmp_xmi_file.setAutoRemove(false);
845         if (!tmp_xmi_file.open()) {
846             uError() << "could not open" << tmp_xmi_file.fileName();
847 #if QT_VERSION >= 0x050000
848             KMessageBox::error(0, i18n("There was a problem saving: %1", url.url(QUrl::PreferLocalFile)), i18n("Save Error"));
849 #else
850             KMessageBox::error(0, i18n("There was a problem saving file: %1", url.pathOrUrl()), i18n("Save Error"));
851 #endif
852             delete archive;
853             return false;
854         }
855         saveToXMI1(tmp_xmi_file); // save XMI to this file...
856 
857         // now add this file to the archive, but without the extension
858         QString tmpQString = url.fileName();
859         if (fileFormat == QLatin1String("tgz")) {
860             tmpQString.remove(QRegExp(QLatin1String("\\.tgz$")));
861         }
862         else {
863             tmpQString.remove(QRegExp(QLatin1String("\\.tar\\.bz2$")));
864         }
865         archive->addLocalFile(tmp_xmi_file.fileName(), tmpQString);
866 
867         if (!archive->close()) {
868             uError() << "could not close" << archive->fileName();
869 #if QT_VERSION >= 0x050000
870             KMessageBox::error(0, i18n("There was a problem saving: %1", url.url(QUrl::PreferLocalFile)), i18n("Save Error"));
871 #else
872             KMessageBox::error(0, i18n("There was a problem saving file: %1", url.pathOrUrl()), i18n("Save Error"));
873 #endif
874             delete archive;
875             return false;
876         }
877         // now the xmi file was added to the archive, so we can delete it
878         tmp_xmi_file.setAutoRemove(true);
879 
880         // now we have to check, if we have to upload the file
881         if (!url.isLocalFile()) {
882 #if QT_VERSION >= 0x050000
883             KIO::FileCopyJob *job = KIO::file_copy(QUrl::fromLocalFile(tmp_tgz_file.fileName()), m_doc_url);
884             KJobWidgets::setWindow(job, UMLApp::app());
885             job->exec();
886             uploaded = !job->error();
887 #else
888             uploaded = KIO::NetAccess::upload(tmp_tgz_file.fileName(), m_doc_url, UMLApp::app());
889 #endif
890             if (!uploaded)
891                 uError() << "could not upload file" << tmp_tgz_file.fileName() << "to" << url;
892         }
893 
894         // now the archive was written to disk (or remote) so we can delete the
895         // objects
896         tmp_tgz_file.setAutoRemove(true);
897         delete archive;
898 
899     }
900     else {
901         // save as normal uncompressed XMI
902 
903 #if QT_VERSION >= 0x050000
904         QTemporaryFile tmpfile; // we need this tmp file if we are writing to a remote file
905 #else
906         KTemporaryFile tmpfile; // we need this tmp file if we are writing to a remote file
907 #endif
908         tmpfile.setAutoRemove(false);
909 
910         // save in _any_ case to a temp file
911         // -> if something goes wrong during saveToXMI1, the
912         //     original content is preserved
913         //     (e.g. if umbrello dies in the middle of the document model parsing
914         //      for saveToXMI1 due to some problems)
915         /// @todo insert some checks in saveToXMI1 to detect a failed save attempt
916 
917         // lets open the file for writing
918         if (!tmpfile.open()) {
919             uError() << "could not open" << tmpfile.fileName();
920 #if QT_VERSION >= 0x050000
921             KMessageBox::error(0, i18n("There was a problem saving: %1", url.url(QUrl::PreferLocalFile)), i18n("Save Error"));
922 #else
923             KMessageBox::error(0, i18n("There was a problem saving file: %1", url.pathOrUrl()), i18n("Save Error"));
924 #endif
925             return false;
926         }
927         saveToXMI1(tmpfile); // save the xmi stuff to it
928         tmpfile.close();
929 
930         // if it is a remote file, we have to upload the tmp file
931         if (!url.isLocalFile()) {
932 #if QT_VERSION >= 0x050000
933             KIO::FileCopyJob *job = KIO::file_copy(QUrl::fromLocalFile(tmpfile.fileName()), m_doc_url);
934             KJobWidgets::setWindow(job, UMLApp::app());
935             job->exec();
936             uploaded = !job->error();
937 #else
938             uploaded = KIO::NetAccess::upload(tmpfile.fileName(), m_doc_url, UMLApp::app());
939 #endif
940             if (!uploaded)
941                 uError() << "could not upload file" << tmpfile.fileName() << "to" << url;
942         }
943         else {
944             // now remove the original file
945 #ifdef Q_OS_WIN
946             tmpfile.setAutoRemove(true);
947 #if QT_VERSION >= 0x050000
948             KIO::FileCopyJob* fcj = KIO::file_copy(QUrl::fromLocalFile(tmpfile.fileName()), url, -1, KIO::Overwrite);
949 #else
950             KIO::FileCopyJob* fcj = KIO::file_copy(tmpfile.fileName(), url, -1, KIO::Overwrite);
951 #endif
952 #else
953 #if QT_VERSION >= 0x050000
954             KIO::FileCopyJob* fcj = KIO::file_move(QUrl::fromLocalFile(tmpfile.fileName()), url, -1, KIO::Overwrite);
955 #else
956             KIO::FileCopyJob* fcj = KIO::file_move(tmpfile.fileName(), url, -1, KIO::Overwrite);
957 #endif
958 #endif
959 #if QT_VERSION >= 0x050000
960             KJobWidgets::setWindow(fcj, (QWidget*)UMLApp::app());
961             fcj->exec();
962             if (fcj->error()) {
963                 uError() << "Could not move" << tmpfile.fileName() << "to" << url;
964                 KMessageBox::error(0, i18n("There was a problem saving: %1", url.url(QUrl::PreferLocalFile)), i18n("Save Error"));
965 #else
966             if (KIO::NetAccess::synchronousRun(fcj, (QWidget*)UMLApp::app()) == false) {
967                 KMessageBox::error(0, i18n("There was a problem saving file: %1", url.pathOrUrl()), i18n("Save Error"));
968 #endif
969                 setUrlUntitled();
970                 return false;
971             }
972         }
973     }
974     if (!uploaded) {
975 #if QT_VERSION >= 0x050000
976         KMessageBox::error(0, i18n("There was a problem uploading: %1", url.url(QUrl::PreferLocalFile)), i18n("Save Error"));
977 #else
978         KMessageBox::error(0, i18n("There was a problem uploading file: %1", url.pathOrUrl()), i18n("Save Error"));
979 #endif
980         setUrlUntitled();
981     }
982     setModified(false);
983     return uploaded;
984 }
985 
986 /**
987  * Sets up the signals needed by the program for it to work.
988  */
989 void UMLDoc::setupSignals()
990 {
991     WorkToolBar *tb = UMLApp::app()->workToolBar();
992     connect(this, SIGNAL(sigDiagramChanged(Uml::DiagramType::Enum)), tb, SLOT(slotCheckToolBar(Uml::DiagramType::Enum)));
993 }
994 
995 /**
996  * Finds a view (diagram) by the ID given to method.
997  *
998  * @param id   The ID of the view to search for.
999  * @return  Pointer to the view found, or NULL if not found.
1000  */
1001 UMLView * UMLDoc::findView(Uml::ID::Type id) const
1002 {
1003     UMLView *v = 0;
1004     for (int i = 0; i < Uml::ModelType::N_MODELTYPES; ++i) {
1005         v = m_root[i]->findView(id);
1006         if (v) {
1007             break;
1008         }
1009     }
1010     return v;
1011 }
1012 
1013 /**
1014  * Finds a view (diagram) by the type and name given.
1015  *
1016  * @param type            The type of view to find.
1017  * @param name            The name of the view to find.
1018  * @param searchAllScopes Search in all subfolders (default: false.)
1019  * @return  Pointer to the view found, or NULL if not found.
1020  */
1021 UMLView * UMLDoc::findView(Uml::DiagramType::Enum type, const QString &name,
1022                            bool searchAllScopes /* =false */) const
1023 {
1024     Uml::ModelType::Enum mt = Model_Utils::convert_DT_MT(type);
1025     if (mt == Uml::ModelType::N_MODELTYPES) {
1026         uWarning() << "UMLDoc::findView : Returning null because DiagramType "
1027                    << type << " cannot be mapped to ModelType";
1028         return nullptr;
1029     }
1030     return m_root[mt]->findView(type, name, searchAllScopes);
1031 }
1032 
1033 /**
1034  * Used to find a reference to a @ref UMLObject by its ID.
1035  *
1036  * @param id   The @ref UMLObject to find.
1037  * @return  Pointer to the UMLObject found, or NULL if not found.
1038  */
1039 UMLObject* UMLDoc::findObjectById(Uml::ID::Type id)
1040 {
1041     UMLObject *o = 0;
1042     for (int i = 0; i < Uml::ModelType::N_MODELTYPES; ++i) {
1043         if (id == m_root[i]->id()) {
1044             return m_root[i];
1045         }
1046         o = m_root[i]->findObjectById(id);
1047         if (o) {
1048             return o;
1049         }
1050     }
1051     o = findStereotypeById(id);
1052     return o;
1053 }
1054 
1055 /**
1056  * Used to find a @ref UMLObject by its type and name.
1057  *
1058  * @param name         The name of the @ref UMLObject to find.
1059  * @param type         ObjectType of the object to find (optional.)
1060  *                     When the given type is ot_UMLObject the type is
1061  *                     disregarded, i.e. the given name is the only
1062  *                     search criterion.
1063  * @param currentObj   Object relative to which to search (optional.)
1064  *                     If given then the enclosing scope(s) of this
1065  *                     object are searched before the global scope.
1066  * @return  Pointer to the UMLObject found, or NULL if not found.
1067  */
1068 UMLObject* UMLDoc::findUMLObject(const QString &name,
1069                                  UMLObject::ObjectType type /* = ot_UMLObject */,
1070                                  UMLObject *currentObj /* = 0 */)
1071 {
1072     UMLObject *o = m_datatypeRoot->findObject(name);
1073     if (o) {
1074         return o;
1075     }
1076     for (int i = 0; i < Uml::ModelType::N_MODELTYPES; ++i) {
1077         UMLObjectList list = m_root[i]->containedObjects();
1078         if (list.size() == 0)
1079             continue;
1080         o = Model_Utils::findUMLObject(list, name, type, currentObj);
1081         if (o) {
1082             return o;
1083         }
1084         if ((type == UMLObject::ot_UMLObject || type == UMLObject::ot_Folder) &&
1085              name == m_root[i]->name()) {
1086             return m_root[i];
1087         }
1088     }
1089     return 0;
1090 }
1091 
1092 /**
1093  * Used to find a @ref UMLObject by its type and raw name.
1094  *
1095  * @param modelType    The model type in which to search for the object
1096  * @param name         The raw name of the @ref UMLObject to find.
1097  * @param type         ObjectType of the object to find
1098  * @return  Pointer to the UMLObject found, or NULL if not found.
1099  */
1100 UMLObject* UMLDoc::findUMLObjectRaw(Uml::ModelType::Enum modelType,
1101                                     const QString &name,
1102                                     UMLObject::ObjectType type)
1103 {
1104     return findUMLObjectRaw(rootFolder(modelType), name, type);
1105 }
1106 
1107 /**
1108  * Used to find a @ref UMLObject by its type and raw name.
1109  *
1110  * @param folder       The UMLFolder in which to search for the object
1111  * @param name         The raw name of the @ref UMLObject to find.
1112  * @param type         ObjectType of the object to find
1113  * @return  Pointer to the UMLObject found, or NULL if not found.
1114  */
1115 UMLObject* UMLDoc::findUMLObjectRaw(UMLFolder *folder,
1116                                     const QString &name,
1117                                     UMLObject::ObjectType type)
1118 {
1119     if (folder == 0)
1120         return 0;
1121     UMLObjectList list = folder->containedObjects();
1122     if (list.size() == 0)
1123         return 0;
1124     return Model_Utils::findUMLObjectRaw(list, name, type, 0);
1125 }
1126 
1127 /**
1128  * Used to find a @ref UMLObject by its type and raw name recursively
1129  *
1130  * @param modelType    The model type in which to search for the object
1131  * @param name         The raw name of the @ref UMLObject to find.
1132  * @param type         ObjectType of the object to find
1133  * @return  Pointer to the UMLObject found, or NULL if not found.
1134  */
1135 UMLObject* UMLDoc::findUMLObjectRecursive(Uml::ModelType::Enum modelType,
1136                                           const QString &name,
1137                                           UMLObject::ObjectType type)
1138 {
1139     return findUMLObjectRecursive(rootFolder(modelType), name, type);
1140 }
1141 
1142 /**
1143  * Used to find a @ref UMLObject by its type and raw name recursively
1144  *
1145  * @param folder       The UMLFolder in which to search for the object
1146  * @param name         The raw name of the @ref UMLObject to find.
1147  * @param type         ObjectType of the object to find
1148  * @return  Pointer to the UMLObject found, or NULL if not found.
1149  */
1150 UMLObject* UMLDoc::findUMLObjectRecursive(UMLFolder *folder,
1151                                           const QString &name,
1152                                           UMLObject::ObjectType type)
1153 {
1154     if (folder == 0)
1155         return 0;
1156     UMLObjectList list = folder->containedObjects();
1157     if (list.size() == 0)
1158         return 0;
1159     return Model_Utils::findUMLObjectRecursive(list, name, type);
1160 }
1161 
1162 /**
1163  * Used to find a @ref UMLClassifier by its name.
1164  *
1165  * @param name   The name of the @ref UMLObject to find.
1166  */
1167 UMLClassifier* UMLDoc::findUMLClassifier(const QString &name)
1168 {
1169     //this is used only by code generator so we don't need to look at Datatypes
1170     UMLObject * obj = findUMLObject(name);
1171     return obj->asUMLClassifier();
1172 }
1173 
1174 /**
1175  * Adds a UMLObject that is already created but doesn't change
1176  * any ids or signal.  Use AddUMLObjectPaste if pasting.
1177  *
1178  * @param object   The object to add.
1179  * @return  True if the object was actually added.
1180  */
1181 bool UMLDoc::addUMLObject(UMLObject* object)
1182 {
1183     if (object->isUMLStereotype()) {
1184         DEBUG(DBG_SRC) << object->name() << ": not adding type " << object->baseTypeStr();
1185         return false;
1186     }
1187     UMLPackage *pkg = object->umlPackage();
1188     if (pkg == 0) {
1189         pkg = currentRoot();
1190         DEBUG(DBG_SRC) << object->name() << ": no parent package set, assuming "
1191                        << pkg->name();
1192         object->setUMLPackage(pkg);
1193     }
1194 
1195     // FIXME restore stereotype
1196     UMLClassifierListItem *c = object->asUMLClassifierListItem();
1197     if (c) {
1198         if (!pkg->subordinates().contains(c))
1199             pkg->subordinates().append(c);
1200         return true;
1201     }
1202     return pkg->addObject(object);
1203 }
1204 
1205 /**
1206  * Write text to the status bar.
1207  * @param text   the text to write
1208  */
1209 void UMLDoc::writeToStatusBar(const QString &text)
1210 {
1211     emit sigWriteToStatusBar(text);
1212 }
1213 
1214 /**
1215  * Simple removal of an object.
1216  * @param object   the UMLObject to be removed
1217  */
1218 void UMLDoc::slotRemoveUMLObject(UMLObject* object)
1219 {
1220     //m_objectList.remove(object);
1221     UMLPackage *pkg = object->umlPackage();
1222     if (pkg == 0) {
1223         uError() << object->name() << ": parent package is not set !";
1224         return;
1225     }
1226     pkg->removeObject(object);
1227 }
1228 
1229 /**
1230  * Returns true if the given name is unique within its scope.
1231  *
1232  * @param name   The name to check.
1233  * @return  True if name is unique.
1234  */
1235 bool UMLDoc::isUnique(const QString &name) const
1236 {
1237     UMLListView *listView = UMLApp::app()->listView();
1238     UMLListViewItem *currentItem = (UMLListViewItem*)listView->currentItem();
1239     UMLListViewItem *parentItem = 0;
1240 
1241     // check for current item, if its a package, then we do a check on that
1242     // otherwise, if current item exists, find its parent and check if thats
1243     // a package..
1244     if (currentItem) {
1245         // its possible that the current item *is* a package, then just
1246         // do check now
1247         if (Model_Utils::typeIsContainer(currentItem->type())) {
1248             return isUnique (name, (UMLPackage*) currentItem->umlObject());
1249         }
1250         parentItem = (UMLListViewItem*)currentItem->parent();
1251     }
1252 
1253     // item is in a package so do check only in that
1254     if (parentItem != 0 && Model_Utils::typeIsContainer(parentItem->type())) {
1255         UMLPackage *parentPkg = parentItem->umlObject()->asUMLPackage();
1256         return isUnique(name, parentPkg);
1257     }
1258 
1259     uError() << name << ": Not currently in a package";
1260     /* Check against all objects that _don't_ have a parent package.
1261     for (UMLObjectListIt oit(m_objectList); oit.current(); ++oit) {
1262         UMLObject *obj = oit.current();
1263         if ((obj->getUMLPackage() == 0) && (obj->getName() == name))
1264             return false;
1265     }
1266      */
1267     return true;
1268 }
1269 
1270 /**
1271  * Returns true if the given name is unique within its scope of given package.
1272  *
1273  * @param name      The name to check.
1274  * @param package   The UMLPackage in which we have to determine the unique-ness
1275  * @return      True if name is unique.
1276  */
1277 bool UMLDoc::isUnique(const QString &name, UMLPackage *package) const
1278 {
1279     // if a package, then only do check in that
1280     if (package) {
1281         return (package->findObject(name) == 0);
1282     }
1283 
1284     // Not currently in a package: ERROR
1285     uError() << name << " (2): Not currently in a package";
1286     /* Check against all objects that _don't_ have a parent package.
1287     for (UMLObjectListIt oit(m_objectList); oit.current(); ++oit) {
1288         UMLObject *obj = oit.current();
1289         if ((obj->getUMLPackage() == 0) && (obj->getName() == name))
1290             return false;
1291     }
1292      */
1293     return true;
1294 }
1295 
1296 /**
1297  * Creates a stereotype for the parent object.
1298  * @param name   the name of the stereotype
1299  */
1300 UMLStereotype* UMLDoc::createStereotype(const QString &name)
1301 {
1302     UMLStereotype *s = new UMLStereotype(name, Uml::ID::fromString(name));
1303     addStereotype(s);
1304     return s;
1305 }
1306 
1307 /**
1308  * Finds a UMLStereotype by its name.
1309  *
1310  * @param name   The name of the UMLStereotype to find.
1311  * @return  Pointer to the UMLStereotype found, or NULL if not found.
1312  */
1313 UMLStereotype* UMLDoc::findStereotype(const QString &name) const
1314 {
1315     foreach (UMLStereotype *s, m_stereoList) {
1316         if (s->name() == name) {
1317             return s;
1318         }
1319     }
1320     return 0;
1321 }
1322 
1323 /**
1324  * Finds or creates a stereotype for the parent object.
1325  * @param name   the name of the stereotype
1326  * @return the found stereotype object or a just created one
1327  */
1328 UMLStereotype* UMLDoc::findOrCreateStereotype(const QString &name)
1329 {
1330     UMLStereotype *s = findStereotype(name);
1331     if (s != 0) {
1332         return s;
1333     }
1334     return createStereotype(name);
1335 }
1336 
1337 /**
1338  * Find a UMLStereotype by its unique ID.
1339  * @param id   the unique ID
1340  * @return the found stereotype or NULL
1341  */
1342 UMLStereotype * UMLDoc::findStereotypeById(Uml::ID::Type id) const
1343 {
1344     foreach (UMLStereotype *s, m_stereoList) {
1345         if (s->id() == id)
1346             return s;
1347     }
1348     return 0;
1349 }
1350 
1351 /**
1352  * Add a UMLStereotype to the application.
1353  * @param s  the stereotype to be added
1354  */
1355 void UMLDoc::addStereotype(UMLStereotype *s)
1356 {
1357     if (m_stereotypesModel->addStereotype(s))
1358         emit sigObjectCreated(s);
1359 }
1360 
1361 /**
1362  * Remove a UMLStereotype from the application.
1363  * @param s   the stereotype to be removed
1364  */
1365 void UMLDoc::removeStereotype(UMLStereotype *s)
1366 {
1367     if (m_stereotypesModel->removeStereotype(s))
1368         emit sigObjectRemoved(s);
1369 }
1370 
1371 /**
1372  * Add a stereotype if it doesn't already exist.
1373  * Used by code generators, operations and attribute dialog.
1374  */
1375 void UMLDoc::addDefaultStereotypes()
1376 {
1377     CodeGenerator *gen = UMLApp::app()->generator();
1378     if (gen) {
1379         gen->createDefaultStereotypes();
1380     }
1381 }
1382 
1383 /**
1384  * Returns a list of the stereotypes in this UMLDoc.
1385  *
1386  * @return  List of UML stereotypes.
1387  */
1388 const UMLStereotypeList& UMLDoc::stereotypes() const
1389 {
1390     return m_stereoList;
1391 }
1392 
1393 /**
1394  * Removes an association.
1395  *
1396  * @param assoc    Pointer to the UMLAssociation to remove.
1397  * @param doSetModified  Whether to mark the document as modified (default: true.)
1398  */
1399 void UMLDoc::removeAssociation (UMLAssociation * assoc, bool doSetModified /*=true*/)
1400 {
1401     if (!assoc) {
1402         return;
1403     }
1404 
1405     // Remove the UMLAssociation from m_objectList.
1406     UMLPackage *pkg = assoc->umlPackage();
1407     if (pkg == 0) {
1408         uError() << assoc->name() << ": parent package is not set !";
1409         return;
1410     }
1411     pkg->removeObject(assoc);
1412 
1413     if (doSetModified) {  // so we will save our document
1414         setModified(true);
1415     }
1416 }
1417 
1418 /**
1419  * Finds an association.
1420  *
1421  * @param assocType Type of the UMLAssociation to seek.
1422  * @param roleAObj  Pointer to the role A UMLCanvasObject.
1423  * @param roleBObj  Pointer to the role B UMLCanvasObject.
1424  * @param swap      Optional pointer to boolean.
1425  *                  The bool is set to true if the association
1426  *                  matched with swapped roles, else it is set
1427  *                  to false.
1428  * @return  Pointer to the UMLAssociation found or NULL if not found.
1429  */
1430 UMLAssociation * UMLDoc::findAssociation(Uml::AssociationType::Enum assocType,
1431         const UMLObject *roleAObj,
1432         const UMLObject *roleBObj,
1433         bool *swap) const
1434 {
1435     UMLAssociationList assocs = associations();
1436     UMLAssociation *ret = 0;
1437     foreach (UMLAssociation* a, assocs) {
1438         if (a->getAssocType() != assocType) {
1439             continue;
1440         }
1441         if (a->getObject(Uml::RoleType::A) == roleAObj && a->getObject(Uml::RoleType::B) == roleBObj) {
1442             return a;
1443         }
1444         if (a->getObject(Uml::RoleType::A) == roleBObj && a->getObject(Uml::RoleType::B) == roleAObj) {
1445             ret = a;
1446         }
1447     }
1448     if (swap) {
1449         *swap = (ret != 0);
1450     }
1451     return ret;
1452 }
1453 
1454 /**
1455  * Creates AND adds an association between two UMLObjects.
1456  * Used by refactoring assistant.
1457  * NOTE: this method does not check if the association is valid / legal
1458  *
1459  * @param a      The UMLObject "A" for the association (source)
1460  * @param b      The UMLObject "B" for the association (destination)
1461  * @param type   The association's type
1462  * @return  The Association created
1463  */
1464 UMLAssociation* UMLDoc::createUMLAssociation(UMLObject *a, UMLObject *b,
1465                                              Uml::AssociationType::Enum type)
1466 {
1467     bool swap;
1468     UMLAssociation *assoc = findAssociation(type, a, b, &swap);
1469     if (assoc == 0) {
1470         assoc = new UMLAssociation(type, a, b);
1471         assoc->setUMLPackage(a->umlPackage());
1472         addAssociation(assoc);
1473     }
1474     return assoc;
1475 }
1476 
1477 /**
1478  * Adds an association.
1479  *
1480  * @param assoc    Pointer to the UMLAssociation to add.
1481  */
1482 void UMLDoc::addAssociation(UMLAssociation *assoc)
1483 {
1484     if (assoc == 0) {
1485         return;
1486     }
1487 
1488     // First, check that this association has not already been added.
1489     // This may happen when loading old XMI files where all the association
1490     // information was taken from the <UML:AssocWidget> tag.
1491     UMLAssociationList assocs = associations();
1492     foreach (UMLAssociation* a,  assocs) {
1493         // check if its already been added (shouldn't be the case right now
1494         // as UMLAssociations only belong to one associationwidget at a time)
1495         if (a == assoc) {
1496             DEBUG(DBG_SRC) << "duplicate addition attempted";
1497             return;
1498         }
1499     }
1500 
1501     // If we get here it's really a new association.
1502 
1503     // Add the UMLAssociation at the owning UMLPackage.
1504     UMLPackage *pkg = assoc->umlPackage();
1505     if (pkg == 0) {
1506         uError() << assoc->name() << ": parent package is not set !";
1507         return;
1508     }
1509     pkg->addObject(assoc);
1510 
1511     // I don't believe this appropriate, UMLAssociations ARENT UMLWidgets -b.t.
1512     // emit sigObjectCreated(o);
1513 
1514     setModified(true);
1515 }
1516 
1517 /**
1518  * Returns a name for the new object, appended with a number
1519  * if the default name is taken e.g. class diagram, class
1520  * diagram_1 etc.
1521  * @param type   the diagram type
1522  * @return the unique view name
1523  */
1524 QString UMLDoc::uniqueViewName(const Uml::DiagramType::Enum type) const
1525 {
1526     QString dname;
1527     switch (type) {
1528         case Uml::DiagramType::UseCase:
1529             dname = i18n("use case diagram");
1530             break;
1531         case Uml::DiagramType::Class:
1532             dname = i18n("class diagram");
1533             break;
1534         case Uml::DiagramType::Sequence:
1535             dname = i18n("sequence diagram");
1536             break;
1537         case Uml::DiagramType::Collaboration:
1538             dname = i18n("communication diagram");
1539             break;
1540         case Uml::DiagramType::Object:
1541             dname = i18n("object diagram");
1542             break;
1543         case Uml::DiagramType::State:
1544             dname = i18n("state diagram");
1545             break;
1546         case Uml::DiagramType::Activity:
1547             dname = i18n("activity diagram");
1548             break;
1549         case Uml::DiagramType::Component:
1550             dname = i18n("component diagram");
1551             break;
1552         case Uml::DiagramType::Deployment:
1553             dname = i18n("deployment diagram");
1554             break;
1555         case Uml::DiagramType::EntityRelationship:
1556             dname = i18n("entity relationship diagram");
1557             break;
1558         default:
1559             uWarning() << "called with unknown diagram type";
1560             break;
1561     }
1562     QString name = dname;
1563     for (int number = 1; findView(type, name, true); ++number) {
1564         name = dname + QLatin1Char('_') + QString::number(number);
1565     }
1566     return name;
1567 }
1568 
1569 /**
1570  * Returns true when loading a document file.
1571  * @return the value of the flag
1572  */
1573 bool UMLDoc::loading() const
1574 {
1575     return m_bLoading || !m_bTypesAreResolved;
1576 }
1577 
1578 /**
1579  * Sets loading boolean flag to the value given.
1580  * @param state   value to set
1581  */
1582 void UMLDoc::setLoading(bool state /* = true */)
1583 {
1584     m_bLoading = state;
1585 }
1586 
1587 /**
1588  * Returns true when importing file(s).
1589  * @return the value of the flag
1590  */
1591 bool UMLDoc::importing() const
1592 {
1593     return m_importing;
1594 }
1595 /**
1596  * Sets importing boolean flag to the value given.
1597  * @param state   value to set
1598  */
1599 void UMLDoc::setImporting(bool state /* = true */)
1600 {
1601     m_importing = state;
1602 }
1603 
1604 /**
1605  * Returns the m_bClosing flag.
1606  * @return the value of the flag
1607  */
1608 bool UMLDoc::closing() const
1609 {
1610     return m_bClosing;
1611 }
1612 
1613 /**
1614  * Creates the name of the given diagram type.
1615  * @param type         The type of diagram to create.
1616  * @param askForName   If true shows a dialog box asking for name,
1617  *                     else uses a default name.
1618  * @return             name of the new diagram
1619  */
1620 QString UMLDoc::createDiagramName(Uml::DiagramType::Enum type, bool askForName /*= true */)
1621 {
1622     QString defaultName = uniqueViewName(type);
1623     QString name = defaultName;
1624 
1625     while (true) {
1626         if (askForName && !Dialog_Utils::askName(i18nc("diagram name", "Name"), i18n("Enter name:"), name))
1627             break;
1628 
1629         if (name.length() == 0)  {
1630             KMessageBox::error(0, i18n("That is an invalid name for a diagram."), i18n("Invalid Name"));
1631         } else if (findView(type, name)) {
1632                 KMessageBox::error(0, i18n("A diagram is already using that name."), i18n("Not a Unique Name"));
1633         } else {
1634             return name;
1635         }
1636     } // end while
1637     return QString();
1638 }
1639 
1640 /**
1641  * Creates a diagram of the given type.
1642  *
1643  * @param folder   the folder in which tp create the diagram.
1644  * @param type     the type of diagram to create
1645  * @param name     the name for the diagram to create
1646  * @param id       optional ID of new diagram
1647  * @return         pointer to the UMLView of the new diagram
1648  */
1649 UMLView* UMLDoc::createDiagram(UMLFolder *folder, Uml::DiagramType::Enum type, const QString& name, Uml::ID::Type id)
1650 {
1651     DEBUG(DBG_SRC) << "folder=" << folder->name()
1652                    << " / type=" << Uml::DiagramType::toString(type)
1653                    << " / name=" << name;
1654 
1655     if (id == Uml::ID::None) {
1656         id = UniqueID::gen();
1657     }
1658 
1659     if (name.length() > 0) {
1660         UMLView* view = new UMLView(folder);
1661         view->umlScene()->setOptionState(Settings::optionState());
1662         view->umlScene()->setName(name);
1663         view->umlScene()->setType(type);
1664         view->umlScene()->setID(id);
1665         addView(view);
1666         emit sigDiagramCreated(id);
1667         setModified(true);
1668         UMLApp::app()->enablePrint(true);
1669         changeCurrentView(id);
1670         return view;
1671     }
1672     return 0;
1673 }
1674 
1675 /**
1676  * Used to rename a document. This method takes care of everything.
1677  * You just need to give the ID of the diagram to the method.
1678  *
1679  * @param id   The ID of the diagram to rename.
1680  */
1681 void UMLDoc::renameDiagram(Uml::ID::Type id)
1682 {
1683     UMLView *view = findView(id);
1684     Uml::DiagramType::Enum type = view->umlScene()->type();
1685 
1686     QString name = view->umlScene()->name();
1687     while (true) {
1688         bool ok = Dialog_Utils::askName(i18nc("renaming diagram", "Name"),
1689                                         i18n("Enter name:"),
1690                                         name);
1691         if (!ok) {
1692             break;
1693         }
1694         if (name.length() == 0) {
1695             KMessageBox::error(0, i18n("That is an invalid name for a diagram."), i18n("Invalid Name"));
1696         } else if (!findView(type, name)) {
1697             view->umlScene()->setName(name);
1698             emit sigDiagramRenamed(id);
1699             setModified(true);
1700             break;
1701         } else {
1702             KMessageBox::error(0, i18n("A diagram is already using that name."), i18n("Not a Unique Name"));
1703         }
1704     }
1705 }
1706 
1707 /**
1708  * Used to rename a @ref UMLObject.  The @ref UMLObject is to be an
1709  * actor, use case or concept.
1710  *
1711  * @param o The object to rename.
1712  */
1713 void UMLDoc::renameUMLObject(UMLObject *o)
1714 {
1715     QString name = o->name();
1716     while (true) {
1717         bool ok = Dialog_Utils::askName(i18nc("renaming uml object", "Name"),
1718                                         i18n("Enter name:"),
1719                                         name);
1720         if (!ok) {
1721             break;
1722         }
1723         if (name.length() == 0) {
1724             KMessageBox::error(0, i18n("That is an invalid name."), i18n("Invalid Name"));
1725         } else if (isUnique(name)) {
1726             UMLApp::app()->executeCommand(new Uml::CmdRenameUMLObject(o, name));
1727             setModified(true);
1728             break;
1729         } else {
1730             KMessageBox::error(0, i18n("That name is already being used."), i18n("Not a Unique Name"));
1731         }
1732     }
1733     return;
1734 }
1735 
1736 /**
1737  * Used to rename an operation or attribute of a concept.
1738  *
1739  * @param o The attribute or operation to rename.
1740  */
1741 void UMLDoc::renameChildUMLObject(UMLObject *o)
1742 {
1743     UMLClassifier* p = o->umlParent()->asUMLClassifier();
1744     if (!p) {
1745         DEBUG(DBG_SRC) << "Cannot create object, no parent found.";
1746         return;
1747     }
1748 
1749     QString name = o->name();
1750     while (true) {
1751         bool ok = Dialog_Utils::askName(i18nc("renaming child uml object", "Name"),
1752                                         i18n("Enter name:"),
1753                                         name);
1754         if (!ok) {
1755             break;
1756         }
1757         if (name.length() == 0) {
1758             KMessageBox::error(0, i18n("That is an invalid name."), i18n("Invalid Name"));
1759         } else if (p->findChildObject(name) == 0
1760                     || ((o->baseType() == UMLObject::ot_Operation) && KMessageBox::warningYesNo(0,
1761                             i18n("The name you entered was not unique.\nIs this what you wanted?"),
1762                             i18n("Name Not Unique"), KGuiItem(i18n("Use Name")), KGuiItem(i18n("Enter New Name"))) == KMessageBox::Yes)) {
1763                 UMLApp::app()->executeCommand(new Uml::CmdRenameUMLObject(o, name));
1764                 setModified(true);
1765                 break;
1766         } else {
1767             KMessageBox::error(0, i18n("That name is already being used."), i18n("Not a Unique Name"));
1768         }
1769     }
1770 }
1771 
1772 /**
1773  * Changes the current view (diagram) to the view with the given ID.
1774  *
1775  * @param id   The ID of the view to change to.
1776  */
1777 void UMLDoc::changeCurrentView(Uml::ID::Type id)
1778 {
1779     DEBUG(DBG_SRC) << "id=" << Uml::ID::toString(id);
1780     UMLView* view = findView(id);
1781     if (view) {
1782         UMLScene* scene = view->umlScene();
1783         scene->setIsOpen(true);
1784         UMLApp::app()->setCurrentView(view);
1785         emit sigDiagramChanged(scene->type());
1786         UMLApp::app()->setDiagramMenuItemsState(true);
1787         setModified(true);
1788         emit sigCurrentViewChanged();
1789         // when clicking on a tab, the documentation of diagram is upated in docwindow
1790         UMLApp::app()->docWindow()->showDocumentation(scene);
1791     }
1792     else {
1793         uWarning() << "New current view was not found with id=" << Uml::ID::toString(id) << "!";
1794     }
1795 }
1796 
1797 /**
1798  * Deletes a diagram from the current file.
1799  *
1800  * Undo command
1801  *
1802  * @param id   The ID of the diagram to delete.
1803  */
1804 void UMLDoc::removeDiagram(Uml::ID::Type id)
1805 {
1806     UMLView* umlView = findView(id);
1807     if (!umlView) {
1808         uError() << "Request to remove diagram " << Uml::ID::toString(id) << ": Diagram not found!";
1809         return;
1810     }
1811 
1812     UMLScene* umlScene = umlView->umlScene();
1813     if (Dialog_Utils::askDeleteDiagram(umlScene->name())) {
1814         UMLApp::app()->executeCommand(new Uml::CmdRemoveDiagram(
1815             umlScene->folder(),
1816             umlScene->type(),
1817             umlScene->name(),
1818             id
1819         ));
1820     }
1821 }
1822 
1823 /**
1824  * Deletes a diagram from the current file.
1825  *
1826  * @param id   The ID of the diagram to delete.
1827  */
1828 void UMLDoc::removeDiagramCmd(Uml::ID::Type id)
1829 {
1830     UMLApp::app()->docWindow()->updateDocumentation(true);
1831     UMLView* umlview = findView(id);
1832     if (!umlview) {
1833         uError() << "Request to remove diagram " << Uml::ID::toString(id) << ": Diagram not found!";
1834         return;
1835     }
1836 
1837     removeView(umlview);
1838     emit sigDiagramRemoved(id);
1839     setModified(true);
1840 }
1841 
1842 /**
1843  * Return the currently selected root folder.
1844  * This will be an element from the m_root[] array.
1845  * @return the currently selected root folder or NULL
1846  */
1847 UMLFolder *UMLDoc::currentRoot() const
1848 {
1849     UMLView *currentView = UMLApp::app()->currentView();
1850     if (currentView == 0) {
1851         if (m_pCurrentRoot) {
1852             return m_pCurrentRoot;
1853         }
1854         uError() << "m_pCurrentRoot is NULL";
1855         return 0;
1856     }
1857     UMLFolder *f = currentView->umlScene()->folder();
1858     while (f && f->umlPackage()) {
1859         f = f->umlParent()->asUMLFolder();
1860     }
1861     return f;
1862 }
1863 
1864 /**
1865  * Set the current root folder.
1866  *
1867  * @param rootType    The type of the root folder to set.
1868  *                    The element from m_root[] which is indexed
1869  *                    by this type is selected.
1870  */
1871 void UMLDoc::setCurrentRoot(Uml::ModelType::Enum rootType)
1872 {
1873     m_pCurrentRoot = m_root[rootType];
1874 }
1875 
1876 /**
1877  * Removes an @ref UMLObject from the current file.  If this object
1878  * is being represented on a diagram it will also delete all those
1879  * representations.
1880  *
1881  * @param umlobject   Pointer to the UMLObject to delete.
1882  * @param deleteObject Delete the UMLObject instance.
1883  */
1884 void UMLDoc::removeUMLObject(UMLObject* umlobject, bool deleteObject)
1885 {
1886     if (umlobject == 0) {
1887         uError() << "called with NULL parameter";
1888         return;
1889     }
1890     UMLApp::app()->docWindow()->updateDocumentation(true);
1891     UMLObject::ObjectType type = umlobject->baseType();
1892 
1893     umlobject->setUMLStereotype(0);  // triggers possible cleanup of UMLStereotype
1894     if (umlobject->asUMLClassifierListItem())  {
1895         UMLClassifier* parent = umlobject->umlParent()->asUMLClassifier();
1896         if (parent == 0) {
1897             uError() << "parent of umlobject is NULL";
1898             return;
1899         }
1900         if (type == UMLObject::ot_Operation) {
1901             parent->removeOperation(umlobject->asUMLOperation());
1902             if (deleteObject)
1903                 delete umlobject->asUMLOperation();
1904         } else if (type == UMLObject::ot_EnumLiteral) {
1905             UMLEnum *e = parent->asUMLEnum();
1906             e->removeEnumLiteral(umlobject->asUMLEnumLiteral());
1907         } else if (type == UMLObject::ot_EntityAttribute) {
1908             UMLEntity *ent = parent->asUMLEntity();
1909             ent->removeEntityAttribute(umlobject->asUMLClassifierListItem());
1910         } else if (type == UMLObject::ot_UniqueConstraint || type == UMLObject::ot_ForeignKeyConstraint ||
1911                     type == UMLObject::ot_CheckConstraint) {
1912             UMLEntity* ent = parent->asUMLEntity();
1913             ent->removeConstraint(umlobject->asUMLEntityConstraint());
1914         } else {
1915             UMLClassifier* pClass = parent->asUMLClassifier();
1916             if (pClass == 0)  {
1917                 uError() << "parent of umlobject has unexpected type "
1918                          << parent->baseType();
1919                 return;
1920             }
1921             if (type == UMLObject::ot_Attribute) {
1922                 pClass->removeAttribute(umlobject->asUMLAttribute());
1923             } else if (type == UMLObject::ot_Template) {
1924                 pClass->removeTemplate(umlobject->asUMLTemplate());
1925                 if (deleteObject)
1926                     delete umlobject->asUMLTemplate();
1927             } else {
1928                 uError() << "umlobject has unexpected type " << type;
1929             }
1930         }
1931     } else if (type == UMLObject::ot_Association) {
1932         UMLAssociation *a = umlobject->asUMLAssociation();
1933         removeAssociation(a, false);  // don't call setModified here, it's done below
1934         emit sigObjectRemoved(umlobject);
1935         if (deleteObject)
1936             delete a;
1937     } else {
1938         UMLPackage* pkg = umlobject->umlPackage();
1939         if (pkg) {
1940             // Remove associations that this object may participate in.
1941             UMLCanvasObject *c = umlobject->asUMLCanvasObject();
1942             if (c) {
1943                 // In the current implementation, all associations live in the
1944                 // root folder.
1945                 UMLPackage* rootPkg = Model_Utils::rootPackage(c);
1946                 if (rootPkg == 0) {
1947                     uError() << umlobject->name() << ": root package is not set !";
1948                     return;
1949                 }
1950                 UMLObjectList rootObjects = rootPkg->containedObjects();
1951                 // Store the associations to remove in a buffer because we
1952                 // should not remove elements from m_objectList while it is
1953                 // being iterated over.
1954                 UMLAssociationList assocsToRemove;
1955                 foreach (UMLObject *obj, rootObjects) {
1956                     uIgnoreZeroPointer(obj);
1957                     if (obj->baseType() == UMLObject::ot_Association) {
1958                         UMLAssociation *assoc = obj->asUMLAssociation();
1959                         if (c->hasAssociation(assoc)) {
1960                             assocsToRemove.append(assoc);
1961                         }
1962                     }
1963                 }
1964                 foreach (UMLAssociation *a, assocsToRemove) {
1965                     removeAssociation(a, false);
1966                 }
1967             }
1968             pkg->removeObject(umlobject);
1969             emit sigObjectRemoved(umlobject);
1970             if (deleteObject)
1971                 delete umlobject;
1972         } else {
1973             uError() << umlobject->name() << ": parent package is not set !";
1974         }
1975     }
1976     setModified(true);
1977 }
1978 
1979 /**
1980  * Signal that a UMLObject has been created.
1981  *
1982  * @param o The object that has been created.
1983  */
1984 void UMLDoc::signalUMLObjectCreated(UMLObject * o)
1985 {
1986     emit sigObjectCreated(o);
1987     /* This is the wrong place to do:
1988                setModified(true);
1989        Instead, that should be done by the callers when object creation and all
1990        its side effects (e.g. new widget in view, new list view item, etc.) is
1991        finalized.
1992      */
1993 }
1994 
1995 /**
1996  * Set the name of this model.
1997  */
1998 void UMLDoc::setName(const QString& name)
1999 {
2000     m_Name = name;
2001 }
2002 
2003 /**
2004  * Return the name of this model.
2005  */
2006 QString UMLDoc::name() const
2007 {
2008     return m_Name;
2009 }
2010 
2011 /**
2012  * Set coordinates resolution for current document.
2013 
2014  * @param resolution document resolution in DPI
2015  */
2016 void UMLDoc::setResolution(qreal resolution)
2017 {
2018     m_resolution = resolution;
2019     if (!qFuzzyIsNull(resolution)) {
2020         uDebug() << "screen dpi:" << qApp->desktop()->logicalDpiX()
2021                  << "file dpi:" <<  resolution
2022                  << "scale:" << qApp->desktop()->logicalDpiX() / resolution;
2023     }
2024 }
2025 
2026 /**
2027  * Returns coordinates resolution for current document.
2028 
2029  * @return document resolution in DPI
2030  */
2031 qreal UMLDoc::resolution() const
2032 {
2033     return m_resolution;
2034 }
2035 
2036 /**
2037  * Returns scale factor for recalculation of document coordinates.
2038 
2039  * @return scale factor
2040  */
2041 qreal UMLDoc::dpiScale() const
2042 {
2043 #ifdef ENABLE_XMIRESOLUTION
2044     if (resolution() != 0.0)
2045         return (qreal)qApp->desktop()->logicalDpiX() / resolution();
2046     else
2047 #endif
2048         return 1.0;
2049 }
2050 
2051 /**
2052  * Return the m_modelID (currently this a fixed value:
2053  * Umbrello supports only a single document.)
2054  */
2055 Uml::ID::Type UMLDoc::modelID() const
2056 {
2057     return m_modelID;
2058 }
2059 
2060 /**
2061  * This method is called for saving the given model as a XMI file.
2062  * It is virtual and calls the corresponding saveToXMI1() functions
2063  * of the derived classes.
2064  *
2065  * @param file   The file to be saved to.
2066  */
2067 void UMLDoc::saveToXMI1(QIODevice& file)
2068 {
2069     QXmlStreamWriter writer(&file);
2070     writer.setCodec("UTF-8");
2071     writer.setAutoFormatting(true);
2072     writer.setAutoFormattingIndent(1);
2073     // writer.writeProcessingInstruction(QLatin1String("xml"));
2074     writer.writeStartDocument();
2075     writer.writeStartElement(QLatin1String("XMI"));
2076     // writer.writeStartElement(QLatin1String("http://schema.omg.org/spec/UML/1.4"), QLatin1String("XMI"));
2077     // writer.writeStartElement(QLatin1String("UML"), QLatin1String("XMI"));
2078     // <XMI xmlns:UML="http://schema.omg.org/spec/UML/1.4" verified="false" xmi.version="1.2" timestamp="2020-10-10T14:53:06">
2079     writer.writeAttribute(QLatin1String("xmi.version"), QLatin1String("1.2"));
2080     QDateTime now = QDateTime::currentDateTime();
2081     writer.writeAttribute(QLatin1String("timestamp"), now.toString(Qt::ISODate));
2082     writer.writeAttribute(QLatin1String("verified"), QLatin1String("false"));
2083 
2084     writer.writeNamespace(QLatin1String("http://schema.omg.org/spec/UML/1.4"), QLatin1String("UML"));
2085     writer.writeStartElement(QLatin1String("XMI.header"));
2086 
2087     writer.writeStartElement(QLatin1String("XMI.documentation"));
2088     QString expoText(QLatin1String("umbrello uml modeller "));
2089     expoText += QLatin1String(umbrelloVersion());
2090     expoText += QLatin1String(" http://umbrello.kde.org");
2091     writer.writeTextElement(QLatin1String("XMI.exporter"), expoText);
2092 
2093     writer.writeTextElement(QLatin1String("XMI.exporterVersion"), QLatin1String(XMI_FILE_VERSION));
2094 
2095     // all files are now saved with correct Unicode encoding, we add this
2096     // information to the header, so that the file will be loaded correctly
2097     writer.writeTextElement(QLatin1String("XMI.exporterEncoding"), QLatin1String("UnicodeUTF8"));
2098     writer.writeEndElement();  // XMI.documentation
2099 
2100     writer.writeStartElement(QLatin1String("XMI.metamodel"));
2101     writer.writeAttribute(QLatin1String("xmi.name"), QLatin1String("UML"));
2102     writer.writeAttribute(QLatin1String("xmi.version"), QLatin1String("1.4"));
2103     writer.writeAttribute(QLatin1String("href"), QLatin1String("UML.xml"));
2104     writer.writeEndElement();  // XMI.metamodel
2105 
2106     writer.writeEndElement();  // XMI.header
2107 
2108     writer.writeStartElement(QLatin1String("XMI.content"));             // content
2109 
2110     writer.writeStartElement(QLatin1String("UML:Model"));               // objectsElement
2111     writer.writeAttribute(QLatin1String("xmi.id"), Uml::ID::toString(m_modelID));
2112     writer.writeAttribute(QLatin1String("name"), m_Name);
2113     writer.writeAttribute(QLatin1String("isSpecification"), QLatin1String("false"));
2114     writer.writeAttribute(QLatin1String("isAbstract"), QLatin1String("false"));
2115     writer.writeAttribute(QLatin1String("isRoot"), QLatin1String("false"));
2116     writer.writeAttribute(QLatin1String("isLeaf"), QLatin1String("false"));
2117 
2118     writer.writeStartElement(QLatin1String("UML:Namespace.ownedElement"));  // ownedNS
2119 
2120     // Save stereotypes and toplevel datatypes first so that upon loading
2121     // they are known first.
2122     // There is a bug causing duplication of the same stereotype in m_stereoList.
2123     // As a workaround, we use a string list to memorize which stereotype has been saved.
2124     QStringList stereoNames;
2125     foreach (UMLStereotype *s, m_stereoList) {
2126         QString stName = s->name();
2127         if (!stereoNames.contains(stName)) {
2128             s->saveToXMI1(writer);
2129             stereoNames.append(stName);
2130         }
2131     }
2132     for (int i = 0; i < Uml::ModelType::N_MODELTYPES; ++i) {
2133         m_root[i]->saveToXMI1(writer);
2134     }
2135 
2136     writer.writeEndElement();      // UML:Namespace.ownedElement
2137 
2138     writer.writeEndElement();    // UML:Model
2139 
2140     writer.writeEndElement();  // XMI.content
2141 
2142     // Save the XMI extensions: docsettings, diagrams, listview, and codegeneration.
2143     writer.writeStartElement(QLatin1String("XMI.extensions"));
2144     writer.writeAttribute(QLatin1String("xmi.extender"), QLatin1String("umbrello"));
2145 
2146     writer.writeStartElement(QLatin1String("docsettings"));
2147     Uml::ID::Type viewID = Uml::ID::None;
2148     UMLView *currentView = UMLApp::app()->currentView();
2149     if (currentView) {
2150         viewID = currentView->umlScene()->ID();
2151     }
2152     writer.writeAttribute(QLatin1String("viewid"), Uml::ID::toString(viewID));
2153     writer.writeAttribute(QLatin1String("documentation"), m_Doc);
2154     writer.writeAttribute(QLatin1String("uniqueid"), Uml::ID::toString(UniqueID::get()));
2155     writer.writeEndElement();  // docsettings
2156 
2157     //  save listview
2158     UMLApp::app()->listView()->saveToXMI1(writer);
2159 
2160     // save code generator
2161     CodeGenerator *codegen = UMLApp::app()->generator();
2162     if (codegen) {
2163         writer.writeStartElement(QLatin1String("codegeneration"));
2164         codegen->saveToXMI1(writer);
2165         writer.writeEndElement();  // codegeneration
2166     }
2167 
2168     writer.writeEndElement();  // XMI.extensions
2169     writer.writeEndElement();  // XMI
2170     writer.writeEndDocument();
2171 }
2172 
2173 /**
2174  * Checks the given XMI file if it was saved with correct Unicode
2175  * encoding set or not.
2176  *
2177  * @param file   The file to be checked.
2178  */
2179 short UMLDoc::encoding(QIODevice & file)
2180 {
2181     QTextStream stream(&file);
2182     stream.setCodec("UTF-8");
2183     QString data = stream.readAll();
2184     QString error;
2185     int line;
2186     QDomDocument doc;
2187     if (!doc.setContent(data, false, &error, &line)) {
2188         uWarning() << "Cannot set content: " << error << " Line: " << line;
2189         return ENC_UNKNOWN;
2190     }
2191 
2192     // we start at the beginning and go to the point in the header where we can
2193     // find out if the file was saved using Unicode
2194     QDomNode node = doc.firstChild();
2195     short enc = ENC_UNKNOWN;
2196     while (node.isComment() || node.isProcessingInstruction()) {
2197         if (node.isProcessingInstruction()) {
2198             const QDomProcessingInstruction& pi = node.toProcessingInstruction();
2199             QRegExp rx(QLatin1String("\\bencoding=['\"]([^'\"]+)['\"]"));
2200             const int pos = rx.indexIn(pi.data());
2201             if (pos >= 0) {
2202                 const QString& encData = rx.cap(1);
2203                 if (encData == QLatin1String("UTF-8")) {
2204                     enc = ENC_UNICODE;
2205                 } else if (encData == QLatin1String("windows-1252")) {
2206                     enc = ENC_WINDOWS;
2207                 } else {
2208                     uDebug() << "ProcessingInstruction encoding=" << encData << " is not yet implemented";
2209                     enc = ENC_OLD_ENC;
2210                 }
2211             }
2212         }
2213         node = node.nextSibling();
2214     }
2215     QDomElement root = node.toElement();
2216     if (root.isNull()) {
2217         uDebug() << "Null element at " << node.nodeName() << " : " << node.nodeValue();
2218         return enc;
2219     }
2220     //  make sure it is an XMI file
2221     if (root.tagName() != QLatin1String("XMI") && root.tagName() != QLatin1String("xmi:XMI")) {
2222         uDebug() << "Unknown tag at " << root.tagName();
2223         return enc;
2224     }
2225 
2226     if (node.firstChild().isNull()) {
2227         uDebug() << "No child at " << node.nodeName() << " : " << node.nodeValue();
2228         return enc;
2229     }
2230     node = node.firstChild();
2231 
2232     QDomElement element = node.toElement();
2233     // check header
2234     if (element.isNull()) {
2235         uDebug() << "No element at " << node.nodeName() << " : " << node.nodeValue();
2236         return enc;
2237     }
2238     if (element.tagName() != QLatin1String("XMI.header")) {
2239         uDebug() << "Expecting XMI.header at " << element.tagName();
2240         return enc;
2241     }
2242 
2243     QDomNode headerNode = node.firstChild();
2244     while (!headerNode.isNull()) {
2245         QDomElement headerElement = headerNode.toElement();
2246         // the information if Unicode was used is now stored in the
2247         // XMI.documentation section of the header
2248         if (headerElement.isNull() ||
2249                 headerElement.tagName() != QLatin1String("XMI.documentation")) {
2250             headerNode = headerNode.nextSibling();
2251             continue;
2252         }
2253         QDomNode docuNode = headerNode.firstChild();
2254         while (!docuNode.isNull()) {
2255             QDomElement docuElement = docuNode.toElement();
2256             // a tag XMI.exporterEncoding was added since version 1.2 to
2257             // mark a file as saved with Unicode
2258             if (! docuElement.isNull() &&
2259                     docuElement.tagName() == QLatin1String("XMI.exporterEncoding")) {
2260                 // at the moment this isn't really necessary but maybe
2261                 // later we will have other encoding standards
2262                 if (docuElement.text() == QLatin1String("UnicodeUTF8")) {
2263                     return ENC_UNICODE; // stop here
2264                 }
2265             }
2266             docuNode = docuNode.nextSibling();
2267         }
2268         break;
2269     }
2270     return ENC_OLD_ENC;
2271 }
2272 
2273 /**
2274  * Load a given XMI model from a file. If the encoding of the file
2275  * is already known it can be passed to the function. If this info
2276  * isn't given, loadFromXMI1 will check which encoding was used.
2277  *
2278  * @param file     The file to be loaded.
2279  * @param encode   The encoding used.
2280  */
2281 bool UMLDoc::loadFromXMI1(QIODevice & file, short encode)
2282 {
2283     // old Umbrello versions (version < 1.2) didn't save the XMI in Unicode
2284     // this wasn't correct, because non Latin1 chars where lost
2285     // to ensure backward compatibility we have to ensure to load the old files
2286     // with non Unicode encoding
2287     if (encode == ENC_UNKNOWN) {
2288         if ((encode = encoding(file)) == ENC_UNKNOWN) {
2289             return false;
2290         }
2291         file.reset();
2292     }
2293     QTextStream stream(&file);
2294     if (encode == ENC_UNICODE) {
2295         stream.setCodec("UTF-8");
2296     } else if (encode == ENC_WINDOWS) {
2297         stream.setCodec("windows-1252");
2298     }
2299 
2300     QString data = stream.readAll();
2301     qApp->processEvents();  // give UI events a chance
2302     QString error;
2303     int line;
2304     QDomDocument doc;
2305     if (!doc.setContent(data, false, &error, &line)) {
2306         uWarning() << "Cannot set content:" << error << " Line:" << line;
2307         return false;
2308     }
2309     qApp->processEvents();  // give UI events a chance
2310     QDomNode node = doc.firstChild();
2311     //Before Umbrello 1.1-rc1 we didn't add a <?xml heading
2312     //so we allow the option of this being missing
2313     while (node.isComment() || node.isProcessingInstruction()) {
2314         node = node.nextSibling();
2315     }
2316 
2317     QDomElement root = node.toElement();
2318     if (root.isNull()) {
2319         return false;
2320     }
2321     //  make sure it is an XMI file
2322     if (root.tagName() != QLatin1String("XMI") && root.tagName() != QLatin1String("xmi:XMI")) {
2323         return false;
2324     }
2325 
2326     QString versionString = root.attribute(QLatin1String("xmi.version"));
2327     if (versionString.isEmpty())
2328         versionString = root.attribute(QLatin1String("xmi:version"));
2329     if (! versionString.isEmpty()) {
2330         double version = versionString.toDouble();
2331         if (version < 1.0) {
2332             QString error = i18n("Unsupported xmi file version: %1", versionString);
2333             m_d->errors << error;
2334             DEBUG(DBG_SRC) << error;
2335             return false;
2336         }
2337     }
2338 
2339     m_nViewID = Uml::ID::None;
2340     for (node = node.firstChild(); !node.isNull(); node = node.nextSibling()) {
2341         if (node.isComment()) {
2342             continue;
2343         }
2344         QDomElement element = node.toElement();
2345         if (element.isNull()) {
2346             DEBUG(DBG_SRC) << "loadFromXMI: skip empty elem";
2347             continue;
2348         }
2349         bool recognized = false;
2350         QString outerTag = element.tagName();
2351         //check header
2352         if (outerTag == QLatin1String("XMI.header")) {
2353             QDomNode headerNode = node.firstChild();
2354             if (!validateXMI1Header(headerNode)) {
2355                 return false;
2356             }
2357             recognized = true;
2358         } else if (outerTag == QLatin1String("XMI.extensions")) {
2359             QDomNode extensionsNode = node.firstChild();
2360             while (! extensionsNode.isNull()) {
2361                 loadExtensionsFromXMI1(extensionsNode);
2362                 extensionsNode = extensionsNode.nextSibling();
2363             }
2364             recognized = true;
2365         } else if (tagEq(outerTag, QLatin1String("Model")) ||
2366                    tagEq(outerTag, QLatin1String("Package"))) {
2367             if(!loadUMLObjectsFromXMI1(element)) {
2368                 uWarning() << "failed load on objects";
2369                 return false;
2370             }
2371             m_Name = element.attribute(QLatin1String("name"), i18n("UML Model"));
2372             UMLListView *lv = UMLApp::app()->listView();
2373             lv->setTitle(0, m_Name);
2374             recognized = true;
2375         }
2376         if (outerTag != QLatin1String("XMI.content")) {
2377             if (!recognized) {
2378                 DEBUG(DBG_SRC) << "skipping <" << outerTag << ">";
2379             }
2380             continue;
2381         }
2382         bool seen_UMLObjects = false;
2383         //process content
2384         for (QDomNode child = node.firstChild(); !child.isNull();
2385                 child = child.nextSibling()) {
2386             if (child.isComment()) {
2387                 continue;
2388             }
2389             element = child.toElement();
2390             QString tag = element.tagName();
2391             if (tag == QLatin1String("umlobjects")  // for bkwd compat.
2392                     || tagEq(tag, QLatin1String("Subsystem"))
2393                     || tagEq(tag, QLatin1String("Project"))  // Embarcadero's Describe
2394                     || tagEq(tag, QLatin1String("Model"))) {
2395                 if(!loadUMLObjectsFromXMI1(element)) {
2396                     uWarning() << "failed load on objects";
2397                     return false;
2398                 }
2399                 m_Name = element.attribute(QLatin1String("name"), i18n("UML Model"));
2400                 UMLListView *lv = UMLApp::app()->listView();
2401                 lv->setTitle(0, m_Name);
2402                 seen_UMLObjects = true;
2403             } else if (tagEq(tag, QLatin1String("Package")) ||
2404                        tagEq(tag, QLatin1String("Class")) ||
2405                        tagEq(tag, QLatin1String("Interface"))) {
2406                 // These tests are only for foreign XMI files that
2407                 // are missing the <Model> tag (e.g. NSUML)
2408                 QString stID = element.attribute(QLatin1String("stereotype"));
2409                 UMLObject *pObject = Object_Factory::makeObjectFromXMI(tag, stID);
2410                 if (!pObject) {
2411                     uWarning() << "Unknown type of umlobject to create: " << tag;
2412                     // We want a best effort, therefore this is handled as a
2413                     // soft error.
2414                     continue;
2415                 }
2416                 UMLObject::ObjectType ot = pObject->baseType();
2417                 // Set the parent root folder.
2418                 UMLPackage *pkg = 0;
2419                 if (ot != UMLObject::ot_Stereotype) {
2420                     if (ot == UMLObject::ot_Datatype) {
2421                         pkg = m_datatypeRoot;
2422                     } else {
2423                         Uml::ModelType::Enum guess = Model_Utils::guessContainer(pObject);
2424                         if (guess != Uml::ModelType::N_MODELTYPES) {
2425                             pkg = m_root[guess];
2426                         }
2427                         else {
2428                             uError() << "Guess is Uml::ModelType::N_MODELTYPES - package not set correctly for "
2429                                      << pObject->name() << " / base type " << pObject->baseTypeStr();
2430                             pkg = m_root[Uml::ModelType::Logical];
2431                         }
2432                     }
2433                 }
2434                 pObject->setUMLPackage(pkg);
2435                 bool status = pObject->loadFromXMI1(element);
2436                 if (!status) {
2437                     delete pObject;
2438                     return false;
2439                 }
2440                 seen_UMLObjects = true;
2441             } else if (tagEq(tag, QLatin1String("TaggedValue"))) {
2442                 // This tag is produced here, i.e. outside of <UML:Model>,
2443                 // by the Unisys.JCR.1 Rose-to-XMI tool.
2444                 if (! seen_UMLObjects) {
2445                     DEBUG(DBG_SRC) << "skipping TaggedValue because not seen_UMLObjects";
2446                     continue;
2447                 }
2448                 tag = element.attribute(QLatin1String("tag"));
2449                 if (tag != QLatin1String("documentation")) {
2450                     continue;
2451                 }
2452                 QString modelElement = element.attribute(QLatin1String("modelElement"));
2453                 if (modelElement.isEmpty()) {
2454                     DEBUG(DBG_SRC) << "skipping TaggedValue(documentation) because "
2455                                    << "modelElement.isEmpty()";
2456                     continue;
2457                 }
2458                 UMLObject *o = findObjectById(Uml::ID::fromString(modelElement));
2459                 if (o == 0) {
2460                     DEBUG(DBG_SRC) << "TaggedValue(documentation): cannot find object"
2461                                    << " for modelElement " << modelElement;
2462                     continue;
2463                 }
2464                 QString value = element.attribute(QLatin1String("value"));
2465                 if (! value.isEmpty()) {
2466                     o->setDoc(value);
2467                 }
2468             } else {
2469                 // for backward compatibility
2470                 loadExtensionsFromXMI1(child);
2471             }
2472         }
2473     }
2474 
2475     resolveTypes();
2476     loadDiagrams1();
2477 
2478     // set a default code generator if no <XMI.extensions><codegeneration> tag seen
2479     if (UMLApp::app()->generator() == 0) {
2480         UMLApp::app()->setGenerator(UMLApp::app()->defaultLanguage());
2481     }
2482     emit sigWriteToStatusBar(i18n("Setting up the document..."));
2483     qApp->processEvents();  // give UI events a chance
2484     activateAllViews();
2485 
2486     UMLView *viewToBeSet = 0;
2487     if (m_nViewID != Uml::ID::None) {
2488         viewToBeSet = findView(m_nViewID);
2489     }
2490     if (viewToBeSet) {
2491         changeCurrentView(m_nViewID);
2492     } else {
2493         QString name = createDiagramName(Uml::DiagramType::Class, false);
2494         createDiagram(m_root[Uml::ModelType::Logical], Uml::DiagramType::Class, name);
2495         m_pCurrentRoot = m_root[Uml::ModelType::Logical];
2496     }
2497     emit sigResetStatusbarProgress();
2498     return true;
2499 }
2500 
2501 /**
2502  * Type resolution pass.
2503  */
2504 void UMLDoc::resolveTypes()
2505 {
2506     // Resolve the types.
2507     // This is done in a separate pass because of possible forward references.
2508     if (m_bTypesAreResolved) {
2509         return;
2510     }
2511     writeToStatusBar(i18n("Resolving object references..."));
2512     for (int i = 0; i < Uml::ModelType::N_MODELTYPES; ++i) {
2513        UMLFolder *obj = m_root[i];
2514 #ifdef VERBOSE_DEBUGGING
2515         DEBUG(DBG_SRC) << "UMLDoc: invoking resolveRef() for " << obj->name()
2516                        << " (id=" << Uml::ID::toString(obj->id()) << ")";
2517 #endif
2518         obj->resolveRef();
2519     }
2520     m_bTypesAreResolved = true;
2521     qApp->processEvents();  // give UI events a chance
2522 }
2523 
2524 /**
2525  * Load all diagrams collected from the xmi file.
2526  *
2527  * Loading diagrams is implemented as additional pass to avoid unresolved
2528  * uml objects which are defined later in the xmi file.
2529  */
2530 bool UMLDoc::loadDiagrams1()
2531 {
2532     bool result = true;
2533     DiagramsMap::const_iterator i;
2534     for (i = m_diagramsToLoad.constBegin(); i != m_diagramsToLoad.constEnd(); i++) {
2535         UMLFolder *f = i.key();
2536         foreach(QDomNode node, i.value())
2537             if (!f->loadDiagramsFromXMI1(node))
2538                 result = false;
2539     }
2540 
2541     m_diagramsToLoad.clear();
2542     return result;
2543 }
2544 
2545 /**
2546  * Add a xml node containing a diagram to the list of diagrams to load.
2547  * Helper function for loadDiagrams().
2548  *
2549  * @param folder pointer to UMFolder instance the diagrams belongs to
2550  * @param node xml document node containing the diagram
2551  */
2552 void UMLDoc::addDiagramToLoad(UMLFolder *folder, QDomNode node)
2553 {
2554     if (m_diagramsToLoad.contains(folder))
2555         m_diagramsToLoad[folder].append(node);
2556     else
2557         m_diagramsToLoad[folder] = QList<QDomNode>() << node;
2558 }
2559 
2560 DiagramsModel *UMLDoc::diagramsModel() const
2561 {
2562     return m_diagramsModel;
2563 }
2564 
2565 ObjectsModel *UMLDoc::objectsModel() const
2566 {
2567     return m_objectsModel;
2568 }
2569 
2570 void UMLDoc::setLoadingError(const QString &text)
2571 {
2572     m_d->errors << text;
2573 }
2574 
2575 StereotypesModel *UMLDoc::stereotypesModel() const
2576 {
2577     return m_stereotypesModel;
2578 }
2579 
2580 /**
2581  * Ensures the XMI file is a valid UML file.
2582  * Currently only checks for metamodel=UML.
2583  *
2584  * @param headerNode   The <XMI.header> node
2585  */
2586 bool UMLDoc::validateXMI1Header(QDomNode& headerNode)
2587 {
2588     QDomElement headerElement = headerNode.toElement();
2589     while (!headerNode.isNull()) {
2590         /*  //Seems older Umbrello files used a different metamodel, so don't validate it for now
2591           if(!headerElement.isNull() && headerElement.tagName() == "XMI.metamodel") {
2592               String metamodel = headerElement.attribute("xmi.name");
2593               if (metamodel != "UML") {
2594                   return false;
2595               }
2596           }
2597         */
2598         headerNode = headerNode.nextSibling();
2599         headerElement = headerNode.toElement();
2600     }
2601     return true;
2602 }
2603 
2604 /**
2605  * Loads all UML objects from XMI into the current UMLDoc.
2606  *
2607  * @return  True if operation successful.
2608  */
2609 bool UMLDoc::loadUMLObjectsFromXMI1(QDomElement& element)
2610 {
2611     /* FIXME need a way to make status bar actually reflect
2612        how much of the file has been loaded rather than just
2613        counting to 10 (an arbitrary number)
2614     emit sigResetStatusbarProgress();
2615     emit sigSetStatusbarProgress(0);
2616     emit sigSetStatusbarProgressSteps(10);
2617     m_count = 0;
2618      */
2619     emit sigWriteToStatusBar(i18n("Loading UML elements..."));
2620 
2621     // For Umbrello native XMI files, when called from loadFromXMI1() we
2622     // get here with Element.tagName() == "UML:Model" from the XMI input:
2623     // <UML:Model name="UML Model">
2624     for (QDomNode node = element.firstChild(); !node.isNull();
2625             node = node.nextSibling()) {
2626         if (node.isComment()) {
2627             continue;
2628         }
2629         QDomElement tempElement = node.toElement();
2630         QString type = tempElement.tagName();
2631         if (tagEq(type, QLatin1String("Model"))) {
2632             // Handling of Umbrello native XMI files:
2633             // We get here from a recursive call to loadUMLObjectsFromXMI()
2634             // a few lines below, see
2635             //       if (tagEq(type, "Namespace.ownedElement") ....
2636             // Inside this Namespace.ownedElement envelope there are the
2637             // four submodels:
2638             // <UML:Model name="Logical View">
2639             // <UML:Model name="Use Case View">
2640             // <UML:Model name="Component View">
2641             // <UML:Model name="Deployment View">
2642             // These are ultimately loaded by UMLFolder::loadFromXMI1()
2643             // Furthermore, in Umbrello native XMI format this
2644             // Namespace.ownedElement is the container of all stereotypes
2645             // (<UML:Stereotype>).
2646             bool foundUmbrelloRootFolder = false;
2647             QString name = tempElement.attribute(QLatin1String("name"));
2648             for (int i = 0; i < Uml::ModelType::N_MODELTYPES; ++i) {
2649                 if (name == m_root[i]->name()) {
2650                     m_pCurrentRoot = m_root[i];
2651                     m_root[i]->loadFromXMI1(tempElement);
2652                     foundUmbrelloRootFolder = true;
2653                     break;
2654                 }
2655             }
2656             if (foundUmbrelloRootFolder) {
2657                 continue;
2658             }
2659         }
2660         if (tagEq(type, QLatin1String("Namespace.ownedElement")) ||
2661                 tagEq(type, QLatin1String("Namespace.contents")) ||
2662                 tagEq(type, QLatin1String("Element.ownedElement")) ||  // Embarcadero's Describe
2663                 tagEq(type, QLatin1String("Model"))) {
2664             //CHECK: Umbrello currently assumes that nested elements
2665             // are ownedElements anyway.
2666             // Therefore the <UML:Namespace.ownedElement> tag is of no
2667             // significance.
2668             // The tagEq(type, "Namespace.contents") and tagEq(type, "Model")
2669             // tests do not become true for Umbrello native files, only for
2670             // some foreign XMI files.
2671             if (!loadUMLObjectsFromXMI1(tempElement)) {
2672                 uWarning() << "failed load on " << type;
2673                 return false;
2674             }
2675             continue;
2676         }
2677         // From here on, it's support for stereotypes, pre 1.5.5 versions,
2678         // and foreign files
2679         if (Model_Utils::isCommonXMI1Attribute(type)) {
2680             continue;
2681         } else if (tagEq(type, QLatin1String("packagedElement")) ||
2682                    tagEq(type, QLatin1String("ownedElement"))) {
2683             type = tempElement.attribute(QLatin1String("xmi:type"));
2684         }
2685         if (!tempElement.hasAttribute(QLatin1String("xmi.id")) &&
2686             !tempElement.hasAttribute(QLatin1String("xmi:id"))) {
2687             QString idref = tempElement.attribute(QLatin1String("xmi.idref"));
2688             if (! idref.isEmpty()) {
2689                 DEBUG(DBG_SRC) << "resolution of xmi.idref " << idref
2690                                << " is not yet implemented";
2691             } else {
2692                 uError() << "Cannot load " << type
2693                          << " because xmi.id is missing";
2694             }
2695             continue;
2696         }
2697         QString stID = tempElement.attribute(QLatin1String("stereotype"));
2698         UMLObject *pObject = Object_Factory::makeObjectFromXMI(type, stID);
2699         if (!pObject) {
2700             uWarning() << "Unknown type of umlobject to create: " << type;
2701             // We want a best effort, therefore this is handled as a
2702             // soft error.
2703             continue;
2704         }
2705         UMLObject::ObjectType ot = pObject->baseType();
2706         // Set the parent root folder.
2707         UMLPackage *pkg = 0;
2708         if (ot != UMLObject::ot_Stereotype) {
2709             if (ot == UMLObject::ot_Datatype) {
2710                 pkg = m_datatypeRoot;
2711             } else {
2712                 Uml::ModelType::Enum guess = Model_Utils::guessContainer(pObject);
2713                 if (guess != Uml::ModelType::N_MODELTYPES) {
2714                     pkg = m_root[guess];
2715                 }
2716                 else {
2717                     uError() << "Guess is Uml::ModelType::N_MODELTYPES - package not set correctly for "
2718                              << pObject->name() << " / base type " << pObject->baseTypeStr();
2719                     pkg = m_root[Uml::ModelType::Logical];
2720                 }
2721             }
2722         }
2723         pObject->setUMLPackage(pkg);
2724 
2725         bool status = pObject->loadFromXMI1(tempElement);
2726         if (!status) {
2727             delete pObject;
2728             return false;
2729         }
2730         pkg = pObject->umlPackage();
2731         if (ot == UMLObject::ot_Stereotype) {
2732             UMLStereotype *s = pObject->asUMLStereotype();
2733             UMLStereotype *exist = findStereotype(pObject->name());
2734             if (exist) {
2735                 if (exist->id() == pObject->id()) {
2736                     delete pObject;
2737                 } else {
2738                     DEBUG(DBG_SRC) << "Stereotype " << pObject->name()
2739                                    << "(id=" << Uml::ID::toString(pObject->id())
2740                                    << ") already exists with id="
2741                                    << Uml::ID::toString(exist->id());
2742                     addStereotype(s);
2743                 }
2744             } else {
2745                 addStereotype(s);
2746             }
2747             continue;
2748         }
2749         if (pkg) {
2750             UMLObjectList objects = pkg->containedObjects();
2751             if (! objects.contains(pObject)) {
2752                 DEBUG(DBG_SRC) << "CHECK: adding " << pObject->name()
2753                                << " to " << pkg->name();
2754                 if (!pkg->addObject(pObject)) {
2755                     DEBUG(DBG_SRC) << "pkg->addObject failed";
2756                 }
2757             }
2758         }
2759         else if (ot != UMLObject::ot_Stereotype) {
2760             uError() << "Package is NULL for " << pObject->name();
2761             return false;
2762         }
2763 
2764         /* FIXME see comment at loadUMLObjectsFromXMI1
2765         emit sigSetStatusbarProgress(++m_count);
2766          */
2767     }
2768     return true;
2769 }
2770 
2771 /**
2772  * Sets m_nViewID.
2773  */
2774 void UMLDoc::setMainViewID(Uml::ID::Type viewID)
2775 {
2776     m_nViewID = viewID;
2777 }
2778 
2779 /**
2780  * Loads umbrello specific extensions from XMI to the UMLDoc.
2781  * The extension tags are: "docsettings", "diagrams", "listview",
2782  * and "codegeneration".
2783  */
2784 void UMLDoc::loadExtensionsFromXMI1(QDomNode& node)
2785 {
2786     QDomElement element = node.toElement();
2787     QString tag = element.tagName();
2788 
2789     if (tag == QLatin1String("docsettings")) {
2790         QString viewID = element.attribute(QLatin1String("viewid"), QLatin1String("-1"));
2791         m_Doc = element.attribute(QLatin1String("documentation"));
2792         QString uniqueid = element.attribute(QLatin1String("uniqueid"), QLatin1String("0"));
2793 
2794         m_nViewID = Uml::ID::fromString(viewID);
2795         UniqueID::set(Uml::ID::fromString(uniqueid));
2796         UMLApp::app()->docWindow()->reset();
2797 
2798     } else if (tag == QLatin1String("diagrams") || tag == QLatin1String("UISModelElement")) {
2799         // For backward compatibility only:
2800         // Since version 1.5.5 diagrams are saved as part of the UMLFolder.
2801         QDomNode diagramNode = node.firstChild();
2802         if (tag == QLatin1String("UISModelElement")) {          // Unisys.IntegratePlus.2
2803             element = diagramNode.toElement();
2804             tag = element.tagName();
2805             if (tag != QLatin1String("uisOwnedDiagram")) {
2806                 uError() << "unknown child node " << tag;
2807                 return;
2808             }
2809             diagramNode = diagramNode.firstChild();
2810         } else {
2811             qreal resolution = 0.0;
2812             QString res = node.toElement().attribute(QLatin1String("resolution"), QLatin1String(""));
2813             if (!res.isEmpty()) {
2814                resolution = res.toDouble();
2815             }
2816             if (resolution != 0.0) {
2817                UMLApp::app()->document()->setResolution(resolution);
2818             } else {
2819                // see UMLFolder::loadDiagramsFromXMI()
2820                UMLApp::app()->document()->setResolution(0.0);
2821             }
2822         }
2823         if (!loadDiagramsFromXMI1(diagramNode)) {
2824             uWarning() << "failed load on diagrams";
2825         }
2826 
2827     } else if (tag == QLatin1String("listview")) {
2828         //FIXME: Need to resolveTypes() before loading listview,
2829         //       else listview items are duplicated.
2830         resolveTypes();
2831         if (!UMLApp::app()->listView()->loadFromXMI1(element)) {
2832             uWarning() << "failed load on listview";
2833         }
2834 
2835     } else if (tag == QLatin1String("codegeneration")) {
2836         QDomNode cgnode = node.firstChild();
2837         QDomElement cgelement = cgnode.toElement();
2838         while (!cgelement.isNull()) {
2839             QString nodeName = cgelement.tagName();
2840             QString lang = cgelement.attribute(QLatin1String("language"), QLatin1String("UNKNOWN"));
2841             Uml::ProgrammingLanguage::Enum pl = Uml::ProgrammingLanguage::fromString(lang);
2842             if (pl != Uml::ProgrammingLanguage::Reserved) {
2843                 CodeGenerator *g = UMLApp::app()->setGenerator(pl);
2844                 g->loadFromXMI1(cgelement);
2845             } else
2846                 uDebug() << "codegeneration : no setup performed for" << lang;
2847             cgnode = cgnode.nextSibling();
2848             cgelement = cgnode.toElement();
2849         }
2850         if (UMLApp::app()->generator() == 0) {
2851             UMLApp::app()->setGenerator(UMLApp::app()->defaultLanguage());
2852         }
2853     }
2854 }
2855 
2856 /**
2857  * Loads all diagrams from XMI into the current UMLDoc.
2858  * For backward compatibility only:
2859  * Since version 1.5.5 diagrams are saved as part of the UMLFolder.
2860  *
2861  * @return  True if operation successful.
2862  */
2863 bool UMLDoc::loadDiagramsFromXMI1(QDomNode & node)
2864 {
2865     emit sigWriteToStatusBar(i18n("Loading diagrams..."));
2866     emit sigResetStatusbarProgress();
2867     emit sigSetStatusbarProgress(0);
2868     emit sigSetStatusbarProgressSteps(10); //FIX ME
2869     QDomElement element = node.toElement();
2870     if (element.isNull()) {
2871         return true;  //return ok as it means there is no umlobjects
2872     }
2873     const Settings::OptionState state = Settings::optionState();
2874     UMLView * pView = 0;
2875     int count = 0;
2876     while (!element.isNull()) {
2877         QString tag = element.tagName();
2878         if (tag == QLatin1String("diagram") || tag == QLatin1String("UISDiagram")) {
2879             pView = new UMLView(0);
2880             // IMPORTANT: Set OptionState of new UMLView _BEFORE_
2881             // reading the corresponding diagram:
2882             // + allow using per-diagram color and line-width settings
2883             // + avoid crashes due to uninitialized values for lineWidth
2884             pView->umlScene()->setOptionState(state);
2885             bool success = false;
2886             if (tag == QLatin1String("UISDiagram")) {
2887                 success = pView->umlScene()->loadUISDiagram(element);
2888             } else {
2889                 success = pView->umlScene()->loadFromXMI1(element);
2890             }
2891             if (!success) {
2892                 uWarning() << "failed load on viewdata loadfromXMI";
2893                 delete pView;
2894                 return false;
2895             }
2896             // Put diagram in default predefined folder.
2897             // @todo pass in the parent folder - it might be a user defined one.
2898             Uml::ModelType::Enum mt = Model_Utils::convert_DT_MT(pView->umlScene()->type());
2899             if (mt != Uml::ModelType::N_MODELTYPES) {
2900                 pView->umlScene()->setFolder(m_root[mt]);
2901                 pView->hide();
2902                 addView(pView);
2903             } else {
2904                 uWarning() << "cannot add " << tag << " because umlScene type "
2905                            << pView->umlScene()->type() << " cannot be mapped to ModelType";
2906             }
2907             emit sigSetStatusbarProgress(++count);
2908             qApp->processEvents();  // give UI events a chance
2909         }
2910         node = node.nextSibling();
2911         element = node.toElement();
2912     }
2913     return true;
2914 }
2915 
2916 /**
2917  * Call to remove all the views (diagrams) in the current file.
2918  */
2919 void UMLDoc::removeAllViews()
2920 {
2921     for (int i = 0; i < Uml::ModelType::N_MODELTYPES; ++i) {
2922         m_root[i]->removeAllViews();
2923     }
2924 
2925     UMLApp::app()->setCurrentView(0);
2926     emit sigDiagramChanged(Uml::DiagramType::Undefined);
2927     UMLApp::app()->setDiagramMenuItemsState(false);
2928 }
2929 
2930 /**
2931  * Call to remove all objects in the current file.
2932  */
2933 void UMLDoc::removeAllObjects()
2934 {
2935     m_root[Uml::ModelType::Logical]->removeObject(m_datatypeRoot);
2936 
2937     for (int i = 0; i < Uml::ModelType::N_MODELTYPES; ++i) {
2938         m_root[i]->removeAllObjects();
2939     }
2940 }
2941 
2942 /**
2943  * Returns a list of the packages in this UMLDoc,
2944  *
2945  * @return List of UMLPackages.
2946  */
2947 UMLPackageList UMLDoc::packages(bool includeNested /* = true */, Uml::ModelType::Enum model) const
2948 {
2949     UMLPackageList packageList;
2950     m_root[model]->appendPackages(packageList, includeNested);
2951     return packageList;
2952 }
2953 
2954 /**
2955  * Returns the datatype folder.
2956  *
2957  * @return  Pointer to the predefined folder for datatypes.
2958  */
2959 UMLFolder * UMLDoc::datatypeFolder() const
2960 {
2961     return m_datatypeRoot;
2962 }
2963 
2964 /**
2965  * Returns a list of the concepts in this UMLDoc.
2966  *
2967  * @param includeNested   Whether to include the concepts from
2968  *                        nested packages (default: true.)
2969  * @return  List of UML concepts.
2970  */
2971 UMLClassifierList UMLDoc::concepts(bool includeNested /* =true */) const
2972 {
2973     UMLClassifierList conceptList;
2974     m_root[Uml::ModelType::Logical]->appendClassifiers(conceptList, includeNested);
2975     return conceptList;
2976 }
2977 
2978 /**
2979  * Returns a list of the classes and interfaces in this UMLDoc.
2980  *
2981  * @param includeNested   Whether to include the concepts from
2982  *                        nested packages (default: true.)
2983  * @return  List of UML concepts.
2984  */
2985 UMLClassifierList UMLDoc::classesAndInterfaces(bool includeNested /* =true */) const
2986 {
2987     UMLClassifierList conceptList;
2988     m_root[Uml::ModelType::Logical]->appendClassesAndInterfaces(conceptList, includeNested);
2989     return conceptList;
2990 }
2991 
2992 /**
2993  * Returns a list of the entities in this UMLDoc.
2994  *
2995  * @param includeNested   Whether to include the entities from
2996  *                        nested packages (default: true.)
2997  * @return  List of UML Entities.
2998  */
2999 UMLEntityList UMLDoc::entities(bool includeNested /* =true */) const
3000 {
3001     UMLEntityList entityList;
3002     m_root[Uml::ModelType::EntityRelationship]->appendEntities(entityList, includeNested);
3003     return entityList;
3004 }
3005 
3006 /**
3007  * Returns a list of the datatypes in this UMLDoc.
3008  *
3009  * @param includeInactive  Include inactive datatypes which may have accrued by
3010  *                         changing the active programming language.
3011  * @return  List of datatypes.
3012  */
3013 UMLClassifierList UMLDoc::datatypes(bool includeInactive /* = false */) const
3014 {
3015     UMLObjectList objects = m_datatypeRoot->containedObjects(includeInactive);
3016     UMLClassifierList datatypeList;
3017     foreach (UMLObject *obj, objects) {
3018         uIgnoreZeroPointer(obj);
3019         if (obj->isUMLDatatype()) {
3020             datatypeList.append(obj->asUMLClassifier());
3021         }
3022     }
3023     return datatypeList;
3024 }
3025 
3026 /**
3027  * Returns a list of the associations in this UMLDoc.
3028  *
3029  * @return  List of UML associations.
3030  */
3031 UMLAssociationList UMLDoc::associations() const
3032 {
3033     UMLAssociationList associationList;
3034     for (int i = 0; i < Uml::ModelType::N_MODELTYPES; ++i) {
3035         UMLAssociationList assocs = m_root[i]->getAssociations();
3036 
3037         foreach (UMLAssociation* a, assocs) {
3038             associationList.append(a);
3039         }
3040     }
3041     return associationList;
3042 }
3043 
3044 /**
3045  * Controls the printing of the program.
3046  *
3047  * @param pPrinter  The printer (object) to use.
3048  * @param selectPage  The DiagramPrintPage by which diagrams are selected for printing
3049  */
3050 void UMLDoc::print(QPrinter * pPrinter, DiagramPrintPage * selectPage)
3051 {
3052     UMLView * printView = 0;
3053     int count = selectPage->printUmlCount();
3054     QPainter painter(pPrinter);
3055     for (int i = 0; i < count; ++i) {
3056         if (i>0) {
3057             pPrinter->newPage();
3058         }
3059         QString sID = selectPage->printUmlDiagram(i);
3060         Uml::ID::Type id = Uml::ID::fromString(sID);
3061         printView = findView(id);
3062 
3063         if (printView) {
3064             printView->umlScene()->print(pPrinter, painter);
3065         }
3066         printView = 0;
3067     }
3068     painter.end();
3069 }
3070 
3071 /**
3072  * Return the list of views for this document.
3073  *
3074  * @return  List of UML views.
3075  */
3076 UMLViewList UMLDoc::viewIterator() const
3077 {
3078     UMLViewList accumulator;
3079     for (int i = 0; i < Uml::ModelType::N_MODELTYPES; ++i) {
3080         m_root[i]->appendViews(accumulator, true);
3081     }
3082     return accumulator;
3083 }
3084 
3085 /**
3086  * Return a list of filtered views of this document by type.
3087 
3088  * @param type diagram type to filter
3089  * @return  List of UML views.
3090  */
3091 UMLViewList UMLDoc::views(Uml::DiagramType::Enum type) const
3092 {
3093     UMLViewList result;
3094     foreach(UMLView *v, viewIterator()) {
3095         if (type == Uml::DiagramType::Undefined || v->umlScene()->type() == type)
3096             result.append(v);
3097     }
3098     return result;
3099 }
3100 
3101 /**
3102  * Sets the modified flag for the document after a modifying
3103  * action on the view connected to the document.
3104  *
3105  * @param modified   The value to set the modified flag to.
3106  */
3107 void UMLDoc::setModified(bool modified /*=true*/)
3108 {
3109     if (!m_bLoading) {
3110         m_modified = modified;
3111         UMLApp::app()->setModified(modified);
3112     }
3113 }
3114 
3115 /**
3116  * Returns if the document is modified or not. Use this to
3117  * determine if your document needs saving by the user on
3118  * closing.
3119  *
3120  * @return  True if this UMLDoc is modified.
3121  */
3122 bool UMLDoc::isModified() const
3123 {
3124     return m_modified;
3125 }
3126 
3127 /**
3128  * Assigns an already created UMLObject a new ID.
3129  * If the object is a classifier then the operations/attributes
3130  * are also assigned new IDs.
3131  *
3132  * @param obj   Pointer to the UMLObject to add.
3133  * @return  True if operation successful.
3134  */
3135 bool UMLDoc::assignNewIDs(UMLObject* obj)
3136 {
3137     if (!obj || !m_pChangeLog) {
3138         DEBUG(DBG_SRC) << "no obj || Changelog";
3139         return false;
3140     }
3141     Uml::ID::Type result = assignNewID(obj->id());
3142     obj->setID(result);
3143 
3144     //If it is a CONCEPT then change the ids of all its operations and attributes
3145     if (obj->baseType() == UMLObject::ot_Class) {
3146         UMLClassifier *c = obj->asUMLClassifier();
3147         UMLClassifierListItemList attributes = c->getFilteredList(UMLObject::ot_Attribute);
3148         foreach (UMLObject* listItem,  attributes) {
3149             result = assignNewID(listItem->id());
3150             listItem->setID(result);
3151         }
3152 
3153         UMLClassifierListItemList templates = c->getFilteredList(UMLObject::ot_Template);
3154         foreach (UMLObject* listItem, templates) {
3155             result = assignNewID(listItem->id());
3156             listItem->setID(result);
3157         }
3158     }
3159 
3160     if (obj->baseType() == UMLObject::ot_Interface || obj->baseType() == UMLObject::ot_Class) {
3161         UMLOperationList operations(((UMLClassifier*)obj)->getOpList());
3162         foreach (UMLObject* listItem, operations) {
3163             result = assignNewID(listItem->id());
3164             listItem->setID(result);
3165         }
3166     }
3167 
3168     setModified(true);
3169 
3170     return true;
3171 }
3172 
3173 /**
3174  * Return the predefined root folder of the given type.
3175  */
3176 UMLFolder *UMLDoc::rootFolder(Uml::ModelType::Enum mt) const
3177 {
3178     if (mt < Uml::ModelType::Logical || mt >= Uml::ModelType::N_MODELTYPES) {
3179         uError() << "illegal input value " << Uml::ModelType::toString(mt);
3180         return 0;
3181     }
3182     return m_root[mt];
3183 }
3184 
3185 /**
3186  * Return the corresponding Model_Type if the given object
3187  * is one of the root folders.
3188  * When the given object is not one of the root folders then
3189  * return Uml::ModelType::N_MODELTYPES.
3190  */
3191 Uml::ModelType::Enum UMLDoc::rootFolderType(UMLObject *obj) const
3192 {
3193     for (int i = 0; i < Uml::ModelType::N_MODELTYPES; ++i) {
3194         const Uml::ModelType::Enum m = Uml::ModelType::fromInt(i);
3195         if (obj == m_root[m]) {
3196             return m;
3197         }
3198     }
3199     return Uml::ModelType::N_MODELTYPES;
3200 }
3201 
3202 /**
3203  * Read property of IDChangeLog* m_pChangeLog.
3204  *
3205  * @return  Pointer to the IDChangeLog object.
3206  */
3207 IDChangeLog* UMLDoc::changeLog() const
3208 {
3209     return m_pChangeLog;
3210 }
3211 
3212 /**
3213  * Opens a Paste session, deletes the old ChangeLog and
3214  * creates an empty one.
3215  */
3216 void UMLDoc::beginPaste()
3217 {
3218     if (m_pChangeLog) {
3219         delete m_pChangeLog;
3220         m_pChangeLog = 0;
3221     }
3222     m_pChangeLog = new IDChangeLog;
3223 }
3224 
3225 /**
3226  * Closes a paste session, deletes the ChangeLog.
3227  */
3228 void UMLDoc::endPaste()
3229 {
3230     if (m_pChangeLog) {
3231         delete m_pChangeLog;
3232         m_pChangeLog = 0;
3233     }
3234 }
3235 
3236 /**
3237  * Assigns a New ID to an Object, and also logs the assignment
3238  * to its internal ChangeLog.
3239  *
3240  * @param oldID   The present ID of the object.
3241  * @return  The new ID assigned to the object.
3242  */
3243 Uml::ID::Type UMLDoc::assignNewID(Uml::ID::Type oldID)
3244 {
3245     Uml::ID::Type result = UniqueID::gen();
3246     if (m_pChangeLog) {
3247         m_pChangeLog->addIDChange(oldID, result);
3248     }
3249     return result;
3250 }
3251 
3252 /**
3253  * Returns the documentation for the project.
3254  *
3255  * @return  The documentation text of this UMLDoc.
3256  */
3257 QString UMLDoc::documentation() const
3258 {
3259     return m_Doc;
3260 }
3261 
3262 /**
3263  * Sets the documentation for the project.
3264  *
3265  * @param doc   The documentation to set for this UMLDoc.
3266  */
3267 void UMLDoc::setDocumentation(const QString &doc)
3268 {
3269     m_Doc = doc;
3270 }
3271 
3272 /**
3273  * Adds an already created UMLView to the document, it gets
3274  * assigned a new ID, if its name is already in use then the
3275  * function appends a number to it to differentiate it from
3276  * the others; this number is incremental so if number 1 is in
3277  * use then it tries 2 and then 3 and so on
3278  *
3279  * @param pView   Pointer to the UMLView to add.
3280  * @return  True if operation successful.
3281  */
3282 bool UMLDoc::addUMLView(UMLView * pView)
3283 {
3284     if (!pView || !m_pChangeLog) {
3285         return false;
3286     }
3287 
3288     Uml::ID::Type oldID = pView->umlScene()->ID();
3289 
3290     int i = 0;
3291     QString viewName = pView->umlScene()->name();
3292     QString name = viewName;
3293     while (findView(pView->umlScene()->type(), name) != 0) {
3294         name = viewName + QLatin1Char('_') + QString::number(++i);
3295     }
3296     if (i) { //If name was modified
3297         pView->umlScene()->setName(name);
3298     }
3299 
3300     Uml::ID::Type newID = assignNewID(oldID);
3301     pView->umlScene()->setID(newID);
3302 
3303     pView->umlScene()->activateAfterLoad(true);
3304     pView->umlScene()->endPartialWidgetPaste();
3305     pView->umlScene()->setOptionState(Settings::optionState());
3306     addView(pView);
3307 
3308     emit sigDiagramCreated(pView->umlScene()->ID());
3309 
3310     setModified(true);
3311     return true;
3312 }
3313 
3314 /**
3315  * Activate all the diagrams/views after loading so all their
3316  * widgets keep their IDs.
3317  */
3318 void UMLDoc::activateAllViews()
3319 {
3320     // store old setting - for restore of last setting
3321     bool m_bLoading_old = m_bLoading;
3322     m_bLoading = true; //this is to prevent document becoming modified when activating a view
3323 
3324     for (int i = 0; i < Uml::ModelType::N_MODELTYPES; ++i) {
3325         m_root[i]->activateViews();
3326     }
3327     m_bLoading = m_bLoading_old;
3328 }
3329 
3330 /**
3331  * Sets the default settings to the given settings.
3332  * @param optionState   settings
3333  */
3334 void UMLDoc::settingsChanged(Settings::OptionState &optionState)
3335 {
3336     for (int i = 0; i < Uml::ModelType::N_MODELTYPES; ++i) {
3337         m_root[i]->setViewOptions(optionState);
3338     }
3339     initSaveTimer();
3340 }
3341 
3342 /**
3343  * Sets up the autosave timer.
3344  */
3345 void UMLDoc::initSaveTimer()
3346 {
3347     if (m_pAutoSaveTimer) {
3348         m_pAutoSaveTimer->stop();
3349         disconnect(m_pAutoSaveTimer, SIGNAL(timeout()), this, SLOT(slotAutoSave()));
3350         delete m_pAutoSaveTimer;
3351         m_pAutoSaveTimer = 0;
3352     }
3353     Settings::OptionState optionState = Settings::optionState();
3354     if (optionState.generalState.autosave) {
3355         m_pAutoSaveTimer = new QTimer(this);
3356         connect(m_pAutoSaveTimer, SIGNAL(timeout()), this, SLOT(slotAutoSave()));
3357         m_pAutoSaveTimer->setSingleShot(false);
3358         m_pAutoSaveTimer->start(optionState.generalState.autosavetime * 60000);
3359     }
3360 }
3361 
3362 /**
3363  * Called after a specified time to autosave the document.
3364  */
3365 void UMLDoc::slotAutoSave()
3366 {
3367     //Only save if modified.
3368     if (!m_modified) {
3369         return;
3370     }
3371 #if QT_VERSION >= 0x050000
3372     QUrl tempUrl = m_doc_url;
3373 #else
3374     KUrl tempUrl = m_doc_url;
3375 #endif
3376     if (tempUrl.fileName() == i18n("Untitled")) {
3377 #if QT_VERSION >= 0x050000
3378         tempUrl.setScheme(QLatin1String("file"));
3379 #endif
3380         tempUrl.setPath(QDir::homePath() + i18n("/autosave%1", QLatin1String(".xmi")));
3381         saveDocument(tempUrl);
3382         setUrlUntitled();
3383         m_modified = true;
3384         UMLApp::app()->setModified(m_modified);
3385     } else {
3386         // 2004-05-17 Achim Spangler
3387 #if QT_VERSION >= 0x050000
3388         QUrl orgDocUrl = m_doc_url;
3389 #else
3390         KUrl orgDocUrl = m_doc_url;
3391 #endif
3392         QString orgFileName = m_doc_url.fileName();
3393         // don't overwrite manually saved file with autosave content
3394         QString fileName = tempUrl.fileName();
3395         Settings::OptionState optionState = Settings::optionState();
3396         fileName.replace(QLatin1String(".xmi"), optionState.generalState.autosavesuffix);
3397 #if QT_VERSION >= 0x050000
3398         tempUrl.setUrl(tempUrl.toString(QUrl::RemoveFilename) + fileName);
3399 #else
3400         tempUrl.setFileName(fileName);
3401 #endif
3402         // End Achim Spangler
3403 
3404         saveDocument(tempUrl);
3405         // 2004-05-17 Achim Spangler
3406         // re-activate m_modified if autosave is writing to other file
3407         // than the main project file->autosave-suffix != ".xmi"
3408         if (optionState.generalState.autosavesuffix != QLatin1String(".xmi")) {
3409             m_modified = true;
3410             UMLApp::app()->setModified(m_modified);
3411         }
3412         // restore original file name -
3413         // UMLDoc::saveDocument() sets doc_url to filename which is given as autosave-filename
3414         setUrl(orgDocUrl);
3415         UMLApp * pApp = UMLApp::app();
3416         pApp->setCaption(orgFileName, isModified());
3417         // End Achim Spangler
3418     }
3419 }
3420 
3421 /**
3422  * Signal a view/diagram has been renamed.
3423  */
3424 void UMLDoc::signalDiagramRenamed(UMLView* view)
3425 {
3426     if (view) {
3427         Settings::OptionState optionState = Settings::optionState();
3428         if (optionState.generalState.tabdiagrams) {
3429             UMLApp::app()->tabWidget()->setTabText(UMLApp::app()->tabWidget()->indexOf(view), view->umlScene()->name());
3430         }
3431         emit sigDiagramRenamed(view->umlScene()->ID());
3432     }
3433     else {
3434       uError() << "Cannot signal diagram renamed - view is NULL!";
3435     }
3436 }
3437 
3438 /**
3439  * Calls the active code generator to create its default datatypes.
3440  */
3441 void UMLDoc::addDefaultDatatypes()
3442 {
3443     CodeGenerator *cg = UMLApp::app()->generator();
3444     if (cg == 0) {
3445         DEBUG(DBG_SRC) << "CodeGenerator is NULL : Assume UMLPrimitiveTypes";
3446         for (int i = 0; i < Uml::PrimitiveTypes::n_types; i++) {
3447             createDatatype(Uml::PrimitiveTypes::toString(i));
3448         }
3449     } else {
3450         QStringList entries = cg->defaultDatatypes();
3451         QStringList::Iterator end(entries.end());
3452         for (QStringList::Iterator it = entries.begin(); it != end; ++it) {
3453             createDatatype(*it);
3454         }
3455     }
3456     UMLApp::app()->listView()->closeDatatypesFolder();
3457 }
3458 
3459 /**
3460  * Add a datatype if it doesn't already exist.
3461  * Used by addDefaultDatatypes().
3462  */
3463 void UMLDoc::createDatatype(const QString &name)
3464 {
3465     UMLObjectList datatypes = m_datatypeRoot->containedObjects(true);
3466     UMLObject* umlobject = Model_Utils::findUMLObject(datatypes, name,
3467                                                       UMLObject::ot_Datatype, m_datatypeRoot);
3468     UMLDatatype *dt = nullptr;
3469     if (umlobject)
3470         dt = umlobject->asUMLDatatype();
3471     if (dt) {
3472         dt->setActive(true);
3473         signalUMLObjectCreated(umlobject);
3474         qApp->processEvents();
3475     } else {
3476         if (umlobject) {
3477             uWarning() << "UMLDoc::createDatatype(" << name
3478                        << ") : Name already exists but is not a Datatype";
3479         }
3480         Object_Factory::createUMLObject(UMLObject::ot_Datatype, name, m_datatypeRoot);
3481     }
3482 }
3483 
3484 /**
3485  * Remove a datatype by name.
3486  * Used when changing the active programming language.
3487  */
3488 void UMLDoc::removeDatatype(const QString &name)
3489 {
3490     UMLObjectList datatypes = m_datatypeRoot->containedObjects();
3491     // We don't use Model_Utils::findUMLObject because that function considers
3492     // case sensitivity of the active language, which we don't want here.
3493     foreach (UMLObject *obj, datatypes) {
3494         uIgnoreZeroPointer(obj);
3495         if (obj->name() == name) {
3496             removeUMLObject(obj);
3497             break;
3498         }
3499     }
3500 }
3501 
3502 /**
3503  * Make a popup menu for the tabs
3504  * signalled from tabWidget's contextMenu().
3505  */
3506 void UMLDoc::slotDiagramPopupMenu(QWidget* umlview, const QPoint& point)
3507 {
3508     UMLView* view = (UMLView*) umlview;
3509 
3510     UMLListViewItem::ListViewType type = UMLListViewItem::lvt_Unknown;
3511     switch (view->umlScene()->type()) {
3512     case Uml::DiagramType::Class:
3513         type = UMLListViewItem::lvt_Class_Diagram;
3514         break;
3515 
3516     case Uml::DiagramType::UseCase:
3517         type = UMLListViewItem::lvt_UseCase_Diagram;
3518         break;
3519 
3520     case Uml::DiagramType::Sequence:
3521         type = UMLListViewItem::lvt_Sequence_Diagram;
3522         break;
3523 
3524     case Uml::DiagramType::Collaboration:
3525         type = UMLListViewItem::lvt_Collaboration_Diagram;
3526         break;
3527 
3528     case Uml::DiagramType::State:
3529         type = UMLListViewItem::lvt_State_Diagram;
3530         break;
3531 
3532     case Uml::DiagramType::Activity:
3533         type = UMLListViewItem::lvt_Activity_Diagram;
3534         break;
3535 
3536     case Uml::DiagramType::Component:
3537         type = UMLListViewItem::lvt_Component_Diagram;
3538         break;
3539 
3540     case Uml::DiagramType::Deployment:
3541         type = UMLListViewItem::lvt_Deployment_Diagram;
3542         break;
3543 
3544     case Uml::DiagramType::EntityRelationship:
3545         type = UMLListViewItem::lvt_EntityRelationship_Diagram;
3546         break;
3547 
3548     default:
3549         uWarning() << "unknown diagram type " << view->umlScene()->type();
3550         return;
3551     }//end switch
3552 
3553     UMLListViewItem item((UMLListView *)0, QString(), type);
3554     UMLListViewPopupMenu popup(UMLApp::app()->mainViewWidget(), &item);
3555     QAction *triggered = popup.exec(point);
3556     view->umlScene()->slotMenuSelection(triggered);
3557 }
3558 
3559 /**
3560  * Function for comparing tags in XMI files.
3561  */
3562 bool UMLDoc::tagEq (const QString& inTag, const QString& inPattern)
3563 {
3564     QString tag = inTag;
3565     QString pattern = inPattern;
3566     tag.remove(QRegExp(QLatin1String("^\\w+:")));  // remove leading "UML:" or other
3567     int patSections = pattern.count(QLatin1Char('.')) + 1;
3568     QString tagEnd = tag.section(QLatin1Char('.'), -patSections);
3569     return (tagEnd.toLower() == pattern.toLower());
3570 }
3571 
3572