umbrello API Documentation

umldoc.cpp

00001 /***************************************************************************
00002  *                                                                         *
00003  *   This program is free software; you can redistribute it and/or modify  *
00004  *   it under the terms of the GNU General Public License as published by  *
00005  *   the Free Software Foundation; either version 2 of the License, or     *
00006  *   (at your option) any later version.                                   *
00007  *                                                                         *
00008  *  copyright (C) 2002-2007                                                *
00009  *  Umbrello UML Modeller Authors <uml-devel@uml.sf.net>                   *
00010  ***************************************************************************/
00011 
00012 // own header
00013 #include "umldoc.h"
00014 
00015 // qt includes
00016 #include <qpainter.h>
00017 #include <qtimer.h>
00018 #include <qdatetime.h>
00019 #include <qbuffer.h>
00020 #include <qdir.h>
00021 #include <qregexp.h>
00022 #include <qlabel.h>
00023 
00024 // kde includes
00025 #include <kapplication.h>
00026 #include <kdeversion.h>
00027 #include <kdebug.h>
00028 #include <kio/job.h>
00029 #include <kio/netaccess.h>
00030 #include <klocale.h>
00031 #include <kmessagebox.h>
00032 #include <kmimetype.h>
00033 #include <kprinter.h>
00034 #include <ktar.h>
00035 #include <ktempdir.h>
00036 #include <ktempfile.h>
00037 #include <kiconloader.h>
00038 #include <kinputdialog.h>
00039 #include <ktabwidget.h>
00040 
00041 // app includes
00042 #include "uniqueid.h"
00043 #include "associationwidget.h"
00044 #include "association.h"
00045 #include "package.h"
00046 #include "folder.h"
00047 #include "codegenerator.h"
00048 #include "classifier.h"
00049 #include "enum.h"
00050 #include "entity.h"
00051 #include "docwindow.h"
00052 #include "operation.h"
00053 #include "attribute.h"
00054 #include "template.h"
00055 #include "enumliteral.h"
00056 #include "entityattribute.h"
00057 #include "stereotype.h"
00058 #include "classifierlistitem.h"
00059 #include "object_factory.h"
00060 #include "import_rose.h"
00061 #include "model_utils.h"
00062 #include "widget_utils.h"
00063 #include "uml.h"
00064 #include "umllistview.h"
00065 #include "umllistviewitem.h"
00066 #include "umlview.h"
00067 #include "clipboard/idchangelog.h"
00068 #include "dialogs/classpropdlg.h"
00069 #include "codegenerators/codegenfactory.h"
00070 #include "listpopupmenu.h"
00071 #include "version.h"
00072 
00073 #define XMI_FILE_VERSION UMBRELLO_VERSION
00074 // For the moment, the XMI_FILE_VERSION changes with each UMBRELLO_VERSION.
00075 // But someday that may stabilize ;)
00076 
00077 using namespace Uml;
00078 
00079 static const uint undoMax = 30;
00080 
00081 UMLDoc::UMLDoc() {
00082     m_Name = i18n("UML Model");
00083     m_modelID = "m1";
00084     m_count = 0;
00085     m_pChangeLog = 0;
00086     m_Doc = "";
00087     m_modified = false;
00088     m_bLoading = false;
00089     m_bTypesAreResolved = false;
00090     m_pAutoSaveTimer = 0;
00091     m_nViewID = Uml::id_None;
00092     m_pTabPopupMenu = 0;
00093     m_pCurrentRoot = NULL;
00094 }
00095 
00096 void UMLDoc::init() {
00097     // Initialize predefined folders.
00098     const QString nativeRootName[Uml::N_MODELTYPES] = {
00099         "Logical View",
00100         "Use Case View",
00101         "Component View",
00102         "Deployment View",
00103         "Entity Relationship Model"
00104     };
00105     const QString localizedRootName[Uml::N_MODELTYPES] = {
00106         i18n("Logical View"),
00107         i18n("Use Case View"),
00108         i18n("Component View"),
00109         i18n("Deployment View"),
00110         i18n("Entity Relationship Model")
00111     };
00112     for (int i = 0; i < Uml::N_MODELTYPES; i++) {
00113         m_root[i] = new UMLFolder(nativeRootName[i], STR2ID(nativeRootName[i]));
00114         m_root[i]->setLocalName(localizedRootName[i]);
00115     }
00116     m_datatypeRoot = new UMLFolder("Datatypes", "Datatypes");
00117     m_datatypeRoot->setLocalName(i18n("Datatypes"));
00118     m_datatypeRoot->setUMLPackage(m_root[Uml::mt_Logical]);
00119     m_root[Uml::mt_Logical]->addObject(m_datatypeRoot);
00120 
00121     // Connect signals.
00122     UMLApp * pApp = UMLApp::app();
00123     connect(this, SIGNAL(sigDiagramCreated(Uml::IDType)), pApp, SLOT(slotUpdateViews()));
00124     connect(this, SIGNAL(sigDiagramRemoved(Uml::IDType)), pApp, SLOT(slotUpdateViews()));
00125     connect(this, SIGNAL(sigDiagramRenamed(Uml::IDType)), pApp, SLOT(slotUpdateViews()));
00126     connect(this, SIGNAL( sigCurrentViewChanged() ), pApp, SLOT( slotCurrentViewChanged() ) );
00127 }
00128 
00129 UMLDoc::~UMLDoc() {
00130     delete m_pChangeLog;
00131     m_pChangeLog = 0;
00132 }
00133 
00134 void UMLDoc::addView(UMLView *view) {
00135     if (view == NULL) {
00136         kError() << "UMLDoc::addView: argument is NULL" << endl;
00137         return;
00138     }
00139     UMLFolder *f = view->getFolder();
00140     if (f == NULL) {
00141         kError() << "UMLDoc::addView: view folder is not set" << endl;
00142         return;
00143     }
00144     f->addView(view);
00145 
00146     UMLApp * pApp = UMLApp::app();
00147     if ( pApp->getListView() )
00148         connect(this, SIGNAL(sigObjectRemoved(UMLObject *)), view, SLOT(slotObjectRemoved(UMLObject *)));
00149 
00150     pApp->setCurrentView(view);
00151     if ( ! m_bLoading ) {
00152         view -> show();
00153         emit sigDiagramChanged(view ->getType());
00154     }
00155 
00156     Settings::OptionState optionState = Settings::getOptionState();
00157     KTabWidget* tabWidget = NULL;
00158     if (optionState.generalState.tabdiagrams) {
00159         tabWidget = UMLApp::app()->tabWidget();
00160         tabWidget->addTab(view, view->getName());
00161         tabWidget->setTabIconSet(view, Widget_Utils::iconSet(view->getType()));
00162     }
00163     pApp->setDiagramMenuItemsState(true);
00164     pApp->slotUpdateViews();
00165     pApp->setCurrentView(view);
00166     if (tabWidget) {
00167         tabWidget->showPage(view);
00168         tabWidget->setCurrentPage(tabWidget->currentPageIndex());
00169     }
00170 }
00171 
00172 void UMLDoc::removeView(UMLView *view , bool enforceCurrentView ) {
00173     if(!view)
00174     {
00175         kError()<<"UMLDoc::removeView(UMLView *view) called with view = 0"<<endl;
00176         return;
00177     }
00178     if ( UMLApp::app()->getListView() ) {
00179         disconnect(this,SIGNAL(sigObjectRemoved(UMLObject *)), view,SLOT(slotObjectRemoved(UMLObject *)));
00180     }
00181     view->hide();
00182     //remove all widgets before deleting view
00183     view->removeAllWidgets();
00184     UMLFolder *f = view->getFolder();
00185     if (f == NULL) {
00186         kError() << "UMLDoc::removeView(" << view->getName()
00187             << "): view->getFolder() returns NULL" << endl;
00188         return;
00189     }
00190     f->removeView(view);
00191     UMLView *currentView = UMLApp::app()->getCurrentView();
00192     if (currentView == view)
00193     {
00194         UMLApp::app()->setCurrentView(NULL);
00195         UMLViewList viewList;
00196         m_root[mt_Logical]->appendViews(viewList);
00197         UMLView* firstView = viewList.first();
00198         if (!firstView && enforceCurrentView) //create a diagram
00199         {
00200             createDiagram(m_root[mt_Logical], dt_Class, false);
00201             kapp->processEvents();
00202             m_root[mt_Logical]->appendViews(viewList);
00203             firstView = viewList.first();
00204         }
00205 
00206         if ( firstView )
00207         {
00208             changeCurrentView( firstView->getID() );
00209             UMLApp::app()->setDiagramMenuItemsState(true);
00210         }
00211     }
00212 }
00213 
00214 void UMLDoc::setURL(const KURL &url) {
00215     m_doc_url = url;
00216     return;
00217 }
00218 
00219 const KURL& UMLDoc::URL() const {
00220     return m_doc_url;
00221 }
00222 
00223 bool UMLDoc::saveModified() {
00224     bool completed(true);
00225     if (!m_modified)
00226         return completed;
00227 
00228     UMLApp *win = UMLApp::app();
00229     int want_save = KMessageBox::warningYesNoCancel(win, i18n("The current file has been modified.\nDo you want to save it?"), i18n("Warning"),KStdGuiItem::save(),KStdGuiItem::discard());
00230     switch(want_save) {
00231     case KMessageBox::Yes:
00232         if (m_doc_url.fileName() == i18n("Untitled")) {
00233             if (win->slotFileSaveAs()) {
00234                 closeDocument();
00235                 completed=true;
00236             } else {
00237                 completed=false;
00238             }
00239         } else {
00240             saveDocument(URL());
00241             closeDocument();
00242             completed=true;
00243         }
00244         break;
00245 
00246     case KMessageBox::No:
00247         setModified(false);
00248         closeDocument();
00249         completed=true;
00250         break;
00251 
00252     case KMessageBox::Cancel:
00253         completed=false;
00254         break;
00255 
00256     default:
00257         completed=false;
00258         break;
00259     }
00260     return completed;
00261 }
00262 
00263 void UMLDoc::closeDocument() {
00264     UMLApp::app()->setGenerator(Uml::pl_Reserved);  // delete the codegen
00265     m_Doc = "";
00266     DocWindow* dw = UMLApp::app()->getDocWindow();
00267     if (dw) {
00268         dw->newDocumentation();
00269     }
00270 
00271     UMLListView *listView = UMLApp::app()->getListView();
00272     if (listView) {
00273         listView->init();
00274         // store old setting - for restore of last setting
00275         bool m_bLoading_old = m_bLoading;
00276         m_bLoading = true; // This is to prevent document becoming modified.
00277         // For reference, here is an example of a call sequence that would
00278         // otherwise result in futile addToUndoStack() calls:
00279         //  removeAllViews()  =>
00280         //   UMLView::removeAllAssociations()  =>
00281         //    UMLView::removeAssoc()  =>
00282         //     UMLDoc::setModified(true, true)  =>
00283         //      addToUndoStack().
00284         removeAllViews();
00285         m_bLoading = m_bLoading_old;
00286         // Remove all objects from the predefined folders.
00287         // @fixme With advanced code generation enabled, this crashes.
00288         UMLObject *obj;
00289         for (int i = 0; i < Uml::N_MODELTYPES; i++)
00290             m_root[i]->removeAllObjects();
00291         // Restore the datatype folder, it has been deleted above.
00292         m_datatypeRoot = new UMLFolder("Datatypes", "Datatypes");
00293         m_datatypeRoot->setLocalName(i18n("Datatypes"));
00294         m_datatypeRoot->setUMLPackage(m_root[Uml::mt_Logical]);
00295         m_root[Uml::mt_Logical]->addObject(m_datatypeRoot);
00296         listView->theDatatypeFolder()->setUMLObject(m_datatypeRoot);
00297         /* Remove any stereotypes.
00298         if (m_stereoList.count() > 0) {
00299             UMLStereotype *s;
00300             for (UMLStereotypeListIt sit(m_stereoList); (s = sit.current()) != 0; ++sit)
00301                 delete s;
00302             m_stereoList.clear();
00303         }
00304          */
00305     }
00306     m_bTypesAreResolved = false;
00307 }
00308 
00309 bool UMLDoc::newDocument() {
00310     closeDocument();
00311     UMLApp::app()->setCurrentView(NULL);
00312     m_doc_url.setFileName(i18n("Untitled"));
00313     //see if we need to start with a new diagram
00314     Settings::OptionState optionState = Settings::getOptionState();
00315     Uml::Diagram_Type dt = optionState.generalState.diagram;
00316     Uml::Model_Type mt = Model_Utils::convert_DT_MT(dt);
00317     if (mt == Uml::N_MODELTYPES) {  // don't allow no diagram
00318         dt = Uml::dt_Class;
00319         mt = Uml::mt_Logical;
00320     }
00321     createDiagram(m_root[mt], dt, false);
00322 
00323     UMLApp::app()->initGenerator();
00324     addDefaultDatatypes();
00325     addDefaultStereotypes();
00326 
00327     setModified(false);
00328     initSaveTimer();
00329 
00330     UMLApp::app()->enableUndo(false);
00331     clearUndoStack();
00332     addToUndoStack();
00333 
00334     return true;
00335 }
00336 
00337 bool UMLDoc::openDocument(const KURL& url, const char* /*format =0*/) {
00338     if(url.fileName().length() == 0) {
00339         newDocument();
00340         return false;
00341     }
00342 
00343     m_doc_url = url;
00344     QDir d = url.path(1);
00345     closeDocument();
00346     // IMPORTANT: set m_bLoading to true
00347     // _AFTER_ the call of UMLDoc::closeDocument()
00348     // as it sets m_bLoading to false afer it was temporarily
00349     // changed to true to block recording of changes in redo-buffer
00350     m_bLoading = true;
00351     QString tmpfile;
00352     KIO::NetAccess::download( url, tmpfile, UMLApp::app() );
00353     QFile file( tmpfile );
00354     if ( !file.exists() ) {
00355         KMessageBox::error(0, i18n("The file %1 does not exist.").arg(d.path()), i18n("Load Error"));
00356         m_doc_url.setFileName(i18n("Untitled"));
00357         m_bLoading = false;
00358         newDocument();
00359         return false;
00360     }
00361 
00362     // status of XMI loading
00363     bool status = false;
00364 
00365     // check if the xmi file is a compressed archive like tar.bzip2 or tar.gz
00366     QString filetype = m_doc_url.fileName(true);
00367     QString mimetype = "";
00368     if (filetype.find(QRegExp("\\.tgz$")) != -1)
00369     {
00370         mimetype = "application/x-gzip";
00371     } else if (filetype.find(QRegExp("\\.tar.bz2$")) != -1) {
00372         mimetype = "application/x-bzip2";
00373     }
00374 
00375     if (mimetype.isEmpty() == false)
00376     {
00377         KTar archive(tmpfile, mimetype);
00378         if (archive.open(IO_ReadOnly) == false)
00379         {
00380             KMessageBox::error(0, i18n("The file %1 seems to be corrupted.").arg(d.path()), i18n("Load Error"));
00381             m_doc_url.setFileName(i18n("Untitled"));
00382             m_bLoading = false;
00383             newDocument();
00384             return false;
00385         }
00386 
00387         // get the root directory and all entries in
00388         const KArchiveDirectory * rootDir = archive.directory();
00389         QStringList entries = rootDir->entries();
00390         QString entryMimeType;
00391         bool foundXMI = false;
00392         QStringList::Iterator it;
00393         QStringList::Iterator end(entries.end());
00394 
00395         // now go through all entries till we find an xmi file
00396         for (it = entries.begin(); it != end; ++it)
00397         {
00398             // only check files, we do not go in subdirectories
00399             if (rootDir->entry(*it)->isFile() == true)
00400             {
00401                 // we found a file, check the mimetype
00402                 entryMimeType = KMimeType::findByPath(*it, 0, true)->name();
00403                 if (entryMimeType == "application/x-uml")
00404                 {
00405                     foundXMI = true;
00406                     break;
00407                 }
00408             }
00409         }
00410 
00411         // if we found an XMI file, we have to extract it to a temporary file
00412         if (foundXMI == true)
00413         {
00414             KTempDir tmp_dir;
00415             KArchiveEntry * entry;
00416             KArchiveFile * fileEntry;
00417 
00418             // try to cast the file entry in the archive to an archive entry
00419             entry = const_cast<KArchiveEntry*>(rootDir->entry(*it));
00420             if (entry == 0)
00421             {
00422                 KMessageBox::error(0, i18n("There was no XMI file found in the compressed file %1.").arg(d.path()), i18n("Load Error"));
00423                 m_doc_url.setFileName(i18n("Untitled"));
00424                 m_bLoading = false;
00425                 newDocument();
00426                 return false;
00427             }
00428 
00429             // now try to cast the archive entry to a file entry, so that we can
00430             // extract the file
00431             fileEntry = dynamic_cast<KArchiveFile*>(entry);
00432             if (fileEntry == 0)
00433             {
00434                 KMessageBox::error(0, i18n("There was no XMI file found in the compressed file %1.").arg(d.path()), i18n("Load Error"));
00435                 m_doc_url.setFileName(i18n("Untitled"));
00436                 m_bLoading = false;
00437                 newDocument();
00438                 return false;
00439             }
00440 
00441             // now we can extract the file to the temporary directory
00442             fileEntry->copyTo(tmp_dir.name());
00443 
00444             // now open the extracted file for reading
00445             QFile xmi_file(tmp_dir.name() + *it);
00446             if( !xmi_file.open( IO_ReadOnly ) )
00447             {
00448                 KMessageBox::error(0, i18n("There was a problem loading the extracted file: %1").arg(d.path()), i18n("Load Error"));
00449                 m_doc_url.setFileName(i18n("Untitled"));
00450                 m_bLoading = false;
00451                 newDocument();
00452                 return false;
00453             }
00454             status = loadFromXMI( xmi_file, ENC_UNKNOWN );
00455 
00456             // close the extracted file and the temporary directory
00457             xmi_file.close();
00458             tmp_dir.unlink();
00459 
00460         } else {
00461             KMessageBox::error(0, i18n("There was no XMI file found in the compressed file %1.").arg(d.path()), i18n("Load Error"));
00462             m_doc_url.setFileName(i18n("Untitled"));
00463             m_bLoading = false;
00464             newDocument();
00465             return false;
00466         }
00467 
00468         archive.close();
00469     } else {
00470         // no, it seems to be an ordinary file
00471         if( !file.open( IO_ReadOnly ) ) {
00472             KMessageBox::error(0, i18n("There was a problem loading file: %1").arg(d.path()), i18n("Load Error"));
00473             m_doc_url.setFileName(i18n("Untitled"));
00474             m_bLoading = false;
00475             newDocument();
00476             return false;
00477         }
00478         if (filetype.endsWith(".mdl"))
00479             status = Import_Rose::loadFromMDL(file);
00480         else
00481             status = loadFromXMI( file, ENC_UNKNOWN );
00482     }
00483 
00484     file.close();
00485     KIO::NetAccess::removeTempFile( tmpfile );
00486     if( !status )
00487     {
00488         KMessageBox::error(0, i18n("There was a problem loading file: %1").arg(d.path()), i18n("Load Error"));
00489         m_bLoading = false;
00490         newDocument();
00491         return false;
00492     }
00493     setModified(false);
00494     m_bLoading = false;
00495     initSaveTimer();
00496 
00497     UMLApp::app()->enableUndo(false);
00498     clearUndoStack();
00499     addToUndoStack();
00500     // for compatibility
00501     addDefaultStereotypes();
00502 
00503     return true;
00504 }
00505 
00506 bool UMLDoc::saveDocument(const KURL& url, const char * /* format */) {
00507     m_doc_url = url;
00508     QDir d = m_doc_url.path(1);
00509     QFile file;
00510     bool uploaded = true;
00511 
00512     // first, we have to find out which format to use
00513     QString strFileName = url.path(-1);
00514     QFileInfo fileInfo(strFileName);
00515     QString fileExt = fileInfo.extension();
00516     QString fileFormat = "xmi";
00517     if (fileExt == "xmi" || fileExt == "bak.xmi")
00518     {
00519         fileFormat = "xmi";
00520     } else if (fileExt == "xmi.tgz" || fileExt == "bak.xmi.tgz") {
00521         fileFormat = "tgz";
00522     } else if (fileExt == "xmi.tar.bz2" || fileExt == "bak.xmi.tar.bz2") {
00523         fileFormat = "bz2";
00524     } else {
00525         fileFormat = "xmi";
00526     }
00527 
00528     initSaveTimer();
00529 
00530     if (fileFormat == "tgz" || fileFormat == "bz2")
00531     {
00532         KTar * archive;
00533         KTempFile tmp_tgz_file;
00534 
00535         // first we have to check if we are saving to a local or remote file
00536         if (url.isLocalFile())
00537         {
00538             if (fileFormat == "tgz") // check tgz or bzip2
00539             {
00540                 archive = new KTar(d.path(), "application/x-gzip");
00541             } else {
00542                 archive = new KTar(d.path(), "application/x-bzip2");
00543             }
00544         } else {
00545             if (fileFormat == "tgz") // check tgz or bzip2
00546             {
00547                 archive = new KTar(tmp_tgz_file.name(), "application/x-gzip");
00548             } else {
00549                 archive = new KTar(tmp_tgz_file.name(), "application/x-bzip2");
00550             }
00551         }
00552 
00553         // now check if we can write to the file
00554         if (archive->open(IO_WriteOnly) == false)
00555         {
00556             KMessageBox::error(0, i18n("There was a problem saving file: %1").arg(d.path()), i18n("Save Error"));
00557             return false;
00558         }
00559 
00560         // we have to create a temporary xmi file
00561         // we will add this file later to the archive
00562         KTempFile tmp_xmi_file;
00563         file.setName(tmp_xmi_file.name());
00564         if( !file.open( IO_WriteOnly ) ) {
00565             KMessageBox::error(0, i18n("There was a problem saving file: %1").arg(d.path()), i18n("Save Error"));
00566             return false;
00567         }
00568         saveToXMI(file); // save XMI to this file...
00569         file.close(); // ...and close it
00570 
00571         // now add this file to the archive, but without the extension
00572         QString tmpQString = url.fileName();
00573         if (fileFormat == "tgz")
00574         {
00575             tmpQString.replace(QRegExp("\\.tgz$"), "");
00576         } else {
00577             tmpQString.replace(QRegExp("\\.tar\\.bz2$"), "");
00578         }
00579         archive->addLocalFile(tmp_xmi_file.name(), tmpQString);
00580         archive->close();
00581 
00582 #if KDE_IS_VERSION(3,4,89)
00583         if (!archive->closeSucceeded())
00584         {
00585             KMessageBox::error(0, i18n("There was a problem saving file: %1").arg(d.path()), i18n("Save Error"));
00586             return false;
00587         }
00588 #endif
00589         // now the xmi file was added to the archive, so we can delete it
00590         tmp_xmi_file.close();
00591         tmp_xmi_file.unlink();
00592 
00593         // now we have to check, if we have to upload the file
00594         if ( !url.isLocalFile() ) {
00595             uploaded = KIO::NetAccess::upload( tmp_tgz_file.name(), m_doc_url,
00596                                                UMLApp::app() );
00597         }
00598 
00599         // now the archive was written to disk (or remote) so we can delete the
00600         // objects
00601         tmp_tgz_file.close();
00602         tmp_tgz_file.unlink();
00603         delete archive;
00604 
00605     } else {
00606         // save as normal uncompressed XMI
00607 
00608         KTempFile tmpfile; // we need this tmp file if we are writing to a remote file
00609 
00610         // save in _any_ case to a temp file
00611         // -> if something goes wrong during saveToXmi, the
00612         //     original content is preserved
00613         //     ( e.g. if umbrello dies in the middle of the document model parsing
00614         //      for saveToXMI due to some problems )
00616         file.setName( tmpfile.name() );
00617 
00618         // lets open the file for writing
00619         if( !file.open( IO_WriteOnly ) ) {
00620             KMessageBox::error(0, i18n("There was a problem saving file: %1").arg(d.path()), i18n("Save Error"));
00621             return false;
00622         }
00623         saveToXMI(file); // save the xmi stuff to it
00624         file.close();
00625         tmpfile.close();
00626 
00627         // if it is a remote file, we have to upload the tmp file
00628         if ( !url.isLocalFile() ) {
00629             uploaded = KIO::NetAccess::upload( tmpfile.name(), m_doc_url, UMLApp::app() );
00630         } else {
00631             // now remove the original file
00632             if ( KIO::NetAccess::file_move( tmpfile.name(), d.path(), -1, true ) == false ) {
00633                 KMessageBox::error(0, i18n("There was a problem saving file: %1").arg(d.path()), i18n("Save Error"));
00634                 m_doc_url.setFileName(i18n("Untitled"));
00635                 return false;
00636             }
00637         }
00638     }
00639     if( !uploaded )
00640     {
00641         KMessageBox::error(0, i18n("There was a problem uploading file: %1").arg(d.path()), i18n("Save Error"));
00642         m_doc_url.setFileName(i18n("Untitled"));
00643     }
00644     setModified(false);
00645     return uploaded;
00646 }
00647 
00648 void UMLDoc::setupSignals() {
00649     WorkToolBar *tb = UMLApp::app() -> getWorkToolBar();
00650 
00651 
00652     connect(this, SIGNAL(sigDiagramChanged(Uml::Diagram_Type)), tb, SLOT(slotCheckToolBar(Uml::Diagram_Type)));
00653     //new signals below
00654 
00655     return;
00656 }
00657 
00658 UMLView * UMLDoc::findView(Uml::IDType id) {
00659     UMLView *v = NULL;
00660     for (int i = 0; i < Uml::N_MODELTYPES; i++) {
00661         v = m_root[i]->findView(id);
00662         if (v)
00663             break;
00664     }
00665     return v;
00666 }
00667 
00668 UMLView * UMLDoc::findView(Diagram_Type type, const QString &name,
00669                            bool searchAllScopes /* =false */) {
00670     Uml::Model_Type mt = Model_Utils::convert_DT_MT(type);
00671     return m_root[mt]->findView(type, name, searchAllScopes);
00672 }
00673 
00674 UMLObject* UMLDoc::findObjectById(Uml::IDType id) {
00675     UMLObject *o = NULL;
00676     for (int i = 0; i < Uml::N_MODELTYPES; i++) {
00677         if (id == m_root[i]->getID())
00678             return m_root[i];
00679         o = m_root[i]->findObjectById(id);
00680         if (o)
00681             return o;
00682     }
00683     o = findStereotypeById(id);
00684     return o;
00685 }
00686 
00687 UMLStereotype * UMLDoc::findStereotypeById(Uml::IDType id) {
00688     for (UMLStereotype *s = m_stereoList.first(); s; s = m_stereoList.next() ) {
00689         if (s->getID() == id)
00690             return s;
00691     }
00692     return NULL;
00693 }
00694 
00695 UMLObject* UMLDoc::findUMLObject(const QString &name,
00696                                  Object_Type type /* = ot_UMLObject */,
00697                                  UMLObject *currentObj /* = NULL */) {
00698     UMLObject *o = m_datatypeRoot->findObject(name);
00699     if (o)
00700         return o;
00701     for (int i = 0; i < Uml::N_MODELTYPES; i++) {
00702         UMLObjectList list = m_root[i]->containedObjects();
00703         o = Model_Utils::findUMLObject(list, name, type, currentObj);
00704         if (o)
00705             return o;
00706         if ((type == ot_UMLObject || type == ot_Folder) &&
00707              name == m_root[i]->getName())
00708             return m_root[i];
00709     }
00710     return NULL;
00711 }
00712 
00713 UMLClassifier* UMLDoc::findUMLClassifier(const QString &name) {
00714     //this is used only by code generator so we don't need to look at Datatypes
00715     UMLObject * obj = findUMLObject(name);
00716     return dynamic_cast<UMLClassifier*>(obj);
00717 }
00718 
00724 bool UMLDoc::addUMLObject(UMLObject* object) {
00725     Object_Type ot = object->getBaseType();
00726     if (ot == ot_Attribute || ot == ot_Operation || ot == ot_EnumLiteral
00727             || ot == ot_EntityAttribute || ot == ot_Template || ot == ot_Stereotype) {
00728         kDebug() << "UMLDoc::addUMLObject(" << object->getName()
00729             << "): not adding type " << ot << endl;
00730         return false;
00731     }
00732     UMLPackage *pkg = object->getUMLPackage();
00733     if (pkg == NULL) {
00734         pkg = currentRoot();
00735         kDebug() << "UMLDoc::addUMLObject(" << object->getName()
00736             << "): no parent package set, assuming " << pkg->getName() << endl;
00737         object->setUMLPackage( pkg );
00738     }
00739     return pkg->addObject(object);
00740 }
00741 
00742 void UMLDoc::addStereotype(const UMLStereotype *s) {
00743     if (! m_stereoList.contains(s))
00744         m_stereoList.append(s);
00745 }
00746 
00747 void UMLDoc::removeStereotype(const UMLStereotype *s) {
00748     if (m_stereoList.contains(s))
00749         m_stereoList.remove(s);
00750 }
00751 
00752 void UMLDoc::writeToStatusBar(const QString &text) {
00753     emit sigWriteToStatusBar(text);
00754 }
00755 
00756 // simple removal of an object
00757 void UMLDoc::slotRemoveUMLObject(UMLObject* object)  {
00758     //m_objectList.remove(object);
00759     UMLPackage *pkg = object->getUMLPackage();
00760     if (pkg == NULL) {
00761         kError() << "UMLDoc::slotRemoveUMLObject(" << object->getName()
00762             << "): parent package is not set !" << endl;
00763         return;
00764     }
00765     pkg->removeObject(object);
00766 }
00767 
00768 bool UMLDoc::isUnique(const QString &name)
00769 {
00770     UMLListView *listView = UMLApp::app()->getListView();
00771     UMLListViewItem *currentItem = (UMLListViewItem*)listView->currentItem();
00772     UMLListViewItem *parentItem = 0;
00773 
00774     // check for current item, if its a package, then we do a check on that
00775     // otherwise, if current item exists, find its parent and check if thats
00776     // a package..
00777     if(currentItem)
00778     {
00779         // its possible that the current item *is* a package, then just
00780         // do check now
00781         if (Model_Utils::typeIsContainer(currentItem->getType()))
00782             return isUnique (name, (UMLPackage*) currentItem->getUMLObject());
00783         parentItem = (UMLListViewItem*)currentItem->parent();
00784     }
00785 
00786     // item is in a package so do check only in that
00787     if (parentItem != NULL && Model_Utils::typeIsContainer(parentItem->getType())) {
00788         UMLPackage *parentPkg = static_cast<UMLPackage*>(parentItem->getUMLObject());
00789         return isUnique(name, parentPkg);
00790     }
00791 
00792     kError() << "UMLDoc::isUnique(" << name << "): Not currently in a package"
00793         << endl;
00794     /* Check against all objects that _don't_ have a parent package.
00795     for (UMLObjectListIt oit(m_objectList); oit.current(); ++oit) {
00796         UMLObject *obj = oit.current();
00797         if (obj->getUMLPackage() == NULL && obj->getName() == name)
00798             return false;
00799     }
00800      */
00801     return true;
00802 }
00803 
00804 bool UMLDoc::isUnique(const QString &name, UMLPackage *package)
00805 {
00806     // if a package, then only do check in that
00807     if (package)
00808         return (package->findObject(name) == NULL);
00809 
00810     // Not currently in a package: ERROR
00811     kError() << "UMLDoc::isUnique(2)(" << name << "): Not currently in a package"
00812         << endl;
00813     /* Check against all objects that _don't_ have a parent package.
00814     for (UMLObjectListIt oit(m_objectList); oit.current(); ++oit) {
00815         UMLObject *obj = oit.current();
00816         if (obj->getUMLPackage() == NULL && obj->getName() == name)
00817             return false;
00818     }
00819      */
00820     return true;
00821 }
00822 
00823 UMLStereotype* UMLDoc::findStereotype(const QString &name) {
00824     UMLStereotype *s;
00825     for (UMLStereotypeListIt it(m_stereoList); (s = it.current()) != NULL; ++it) {
00826         if (s->getName() == name)
00827             return s;
00828     }
00829     return NULL;
00830 }
00831 
00832 UMLStereotype* UMLDoc::findOrCreateStereotype(const QString &name) {
00833     UMLStereotype *s = findStereotype(name);
00834     if (s != NULL) {
00835         return s;
00836     }
00837     s = new UMLStereotype(name, STR2ID(name));
00838     addStereotype(s);
00839     //emit modified();
00840     return s;
00841 }
00842 
00843 void UMLDoc::removeAssociation (UMLAssociation * assoc, bool doSetModified /*=true*/) {
00844     if(!assoc)
00845         return;
00846 
00847     // Remove the UMLAssociation from m_objectList.
00848     UMLPackage *pkg = assoc->getUMLPackage();
00849     if (pkg == NULL) {
00850         kError() << "UMLDoc::removeAssociation(" << assoc->getName()
00851             << "): parent package is not set !" << endl;
00852         return;
00853     }
00854     pkg->removeObject(assoc);
00855 
00856     if (doSetModified)  // so we will save our document
00857         setModified(true, false);
00858 }
00859 
00860 UMLAssociation * UMLDoc::findAssociation(Uml::Association_Type assocType,
00861         const UMLObject *roleAObj,
00862         const UMLObject *roleBObj,
00863         bool *swap)
00864 {
00865     UMLAssociationList assocs = getAssociations();
00866     UMLAssociation *a, *ret = NULL;
00867     for (a = assocs.first(); a; a = assocs.next()) {
00868         if (a->getAssocType() != assocType)
00869             continue;
00870         if (a->getObject(Uml::A) == roleAObj && a->getObject(Uml::B) == roleBObj)
00871             return a;
00872         if (a->getObject(Uml::A) == roleBObj && a->getObject(Uml::B) == roleAObj) {
00873             ret = a;
00874         }
00875     }
00876     if (swap)
00877         *swap = (ret != NULL);
00878     return ret;
00879 }
00880 
00881 // create AND add an association. Used by refactoring assistant.
00882 UMLAssociation* UMLDoc::createUMLAssociation(UMLObject *a, UMLObject *b, Uml::Association_Type type)
00883 {
00884     bool swap;
00885     UMLAssociation *assoc = findAssociation(type, a, b, &swap);
00886     if (assoc == NULL) {
00887         assoc = new UMLAssociation(type, a, b );
00888         addAssociation(assoc);
00889     }
00890     return assoc;
00891 }
00892 
00893 void UMLDoc::addAssociation(UMLAssociation *Assoc)
00894 {
00895     if (Assoc == NULL)
00896         return;
00897 
00898     // First, check that this association has not already been added.
00899     // This may happen when loading old XMI files where all the association
00900     // information was taken from the <UML:AssocWidget> tag.
00901     UMLAssociationList assocs = getAssociations();
00902     for (UMLAssociationListIt ait(assocs); ait.current(); ++ait) {
00903         UMLAssociation *a = ait.current();
00904         // check if its already been added (shouldn't be the case right now
00905         // as UMLAssociations only belong to one associationwidget at a time)
00906         if (a == Assoc)
00907         {
00908             kDebug() << "UMLDoc::addAssociation: duplicate addition attempted"
00909                 << endl;
00910             return;
00911         }
00912     }
00913 
00914     // If we get here it's really a new association.
00915 
00916     // Add the UMLAssociation at the owning UMLPackage.
00917     UMLPackage *pkg = Assoc->getUMLPackage();
00918     if (pkg == NULL) {
00919         kError() << "UMLDoc::addAssociation(" << Assoc->getName()
00920             << "): parent package is not set !" << endl;
00921         return;
00922     }
00923     pkg->addObject(Assoc);
00924 
00925     // I don't believe this appropriate, UMLAssociations ARENT UMLWidgets -b.t.
00926     // emit sigObjectCreated(o);
00927 
00928     setModified(true);
00929 }
00930 
00931 QString UMLDoc::uniqViewName(const Diagram_Type type) {
00932     QString dname;
00933     if(type == dt_UseCase)
00934         dname = i18n("use case diagram");
00935     else if(type == dt_Class)
00936         dname = i18n("class diagram");
00937     else if(type == dt_Sequence)
00938         dname = i18n("sequence diagram");
00939     else if(type == dt_Collaboration)
00940         dname = i18n("collaboration diagram");
00941     else if( type == dt_State )
00942         dname = i18n( "state diagram" );
00943     else if( type == dt_Activity )
00944         dname = i18n( "activity diagram" );
00945     else if( type == dt_Component )
00946         dname = i18n( "component diagram" );
00947     else if( type == dt_Deployment )
00948         dname = i18n( "deployment diagram" );
00949     else if( type == dt_EntityRelationship )
00950         dname = i18n( "entity relationship diagram" );
00951     else {
00952         kWarning() << "uniqViewName() called with unknown diagram type" << endl;
00953     }
00954     QString name = dname;
00955     for (int number = 0; findView(type, name, true); ++number,
00956             name = dname + '_' + QString::number(number))
00957         ;
00958     return name;
00959 }
00960 
00961 bool UMLDoc::loading() const {
00962     return m_bLoading;
00963 }
00964 
00965 void UMLDoc::setLoading(bool state /* = true */) {
00966     m_bLoading = state;
00967 }
00968 
00969 void UMLDoc::createDiagram(UMLFolder *folder, Diagram_Type type, bool askForName /*= true */) {
00970     bool ok = true;
00971     QString name,
00972     dname = uniqViewName(type);
00973 
00974     while(true) {
00975         if (askForName)  {
00976             name = KInputDialog::getText(i18n("Name"), i18n("Enter name:"), dname, &ok, (QWidget*)UMLApp::app());
00977         } else {
00978             name = dname;
00979         }
00980         if (!ok)  {
00981             break;
00982         }
00983         if (name.length() == 0)  {
00984             KMessageBox::error(0, i18n("That is an invalid name for a diagram."), i18n("Invalid Name"));
00985         } else if(!findView(type, name)) {
00986             UMLView* temp = new UMLView(folder);
00987             temp -> setOptionState( Settings::getOptionState() );
00988             temp->setName( name );
00989             temp->setType( type );
00990             temp->setID( UniqueID::gen() );
00991             addView(temp);
00992             emit sigDiagramCreated( temp->getID() );
00993             setModified(true, false);
00994             UMLApp::app()->enablePrint(true);
00995             changeCurrentView( temp->getID() );
00996             break;
00997         } else {
00998             KMessageBox::error(0, i18n("A diagram is already using that name."), i18n("Not a Unique Name"));
00999         }
01000     }//end while
01001 }
01002 
01003 void UMLDoc::renameDiagram(Uml::IDType id) {
01004     bool ok = false;
01005 
01006     UMLView *temp =  findView(id);
01007     Diagram_Type type = temp->getType();
01008 
01009     QString oldName= temp->getName();
01010     while(true) {
01011         QString name = KInputDialog::getText(i18n("Name"), i18n("Enter name:"), oldName, &ok, (QWidget*)UMLApp::app());
01012 
01013         if(!ok)
01014             break;
01015         if(name.length() == 0)
01016             KMessageBox::error(0, i18n("That is an invalid name for a diagram."), i18n("Invalid Name"));
01017         else if(!findView(type, name)) {
01018             temp->setName(name);
01019 
01020             emit sigDiagramRenamed(id);
01021             setModified(true);
01022             break;
01023         } else
01024             KMessageBox::error(0, i18n("A diagram is already using that name."), i18n("Not a Unique Name"));
01025     }
01026 }
01027 
01028 void UMLDoc::renameUMLObject(UMLObject *o) {
01029     bool ok = false;
01030     QString oldName= o->getName();
01031     while(true) {
01032         QString name = KInputDialog::getText(i18n("Name"), i18n("Enter name:"), oldName, &ok, (QWidget*)UMLApp::app());
01033         if(!ok)
01034             break;
01035         if(name.length() == 0)
01036             KMessageBox::error(0, i18n("That is an invalid name."), i18n("Invalid Name"));
01037         else if (isUnique(name)) {
01038             o->setName(name);
01039             setModified(true);
01040             break;
01041         } else {
01042             KMessageBox::error(0, i18n("That name is already being used."), i18n("Not a Unique Name"));
01043         }
01044     }
01045     return;
01046 }
01047 
01048 void UMLDoc::renameChildUMLObject(UMLObject *o) {
01049     bool ok = false;
01050     UMLClassifier* p = dynamic_cast<UMLClassifier *>(o->parent());
01051     if(!p) {
01052         kDebug() << "Can't create object, no parent found" << endl;
01053         return;
01054     }
01055 
01056     QString oldName= o->getName();
01057     while(true) {
01058         QString name = KInputDialog::getText(i18n("Name"), i18n("Enter name:"), oldName, &ok, (QWidget*)UMLApp::app());
01059         if(!ok)
01060             break;
01061         if(name.length() == 0)
01062             KMessageBox::error(0, i18n("That is an invalid name."), i18n("Invalid Name"));
01063         else {
01064             if (p->findChildObject(name) == NULL
01065                     || ((o->getBaseType() == Uml::ot_Operation) && KMessageBox::warningYesNo( kapp -> mainWidget() ,
01066                             i18n( "The name you entered was not unique.\nIs this what you wanted?" ),
01067                             i18n( "Name Not Unique"),i18n("Use Name"),i18n("Enter New Name")) == KMessageBox::Yes) ) {
01068                 o->setName(name);
01069                 setModified(true);
01070                 break;
01071             } else {
01072                 KMessageBox::error(0, i18n("That name is already being used."), i18n("Not a Unique Name"));
01073             }
01074         }
01075     }
01076 }
01077 
01078 void UMLDoc::changeCurrentView(Uml::IDType id) {
01079     UMLApp* pApp = UMLApp::app();
01080     UMLView* w = findView(id);
01081     if (w) {
01082         pApp->setCurrentView(w);
01083         emit sigDiagramChanged(w->getType());
01084         pApp->setDiagramMenuItemsState( true );
01085         setModified(true);
01086     }
01087     emit sigCurrentViewChanged();
01088 }
01089 
01090 void UMLDoc::removeDiagram(Uml::IDType id) {
01091     UMLApp::app()->getDocWindow()->updateDocumentation(true);
01092     UMLView* umlview = findView(id);
01093     if(!umlview)
01094     {
01095         kError()<<"Request to remove diagram " << ID2STR(id) << ": Diagram not found!"<<endl;
01096         return;
01097     }
01098     if (KMessageBox::warningContinueCancel(0, i18n("Are you sure you want to delete diagram %1?").arg(umlview->getName()), i18n("Delete Diagram"),KGuiItem( i18n("&Delete"), "editdelete")) == KMessageBox::Continue) {
01099         removeView(umlview);
01100         emit sigDiagramRemoved(id);
01101         setModified(true);
01102         /* if (infoWidget->isVisible()) {
01103                emit sigDiagramChanged(dt_Undefined);
01104                UMLApp::app()->enablePrint(false);
01105            }
01106         */ //FIXME sort out all the KActions for when there's no diagram
01107         //also remove the buttons from the WorkToolBar, then get rid of infowidget
01108     }
01109 }
01110 
01111 UMLFolder *UMLDoc::currentRoot() {
01112     UMLView *currentView = UMLApp::app()->getCurrentView();
01113     if (currentView == NULL) {
01114         if (m_pCurrentRoot)
01115             return m_pCurrentRoot;
01116         kDebug() << "UMLDoc::currentRoot: currentView is NULL, assuming Logical View"
01117             << endl;
01118         return m_root[Uml::mt_Logical];
01119     }
01120     UMLFolder *f = currentView->getFolder();
01121     while (f->getUMLPackage()) {
01122         f = static_cast<UMLFolder*>(f->getUMLPackage());
01123     }
01124     return f;
01125 }
01126 
01127 void UMLDoc::setCurrentRoot(Uml::Model_Type rootType) {
01128     m_pCurrentRoot = m_root[rootType];
01129 }
01130 
01131 void UMLDoc::removeUMLObject(UMLObject* umlobject) {
01132     UMLApp::app()->getDocWindow()->updateDocumentation(true);
01133     Object_Type type = umlobject->getBaseType();
01134 
01135     umlobject->setUMLStereotype(NULL);  // triggers possible cleanup of UMLStereotype
01136     if (dynamic_cast<UMLClassifierListItem*>(umlobject))  {
01137         UMLClassifier* parent = dynamic_cast<UMLClassifier*>(umlobject->parent());
01138         if (parent == NULL) {
01139             kError() << "UMLDoc::removeUMLObject: parent of umlobject is NULL"
01140                 << endl;
01141             return;
01142         }
01143         if (type == ot_Operation) {
01144             parent->removeOperation(static_cast<UMLOperation*>(umlobject));
01145         } else if (type == ot_EnumLiteral) {
01146             UMLEnum *e = static_cast<UMLEnum*>(parent);
01147             e->removeEnumLiteral(static_cast<UMLEnumLiteral*>(umlobject));
01148         } else if (type == ot_EntityAttribute) {
01149             UMLEntity *ent = static_cast<UMLEntity*>(parent);
01150             ent->removeEntityAttribute(static_cast<UMLClassifierListItem*>(umlobject));
01151         } else {
01152             UMLClassifier* pClass = dynamic_cast<UMLClassifier*>(parent);
01153             if (pClass == NULL)  {
01154                 kError() << "UMLDoc::removeUMLObject: parent of umlobject has "
01155                     << "unexpected type " << parent->getBaseType() << endl;
01156                 return;
01157             }
01158             if (type == ot_Attribute) {
01159                 pClass->removeAttribute(static_cast<UMLAttribute*>(umlobject));
01160             } else if (type == ot_Template) {
01161                 pClass->removeTemplate(static_cast<UMLTemplate*>(umlobject));
01162             } else {
01163                 kError() << "UMLDoc::removeUMLObject: umlobject has "
01164                     << "unexpected type " << type << endl;
01165             }
01166         }
01167     } else {
01168         if (type == ot_Association) {
01169             UMLAssociation *a = (UMLAssociation *)umlobject;
01170             removeAssociation(a, false);  // don't call setModified here, it's done below
01171         } else {
01172             UMLPackage* pkg = umlobject->getUMLPackage();
01173             if (pkg) {
01174                 pkg->removeObject(umlobject);
01175             } else {
01176                 kError() << "UMLDoc::removeUMLObject(" << umlobject->getName()
01177                     << "): parent package is not set !" << endl;
01178             }
01179         }
01180         emit sigObjectRemoved(umlobject);
01181     }
01182     setModified(true);
01183 }
01184 
01185 void UMLDoc::signalUMLObjectCreated(UMLObject * o) {
01186     emit sigObjectCreated(o);
01187     /* This is the wrong place to do:
01188                setModified(true);
01189        Instead, that should be done by the callers when object creation and all
01190        its side effects (e.g. new widget in view, new list view item, etc.) is
01191        finalized.
01192      */
01193 }
01194 
01195 void UMLDoc::setName(const QString& name) {
01196     m_Name = name;
01197 }
01198 
01199 QString UMLDoc::getName() const {
01200     return m_Name;
01201 }
01202 
01203 Uml::IDType UMLDoc::getModelID() const {
01204     return m_modelID;
01205 }
01206 
01207 void UMLDoc::saveToXMI(QIODevice& file) {
01208     QDomDocument doc;
01209 
01210     QDomProcessingInstruction xmlHeading =
01211         doc.createProcessingInstruction("xml", "version=\"1.0\" encoding=\"UTF-8\"");
01212     doc.appendChild(xmlHeading);
01213 
01214     QDomElement root = doc.createElement( "XMI" );
01215     root.setAttribute( "xmi.version", "1.2" );
01216     QDateTime now = QDateTime::currentDateTime();
01217     root.setAttribute( "timestamp", now.toString(Qt::ISODate));
01218     root.setAttribute( "verified", "false");
01219     root.setAttribute( "xmlns:UML", "http://schema.omg.org/spec/UML/1.3");
01220     doc.appendChild( root );
01221 
01222     QDomElement header = doc.createElement( "XMI.header" );
01223     QDomElement meta = doc.createElement( "XMI.metamodel" );
01224     meta.setAttribute( "xmi.name", "UML" );
01225     meta.setAttribute( "xmi.version", "1.3" );
01226     meta.setAttribute( "href", "UML.xml" );
01227     header.appendChild( meta );
01228 
01244     QDomElement documentation = doc.createElement( "XMI.documentation" );
01245 
01246     // If we consider it useful we might add user and contact details
01247     // QDomElement owner = doc.createElement( "XMI.owner" );
01248     // owner.appendChild( doc.createTextNode( "Jens Kruger" ) ); // Add a User
01249     // documentation.appendChild( owner );
01250 
01251     // QDomElement contact = doc.createElement( "XMI.contact" );
01252     // contact.appendChild( doc.createTextNode( "je.krueger@web.de" ) );       // add a contact
01253     // documentation.appendChild( contact );
01254 
01255     QDomElement exporter = doc.createElement( "XMI.exporter" );
01256     exporter.appendChild( doc.createTextNode( "umbrello uml modeller http://uml.sf.net" ) );
01257     documentation.appendChild( exporter );
01258 
01259     QDomElement exporterVersion = doc.createElement( "XMI.exporterVersion" );
01260     exporterVersion.appendChild( doc.createTextNode( XMI_FILE_VERSION ) );
01261     documentation.appendChild( exporterVersion );
01262 
01263     // all files are now saved with correct Unicode encoding, we add this
01264     // information to the header, so that the file will be loaded correctly
01265     QDomElement exporterEncoding = doc.createElement( "XMI.exporterEncoding" );
01266     exporterEncoding.appendChild( doc.createTextNode( "UnicodeUTF8" ) );
01267     documentation.appendChild( exporterEncoding );
01268 
01269     header.appendChild( documentation );
01270 
01275     header.appendChild( meta );
01276     root.appendChild( header );
01277 
01278     QDomElement content = doc.createElement( "XMI.content" );
01279 
01280     QDomElement contentNS = doc.createElement( "UML:Namespace.contents" );
01281 
01282     QDomElement objectsElement = doc.createElement( "UML:Model" );
01283     objectsElement.setAttribute( "xmi.id", ID2STR(m_modelID) );
01284     objectsElement.setAttribute( "name", m_Name );
01285     objectsElement.setAttribute( "isSpecification", "false" );
01286     objectsElement.setAttribute( "isAbstract", "false" );
01287     objectsElement.setAttribute( "isRoot", "false" );
01288     objectsElement.setAttribute( "isLeaf", "false" );
01289 
01290     QDomElement ownedNS = doc.createElement( "UML:Namespace.ownedElement" );
01291 
01292     // Save stereotypes and toplevel datatypes first so that upon loading
01293     // they are known first.
01294     // There is a bug causing duplication of the same stereotype in m_stereoList.
01295     // As a workaround, we use a string list to memorize which stereotype has been saved.
01296     QStringList stereoNames;
01297     QValueList<Uml::IDType> stereoIDs;
01298     for (UMLStereotype *s = m_stereoList.first(); s; s = m_stereoList.next() ) {
01299         QString stName = s->getName();
01300         Uml::IDType stID = s->getID();
01301         if (!stereoNames.contains(stName) && !stereoIDs.contains(stID)) {
01302             s->saveToXMI(doc, ownedNS);
01303             stereoNames.append(stName);
01304             stereoIDs.append(stID);
01305         } else {
01306             kDebug() << "UMLDoc::saveToXMI: encountered duplicated stereotype "
01307                 << stName << " (id " << ID2STR(stID) << "), see bug 144924" << endl;
01308         }
01309     }
01310     for (int i = 0; i < Uml::N_MODELTYPES; i++) {
01311         m_root[i]->saveToXMI(doc, ownedNS);
01312     }
01313 
01314     objectsElement.appendChild( ownedNS );
01315 
01316     content.appendChild( objectsElement );
01317 
01318     root.appendChild( content );
01319 
01320     // Save the XMI extensions: docsettings, diagrams, listview, and codegeneration.
01321     QDomElement extensions = doc.createElement( "XMI.extensions" );
01322     extensions.setAttribute( "xmi.extender", "umbrello" );
01323 
01324     QDomElement docElement = doc.createElement( "docsettings" );
01325     Uml::IDType viewID = Uml::id_None;
01326     UMLView *currentView = UMLApp::app()->getCurrentView();
01327     if (currentView)
01328         viewID = currentView->getID();
01329     docElement.setAttribute( "viewid", ID2STR(viewID) );
01330     docElement.setAttribute( "documentation", m_Doc );
01331     docElement.setAttribute( "uniqueid", ID2STR(UniqueID::get()) );
01332     extensions.appendChild( docElement );
01333 
01334     //  save listview
01335     UMLApp::app()->getListView()->saveToXMI(doc, extensions);
01336 
01337     // save code generator
01338     CodeGenerator *codegen = UMLApp::app()->getGenerator();
01339     if (codegen) {
01340         QDomElement codeGenElement = doc.createElement( "codegeneration" );
01341         codegen->saveToXMI( doc, codeGenElement );
01342         extensions.appendChild( codeGenElement );
01343     }
01344 
01345     root.appendChild( extensions );
01346 
01347     QTextStream stream( &file );
01348     stream.setEncoding(QTextStream::UnicodeUTF8);
01349     stream << doc.toString();
01350 }
01351 
01352 short UMLDoc::getEncoding(QIODevice & file)
01353 {
01354     QTextStream stream( &file );
01355     stream.setEncoding(QTextStream::UnicodeUTF8);
01356     QString data = stream.read();
01357     QString error;
01358     int line;
01359     QDomDocument doc;
01360     if( !doc.setContent( data, false, &error, &line ) )
01361     {
01362         kWarning()<<"Can't set content: "<<error<<" Line: "<<line<<endl;
01363         return ENC_UNKNOWN;
01364     }
01365 
01366     // we start at the beginning and go to the point in the header where we can
01367     // find out if the file was saved using Unicode
01368     QDomNode node = doc.firstChild();
01369     while (node.isComment() || node.isProcessingInstruction())
01370     {
01371         node = node.nextSibling();
01372     }
01373     QDomElement root = node.toElement();
01374     if( root.isNull() )
01375     {
01376         return ENC_UNKNOWN;
01377     }
01378     //  make sure it is an XMI file
01379     if( root.tagName() != "XMI" )
01380     {
01381         return ENC_UNKNOWN;
01382     }
01383     node = node.firstChild();
01384 
01385     if ( node.isNull() )
01386         return ENC_UNKNOWN;
01387 
01388     QDomElement element = node.toElement();
01389     // check header
01390     if( element.isNull() || element.tagName() != "XMI.header" )
01391         return ENC_UNKNOWN;
01392 
01393     QDomNode headerNode = node.firstChild();
01394     while ( !headerNode.isNull() )
01395     {
01396         QDomElement headerElement = headerNode.toElement();
01397         // the information if Unicode was used is now stored in the
01398         // XMI.documentation section of the header
01399         if (headerElement.isNull() ||
01400                 headerElement.tagName() != "XMI.documentation") {
01401             headerNode = headerNode.nextSibling();
01402             continue;
01403         }
01404         QDomNode docuNode = headerNode.firstChild();
01405         while ( !docuNode.isNull() )
01406         {
01407             QDomElement docuElement = docuNode.toElement();
01408             // a tag XMI.exporterEncoding was added since version 1.2 to
01409             // mark a file as saved with Unicode
01410             if (! docuElement.isNull() &&
01411                     docuElement.tagName() == "XMI.exporterEncoding")
01412             {
01413                 // at the moment this if isn't really necessary, but maybe
01414                 // later we will have other encoding standards
01415                 if (docuElement.text() == QString("UnicodeUTF8"))
01416                 {
01417                     return ENC_UNICODE; // stop here
01418                 }
01419             }
01420             docuNode = docuNode.nextSibling();
01421         }
01422         break;
01423     }
01424     return ENC_OLD_ENC;
01425 }
01426 
01427 bool UMLDoc::loadFromXMI( QIODevice & file, short encode )
01428 {
01429     // old Umbrello versions (version < 1.2) didn't save the XMI in Unicode
01430     // this wasn't correct, because non Latin1 chars where lost
01431     // to ensure backward compatibility we have to ensure to load the old files
01432     // with non Unicode encoding
01433     if (encode == ENC_UNKNOWN)
01434     {
01435         if ((encode = getEncoding(file)) == ENC_UNKNOWN)
01436             return false;
01437         file.reset();
01438     }
01439     QTextStream stream( &file );
01440     if (encode == ENC_UNICODE)
01441     {
01442         stream.setEncoding(QTextStream::UnicodeUTF8);
01443     }
01444 
01445     QString data = stream.read();
01446     kapp->processEvents();  // give UI events a chance
01447     QString error;
01448     int line;
01449     QDomDocument doc;
01450     if( !doc.setContent( data, false, &error, &line ) ) {
01451         kWarning()<<"Can't set content:"<<error<<" Line:"<<line<<endl;
01452         return false;
01453     }
01454     kapp->processEvents();  // give UI events a chance
01455     QDomNode node = doc.firstChild();
01456     //Before Umbrello 1.1-rc1 we didn't add a <?xml heading
01457     //so we allow the option of this being missing
01458     while (node.isComment() || node.isProcessingInstruction()) {
01459         node = node.nextSibling();
01460     }
01461 
01462     QDomElement root = node.toElement();
01463     if( root.isNull() ) {
01464         return false;
01465     }
01466     //  make sure it is an XMI file
01467     if( root.tagName() != "XMI" ) {
01468         return false;
01469     }
01470 
01471     m_nViewID = Uml::id_None;
01472     for (node = node.firstChild(); !node.isNull(); node = node.nextSibling()) {
01473         if (node.isComment())
01474             continue;
01475         QDomElement element = node.toElement();
01476         if (element.isNull()) {
01477             kDebug() << "loadFromXMI: skip empty elem" << endl;
01478             continue;
01479         }
01480         bool recognized = false;
01481         QString outerTag = element.tagName();
01482         //check header
01483         if (outerTag == "XMI.header") {
01484             QDomNode headerNode = node.firstChild();
01485             if ( !validateXMIHeader(headerNode) ) {
01486                 return false;
01487             }
01488             recognized = true;
01489         } else if (outerTag == "XMI.extensions") {
01490             QDomNode extensionsNode = node.firstChild();
01491             while (! extensionsNode.isNull()) {
01492                 loadExtensionsFromXMI(extensionsNode);
01493                 extensionsNode = extensionsNode.nextSibling();
01494             }
01495             recognized = true;
01496         }
01497         if (outerTag != "XMI.content" ) {
01498             if (!recognized)
01499                 kDebug() << "UMLDoc::loadFromXMI: skipping <"
01500                     << outerTag << ">" << endl;
01501             continue;
01502         }
01503         bool seen_UMLObjects = false;
01504         //process content
01505         for (QDomNode child = node.firstChild(); !child.isNull();
01506                 child = child.nextSibling()) {
01507             if (child.isComment())
01508                 continue;
01509             element = child.toElement();
01510             QString tag = element.tagName();
01511             if (tag == "umlobjects"  // for bkwd compat.
01512                     || tagEq(tag, "Subsystem")
01513                     || tagEq(tag, "Model") ) {
01514                 if( !loadUMLObjectsFromXMI( element ) ) {
01515                     kWarning() << "failed load on objects" << endl;
01516                     return false;
01517                 }
01518                 m_Name = element.attribute( "name", i18n("UML Model") );
01519                 UMLListView *lv = UMLApp::app()->getListView();
01520                 lv->setColumnText(0, m_Name);
01521                 seen_UMLObjects = true;
01522             } else if (tagEq(tag, "Package") ||
01523                        tagEq(tag, "Class") ||
01524                        tagEq(tag, "Interface")) {
01525                 // These tests are only for foreign XMI files that
01526                 // are missing the <Model> tag (e.g. NSUML)
01527                 QDomElement parentElem = node.toElement();
01528                 if( !loadUMLObjectsFromXMI( parentElem ) ) {
01529                     kWarning() << "failed load on model objects" << endl;
01530                     return false;
01531                 }
01532                 seen_UMLObjects = true;
01533             } else if (tagEq(tag, "TaggedValue")) {
01534                 // This tag is produced here, i.e. outside of <UML:Model>,
01535                 // by the Unisys.JCR.1 Rose-to-XMI tool.
01536                 if (! seen_UMLObjects) {
01537                     kDebug() << "skipping TaggedValue because not seen_UMLObjects"
01538                         << endl;
01539                     continue;
01540                 }
01541                 tag = element.attribute("tag", "");
01542                 if (tag != "documentation") {
01543                     continue;
01544                 }
01545                 QString modelElement = element.attribute("modelElement", "");
01546                 if (modelElement.isEmpty()) {
01547                     kDebug() << "skipping TaggedValue(documentation) because "
01548                         << "modelElement.isEmpty()" << endl;
01549                     continue;
01550                 }
01551                 UMLObject *o = findObjectById(STR2ID(modelElement));
01552                 if (o == NULL) {
01553                     kDebug() << "TaggedValue(documentation): cannot find object"
01554                         << " for modelElement " << modelElement << endl;
01555                     continue;
01556                 }
01557                 QString value = element.attribute("value", "");
01558                 if (! value.isEmpty())
01559                     o->setDoc(value);
01560             } else {
01561                 // for backward compatibility
01562                 loadExtensionsFromXMI(child);
01563             }
01564         }
01565     }
01566 #ifdef VERBOSE_DEBUGGING
01567     kDebug() << "UMLDoc::m_objectList.count() is " << m_objectList.count() << endl;
01568 #endif
01569     resolveTypes();
01570     // set a default code generator if no <XMI.extensions><codegeneration> tag seen
01571     if (UMLApp::app()->getGenerator() == NULL)
01572         UMLApp::app()->setGenerator(UMLApp::app()->getDefaultLanguage());
01573     emit sigWriteToStatusBar( i18n("Setting up the document...") );
01574     kapp->processEvents();  // give UI events a chance
01575     activateAllViews();
01576 
01577     UMLView *viewToBeSet = NULL;
01578     if (m_nViewID != Uml::id_None)
01579         viewToBeSet = findView( m_nViewID );
01580     if (viewToBeSet) {
01581         changeCurrentView( m_nViewID );
01582         Settings::OptionState optionState = Settings::getOptionState();
01583         if (optionState.generalState.tabdiagrams) {
01584             UMLApp::app()->tabWidget()->showPage(viewToBeSet);
01585         }
01586     } else {
01587         createDiagram(m_root[mt_Logical], Uml::dt_Class, false);
01588         m_pCurrentRoot = m_root[mt_Logical];
01589     }
01590     emit sigResetStatusbarProgress();
01591     return true;
01592 }
01593 
01594 void UMLDoc::resolveTypes() {
01595     // Resolve the types.
01596     // This is done in a separate pass because of possible forward references.
01597     if (m_bTypesAreResolved)
01598         return;
01599     m_bTypesAreResolved = true;
01600     writeToStatusBar( i18n("Resolving object references...") );
01601     for (int i = 0; i < Uml::N_MODELTYPES; i++) {
01602        UMLFolder *obj = m_root[i];
01603 #ifdef VERBOSE_DEBUGGING
01604         kDebug() << "UMLDoc: invoking resolveRef() for " << obj->getName()
01605             << " (id=" << ID2STR(obj->getID()) << ")" << endl;
01606 #endif
01607         obj->resolveRef();
01608     }
01609     kapp->processEvents();  // give UI events a chance
01610 }
01611 
01612 bool UMLDoc::validateXMIHeader(QDomNode& headerNode) {
01613     QDomElement headerElement = headerNode.toElement();
01614     while ( !headerNode.isNull() ) {
01615         /*  //Seems older Umbrello files used a different metamodel, so don't validate it for now
01616           if( !headerElement.isNull() && headerElement.tagName() == "XMI.metamodel" ) {
01617               String metamodel = headerElement.attribute("xmi.name", "");
01618               if (metamodel != "UML") {
01619                   return false;
01620               }
01621           }
01622         */
01623         headerNode = headerNode.nextSibling();
01624         headerElement = headerNode.toElement();
01625     }
01626     return true;
01627 }
01628 
01629 bool UMLDoc::loadUMLObjectsFromXMI(QDomElement& element) {
01630     /* FIXME need a way to make status bar actually reflect
01631        how much of the file has been loaded rather than just
01632        counting to 10 (an arbitrary number)
01633     emit sigResetStatusbarProgress();
01634     emit sigSetStatusbarProgress( 0 );
01635     emit sigSetStatusbarProgressSteps( 10 );
01636     m_count = 0;
01637      */
01638     emit sigWriteToStatusBar( i18n("Loading UML elements...") );
01639 
01640     for (QDomNode node = element.firstChild(); !node.isNull();
01641             node = node.nextSibling()) {
01642         if (node.isComment())
01643             continue;
01644         QDomElement tempElement = node.toElement();
01645         QString type = tempElement.tagName();
01646         if (tagEq(type, "Model")) {
01647             bool foundUmbrelloRootFolder = false;
01648             QString name = tempElement.attribute("name");
01649             for (int i = 0; i < Uml::N_MODELTYPES; i++) {
01650                 if (name == m_root[i]->getName()) {
01651                     m_pCurrentRoot = m_root[i];
01652                     m_root[i]->loadFromXMI(tempElement);
01653                     foundUmbrelloRootFolder = true;
01654                     break;
01655                 }
01656             }
01657             if (foundUmbrelloRootFolder)
01658                 continue;
01659         }
01660         // From here on, it's support for stereotypes, pre 1.5.5 versions, and foreign files
01661         if (tagEq(type, "Namespace.ownedElement") ||
01662                 tagEq(type, "Namespace.contents") ||
01663                 tagEq(type, "Model") || tagEq(type, "ModelElement.stereotype")) {
01664             //CHECK: Umbrello currently assumes that nested elements
01665             // are ownedElements anyway.
01666             // Therefore the <UML:Namespace.ownedElement> tag is of no
01667             // significance.
01668             if( !loadUMLObjectsFromXMI( tempElement ) ) {
01669                 if (! tagEq(type, "ModelElement.stereotype")) {  // not yet implemented
01670                     kWarning() << "failed load on " << type << endl;
01671                     return false;
01672                 }
01673             }
01674             continue;
01675         }
01676         if (Model_Utils::isCommonXMIAttribute(type))
01677             continue;
01678         if (! tempElement.hasAttribute("xmi.id")) {
01679             QString idref = tempElement.attribute("xmi.idref", "");
01680             if (! idref.isEmpty()) {
01681                 kDebug() << "resolution of xmi.idref " << idref
01682                     << " is not yet implemented" << endl;
01683             } else {
01684                 kError() << "Cannot load " << type
01685                     << " because xmi.id is missing" << endl;
01686             }
01687             continue;
01688         }
01689         QString stID = tempElement.attribute("stereotype", "");
01690         UMLObject *pObject = Object_Factory::makeObjectFromXMI(type, stID);
01691         if( !pObject ) {
01692             kWarning() << "Unknown type of umlobject to create: " << type << endl;
01693             // We want a best effort, therefore this is handled as a
01694             // soft error.
01695             continue;
01696         }
01697         Uml::Object_Type ot = pObject->getBaseType();
01698         // Set the parent root folder.
01699         UMLPackage *pkg = NULL;
01700         if (ot == Uml::ot_Datatype) {
01701             pkg = m_datatypeRoot;
01702         } else {
01703             Uml::Model_Type guess = Model_Utils::guessContainer(pObject);
01704             if (guess != Uml::N_MODELTYPES)
01705                 pkg = m_root[guess];
01706         }
01707         pObject->setUMLPackage(pkg);
01708 
01709         bool status = pObject -> loadFromXMI( tempElement );
01710         if ( !status ) {
01711             //delete pObject;
01712             // Unfortunately we cannot do this because the pObject
01713             // may be still referenced by other model objects.
01714             kError() << "loadFromXMI failed for " << pObject->getName() << " xmi.id="
01715                 << ID2STR(pObject->getID()) << endl;
01716             continue;
01717         }
01718         pkg = pObject->getUMLPackage();
01719         if (ot == ot_Stereotype) {
01720             UMLStereotype *s = static_cast<UMLStereotype*>(pObject);
01721             UMLStereotype *exist = findStereotype(pObject->getName());
01722             if (exist) {
01723                 if (exist->getID() == pObject->getID()) {
01724                     delete pObject;
01725                 } else {
01726                     kDebug() << "Stereotype " << pObject->getName()
01727                         << "(id=" << ID2STR(pObject->getID())
01728                         << ") already exists with id="
01729                         << ID2STR(exist->getID()) << endl;
01730                     addStereotype(s);
01731                 }
01732             } else {
01733                 addStereotype(s);
01734             }
01735             continue;
01736         }
01737         if (pkg == NULL)
01738             kError() << "UMLDoc::loadUMLObjectsFromXMI: pkg is NULL for "
01739                 << pObject->getName() << " xmi.id="
01740                 << ID2STR(pObject->getID()) << endl;
01741         else
01742             pkg->addObject(pObject);
01743 
01744         /* FIXME see comment at loadUMLObjectsFromXMI
01745         emit sigSetStatusbarProgress( ++m_count );
01746          */
01747     }
01748     return true;
01749 }
01750 
01751 void UMLDoc::setMainViewID(Uml::IDType viewID) {
01752     m_nViewID = viewID;
01753 }
01754 
01755 void UMLDoc::loadExtensionsFromXMI(QDomNode& node) {
01756     QDomElement element = node.toElement();
01757     QString tag = element.tagName();
01758 
01759     if (tag == "docsettings") {
01760         QString viewID = element.attribute( "viewid", "-1" );
01761         m_Doc = element.attribute( "documentation", "" );
01762         QString uniqueid = element.attribute( "uniqueid", "0" );
01763 
01764         m_nViewID = STR2ID(viewID);
01765         UniqueID::set(STR2ID(uniqueid));
01766         UMLApp::app()->getDocWindow() -> newDocumentation();
01767 
01768     } else if (tag == "diagrams" || tag == "UISModelElement") {
01769         // For backward compatibility only:
01770         // Since version 1.5.5 diagrams are saved as part of the UMLFolder.
01771         QDomNode diagramNode = node.firstChild();
01772         if (tag == "UISModelElement") {          // Unisys.IntegratePlus.2
01773             element = diagramNode.toElement();
01774             tag = element.tagName();
01775             if (tag != "uisOwnedDiagram") {
01776                 kError() << "unknown child node " << tag << endl;
01777                 return;
01778             }
01779             diagramNode = diagramNode.firstChild();
01780         }
01781         if( !loadDiagramsFromXMI( diagramNode ) ) {
01782             kWarning() << "failed load on diagrams" << endl;
01783         }
01784 
01785     } else if (tag == "listview") {
01786         //FIXME: Need to resolveTypes() before loading listview,
01787         //       else listview items are duplicated.
01788         resolveTypes();
01789         if( !UMLApp::app()->getListView() -> loadFromXMI( element ) ) {
01790             kWarning() << "failed load on listview" << endl;
01791         }
01792 
01793     } else if (tag == "codegeneration") {
01794         QDomNode cgnode = node.firstChild();
01795         QDomElement cgelement = cgnode.toElement();
01796         while( !cgelement.isNull() ) {
01797             QString nodeName = cgelement.tagName();
01798             QString lang = cgelement.attribute("language","UNKNOWN");
01799             Uml::Programming_Language pl = Model_Utils::stringToProgLang(lang);
01800             CodeGenerator *g = UMLApp::app()->setGenerator(pl);
01801             g->loadFromXMI(cgelement);
01802             cgnode = cgnode.nextSibling();
01803             cgelement = cgnode.toElement();
01804         }
01805         if (UMLApp::app()->getGenerator() == NULL)
01806             UMLApp::app()->setGenerator(UMLApp::app()->getDefaultLanguage());
01807     }
01808 }
01809 
01810 // For backward compatibility only:
01811 // Since version 1.5.5 diagrams are saved as part of the UMLFolder.
01812 bool UMLDoc::loadDiagramsFromXMI( QDomNode & node ) {
01813     emit sigWriteToStatusBar( i18n("Loading diagrams...") );
01814     emit sigResetStatusbarProgress();
01815     emit sigSetStatusbarProgress( 0 );
01816     emit sigSetStatusbarProgressSteps( 10 ); //FIX ME
01817     QDomElement element = node.toElement();
01818     if( element.isNull() )
01819         return true;//return ok as it means there is no umlobjects
01820     const Settings::OptionState state = Settings::getOptionState();
01821     UMLView * pView = 0;
01822     int count = 0;
01823     while( !element.isNull() ) {
01824         QString tag = element.tagName();
01825         if (tag == "diagram" || tag == "UISDiagram") {
01826             pView = new UMLView(NULL);
01827             // IMPORTANT: Set OptionState of new UMLView _BEFORE_
01828             // reading the corresponding diagram:
01829             // + allow using per-diagram color and line-width settings
01830             // + avoid crashes due to uninitialized values for lineWidth
01831             pView -> setOptionState( state );
01832             bool success = false;
01833             if (tag == "UISDiagram") {
01834                 success = pView->loadUISDiagram(element);
01835             } else {
01836                 success = pView->loadFromXMI(element);
01837             }
01838             if (!success) {
01839                 kWarning() << "failed load on viewdata loadfromXMI" << endl;
01840                 delete pView;
01841                 return false;
01842             }
01843             // Put diagram in default predefined folder.
01844             // @todo pass in the parent folder - it might be a user defined one.
01845             Uml::Model_Type mt = Model_Utils::convert_DT_MT(pView->getType());
01846             pView->setFolder(m_root[mt]);
01847             pView -> hide();
01848             addView( pView );
01849             emit sigSetStatusbarProgress( ++count );
01850             kapp->processEvents();  // give UI events a chance
01851         }
01852         node = node.nextSibling();
01853         element = node.toElement();
01854     }
01855     return true;
01856 }
01857 
01858 void UMLDoc::removeAllViews() {
01859     for (int i = 0; i < Uml::N_MODELTYPES; i++)
01860         m_root[i]->removeAllViews();
01861     UMLApp::app()->setCurrentView(NULL);
01862     emit sigDiagramChanged(dt_Undefined);
01863     UMLApp::app()->setDiagramMenuItemsState(false);
01864 }
01865 
01866 UMLClassifierList UMLDoc::getConcepts(bool includeNested /* =true */) {
01867     UMLClassifierList conceptList;
01868     m_root[mt_Logical]->appendClassifiers(conceptList, includeNested);
01869     return conceptList;
01870 }
01871 
01872 UMLClassifierList UMLDoc::getClasses(bool includeNested /* =true */) {
01873     UMLClassifierList conceptList;
01874     m_root[mt_Logical]->appendClasses(conceptList, includeNested);
01875     return conceptList;
01876 }
01877 
01878 UMLClassifierList UMLDoc::getClassesAndInterfaces(bool includeNested /* =true */) {
01879     UMLClassifierList conceptList;
01880     m_root[mt_Logical]->appendClassesAndInterfaces(conceptList, includeNested);
01881     return conceptList;
01882 }
01883 
01884 UMLClassifierList UMLDoc::getInterfaces(bool includeNested /* =true */) {
01885     UMLClassifierList interfaceList;
01886     m_root[mt_Logical]->appendInterfaces(interfaceList, includeNested);
01887     return interfaceList;
01888 }
01889 
01890 UMLClassifierList UMLDoc::getDatatypes() {
01891     UMLObjectList objects = m_datatypeRoot->containedObjects();
01892     UMLClassifierList datatypeList;
01893     UMLObject *obj;
01894     for (UMLObjectListIt oit(objects); (obj = oit.current()) != NULL; ++oit) {
01895         if (obj->getBaseType() == ot_Datatype) {
01896             datatypeList.append(static_cast<UMLClassifier*>(obj));
01897         }
01898     }
01899     return datatypeList;
01900 }
01901 
01902 UMLAssociationList UMLDoc::getAssociations() {
01903     UMLAssociationList associationList;
01904     for (int i = 0; i < Uml::N_MODELTYPES; i++) {
01905         UMLAssociationList assocs = m_root[i]->getAssociations();
01906         UMLAssociation *a;
01907         for (UMLAssociationListIt ait(assocs); (a = ait.current()) != NULL; ++ait)
01908             associationList.append(a);
01909     }
01910     return associationList;
01911 }
01912 
01913 void UMLDoc::print(KPrinter * pPrinter) {
01914     UMLView * printView = 0;
01915     int count = QString(pPrinter -> option("kde-uml-count")).toInt();
01916     QPainter painter(pPrinter);
01917     for(int i = 0;i < count;i++) {
01918         if(i>0)
01919             pPrinter -> newPage();
01920         QString diagram = i18n("kde-uml-Diagram") + QString("%1").arg(i);
01921         QString sID = pPrinter -> option(diagram);
01922         Uml::IDType id = STR2ID(sID);
01923         printView = findView(id);
01924 
01925         if(printView)
01926             printView ->print(pPrinter, painter);
01927         printView = 0;
01928     }
01929     painter.end();
01930 }
01931 
01932 UMLViewList UMLDoc::getViewIterator() {
01933     UMLViewList accumulator;
01934     for (int i = 0; i < Uml::N_MODELTYPES; i++)
01935         m_root[i]->appendViews(accumulator, true);
01936     return accumulator;
01937 }
01938 
01939 void UMLDoc::setModified(bool modified /*=true*/, bool addToUndo /*=true*/) {
01940     if(!m_bLoading) {
01941         m_modified = modified;
01942         UMLApp::app()->setModified(modified);
01943 
01944         if (modified && addToUndo) {
01945             addToUndoStack();
01946             clearRedoStack();
01947         }
01948     }
01949 }
01950 
01951 bool UMLDoc::assignNewIDs(UMLObject* Obj) {
01952     if(!Obj || !m_pChangeLog) {
01953         kDebug() << "no Obj || Changelog" << endl;
01954         return false;
01955     }
01956     Uml::IDType result = assignNewID(Obj->getID());
01957     Obj->setID(result);
01958 
01959     //If it is a CONCEPT then change the ids of all its operations and attributes
01960     if(Obj->getBaseType() == ot_Class ) {
01961         UMLClassifier *c = static_cast<UMLClassifier*>(Obj);
01962         UMLClassifierListItemList attributes = c->getFilteredList(ot_Attribute);
01963         for(UMLObject* listItem = attributes.first(); listItem; listItem = attributes.next()) {
01964             result = assignNewID(listItem->getID());
01965             listItem->setID(result);
01966         }
01967 
01968         UMLClassifierListItemList templates = c->getFilteredList(ot_Template);
01969         for(UMLObject* listItem = templates.first(); listItem; listItem = templates.next()) {
01970             result = assignNewID(listItem->getID());
01971             listItem->setID(result);
01972         }
01973     }
01974 
01975     if(Obj->getBaseType() == ot_Interface || Obj->getBaseType() == ot_Class ) {
01976         UMLOperationList operations(((UMLClassifier*)Obj)->getOpList());
01977         for(UMLObject* listItem = operations.first(); listItem; listItem = operations.next()) {
01978             result = assignNewID(listItem->getID());
01979             listItem->setID(result);
01980         }
01981     }
01982 
01983     setModified(true);
01984 
01985     return true;
01986 }
01987 
01988 UMLFolder *UMLDoc::getRootFolder(Uml::Model_Type mt) {
01989     if (mt < Uml::mt_Logical || mt >= Uml::N_MODELTYPES) {
01990         kError() << "UMLDoc::getRootFolder: illegal input value " << mt << endl;
01991         return NULL;
01992     }
01993     return m_root[mt];
01994 }
01995 
01996 Uml::Model_Type UMLDoc::rootFolderType(UMLObject *obj) {
01997     for (int i = 0; i < Uml::N_MODELTYPES; i++) {
01998         const Uml::Model_Type m = (Uml::Model_Type)i;
01999         if (obj == m_root[m])
02000             return m;
02001     }
02002     return Uml::N_MODELTYPES;
02003 }
02004 
02006 IDChangeLog* UMLDoc::getChangeLog() {
02007     return m_pChangeLog;
02008 }
02009 
02013 void UMLDoc::beginPaste() {
02014     if(m_pChangeLog) {
02015         delete m_pChangeLog;
02016         m_pChangeLog = 0;
02017     }
02018     m_pChangeLog = new IDChangeLog;
02019 }
02020 
02023 void UMLDoc::endPaste() {
02024     if(m_pChangeLog) {
02025         delete m_pChangeLog;
02026         m_pChangeLog = 0;
02027     }
02028 }
02029 
02032 Uml::IDType UMLDoc::assignNewID(Uml::IDType OldID) {
02033     Uml::IDType result = UniqueID::gen();
02034     if (m_pChangeLog) {
02035         m_pChangeLog->addIDChange(OldID, result);
02036     }
02037     return result;
02038 }
02039 
02044 bool UMLDoc::addUMLView(UMLView * pView ) {
02045     if(!pView || !m_pChangeLog)
02046         return false;
02047 
02048     int i = 0;
02049     QString viewName = (QString)pView->getName();
02050     QString name = viewName;
02051     while( findView(pView->getType(), name) != NULL) {
02052         name = viewName + '_' + QString::number(++i);
02053     }
02054     if(i) //If name was modified
02055         pView->setName(name);
02056     Uml::IDType result = assignNewID(pView->getID());
02057     pView->setID(result);
02058 
02059     pView->activateAfterLoad( true );
02060     pView->endPartialWidgetPaste();
02061     pView->setOptionState( Settings::getOptionState() );
02062     addView(pView);
02063     setModified(true);
02064     return true;
02065 }
02066 
02067 void UMLDoc::activateAllViews() {
02068     // store old setting - for restore of last setting
02069     bool m_bLoading_old = m_bLoading;
02070     m_bLoading = true; //this is to prevent document becoming modified when activating a view
02071 
02072     for (int i = 0; i < Uml::N_MODELTYPES; i++)
02073         m_root[i]->activateViews();
02074     m_bLoading = m_bLoading_old;
02075 }
02076 
02077 void UMLDoc::settingsChanged(Settings::OptionState optionState) {
02078     for (int i = 0; i < Uml::N_MODELTYPES; i++)
02079         m_root[i]->setViewOptions(optionState);
02080     initSaveTimer();
02081 }
02082 
02083 void UMLDoc::initSaveTimer() {
02084     if( m_pAutoSaveTimer ) {
02085         m_pAutoSaveTimer -> stop();
02086         disconnect( m_pAutoSaveTimer, SIGNAL( timeout() ), this, SLOT( slotAutoSave() ) );
02087         delete m_pAutoSaveTimer;
02088         m_pAutoSaveTimer = 0;
02089     }
02090     Settings::OptionState optionState = Settings::getOptionState();
02091     if( optionState.generalState.autosave ) {
02092         m_pAutoSaveTimer = new QTimer(this, "_AUTOSAVETIMER_" );
02093         connect( m_pAutoSaveTimer, SIGNAL( timeout() ), this, SLOT( slotAutoSave() ) );
02094         m_pAutoSaveTimer->start( optionState.generalState.autosavetime * 60000, false );
02095     }
02096     return;
02097 }
02098 
02099 void UMLDoc::slotAutoSave() {
02100     //Only save if modified.
02101     if( !m_modified ) {
02102         return;
02103     }
02104     KURL tempURL = m_doc_url;
02105     if( tempURL.fileName() == i18n("Untitled") ) {
02106         tempURL.setPath( QDir::homeDirPath() + i18n("/autosave%1").arg(".xmi") );
02107         saveDocument( tempURL );
02108         m_doc_url.setFileName( i18n("Untitled") );
02109         m_modified = true;
02110         UMLApp::app()->setModified( m_modified );
02111     } else {
02112         // 2004-05-17 Achim Spangler
02113         KURL orgDocUrl = m_doc_url;
02114         QString orgFileName = m_doc_url.fileName();
02115         // don't overwrite manually saved file with autosave content
02116         QString fileName = tempURL.fileName();
02117         Settings::OptionState optionState = Settings::getOptionState();
02118         fileName.replace( ".xmi", optionState.generalState.autosavesuffix );
02119         tempURL.setFileName( fileName );
02120         // End Achim Spangler
02121 
02122         saveDocument( tempURL );
02123         // 2004-05-17 Achim Spangler
02124         // re-activate m_modified if autosave is writing to other file
02125         // than the main project file -> autosave-suffix != ".xmi"
02126         if ( ".xmi" != optionState.generalState.autosavesuffix ) {
02127             m_modified = true;
02128             UMLApp::app()->setModified( m_modified );
02129         }
02130         // restore original file name -
02131         // UMLDoc::saveDocument() sets doc_url to filename which is given as autosave-filename
02132         setURL( orgDocUrl );
02133         UMLApp * pApp = UMLApp::app();
02134         pApp->setCaption(orgFileName, isModified() );
02135         // End Achim Spangler
02136     }
02137 }
02138 
02139 void UMLDoc::signalDiagramRenamed(UMLView* pView ) {
02140     Settings::OptionState optionState = Settings::getOptionState();
02141     if (optionState.generalState.tabdiagrams)
02142         UMLApp::app()->tabWidget()->setTabLabel( pView, pView->getName() );
02143     emit sigDiagramRenamed( pView -> getID() );
02144 }
02145 
02146 void UMLDoc::addToUndoStack() {
02147     Settings::OptionState optionState = Settings::getOptionState();
02148     if (!m_bLoading && optionState.generalState.undo) {
02149         QBuffer* buffer = new QBuffer();
02150         buffer->open(IO_WriteOnly);
02151         QDataStream* undoData = new QDataStream();
02152         undoData->setDevice(buffer);
02153         saveToXMI(*buffer);
02154         buffer->close();
02155         undoStack.prepend(undoData);
02156 
02157         if (undoStack.count() > 1) {
02158             UMLApp::app()->enableUndo(true);
02159         }
02160     }
02161 }
02162 
02163 void UMLDoc::clearUndoStack() {
02164     undoStack.setAutoDelete(true);
02165     undoStack.clear();
02166     UMLApp::app()->enableRedo(false);
02167     undoStack.setAutoDelete(false);
02168     clearRedoStack();
02169 }
02170 
02171 void UMLDoc::clearRedoStack() {
02172     redoStack.setAutoDelete(true);
02173     redoStack.clear();
02174     UMLApp::app()->enableRedo(false);
02175     redoStack.setAutoDelete(false);
02176 }
02177 
02178 void UMLDoc::loadUndoData() {
02179     if (undoStack.count() < 1) {
02180         kWarning() << "no data in undostack" << endl;
02181         return;
02182     }
02183     UMLView *currentView = UMLApp::app()->getCurrentView();
02184     if (currentView == NULL) {
02185         kWarning() << "UMLDoc::loadUndoData: currentView is NULL" << endl;
02186         undoStack.setAutoDelete(true);
02187         undoStack.clear();
02188         undoStack.setAutoDelete(false);
02189         UMLApp::app()->enableUndo(false);
02190         return;
02191     }
02192     Uml::IDType currentViewID = currentView->getID();
02193     // store old setting - for restore of last setting
02194     bool m_bLoading_old = m_bLoading;
02195     m_bLoading = true;
02196     closeDocument();
02197     redoStack.prepend( undoStack.take(0) );
02198     QDataStream* undoData = undoStack.getFirst();
02199     QBuffer* buffer = static_cast<QBuffer*>( undoData->device() );
02200     buffer->open(IO_ReadOnly);
02201     loadFromXMI(*buffer);
02202     buffer->close();
02203 
02204     setModified(true, false);
02205     m_bLoading = m_bLoading_old;
02206 
02207     undoStack.setAutoDelete(true);
02208     if (undoStack.count() <= 1) {
02209         UMLApp::app()->enableUndo(false);
02210     }
02211     if (redoStack.count() >= 1) {
02212         UMLApp::app()->enableRedo(true);
02213     }
02214     while (undoStack.count() > undoMax) {
02215         undoStack.removeLast();
02216     }
02217     undoStack.setAutoDelete(false);
02218 
02219     currentView = UMLApp::app()->getCurrentView();
02220     if (currentView) {
02221         if (currentView->getID() != currentViewID)
02222             changeCurrentView( currentView->getID() );
02223         currentView->resizeCanvasToItems();
02224     }
02225 }
02226 
02227 void UMLDoc::loadRedoData() {
02228     if (redoStack.count() >= 1) {
02229         UMLView *currentView = UMLApp::app()->getCurrentView();
02230         Uml::IDType currentViewID = currentView->getID();
02231         // store old setting - for restore of last setting
02232         bool m_bLoading_old = m_bLoading;
02233         m_bLoading = true;
02234         closeDocument();
02235         undoStack.prepend( redoStack.getFirst() );
02236         QDataStream* redoData = redoStack.getFirst();
02237         redoStack.removeFirst();
02238         QBuffer* buffer = static_cast<QBuffer*>( redoData->device() );
02239         buffer->open(IO_ReadOnly);
02240         loadFromXMI(*buffer);
02241         buffer->close();
02242 
02243         setModified(true, false);
02244         currentView = UMLApp::app()->getCurrentView();
02245         currentView->resizeCanvasToItems();
02246         m_bLoading = m_bLoading_old;
02247 
02248         redoStack.setAutoDelete(true);
02249         if (redoStack.count() < 1) {
02250             UMLApp::app()->enableRedo(false);
02251         }
02252         if (undoStack.count() > 1) {
02253             UMLApp::app()->enableUndo(true);
02254         }
02255         if (currentView->getID() != currentViewID) {
02256             changeCurrentView(currentViewID);
02257         }
02258         redoStack.setAutoDelete(false);
02259     } else {
02260         kWarning() << "no data in redostack" << endl;
02261     }
02262 }
02263 
02264 void UMLDoc::addDefaultDatatypes() {
02265     CodeGenerator *cg = UMLApp::app()->getGenerator();
02266     if (cg == NULL) {
02267         kDebug() << "UMLDoc::addDefaultDatatypes: CodeGenerator is still NULL"
02268             << endl;
02269         return;
02270     }
02271     QStringList entries = cg->defaultDatatypes();
02272     QStringList::Iterator end(entries.end());
02273     for (QStringList::Iterator it = entries.begin(); it != end; ++it)
02274         createDatatype(*it);
02275 }
02276 
02277 void UMLDoc::createDatatype(const QString &name)  {
02278     UMLObjectList datatypes = m_datatypeRoot->containedObjects();
02279     UMLObject* umlobject = Model_Utils::findUMLObject(datatypes, name,
02280                                                       ot_Datatype, m_datatypeRoot);
02281     if (!umlobject) {
02282         Object_Factory::createUMLObject(ot_Datatype, name, m_datatypeRoot);
02283     }
02284     UMLApp::app()->getListView()->closeDatatypesFolder();
02285 }
02286 
02287 void UMLDoc::slotDiagramPopupMenu(QWidget* umlview, const QPoint& point) {
02288     UMLView* view = (UMLView*) umlview;
02289     if(m_pTabPopupMenu != 0) {
02290         m_pTabPopupMenu->hide();
02291         delete m_pTabPopupMenu;
02292         m_pTabPopupMenu = 0;
02293     }
02294     Settings::OptionState optionState = Settings::getOptionState();
02295     if (! optionState.generalState.tabdiagrams)
02296         return;
02297 
02298     Uml::ListView_Type type = lvt_Unknown;
02299     switch( view->getType() ) {
02300     case dt_Class:
02301         type = lvt_Class_Diagram;
02302         break;
02303 
02304     case dt_UseCase:
02305         type = lvt_UseCase_Diagram;
02306         break;
02307 
02308     case dt_Sequence:
02309         type = lvt_Sequence_Diagram;
02310         break;
02311 
02312     case dt_Collaboration:
02313         type = lvt_Collaboration_Diagram;
02314         break;
02315 
02316     case dt_State:
02317         type = lvt_State_Diagram;
02318         break;
02319 
02320     case dt_Activity:
02321         type = lvt_Activity_Diagram;
02322         break;
02323 
02324     case dt_Component:
02325         type = lvt_Component_Diagram;
02326         break;
02327 
02328     case dt_Deployment:
02329         type = lvt_Deployment_Diagram;
02330         break;
02331 
02332     case dt_EntityRelationship:
02333         type = lvt_EntityRelationship_Diagram;
02334         break;
02335 
02336     default:
02337         kWarning() << "unknown diagram type in slotDiagramPopupMenu()" << endl;
02338         break;
02339     }//end switch
02340 
02341     m_pTabPopupMenu = new ListPopupMenu(UMLApp::app()->getMainViewWidget(), type);
02342     m_pTabPopupMenu->popup(point);
02343     connect(m_pTabPopupMenu, SIGNAL(activated(int)), view, SLOT(slotMenuSelection(int)));
02344 }
02345 
02346 void UMLDoc::addDefaultStereotypes() {
02347     CodeGenerator *gen = UMLApp::app()->getGenerator();
02348     if (gen)
02349         gen->createDefaultStereotypes();
02350 }
02351 
02352 const UMLStereotypeList& UMLDoc::getStereotypes() {
02353     return m_stereoList;
02354 }
02355 
02356 
02357 #include "umldoc.moc"
02358 
KDE Logo
This file is part of the documentation for umbrello Version 3.1.0.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Tue Jun 26 08:08:00 2007 by doxygen 1.4.1 written by Dimitri van Heesch, © 1997-2003