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