/*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * copyright (C) 2002-2007 * * Umbrello UML Modeller Authors * ***************************************************************************/ // own header #include "umldoc.h" // qt includes #include #include #include #include #include #include #include // kde includes #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // app includes #include "uniqueid.h" #include "associationwidget.h" #include "association.h" #include "package.h" #include "folder.h" #include "codegenerator.h" #include "classifier.h" #include "enum.h" #include "entity.h" #include "docwindow.h" #include "operation.h" #include "attribute.h" #include "template.h" #include "enumliteral.h" #include "entityattribute.h" #include "stereotype.h" #include "classifierlistitem.h" #include "object_factory.h" #include "import_rose.h" #include "model_utils.h" #include "widget_utils.h" #include "uml.h" #include "umllistview.h" #include "umllistviewitem.h" #include "umlview.h" #include "clipboard/idchangelog.h" #include "dialogs/classpropdlg.h" #include "codegenerators/codegenfactory.h" #include "listpopupmenu.h" #include "version.h" #define XMI_FILE_VERSION UMBRELLO_VERSION // For the moment, the XMI_FILE_VERSION changes with each UMBRELLO_VERSION. // But someday that may stabilize ;) using namespace Uml; static const uint undoMax = 30; UMLDoc::UMLDoc() { m_Name = i18n("UML Model"); m_modelID = "m1"; m_count = 0; m_pChangeLog = 0; m_Doc = ""; m_modified = false; m_bLoading = false; m_bTypesAreResolved = false; m_pAutoSaveTimer = 0; m_nViewID = Uml::id_None; m_pTabPopupMenu = 0; m_pCurrentRoot = NULL; } void UMLDoc::init() { // Initialize predefined folders. const TQString nativeRootName[Uml::N_MODELTYPES] = { "Logical View", "Use Case View", "Component View", "Deployment View", "Entity Relationship Model" }; const TQString localizedRootName[Uml::N_MODELTYPES] = { i18n("Logical View"), i18n("Use Case View"), i18n("Component View"), i18n("Deployment View"), i18n("Entity Relationship Model") }; for (int i = 0; i < Uml::N_MODELTYPES; i++) { m_root[i] = new UMLFolder(nativeRootName[i], STR2ID(nativeRootName[i])); m_root[i]->setLocalName(localizedRootName[i]); } m_datatypeRoot = new UMLFolder("Datatypes", "Datatypes"); m_datatypeRoot->setLocalName(i18n("Datatypes")); m_datatypeRoot->setUMLPackage(m_root[Uml::mt_Logical]); m_root[Uml::mt_Logical]->addObject(m_datatypeRoot); // Connect signals. UMLApp * pApp = UMLApp::app(); connect(this, TQT_SIGNAL(sigDiagramCreated(Uml::IDType)), pApp, TQT_SLOT(slotUpdateViews())); connect(this, TQT_SIGNAL(sigDiagramRemoved(Uml::IDType)), pApp, TQT_SLOT(slotUpdateViews())); connect(this, TQT_SIGNAL(sigDiagramRenamed(Uml::IDType)), pApp, TQT_SLOT(slotUpdateViews())); connect(this, TQT_SIGNAL( sigCurrentViewChanged() ), pApp, TQT_SLOT( slotCurrentViewChanged() ) ); } UMLDoc::~UMLDoc() { delete m_pChangeLog; m_pChangeLog = 0; } void UMLDoc::addView(UMLView *view) { if (view == NULL) { kError() << "UMLDoc::addView: argument is NULL" << endl; return; } UMLFolder *f = view->getFolder(); if (f == NULL) { kError() << "UMLDoc::addView: view folder is not set" << endl; return; } f->addView(view); UMLApp * pApp = UMLApp::app(); if ( pApp->getListView() ) connect(this, TQT_SIGNAL(sigObjectRemoved(UMLObject *)), view, TQT_SLOT(slotObjectRemoved(UMLObject *))); pApp->setCurrentView(view); if ( ! m_bLoading ) { view -> show(); emit sigDiagramChanged(view ->getType()); } Settings::OptionState optionState = Settings::getOptionState(); KTabWidget* tabWidget = NULL; if (optionState.generalState.tabdiagrams) { tabWidget = UMLApp::app()->tabWidget(); tabWidget->addTab(view, view->getName()); tabWidget->setTabIconSet(view, Widget_Utils::iconSet(view->getType())); } pApp->setDiagramMenuItemsState(true); pApp->slotUpdateViews(); pApp->setCurrentView(view); if (tabWidget) { tabWidget->showPage(view); tabWidget->setCurrentPage(tabWidget->currentPageIndex()); } } void UMLDoc::removeView(UMLView *view , bool enforceCurrentView ) { if(!view) { kError()<<"UMLDoc::removeView(UMLView *view) called with view = 0"<getListView() ) { disconnect(this,TQT_SIGNAL(sigObjectRemoved(UMLObject *)), view,TQT_SLOT(slotObjectRemoved(UMLObject *))); } view->hide(); //remove all widgets before deleting view view->removeAllWidgets(); UMLFolder *f = view->getFolder(); if (f == NULL) { kError() << "UMLDoc::removeView(" << view->getName() << "): view->getFolder() returns NULL" << endl; return; } f->removeView(view); UMLView *currentView = UMLApp::app()->getCurrentView(); if (currentView == view) { UMLApp::app()->setCurrentView(NULL); UMLViewList viewList; m_root[mt_Logical]->appendViews(viewList); UMLView* firstView = viewList.first(); if (!firstView && enforceCurrentView) //create a diagram { createDiagram(m_root[mt_Logical], dt_Class, false); kapp->processEvents(); m_root[mt_Logical]->appendViews(viewList); firstView = viewList.first(); } if ( firstView ) { changeCurrentView( firstView->getID() ); UMLApp::app()->setDiagramMenuItemsState(true); } } } void UMLDoc::setURL(const KURL &url) { m_doc_url = url; return; } const KURL& UMLDoc::URL() const { return m_doc_url; } bool UMLDoc::saveModified() { bool completed(true); if (!m_modified) return completed; UMLApp *win = UMLApp::app(); 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()); switch(want_save) { case KMessageBox::Yes: if (m_doc_url.fileName() == i18n("Untitled")) { if (win->slotFileSaveAs()) { closeDocument(); completed=true; } else { completed=false; } } else { saveDocument(URL()); closeDocument(); completed=true; } break; case KMessageBox::No: setModified(false); closeDocument(); completed=true; break; case KMessageBox::Cancel: completed=false; break; default: completed=false; break; } return completed; } void UMLDoc::closeDocument() { UMLApp::app()->setGenerator(Uml::pl_Reserved); // delete the codegen m_Doc = ""; DocWindow* dw = UMLApp::app()->getDocWindow(); if (dw) { dw->newDocumentation(); } UMLListView *listView = UMLApp::app()->getListView(); if (listView) { listView->init(); // store old setting - for restore of last setting bool m_bLoading_old = m_bLoading; m_bLoading = true; // This is to prevent document becoming modified. // For reference, here is an example of a call sequence that would // otherwise result in futile addToUndoStack() calls: // removeAllViews() => // UMLView::removeAllAssociations() => // UMLView::removeAssoc() => // UMLDoc::setModified(true, true) => // addToUndoStack(). removeAllViews(); m_bLoading = m_bLoading_old; // Remove all objects from the predefined folders. // @fixme With advanced code generation enabled, this crashes. UMLObject *obj; for (int i = 0; i < Uml::N_MODELTYPES; i++) m_root[i]->removeAllObjects(); // Restore the datatype folder, it has been deleted above. m_datatypeRoot = new UMLFolder("Datatypes", "Datatypes"); m_datatypeRoot->setLocalName(i18n("Datatypes")); m_datatypeRoot->setUMLPackage(m_root[Uml::mt_Logical]); m_root[Uml::mt_Logical]->addObject(m_datatypeRoot); listView->theDatatypeFolder()->setUMLObject(m_datatypeRoot); /* Remove any stereotypes. if (m_stereoList.count() > 0) { UMLStereotype *s; for (UMLStereotypeListIt sit(m_stereoList); (s = sit.current()) != 0; ++sit) delete s; m_stereoList.clear(); } */ } m_bTypesAreResolved = false; } bool UMLDoc::newDocument() { closeDocument(); UMLApp::app()->setCurrentView(NULL); m_doc_url.setFileName(i18n("Untitled")); //see if we need to start with a new diagram Settings::OptionState optionState = Settings::getOptionState(); Uml::Diagram_Type dt = optionState.generalState.diagram; Uml::Model_Type mt = Model_Utils::convert_DT_MT(dt); if (mt == Uml::N_MODELTYPES) { // don't allow no diagram dt = Uml::dt_Class; mt = Uml::mt_Logical; } createDiagram(m_root[mt], dt, false); UMLApp::app()->initGenerator(); addDefaultDatatypes(); addDefaultStereotypes(); setModified(false); initSaveTimer(); UMLApp::app()->enableUndo(false); clearUndoStack(); addToUndoStack(); return true; } bool UMLDoc::openDocument(const KURL& url, const char* /*format =0*/) { if(url.fileName().length() == 0) { newDocument(); return false; } m_doc_url = url; TQDir d = url.path(1); closeDocument(); // IMPORTANT: set m_bLoading to true // _AFTER_ the call of UMLDoc::closeDocument() // as it sets m_bLoading to false afer it was temporarily // changed to true to block recording of changes in redo-buffer m_bLoading = true; TQString tmpfile; TDEIO::NetAccess::download( url, tmpfile, UMLApp::app() ); TQFile file( tmpfile ); if ( !file.exists() ) { KMessageBox::error(0, i18n("The file %1 does not exist.").arg(d.path()), i18n("Load Error")); m_doc_url.setFileName(i18n("Untitled")); m_bLoading = false; newDocument(); return false; } // status of XMI loading bool status = false; // check if the xmi file is a compressed archive like tar.bzip2 or tar.gz TQString filetype = m_doc_url.fileName(true); TQString mimetype = ""; if (filetype.find(TQRegExp("\\.tgz$")) != -1) { mimetype = "application/x-gzip"; } else if (filetype.find(TQRegExp("\\.tar.bz2$")) != -1) { mimetype = "application/x-bzip2"; } if (mimetype.isEmpty() == false) { KTar archive(tmpfile, mimetype); if (archive.open(IO_ReadOnly) == false) { KMessageBox::error(0, i18n("The file %1 seems to be corrupted.").arg(d.path()), i18n("Load Error")); m_doc_url.setFileName(i18n("Untitled")); m_bLoading = false; newDocument(); return false; } // get the root directory and all entries in const KArchiveDirectory * rootDir = archive.directory(); TQStringList entries = rootDir->entries(); TQString entryMimeType; bool foundXMI = false; TQStringList::Iterator it; TQStringList::Iterator end(entries.end()); // now go through all entries till we find an xmi file for (it = entries.begin(); it != end; ++it) { // only check files, we do not go in subdirectories if (rootDir->entry(*it)->isFile() == true) { // we found a file, check the mimetype entryMimeType = KMimeType::findByPath(*it, 0, true)->name(); if (entryMimeType == "application/x-uml") { foundXMI = true; break; } } } // if we found an XMI file, we have to extract it to a temporary file if (foundXMI == true) { KTempDir tmp_dir; KArchiveEntry * entry; KArchiveFile * fileEntry; // try to cast the file entry in the archive to an archive entry entry = const_cast(rootDir->entry(*it)); if (entry == 0) { KMessageBox::error(0, i18n("There was no XMI file found in the compressed file %1.").arg(d.path()), i18n("Load Error")); m_doc_url.setFileName(i18n("Untitled")); m_bLoading = false; newDocument(); return false; } // now try to cast the archive entry to a file entry, so that we can // extract the file fileEntry = dynamic_cast(entry); if (fileEntry == 0) { KMessageBox::error(0, i18n("There was no XMI file found in the compressed file %1.").arg(d.path()), i18n("Load Error")); m_doc_url.setFileName(i18n("Untitled")); m_bLoading = false; newDocument(); return false; } // now we can extract the file to the temporary directory fileEntry->copyTo(tmp_dir.name()); // now open the extracted file for reading TQFile xmi_file(tmp_dir.name() + *it); if( !xmi_file.open( IO_ReadOnly ) ) { KMessageBox::error(0, i18n("There was a problem loading the extracted file: %1").arg(d.path()), i18n("Load Error")); m_doc_url.setFileName(i18n("Untitled")); m_bLoading = false; newDocument(); return false; } status = loadFromXMI( *TQT_TQIODEVICE(&xmi_file), ENC_UNKNOWN ); // close the extracted file and the temporary directory xmi_file.close(); tmp_dir.unlink(); } else { KMessageBox::error(0, i18n("There was no XMI file found in the compressed file %1.").arg(d.path()), i18n("Load Error")); m_doc_url.setFileName(i18n("Untitled")); m_bLoading = false; newDocument(); return false; } archive.close(); } else { // no, it seems to be an ordinary file if( !file.open( IO_ReadOnly ) ) { KMessageBox::error(0, i18n("There was a problem loading file: %1").arg(d.path()), i18n("Load Error")); m_doc_url.setFileName(i18n("Untitled")); m_bLoading = false; newDocument(); return false; } if (filetype.endsWith(".mdl")) status = Import_Rose::loadFromMDL(*TQT_TQIODEVICE(&file)); else status = loadFromXMI( *TQT_TQIODEVICE(&file), ENC_UNKNOWN ); } file.close(); TDEIO::NetAccess::removeTempFile( tmpfile ); if( !status ) { KMessageBox::error(0, i18n("There was a problem loading file: %1").arg(d.path()), i18n("Load Error")); m_bLoading = false; newDocument(); return false; } setModified(false); m_bLoading = false; initSaveTimer(); UMLApp::app()->enableUndo(false); clearUndoStack(); addToUndoStack(); // for compatibility addDefaultStereotypes(); return true; } bool UMLDoc::saveDocument(const KURL& url, const char * /* format */) { m_doc_url = url; TQDir d = m_doc_url.path(1); TQFile file; bool uploaded = true; // first, we have to find out which format to use TQString strFileName = url.path(-1); TQFileInfo fileInfo(strFileName); TQString fileExt = fileInfo.extension(); TQString fileFormat = "xmi"; if (fileExt == "xmi" || fileExt == "bak.xmi") { fileFormat = "xmi"; } else if (fileExt == "xmi.tgz" || fileExt == "bak.xmi.tgz") { fileFormat = "tgz"; } else if (fileExt == "xmi.tar.bz2" || fileExt == "bak.xmi.tar.bz2") { fileFormat = "bz2"; } else { fileFormat = "xmi"; } initSaveTimer(); if (fileFormat == "tgz" || fileFormat == "bz2") { KTar * archive; KTempFile tmp_tgz_file; // first we have to check if we are saving to a local or remote file if (url.isLocalFile()) { if (fileFormat == "tgz") // check tgz or bzip2 { archive = new KTar(d.path(), "application/x-gzip"); } else { archive = new KTar(d.path(), "application/x-bzip2"); } } else { if (fileFormat == "tgz") // check tgz or bzip2 { archive = new KTar(tmp_tgz_file.name(), "application/x-gzip"); } else { archive = new KTar(tmp_tgz_file.name(), "application/x-bzip2"); } } // now check if we can write to the file if (archive->open(IO_WriteOnly) == false) { KMessageBox::error(0, i18n("There was a problem saving file: %1").arg(d.path()), i18n("Save Error")); return false; } // we have to create a temporary xmi file // we will add this file later to the archive KTempFile tmp_xmi_file; file.setName(tmp_xmi_file.name()); if( !file.open( IO_WriteOnly ) ) { KMessageBox::error(0, i18n("There was a problem saving file: %1").arg(d.path()), i18n("Save Error")); return false; } saveToXMI(*TQT_TQIODEVICE(&file)); // save XMI to this file... file.close(); // ...and close it // now add this file to the archive, but without the extension TQString tmpTQString = url.fileName(); if (fileFormat == "tgz") { tmpTQString.replace(TQRegExp("\\.tgz$"), ""); } else { tmpTQString.replace(TQRegExp("\\.tar\\.bz2$"), ""); } archive->addLocalFile(tmp_xmi_file.name(), tmpTQString); archive->close(); #if KDE_IS_VERSION(3,4,89) if (!archive->closeSucceeded()) { KMessageBox::error(0, i18n("There was a problem saving file: %1").arg(d.path()), i18n("Save Error")); return false; } #endif // now the xmi file was added to the archive, so we can delete it tmp_xmi_file.close(); tmp_xmi_file.unlink(); // now we have to check, if we have to upload the file if ( !url.isLocalFile() ) { uploaded = TDEIO::NetAccess::upload( tmp_tgz_file.name(), m_doc_url, UMLApp::app() ); } // now the archive was written to disk (or remote) so we can delete the // objects tmp_tgz_file.close(); tmp_tgz_file.unlink(); delete archive; } else { // save as normal uncompressed XMI KTempFile tmpfile; // we need this tmp file if we are writing to a remote file // save in _any_ case to a temp file // -> if something goes wrong during saveToXmi, the // original content is preserved // ( e.g. if umbrello dies in the middle of the document model parsing // for saveToXMI due to some problems ) /// @TODO insert some checks in saveToXMI to detect a failed save attempt file.setName( tmpfile.name() ); // lets open the file for writing if( !file.open( IO_WriteOnly ) ) { KMessageBox::error(0, i18n("There was a problem saving file: %1").arg(d.path()), i18n("Save Error")); return false; } saveToXMI(*TQT_TQIODEVICE(&file)); // save the xmi stuff to it file.close(); tmpfile.close(); // if it is a remote file, we have to upload the tmp file if ( !url.isLocalFile() ) { uploaded = TDEIO::NetAccess::upload( tmpfile.name(), m_doc_url, UMLApp::app() ); } else { // now remove the original file if ( TDEIO::NetAccess::file_move( tmpfile.name(), d.path(), -1, true ) == false ) { KMessageBox::error(0, i18n("There was a problem saving file: %1").arg(d.path()), i18n("Save Error")); m_doc_url.setFileName(i18n("Untitled")); return false; } } } if( !uploaded ) { KMessageBox::error(0, i18n("There was a problem uploading file: %1").arg(d.path()), i18n("Save Error")); m_doc_url.setFileName(i18n("Untitled")); } setModified(false); return uploaded; } void UMLDoc::setupSignals() { WorkToolBar *tb = UMLApp::app() -> getWorkToolBar(); connect(this, TQT_SIGNAL(sigDiagramChanged(Uml::Diagram_Type)), tb, TQT_SLOT(slotCheckToolBar(Uml::Diagram_Type))); //new signals below return; } UMLView * UMLDoc::findView(Uml::IDType id) { UMLView *v = NULL; for (int i = 0; i < Uml::N_MODELTYPES; i++) { v = m_root[i]->findView(id); if (v) break; } return v; } UMLView * UMLDoc::findView(Uml::Diagram_Type type, const TQString &name, bool searchAllScopes /* =false */) { Uml::Model_Type mt = Model_Utils::convert_DT_MT(type); return m_root[mt]->findView(type, name, searchAllScopes); } UMLObject* UMLDoc::findObjectById(Uml::IDType id) { UMLObject *o = NULL; for (int i = 0; i < Uml::N_MODELTYPES; i++) { if (id == m_root[i]->getID()) return m_root[i]; o = m_root[i]->findObjectById(id); if (o) return o; } o = findStereotypeById(id); return o; } UMLStereotype * UMLDoc::findStereotypeById(Uml::IDType id) { for (UMLStereotype *s = m_stereoList.first(); s; s = m_stereoList.next() ) { if (s->getID() == id) return s; } return NULL; } UMLObject* UMLDoc::findUMLObject(const TQString &name, Uml::Object_Type type /* = ot_UMLObject */, UMLObject *currentObj /* = NULL */) { UMLObject *o = m_datatypeRoot->findObject(name); if (o) return o; for (int i = 0; i < Uml::N_MODELTYPES; i++) { UMLObjectList list = m_root[i]->containedObjects(); o = Model_Utils::findUMLObject(list, name, type, currentObj); if (o) return o; if ((type == ot_UMLObject || type == ot_Folder) && name == m_root[i]->getName()) return m_root[i]; } return NULL; } UMLClassifier* UMLDoc::findUMLClassifier(const TQString &name) { //this is used only by code generator so we don't need to look at Datatypes UMLObject * obj = findUMLObject(name); return dynamic_cast(obj); } /** * Adds a UMLObject thats already created but doesn't change * any ids or signal. Used by the list view. Use * AddUMLObjectPaste if pasting. */ bool UMLDoc::addUMLObject(UMLObject* object) { Object_Type ot = object->getBaseType(); if (ot == ot_Attribute || ot == ot_Operation || ot == ot_EnumLiteral || ot == ot_EntityAttribute || ot == ot_Template || ot == ot_Stereotype) { kDebug() << "UMLDoc::addUMLObject(" << object->getName() << "): not adding type " << ot << endl; return false; } UMLPackage *pkg = object->getUMLPackage(); if (pkg == NULL) { pkg = currentRoot(); kDebug() << "UMLDoc::addUMLObject(" << object->getName() << "): no parent package set, assuming " << pkg->getName() << endl; object->setUMLPackage( pkg ); } return pkg->addObject(object); } void UMLDoc::addStereotype(const UMLStereotype *s) { if (! m_stereoList.contains(s)) m_stereoList.append(s); } void UMLDoc::removeStereotype(const UMLStereotype *s) { if (m_stereoList.contains(s)) m_stereoList.remove(s); } void UMLDoc::writeToStatusBar(const TQString &text) { emit sigWriteToStatusBar(text); } // simple removal of an object void UMLDoc::slotRemoveUMLObject(UMLObject* object) { //m_objectList.remove(object); UMLPackage *pkg = object->getUMLPackage(); if (pkg == NULL) { kError() << "UMLDoc::slotRemoveUMLObject(" << object->getName() << "): parent package is not set !" << endl; return; } pkg->removeObject(object); } bool UMLDoc::isUnique(const TQString &name) { UMLListView *listView = UMLApp::app()->getListView(); UMLListViewItem *currentItem = (UMLListViewItem*)listView->currentItem(); UMLListViewItem *parentItem = 0; // check for current item, if its a package, then we do a check on that // otherwise, if current item exists, find its parent and check if thats // a package.. if(currentItem) { // its possible that the current item *is* a package, then just // do check now if (Model_Utils::typeIsContainer(currentItem->getType())) return isUnique (name, (UMLPackage*) currentItem->getUMLObject()); parentItem = (UMLListViewItem*)currentItem->parent(); } // item is in a package so do check only in that if (parentItem != NULL && Model_Utils::typeIsContainer(parentItem->getType())) { UMLPackage *parentPkg = static_cast(parentItem->getUMLObject()); return isUnique(name, parentPkg); } kError() << "UMLDoc::isUnique(" << name << "): Not currently in a package" << endl; /* Check against all objects that _don't_ have a parent package. for (UMLObjectListIt oit(m_objectList); oit.current(); ++oit) { UMLObject *obj = oit.current(); if (obj->getUMLPackage() == NULL && obj->getName() == name) return false; } */ return true; } bool UMLDoc::isUnique(const TQString &name, UMLPackage *package) { // if a package, then only do check in that if (package) return (package->findObject(name) == NULL); // Not currently in a package: ERROR kError() << "UMLDoc::isUnique(2)(" << name << "): Not currently in a package" << endl; /* Check against all objects that _don't_ have a parent package. for (UMLObjectListIt oit(m_objectList); oit.current(); ++oit) { UMLObject *obj = oit.current(); if (obj->getUMLPackage() == NULL && obj->getName() == name) return false; } */ return true; } UMLStereotype* UMLDoc::findStereotype(const TQString &name) { UMLStereotype *s; for (UMLStereotypeListIt it(m_stereoList); (s = it.current()) != NULL; ++it) { if (s->getName() == name) return s; } return NULL; } UMLStereotype* UMLDoc::findOrCreateStereotype(const TQString &name) { UMLStereotype *s = findStereotype(name); if (s != NULL) { return s; } s = new UMLStereotype(name, STR2ID(name)); addStereotype(s); //emit modified(); return s; } void UMLDoc::removeAssociation (UMLAssociation * assoc, bool doSetModified /*=true*/) { if(!assoc) return; // Remove the UMLAssociation from m_objectList. UMLPackage *pkg = assoc->getUMLPackage(); if (pkg == NULL) { kError() << "UMLDoc::removeAssociation(" << assoc->getName() << "): parent package is not set !" << endl; return; } pkg->removeObject(assoc); if (doSetModified) // so we will save our document setModified(true, false); } UMLAssociation * UMLDoc::findAssociation(Uml::Association_Type assocType, const UMLObject *roleAObj, const UMLObject *roleBObj, bool *swap) { UMLAssociationList assocs = getAssociations(); UMLAssociation *a, *ret = NULL; for (a = assocs.first(); a; a = assocs.next()) { if (a->getAssocType() != assocType) continue; if (a->getObject(Uml::A) == roleAObj && a->getObject(Uml::B) == roleBObj) return a; if (a->getObject(Uml::A) == roleBObj && a->getObject(Uml::B) == roleAObj) { ret = a; } } if (swap) *swap = (ret != NULL); return ret; } // create AND add an association. Used by refactoring assistant. UMLAssociation* UMLDoc::createUMLAssociation(UMLObject *a, UMLObject *b, Uml::Association_Type type) { bool swap; UMLAssociation *assoc = findAssociation(type, a, b, &swap); if (assoc == NULL) { assoc = new UMLAssociation(type, a, b ); addAssociation(assoc); } return assoc; } void UMLDoc::addAssociation(UMLAssociation *Assoc) { if (Assoc == NULL) return; // First, check that this association has not already been added. // This may happen when loading old XMI files where all the association // information was taken from the tag. UMLAssociationList assocs = getAssociations(); for (UMLAssociationListIt ait(assocs); ait.current(); ++ait) { UMLAssociation *a = ait.current(); // check if its already been added (shouldn't be the case right now // as UMLAssociations only belong to one associationwidget at a time) if (a == Assoc) { kDebug() << "UMLDoc::addAssociation: duplicate addition attempted" << endl; return; } } // If we get here it's really a new association. // Add the UMLAssociation at the owning UMLPackage. UMLPackage *pkg = Assoc->getUMLPackage(); if (pkg == NULL) { kError() << "UMLDoc::addAssociation(" << Assoc->getName() << "): parent package is not set !" << endl; return; } pkg->addObject(Assoc); // I don't believe this appropriate, UMLAssociations ARENT UMLWidgets -b.t. // emit sigObjectCreated(o); setModified(true); } TQString UMLDoc::uniqViewName(const Uml::Diagram_Type type) { TQString dname; if(type == dt_UseCase) dname = i18n("use case diagram"); else if(type == dt_Class) dname = i18n("class diagram"); else if(type == dt_Sequence) dname = i18n("sequence diagram"); else if(type == dt_Collaboration) dname = i18n("collaboration diagram"); else if( type == dt_State ) dname = i18n( "state diagram" ); else if( type == dt_Activity ) dname = i18n( "activity diagram" ); else if( type == dt_Component ) dname = i18n( "component diagram" ); else if( type == dt_Deployment ) dname = i18n( "deployment diagram" ); else if( type == dt_EntityRelationship ) dname = i18n( "entity relationship diagram" ); else { kWarning() << "uniqViewName() called with unknown diagram type" << endl; } TQString name = dname; for (int number = 0; findView(type, name, true); ++number, name = dname + '_' + TQString::number(number)) ; return name; } bool UMLDoc::loading() const { return m_bLoading; } void UMLDoc::setLoading(bool state /* = true */) { m_bLoading = state; } UMLView* UMLDoc::createDiagram(UMLFolder *folder, Uml::Diagram_Type type, bool askForName /*= true */) { bool ok = true; TQString name, dname = uniqViewName(type); while(true) { if (askForName) { name = KInputDialog::getText(i18n("Name"), i18n("Enter name:"), dname, &ok, (TQWidget*)UMLApp::app()); } else { name = dname; } if (!ok) { break; } if (name.length() == 0) { KMessageBox::error(0, i18n("That is an invalid name for a diagram."), i18n("Invalid Name")); } else if(!findView(type, name)) { UMLView* temp = new UMLView(folder); temp -> setOptionState( Settings::getOptionState() ); temp->setName( name ); temp->setType( type ); temp->setID( UniqueID::gen() ); addView(temp); emit sigDiagramCreated( temp->getID() ); setModified(true, false); UMLApp::app()->enablePrint(true); changeCurrentView( temp->getID() ); return temp; } else { KMessageBox::error(0, i18n("A diagram is already using that name."), i18n("Not a Unique Name")); } }//end while return 0; } void UMLDoc::renameDiagram(Uml::IDType id) { bool ok = false; UMLView *temp = findView(id); Diagram_Type type = temp->getType(); TQString oldName= temp->getName(); while(true) { TQString name = KInputDialog::getText(i18n("Name"), i18n("Enter name:"), oldName, &ok, (TQWidget*)UMLApp::app()); if(!ok) break; if(name.length() == 0) KMessageBox::error(0, i18n("That is an invalid name for a diagram."), i18n("Invalid Name")); else if(!findView(type, name)) { temp->setName(name); emit sigDiagramRenamed(id); setModified(true); break; } else KMessageBox::error(0, i18n("A diagram is already using that name."), i18n("Not a Unique Name")); } } void UMLDoc::renameUMLObject(UMLObject *o) { bool ok = false; TQString oldName= o->getName(); while(true) { TQString name = KInputDialog::getText(i18n("Name"), i18n("Enter name:"), oldName, &ok, (TQWidget*)UMLApp::app()); if(!ok) break; if(name.length() == 0) KMessageBox::error(0, i18n("That is an invalid name."), i18n("Invalid Name")); else if (isUnique(name)) { o->setName(name); setModified(true); break; } else { KMessageBox::error(0, i18n("That name is already being used."), i18n("Not a Unique Name")); } } return; } void UMLDoc::renameChildUMLObject(UMLObject *o) { bool ok = false; UMLClassifier* p = dynamic_cast(o->parent()); if(!p) { kDebug() << "Can't create object, no parent found" << endl; return; } TQString oldName= o->getName(); while(true) { TQString name = KInputDialog::getText(i18n("Name"), i18n("Enter name:"), oldName, &ok, (TQWidget*)UMLApp::app()); if(!ok) break; if(name.length() == 0) KMessageBox::error(0, i18n("That is an invalid name."), i18n("Invalid Name")); else { if (p->findChildObject(name) == NULL || ((o->getBaseType() == Uml::ot_Operation) && KMessageBox::warningYesNo( kapp -> mainWidget() , i18n( "The name you entered was not unique.\nIs this what you wanted?" ), i18n( "Name Not Unique"),i18n("Use Name"),i18n("Enter New Name")) == KMessageBox::Yes) ) { o->setName(name); setModified(true); break; } else { KMessageBox::error(0, i18n("That name is already being used."), i18n("Not a Unique Name")); } } } } void UMLDoc::changeCurrentView(Uml::IDType id) { UMLApp* pApp = UMLApp::app(); UMLView* w = findView(id); if (w) { pApp->setCurrentView(w); emit sigDiagramChanged(w->getType()); pApp->setDiagramMenuItemsState( true ); setModified(true); } emit sigCurrentViewChanged(); } void UMLDoc::removeDiagram(Uml::IDType id) { UMLApp::app()->getDocWindow()->updateDocumentation(true); UMLView* umlview = findView(id); if(!umlview) { kError()<<"Request to remove diagram " << ID2STR(id) << ": Diagram not found!"<getName()), i18n("Delete Diagram"),KGuiItem( i18n("&Delete"), "edit-delete")) == KMessageBox::Continue) { removeView(umlview); emit sigDiagramRemoved(id); setModified(true); /* if (infoWidget->isVisible()) { emit sigDiagramChanged(dt_Undefined); UMLApp::app()->enablePrint(false); } */ //FIXME sort out all the TDEActions for when there's no diagram //also remove the buttons from the WorkToolBar, then get rid of infowidget } } UMLFolder *UMLDoc::currentRoot() { UMLView *currentView = UMLApp::app()->getCurrentView(); if (currentView == NULL) { if (m_pCurrentRoot) return m_pCurrentRoot; kError() << "UMLDoc::currentRoot: m_pCurrentRoot is NULL" << endl; return NULL; } UMLFolder *f = currentView->getFolder(); while (f->getUMLPackage()) { f = static_cast(f->getUMLPackage()); } return f; } void UMLDoc::setCurrentRoot(Uml::Model_Type rootType) { m_pCurrentRoot = m_root[rootType]; } void UMLDoc::removeUMLObject(UMLObject* umlobject) { UMLApp::app()->getDocWindow()->updateDocumentation(true); Object_Type type = umlobject->getBaseType(); umlobject->setUMLStereotype(NULL); // triggers possible cleanup of UMLStereotype if (dynamic_cast(umlobject)) { UMLClassifier* parent = dynamic_cast(umlobject->parent()); if (parent == NULL) { kError() << "UMLDoc::removeUMLObject: parent of umlobject is NULL" << endl; return; } if (type == ot_Operation) { parent->removeOperation(static_cast(umlobject)); } else if (type == ot_EnumLiteral) { UMLEnum *e = static_cast(parent); e->removeEnumLiteral(static_cast(umlobject)); } else if (type == ot_EntityAttribute) { UMLEntity *ent = static_cast(parent); ent->removeEntityAttribute(static_cast(umlobject)); } else { UMLClassifier* pClass = dynamic_cast(parent); if (pClass == NULL) { kError() << "UMLDoc::removeUMLObject: parent of umlobject has " << "unexpected type " << parent->getBaseType() << endl; return; } if (type == ot_Attribute) { pClass->removeAttribute(static_cast(umlobject)); } else if (type == ot_Template) { pClass->removeTemplate(static_cast(umlobject)); } else { kError() << "UMLDoc::removeUMLObject: umlobject has " << "unexpected type " << type << endl; } } } else { if (type == ot_Association) { UMLAssociation *a = (UMLAssociation *)umlobject; removeAssociation(a, false); // don't call setModified here, it's done below } else { UMLPackage* pkg = umlobject->getUMLPackage(); if (pkg) { pkg->removeObject(umlobject); } else { kError() << "UMLDoc::removeUMLObject(" << umlobject->getName() << "): parent package is not set !" << endl; } } emit sigObjectRemoved(umlobject); } setModified(true); } void UMLDoc::signalUMLObjectCreated(UMLObject * o) { emit sigObjectCreated(o); /* This is the wrong place to do: setModified(true); Instead, that should be done by the callers when object creation and all its side effects (e.g. new widget in view, new list view item, etc.) is finalized. */ } void UMLDoc::setName(const TQString& name) { m_Name = name; } TQString UMLDoc::getName() const { return m_Name; } Uml::IDType UMLDoc::getModelID() const { return m_modelID; } void UMLDoc::saveToXMI(TQIODevice& file) { TQDomDocument doc; TQDomProcessingInstruction xmlHeading = doc.createProcessingInstruction("xml", "version=\"1.0\" encoding=\"UTF-8\""); doc.appendChild(xmlHeading); TQDomElement root = doc.createElement( "XMI" ); root.setAttribute( "xmi.version", "1.2" ); TQDateTime now = TQDateTime::currentDateTime(); root.setAttribute( "timestamp", now.toString(Qt::ISODate)); root.setAttribute( "verified", "false"); root.setAttribute( "xmlns:UML", "http://schema.omg.org/spec/UML/1.3"); doc.appendChild( root ); TQDomElement header = doc.createElement( "XMI.header" ); TQDomElement meta = doc.createElement( "XMI.metamodel" ); meta.setAttribute( "xmi.name", "UML" ); meta.setAttribute( "xmi.version", "1.3" ); meta.setAttribute( "href", "UML.xml" ); header.appendChild( meta ); /** * bugs.kde.org/56184 comment by M. Alanen 2004-12-19: * " XMI.model requires xmi.version. (or leave the whole XMI.model out, * it's not required) " TQDomElement model = doc.createElement( "XMI.model" ); TQFile* qfile = dynamic_cast(&file); if (qfile) { TQString modelName = qfile->name(); modelName = modelName.section('/', -1 ); modelName = modelName.section('.', 0, 0); model.setAttribute( "xmi.name", modelName ); model.setAttribute( "href", qfile->name() ); } */ TQDomElement documentation = doc.createElement( "XMI.documentation" ); // If we consider it useful we might add user and contact details // TQDomElement owner = doc.createElement( "XMI.owner" ); // owner.appendChild( doc.createTextNode( "Jens Kruger" ) ); // Add a User // documentation.appendChild( owner ); // TQDomElement contact = doc.createElement( "XMI.contact" ); // contact.appendChild( doc.createTextNode( "je.krueger@web.de" ) ); // add a contact // documentation.appendChild( contact ); TQDomElement exporter = doc.createElement( "XMI.exporter" ); exporter.appendChild( doc.createTextNode( "umbrello uml modeller http://uml.sf.net" ) ); documentation.appendChild( exporter ); TQDomElement exporterVersion = doc.createElement( "XMI.exporterVersion" ); exporterVersion.appendChild( doc.createTextNode( XMI_FILE_VERSION ) ); documentation.appendChild( exporterVersion ); // all files are now saved with correct Unicode encoding, we add this // information to the header, so that the file will be loaded correctly TQDomElement exporterEncoding = doc.createElement( "XMI.exporterEncoding" ); exporterEncoding.appendChild( doc.createTextNode( "UnicodeUTF8" ) ); documentation.appendChild( exporterEncoding ); header.appendChild( documentation ); /** * See comment on above header.appendChild( model ); */ header.appendChild( meta ); root.appendChild( header ); TQDomElement content = doc.createElement( "XMI.content" ); TQDomElement contentNS = doc.createElement( "UML:Namespace.contents" ); TQDomElement objectsElement = doc.createElement( "UML:Model" ); objectsElement.setAttribute( "xmi.id", ID2STR(m_modelID) ); objectsElement.setAttribute( "name", m_Name ); objectsElement.setAttribute( "isSpecification", "false" ); objectsElement.setAttribute( "isAbstract", "false" ); objectsElement.setAttribute( "isRoot", "false" ); objectsElement.setAttribute( "isLeaf", "false" ); TQDomElement ownedNS = doc.createElement( "UML:Namespace.ownedElement" ); // Save stereotypes and toplevel datatypes first so that upon loading // they are known first. // There is a bug causing duplication of the same stereotype in m_stereoList. // As a workaround, we use a string list to memorize which stereotype has been saved. TQStringList stereoNames; TQValueList stereoIDs; for (UMLStereotype *s = m_stereoList.first(); s; s = m_stereoList.next() ) { TQString stName = s->getName(); Uml::IDType stID = s->getID(); if (!stereoNames.contains(stName) && !stereoIDs.contains(stID)) { s->saveToXMI(doc, ownedNS); stereoNames.append(stName); stereoIDs.append(stID); } else { kDebug() << "UMLDoc::saveToXMI: encountered duplicated stereotype " << stName << " (id " << ID2STR(stID) << "), see bug 144924" << endl; } } for (int i = 0; i < Uml::N_MODELTYPES; i++) { m_root[i]->saveToXMI(doc, ownedNS); } objectsElement.appendChild( ownedNS ); content.appendChild( objectsElement ); root.appendChild( content ); // Save the XMI extensions: docsettings, diagrams, listview, and codegeneration. TQDomElement extensions = doc.createElement( "XMI.extensions" ); extensions.setAttribute( "xmi.extender", "umbrello" ); TQDomElement docElement = doc.createElement( "docsettings" ); Uml::IDType viewID = Uml::id_None; UMLView *currentView = UMLApp::app()->getCurrentView(); if (currentView) viewID = currentView->getID(); docElement.setAttribute( "viewid", ID2STR(viewID) ); docElement.setAttribute( "documentation", m_Doc ); docElement.setAttribute( "uniqueid", ID2STR(UniqueID::get()) ); extensions.appendChild( docElement ); // save listview UMLApp::app()->getListView()->saveToXMI(doc, extensions); // save code generator CodeGenerator *codegen = UMLApp::app()->getGenerator(); if (codegen) { TQDomElement codeGenElement = doc.createElement( "codegeneration" ); codegen->saveToXMI( doc, codeGenElement ); extensions.appendChild( codeGenElement ); } root.appendChild( extensions ); TQTextStream stream( &file ); stream.setEncoding(TQTextStream::UnicodeUTF8); stream << doc.toString(); } short UMLDoc::getEncoding(TQIODevice & file) { TQTextStream stream( &file ); stream.setEncoding(TQTextStream::UnicodeUTF8); TQString data = stream.read(); TQString error; int line; TQDomDocument doc; if( !doc.setContent( data, false, &error, &line ) ) { kWarning()<<"Can't set content: "<processEvents(); // give UI events a chance TQString error; int line; TQDomDocument doc; if( !doc.setContent( data, false, &error, &line ) ) { kWarning()<<"Can't set content:"<processEvents(); // give UI events a chance TQDomNode node = doc.firstChild(); //Before Umbrello 1.1-rc1 we didn't add a " << endl; continue; } bool seen_UMLObjects = false; //process content for (TQDomNode child = node.firstChild(); !child.isNull(); child = child.nextSibling()) { if (child.isComment()) continue; element = child.toElement(); TQString tag = element.tagName(); if (tag == "umlobjects" // for bkwd compat. || tagEq(tag, "Subsystem") || tagEq(tag, "Model") ) { if( !loadUMLObjectsFromXMI( element ) ) { kWarning() << "failed load on objects" << endl; return false; } m_Name = element.attribute( "name", i18n("UML Model") ); UMLListView *lv = UMLApp::app()->getListView(); lv->setColumnText(0, m_Name); seen_UMLObjects = true; } else if (tagEq(tag, "Package") || tagEq(tag, "Class") || tagEq(tag, "Interface")) { // These tests are only for foreign XMI files that // are missing the tag (e.g. NSUML) TQDomElement parentElem = node.toElement(); if( !loadUMLObjectsFromXMI( parentElem ) ) { kWarning() << "failed load on model objects" << endl; return false; } seen_UMLObjects = true; } else if (tagEq(tag, "TaggedValue")) { // This tag is produced here, i.e. outside of , // by the Unisys.JCR.1 Rose-to-XMI tool. if (! seen_UMLObjects) { kDebug() << "skipping TaggedValue because not seen_UMLObjects" << endl; continue; } tag = element.attribute("tag", ""); if (tag != "documentation") { continue; } TQString modelElement = element.attribute("modelElement", ""); if (modelElement.isEmpty()) { kDebug() << "skipping TaggedValue(documentation) because " << "modelElement.isEmpty()" << endl; continue; } UMLObject *o = findObjectById(STR2ID(modelElement)); if (o == NULL) { kDebug() << "TaggedValue(documentation): cannot find object" << " for modelElement " << modelElement << endl; continue; } TQString value = element.attribute("value", ""); if (! value.isEmpty()) o->setDoc(value); } else { // for backward compatibility loadExtensionsFromXMI(child); } } } #ifdef VERBOSE_DEBUGGING kDebug() << "UMLDoc::m_objectList.count() is " << m_objectList.count() << endl; #endif resolveTypes(); // set a default code generator if no tag seen if (UMLApp::app()->getGenerator() == NULL) UMLApp::app()->setGenerator(UMLApp::app()->getDefaultLanguage()); emit sigWriteToStatusBar( i18n("Setting up the document...") ); kapp->processEvents(); // give UI events a chance activateAllViews(); UMLView *viewToBeSet = NULL; if (m_nViewID != Uml::id_None) viewToBeSet = findView( m_nViewID ); if (viewToBeSet) { changeCurrentView( m_nViewID ); Settings::OptionState optionState = Settings::getOptionState(); if (optionState.generalState.tabdiagrams) { UMLApp::app()->tabWidget()->showPage(viewToBeSet); } } else { createDiagram(m_root[mt_Logical], Uml::dt_Class, false); m_pCurrentRoot = m_root[mt_Logical]; } emit sigResetStatusbarProgress(); return true; } void UMLDoc::resolveTypes() { // Resolve the types. // This is done in a separate pass because of possible forward references. if (m_bTypesAreResolved) return; m_bTypesAreResolved = true; writeToStatusBar( i18n("Resolving object references...") ); for (int i = 0; i < Uml::N_MODELTYPES; i++) { UMLFolder *obj = m_root[i]; #ifdef VERBOSE_DEBUGGING kDebug() << "UMLDoc: invoking resolveRef() for " << obj->getName() << " (id=" << ID2STR(obj->getID()) << ")" << endl; #endif obj->resolveRef(); } kapp->processEvents(); // give UI events a chance } bool UMLDoc::validateXMIHeader(TQDomNode& headerNode) { TQDomElement headerElement = headerNode.toElement(); while ( !headerNode.isNull() ) { /* //Seems older Umbrello files used a different metamodel, so don't validate it for now if( !headerElement.isNull() && headerElement.tagName() == "XMI.metamodel" ) { String metamodel = headerElement.attribute("xmi.name", ""); if (metamodel != "UML") { return false; } } */ headerNode = headerNode.nextSibling(); headerElement = headerNode.toElement(); } return true; } bool UMLDoc::loadUMLObjectsFromXMI(TQDomElement& element) { /* FIXME need a way to make status bar actually reflect how much of the file has been loaded rather than just counting to 10 (an arbitrary number) emit sigResetStatusbarProgress(); emit sigSetStatusbarProgress( 0 ); emit sigSetStatusbarProgressSteps( 10 ); m_count = 0; */ emit sigWriteToStatusBar( i18n("Loading UML elements...") ); for (TQDomNode node = element.firstChild(); !node.isNull(); node = node.nextSibling()) { if (node.isComment()) continue; TQDomElement tempElement = node.toElement(); TQString type = tempElement.tagName(); if (tagEq(type, "Model")) { bool foundUmbrelloRootFolder = false; TQString name = tempElement.attribute("name"); for (int i = 0; i < Uml::N_MODELTYPES; i++) { if (name == m_root[i]->getName()) { m_pCurrentRoot = m_root[i]; m_root[i]->loadFromXMI(tempElement); foundUmbrelloRootFolder = true; break; } } if (foundUmbrelloRootFolder) continue; } // From here on, it's support for stereotypes, pre 1.5.5 versions, and foreign files if (tagEq(type, "Namespace.ownedElement") || tagEq(type, "Namespace.contents") || tagEq(type, "Model")) { //CHECK: Umbrello currently assumes that nested elements // are ownedElements anyway. // Therefore the tag is of no // significance. if( !loadUMLObjectsFromXMI( tempElement ) ) { kWarning() << "failed load on " << type << endl; return false; } continue; } if (Model_Utils::isCommonXMIAttribute(type)) continue; if (! tempElement.hasAttribute("xmi.id")) { TQString idref = tempElement.attribute("xmi.idref", ""); if (! idref.isEmpty()) { kDebug() << "resolution of xmi.idref " << idref << " is not yet implemented" << endl; } else { kError() << "Cannot load " << type << " because xmi.id is missing" << endl; } continue; } TQString stID = tempElement.attribute("stereotype", ""); UMLObject *pObject = Object_Factory::makeObjectFromXMI(type, stID); if( !pObject ) { kWarning() << "Unknown type of umlobject to create: " << type << endl; // We want a best effort, therefore this is handled as a // soft error. continue; } Uml::Object_Type ot = pObject->getBaseType(); // Set the parent root folder. UMLPackage *pkg = NULL; if (ot == Uml::ot_Datatype) { pkg = m_datatypeRoot; } else { Uml::Model_Type guess = Model_Utils::guessContainer(pObject); if (guess != Uml::N_MODELTYPES) pkg = m_root[guess]; } pObject->setUMLPackage(pkg); bool status = pObject -> loadFromXMI( tempElement ); if ( !status ) { //delete pObject; // Unfortunately we cannot do this because the pObject // may be still referenced by other model objects. kError() << "loadFromXMI failed for " << pObject->getName() << " xmi.id=" << ID2STR(pObject->getID()) << endl; continue; } pkg = pObject->getUMLPackage(); if (ot == ot_Stereotype) { UMLStereotype *s = static_cast(pObject); UMLStereotype *exist = findStereotype(pObject->getName()); if (exist) { if (exist->getID() == pObject->getID()) { delete pObject; } else { kDebug() << "Stereotype " << pObject->getName() << "(id=" << ID2STR(pObject->getID()) << ") already exists with id=" << ID2STR(exist->getID()) << endl; addStereotype(s); } } else { addStereotype(s); } continue; } if (pkg == NULL) kError() << "UMLDoc::loadUMLObjectsFromXMI: pkg is NULL for " << pObject->getName() << " xmi.id=" << ID2STR(pObject->getID()) << endl; else pkg->addObject(pObject); /* FIXME see comment at loadUMLObjectsFromXMI emit sigSetStatusbarProgress( ++m_count ); */ } return true; } void UMLDoc::setMainViewID(Uml::IDType viewID) { m_nViewID = viewID; } void UMLDoc::loadExtensionsFromXMI(TQDomNode& node) { TQDomElement element = node.toElement(); TQString tag = element.tagName(); if (tag == "docsettings") { TQString viewID = element.attribute( "viewid", "-1" ); m_Doc = element.attribute( "documentation", "" ); TQString uniqueid = element.attribute( "uniqueid", "0" ); m_nViewID = STR2ID(viewID); UniqueID::set(STR2ID(uniqueid)); UMLApp::app()->getDocWindow() -> newDocumentation(); } else if (tag == "diagrams" || tag == "UISModelElement") { // For backward compatibility only: // Since version 1.5.5 diagrams are saved as part of the UMLFolder. TQDomNode diagramNode = node.firstChild(); if (tag == "UISModelElement") { // Unisys.IntegratePlus.2 element = diagramNode.toElement(); tag = element.tagName(); if (tag != "uisOwnedDiagram") { kError() << "unknown child node " << tag << endl; return; } diagramNode = diagramNode.firstChild(); } if( !loadDiagramsFromXMI( diagramNode ) ) { kWarning() << "failed load on diagrams" << endl; } } else if (tag == "listview") { //FIXME: Need to resolveTypes() before loading listview, // else listview items are duplicated. resolveTypes(); if( !UMLApp::app()->getListView() -> loadFromXMI( element ) ) { kWarning() << "failed load on listview" << endl; } } else if (tag == "codegeneration") { TQDomNode cgnode = node.firstChild(); TQDomElement cgelement = cgnode.toElement(); while( !cgelement.isNull() ) { TQString nodeName = cgelement.tagName(); TQString lang = cgelement.attribute("language","UNKNOWN"); Uml::Programming_Language pl = Model_Utils::stringToProgLang(lang); CodeGenerator *g = UMLApp::app()->setGenerator(pl); g->loadFromXMI(cgelement); cgnode = cgnode.nextSibling(); cgelement = cgnode.toElement(); } if (UMLApp::app()->getGenerator() == NULL) UMLApp::app()->setGenerator(UMLApp::app()->getDefaultLanguage()); } } // For backward compatibility only: // Since version 1.5.5 diagrams are saved as part of the UMLFolder. bool UMLDoc::loadDiagramsFromXMI( TQDomNode & node ) { emit sigWriteToStatusBar( i18n("Loading diagrams...") ); emit sigResetStatusbarProgress(); emit sigSetStatusbarProgress( 0 ); emit sigSetStatusbarProgressSteps( 10 ); //FIX ME TQDomElement element = node.toElement(); if( element.isNull() ) return true;//return ok as it means there is no umlobjects const Settings::OptionState state = Settings::getOptionState(); UMLView * pView = 0; int count = 0; while( !element.isNull() ) { TQString tag = element.tagName(); if (tag == "diagram" || tag == "UISDiagram") { pView = new UMLView(NULL); // IMPORTANT: Set OptionState of new UMLView _BEFORE_ // reading the corresponding diagram: // + allow using per-diagram color and line-width settings // + avoid crashes due to uninitialized values for lineWidth pView -> setOptionState( state ); bool success = false; if (tag == "UISDiagram") { success = pView->loadUISDiagram(element); } else { success = pView->loadFromXMI(element); } if (!success) { kWarning() << "failed load on viewdata loadfromXMI" << endl; delete pView; return false; } // Put diagram in default predefined folder. // @todo pass in the parent folder - it might be a user defined one. Uml::Model_Type mt = Model_Utils::convert_DT_MT(pView->getType()); pView->setFolder(m_root[mt]); pView -> hide(); addView( pView ); emit sigSetStatusbarProgress( ++count ); kapp->processEvents(); // give UI events a chance } node = node.nextSibling(); element = node.toElement(); } return true; } void UMLDoc::removeAllViews() { for (int i = 0; i < Uml::N_MODELTYPES; i++) m_root[i]->removeAllViews(); UMLApp::app()->setCurrentView(NULL); emit sigDiagramChanged(dt_Undefined); UMLApp::app()->setDiagramMenuItemsState(false); } UMLClassifierList UMLDoc::getConcepts(bool includeNested /* =true */) { UMLClassifierList conceptList; m_root[mt_Logical]->appendClassifiers(conceptList, includeNested); return conceptList; } UMLClassifierList UMLDoc::getClasses(bool includeNested /* =true */) { UMLClassifierList conceptList; m_root[mt_Logical]->appendClasses(conceptList, includeNested); return conceptList; } UMLClassifierList UMLDoc::getClassesAndInterfaces(bool includeNested /* =true */) { UMLClassifierList conceptList; m_root[mt_Logical]->appendClassesAndInterfaces(conceptList, includeNested); return conceptList; } UMLClassifierList UMLDoc::getInterfaces(bool includeNested /* =true */) { UMLClassifierList interfaceList; m_root[mt_Logical]->appendInterfaces(interfaceList, includeNested); return interfaceList; } UMLClassifierList UMLDoc::getDatatypes() { UMLObjectList objects = m_datatypeRoot->containedObjects(); UMLClassifierList datatypeList; UMLObject *obj; for (UMLObjectListIt oit(objects); (obj = oit.current()) != NULL; ++oit) { if (obj->getBaseType() == ot_Datatype) { datatypeList.append(static_cast(obj)); } } return datatypeList; } UMLAssociationList UMLDoc::getAssociations() { UMLAssociationList associationList; for (int i = 0; i < Uml::N_MODELTYPES; i++) { UMLAssociationList assocs = m_root[i]->getAssociations(); UMLAssociation *a; for (UMLAssociationListIt ait(assocs); (a = ait.current()) != NULL; ++ait) associationList.append(a); } return associationList; } void UMLDoc::print(KPrinter * pPrinter) { UMLView * printView = 0; int count = TQString(pPrinter -> option("kde-uml-count")).toInt(); TQPainter painter(pPrinter); for(int i = 0;i < count;i++) { if(i>0) pPrinter -> newPage(); TQString diagram = i18n("kde-uml-Diagram") + TQString("%1").arg(i); TQString sID = pPrinter -> option(diagram); Uml::IDType id = STR2ID(sID); printView = findView(id); if(printView) printView ->print(pPrinter, painter); printView = 0; } painter.end(); } UMLViewList UMLDoc::getViewIterator() { UMLViewList accumulator; for (int i = 0; i < Uml::N_MODELTYPES; i++) m_root[i]->appendViews(accumulator, true); return accumulator; } void UMLDoc::setModified(bool modified /*=true*/, bool addToUndo /*=true*/) { if(!m_bLoading) { m_modified = modified; UMLApp::app()->setModified(modified); if (modified && addToUndo) { addToUndoStack(); clearRedoStack(); } } } bool UMLDoc::assignNewIDs(UMLObject* Obj) { if(!Obj || !m_pChangeLog) { kDebug() << "no Obj || Changelog" << endl; return false; } Uml::IDType result = assignNewID(Obj->getID()); Obj->setID(result); //If it is a CONCEPT then change the ids of all its operations and attributes if(Obj->getBaseType() == ot_Class ) { UMLClassifier *c = static_cast(Obj); UMLClassifierListItemList attributes = c->getFilteredList(ot_Attribute); for(UMLObject* listItem = attributes.first(); listItem; listItem = attributes.next()) { result = assignNewID(listItem->getID()); listItem->setID(result); } UMLClassifierListItemList templates = c->getFilteredList(ot_Template); for(UMLObject* listItem = templates.first(); listItem; listItem = templates.next()) { result = assignNewID(listItem->getID()); listItem->setID(result); } } if(Obj->getBaseType() == ot_Interface || Obj->getBaseType() == ot_Class ) { UMLOperationList operations(((UMLClassifier*)Obj)->getOpList()); for(UMLObject* listItem = operations.first(); listItem; listItem = operations.next()) { result = assignNewID(listItem->getID()); listItem->setID(result); } } setModified(true); return true; } UMLFolder *UMLDoc::getRootFolder(Uml::Model_Type mt) { if (mt < Uml::mt_Logical || mt >= Uml::N_MODELTYPES) { kError() << "UMLDoc::getRootFolder: illegal input value " << mt << endl; return NULL; } return m_root[mt]; } Uml::Model_Type UMLDoc::rootFolderType(UMLObject *obj) { for (int i = 0; i < Uml::N_MODELTYPES; i++) { const Uml::Model_Type m = (Uml::Model_Type)i; if (obj == m_root[m]) return m; } return Uml::N_MODELTYPES; } /** Read property of IDChangeLog* m_pChangeLog. */ IDChangeLog* UMLDoc::getChangeLog() { return m_pChangeLog; } /** Opens a Paste session, Deletes the Old ChangeLog and Creates an empty one */ void UMLDoc::beginPaste() { if(m_pChangeLog) { delete m_pChangeLog; m_pChangeLog = 0; } m_pChangeLog = new IDChangeLog; } /** Closes a Paste session, Deletes the ChangeLog */ void UMLDoc::endPaste() { if(m_pChangeLog) { delete m_pChangeLog; m_pChangeLog = 0; } } /** Assigns a New ID to an Object, and also logs the assignment to its internal ChangeLog */ Uml::IDType UMLDoc::assignNewID(Uml::IDType OldID) { Uml::IDType result = UniqueID::gen(); if (m_pChangeLog) { m_pChangeLog->addIDChange(OldID, result); } return result; } /** Adds an already created UMLView to the document, it gets assigned a new ID. If its name is already in use then the function appends a number to it to differentiate it from the others; this number is incremental so if number 1 is in use then it tries 2 and then 3 and so on */ bool UMLDoc::addUMLView(UMLView * pView ) { if(!pView || !m_pChangeLog) return false; int i = 0; TQString viewName = (TQString)pView->getName(); TQString name = viewName; while( findView(pView->getType(), name) != NULL) { name = viewName + '_' + TQString::number(++i); } if(i) //If name was modified pView->setName(name); Uml::IDType result = assignNewID(pView->getID()); pView->setID(result); pView->activateAfterLoad( true ); pView->endPartialWidgetPaste(); pView->setOptionState( Settings::getOptionState() ); addView(pView); setModified(true); return true; } void UMLDoc::activateAllViews() { // store old setting - for restore of last setting bool m_bLoading_old = m_bLoading; m_bLoading = true; //this is to prevent document becoming modified when activating a view for (int i = 0; i < Uml::N_MODELTYPES; i++) m_root[i]->activateViews(); m_bLoading = m_bLoading_old; } void UMLDoc::settingsChanged(Settings::OptionState optionState) { for (int i = 0; i < Uml::N_MODELTYPES; i++) m_root[i]->setViewOptions(optionState); initSaveTimer(); } void UMLDoc::initSaveTimer() { if( m_pAutoSaveTimer ) { m_pAutoSaveTimer -> stop(); disconnect( m_pAutoSaveTimer, TQT_SIGNAL( timeout() ), this, TQT_SLOT( slotAutoSave() ) ); delete m_pAutoSaveTimer; m_pAutoSaveTimer = 0; } Settings::OptionState optionState = Settings::getOptionState(); if( optionState.generalState.autosave ) { m_pAutoSaveTimer = new TQTimer(this, "_AUTOSAVETIMER_" ); connect( m_pAutoSaveTimer, TQT_SIGNAL( timeout() ), this, TQT_SLOT( slotAutoSave() ) ); m_pAutoSaveTimer->start( optionState.generalState.autosavetime * 60000, false ); } return; } void UMLDoc::slotAutoSave() { //Only save if modified. if( !m_modified ) { return; } KURL tempURL = m_doc_url; if( tempURL.fileName() == i18n("Untitled") ) { tempURL.setPath( TQDir::homeDirPath() + i18n("/autosave%1").arg(".xmi") ); saveDocument( tempURL ); m_doc_url.setFileName( i18n("Untitled") ); m_modified = true; UMLApp::app()->setModified( m_modified ); } else { // 2004-05-17 Achim Spangler KURL orgDocUrl = m_doc_url; TQString orgFileName = m_doc_url.fileName(); // don't overwrite manually saved file with autosave content TQString fileName = tempURL.fileName(); Settings::OptionState optionState = Settings::getOptionState(); fileName.replace( ".xmi", optionState.generalState.autosavesuffix ); tempURL.setFileName( fileName ); // End Achim Spangler saveDocument( tempURL ); // 2004-05-17 Achim Spangler // re-activate m_modified if autosave is writing to other file // than the main project file -> autosave-suffix != ".xmi" if ( ".xmi" != optionState.generalState.autosavesuffix ) { m_modified = true; UMLApp::app()->setModified( m_modified ); } // restore original file name - // UMLDoc::saveDocument() sets doc_url to filename which is given as autosave-filename setURL( orgDocUrl ); UMLApp * pApp = UMLApp::app(); pApp->setCaption(orgFileName, isModified() ); // End Achim Spangler } } void UMLDoc::signalDiagramRenamed(UMLView* pView ) { Settings::OptionState optionState = Settings::getOptionState(); if (optionState.generalState.tabdiagrams) UMLApp::app()->tabWidget()->setTabLabel( pView, pView->getName() ); emit sigDiagramRenamed( pView -> getID() ); } void UMLDoc::addToUndoStack() { Settings::OptionState optionState = Settings::getOptionState(); if (!m_bLoading && optionState.generalState.undo) { TQBuffer* buffer = new TQBuffer(); buffer->open(IO_WriteOnly); TQDataStream* undoData = new TQDataStream(); undoData->setDevice(buffer); saveToXMI(*TQT_TQIODEVICE(buffer)); buffer->close(); undoStack.prepend(undoData); if (undoStack.count() > 1) { UMLApp::app()->enableUndo(true); } } } void UMLDoc::clearUndoStack() { undoStack.setAutoDelete(true); undoStack.clear(); UMLApp::app()->enableRedo(false); undoStack.setAutoDelete(false); clearRedoStack(); } void UMLDoc::clearRedoStack() { redoStack.setAutoDelete(true); redoStack.clear(); UMLApp::app()->enableRedo(false); redoStack.setAutoDelete(false); } void UMLDoc::loadUndoData() { if (undoStack.count() < 1) { kWarning() << "no data in undostack" << endl; return; } UMLView *currentView = UMLApp::app()->getCurrentView(); if (currentView == NULL) { kWarning() << "UMLDoc::loadUndoData: currentView is NULL" << endl; undoStack.setAutoDelete(true); undoStack.clear(); undoStack.setAutoDelete(false); UMLApp::app()->enableUndo(false); return; } Uml::IDType currentViewID = currentView->getID(); // store old setting - for restore of last setting bool m_bLoading_old = m_bLoading; m_bLoading = true; closeDocument(); redoStack.prepend( undoStack.take(0) ); TQDataStream* undoData = undoStack.getFirst(); TQBuffer* buffer = static_cast( undoData->device() ); buffer->open(IO_ReadOnly); loadFromXMI(*TQT_TQIODEVICE(buffer)); buffer->close(); setModified(true, false); m_bLoading = m_bLoading_old; undoStack.setAutoDelete(true); if (undoStack.count() <= 1) { UMLApp::app()->enableUndo(false); } if (redoStack.count() >= 1) { UMLApp::app()->enableRedo(true); } while (undoStack.count() > undoMax) { undoStack.removeLast(); } undoStack.setAutoDelete(false); currentView = UMLApp::app()->getCurrentView(); if (currentView) { if (currentView->getID() != currentViewID) changeCurrentView( currentView->getID() ); currentView->resizeCanvasToItems(); } } void UMLDoc::loadRedoData() { if (redoStack.count() >= 1) { UMLView *currentView = UMLApp::app()->getCurrentView(); Uml::IDType currentViewID = currentView->getID(); // store old setting - for restore of last setting bool m_bLoading_old = m_bLoading; m_bLoading = true; closeDocument(); undoStack.prepend( redoStack.getFirst() ); TQDataStream* redoData = redoStack.getFirst(); redoStack.removeFirst(); TQBuffer* buffer = static_cast( redoData->device() ); buffer->open(IO_ReadOnly); loadFromXMI(*TQT_TQIODEVICE(buffer)); buffer->close(); setModified(true, false); currentView = UMLApp::app()->getCurrentView(); currentView->resizeCanvasToItems(); m_bLoading = m_bLoading_old; redoStack.setAutoDelete(true); if (redoStack.count() < 1) { UMLApp::app()->enableRedo(false); } if (undoStack.count() > 1) { UMLApp::app()->enableUndo(true); } if (currentView->getID() != currentViewID) { changeCurrentView(currentViewID); } redoStack.setAutoDelete(false); } else { kWarning() << "no data in redostack" << endl; } } void UMLDoc::addDefaultDatatypes() { CodeGenerator *cg = UMLApp::app()->getGenerator(); if (cg == NULL) { kDebug() << "UMLDoc::addDefaultDatatypes: CodeGenerator is still NULL" << endl; return; } TQStringList entries = cg->defaultDatatypes(); TQStringList::Iterator end(entries.end()); for (TQStringList::Iterator it = entries.begin(); it != end; ++it) createDatatype(*it); } void UMLDoc::createDatatype(const TQString &name) { UMLObjectList datatypes = m_datatypeRoot->containedObjects(); UMLObject* umlobject = Model_Utils::findUMLObject(datatypes, name, ot_Datatype, m_datatypeRoot); if (!umlobject) { Object_Factory::createUMLObject(ot_Datatype, name, m_datatypeRoot); } UMLApp::app()->getListView()->closeDatatypesFolder(); } void UMLDoc::slotDiagramPopupMenu(TQWidget* umlview, const TQPoint& point) { UMLView* view = (UMLView*) umlview; if(m_pTabPopupMenu != 0) { m_pTabPopupMenu->hide(); delete m_pTabPopupMenu; m_pTabPopupMenu = 0; } Settings::OptionState optionState = Settings::getOptionState(); if (! optionState.generalState.tabdiagrams) return; Uml::ListView_Type type = lvt_Unknown; switch( view->getType() ) { case dt_Class: type = lvt_Class_Diagram; break; case dt_UseCase: type = lvt_UseCase_Diagram; break; case dt_Sequence: type = lvt_Sequence_Diagram; break; case dt_Collaboration: type = lvt_Collaboration_Diagram; break; case dt_State: type = lvt_State_Diagram; break; case dt_Activity: type = lvt_Activity_Diagram; break; case dt_Component: type = lvt_Component_Diagram; break; case dt_Deployment: type = lvt_Deployment_Diagram; break; case dt_EntityRelationship: type = lvt_EntityRelationship_Diagram; break; default: kWarning() << "unknown diagram type in slotDiagramPopupMenu()" << endl; break; }//end switch m_pTabPopupMenu = new ListPopupMenu(UMLApp::app()->getMainViewWidget(), type); m_pTabPopupMenu->popup(point); connect(m_pTabPopupMenu, TQT_SIGNAL(activated(int)), view, TQT_SLOT(slotMenuSelection(int))); } void UMLDoc::addDefaultStereotypes() { CodeGenerator *gen = UMLApp::app()->getGenerator(); if (gen) gen->createDefaultStereotypes(); } const UMLStereotypeList& UMLDoc::getStereotypes() { return m_stereoList; } #include "umldoc.moc"