diff --git a/umbrello/dialogs/umlforeignkeyconstraintdialog.cpp b/umbrello/dialogs/umlforeignkeyconstraintdialog.cpp index 40e1a5a4d..5b49bca0c 100644 --- a/umbrello/dialogs/umlforeignkeyconstraintdialog.cpp +++ b/umbrello/dialogs/umlforeignkeyconstraintdialog.cpp @@ -1,478 +1,481 @@ /*************************************************************************** * 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) 2003-2014 * * Umbrello UML Modeller Authors * ***************************************************************************/ #include "umlforeignkeyconstraintdialog.h" #include "attribute.h" #include "classifier.h" #include "classifierlistitem.h" #include "debug_utils.h" #include "dialog_utils.h" #include "entityattribute.h" #include "enumliteral.h" #include "enum.h" #include "entity.h" #include "foreignkeyconstraint.h" #include "object_factory.h" #include "operation.h" #include "template.h" #include "uml.h" #include "umldoc.h" #include "umlentitylist.h" #include "uniqueconstraint.h" #include "icon_utils.h" #include #if QT_VERSION < 0x050000 #include #endif #include #include #include #include #include #include #include #include #include #include #include typedef QPair EntityAttributePair; /** * Sets up the UMLForeignKeyConstraintDialog * * @param parent The parent to the UMLForeignKeyConstraintDialog. * @param pForeignKeyConstraint The Unique Constraint to show the properties of */ UMLForeignKeyConstraintDialog::UMLForeignKeyConstraintDialog(QWidget* parent, UMLForeignKeyConstraint* pForeignKeyConstraint) : MultiPageDialogBase(parent), m_doc(UMLApp::app()->document()), m_pForeignKeyConstraint(pForeignKeyConstraint) { setCaption(i18n("Foreign Key Setup")); setupGeneralPage(); setupColumnPage(); connect(this, SIGNAL(okClicked()), this, SLOT(slotOk())); connect(this, SIGNAL(applyClicked()), this, SLOT(slotApply())); } /** * Standard destructor. */ UMLForeignKeyConstraintDialog::~UMLForeignKeyConstraintDialog() { } /** * Adds pair to the list. */ void UMLForeignKeyConstraintDialog::slotAddPair() { // get the index of the selected local column and referenced column int indexL = m_ColumnWidgets.localColumnCB->currentIndex(); int indexR = m_ColumnWidgets.referencedColumnCB->currentIndex(); if (indexL == -1 || indexR == -1) { return; } // local entity attribute UMLEntityAttribute* localColumn = m_pLocalAttributeList.at(indexL); // referenced entity attribute UMLEntityAttribute* referencedColumn = m_pReferencedAttributeList.at(indexR); // remove from combo boxes m_ColumnWidgets.localColumnCB->removeItem(indexL); m_ColumnWidgets.referencedColumnCB->removeItem(indexR); // remove from local cache m_pLocalAttributeList.removeAt(indexL); m_pReferencedAttributeList.removeAt(indexR); // add to local cache of mapping EntityAttributePair pair = qMakePair(localColumn, referencedColumn); m_pAttributeMapList.append(pair); // update mapping view QTreeWidgetItem* mapping = new QTreeWidgetItem(m_ColumnWidgets.mappingTW); mapping->setText(0, localColumn->toString(Uml::SignatureType::SigNoVis)); mapping->setText(1, referencedColumn->toString(Uml::SignatureType::SigNoVis)); m_ColumnWidgets.mappingTW->addTopLevelItem(mapping); slotResetWidgetState(); } /** * Deletes a pair from the list. */ void UMLForeignKeyConstraintDialog::slotDeletePair() { // get the index of the selected pair in the view QTreeWidgetItem* twi = m_ColumnWidgets.mappingTW->currentItem(); int indexP = m_ColumnWidgets.mappingTW->indexOfTopLevelItem(twi); if (indexP == -1) { return; } //find pair in local cache EntityAttributePair pair = m_pAttributeMapList.at(indexP); // remove them from the view and the list m_ColumnWidgets.mappingTW->takeTopLevelItem(indexP); m_pAttributeMapList.removeAt(indexP); // add the attributes to the local caches m_pLocalAttributeList.append(pair.first); m_pReferencedAttributeList.append(pair.second); // add them to the view (combo boxes) uDebug() << (pair.first) << (pair.second); m_ColumnWidgets.localColumnCB->addItem((pair.first)->toString(Uml::SignatureType::SigNoVis)); m_ColumnWidgets.referencedColumnCB->addItem((pair.second)->toString(Uml::SignatureType::SigNoVis)); foreach(const EntityAttributePair& p, m_pAttributeMapList) { uDebug() << (p.first)->name() << " " << (p.first)->baseType() << " " << (p.second)->name() << " " << (p.second)->baseType(); } slotResetWidgetState(); } /** * Checks if changes are valid and applies them if they are, * else returns false. */ bool UMLForeignKeyConstraintDialog::apply() { // set the Referenced Entity QString entityName = m_GeneralWidgets.referencedEntityCB->currentText(); - UMLObject* uo = m_doc->findUMLObject(entityName, UMLObject::ot_Entity); + UMLObject* uo = m_doc->findUMLObjectRecursive(Uml::ModelType::EntityRelationship, + entityName, + UMLObject::ot_Entity); UMLEntity* ue = uo->asUMLEntity(); if (ue == 0) { uDebug() << " Could not find UML Entity with name " << entityName; return false; } m_pForeignKeyConstraint->setReferencedEntity(ue); // set all the update and delete actions UMLForeignKeyConstraint::UpdateDeleteAction updateAction, deleteAction; updateAction = (UMLForeignKeyConstraint::UpdateDeleteAction) m_GeneralWidgets.updateActionCB->currentIndex(); deleteAction = (UMLForeignKeyConstraint::UpdateDeleteAction) m_GeneralWidgets.deleteActionCB->currentIndex(); m_pForeignKeyConstraint->setUpdateAction(updateAction); m_pForeignKeyConstraint->setDeleteAction(deleteAction); // remove all existing mappings first m_pForeignKeyConstraint->clearMappings(); // add all mappings present in local cache foreach(const EntityAttributePair& pair, m_pAttributeMapList) { if (!m_pForeignKeyConstraint->addEntityAttributePair(pair.first, pair.second)) { return false; } } // set the name m_pForeignKeyConstraint->setName(m_GeneralWidgets.nameT->text()); return true; } /** * Setup the General Page. */ void UMLForeignKeyConstraintDialog::setupGeneralPage() { //setup General page QWidget* page = new QWidget(); QVBoxLayout* topLayout = new QVBoxLayout(); page->setLayout(topLayout); pageGeneral = createPage(i18nc("general page title", "General"), i18n("General Settings"), Icon_Utils::it_Properties_General, page); m_GeneralWidgets.generalGB = new QGroupBox(i18nc("general group title", "General")); topLayout->addWidget(m_GeneralWidgets.generalGB); QGridLayout* generalLayout = new QGridLayout(m_GeneralWidgets.generalGB); generalLayout->setSpacing(spacingHint()); generalLayout->setMargin(fontMetrics().height()); Dialog_Utils::makeLabeledEditField(generalLayout, 0, m_GeneralWidgets.nameL, i18nc("label for entering name", "Name"), m_GeneralWidgets.nameT); m_GeneralWidgets.referencedEntityL = new QLabel(i18n("Referenced Entity")); generalLayout->addWidget(m_GeneralWidgets.referencedEntityL, 1, 0); m_GeneralWidgets.referencedEntityCB = new KComboBox(); generalLayout->addWidget(m_GeneralWidgets.referencedEntityCB, 1, 1); m_GeneralWidgets.actionGB = new QGroupBox(i18n("Actions")); topLayout->addWidget(m_GeneralWidgets.actionGB); QGridLayout* actionLayout = new QGridLayout(m_GeneralWidgets.actionGB); generalLayout->setSpacing(spacingHint()); generalLayout->setMargin(fontMetrics().height()); m_GeneralWidgets.onUpdateL = new QLabel(i18n("On Update")); actionLayout->addWidget(m_GeneralWidgets.onUpdateL, 0, 0); m_GeneralWidgets.updateActionCB = new KComboBox(page); actionLayout->addWidget(m_GeneralWidgets.updateActionCB, 0, 1); m_GeneralWidgets.onDeleteL = new QLabel(i18n("On Delete")); actionLayout->addWidget(m_GeneralWidgets.onDeleteL, 1, 0); m_GeneralWidgets.deleteActionCB = new KComboBox(); actionLayout->addWidget(m_GeneralWidgets.deleteActionCB, 1, 1); // set the name m_GeneralWidgets.nameT->setText(m_pForeignKeyConstraint->name()); // fill up the combo boxes // reference entity combo box UMLEntityList entList = m_doc->entities(); foreach(UMLEntity* ent, entList) { m_GeneralWidgets.referencedEntityCB->addItem(ent->name()); } UMLEntity* referencedEntity = m_pForeignKeyConstraint->getReferencedEntity(); int index; if (referencedEntity != 0) { index = m_GeneralWidgets.referencedEntityCB->findText(referencedEntity->name()); if (index != -1) m_GeneralWidgets.referencedEntityCB->setCurrentIndex(index); } m_pReferencedEntityIndex = m_GeneralWidgets.referencedEntityCB->currentIndex(); // action combo boxes // do not change order. It is according to enum specification in foreignkeyconstraint.h QStringList actions; actions << i18n("No Action") << i18n("Restrict") << i18n("Cascade") << i18n("Set Null") << i18n("Set Default"); m_GeneralWidgets.updateActionCB->addItems(actions); m_GeneralWidgets.deleteActionCB->addItems(actions); m_GeneralWidgets.updateActionCB->setCurrentIndex(m_pForeignKeyConstraint->getUpdateAction()); m_GeneralWidgets.deleteActionCB->setCurrentIndex(m_pForeignKeyConstraint->getDeleteAction()); connect(m_GeneralWidgets.referencedEntityCB, SIGNAL(activated(int)), this, SLOT(slotReferencedEntityChanged(int))); } /** * Setup Column Page. */ void UMLForeignKeyConstraintDialog::setupColumnPage() { //setup Columns page QWidget* page = new QWidget(); QVBoxLayout* topLayout = new QVBoxLayout(); page->setLayout(topLayout); pageColumn = createPage(i18n("Columns"), i18n("Columns"), Icon_Utils::it_Properties_Columns, page); m_ColumnWidgets.mappingTW = new QTreeWidget(); topLayout->addWidget(m_ColumnWidgets.mappingTW); QStringList headers; headers << i18nc("column header local", "Local") << i18nc("column header referenced", "Referenced"); m_ColumnWidgets.mappingTW->setHeaderLabels(headers); QWidget* columns = new QWidget(); topLayout->addWidget(columns); QGridLayout* columnsLayout = new QGridLayout(columns); m_ColumnWidgets.localColumnL = new QLabel(i18n("Local Column")); columnsLayout->addWidget(m_ColumnWidgets.localColumnL, 0, 0); m_ColumnWidgets.localColumnCB = new KComboBox(); columnsLayout->addWidget(m_ColumnWidgets.localColumnCB, 0, 1); m_ColumnWidgets.referencedColumnL = new QLabel(i18n("Referenced Column")); columnsLayout->addWidget(m_ColumnWidgets.referencedColumnL, 1, 0); m_ColumnWidgets.referencedColumnCB = new KComboBox(); columnsLayout->addWidget(m_ColumnWidgets.referencedColumnCB, 1, 1); #if QT_VERSION >= 0x050000 QDialogButtonBox* buttonBox = new QDialogButtonBox(); m_ColumnWidgets.addPB = buttonBox->addButton(i18n("&Add"), QDialogButtonBox::ActionRole); connect(m_ColumnWidgets.addPB, SIGNAL(clicked()), this, SLOT(slotAddPair())); m_ColumnWidgets.removePB = buttonBox->addButton(i18n("&Delete"), QDialogButtonBox::ActionRole); connect(m_ColumnWidgets.removePB, SIGNAL(clicked()), this, SLOT(slotDeletePair())); #else KDialogButtonBox* buttonBox = new KDialogButtonBox(page); m_ColumnWidgets.addPB = buttonBox->addButton(i18n("&Add"), KDialogButtonBox::ActionRole, this, SLOT(slotAddPair())); m_ColumnWidgets.removePB = buttonBox->addButton(i18n("&Delete"), KDialogButtonBox::ActionRole, this, SLOT(slotDeletePair())); #endif columnsLayout->addWidget(buttonBox, 2, 1); // fill the column boxes and their local cache. refillLocalAttributeCB(); refillReferencedAttributeCB(); QMap::iterator i; QMap map = m_pForeignKeyConstraint->getEntityAttributePairs(); for (i = map.begin(); i != map.end() ; ++i) { UMLEntityAttribute* localColumn, *referencedColumn; localColumn = const_cast(i.key()); referencedColumn = const_cast(i.value()); // remove these columns from local cache int indexL = m_pLocalAttributeList.indexOf(localColumn); int indexR = m_pReferencedAttributeList.indexOf(referencedColumn); m_pLocalAttributeList.removeAt(indexL); m_pReferencedAttributeList.removeAt(indexR); // remove them from combo boxes // the conditions may never be violated . Just for safety though if (indexL >= 0 && indexL < (m_ColumnWidgets.localColumnCB)->count()) m_ColumnWidgets.localColumnCB->removeItem(indexL); if (indexR >= 0 && indexR < (m_ColumnWidgets.referencedColumnCB)->count()) m_ColumnWidgets.referencedColumnCB->removeItem(indexR); // add to local cache m_pAttributeMapList.append(qMakePair(localColumn, referencedColumn)); // add to view QTreeWidgetItem* mapping = new QTreeWidgetItem(m_ColumnWidgets.mappingTW); mapping->setText(0, localColumn->toString(Uml::SignatureType::SigNoVis)); mapping->setText(1, referencedColumn->toString(Uml::SignatureType::SigNoVis)); m_ColumnWidgets.mappingTW->insertTopLevelItem(0, mapping); } slotResetWidgetState(); connect(m_ColumnWidgets.mappingTW, SIGNAL(itemClicked(QTreeWidgetItem*,int)), this, SLOT(slotResetWidgetState())); } /** * Used when the Apply button is clicked. */ void UMLForeignKeyConstraintDialog::slotApply() { apply(); } /** * Used when the OK button is clicked. Calls apply(). */ void UMLForeignKeyConstraintDialog::slotOk() { if (apply()) { accept(); } } void UMLForeignKeyConstraintDialog::slotReferencedEntityChanged(int index) { if (index == m_pReferencedEntityIndex) { return; } if (!m_pAttributeMapList.empty()) { int result = KMessageBox::questionYesNo(this, i18n("You are attempting to change the Referenced Entity of this ForeignKey Constraint. Any unapplied changes to the mappings between local and referenced entities will be lost. Are you sure you want to continue ?")); if (result != KMessageBox::Yes) { // revert back to old index m_GeneralWidgets.referencedEntityCB->setCurrentIndex(m_pReferencedEntityIndex); return; } } // set the referenced entity index to current index m_pReferencedEntityIndex = index; m_ColumnWidgets.mappingTW->clear(); refillReferencedAttributeCB(); refillLocalAttributeCB(); } void UMLForeignKeyConstraintDialog::refillReferencedAttributeCB() { m_pReferencedAttributeList.clear(); m_ColumnWidgets.referencedColumnCB->clear(); // fill the combo boxes - UMLObject* uo = m_doc->findUMLObject(m_GeneralWidgets.referencedEntityCB->currentText(), - UMLObject::ot_Entity); + UMLObject* uo = m_doc->findUMLObjectRecursive(Uml::ModelType::EntityRelationship, + m_GeneralWidgets.referencedEntityCB->currentText(), + UMLObject::ot_Entity); UMLEntity* ue = uo->asUMLEntity(); if (ue) { UMLClassifierListItemList ual = ue->getFilteredList(UMLObject::ot_EntityAttribute); foreach(UMLClassifierListItem* att, ual) { m_pReferencedAttributeList.append(att->asUMLEntityAttribute()); m_ColumnWidgets.referencedColumnCB->addItem(att->toString(Uml::SignatureType::SigNoVis)); } } } void UMLForeignKeyConstraintDialog::refillLocalAttributeCB() { m_pLocalAttributeList.clear(); m_ColumnWidgets.localColumnCB->clear(); // fill the combo boxes UMLEntity* ue = m_pForeignKeyConstraint->umlParent()->asUMLEntity(); if (ue) { UMLClassifierListItemList ual = ue->getFilteredList(UMLObject::ot_EntityAttribute); foreach(UMLClassifierListItem* att, ual) { m_pLocalAttributeList.append(att->asUMLEntityAttribute()); m_ColumnWidgets.localColumnCB->addItem(att->toString(Uml::SignatureType::SigNoVis)); } } } /** * Enable/Disable the widgets in the Dialog Box. */ void UMLForeignKeyConstraintDialog::slotResetWidgetState() { m_ColumnWidgets.addPB->setEnabled(true); m_ColumnWidgets.removePB->setEnabled(true); m_ColumnWidgets.localColumnCB->setEnabled(true); m_ColumnWidgets.referencedColumnCB->setEnabled(true); // If one of the Combo Boxes is empty, then disable the Combo Box if (m_ColumnWidgets.localColumnCB->count() == 0 || m_ColumnWidgets.referencedColumnCB->count() == 0) { m_ColumnWidgets.localColumnCB->setEnabled(false); m_ColumnWidgets.referencedColumnCB->setEnabled(false); m_ColumnWidgets.addPB->setEnabled(false); } // get index of selected Attribute in List Box if (m_ColumnWidgets.mappingTW->currentItem() == 0) { m_ColumnWidgets.removePB->setEnabled(false); } } diff --git a/umbrello/model_utils.cpp b/umbrello/model_utils.cpp index 9fa7f3efa..e552f17dd 100644 --- a/umbrello/model_utils.cpp +++ b/umbrello/model_utils.cpp @@ -1,2095 +1,2124 @@ /*************************************************************************** * 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) 2004-2014 * * Umbrello UML Modeller Authors * ***************************************************************************/ // own header #include "model_utils.h" // app includes #include "floatingtextwidget.h" #include "debug_utils.h" #include "umlobject.h" #include "umlpackagelist.h" #include "uniqueconstraint.h" #include "package.h" #include "folder.h" #include "classifier.h" #include "enum.h" #include "entity.h" #include "template.h" #include "operation.h" #include "attribute.h" #include "association.h" #include "umlrole.h" #include "umldoc.h" #include "uml.h" #include "umllistview.h" #include "umllistviewitem.h" #include "umlscene.h" #include "umlview.h" #include "codegenerator.h" // kde includes #include // qt includes #include #include namespace Model_Utils { /** * Determines whether the given widget type is cloneable. * * @param type The input WidgetType. * @return True if the given type is cloneable. */ bool isCloneable(WidgetBase::WidgetType type) { switch (type) { case WidgetBase::wt_Actor: case WidgetBase::wt_UseCase: case WidgetBase::wt_Class: case WidgetBase::wt_Interface: case WidgetBase::wt_Enum: case WidgetBase::wt_Datatype: case WidgetBase::wt_Package: case WidgetBase::wt_Component: case WidgetBase::wt_Port: case WidgetBase::wt_Node: case WidgetBase::wt_Artifact: case WidgetBase::wt_Instance: case WidgetBase::wt_Entity: return true; default: return false; } } /** * Seek the given id in the given list of objects. * Each list element may itself contain other objects * and the search is done recursively. * * @param id The unique ID to seek. * @param inList The UMLObjectList in which to search. * @return Pointer to the UMLObject that matches the ID (NULL if none matches). */ UMLObject* findObjectInList(Uml::ID::Type id, const UMLObjectList& inList) { for (UMLObjectListIt oit(inList); oit.hasNext();) { UMLObject *obj = oit.next(); if (obj->id() == id) return obj; UMLObject *o; UMLObject::ObjectType t = obj->baseType(); switch (t) { case UMLObject::ot_Folder: case UMLObject::ot_Package: case UMLObject::ot_Component: o = obj->asUMLPackage()->findObjectById(id); if (o) return o; break; case UMLObject::ot_Interface: case UMLObject::ot_Class: case UMLObject::ot_Enum: case UMLObject::ot_Entity: case UMLObject::ot_Instance: o = obj->asUMLClassifier()->findChildObjectById(id); if (o == 0 && (t == UMLObject::ot_Interface || t == UMLObject::ot_Class)) o = ((UMLPackage*)obj)->findObjectById(id); if (o) return o; break; case UMLObject::ot_Association: { UMLAssociation *assoc = obj->asUMLAssociation(); UMLRole *rA = assoc->getUMLRole(Uml::RoleType::A); if (rA->id() == id) return rA; UMLRole *rB = assoc->getUMLRole(Uml::RoleType::B); if (rB->id() == id) return rB; } break; default: break; } } return 0; } /** * Find the UML object of the given type and name in the passed-in list. * * @param inList List in which to seek the object. * @param inName Name of the object to find. * @param type ObjectType of the object to find (optional.) * When the given type is ot_UMLObject the type is * disregarded, i.e. the given name is the only * search criterion. * @param currentObj Object relative to which to search (optional.) * If given then the enclosing scope(s) of this * object are searched before the global scope. * @return Pointer to the UMLObject found, or NULL if not found. */ UMLObject* findUMLObject(const UMLObjectList& inList, const QString& inName, UMLObject::ObjectType type /* = ot_UMLObject */, UMLObject *currentObj /* = 0 */) { const bool caseSensitive = UMLApp::app()->activeLanguageIsCaseSensitive(); QString name = inName; const bool atGlobalScope = name.startsWith(QLatin1String("::")); if (atGlobalScope) { name = name.mid(2); currentObj = 0; } QStringList components; #ifdef TRY_BUGFIX_120682 // If we have a pointer or a reference in cpp we need to remove // the asterisks and ampersands in order to find the appropriate object if (UMLApp::app()->getActiveLanguage() == Uml::pl_Cpp) { if (name.endsWith(QLatin1Char('*'))) name.remove(QLatin1Char('*')); else if (name.contains(QLatin1Char('&'))) name.remove(QLatin1Char('&')); } #endif QString scopeSeparator = UMLApp::app()->activeLanguageScopeSeparator(); if (name.contains(scopeSeparator)) components = name.split(scopeSeparator); QString nameWithoutFirstPrefix; if (components.size() > 1) { name = components.front(); components.pop_front(); nameWithoutFirstPrefix = components.join(scopeSeparator); } if (currentObj) { UMLPackage *pkg = 0; if (currentObj->asUMLClassifierListItem()) { currentObj = currentObj->umlParent(); } pkg = currentObj->asUMLPackage(); if (pkg == 0 || pkg->baseType() == UMLObject::ot_Association) pkg = currentObj->umlPackage(); // Remember packages that we've seen - for avoiding cycles. UMLPackageList seenPkgs; for (; pkg; pkg = currentObj->umlPackage()) { if (nameWithoutFirstPrefix.isEmpty() && (type == UMLObject::ot_UMLObject || type == UMLObject::ot_Folder || type == UMLObject::ot_Package || type == pkg->baseType())) { if (caseSensitive) { if (pkg->name() == name) return pkg; } else if (pkg->name().toLower() == name.toLower()) { return pkg; } } if (seenPkgs.indexOf(pkg) != -1) { uError() << "findUMLObject(" << name << "): " << "breaking out of cycle involving " << pkg->name(); break; } seenPkgs.append(pkg); // exclude non package type // pg->asUMLPackage() fails for unknown reason // see https://bugs.kde.org/show_bug.cgi?id=341709 UMLObject::ObjectType foundType = pkg->baseType(); if (foundType != UMLObject::ot_Package && foundType != UMLObject::ot_Folder && foundType != UMLObject::ot_Class && foundType != UMLObject::ot_Interface && foundType != UMLObject::ot_Component) { continue; } UMLObjectList &objectsInCurrentScope = pkg->containedObjects(); for (UMLObjectListIt oit(objectsInCurrentScope); oit.hasNext();) { UMLObject *obj = oit.next(); uIgnoreZeroPointer(obj); if (caseSensitive) { if (obj->name() != name) continue; } else if (obj->name().toLower() != name.toLower()) { continue; } UMLObject::ObjectType foundType = obj->baseType(); if (nameWithoutFirstPrefix.isEmpty()) { if (type != UMLObject::ot_UMLObject && type != foundType) { uDebug() << "type mismatch for " << name << " (seeking type: " << UMLObject::toString(type) << ", found type: " << UMLObject::toString(foundType) << ")"; // Class, Interface, and Datatype are all Classifiers // and are considered equivalent. // The caller must be prepared to handle possible mismatches. if ((type == UMLObject::ot_Class || type == UMLObject::ot_Interface || type == UMLObject::ot_Datatype) && (foundType == UMLObject::ot_Class || foundType == UMLObject::ot_Interface || foundType == UMLObject::ot_Datatype)) { return obj; } continue; } return obj; } if (foundType != UMLObject::ot_Package && foundType != UMLObject::ot_Folder && foundType != UMLObject::ot_Class && foundType != UMLObject::ot_Interface && foundType != UMLObject::ot_Component) { uDebug() << "found " << UMLObject::toString(foundType) << name << " is not a package (?)"; continue; } UMLPackage *pkg = obj->asUMLPackage(); return findUMLObject(pkg->containedObjects(), nameWithoutFirstPrefix, type); } currentObj = pkg; } } for (UMLObjectListIt oit(inList); oit.hasNext();) { UMLObject *obj = oit.next(); uIgnoreZeroPointer(obj); if (caseSensitive) { if (obj->name() != name) continue; } else if (obj->name().toLower() != name.toLower()) { continue; } UMLObject::ObjectType foundType = obj->baseType(); if (nameWithoutFirstPrefix.isEmpty()) { if (type != UMLObject::ot_UMLObject && type != foundType) { uDebug() << "type mismatch for " << name << " (seeking type: " << UMLObject::toString(type) << ", found type: " << UMLObject::toString(foundType) << ")"; continue; } return obj; } if (foundType != UMLObject::ot_Package && foundType != UMLObject::ot_Folder && foundType != UMLObject::ot_Class && foundType != UMLObject::ot_Interface && foundType != UMLObject::ot_Component) { uDebug() << "found " << name << "(" << UMLObject::toString(foundType) << ")" << " is not a package (?)"; continue; } UMLPackage *pkg = obj->asUMLPackage(); return findUMLObject(pkg->containedObjects(), nameWithoutFirstPrefix, type); } return 0; } /** * Find the UML object of the given type and name in the passed-in list. * This method searches for the raw name. * * @param inList List in which to seek the object. * @param name Name of the object to find. * @param type ObjectType of the object to find (optional.) * When the given type is ot_UMLObject the type is * disregarded, i.e. the given name is the only * search criterion. * @param currentObj Object relative to which to search (optional.) * If given then the enclosing scope(s) of this * object are searched before the global scope. * @return Pointer to the UMLObject found, or NULL if not found. */ UMLObject* findUMLObjectRaw(const UMLObjectList& inList, const QString& name, UMLObject::ObjectType type /* = ot_UMLObject */, UMLObject *currentObj /*= 0*/) { Q_UNUSED(currentObj); for (UMLObjectListIt oit(inList); oit.hasNext();) { UMLObject *obj = oit.next(); if (obj->name() == name && type == obj->baseType()) return obj; } return 0; } +/** + * Find the UML object of the given type and name in the passed-in list. + * This method searches for the raw name. + * + * @param inList List in which to seek the object. + * @param name Name of the object to find. + * @param type ObjectType of the object to find (optional.) + * When the given type is ot_UMLObject the type is + * disregarded, i.e. the given name is the only + * search criterion. + * @return Pointer to the UMLObject found, or NULL if not found. + */ +UMLObject* findUMLObjectRecursive(const UMLObjectList& inList, + const QString& name, + UMLObject::ObjectType type /* = ot_UMLObject */) +{ + foreach(UMLObject *obj, inList) { + if (obj->name() == name && type == obj->baseType()) + return obj; + UMLPackage *pkg = obj->asUMLPackage(); + if (pkg && pkg->containedObjects().size() > 0) { + UMLObject *o = findUMLObjectRecursive(pkg->containedObjects(), name, type); + if (o) + return o; + } + } + return 0; +} + /** * Get the root folder of the given UMLObject. */ UMLPackage* rootPackage(UMLObject* obj) { if (obj == 0) return 0; UMLPackage* root = obj->umlPackage(); if (root == 0) { root = obj->asUMLPackage(); } else { while (root->umlPackage() != 0) { root = root->umlPackage(); } } return root; } /** * Add the given list of views to the tree view. * @param viewList the list of views to add */ void treeViewAddViews(const UMLViewList& viewList) { UMLListView* tree = UMLApp::app()->listView(); foreach (UMLView* v, viewList) { if (tree->findItem(v->umlScene()->ID()) != 0) { continue; } tree->createDiagramItem(v); } } /** * Change an icon of an object in the tree view. * @param object the object in the treeViewAddViews * @param to the new icon type for the given object */ void treeViewChangeIcon(UMLObject* object, Icon_Utils::IconType to) { UMLListView* tree = UMLApp::app()->listView(); tree->changeIconOf(object, to); } /** * Set the given object to the current item in the tree view. * @param object the object which will be the current item */ void treeViewSetCurrentItem(UMLObject* object) { UMLListView* tree = UMLApp::app()->listView(); UMLListViewItem* item = tree->findUMLObject(object); tree->setCurrentItem(item); } /** * Move an object to a new container in the tree view. * @param container the new container for the object * @param object the to be moved object */ void treeViewMoveObjectTo(UMLObject* container, UMLObject* object) { UMLListView *listView = UMLApp::app()->listView(); UMLListViewItem *newParent = listView->findUMLObject(container); listView->moveObject(object->id(), Model_Utils::convert_OT_LVT(object), newParent); } /** * Return the current UMLObject from the tree view. * @return the UML object of the current item */ UMLObject* treeViewGetCurrentObject() { UMLListView *listView = UMLApp::app()->listView(); UMLListViewItem *current = static_cast(listView->currentItem()); return current->umlObject(); } /** * Return the UMLPackage if the current item * in the tree view is a package. Return the * closest package in the tree view or NULL otherwise * * @return the package or NULL */ UMLPackage* treeViewGetPackageFromCurrent() { UMLListView *listView = UMLApp::app()->listView(); UMLListViewItem *parentItem = (UMLListViewItem*)listView->currentItem(); while (parentItem) { UMLListViewItem::ListViewType lvt = parentItem->type(); if (Model_Utils::typeIsContainer(lvt)) { UMLObject *o = parentItem->umlObject(); return o->asUMLPackage(); } // selected item is not a container, try to find the // container higher up in the tree view parentItem = static_cast(parentItem->parent()); } return 0; } /** * Build the diagram name from the tree view. * The function returns a relative path constructed from the folder hierarchy. * @param id the id of the diaram * @return the constructed diagram name */ QString treeViewBuildDiagramName(Uml::ID::Type id) { UMLListView *listView = UMLApp::app()->listView(); UMLListViewItem* listViewItem = listView->findItem(id); if (listViewItem) { QString name = listViewItem->text(0); listViewItem = static_cast(listViewItem->parent()); // Relies on the tree structure of the UMLListView. There are a base "Views" folder // and five children, one for each view type (Logical, use case, components, deployment // and entity relationship) while (listView->rootView(listViewItem->type()) == 0) { name.insert(0, listViewItem->text(0) + QLatin1Char('/')); listViewItem = static_cast(listViewItem->parent()); if (listViewItem == 0) break; } return name; } else { uWarning() << "diagram not found - returning empty name!"; return QString(); } } /** * Returns a name for the new object, appended with a number * if the default name is taken e.g. new_actor, new_actor_1 * etc. * @param type The object type. * @param parentPkg The package in which to compare the name. * @param prefix The prefix to use (optional) * If no prefix is given then a type related * prefix will be chosen internally. */ QString uniqObjectName(UMLObject::ObjectType type, UMLPackage *parentPkg, QString prefix) { QString currentName = prefix; if (currentName.isEmpty()) { if(type == UMLObject::ot_Class) currentName = i18n("new_class"); else if(type == UMLObject::ot_Actor) currentName = i18n("new_actor"); else if(type == UMLObject::ot_UseCase) currentName = i18n("new_usecase"); else if(type == UMLObject::ot_Package) currentName = i18n("new_package"); else if(type == UMLObject::ot_Component) currentName = i18n("new_component"); else if(type == UMLObject::ot_Port) currentName = i18n("new_port"); else if(type == UMLObject::ot_Node) currentName = i18n("new_node"); else if(type == UMLObject::ot_Artifact) currentName = i18n("new_artifact"); else if(type == UMLObject::ot_Interface) currentName = i18n("new_interface"); else if(type == UMLObject::ot_Datatype) currentName = i18n("new_datatype"); else if(type == UMLObject::ot_Enum) currentName = i18n("new_enum"); else if(type == UMLObject::ot_Entity) currentName = i18n("new_entity"); else if(type == UMLObject::ot_Folder) currentName = i18n("new_folder"); else if(type == UMLObject::ot_Association) currentName = i18n("new_association"); else if(type == UMLObject::ot_Category) currentName = i18n("new_category"); else if(type == UMLObject::ot_Instance) currentName = i18n("new_object"); else { currentName = i18n("new_object"); uWarning() << "unknown object type in umldoc::uniqObjectName()"; } } UMLDoc *doc = UMLApp::app()->document(); QString name = currentName; for (int number = 1; !doc->isUnique(name, parentPkg); ++number) { name = currentName + QLatin1Char('_') + QString::number(number); } return name; } /** * Return the xmi.id (XMI-1) or xmi:id (XMI-2) of a QDomElement. */ QString getXmiId(QDomElement element) { QString idStr = element.attribute(QLatin1String("xmi.id")); if (idStr.isEmpty()) idStr = element.attribute(QLatin1String("xmi:id")); return idStr; } /** * Return true if the given tag is one of the common XMI * attributes, such as: * "name" | "visibility" | "isRoot" | "isLeaf" | "isAbstract" | * "isActive" | "ownerScope" */ bool isCommonXMI1Attribute(const QString &tag) { bool retval = (UMLDoc::tagEq(tag, QLatin1String("name")) || UMLDoc::tagEq(tag, QLatin1String("visibility")) || UMLDoc::tagEq(tag, QLatin1String("isRoot")) || UMLDoc::tagEq(tag, QLatin1String("isLeaf")) || UMLDoc::tagEq(tag, QLatin1String("isAbstract")) || UMLDoc::tagEq(tag, QLatin1String("isSpecification")) || UMLDoc::tagEq(tag, QLatin1String("isActive")) || UMLDoc::tagEq(tag, QLatin1String("namespace")) || UMLDoc::tagEq(tag, QLatin1String("ownerScope")) || UMLDoc::tagEq(tag, QLatin1String("ModelElement.stereotype")) || UMLDoc::tagEq(tag, QLatin1String("GeneralizableElement.generalization")) || UMLDoc::tagEq(tag, QLatin1String("specialization")) || //NYI UMLDoc::tagEq(tag, QLatin1String("clientDependency")) || //NYI UMLDoc::tagEq(tag, QLatin1String("supplierDependency")) //NYI ); return retval; } /** * Return true if the given type is common among the majority * of programming languages, such as "bool" or "boolean". * TODO: Make this depend on the active programming language. */ bool isCommonDataType(QString type) { CodeGenerator *gen = UMLApp::app()->generator(); if (gen == 0) return false; const bool caseSensitive = UMLApp::app()->activeLanguageIsCaseSensitive(); const QStringList dataTypes = gen->defaultDatatypes(); QStringList::ConstIterator end(dataTypes.end()); for (QStringList::ConstIterator it = dataTypes.begin(); it != end; ++it) { if (caseSensitive) { if (type == *it) return true; } else if (type.toLower() == (*it).toLower()) { return true; } } return false; } /** * Return true if the given object type is a classifier list item type. */ bool isClassifierListitem(UMLObject::ObjectType type) { if (type == UMLObject::ot_Attribute || type == UMLObject::ot_Operation || type == UMLObject::ot_Template || type == UMLObject::ot_EntityAttribute || type == UMLObject::ot_EnumLiteral || type == UMLObject::ot_UniqueConstraint || type == UMLObject::ot_ForeignKeyConstraint || type == UMLObject::ot_CheckConstraint || type == UMLObject::ot_InstanceAttribute ) { return true; } else { return false; } } /** * Try to guess the correct container folder type of an UMLObject. * Object types that can't be guessed are mapped to Uml::ModelType::Logical. * NOTE: This function exists mainly for handling pre-1.5.5 files * and should not be used for new code. */ Uml::ModelType::Enum guessContainer(UMLObject *o) { UMLObject::ObjectType ot = o->baseType(); if (ot == UMLObject::ot_Package && o->stereotype() == QLatin1String("subsystem")) return Uml::ModelType::Component; Uml::ModelType::Enum mt = Uml::ModelType::N_MODELTYPES; switch (ot) { case UMLObject::ot_Package: // trouble: package can also appear in Component view case UMLObject::ot_Interface: case UMLObject::ot_Datatype: case UMLObject::ot_Enum: case UMLObject::ot_Class: case UMLObject::ot_Attribute: case UMLObject::ot_Operation: case UMLObject::ot_EnumLiteral: case UMLObject::ot_Template: case UMLObject::ot_Instance: case UMLObject::ot_InstanceAttribute: mt = Uml::ModelType::Logical; break; case UMLObject::ot_Actor: case UMLObject::ot_UseCase: mt = Uml::ModelType::UseCase; break; case UMLObject::ot_Component: case UMLObject::ot_Port: case UMLObject::ot_Artifact: // trouble: artifact can also appear at Deployment mt = Uml::ModelType::Component; break; case UMLObject::ot_Node: mt = Uml::ModelType::Deployment; break; case UMLObject::ot_Entity: case UMLObject::ot_EntityAttribute: case UMLObject::ot_UniqueConstraint: case UMLObject::ot_ForeignKeyConstraint: case UMLObject::ot_CheckConstraint: case UMLObject::ot_Category: mt = Uml::ModelType::EntityRelationship; break; case UMLObject::ot_Association: { UMLAssociation *assoc = o->asUMLAssociation(); UMLDoc *umldoc = UMLApp::app()->document(); for (int r = Uml::RoleType::A; r <= Uml::RoleType::B; ++r) { UMLObject *roleObj = assoc->getObject(Uml::RoleType::fromInt(r)); if (roleObj == 0) { // Ouch! we have been called while types are not yet resolved return Uml::ModelType::N_MODELTYPES; } UMLPackage *pkg = roleObj->umlPackage(); if (pkg) { while (pkg->umlPackage()) { // wind back to root pkg = pkg->umlPackage(); } const Uml::ModelType::Enum m = umldoc->rootFolderType(pkg); if (m != Uml::ModelType::N_MODELTYPES) return m; } mt = guessContainer(roleObj); if (mt != Uml::ModelType::Logical) break; } } break; default: break; } return mt; } /** * Parse a direction string into the Uml::ParameterDirection::Enum. * * @param input The string to parse: "in", "out", or "inout" * optionally followed by whitespace. * @param result The corresponding Uml::ParameterDirection::Enum. * @return Length of the string matched, excluding the optional * whitespace. */ int stringToDirection(QString input, Uml::ParameterDirection::Enum & result) { QRegExp dirx(QLatin1String("^(in|out|inout)")); int pos = dirx.indexIn(input); if (pos == -1) return 0; const QString dirStr = dirx.capturedTexts().first(); int dirLen = dirStr.length(); if (input.length() > dirLen && !input[dirLen].isSpace()) return 0; // no match after all. if (dirStr == QLatin1String("out")) result = Uml::ParameterDirection::Out; else if (dirStr == QLatin1String("inout")) result = Uml::ParameterDirection::InOut; else result = Uml::ParameterDirection::In; return dirLen; } /** * Parses a template parameter given in UML syntax. * * @param t Input text of the template parameter. * Example: parname : partype * or just: parname (for class type) * @param nmTp NameAndType returned by this method. * @param owningScope Pointer to the owning scope of the template param. * @return Error status of the parse, PS_OK for success. */ Parse_Status parseTemplate(QString t, NameAndType& nmTp, UMLClassifier *owningScope) { UMLDoc *pDoc = UMLApp::app()->document(); t = t.trimmed(); if (t.isEmpty()) return PS_Empty; QStringList nameAndType = t.split(QRegExp(QLatin1String("\\s*:\\s*"))); if (nameAndType.count() == 2) { UMLObject *pType = 0; if (nameAndType[1] != QLatin1String("class")) { pType = pDoc->findUMLObject(nameAndType[1], UMLObject::ot_UMLObject, owningScope); if (pType == 0) return PS_Unknown_ArgType; } nmTp = NameAndType(nameAndType[0], pType); } else { nmTp = NameAndType(t, 0); } return PS_OK; } /** * Parses an attribute given in UML syntax. * * @param a Input text of the attribute in UML syntax. * Example: argname : argtype * @param nmTp NameAndType returned by this method. * @param owningScope Pointer to the owning scope of the attribute. * @param vis Optional pointer to visibility (return value.) * The visibility may be given at the beginning of the * attribute text in mnemonic form as follows: * "+" stands for public * "#" stands for protected * "-" stands for private * "~" stands for implementation level visibility * * @return Error status of the parse, PS_OK for success. */ Parse_Status parseAttribute(QString a, NameAndType& nmTp, UMLClassifier *owningScope, Uml::Visibility::Enum *vis /* = 0 */) { UMLDoc *pDoc = UMLApp::app()->document(); a = a.simplified(); if (a.isEmpty()) return PS_Empty; int colonPos = a.indexOf(QLatin1Char(':')); if (colonPos < 0) { nmTp = NameAndType(a, 0); return PS_OK; } QString name = a.left(colonPos).trimmed(); if (vis) { QRegExp mnemonicVis(QLatin1String("^([\\+\\#\\-\\~] *)")); int pos = mnemonicVis.indexIn(name); if (pos == -1) { *vis = Uml::Visibility::Private; // default value } else { QString caption = mnemonicVis.cap(1); QString strVis = caption.left(1); if (strVis == QLatin1String("+")) *vis = Uml::Visibility::Public; else if (strVis == QLatin1String("#")) *vis = Uml::Visibility::Protected; else if (strVis == QLatin1String("-")) *vis = Uml::Visibility::Private; else *vis = Uml::Visibility::Implementation; } name.remove(mnemonicVis); } Uml::ParameterDirection::Enum pd = Uml::ParameterDirection::In; if (name.startsWith(QLatin1String(QLatin1String("in ")))) { pd = Uml::ParameterDirection::In; name = name.mid(3); } else if (name.startsWith(QLatin1String(QLatin1String("inout ")))) { pd = Uml::ParameterDirection::InOut; name = name.mid(6); } else if (name.startsWith(QLatin1String(QLatin1String("out ")))) { pd = Uml::ParameterDirection::Out; name = name.mid(4); } a = a.mid(colonPos + 1).trimmed(); if (a.isEmpty()) { nmTp = NameAndType(name, 0, pd); return PS_OK; } QStringList typeAndInitialValue = a.split(QRegExp(QLatin1String("\\s*=\\s*"))); const QString &type = typeAndInitialValue[0]; UMLObject *pType = pDoc->findUMLObject(type, UMLObject::ot_UMLObject, owningScope); if (pType == 0) { nmTp = NameAndType(name, 0, pd); return PS_Unknown_ArgType; } QString initialValue; if (typeAndInitialValue.count() == 2) { initialValue = typeAndInitialValue[1]; } nmTp = NameAndType(name, pType, pd, initialValue); return PS_OK; } /** * Parses an operation given in UML syntax. * * @param m Input text of the operation in UML syntax. * Example of a two-argument operation returning "void": * methodname (arg1name : arg1type, arg2name : arg2type) : void * @param desc OpDescriptor returned by this method. * @param owningScope Pointer to the owning scope of the operation. * @return Error status of the parse, PS_OK for success. */ Parse_Status parseOperation(QString m, OpDescriptor& desc, UMLClassifier *owningScope) { UMLDoc *pDoc = UMLApp::app()->document(); m = m.simplified(); if (m.isEmpty()) return PS_Empty; if (m.contains(QRegExp(QLatin1String("operator *()")))) { // C++ special case: two sets of parentheses desc.m_name = QLatin1String("operator()"); m.remove(QRegExp(QLatin1String("operator *()"))); } else { /** * The search pattern includes everything up to the opening parenthesis * because UML also permits non programming-language oriented designs * using narrative names, for example "check water temperature". */ QRegExp beginningUpToOpenParenth(QLatin1String("^([^\\(]+)")); int pos = beginningUpToOpenParenth.indexIn(m); if (pos == -1) return PS_Illegal_MethodName; desc.m_name = beginningUpToOpenParenth.cap(1); } desc.m_pReturnType = 0; QRegExp pat = QRegExp(QLatin1String("\\) *:(.*)$")); int pos = pat.indexIn(m); if (pos != -1) { // return type is optional QString retType = pat.cap(1); retType = retType.trimmed(); if (retType != QLatin1String("void")) { UMLObject *pRetType = owningScope ? owningScope->findTemplate(retType) : 0; if (pRetType == 0) { pRetType = pDoc->findUMLObject(retType, UMLObject::ot_UMLObject, owningScope); if (pRetType == 0) return PS_Unknown_ReturnType; } desc.m_pReturnType = pRetType; } } // Remove possible empty parentheses () m.remove(QRegExp(QLatin1String("\\s*\\(\\s*\\)"))); desc.m_args.clear(); pat = QRegExp(QLatin1String("\\((.*)\\)")); pos = pat.indexIn(m); if (pos == -1) // argument list is optional return PS_OK; QString arglist = pat.cap(1); arglist = arglist.trimmed(); if (arglist.isEmpty()) return PS_OK; const QStringList args = arglist.split(QRegExp(QLatin1String("\\s*, \\s*"))); for (QStringList::ConstIterator lit = args.begin(); lit != args.end(); ++lit) { NameAndType nmTp; Parse_Status ps = parseAttribute(*lit, nmTp, owningScope); if (ps) return ps; desc.m_args.append(nmTp); } return PS_OK; } /** * Parses a constraint. * * @param m Input text of the constraint * * @param name The name returned by this method * @param owningScope Pointer to the owning scope of the constraint * @return Error status of the parse, PS_OK for success. */ Parse_Status parseConstraint(QString m, QString& name, UMLEntity* owningScope) { Q_UNUSED(owningScope); m = m.simplified(); if (m.isEmpty()) return PS_Empty; int colonPos = m.indexOf(QLatin1Char(':')); if (colonPos < 0) { name = m; return PS_OK; } name = m.left(colonPos).trimmed(); return PS_OK; } /** * Returns the Parse_Status as a text. */ QString psText(Parse_Status value) { const QString text[] = { i18n("OK"), i18nc("parse status", "Empty"), i18n("Malformed argument"), i18n("Unknown argument type"), i18n("Illegal method name"), i18n("Unknown return type"), i18n("Unspecified error") }; return text[(unsigned) value]; } /** * Return true if the listview type is one of the predefined root views * (root, logical, usecase, component, deployment, datatype, or entity- * relationship view.) */ bool typeIsRootView(UMLListViewItem::ListViewType type) { switch (type) { case UMLListViewItem::lvt_View: case UMLListViewItem::lvt_Logical_View: case UMLListViewItem::lvt_UseCase_View: case UMLListViewItem::lvt_Component_View: case UMLListViewItem::lvt_Deployment_View: case UMLListViewItem::lvt_EntityRelationship_Model: return true; break; default: break; } return false; } /** * Return true if the listview type also has a widget representation in diagrams. */ bool typeIsCanvasWidget(UMLListViewItem::ListViewType type) { switch (type) { case UMLListViewItem::lvt_Actor: case UMLListViewItem::lvt_UseCase: case UMLListViewItem::lvt_Class: case UMLListViewItem::lvt_Package: case UMLListViewItem::lvt_Logical_Folder: case UMLListViewItem::lvt_UseCase_Folder: case UMLListViewItem::lvt_Component_Folder: case UMLListViewItem::lvt_Deployment_Folder: case UMLListViewItem::lvt_EntityRelationship_Folder: case UMLListViewItem::lvt_Subsystem: case UMLListViewItem::lvt_Component: case UMLListViewItem::lvt_Port: case UMLListViewItem::lvt_Node: case UMLListViewItem::lvt_Artifact: case UMLListViewItem::lvt_Interface: case UMLListViewItem::lvt_Datatype: case UMLListViewItem::lvt_Enum: case UMLListViewItem::lvt_Entity: case UMLListViewItem::lvt_Category: return true; break; default: break; } return false; } /** * Return true if the listview type is a logical, usecase or component folder. */ bool typeIsFolder(UMLListViewItem::ListViewType type) { if (typeIsRootView(type) || type == UMLListViewItem::lvt_Datatype_Folder || type == UMLListViewItem::lvt_Logical_Folder || type == UMLListViewItem::lvt_UseCase_Folder || type == UMLListViewItem::lvt_Component_Folder || type == UMLListViewItem::lvt_Deployment_Folder || type == UMLListViewItem::lvt_EntityRelationship_Folder) { return true; } else { return false; } } /** * Return true if the listview type may act as a container for other objects, * i.e. if it is a folder, package, subsystem, or component. */ bool typeIsContainer(UMLListViewItem::ListViewType type) { if (typeIsFolder(type)) return true; return (type == UMLListViewItem::lvt_Package || type == UMLListViewItem::lvt_Subsystem || type == UMLListViewItem::lvt_Component || type == UMLListViewItem::lvt_Class || type == UMLListViewItem::lvt_Interface); } /** * Return true if the listview type is an attribute, operation, or template. */ bool typeIsClassifierList(UMLListViewItem::ListViewType type) { if (type == UMLListViewItem::lvt_Attribute || type == UMLListViewItem::lvt_Instance || type == UMLListViewItem::lvt_Operation || type == UMLListViewItem::lvt_Template || type == UMLListViewItem::lvt_EntityAttribute || type == UMLListViewItem::lvt_UniqueConstraint || type == UMLListViewItem::lvt_ForeignKeyConstraint || type == UMLListViewItem::lvt_PrimaryKeyConstraint || type == UMLListViewItem::lvt_CheckConstraint || type == UMLListViewItem::lvt_EnumLiteral || type == UMLListViewItem::lvt_InstanteAttribute) { return true; } else { return false; } } /** * Return true if the listview type is a classifier (Class, Entity, Enum) */ bool typeIsClassifier(UMLListViewItem::ListViewType type) { if (type == UMLListViewItem::lvt_Class || type == UMLListViewItem::lvt_Interface || type == UMLListViewItem::lvt_Entity || type == UMLListViewItem::lvt_Enum) { return true; } return false; } /** * Return true if the listview type is a settings entry. */ bool typeIsProperties(UMLListViewItem::ListViewType type) { switch (type) { case UMLListViewItem::lvt_Properties: case UMLListViewItem::lvt_Properties_AutoLayout: case UMLListViewItem::lvt_Properties_Class: case UMLListViewItem::lvt_Properties_CodeImport: case UMLListViewItem::lvt_Properties_CodeGeneration: case UMLListViewItem::lvt_Properties_CodeViewer: case UMLListViewItem::lvt_Properties_Font: case UMLListViewItem::lvt_Properties_General: case UMLListViewItem::lvt_Properties_UserInterface: return true; break; default: break; } return false; } /** * Check if a listviewitem of type childType is allowed * as child of type parentType */ bool typeIsAllowedInType(UMLListViewItem::ListViewType childType, UMLListViewItem::ListViewType parentType) { switch (childType) { case UMLListViewItem::lvt_Class: case UMLListViewItem::lvt_Package: case UMLListViewItem::lvt_Interface: case UMLListViewItem::lvt_Enum: case UMLListViewItem::lvt_Instance: return parentType == UMLListViewItem::lvt_Logical_View || parentType == UMLListViewItem::lvt_Class || parentType == UMLListViewItem::lvt_Package || parentType == UMLListViewItem::lvt_Logical_Folder; case UMLListViewItem::lvt_Attribute: case UMLListViewItem::lvt_EntityAttribute: case UMLListViewItem::lvt_InstanteAttribute: return parentType == UMLListViewItem::lvt_Entity; case UMLListViewItem::lvt_Operation: return parentType == UMLListViewItem::lvt_Class || parentType == UMLListViewItem::lvt_Interface; case UMLListViewItem::lvt_Datatype: return parentType == UMLListViewItem::lvt_Logical_Folder || parentType == UMLListViewItem::lvt_Datatype_Folder || parentType == UMLListViewItem::lvt_Class || parentType == UMLListViewItem::lvt_Interface || parentType == UMLListViewItem::lvt_Package; case UMLListViewItem::lvt_Class_Diagram: case UMLListViewItem::lvt_Collaboration_Diagram: case UMLListViewItem::lvt_State_Diagram: case UMLListViewItem::lvt_Activity_Diagram: case UMLListViewItem::lvt_Sequence_Diagram: case UMLListViewItem::lvt_Object_Diagram: return parentType == UMLListViewItem::lvt_Logical_Folder || parentType == UMLListViewItem::lvt_Logical_View; case UMLListViewItem::lvt_Logical_Folder: return parentType == UMLListViewItem::lvt_Logical_Folder || parentType == UMLListViewItem::lvt_Logical_View; case UMLListViewItem::lvt_UseCase_Folder: return parentType == UMLListViewItem::lvt_UseCase_Folder || parentType == UMLListViewItem::lvt_UseCase_View; case UMLListViewItem::lvt_Component_Folder: return parentType == UMLListViewItem::lvt_Component_Folder || parentType == UMLListViewItem::lvt_Component_View; case UMLListViewItem::lvt_Deployment_Folder: return parentType == UMLListViewItem::lvt_Deployment_Folder || parentType == UMLListViewItem::lvt_Deployment_View; case UMLListViewItem::lvt_EntityRelationship_Folder: return parentType == UMLListViewItem::lvt_EntityRelationship_Folder || parentType == UMLListViewItem::lvt_EntityRelationship_Model; case UMLListViewItem::lvt_Actor: case UMLListViewItem::lvt_UseCase: case UMLListViewItem::lvt_UseCase_Diagram: return parentType == UMLListViewItem::lvt_UseCase_Folder || parentType == UMLListViewItem::lvt_UseCase_View; case UMLListViewItem::lvt_Subsystem: return parentType == UMLListViewItem::lvt_Component_Folder || parentType == UMLListViewItem::lvt_Subsystem; case UMLListViewItem::lvt_Component: case UMLListViewItem::lvt_Port: return parentType == UMLListViewItem::lvt_Component_Folder || parentType == UMLListViewItem::lvt_Component || parentType == UMLListViewItem::lvt_Subsystem; case UMLListViewItem::lvt_Artifact: case UMLListViewItem::lvt_Component_Diagram: return parentType == UMLListViewItem::lvt_Component_Folder || parentType == UMLListViewItem::lvt_Component_View; break; case UMLListViewItem::lvt_Node: case UMLListViewItem::lvt_Deployment_Diagram: return parentType == UMLListViewItem::lvt_Deployment_Folder; case UMLListViewItem::lvt_Entity: case UMLListViewItem::lvt_EntityRelationship_Diagram: case UMLListViewItem::lvt_Category: return parentType == UMLListViewItem::lvt_EntityRelationship_Folder; default: return false; } } /** * Return true if the listview type is a diagram. */ bool typeIsDiagram(UMLListViewItem::ListViewType type) { if (type == UMLListViewItem::lvt_Class_Diagram || type == UMLListViewItem::lvt_Collaboration_Diagram || type == UMLListViewItem::lvt_State_Diagram || type == UMLListViewItem::lvt_Activity_Diagram || type == UMLListViewItem::lvt_Sequence_Diagram || type == UMLListViewItem::lvt_UseCase_Diagram || type == UMLListViewItem::lvt_Component_Diagram || type == UMLListViewItem::lvt_Deployment_Diagram || type == UMLListViewItem::lvt_EntityRelationship_Diagram || type == UMLListViewItem::lvt_Object_Diagram ){ return true; } else { return false; } } /** * Return the Model_Type which corresponds to the given DiagramType. */ Uml::ModelType::Enum convert_DT_MT(Uml::DiagramType::Enum dt) { Uml::ModelType::Enum mt; switch (dt) { case Uml::DiagramType::UseCase: mt = Uml::ModelType::UseCase; break; case Uml::DiagramType::Collaboration: case Uml::DiagramType::Class: case Uml::DiagramType::Object: case Uml::DiagramType::Sequence: case Uml::DiagramType::State: case Uml::DiagramType::Activity: mt = Uml::ModelType::Logical; break; case Uml::DiagramType::Component: mt = Uml::ModelType::Component; break; case Uml::DiagramType::Deployment: mt = Uml::ModelType::Deployment; break; case Uml::DiagramType::EntityRelationship: mt = Uml::ModelType::EntityRelationship; break; default: uError() << "Model_Utils::convert_DT_MT: illegal input value " << dt; mt = Uml::ModelType::N_MODELTYPES; break; } return mt; } /** * Return the ListViewType which corresponds to the given Model_Type. */ UMLListViewItem::ListViewType convert_MT_LVT(Uml::ModelType::Enum mt) { UMLListViewItem::ListViewType lvt = UMLListViewItem::lvt_Unknown; switch (mt) { case Uml::ModelType::Logical: lvt = UMLListViewItem::lvt_Logical_View; break; case Uml::ModelType::UseCase: lvt = UMLListViewItem::lvt_UseCase_View; break; case Uml::ModelType::Component: lvt = UMLListViewItem::lvt_Component_View; break; case Uml::ModelType::Deployment: lvt = UMLListViewItem::lvt_Deployment_View; break; case Uml::ModelType::EntityRelationship: lvt = UMLListViewItem::lvt_EntityRelationship_Model; break; default: break; } return lvt; } /** * Return the Model_Type which corresponds to the given ListViewType. * Returns Uml::N_MODELTYPES if the list view type given does not map * to a Model_Type. */ Uml::ModelType::Enum convert_LVT_MT(UMLListViewItem::ListViewType lvt) { Uml::ModelType::Enum mt = Uml::ModelType::N_MODELTYPES; switch (lvt) { case UMLListViewItem::lvt_Logical_View: mt = Uml::ModelType::Logical; break; case UMLListViewItem::lvt_UseCase_View: mt = Uml::ModelType::UseCase; break; case UMLListViewItem::lvt_Component_View: mt = Uml::ModelType::Component; break; case UMLListViewItem::lvt_Deployment_View: mt = Uml::ModelType::Deployment; break; case UMLListViewItem::lvt_EntityRelationship_Model: mt = Uml::ModelType::EntityRelationship; break; default: break; } return mt; } /** * Convert a diagram type enum to the equivalent list view type. */ UMLListViewItem::ListViewType convert_DT_LVT(Uml::DiagramType::Enum dt) { UMLListViewItem::ListViewType type = UMLListViewItem::lvt_Unknown; switch(dt) { case Uml::DiagramType::UseCase: type = UMLListViewItem::lvt_UseCase_Diagram; break; case Uml::DiagramType::Class: type = UMLListViewItem::lvt_Class_Diagram; break; case Uml::DiagramType::Object: type = UMLListViewItem::lvt_Object_Diagram; break; case Uml::DiagramType::Sequence: type = UMLListViewItem::lvt_Sequence_Diagram; break; case Uml::DiagramType::Collaboration: type = UMLListViewItem::lvt_Collaboration_Diagram; break; case Uml::DiagramType::State: type = UMLListViewItem::lvt_State_Diagram; break; case Uml::DiagramType::Activity: type = UMLListViewItem::lvt_Activity_Diagram; break; case Uml::DiagramType::Component: type = UMLListViewItem::lvt_Component_Diagram; break; case Uml::DiagramType::Deployment: type = UMLListViewItem::lvt_Deployment_Diagram; break; case Uml::DiagramType::EntityRelationship: type = UMLListViewItem::lvt_EntityRelationship_Diagram; break; default: uWarning() << "convert_DT_LVT() called on unknown diagram type"; } return type; } /** * Convert an object's type to the equivalent list view type * * @param o Pointer to the UMLObject whose type shall be converted * to the equivalent ListViewType. We cannot just * pass in a UMLObject::ObjectType because a UMLFolder is mapped * to different ListViewType values, depending on its * location in one of the predefined modelviews (Logical/ * UseCase/etc.) * @return The equivalent ListViewType. */ UMLListViewItem::ListViewType convert_OT_LVT(UMLObject *o) { UMLObject::ObjectType ot = o->baseType(); UMLListViewItem::ListViewType type = UMLListViewItem::lvt_Unknown; switch(ot) { case UMLObject::ot_UseCase: type = UMLListViewItem::lvt_UseCase; break; case UMLObject::ot_Actor: type = UMLListViewItem::lvt_Actor; break; case UMLObject::ot_Class: type = UMLListViewItem::lvt_Class; break; case UMLObject::ot_Package: if (o->stereotype() == QLatin1String("subsystem")) type = UMLListViewItem::lvt_Subsystem; else type = UMLListViewItem::lvt_Package; break; case UMLObject::ot_Folder: { UMLDoc *umldoc = UMLApp::app()->document(); UMLFolder *f = o->asUMLFolder(); do { const Uml::ModelType::Enum mt = umldoc->rootFolderType(f); if (mt != Uml::ModelType::N_MODELTYPES) { switch (mt) { case Uml::ModelType::Logical: type = UMLListViewItem::lvt_Logical_Folder; break; case Uml::ModelType::UseCase: type = UMLListViewItem::lvt_UseCase_Folder; break; case Uml::ModelType::Component: type = UMLListViewItem::lvt_Component_Folder; break; case Uml::ModelType::Deployment: type = UMLListViewItem::lvt_Deployment_Folder; break; case Uml::ModelType::EntityRelationship: type = UMLListViewItem::lvt_EntityRelationship_Folder; break; default: break; } return type; } } while ((f = f->umlPackage()->asUMLFolder()) != 0); uError() << "convert_OT_LVT(" << o->name() << "): internal error - object is not properly nested in folder"; } break; case UMLObject::ot_Component: type = UMLListViewItem::lvt_Component; break; case UMLObject::ot_Port: type = UMLListViewItem::lvt_Port; break; case UMLObject::ot_Node: type = UMLListViewItem::lvt_Node; break; case UMLObject::ot_Artifact: type = UMLListViewItem::lvt_Artifact; break; case UMLObject::ot_Interface: type = UMLListViewItem::lvt_Interface; break; case UMLObject::ot_Datatype: type = UMLListViewItem::lvt_Datatype; break; case UMLObject::ot_Enum: type = UMLListViewItem::lvt_Enum; break; case UMLObject::ot_EnumLiteral: type = UMLListViewItem::lvt_EnumLiteral; break; case UMLObject::ot_Entity: type = UMLListViewItem::lvt_Entity; break; case UMLObject::ot_Category: type = UMLListViewItem::lvt_Category; break; case UMLObject::ot_EntityAttribute: type = UMLListViewItem::lvt_EntityAttribute; break; case UMLObject::ot_UniqueConstraint: { UMLEntity* ent = o->umlParent()->asUMLEntity(); UMLUniqueConstraint* uc = o->asUMLUniqueConstraint(); if (ent->isPrimaryKey(uc)) { type = UMLListViewItem::lvt_PrimaryKeyConstraint; } else { type = UMLListViewItem::lvt_UniqueConstraint; } break; } case UMLObject::ot_ForeignKeyConstraint: type = UMLListViewItem::lvt_ForeignKeyConstraint; break; case UMLObject::ot_CheckConstraint: type = UMLListViewItem::lvt_CheckConstraint; break; case UMLObject::ot_Attribute: type = UMLListViewItem::lvt_Attribute; break; case UMLObject::ot_Operation: type = UMLListViewItem::lvt_Operation; break; case UMLObject::ot_Template: type = UMLListViewItem::lvt_Template; break; case UMLObject::ot_Association: type = UMLListViewItem::lvt_Association; break; case UMLObject::ot_Instance: type = UMLListViewItem::lvt_Instance; break; case UMLObject::ot_InstanceAttribute: type = UMLListViewItem::lvt_InstanteAttribute; break; default: break; } return type; } /** * Converts a list view type enum to the equivalent object type. * * @param lvt The ListViewType to convert. * @return The converted ObjectType if the listview type * has a UMLObject::ObjectType representation, else 0. */ UMLObject::ObjectType convert_LVT_OT(UMLListViewItem::ListViewType lvt) { UMLObject::ObjectType ot = (UMLObject::ObjectType)0; switch (lvt) { case UMLListViewItem::lvt_UseCase: ot = UMLObject::ot_UseCase; break; case UMLListViewItem::lvt_Actor: ot = UMLObject::ot_Actor; break; case UMLListViewItem::lvt_Class: ot = UMLObject::ot_Class; break; case UMLListViewItem::lvt_Package: case UMLListViewItem::lvt_Subsystem: ot = UMLObject::ot_Package; break; case UMLListViewItem::lvt_Component: ot = UMLObject::ot_Component; break; case UMLListViewItem::lvt_Port: ot = UMLObject::ot_Port; break; case UMLListViewItem::lvt_Node: ot = UMLObject::ot_Node; break; case UMLListViewItem::lvt_Artifact: ot = UMLObject::ot_Artifact; break; case UMLListViewItem::lvt_Interface: ot = UMLObject::ot_Interface; break; case UMLListViewItem::lvt_Datatype: ot = UMLObject::ot_Datatype; break; case UMLListViewItem::lvt_Enum: ot = UMLObject::ot_Enum; break; case UMLListViewItem::lvt_Entity: ot = UMLObject::ot_Entity; break; case UMLListViewItem::lvt_Category: ot = UMLObject::ot_Category; break; case UMLListViewItem::lvt_EntityAttribute: ot = UMLObject::ot_EntityAttribute; break; case UMLListViewItem::lvt_UniqueConstraint: ot = UMLObject::ot_UniqueConstraint; break; case UMLListViewItem::lvt_PrimaryKeyConstraint: ot = UMLObject::ot_UniqueConstraint; break; case UMLListViewItem::lvt_ForeignKeyConstraint: ot = UMLObject::ot_ForeignKeyConstraint; break; case UMLListViewItem::lvt_CheckConstraint: ot = UMLObject::ot_CheckConstraint; break; case UMLListViewItem::lvt_Attribute: ot = UMLObject::ot_Attribute; break; case UMLListViewItem::lvt_Operation: ot = UMLObject::ot_Operation; break; case UMLListViewItem::lvt_Template: ot = UMLObject::ot_Template; break; case UMLListViewItem::lvt_EnumLiteral: ot = UMLObject::ot_EnumLiteral; break; case UMLListViewItem::lvt_Instance: ot = UMLObject::ot_Instance; break; case UMLListViewItem::lvt_InstanteAttribute: ot = UMLObject::ot_InstanceAttribute; break; default: if (typeIsFolder(lvt)) ot = UMLObject::ot_Folder; break; } return ot; } /** * Return the IconType which corresponds to the given listview type. * * @param lvt ListViewType to convert. * @return The Icon_Utils::IconType corresponding to the lvt. * Returns it_Home in case no mapping to IconType exists. */ Icon_Utils::IconType convert_LVT_IT(UMLListViewItem::ListViewType lvt, UMLObject *o) { Icon_Utils::IconType icon = Icon_Utils::it_Home; switch (lvt) { case UMLListViewItem::lvt_UseCase_View: case UMLListViewItem::lvt_UseCase_Folder: icon = Icon_Utils::it_Folder_Grey; break; case UMLListViewItem::lvt_Logical_View: case UMLListViewItem::lvt_Logical_Folder: icon = Icon_Utils::it_Folder_Green; break; case UMLListViewItem::lvt_Datatype_Folder: icon = Icon_Utils::it_Folder_Orange; break; case UMLListViewItem::lvt_Component_View: case UMLListViewItem::lvt_Component_Folder: icon = Icon_Utils::it_Folder_Red; break; case UMLListViewItem::lvt_Deployment_View: case UMLListViewItem::lvt_Deployment_Folder: icon = Icon_Utils::it_Folder_Violet; break; case UMLListViewItem::lvt_EntityRelationship_Model: case UMLListViewItem::lvt_EntityRelationship_Folder: icon = Icon_Utils::it_Folder_Cyan; break; case UMLListViewItem::lvt_Actor: icon = Icon_Utils::it_Actor; break; case UMLListViewItem::lvt_Association: icon = Icon_Utils::it_Association; break; case UMLListViewItem::lvt_UseCase: icon = Icon_Utils::it_UseCase; break; case UMLListViewItem::lvt_Class: if (o && o->stereotype() == QLatin1String("class-or-package")) icon = Icon_Utils::it_ClassOrPackage; else icon = Icon_Utils::it_Class; break; case UMLListViewItem::lvt_Package: icon = Icon_Utils::it_Package; break; case UMLListViewItem::lvt_Subsystem: icon = Icon_Utils::it_Subsystem; break; case UMLListViewItem::lvt_Component: icon = Icon_Utils::it_Component; break; case UMLListViewItem::lvt_Port: icon = Icon_Utils::it_Port; break; case UMLListViewItem::lvt_Node: icon = Icon_Utils::it_Node; break; case UMLListViewItem::lvt_Artifact: icon = Icon_Utils::it_Artifact; break; case UMLListViewItem::lvt_Interface: icon = Icon_Utils::it_Interface; break; case UMLListViewItem::lvt_Datatype: icon = Icon_Utils::it_Datatype; break; case UMLListViewItem::lvt_Enum: icon = Icon_Utils::it_Enum; break; case UMLListViewItem::lvt_Entity: icon = Icon_Utils::it_Entity; break; case UMLListViewItem::lvt_Category: icon = Icon_Utils::it_Category; break; case UMLListViewItem::lvt_Template: icon = Icon_Utils::it_Template; break; case UMLListViewItem::lvt_Attribute: icon = Icon_Utils::it_Private_Attribute; break; case UMLListViewItem::lvt_EntityAttribute: icon = Icon_Utils::it_Private_Attribute; break; case UMLListViewItem::lvt_EnumLiteral: icon = Icon_Utils::it_Public_Attribute; break; case UMLListViewItem::lvt_Operation: icon = Icon_Utils::it_Public_Method; break; case UMLListViewItem::lvt_UniqueConstraint: icon = Icon_Utils::it_Unique_Constraint; break; case UMLListViewItem::lvt_PrimaryKeyConstraint: icon = Icon_Utils::it_PrimaryKey_Constraint; break; case UMLListViewItem::lvt_ForeignKeyConstraint: icon = Icon_Utils::it_ForeignKey_Constraint; break; case UMLListViewItem::lvt_CheckConstraint: icon = Icon_Utils::it_Check_Constraint; break; case UMLListViewItem::lvt_Class_Diagram: icon = Icon_Utils::it_Diagram_Class; break; case UMLListViewItem::lvt_Object_Diagram: icon = Icon_Utils::it_Diagram_Object; break; case UMLListViewItem::lvt_UseCase_Diagram: icon = Icon_Utils::it_Diagram_Usecase; break; case UMLListViewItem::lvt_Sequence_Diagram: icon = Icon_Utils::it_Diagram_Sequence; break; case UMLListViewItem::lvt_Collaboration_Diagram: icon = Icon_Utils::it_Diagram_Collaboration; break; case UMLListViewItem::lvt_State_Diagram: icon = Icon_Utils::it_Diagram_State; break; case UMLListViewItem::lvt_Activity_Diagram: icon = Icon_Utils::it_Diagram_Activity; break; case UMLListViewItem::lvt_Component_Diagram: icon = Icon_Utils::it_Diagram_Component; break; case UMLListViewItem::lvt_Deployment_Diagram: icon = Icon_Utils::it_Diagram_Deployment; break; case UMLListViewItem::lvt_EntityRelationship_Diagram: icon = Icon_Utils::it_Diagram_EntityRelationship; break; case UMLListViewItem::lvt_Properties: icon = Icon_Utils::it_Properties; break; case UMLListViewItem::lvt_Properties_AutoLayout: icon = Icon_Utils::it_Properties_AutoLayout; break; case UMLListViewItem::lvt_Properties_Class: icon = Icon_Utils::it_Properties_Class; break; case UMLListViewItem::lvt_Properties_CodeImport: icon = Icon_Utils::it_Properties_CodeImport; break; case UMLListViewItem::lvt_Properties_CodeGeneration: icon = Icon_Utils::it_Properties_CodeGeneration; break; case UMLListViewItem::lvt_Properties_CodeViewer: icon = Icon_Utils::it_Properties_CodeViewer; break; case UMLListViewItem::lvt_Properties_Font: icon = Icon_Utils::it_Properties_Font; break; case UMLListViewItem::lvt_Properties_General: icon = Icon_Utils::it_Properties_General; break; case UMLListViewItem::lvt_Properties_UserInterface: icon = Icon_Utils::it_Properties_UserInterface; break; case UMLListViewItem::lvt_Instance: icon = Icon_Utils::it_Instance; break; case UMLListViewItem::lvt_InstanteAttribute: icon = Icon_Utils::it_Private_Attribute; break; default: break; } return icon; } /** * Return the DiagramType which corresponds to the given listview type. * * @param lvt ListViewType to convert. * @return The Uml::DiagramType corresponding to the lvt. * Returns dt_Undefined in case no mapping to DiagramType exists. */ Uml::DiagramType::Enum convert_LVT_DT(UMLListViewItem::ListViewType lvt) { Uml::DiagramType::Enum dt = Uml::DiagramType::Undefined; switch (lvt) { case UMLListViewItem::lvt_Class_Diagram: dt = Uml::DiagramType::Class; break; case UMLListViewItem::lvt_UseCase_Diagram: dt = Uml::DiagramType::UseCase; break; case UMLListViewItem::lvt_Sequence_Diagram: dt = Uml::DiagramType::Sequence; break; case UMLListViewItem::lvt_Collaboration_Diagram: dt = Uml::DiagramType::Collaboration; break; case UMLListViewItem::lvt_State_Diagram: dt = Uml::DiagramType::State; break; case UMLListViewItem::lvt_Activity_Diagram: dt = Uml::DiagramType::Activity; break; case UMLListViewItem::lvt_Component_Diagram: dt = Uml::DiagramType::Component; break; case UMLListViewItem::lvt_Deployment_Diagram: dt = Uml::DiagramType::Deployment; break; case UMLListViewItem::lvt_EntityRelationship_Diagram: dt = Uml::DiagramType::EntityRelationship; break; case UMLListViewItem::lvt_Object_Diagram: dt = Uml::DiagramType::Object; break; default: break; } return dt; } /** * Converts a list view type enum to the equivalent settings dialog type. * * @param type The ListViewType to convert. * @return The converted settings dialog type */ MultiPageDialogBase::PageType convert_LVT_PT(UMLListViewItem::ListViewType type) { MultiPageDialogBase::PageType pt = MultiPageDialogBase::GeneralPage; switch (type) { case UMLListViewItem::lvt_Properties: pt = MultiPageDialogBase::GeneralPage; break; case UMLListViewItem::lvt_Properties_AutoLayout: pt = MultiPageDialogBase::AutoLayoutPage; break; case UMLListViewItem::lvt_Properties_Class: pt = MultiPageDialogBase::ClassPage; break; case UMLListViewItem::lvt_Properties_CodeImport: pt = MultiPageDialogBase::CodeImportPage; break; case UMLListViewItem::lvt_Properties_CodeGeneration: pt = MultiPageDialogBase::CodeGenerationPage; break; case UMLListViewItem::lvt_Properties_CodeViewer: pt = MultiPageDialogBase::CodeViewerPage; break; case UMLListViewItem::lvt_Properties_Font: pt = MultiPageDialogBase::FontPage; break; case UMLListViewItem::lvt_Properties_General: pt = MultiPageDialogBase::GeneralPage; break; case UMLListViewItem::lvt_Properties_UserInterface: pt = MultiPageDialogBase::UserInterfacePage; break; default: break; } return pt; } /** * Return the Model_Type which corresponds to the given ObjectType. */ Uml::ModelType::Enum convert_OT_MT(UMLObject::ObjectType ot) { Uml::ModelType::Enum mt = Uml::ModelType::N_MODELTYPES; switch (ot) { case UMLObject::ot_Actor: case UMLObject::ot_UseCase: mt = Uml::ModelType::UseCase; break; case UMLObject::ot_Component: case UMLObject::ot_Port: case UMLObject::ot_Artifact: mt = Uml::ModelType::Component; break; case UMLObject::ot_Node: mt = Uml::ModelType::Deployment; break; case UMLObject::ot_Entity: case UMLObject::ot_EntityAttribute: case UMLObject::ot_UniqueConstraint: case UMLObject::ot_ForeignKeyConstraint: case UMLObject::ot_CheckConstraint: case UMLObject::ot_Category: mt = Uml::ModelType::EntityRelationship; break; default: mt = Uml::ModelType::Logical; break; } return mt; } /** * Converts from the UpdateDeleteAction enum to a QString * @param uda The UpdateDeleteAction enum literal */ QString updateDeleteActionToString(UMLForeignKeyConstraint::UpdateDeleteAction uda) { switch(uda) { case UMLForeignKeyConstraint::uda_NoAction: return QLatin1String("NO ACTION"); case UMLForeignKeyConstraint::uda_Restrict: return QLatin1String("RESTRICT"); case UMLForeignKeyConstraint::uda_Cascade: return QLatin1String("CASCADE"); case UMLForeignKeyConstraint::uda_SetNull: return QLatin1String("SET NULL"); case UMLForeignKeyConstraint::uda_SetDefault: return QLatin1String("SET DEFAULT"); default: return QString(); } } /** * Return true if the object type is allowed in the related diagram * @param o UML object instance * @param scene diagram instance * @return true type is allowed * @return false type is not allowed */ bool typeIsAllowedInDiagram(UMLObject* o, UMLScene *scene) { //make sure dragging item onto correct diagram // concept - class, seq, coll diagram // actor, usecase - usecase diagram UMLObject::ObjectType ot = o->baseType(); Uml::ID::Type id = o->id(); Uml::DiagramType::Enum diagramType = scene->type(); bool bAccept = true; switch (diagramType) { case Uml::DiagramType::UseCase: if ((scene->widgetOnDiagram(id) && ot == UMLObject::ot_Actor) || (ot != UMLObject::ot_Actor && ot != UMLObject::ot_UseCase)) bAccept = false; break; case Uml::DiagramType::Class: if (scene->widgetOnDiagram(id) || (ot != UMLObject::ot_Class && ot != UMLObject::ot_Package && ot != UMLObject::ot_Interface && ot != UMLObject::ot_Enum && ot != UMLObject::ot_Datatype)) { bAccept = false; } break; case Uml::DiagramType::Object: if( scene->widgetOnDiagram(id) || (ot != UMLObject::ot_Instance)) bAccept = false; break; case Uml::DiagramType::Sequence: case Uml::DiagramType::Collaboration: if (ot != UMLObject::ot_Class && ot != UMLObject::ot_Interface && ot != UMLObject::ot_Actor) bAccept = false; break; case Uml::DiagramType::Deployment: if (scene->widgetOnDiagram(id)) bAccept = false; else if (ot != UMLObject::ot_Interface && ot != UMLObject::ot_Package && ot != UMLObject::ot_Component && ot != UMLObject::ot_Class && ot != UMLObject::ot_Node) bAccept = false; else if (ot == UMLObject::ot_Package && o->stereotype() != QLatin1String("subsystem")) bAccept = false; break; case Uml::DiagramType::Component: if (scene->widgetOnDiagram(id) || (ot != UMLObject::ot_Interface && ot != UMLObject::ot_Package && ot != UMLObject::ot_Component && ot != UMLObject::ot_Port && ot != UMLObject::ot_Artifact && ot != UMLObject::ot_Class)) bAccept = false; else if (ot == UMLObject::ot_Class && !o->isAbstract()) bAccept = false; else if (ot == UMLObject::ot_Port) { const bool componentOnDiagram = scene->widgetOnDiagram(o->umlPackage()->id()); bAccept = componentOnDiagram; } break; case Uml::DiagramType::EntityRelationship: if (scene->widgetOnDiagram(id) || (ot != UMLObject::ot_Entity && ot != UMLObject::ot_Category)) bAccept = false; break; default: break; } return bAccept; } /** * Return true if the widget type is allowed in the related diagram * @param w UML widget object * @param scene diagram instance * @return true type is allowed * @return false type is not allowed */ bool typeIsAllowedInDiagram(UMLWidget* w, UMLScene *scene) { UMLWidget::WidgetType wt = w->baseType(); Uml::DiagramType::Enum diagramType = scene->type(); bool bAccept = true; // TODO: check additional widgets switch (diagramType) { case Uml::DiagramType::Activity: case Uml::DiagramType::Class: case Uml::DiagramType::Object: case Uml::DiagramType::Collaboration: case Uml::DiagramType::Component: case Uml::DiagramType::Deployment: case Uml::DiagramType::EntityRelationship: case Uml::DiagramType::Sequence: case Uml::DiagramType::State: case Uml::DiagramType::UseCase: default: switch(wt) { case WidgetBase::wt_Note: break; case WidgetBase::wt_Text: { FloatingTextWidget *ft = w->asFloatingTextWidget(); if (ft && ft->textRole() != Uml::TextRole::Floating) { bAccept = false; } } break; default: bAccept = false; break; } break; } return bAccept; } /** * return true if given object type supports associatons * @param type uml object type to check */ bool hasAssociations(UMLObject::ObjectType type) { switch (type) { case UMLObject::ot_Actor: case UMLObject::ot_UseCase: case UMLObject::ot_Class: case UMLObject::ot_Package: case UMLObject::ot_Component: case UMLObject::ot_Node: case UMLObject::ot_Artifact: case UMLObject::ot_Interface: case UMLObject::ot_Enum: case UMLObject::ot_Entity: case UMLObject::ot_Datatype: case UMLObject::ot_Category: case UMLObject::ot_Instance: return true; default: return false; } } } // namespace Model_Utils diff --git a/umbrello/model_utils.h b/umbrello/model_utils.h index 150098f3b..322662337 100644 --- a/umbrello/model_utils.h +++ b/umbrello/model_utils.h @@ -1,139 +1,143 @@ /*************************************************************************** * 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) 2004-2014 * * Umbrello UML Modeller Authors * ***************************************************************************/ #ifndef MODEL_UTILS_H #define MODEL_UTILS_H #include "basictypes.h" #include "umllistviewitem.h" #include "umlobjectlist.h" #include "umlviewlist.h" #include "foreignkeyconstraint.h" #include "icon_utils.h" #include "multipagedialogbase.h" #include "widgetbase.h" #include #include // forward declarations class UMLClassifier; class UMLPackage; class UMLEntity; /** * General purpose model utilities. * @author Oliver Kellogg * Bugs and comments to umbrello-devel@kde.org or http://bugs.kde.org */ namespace Model_Utils { bool isCloneable(WidgetBase::WidgetType type); UMLObject* findObjectInList(Uml::ID::Type id, const UMLObjectList& inList); UMLObject* findUMLObject(const UMLObjectList& inList, const QString& name, UMLObject::ObjectType type = UMLObject::ot_UMLObject, UMLObject *currentObj = 0); UMLObject* findUMLObjectRaw(const UMLObjectList& inList, const QString& name, UMLObject::ObjectType type = UMLObject::ot_UMLObject, UMLObject *currentObj = 0); +UMLObject* findUMLObjectRecursive(const UMLObjectList& inList, + const QString& name, + UMLObject::ObjectType type = UMLObject::ot_UMLObject); + UMLPackage* rootPackage(UMLObject* obj); void treeViewAddViews(const UMLViewList& viewList); void treeViewChangeIcon(UMLObject* object, Icon_Utils::IconType to); void treeViewSetCurrentItem(UMLObject* object); void treeViewMoveObjectTo(UMLObject* container, UMLObject* object); UMLObject* treeViewGetCurrentObject(); UMLPackage* treeViewGetPackageFromCurrent(); QString treeViewBuildDiagramName(Uml::ID::Type id); QString uniqObjectName(UMLObject::ObjectType type, UMLPackage *parentPkg, QString prefix = QString()); QString getXmiId(QDomElement element); bool isCommonXMI1Attribute(const QString &tag); bool isCommonDataType(QString type); bool isClassifierListitem(UMLObject::ObjectType ot); bool typeIsCanvasWidget(UMLListViewItem::ListViewType type); bool typeIsRootView(UMLListViewItem::ListViewType type); bool typeIsFolder(UMLListViewItem::ListViewType type); bool typeIsContainer(UMLListViewItem::ListViewType type); bool typeIsDiagram(UMLListViewItem::ListViewType type); bool typeIsClassifierList(UMLListViewItem::ListViewType type); bool typeIsClassifier(UMLListViewItem::ListViewType type); bool typeIsProperties(UMLListViewItem::ListViewType type); bool typeIsAllowedInType(UMLListViewItem::ListViewType childType, UMLListViewItem::ListViewType parentType); bool typeIsAllowedInDiagram(UMLObject *o, UMLScene *scene); bool typeIsAllowedInDiagram(UMLWidget *w, UMLScene *scene); bool hasAssociations(UMLObject::ObjectType type); Uml::ModelType::Enum convert_DT_MT(Uml::DiagramType::Enum dt); UMLListViewItem::ListViewType convert_MT_LVT(Uml::ModelType::Enum mt); Uml::ModelType::Enum convert_LVT_MT(UMLListViewItem::ListViewType lvt); UMLListViewItem::ListViewType convert_DT_LVT(Uml::DiagramType::Enum dt); UMLObject::ObjectType convert_LVT_OT(UMLListViewItem::ListViewType lvt); UMLListViewItem::ListViewType convert_OT_LVT(UMLObject *o); Icon_Utils::IconType convert_LVT_IT(UMLListViewItem::ListViewType lvt, UMLObject *o=0); Uml::DiagramType::Enum convert_LVT_DT(UMLListViewItem::ListViewType lvt); MultiPageDialogBase::PageType convert_LVT_PT(UMLListViewItem::ListViewType type); Uml::ModelType::Enum convert_OT_MT(UMLObject::ObjectType ot); Uml::ModelType::Enum guessContainer(UMLObject *o); // deprecated ! int stringToDirection(QString input, Uml::ParameterDirection::Enum & result); enum Parse_Status { ///< Return type of parseOperation(). PS_OK, PS_Empty, PS_Malformed_Arg, PS_Unknown_ArgType, PS_Illegal_MethodName, PS_Unknown_ReturnType, PS_Unspecified_Error }; struct NameAndType { ///< Data structure filled by parseAttribute(). QString m_name; UMLObject *m_type; Uml::ParameterDirection::Enum m_direction; QString m_initialValue; NameAndType() : m_type(0), m_direction(Uml::ParameterDirection::In) {} NameAndType(QString name, UMLObject *type, Uml::ParameterDirection::Enum direction = Uml::ParameterDirection::In, QString initialValue = QString()) : m_name(name), m_type(type), m_direction(direction), m_initialValue(initialValue) {} }; typedef QLinkedList NameAndType_List; ///< Auxiliary type for OpDescriptor. typedef QLinkedList::iterator NameAndType_ListIt; ///< Auxiliary type for OpDescriptor. struct OpDescriptor { ///< Data structure filled by parseOperation(). QString m_name; NameAndType_List m_args; UMLObject *m_pReturnType; }; Parse_Status parseTemplate(QString t, NameAndType& nmTp, UMLClassifier *owningScope); Parse_Status parseAttribute(QString a, NameAndType& nmTp, UMLClassifier *owningScope, Uml::Visibility::Enum *vis = 0); Parse_Status parseOperation(QString m, OpDescriptor& desc, UMLClassifier *owningScope); Parse_Status parseConstraint(QString m, QString& name, UMLEntity* owningScope); QString psText(Parse_Status value); QString updateDeleteActionToString(UMLForeignKeyConstraint::UpdateDeleteAction uda); } #endif diff --git a/umbrello/umldoc.cpp b/umbrello/umldoc.cpp index 402013607..441d98a2a 100644 --- a/umbrello/umldoc.cpp +++ b/umbrello/umldoc.cpp @@ -1,3474 +1,3509 @@ /*************************************************************************** * 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-2014 * * Umbrello UML Modeller Authors * ***************************************************************************/ // own header #include "umldoc.h" // app includes #include "debug_utils.h" #include "uniqueid.h" #include "association.h" #include "package.h" #include "folder.h" #include "codegenerator.h" #include "classifier.h" #include "dialog_utils.h" #include "enum.h" #include "entity.h" #include "docwindow.h" #include "operation.h" #include "attribute.h" #include "template.h" #include "enumliteral.h" #include "stereotype.h" #include "classifierlistitem.h" #include "object_factory.h" #include "import_argo.h" #include "import_rose.h" #include "model_utils.h" #include "uml.h" #include "umllistview.h" #include "umllistviewitem.h" #include "umlview.h" #include "entityconstraint.h" #include "idchangelog.h" #include "umllistviewpopupmenu.h" #include "cmds.h" #include "diagramprintpage.h" #include "umlscene.h" #include "version.h" #include "worktoolbar.h" #include "models/diagramsmodel.h" #include "models/objectsmodel.h" #include "models/stereotypesmodel.h" // kde includes #include #if QT_VERSION < 0x050000 #include #endif #if QT_VERSION >= 0x050000 #include #endif #include #include #if QT_VERSION < 0x050000 #include #endif #include #if QT_VERSION < 0x050000 #include #include #include #endif // qt includes #include #include #include #include #include #include #include #include #if QT_VERSION >= 0x050000 #include #endif #include #include #include #if QT_VERSION >= 0x050000 #include #include #endif #include #include DEBUG_REGISTER(UMLDoc) /** * Constructor for the fileclass of the application. */ UMLDoc::UMLDoc() : m_datatypeRoot(0), m_stereoList(UMLStereotypeList()), m_Name(i18n("UML Model")), m_modelID("m1"), m_count(0), m_modified(false), #if QT_VERSION >= 0x050000 m_doc_url(QUrl()), #else m_doc_url(KUrl()), #endif m_pChangeLog(0), m_bLoading(false), m_importing(false), m_Doc(QString()), m_pAutoSaveTimer(0), m_nViewID(Uml::ID::None), m_bTypesAreResolved(true), m_pCurrentRoot(0), m_bClosing(false), m_diagramsModel(new DiagramsModel), m_objectsModel(new ObjectsModel), m_stereotypesModel(new StereotypesModel(&m_stereoList)), m_resolution(0.0) { for (int i = 0; i < Uml::ModelType::N_MODELTYPES; ++i) m_root[i] = 0; } /** * Initialize the UMLDoc. * To be called after the constructor, before anything else. */ void UMLDoc::init() { // Initialize predefined folders. const char* nativeRootName[Uml::ModelType::N_MODELTYPES] = { "Logical View", "Use Case View", "Component View", "Deployment View", "Entity Relationship Model" }; const QString localizedRootName[Uml::ModelType::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::ModelType::N_MODELTYPES; ++i) { const QString rootName = QString::fromLatin1(nativeRootName[i]); QString id = rootName; id.replace(QLatin1Char(' '), QLatin1Char('_')); m_root[i] = new UMLFolder(rootName, Uml::ID::fromString(id)); m_root[i]->setLocalName(localizedRootName[i]); } createDatatypeFolder(); // Connect signals. UMLApp * pApp = UMLApp::app(); connect(this, SIGNAL(sigDiagramCreated(Uml::ID::Type)), pApp, SLOT(slotUpdateViews())); connect(this, SIGNAL(sigDiagramRemoved(Uml::ID::Type)), pApp, SLOT(slotUpdateViews())); connect(this, SIGNAL(sigDiagramRenamed(Uml::ID::Type)), pApp, SLOT(slotUpdateViews())); connect(this, SIGNAL(sigCurrentViewChanged()), pApp, SLOT(slotCurrentViewChanged())); } /** * Create the datatype folder and add it to the logical folder. */ void UMLDoc::createDatatypeFolder() { delete m_datatypeRoot; m_datatypeRoot = new UMLFolder(QLatin1String("Datatypes"), "Datatypes"); m_datatypeRoot->setLocalName(i18n("Datatypes")); m_datatypeRoot->setUMLPackage(m_root[Uml::ModelType::Logical]); Q_ASSERT(m_root[Uml::ModelType::Logical]); m_root[Uml::ModelType::Logical]->addObject(m_datatypeRoot); } /** * Destructor for the fileclass of the application. */ UMLDoc::~UMLDoc() { UMLApp * pApp = UMLApp::app(); disconnect(this, SIGNAL(sigDiagramCreated(Uml::ID::Type)), pApp, SLOT(slotUpdateViews())); disconnect(this, SIGNAL(sigDiagramRemoved(Uml::ID::Type)), pApp, SLOT(slotUpdateViews())); disconnect(this, SIGNAL(sigDiagramRenamed(Uml::ID::Type)), pApp, SLOT(slotUpdateViews())); disconnect(this, SIGNAL(sigCurrentViewChanged()), pApp, SLOT(slotCurrentViewChanged())); disconnect(m_pAutoSaveTimer, SIGNAL(timeout()), this, SLOT(slotAutoSave())); delete m_pAutoSaveTimer; m_root[Uml::ModelType::Logical]->removeObject(m_datatypeRoot); delete m_datatypeRoot; for (int i = 0; i < Uml::ModelType::N_MODELTYPES; ++i) { delete m_root[i]; } delete m_pChangeLog; qDeleteAll(m_stereoList); delete m_stereotypesModel; delete m_diagramsModel; delete m_objectsModel; } /** * Adds a view to the document which represents the document * contents. Usually this is your main view. * * @param view Pointer to the UMLView to add. */ void UMLDoc::addView(UMLView *view) { if (view == 0) { uError() << "argument is NULL"; return; } UMLFolder *f = view->umlScene()->folder(); if (f == 0) { uError() << "view folder is not set"; return; } DEBUG(DBG_SRC) << view->umlScene()->name() << " to folder " << *f << " (" << f->name() << ")"; f->addView(view); m_diagramsModel->addDiagram(view); UMLApp * pApp = UMLApp::app(); if (pApp->listView()) { connect(this, SIGNAL(sigObjectRemoved(UMLObject*)), view->umlScene(), SLOT(slotObjectRemoved(UMLObject*))); } if (!m_bLoading || pApp->currentView() == 0) { pApp->setCurrentView(view); } if (!m_bLoading) { view->show(); emit sigDiagramChanged(view->umlScene()->type()); } pApp->setDiagramMenuItemsState(true); pApp->slotUpdateViews(); } /** * Removes a view from the list of currently connected views. * * @param view Pointer to the UMLView to remove. * @param enforceCurrentView Switch to determine if we have a current view or not. * Most of the time, we DO want this, except when exiting the program. */ void UMLDoc::removeView(UMLView *view, bool enforceCurrentView) { if (!view) { uError() << "UMLDoc::removeView(UMLView *view) called with view = 0"; return; } DEBUG(DBG_SRC) << "<" << view->umlScene()->name() << ">"; if (UMLApp::app()->listView()) { disconnect(this, SIGNAL(sigObjectRemoved(UMLObject*)), view->umlScene(), SLOT(slotObjectRemoved(UMLObject*))); } view->hide(); UMLFolder *f = view->umlScene()->folder(); if (f == 0) { uError() << view->umlScene()->name() << ": view->getFolder() returns NULL"; return; } m_diagramsModel->removeDiagram(view); f->removeView(view); UMLView *currentView = UMLApp::app()->currentView(); if (currentView == view) { UMLApp::app()->setCurrentView(0); UMLViewList viewList; m_root[Uml::ModelType::Logical]->appendViews(viewList); UMLView* firstView = 0; if (!viewList.isEmpty()) { firstView = viewList.first(); } if (!firstView && enforceCurrentView) { //create a diagram QString name = createDiagramName(Uml::DiagramType::Class, false); createDiagram(m_root[Uml::ModelType::Logical], Uml::DiagramType::Class, name); qApp->processEvents(); m_root[Uml::ModelType::Logical]->appendViews(viewList); firstView = viewList.first(); } if (firstView) { changeCurrentView(firstView->umlScene()->ID()); UMLApp::app()->setDiagramMenuItemsState(true); } } delete view; } /** * Sets the URL of the document. * * @param url The KUrl to set. */ #if QT_VERSION >= 0x050000 void UMLDoc::setUrl(const QUrl &url) #else void UMLDoc::setUrl(const KUrl &url) #endif { m_doc_url = url; } /** * Returns the KUrl of the document. * * @return The KUrl of this UMLDoc. */ #if QT_VERSION >= 0x050000 const QUrl& UMLDoc::url() const #else const KUrl& UMLDoc::url() const #endif { return m_doc_url; } /** * Sets the URL of the document to "Untitled". */ void UMLDoc::setUrlUntitled() { #if QT_VERSION >= 0x050000 m_doc_url.setUrl(m_doc_url.toString(QUrl::RemoveFilename) + i18n("Untitled")); #else m_doc_url.setFileName(i18n("Untitled")); #endif } /** * "save modified" - Asks the user for saving if the document * is modified. * * @return True if document can be closed. */ 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?"), i18nc("warning message", "Warning"), KStandardGuiItem::save(), KStandardGuiItem::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; } /** * Closes the current document. */ void UMLDoc::closeDocument() { m_bClosing = true; UMLApp::app()->setGenerator(Uml::ProgrammingLanguage::Reserved); // delete the codegen m_Doc = QString(); DocWindow* dw = UMLApp::app()->docWindow(); if (dw) { dw->reset(); } UMLApp::app()->logWindow()->clear(); UMLListView *listView = UMLApp::app()->listView(); if (listView) { listView->clean(); // 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. removeAllObjects(); // Remove any stereotypes. if (stereotypes().count() > 0) { foreach(UMLStereotype *s, stereotypes()) { m_stereotypesModel->removeStereotype(s); delete s; } m_stereoList.clear(); } // Restore the datatype folder, it has been deleted above. createDatatypeFolder(); // this creates to much items only Logical View should be created listView->init(); } m_bClosing = false; } /** * Initializes the document generally. * * @return True if operation successful. */ bool UMLDoc::newDocument() { bool state = UMLApp::app()->document()->loading(); UMLApp::app()->document()->setLoading(true); closeDocument(); UMLApp::app()->setCurrentView(0); setUrlUntitled(); setResolution(qApp->desktop()->logicalDpiX()); //see if we need to start with a new diagram Settings::OptionState optionState = Settings::optionState(); Uml::DiagramType::Enum dt = optionState.generalState.diagram; Uml::ModelType::Enum mt = Model_Utils::convert_DT_MT(dt); if (mt == Uml::ModelType::N_MODELTYPES) { // don't allow no diagram dt = Uml::DiagramType::Class; mt = Uml::ModelType::Logical; } QString name = createDiagramName(dt, false); createDiagram(m_root[mt], dt, name); UMLApp::app()->initGenerator(); setModified(false); initSaveTimer(); UMLApp::app()->enableUndoAction(false); UMLApp::app()->clearUndoStack(); UMLApp::app()->document()->setLoading(state); return true; } /** * Loads the document by filename and format and emits the * updateViews() signal. * * @param url The filename in KUrl format. * @param format The format (optional.) * @return True if operation successful. */ #if QT_VERSION >= 0x050000 bool UMLDoc::openDocument(const QUrl& url, const char* format /* =0 */) #else bool UMLDoc::openDocument(const KUrl& url, const char* format /* =0 */) #endif { Q_UNUSED(format); if (url.fileName().length() == 0) { newDocument(); return false; } m_doc_url = url; closeDocument(); setResolution(0.0); // IMPORTANT: set m_bLoading to true // _AFTER_ the call of UMLDoc::closeDocument() // as it sets m_bLoading to false after it was temporarily // changed to true to block recording of changes in redo-buffer m_bLoading = true; #if QT_VERSION >= 0x050000 QTemporaryFile tmpfile; tmpfile.open(); QUrl dest(QUrl::fromLocalFile(tmpfile.fileName())); DEBUG(DBG_SRC) << "UMLDoc::openDocument: copy from " << url << " to " << dest << "."; KIO::FileCopyJob *job = KIO::file_copy(url, dest, -1, KIO::Overwrite); KJobWidgets::setWindow(job, UMLApp::app()); job->exec(); QFile file(tmpfile.fileName()); if (job->error() || !tmpfile.exists()) { if (!tmpfile.exists()) DEBUG(DBG_SRC) << "UMLDoc::openDocument: temporary file <" << tmpfile.fileName() << "> failed!"; if (job->error()) DEBUG(DBG_SRC) << "UMLDoc::openDocument: " << job->errorString(); KMessageBox::error(0, i18n("The file <%1> does not exist.", url.toString()), i18n("Load Error")); setUrlUntitled(); m_bLoading = false; newDocument(); return false; } #else QString tmpfile; KIO::NetAccess::download(url, tmpfile, UMLApp::app()); QFile file(tmpfile); if (!file.exists()) { KMessageBox::error(0, i18n("The file %1 does not exist.", url.pathOrUrl()), i18n("Load Error")); setUrlUntitled(); m_bLoading = false; newDocument(); return false; } #endif // status of XMI loading bool status = false; // check if the xmi file is a compressed archive like tar.bzip2 or tar.gz QString filetype = m_doc_url.fileName(); QString mimetype; if (filetype.endsWith(QLatin1String(".tgz")) || filetype.endsWith(QLatin1String(".tar.gz"))) { mimetype = QLatin1String("application/x-gzip"); } else if (filetype.endsWith(QLatin1String(".tar.bz2"))) { mimetype = QLatin1String("application/x-bzip"); } if (mimetype.isEmpty() == false) { #if QT_VERSION >= 0x050000 KTar archive(tmpfile.fileName(), mimetype); #else KTar archive(tmpfile, mimetype); #endif if (archive.open(QIODevice::ReadOnly) == false) { #if QT_VERSION >= 0x050000 KMessageBox::error(0, i18n("The file %1 seems to be corrupted.", url.toString()), i18n("Load Error")); #else KMessageBox::error(0, i18n("The file %1 seems to be corrupted.", url.pathOrUrl()), i18n("Load Error")); KIO::NetAccess::removeTempFile(tmpfile); #endif setUrlUntitled(); m_bLoading = false; newDocument(); return false; } // get the root directory and all entries in const KArchiveDirectory * rootDir = archive.directory(); const QStringList entries = rootDir->entries(); QString entryMimeType; bool foundXMI = false; QStringList::ConstIterator it; QStringList::ConstIterator 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 #if QT_VERSION >= 0x050000 QMimeDatabase db; entryMimeType = db.mimeTypeForFile(*it, QMimeDatabase::MatchExtension).name(); #else entryMimeType = KMimeType::findByPath(*it, 0, true)->name(); #endif if (entryMimeType == QLatin1String("application/x-uml")) { foundXMI = true; break; } } } // if we found an XMI file, we have to extract it to a temporary file if (foundXMI == true) { #if QT_VERSION >= 0x050000 QTemporaryDir tmp_dir; #else KTempDir tmp_dir; #endif 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) { #if QT_VERSION >= 0x050000 KMessageBox::error(0, i18n("There was no XMI file found in the compressed file %1.", url.toString()), i18n("Load Error")); #else KMessageBox::error(0, i18n("There was no XMI file found in the compressed file %1.", url.pathOrUrl()), i18n("Load Error")); KIO::NetAccess::removeTempFile(tmpfile); #endif setUrlUntitled(); 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) { #if QT_VERSION >= 0x050000 KMessageBox::error(0, i18n("There was no XMI file found in the compressed file %1.", url.toString()), i18n("Load Error")); #else KMessageBox::error(0, i18n("There was no XMI file found in the compressed file %1.", url.pathOrUrl()), i18n("Load Error")); KIO::NetAccess::removeTempFile(tmpfile); #endif setUrlUntitled(); m_bLoading = false; newDocument(); return false; } // now we can extract the file to the temporary directory #if QT_VERSION >= 0x050000 fileEntry->copyTo(tmp_dir.path() + QLatin1Char('/')); // now open the extracted file for reading QFile xmi_file(tmp_dir.path() + QLatin1Char('/') + *it); #else fileEntry->copyTo(tmp_dir.name()); // now open the extracted file for reading QFile xmi_file(tmp_dir.name() + *it); #endif if(!xmi_file.open(QIODevice::ReadOnly)) { #if QT_VERSION >= 0x050000 KMessageBox::error(0, i18n("There was a problem loading the extracted file: %1", url.toString()), i18n("Load Error")); #else KMessageBox::error(0, i18n("There was a problem loading the extracted file: %1", url.pathOrUrl()), i18n("Load Error")); KIO::NetAccess::removeTempFile(tmpfile); #endif setUrlUntitled(); m_bLoading = false; newDocument(); return false; } m_bTypesAreResolved = false; status = loadFromXMI1(xmi_file, ENC_UNKNOWN); // close the extracted file and the temporary directory xmi_file.close(); } else { #if QT_VERSION >= 0x050000 KMessageBox::error(0, i18n("There was no XMI file found in the compressed file %1.", url.toString()), i18n("Load Error")); #else KMessageBox::error(0, i18n("There was no XMI file found in the compressed file %1.", url.pathOrUrl()), i18n("Load Error")); KIO::NetAccess::removeTempFile(tmpfile); #endif setUrlUntitled(); m_bLoading = false; newDocument(); return false; } archive.close(); } else { // no, it seems to be an ordinary file if (!file.open(QIODevice::ReadOnly)) { #if QT_VERSION >= 0x050000 KMessageBox::error(0, i18n("There was a problem loading file: %1", url.toString()), i18n("Load Error")); #else KMessageBox::error(0, i18n("There was a problem loading file: %1", url.pathOrUrl()), i18n("Load Error")); KIO::NetAccess::removeTempFile(tmpfile); #endif setUrlUntitled(); m_bLoading = false; newDocument(); return false; } if (filetype.endsWith(QLatin1String(".mdl"))) { setUrlUntitled(); m_bTypesAreResolved = false; status = Import_Rose::loadFromMDL(file); if (status) { if (UMLApp::app()->currentView() == 0) { QString name = createDiagramName(Uml::DiagramType::Class, false); createDiagram(m_root[Uml::ModelType::Logical], Uml::DiagramType::Class, name); setCurrentRoot(Uml::ModelType::Logical); } } } else if (filetype.endsWith(QLatin1String(".zargo"))) { setUrlUntitled(); status = Import_Argo::loadFromZArgoFile(file); } else { m_bTypesAreResolved = false; status = loadFromXMI1(file, ENC_UNKNOWN); } } if (file.isOpen()) file.close(); #if QT_VERSION < 0x050000 KIO::NetAccess::removeTempFile(tmpfile); #endif m_bLoading = false; m_bTypesAreResolved = true; if (!status) { #if QT_VERSION >= 0x050000 KMessageBox::error(0, i18n("There was a problem loading file: %1", url.toString()), i18n("Load Error")); #else KMessageBox::error(0, i18n("There was a problem loading file: %1", url.pathOrUrl()), i18n("Load Error")); #endif newDocument(); return false; } setModified(false); initSaveTimer(); UMLApp::app()->enableUndoAction(false); UMLApp::app()->clearUndoStack(); // for compatibility addDefaultStereotypes(); return true; } /** * Saves the document using the given filename and format. * * @param url The filename in KUrl format. * @param format The format (optional.) * @return True if operation successful. */ #if QT_VERSION >= 0x050000 bool UMLDoc::saveDocument(const QUrl& url, const char * format) #else bool UMLDoc::saveDocument(const KUrl& url, const char * format) #endif { Q_UNUSED(format); m_doc_url = url; bool uploaded = true; // first, we have to find out which format to use #if QT_VERSION >= 0x050000 QString strFileName = url.path(); #else QString strFileName = url.path(KUrl::RemoveTrailingSlash); #endif QFileInfo fileInfo(strFileName); QString fileExt = fileInfo.completeSuffix(); QString fileFormat = QLatin1String("xmi"); if (fileExt == QLatin1String("xmi") || fileExt == QLatin1String("bak.xmi")) { fileFormat = QLatin1String("xmi"); } else if (fileExt == QLatin1String("xmi.tgz") || fileExt == QLatin1String("bak.xmi.tgz")) { fileFormat = QLatin1String("tgz"); } else if (fileExt == QLatin1String("xmi.tar.bz2") || fileExt == QLatin1String("bak.xmi.tar.bz2")) { fileFormat = QLatin1String("bz2"); } else { fileFormat = QLatin1String("xmi"); } initSaveTimer(); if (fileFormat == QLatin1String("tgz") || fileFormat == QLatin1String("bz2")) { KTar * archive; #if QT_VERSION >= 0x050000 QTemporaryFile tmp_tgz_file; #else KTemporaryFile tmp_tgz_file; #endif tmp_tgz_file.setAutoRemove(false); tmp_tgz_file.open(); // first we have to check if we are saving to a local or remote file if (url.isLocalFile()) { if (fileFormat == QLatin1String("tgz")) { // check tgz or bzip archive = new KTar(url.toLocalFile(), QLatin1String("application/x-gzip")); } else { archive = new KTar(url.toLocalFile(), QLatin1String("application/x-bzip")); } } else { if (fileFormat == QLatin1String("tgz")) { // check tgz or bzip2 archive = new KTar(tmp_tgz_file.fileName(), QLatin1String("application/x-gzip")); } else { archive = new KTar(tmp_tgz_file.fileName(), QLatin1String("application/x-bzip")); } } // now check if we can write to the file if (archive->open(QIODevice::WriteOnly) == false) { uError() << "could not open" << archive->fileName(); #if QT_VERSION >= 0x050000 KMessageBox::error(0, i18n("There was a problem saving: %1", url.url(QUrl::PreferLocalFile)), i18n("Save Error")); #else KMessageBox::error(0, i18n("There was a problem saving file: %1", url.pathOrUrl()), i18n("Save Error")); #endif delete archive; return false; } // we have to create a temporary xmi file // we will add this file later to the archive #if QT_VERSION >= 0x050000 QTemporaryFile tmp_xmi_file; #else KTemporaryFile tmp_xmi_file; #endif tmp_xmi_file.setAutoRemove(false); if (!tmp_xmi_file.open()) { uError() << "could not open" << tmp_xmi_file.fileName(); #if QT_VERSION >= 0x050000 KMessageBox::error(0, i18n("There was a problem saving: %1", url.url(QUrl::PreferLocalFile)), i18n("Save Error")); #else KMessageBox::error(0, i18n("There was a problem saving file: %1", url.pathOrUrl()), i18n("Save Error")); #endif delete archive; return false; } saveToXMI1(tmp_xmi_file); // save XMI to this file... // now add this file to the archive, but without the extension QString tmpQString = url.fileName(); if (fileFormat == QLatin1String("tgz")) { tmpQString.remove(QRegExp(QLatin1String("\\.tgz$"))); } else { tmpQString.remove(QRegExp(QLatin1String("\\.tar\\.bz2$"))); } archive->addLocalFile(tmp_xmi_file.fileName(), tmpQString); if (!archive->close()) { uError() << "could not close" << archive->fileName(); #if QT_VERSION >= 0x050000 KMessageBox::error(0, i18n("There was a problem saving: %1", url.url(QUrl::PreferLocalFile)), i18n("Save Error")); #else KMessageBox::error(0, i18n("There was a problem saving file: %1", url.pathOrUrl()), i18n("Save Error")); #endif delete archive; return false; } // now the xmi file was added to the archive, so we can delete it tmp_xmi_file.setAutoRemove(true); // now we have to check, if we have to upload the file if (!url.isLocalFile()) { #if QT_VERSION >= 0x050000 KIO::FileCopyJob *job = KIO::file_copy(QUrl::fromLocalFile(tmp_tgz_file.fileName()), m_doc_url); KJobWidgets::setWindow(job, UMLApp::app()); job->exec(); uploaded = !job->error(); #else uploaded = KIO::NetAccess::upload(tmp_tgz_file.fileName(), m_doc_url, UMLApp::app()); #endif if (!uploaded) uError() << "could not upload file" << tmp_tgz_file.fileName() << "to" << url; } // now the archive was written to disk (or remote) so we can delete the // objects tmp_tgz_file.setAutoRemove(true); delete archive; } else { // save as normal uncompressed XMI #if QT_VERSION >= 0x050000 QTemporaryFile tmpfile; // we need this tmp file if we are writing to a remote file #else KTemporaryFile tmpfile; // we need this tmp file if we are writing to a remote file #endif tmpfile.setAutoRemove(false); // save in _any_ case to a temp file // -> if something goes wrong during saveToXMI1, the // original content is preserved // (e.g. if umbrello dies in the middle of the document model parsing // for saveToXMI1 due to some problems) /// @todo insert some checks in saveToXMI1 to detect a failed save attempt // lets open the file for writing if (!tmpfile.open()) { uError() << "could not open" << tmpfile.fileName(); #if QT_VERSION >= 0x050000 KMessageBox::error(0, i18n("There was a problem saving: %1", url.url(QUrl::PreferLocalFile)), i18n("Save Error")); #else KMessageBox::error(0, i18n("There was a problem saving file: %1", url.pathOrUrl()), i18n("Save Error")); #endif return false; } saveToXMI1(tmpfile); // save the xmi stuff to it // if it is a remote file, we have to upload the tmp file if (!url.isLocalFile()) { #if QT_VERSION >= 0x050000 KIO::FileCopyJob *job = KIO::file_copy(QUrl::fromLocalFile(tmpfile.fileName()), m_doc_url); KJobWidgets::setWindow(job, UMLApp::app()); job->exec(); uploaded = !job->error(); #else uploaded = KIO::NetAccess::upload(tmpfile.fileName(), m_doc_url, UMLApp::app()); #endif if (!uploaded) uError() << "could not upload file" << tmpfile.fileName() << "to" << url; } else { // now remove the original file #ifdef Q_OS_WIN tmpfile.setAutoRemove(true); #if QT_VERSION >= 0x050000 KIO::FileCopyJob* fcj = KIO::file_copy(QUrl::fromLocalFile(tmpfile.fileName()), url, -1, KIO::Overwrite); #else KIO::FileCopyJob* fcj = KIO::file_copy(tmpfile.fileName(), url, -1, KIO::Overwrite); #endif #else #if QT_VERSION >= 0x050000 KIO::FileCopyJob* fcj = KIO::file_move(QUrl::fromLocalFile(tmpfile.fileName()), url, -1, KIO::Overwrite); #else KIO::FileCopyJob* fcj = KIO::file_move(tmpfile.fileName(), url, -1, KIO::Overwrite); #endif #endif #if QT_VERSION >= 0x050000 KJobWidgets::setWindow(fcj, (QWidget*)UMLApp::app()); fcj->exec(); if (fcj->error()) { uError() << "Could not move" << tmpfile.fileName() << "to" << url; KMessageBox::error(0, i18n("There was a problem saving: %1", url.url(QUrl::PreferLocalFile)), i18n("Save Error")); #else if (KIO::NetAccess::synchronousRun(fcj, (QWidget*)UMLApp::app()) == false) { KMessageBox::error(0, i18n("There was a problem saving file: %1", url.pathOrUrl()), i18n("Save Error")); #endif setUrlUntitled(); return false; } } } if (!uploaded) { #if QT_VERSION >= 0x050000 KMessageBox::error(0, i18n("There was a problem uploading: %1", url.url(QUrl::PreferLocalFile)), i18n("Save Error")); #else KMessageBox::error(0, i18n("There was a problem uploading file: %1", url.pathOrUrl()), i18n("Save Error")); #endif setUrlUntitled(); } setModified(false); return uploaded; } /** * Sets up the signals needed by the program for it to work. */ void UMLDoc::setupSignals() { WorkToolBar *tb = UMLApp::app()->workToolBar(); connect(this, SIGNAL(sigDiagramChanged(Uml::DiagramType::Enum)), tb, SLOT(slotCheckToolBar(Uml::DiagramType::Enum))); } /** * Finds a view (diagram) by the ID given to method. * * @param id The ID of the view to search for. * @return Pointer to the view found, or NULL if not found. */ UMLView * UMLDoc::findView(Uml::ID::Type id) { UMLView *v = 0; for (int i = 0; i < Uml::ModelType::N_MODELTYPES; ++i) { v = m_root[i]->findView(id); if (v) { break; } } return v; } /** * Finds a view (diagram) by the type and name given. * * @param type The type of view to find. * @param name The name of the view to find. * @param searchAllScopes Search in all subfolders (default: false.) * @return Pointer to the view found, or NULL if not found. */ UMLView * UMLDoc::findView(Uml::DiagramType::Enum type, const QString &name, bool searchAllScopes /* =false */) { Uml::ModelType::Enum mt = Model_Utils::convert_DT_MT(type); return m_root[mt]->findView(type, name, searchAllScopes); } /** * Used to find a reference to a @ref UMLObject by its ID. * * @param id The @ref UMLObject to find. * @return Pointer to the UMLObject found, or NULL if not found. */ UMLObject* UMLDoc::findObjectById(Uml::ID::Type id) { UMLObject *o = 0; for (int i = 0; i < Uml::ModelType::N_MODELTYPES; ++i) { if (id == m_root[i]->id()) { return m_root[i]; } o = m_root[i]->findObjectById(id); if (o) { return o; } } o = findStereotypeById(id); return o; } /** * Used to find a @ref UMLObject by its type and name. * * @param name The name of the @ref UMLObject to find. * @param type ObjectType of the object to find (optional.) * When the given type is ot_UMLObject the type is * disregarded, i.e. the given name is the only * search criterion. * @param currentObj Object relative to which to search (optional.) * If given then the enclosing scope(s) of this * object are searched before the global scope. * @return Pointer to the UMLObject found, or NULL if not found. */ UMLObject* UMLDoc::findUMLObject(const QString &name, UMLObject::ObjectType type /* = ot_UMLObject */, UMLObject *currentObj /* = 0 */) { UMLObject *o = m_datatypeRoot->findObject(name); if (o) { return o; } for (int i = 0; i < Uml::ModelType::N_MODELTYPES; ++i) { UMLObjectList &list = m_root[i]->containedObjects(); if (list.size() == 0) continue; o = Model_Utils::findUMLObject(list, name, type, currentObj); if (o) { return o; } if ((type == UMLObject::ot_UMLObject || type == UMLObject::ot_Folder) && name == m_root[i]->name()) { return m_root[i]; } } return 0; } /** * Used to find a @ref UMLObject by its type and raw name. * * @param modelType The model type in which to search for the object * @param name The raw name of the @ref UMLObject to find. * @param type ObjectType of the object to find * @return Pointer to the UMLObject found, or NULL if not found. */ UMLObject* UMLDoc::findUMLObjectRaw(Uml::ModelType::Enum modelType, const QString &name, UMLObject::ObjectType type) { return findUMLObjectRaw(rootFolder(modelType), name, type); } /** * Used to find a @ref UMLObject by its type and raw name. * * @param folder The UMLFolder in which to search for the object * @param name The raw name of the @ref UMLObject to find. * @param type ObjectType of the object to find * @return Pointer to the UMLObject found, or NULL if not found. */ UMLObject* UMLDoc::findUMLObjectRaw(UMLFolder *folder, const QString &name, UMLObject::ObjectType type) { if (folder == 0) return 0; UMLObjectList &list = folder->containedObjects(); if (list.size() == 0) return 0; return Model_Utils::findUMLObjectRaw(list, name, type, 0); } +/** + * Used to find a @ref UMLObject by its type and raw name recursivly + * + * @param modelType The model type in which to search for the object + * @param name The raw name of the @ref UMLObject to find. + * @param type ObjectType of the object to find + * @return Pointer to the UMLObject found, or NULL if not found. + */ +UMLObject* UMLDoc::findUMLObjectRecursive(Uml::ModelType::Enum modelType, + const QString &name, + UMLObject::ObjectType type) +{ + return findUMLObjectRecursive(rootFolder(modelType), name, type); +} + +/** + * Used to find a @ref UMLObject by its type and raw name recursivly + * + * @param folder The UMLFolder in which to search for the object + * @param name The raw name of the @ref UMLObject to find. + * @param type ObjectType of the object to find + * @return Pointer to the UMLObject found, or NULL if not found. + */ +UMLObject* UMLDoc::findUMLObjectRecursive(UMLFolder *folder, + const QString &name, + UMLObject::ObjectType type) +{ + if (folder == 0) + return 0; + UMLObjectList &list = folder->containedObjects(); + if (list.size() == 0) + return 0; + return Model_Utils::findUMLObjectRecursive(list, name, type); +} + /** * Used to find a @ref UMLClassifier by its name. * * @param name The name of the @ref UMLObject to find. */ UMLClassifier* UMLDoc::findUMLClassifier(const QString &name) { //this is used only by code generator so we don't need to look at Datatypes UMLObject * obj = findUMLObject(name); return obj->asUMLClassifier(); } /** * Adds a UMLObject thats already created but doesn't change * any ids or signal. Use AddUMLObjectPaste if pasting. * * @param object The object to add. * @return True if the object was actually added. */ bool UMLDoc::addUMLObject(UMLObject* object) { if (object->isUMLStereotype()) { DEBUG(DBG_SRC) << object->name() << ": not adding type " << object->baseTypeStr(); return false; } UMLPackage *pkg = object->umlPackage(); if (pkg == 0) { pkg = currentRoot(); DEBUG(DBG_SRC) << object->name() << ": no parent package set, assuming " << pkg->name(); object->setUMLPackage(pkg); } // FIXME restore stereotype UMLClassifierListItem *c = object->asUMLClassifierListItem(); if (c) { if (!pkg->subordinates().contains(c)) pkg->subordinates().append(c); return true; } return pkg->addObject(object); } /** * Write text to the status bar. * @param text the text to write */ void UMLDoc::writeToStatusBar(const QString &text) { emit sigWriteToStatusBar(text); } /** * Simple removal of an object. * @param object the UMLObject to be removed */ void UMLDoc::slotRemoveUMLObject(UMLObject* object) { //m_objectList.remove(object); UMLPackage *pkg = object->umlPackage(); if (pkg == 0) { uError() << object->name() << ": parent package is not set !"; return; } pkg->removeObject(object); } /** * Returns true if the given name is unique within its scope. * * @param name The name to check. * @return True if name is unique. */ bool UMLDoc::isUnique(const QString &name) { UMLListView *listView = UMLApp::app()->listView(); 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->type())) { return isUnique (name, (UMLPackage*) currentItem->umlObject()); } parentItem = (UMLListViewItem*)currentItem->parent(); } // item is in a package so do check only in that if (parentItem != 0 && Model_Utils::typeIsContainer(parentItem->type())) { UMLPackage *parentPkg = parentItem->umlObject()->asUMLPackage(); return isUnique(name, parentPkg); } uError() << name << ": Not currently in a package"; /* 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() == 0) && (obj->getName() == name)) return false; } */ return true; } /** * Returns true if the given name is unique within its scope of given package. * * @param name The name to check. * @param package The UMLPackage in which we have to determine the unique-ness * @return True if name is unique. */ bool UMLDoc::isUnique(const QString &name, UMLPackage *package) { // if a package, then only do check in that if (package) { return (package->findObject(name) == 0); } // Not currently in a package: ERROR uError() << name << " (2): Not currently in a package"; /* 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() == 0) && (obj->getName() == name)) return false; } */ return true; } /** * Creates a stereotype for the parent object. * @param name the name of the stereotype */ UMLStereotype* UMLDoc::createStereotype(const QString &name) { UMLStereotype *s = new UMLStereotype(name, Uml::ID::fromString(name)); addStereotype(s); return s; } /** * Finds a UMLStereotype by its name. * * @param name The name of the UMLStereotype to find. * @return Pointer to the UMLStereotype found, or NULL if not found. */ UMLStereotype* UMLDoc::findStereotype(const QString &name) { foreach (UMLStereotype *s, m_stereoList) { if (s->name() == name) { return s; } } return 0; } /** * Finds or creates a stereotype for the parent object. * @param name the name of the stereotype * @return the found stereotype object or a just created one */ UMLStereotype* UMLDoc::findOrCreateStereotype(const QString &name) { UMLStereotype *s = findStereotype(name); if (s != 0) { return s; } return createStereotype(name); } /** * Find a UMLStereotype by its unique ID. * @param id the unique ID * @return the found stereotype or NULL */ UMLStereotype * UMLDoc::findStereotypeById(Uml::ID::Type id) { foreach (UMLStereotype *s, m_stereoList) { if (s->id() == id) return s; } return 0; } /** * Add a UMLStereotype to the application. * @param s the stereotype to be added */ void UMLDoc::addStereotype(UMLStereotype *s) { if (m_stereotypesModel->addStereotype(s)) emit sigObjectCreated(s); } /** * Remove a UMLStereotype from the application. * @param s the stereotype to be removed */ void UMLDoc::removeStereotype(UMLStereotype *s) { if (m_stereotypesModel->removeStereotype(s)) emit sigObjectRemoved(s); } /** * Add a stereotype if it doesn't already exist. * Used by code generators, operations and attribute dialog. */ void UMLDoc::addDefaultStereotypes() { CodeGenerator *gen = UMLApp::app()->generator(); if (gen) { gen->createDefaultStereotypes(); } } /** * Returns a list of the stereotypes in this UMLDoc. * * @return List of UML stereotypes. */ const UMLStereotypeList& UMLDoc::stereotypes() const { return m_stereoList; } /** * Removes an association. * * @param assoc Pointer to the UMLAssociation to remove. * @param doSetModified Whether to mark the document as modified (default: true.) */ void UMLDoc::removeAssociation (UMLAssociation * assoc, bool doSetModified /*=true*/) { if (!assoc) { return; } // Remove the UMLAssociation from m_objectList. UMLPackage *pkg = assoc->umlPackage(); if (pkg == 0) { uError() << assoc->name() << ": parent package is not set !"; return; } pkg->removeObject(assoc); if (doSetModified) { // so we will save our document setModified(true); } } /** * Finds an association. * * @param assocType Type of the UMLAssociation to seek. * @param roleAObj Pointer to the role A UMLCanvasObject. * @param roleBObj Pointer to the role B UMLCanvasObject. * @param swap Optional pointer to boolean. * The bool is set to true if the association * matched with swapped roles, else it is set * to false. * @return Pointer to the UMLAssociation found or NULL if not found. */ UMLAssociation * UMLDoc::findAssociation(Uml::AssociationType::Enum assocType, const UMLObject *roleAObj, const UMLObject *roleBObj, bool *swap) { UMLAssociationList assocs = associations(); UMLAssociation *ret = 0; foreach (UMLAssociation* a, assocs) { if (a->getAssocType() != assocType) { continue; } if (a->getObject(Uml::RoleType::A) == roleAObj && a->getObject(Uml::RoleType::B) == roleBObj) { return a; } if (a->getObject(Uml::RoleType::A) == roleBObj && a->getObject(Uml::RoleType::B) == roleAObj) { ret = a; } } if (swap) { *swap = (ret != 0); } return ret; } /** * Creates AND adds an association between two UMLObjects. * Used by refactoring assistant. * NOTE: this method does not check if the association is valid / legal * * @param a The UMLObject "A" for the association (source) * @param b The UMLObject "B" for the association (destination) * @param type The association's type * @return The Association created */ UMLAssociation* UMLDoc::createUMLAssociation(UMLObject *a, UMLObject *b, Uml::AssociationType::Enum type) { bool swap; UMLAssociation *assoc = findAssociation(type, a, b, &swap); if (assoc == 0) { assoc = new UMLAssociation(type, a, b); assoc->setUMLPackage(a->umlPackage()); addAssociation(assoc); } return assoc; } /** * Adds an association. * * @param assoc Pointer to the UMLAssociation to add. */ void UMLDoc::addAssociation(UMLAssociation *assoc) { if (assoc == 0) { 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 = associations(); foreach (UMLAssociation* a, assocs) { // 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) { DEBUG(DBG_SRC) << "duplicate addition attempted"; return; } } // If we get here it's really a new association. // Add the UMLAssociation at the owning UMLPackage. UMLPackage *pkg = assoc->umlPackage(); if (pkg == 0) { uError() << assoc->name() << ": parent package is not set !"; return; } pkg->addObject(assoc); // I don't believe this appropriate, UMLAssociations ARENT UMLWidgets -b.t. // emit sigObjectCreated(o); setModified(true); } /** * Returns a name for the new object, appended with a number * if the default name is taken e.g. class diagram, class * diagram_1 etc. * @param type the diagram type * @return the unique view name */ QString UMLDoc::uniqueViewName(const Uml::DiagramType::Enum type) { QString dname; switch (type) { case Uml::DiagramType::UseCase: dname = i18n("use case diagram"); break; case Uml::DiagramType::Class: dname = i18n("class diagram"); break; case Uml::DiagramType::Sequence: dname = i18n("sequence diagram"); break; case Uml::DiagramType::Collaboration: dname = i18n("communication diagram"); break; case Uml::DiagramType::Object: dname = i18n("object diagram"); break; case Uml::DiagramType::State: dname = i18n("state diagram"); break; case Uml::DiagramType::Activity: dname = i18n("activity diagram"); break; case Uml::DiagramType::Component: dname = i18n("component diagram"); break; case Uml::DiagramType::Deployment: dname = i18n("deployment diagram"); break; case Uml::DiagramType::EntityRelationship: dname = i18n("entity relationship diagram"); break; default: uWarning() << "called with unknown diagram type"; break; } QString name = dname; for (int number = 1; findView(type, name, true); ++number) { name = dname + QLatin1Char('_') + QString::number(number); } return name; } /** * Returns true when loading a document file. * @return the value of the flag */ bool UMLDoc::loading() const { return m_bLoading || !m_bTypesAreResolved; } /** * Sets loading boolean flag to the value given. * @param state value to set */ void UMLDoc::setLoading(bool state /* = true */) { m_bLoading = state; } /** * Returns true when importing file(s). * @return the value of the flag */ bool UMLDoc::importing() const { return m_importing; } /** * Sets importing boolean flag to the value given. * @param state value to set */ void UMLDoc::setImporting(bool state /* = true */) { m_importing = state; } /** * Returns the m_bClosing flag. * @return the value of the flag */ bool UMLDoc::closing() const { return m_bClosing; } /** * Creates the name of the given diagram type. * @param type The type of diagram to create. * @param askForName If true shows a dialog box asking for name, * else uses a default name. * @return name of the new diagram */ QString UMLDoc::createDiagramName(Uml::DiagramType::Enum type, bool askForName /*= true */) { QString defaultName = uniqueViewName(type); QString name = defaultName; while (true) { if (askForName && !Dialog_Utils::askName(i18nc("diagram name", "Name"), i18n("Enter name:"), name)) 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)) { KMessageBox::error(0, i18n("A diagram is already using that name."), i18n("Not a Unique Name")); } else { return name; } } // end while return QString(); } /** * Creates a diagram of the given type. * * @param folder the folder in which tp create the diagram. * @param type the type of diagram to create * @param name the name for the diagram to create * @param id optional ID of new diagram * @return pointer to the UMLView of the new diagram */ UMLView* UMLDoc::createDiagram(UMLFolder *folder, Uml::DiagramType::Enum type, const QString& name, Uml::ID::Type id) { DEBUG(DBG_SRC) << "folder=" << folder->name() << " / type=" << Uml::DiagramType::toString(type) << " / name=" << name; if (id == Uml::ID::None) { id = UniqueID::gen(); } if (name.length() > 0) { UMLView* view = new UMLView(folder); view->umlScene()->setOptionState(Settings::optionState()); view->umlScene()->setName(name); view->umlScene()->setType(type); view->umlScene()->setID(id); addView(view); emit sigDiagramCreated(id); setModified(true); UMLApp::app()->enablePrint(true); changeCurrentView(id); return view; } return 0; } /** * Used to rename a document. This method takes care of everything. * You just need to give the ID of the diagram to the method. * * @param id The ID of the diagram to rename. */ void UMLDoc::renameDiagram(Uml::ID::Type id) { UMLView *view = findView(id); Uml::DiagramType::Enum type = view->umlScene()->type(); QString name = view->umlScene()->name(); while (true) { bool ok = Dialog_Utils::askName(i18nc("renaming diagram", "Name"), i18n("Enter name:"), name); 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)) { view->umlScene()->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")); } } } /** * Used to rename a @ref UMLObject. The @ref UMLObject is to be an * actor, use case or concept. * * @param o The object to rename. */ void UMLDoc::renameUMLObject(UMLObject *o) { QString name = o->name(); while (true) { bool ok = Dialog_Utils::askName(i18nc("renaming uml object", "Name"), i18n("Enter name:"), name); if (!ok) { break; } if (name.length() == 0) { KMessageBox::error(0, i18n("That is an invalid name."), i18n("Invalid Name")); } else if (isUnique(name)) { UMLApp::app()->executeCommand(new Uml::CmdRenameUMLObject(o, name)); setModified(true); break; } else { KMessageBox::error(0, i18n("That name is already being used."), i18n("Not a Unique Name")); } } return; } /** * Used to rename an operation or attribute of a concept. * * @param o The attribute or operation to rename. */ void UMLDoc::renameChildUMLObject(UMLObject *o) { UMLClassifier* p = o->umlParent()->asUMLClassifier(); if (!p) { DEBUG(DBG_SRC) << "Cannot create object, no parent found."; return; } QString name = o->name(); while (true) { bool ok = Dialog_Utils::askName(i18nc("renaming child uml object", "Name"), i18n("Enter name:"), name); if (!ok) { break; } if (name.length() == 0) { KMessageBox::error(0, i18n("That is an invalid name."), i18n("Invalid Name")); } else if (p->findChildObject(name) == 0 || ((o->baseType() == UMLObject::ot_Operation) && KMessageBox::warningYesNo(0, i18n("The name you entered was not unique.\nIs this what you wanted?"), i18n("Name Not Unique"), KGuiItem(i18n("Use Name")), KGuiItem(i18n("Enter New Name"))) == KMessageBox::Yes)) { UMLApp::app()->executeCommand(new Uml::CmdRenameUMLObject(o, name)); setModified(true); break; } else { KMessageBox::error(0, i18n("That name is already being used."), i18n("Not a Unique Name")); } } } /** * Changes the current view (diagram) to the view with the given ID. * * @param id The ID of the view to change to. */ void UMLDoc::changeCurrentView(Uml::ID::Type id) { DEBUG(DBG_SRC) << "id=" << Uml::ID::toString(id); UMLView* view = findView(id); if (view) { UMLScene* scene = view->umlScene(); scene->setIsOpen(true); UMLApp::app()->setCurrentView(view); emit sigDiagramChanged(scene->type()); UMLApp::app()->setDiagramMenuItemsState(true); setModified(true); emit sigCurrentViewChanged(); // when clicking on a tab, the documentation of diagram is upated in docwindow UMLApp::app()->docWindow()->showDocumentation(scene); } else { uWarning() << "New current view was not found with id=" << Uml::ID::toString(id) << "!"; } } /** * Deletes a diagram from the current file. * * Undo command * * @param id The ID of the diagram to delete. */ void UMLDoc::removeDiagram(Uml::ID::Type id) { UMLView* umlView = findView(id); if (!umlView) { uError() << "Request to remove diagram " << Uml::ID::toString(id) << ": Diagram not found!"; return; } UMLScene* umlScene = umlView->umlScene(); if (Dialog_Utils::askDeleteDiagram(umlScene->name())) { UMLApp::app()->executeCommand(new Uml::CmdRemoveDiagram( umlScene->folder(), umlScene->type(), umlScene->name(), id )); } } /** * Deletes a diagram from the current file. * * @param id The ID of the diagram to delete. */ void UMLDoc::removeDiagramCmd(Uml::ID::Type id) { UMLApp::app()->docWindow()->updateDocumentation(true); UMLView* umlview = findView(id); if (!umlview) { uError() << "Request to remove diagram " << Uml::ID::toString(id) << ": Diagram not found!"; return; } removeView(umlview); emit sigDiagramRemoved(id); setModified(true); } /** * Return the currently selected root folder. * This will be an element from the m_root[] array. * @return the currently selected root folder or NULL */ UMLFolder *UMLDoc::currentRoot() { UMLView *currentView = UMLApp::app()->currentView(); if (currentView == 0) { if (m_pCurrentRoot) { return m_pCurrentRoot; } uError() << "m_pCurrentRoot is NULL"; return 0; } UMLFolder *f = currentView->umlScene()->folder(); while (f && f->umlPackage()) { f = f->umlParent()->asUMLFolder(); } return f; } /** * Set the current root folder. * * @param rootType The type of the root folder to set. * The element from m_root[] which is indexed * by this type is selected. */ void UMLDoc::setCurrentRoot(Uml::ModelType::Enum rootType) { m_pCurrentRoot = m_root[rootType]; } /** * Removes an @ref UMLObject from the current file. If this object * is being represented on a diagram it will also delete all those * representations. * * @param umlobject Pointer to the UMLObject to delete. * @param deleteObject Delete the UMLObject instance. */ void UMLDoc::removeUMLObject(UMLObject* umlobject, bool deleteObject) { if (umlobject == 0) { uError() << "called with NULL parameter"; return; } UMLApp::app()->docWindow()->updateDocumentation(true); UMLObject::ObjectType type = umlobject->baseType(); umlobject->setUMLStereotype(0); // triggers possible cleanup of UMLStereotype if (umlobject->asUMLClassifierListItem()) { UMLClassifier* parent = umlobject->umlParent()->asUMLClassifier(); if (parent == 0) { uError() << "parent of umlobject is NULL"; return; } if (type == UMLObject::ot_Operation) { parent->removeOperation(umlobject->asUMLOperation()); if (deleteObject) delete umlobject->asUMLOperation(); } else if (type == UMLObject::ot_EnumLiteral) { UMLEnum *e = parent->asUMLEnum(); e->removeEnumLiteral(umlobject->asUMLEnumLiteral()); } else if (type == UMLObject::ot_EntityAttribute) { UMLEntity *ent = parent->asUMLEntity(); ent->removeEntityAttribute(umlobject->asUMLClassifierListItem()); } else if (type == UMLObject::ot_UniqueConstraint || type == UMLObject::ot_ForeignKeyConstraint || type == UMLObject::ot_CheckConstraint) { UMLEntity* ent = parent->asUMLEntity(); ent->removeConstraint(umlobject->asUMLEntityConstraint()); } else { UMLClassifier* pClass = parent->asUMLClassifier(); if (pClass == 0) { uError() << "parent of umlobject has unexpected type " << parent->baseType(); return; } if (type == UMLObject::ot_Attribute) { pClass->removeAttribute(umlobject->asUMLAttribute()); } else if (type == UMLObject::ot_Template) { pClass->removeTemplate(umlobject->asUMLTemplate()); if (deleteObject) delete umlobject->asUMLTemplate(); } else { uError() << "umlobject has unexpected type " << type; } } } else { if (type == UMLObject::ot_Association) { UMLAssociation *a = umlobject->asUMLAssociation(); removeAssociation(a, false); // don't call setModified here, it's done below emit sigObjectRemoved(umlobject); if (deleteObject) delete a; } else { UMLPackage* pkg = umlobject->umlPackage(); if (pkg) { // Remove associations that this object may participate in. UMLCanvasObject *c = umlobject->asUMLCanvasObject(); if (c) { // In the current implementation, all associations live in the // root folder. UMLPackage* rootPkg = Model_Utils::rootPackage(c); if (rootPkg == 0) { uError() << umlobject->name() << ": root package is not set !"; return; } UMLObjectList &rootObjects = rootPkg->containedObjects(); // Store the associations to remove in a buffer because we // should not remove elements from m_objectList while it is // being iterated over. UMLAssociationList assocsToRemove; foreach (UMLObject *obj, rootObjects) { uIgnoreZeroPointer(obj); if (obj->baseType() == UMLObject::ot_Association) { UMLAssociation *assoc = obj->asUMLAssociation(); if (c->hasAssociation(assoc)) { assocsToRemove.append(assoc); } } } foreach (UMLAssociation *a, assocsToRemove) { removeAssociation(a, false); } } pkg->removeObject(umlobject); emit sigObjectRemoved(umlobject); if (deleteObject) delete umlobject; } else { uError() << umlobject->name() << ": parent package is not set !"; } } } setModified(true); } /** * Signal that a UMLObject has been created. * * @param o The object that has been created. */ 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. */ } /** * Set the name of this model. */ void UMLDoc::setName(const QString& name) { m_Name = name; } /** * Return the name of this model. */ QString UMLDoc::name() const { return m_Name; } /** * Set coordinates resolution for current document. * @param resolution document resolution in DPI */ void UMLDoc::setResolution(qreal resolution) { m_resolution = resolution; uDebug() << "screen dpi:" << qApp->desktop()->logicalDpiX() << "file dpi:" << resolution << "scale:" << qApp->desktop()->logicalDpiX() / resolution; } /** * Returns coordinates resolution for current document. * @return document resolution in DPI */ qreal UMLDoc::resolution() const { return m_resolution; } /** * Returns scale factor for recalculation of document coordinates. * @return scale factor */ qreal UMLDoc::dpiScale() const { #ifdef ENABLE_XMIRESOLUTION if (resolution() != 0.0) return (qreal)qApp->desktop()->logicalDpiX() / resolution(); else #endif return 1.0; } /** * Return the m_modelID (currently this a fixed value: * Umbrello supports only a single document.) */ Uml::ID::Type UMLDoc::modelID() const { return m_modelID; } /** * This method is called for saving the given model as a XMI file. * It is virtual and calls the corresponding saveToXMI1() functions * of the derived classes. * * @param file The file to be saved to. */ void UMLDoc::saveToXMI1(QIODevice& file) { QDomDocument doc; QDomProcessingInstruction xmlHeading = doc.createProcessingInstruction(QLatin1String("xml"), QString::fromLatin1("version=\"1.0\" encoding=\"UTF-8\"")); doc.appendChild(xmlHeading); QDomElement root = doc.createElement(QLatin1String("XMI")); root.setAttribute(QLatin1String("xmi.version"), QLatin1String("1.2")); QDateTime now = QDateTime::currentDateTime(); root.setAttribute(QLatin1String("timestamp"), now.toString(Qt::ISODate)); root.setAttribute(QLatin1String("verified"), QLatin1String("false")); root.setAttribute(QLatin1String("xmlns:UML"), QLatin1String("http://schema.omg.org/spec/UML/1.4")); doc.appendChild(root); QDomElement header = doc.createElement(QLatin1String("XMI.header")); QDomElement meta = doc.createElement(QLatin1String("XMI.metamodel")); meta.setAttribute(QLatin1String("xmi.name"), QLatin1String("UML")); meta.setAttribute(QLatin1String("xmi.version"), QLatin1String("1.4")); meta.setAttribute(QLatin1String("href"), QLatin1String("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) " QDomElement model = doc.createElement("XMI.model"); QFile* qfile = dynamic_cast(&file); if (qfile) { QString modelName = qfile->name(); modelName = modelName.section('/', -1); modelName = modelName.section('.', 0, 0); model.setAttribute("xmi.name", modelName); model.setAttribute("href", qfile->name()); } */ QDomElement documentation = doc.createElement(QLatin1String("XMI.documentation")); // If we consider it useful we might add user and contact details // QDomElement owner = doc.createElement("XMI.owner"); // owner.appendChild(doc.createTextNode("Jens Kruger")); // Add a User // documentation.appendChild(owner); // QDomElement contact = doc.createElement("XMI.contact"); // contact.appendChild(doc.createTextNode("je.krueger@web.de")); // add a contact // documentation.appendChild(contact); QDomElement exporter = doc.createElement(QLatin1String("XMI.exporter")); exporter.appendChild(doc.createTextNode(QLatin1String("umbrello uml modeller http://umbrello.kde.org"))); documentation.appendChild(exporter); QDomElement exporterVersion = doc.createElement(QLatin1String("XMI.exporterVersion")); exporterVersion.appendChild(doc.createTextNode(QLatin1String(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 QDomElement exporterEncoding = doc.createElement(QLatin1String("XMI.exporterEncoding")); exporterEncoding.appendChild(doc.createTextNode(QLatin1String("UnicodeUTF8"))); documentation.appendChild(exporterEncoding); header.appendChild(documentation); // See comment on above // header.appendChild(model); header.appendChild(meta); root.appendChild(header); QDomElement content = doc.createElement(QLatin1String("XMI.content")); QDomElement contentNS = doc.createElement(QLatin1String("UML:Namespace.contents")); QDomElement objectsElement = doc.createElement(QLatin1String("UML:Model")); objectsElement.setAttribute(QLatin1String("xmi.id"), Uml::ID::toString(m_modelID)); objectsElement.setAttribute(QLatin1String("name"), m_Name); objectsElement.setAttribute(QLatin1String("isSpecification"), QLatin1String("false")); objectsElement.setAttribute(QLatin1String("isAbstract"), QLatin1String("false")); objectsElement.setAttribute(QLatin1String("isRoot"), QLatin1String("false")); objectsElement.setAttribute(QLatin1String("isLeaf"), QLatin1String("false")); QDomElement ownedNS = doc.createElement(QLatin1String("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. QStringList stereoNames; foreach (UMLStereotype *s, m_stereoList) { QString stName = s->name(); if (!stereoNames.contains(stName)) { s->saveToXMI1(doc, ownedNS); stereoNames.append(stName); } } for (int i = 0; i < Uml::ModelType::N_MODELTYPES; ++i) { m_root[i]->saveToXMI1(doc, ownedNS); } objectsElement.appendChild(ownedNS); content.appendChild(objectsElement); root.appendChild(content); // Save the XMI extensions: docsettings, diagrams, listview, and codegeneration. QDomElement extensions = doc.createElement(QLatin1String("XMI.extensions")); extensions.setAttribute(QLatin1String("xmi.extender"), QLatin1String("umbrello")); QDomElement docElement = doc.createElement(QLatin1String("docsettings")); Uml::ID::Type viewID = Uml::ID::None; UMLView *currentView = UMLApp::app()->currentView(); if (currentView) { viewID = currentView->umlScene()->ID(); } docElement.setAttribute(QLatin1String("viewid"), Uml::ID::toString(viewID)); docElement.setAttribute(QLatin1String("documentation"), m_Doc); docElement.setAttribute(QLatin1String("uniqueid"), Uml::ID::toString(UniqueID::get())); extensions.appendChild(docElement); // save listview UMLApp::app()->listView()->saveToXMI1(doc, extensions); // save code generator CodeGenerator *codegen = UMLApp::app()->generator(); if (codegen) { QDomElement codeGenElement = doc.createElement(QLatin1String("codegeneration")); codegen->saveToXMI1(doc, codeGenElement); extensions.appendChild(codeGenElement); } root.appendChild(extensions); QTextStream stream(&file); stream.setCodec("UTF-8"); stream << doc.toString(); } /** * Checks the given XMI file if it was saved with correct Unicode * encoding set or not. * * @param file The file to be checked. */ short UMLDoc::encoding(QIODevice & file) { QTextStream stream(&file); stream.setCodec("UTF-8"); QString data = stream.readAll(); QString error; int line; QDomDocument doc; if (!doc.setContent(data, false, &error, &line)) { uWarning() << "Cannot set content: " << error << " Line: " << line; return ENC_UNKNOWN; } // we start at the beginning and go to the point in the header where we can // find out if the file was saved using Unicode QDomNode node = doc.firstChild(); short enc = ENC_UNKNOWN; while (node.isComment() || node.isProcessingInstruction()) { if (node.isProcessingInstruction()) { const QDomProcessingInstruction& pi = node.toProcessingInstruction(); QRegExp rx(QLatin1String("\\bencoding=['\"]([^'\"]+)['\"]")); const int pos = rx.indexIn(pi.data()); if (pos >= 0) { const QString& encData = rx.cap(1); if (encData == QLatin1String("UTF-8")) { enc = ENC_UNICODE; } else if (encData == QLatin1String("windows-1252")) { enc = ENC_WINDOWS; } else { uDebug() << "ProcessingInstruction encoding=" << encData << " is not yet implemented"; enc = ENC_OLD_ENC; } } } node = node.nextSibling(); } QDomElement root = node.toElement(); if (root.isNull()) { uDebug() << "Null element at " << node.nodeName() << " : " << node.nodeValue(); return enc; } // make sure it is an XMI file if (root.tagName() != QLatin1String("XMI") && root.tagName() != QLatin1String("xmi:XMI")) { uDebug() << "Unknown tag at " << root.tagName(); return enc; } if (node.firstChild().isNull()) { uDebug() << "No child at " << node.nodeName() << " : " << node.nodeValue(); return enc; } node = node.firstChild(); QDomElement element = node.toElement(); // check header if (element.isNull()) { uDebug() << "No element at " << node.nodeName() << " : " << node.nodeValue(); return enc; } if (element.tagName() != QLatin1String("XMI.header")) { uDebug() << "Expecting XMI.header at " << element.tagName(); return enc; } QDomNode headerNode = node.firstChild(); while (!headerNode.isNull()) { QDomElement headerElement = headerNode.toElement(); // the information if Unicode was used is now stored in the // XMI.documentation section of the header if (headerElement.isNull() || headerElement.tagName() != QLatin1String("XMI.documentation")) { headerNode = headerNode.nextSibling(); continue; } QDomNode docuNode = headerNode.firstChild(); while (!docuNode.isNull()) { QDomElement docuElement = docuNode.toElement(); // a tag XMI.exporterEncoding was added since version 1.2 to // mark a file as saved with Unicode if (! docuElement.isNull() && docuElement.tagName() == QLatin1String("XMI.exporterEncoding")) { // at the moment this isn't really necessary but maybe // later we will have other encoding standards if (docuElement.text() == QLatin1String("UnicodeUTF8")) { return ENC_UNICODE; // stop here } } docuNode = docuNode.nextSibling(); } break; } return ENC_OLD_ENC; } /** * Load a given XMI model from a file. If the encoding of the file * is already known it can be passed to the function. If this info * isn't given, loadFromXMI1 will check which encoding was used. * * @param file The file to be loaded. * @param encode The encoding used. */ bool UMLDoc::loadFromXMI1(QIODevice & file, short encode) { // old Umbrello versions (version < 1.2) didn't save the XMI in Unicode // this wasn't correct, because non Latin1 chars where lost // to ensure backward compatibility we have to ensure to load the old files // with non Unicode encoding if (encode == ENC_UNKNOWN) { if ((encode = encoding(file)) == ENC_UNKNOWN) { return false; } file.reset(); } QTextStream stream(&file); if (encode == ENC_UNICODE) { stream.setCodec("UTF-8"); } else if (encode == ENC_WINDOWS) { stream.setCodec("windows-1252"); } QString data = stream.readAll(); qApp->processEvents(); // give UI events a chance QString error; int line; QDomDocument doc; if (!doc.setContent(data, false, &error, &line)) { uWarning() << "Cannot set content:" << error << " Line:" << line; return false; } qApp->processEvents(); // give UI events a chance QDomNode node = doc.firstChild(); //Before Umbrello 1.1-rc1 we didn't add a listView(); lv->setTitle(0, m_Name); recognized = true; } if (outerTag != QLatin1String("XMI.content")) { if (!recognized) { DEBUG(DBG_SRC) << "skipping <" << outerTag << ">"; } continue; } bool seen_UMLObjects = false; //process content for (QDomNode child = node.firstChild(); !child.isNull(); child = child.nextSibling()) { if (child.isComment()) { continue; } element = child.toElement(); QString tag = element.tagName(); if (tag == QLatin1String("umlobjects") // for bkwd compat. || tagEq(tag, QLatin1String("Subsystem")) || tagEq(tag, QLatin1String("Project")) // Embarcadero's Describe || tagEq(tag, QLatin1String("Model"))) { if(!loadUMLObjectsFromXMI1(element)) { uWarning() << "failed load on objects"; return false; } m_Name = element.attribute(QLatin1String("name"), i18n("UML Model")); UMLListView *lv = UMLApp::app()->listView(); lv->setTitle(0, m_Name); seen_UMLObjects = true; } else if (tagEq(tag, QLatin1String("Package")) || tagEq(tag, QLatin1String("Class")) || tagEq(tag, QLatin1String("Interface"))) { // These tests are only for foreign XMI files that // are missing the tag (e.g. NSUML) QString stID = element.attribute(QLatin1String("stereotype")); UMLObject *pObject = Object_Factory::makeObjectFromXMI(tag, stID); if (!pObject) { uWarning() << "Unknown type of umlobject to create: " << tag; // We want a best effort, therefore this is handled as a // soft error. continue; } UMLObject::ObjectType ot = pObject->baseType(); // Set the parent root folder. UMLPackage *pkg = 0; if (ot != UMLObject::ot_Stereotype) { if (ot == UMLObject::ot_Datatype) { pkg = m_datatypeRoot; } else { Uml::ModelType::Enum guess = Model_Utils::guessContainer(pObject); if (guess != Uml::ModelType::N_MODELTYPES) { pkg = m_root[guess]; } else { uError() << "Guess is Uml::ModelType::N_MODELTYPES - package not set correctly for " << pObject->name() << " / base type " << pObject->baseTypeStr(); pkg = m_root[Uml::ModelType::Logical]; } } } pObject->setUMLPackage(pkg); bool status = pObject->loadFromXMI1(element); if (!status) { delete pObject; return false; } seen_UMLObjects = true; } else if (tagEq(tag, QLatin1String("TaggedValue"))) { // This tag is produced here, i.e. outside of , // by the Unisys.JCR.1 Rose-to-XMI tool. if (! seen_UMLObjects) { DEBUG(DBG_SRC) << "skipping TaggedValue because not seen_UMLObjects"; continue; } tag = element.attribute(QLatin1String("tag")); if (tag != QLatin1String("documentation")) { continue; } QString modelElement = element.attribute(QLatin1String("modelElement")); if (modelElement.isEmpty()) { DEBUG(DBG_SRC) << "skipping TaggedValue(documentation) because " << "modelElement.isEmpty()"; continue; } UMLObject *o = findObjectById(Uml::ID::fromString(modelElement)); if (o == 0) { DEBUG(DBG_SRC) << "TaggedValue(documentation): cannot find object" << " for modelElement " << modelElement; continue; } QString value = element.attribute(QLatin1String("value")); if (! value.isEmpty()) { o->setDoc(value); } } else { // for backward compatibility loadExtensionsFromXMI1(child); } } } resolveTypes(); loadDiagrams1(); // set a default code generator if no tag seen if (UMLApp::app()->generator() == 0) { UMLApp::app()->setGenerator(UMLApp::app()->defaultLanguage()); } emit sigWriteToStatusBar(i18n("Setting up the document...")); qApp->processEvents(); // give UI events a chance activateAllViews(); UMLView *viewToBeSet = 0; if (m_nViewID != Uml::ID::None) { viewToBeSet = findView(m_nViewID); } if (viewToBeSet) { changeCurrentView(m_nViewID); } else { QString name = createDiagramName(Uml::DiagramType::Class, false); createDiagram(m_root[Uml::ModelType::Logical], Uml::DiagramType::Class, name); m_pCurrentRoot = m_root[Uml::ModelType::Logical]; } emit sigResetStatusbarProgress(); return true; } /** * Type resolution pass. */ void UMLDoc::resolveTypes() { // Resolve the types. // This is done in a separate pass because of possible forward references. if (m_bTypesAreResolved) { return; } writeToStatusBar(i18n("Resolving object references...")); for (int i = 0; i < Uml::ModelType::N_MODELTYPES; ++i) { UMLFolder *obj = m_root[i]; #ifdef VERBOSE_DEBUGGING DEBUG(DBG_SRC) << "UMLDoc: invoking resolveRef() for " << obj->name() << " (id=" << Uml::ID::toString(obj->id()) << ")"; #endif obj->resolveRef(); } m_bTypesAreResolved = true; qApp->processEvents(); // give UI events a chance } /** * Load all diagrams collected from the xmi file. * * Loading diagrams is implemented as additional pass to avoid unresolved * uml objects which are defined later in the xmi file. */ bool UMLDoc::loadDiagrams1() { bool result = true; DiagramsMap::const_iterator i; for (i = m_diagramsToLoad.constBegin(); i != m_diagramsToLoad.constEnd(); i++) { UMLFolder *f = i.key(); foreach(QDomNode node, i.value()) if (!f->loadDiagramsFromXMI1(node)) result = false; } m_diagramsToLoad.clear(); return result; } /** * Add a xml node containing a diagram to the list of diagrams to load. * Helper function for loadDiagrams(). * * @param folder pointer to UMFolder instance the diagrams belongs to * @param node xml document node containing the diagram */ void UMLDoc::addDiagramToLoad(UMLFolder *folder, QDomNode node) { if (m_diagramsToLoad.contains(folder)) m_diagramsToLoad[folder].append(node); else m_diagramsToLoad[folder] = QList() << node; } DiagramsModel *UMLDoc::diagramsModel() { return m_diagramsModel; } ObjectsModel *UMLDoc::objectsModel() { return m_objectsModel; } StereotypesModel *UMLDoc::stereotypesModel() { return m_stereotypesModel; } /** * Ensures the XMI file is a valid UML file. * Currently only checks for metamodel=UML. * * @param headerNode The node */ bool UMLDoc::validateXMI1Header(QDomNode& headerNode) { QDomElement 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; } /** * Loads all UML objects from XMI into the current UMLDoc. * * @return True if operation successful. */ bool UMLDoc::loadUMLObjectsFromXMI1(QDomElement& 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 Umbrello native XMI files, when called from loadFromXMI1() we // get here with Element.tagName() == "UML:Model" from the XMI input: // for (QDomNode node = element.firstChild(); !node.isNull(); node = node.nextSibling()) { if (node.isComment()) { continue; } QDomElement tempElement = node.toElement(); QString type = tempElement.tagName(); if (tagEq(type, QLatin1String("Model"))) { // Handling of Umbrello native XMI files: // We get here from a recursive call to loadUMLObjectsFromXMI() // a few lines below, see // if (tagEq(type, "Namespace.ownedElement") .... // Inside this Namespace.ownedElement envelope there are the // four submodels: // // // // // These are ultimately loaded by UMLFolder::loadFromXMI1() // Furthermore, in Umbrello native XMI format this // Namespace.ownedElement is the container of all stereotypes // (). bool foundUmbrelloRootFolder = false; QString name = tempElement.attribute(QLatin1String("name")); for (int i = 0; i < Uml::ModelType::N_MODELTYPES; ++i) { if (name == m_root[i]->name()) { m_pCurrentRoot = m_root[i]; m_root[i]->loadFromXMI1(tempElement); foundUmbrelloRootFolder = true; break; } } if (foundUmbrelloRootFolder) { continue; } } if (tagEq(type, QLatin1String("Namespace.ownedElement")) || tagEq(type, QLatin1String("Namespace.contents")) || tagEq(type, QLatin1String("Element.ownedElement")) || // Embarcadero's Describe tagEq(type, QLatin1String("Model"))) { //CHECK: Umbrello currently assumes that nested elements // are ownedElements anyway. // Therefore the tag is of no // significance. // The tagEq(type, "Namespace.contents") and tagEq(type, "Model") // tests do not become true for Umbrello native files, only for // some foreign XMI files. if (!loadUMLObjectsFromXMI1(tempElement)) { uWarning() << "failed load on " << type; return false; } continue; } // From here on, it's support for stereotypes, pre 1.5.5 versions, // and foreign files if (Model_Utils::isCommonXMI1Attribute(type)) { continue; } else if (tagEq(type, QLatin1String("packagedElement")) || tagEq(type, QLatin1String("ownedElement"))) { type = tempElement.attribute(QLatin1String("xmi:type")); } if (!tempElement.hasAttribute(QLatin1String("xmi.id")) && !tempElement.hasAttribute(QLatin1String("xmi:id"))) { QString idref = tempElement.attribute(QLatin1String("xmi.idref")); if (! idref.isEmpty()) { DEBUG(DBG_SRC) << "resolution of xmi.idref " << idref << " is not yet implemented"; } else { uError() << "Cannot load " << type << " because xmi.id is missing"; } continue; } QString stID = tempElement.attribute(QLatin1String("stereotype")); UMLObject *pObject = Object_Factory::makeObjectFromXMI(type, stID); if (!pObject) { uWarning() << "Unknown type of umlobject to create: " << type; // We want a best effort, therefore this is handled as a // soft error. continue; } UMLObject::ObjectType ot = pObject->baseType(); // Set the parent root folder. UMLPackage *pkg = 0; if (ot != UMLObject::ot_Stereotype) { if (ot == UMLObject::ot_Datatype) { pkg = m_datatypeRoot; } else { Uml::ModelType::Enum guess = Model_Utils::guessContainer(pObject); if (guess != Uml::ModelType::N_MODELTYPES) { pkg = m_root[guess]; } else { uError() << "Guess is Uml::ModelType::N_MODELTYPES - package not set correctly for " << pObject->name() << " / base type " << pObject->baseTypeStr(); pkg = m_root[Uml::ModelType::Logical]; } } } pObject->setUMLPackage(pkg); bool status = pObject->loadFromXMI1(tempElement); if (!status) { delete pObject; return false; } pkg = pObject->umlPackage(); if (ot == UMLObject::ot_Stereotype) { UMLStereotype *s = pObject->asUMLStereotype(); UMLStereotype *exist = findStereotype(pObject->name()); if (exist) { if (exist->id() == pObject->id()) { delete pObject; } else { DEBUG(DBG_SRC) << "Stereotype " << pObject->name() << "(id=" << Uml::ID::toString(pObject->id()) << ") already exists with id=" << Uml::ID::toString(exist->id()); addStereotype(s); } } else { addStereotype(s); } continue; } if (pkg) { UMLObjectList &objects = pkg->containedObjects(); if (! objects.contains(pObject)) { DEBUG(DBG_SRC) << "CHECK: adding " << pObject->name() << " to " << pkg->name(); pkg->addObject(pObject); } } else if (ot != UMLObject::ot_Stereotype) { uError() << "Package is NULL for " << pObject->name(); return false; } /* FIXME see comment at loadUMLObjectsFromXMI1 emit sigSetStatusbarProgress(++m_count); */ } return true; } /** * Sets m_nViewID. */ void UMLDoc::setMainViewID(Uml::ID::Type viewID) { m_nViewID = viewID; } /** * Loads umbrello specific extensions from XMI to the UMLDoc. * The extension tags are: "docsettings", "diagrams", "listview", * and "codegeneration". */ void UMLDoc::loadExtensionsFromXMI1(QDomNode& node) { QDomElement element = node.toElement(); QString tag = element.tagName(); if (tag == QLatin1String("docsettings")) { QString viewID = element.attribute(QLatin1String("viewid"), QLatin1String("-1")); m_Doc = element.attribute(QLatin1String("documentation")); QString uniqueid = element.attribute(QLatin1String("uniqueid"), QLatin1String("0")); m_nViewID = Uml::ID::fromString(viewID); UniqueID::set(Uml::ID::fromString(uniqueid)); UMLApp::app()->docWindow()->reset(); } else if (tag == QLatin1String("diagrams") || tag == QLatin1String("UISModelElement")) { // For backward compatibility only: // Since version 1.5.5 diagrams are saved as part of the UMLFolder. QDomNode diagramNode = node.firstChild(); if (tag == QLatin1String("UISModelElement")) { // Unisys.IntegratePlus.2 element = diagramNode.toElement(); tag = element.tagName(); if (tag != QLatin1String("uisOwnedDiagram")) { uError() << "unknown child node " << tag; return; } diagramNode = diagramNode.firstChild(); } else { qreal resolution = 0.0; QString res = node.toElement().attribute(QLatin1String("resolution"), QLatin1String("")); if (!res.isEmpty()) { resolution = res.toDouble(); } if (resolution != 0.0) { UMLApp::app()->document()->setResolution(resolution); } else { // see UMLFolder::loadDiagramsFromXMI() UMLApp::app()->document()->setResolution(0.0); } } if (!loadDiagramsFromXMI1(diagramNode)) { uWarning() << "failed load on diagrams"; } } else if (tag == QLatin1String("listview")) { //FIXME: Need to resolveTypes() before loading listview, // else listview items are duplicated. resolveTypes(); if (!UMLApp::app()->listView()->loadFromXMI1(element)) { uWarning() << "failed load on listview"; } } else if (tag == QLatin1String("codegeneration")) { QDomNode cgnode = node.firstChild(); QDomElement cgelement = cgnode.toElement(); while (!cgelement.isNull()) { QString nodeName = cgelement.tagName(); QString lang = cgelement.attribute(QLatin1String("language"), QLatin1String("UNKNOWN")); Uml::ProgrammingLanguage::Enum pl = Uml::ProgrammingLanguage::fromString(lang); CodeGenerator *g = UMLApp::app()->setGenerator(pl); g->loadFromXMI1(cgelement); cgnode = cgnode.nextSibling(); cgelement = cgnode.toElement(); } if (UMLApp::app()->generator() == 0) { UMLApp::app()->setGenerator(UMLApp::app()->defaultLanguage()); } } } /** * Loads all diagrams from XMI into the current UMLDoc. * For backward compatibility only: * Since version 1.5.5 diagrams are saved as part of the UMLFolder. * * @return True if operation successful. */ bool UMLDoc::loadDiagramsFromXMI1(QDomNode & node) { emit sigWriteToStatusBar(i18n("Loading diagrams...")); emit sigResetStatusbarProgress(); emit sigSetStatusbarProgress(0); emit sigSetStatusbarProgressSteps(10); //FIX ME QDomElement element = node.toElement(); if (element.isNull()) { return true; //return ok as it means there is no umlobjects } const Settings::OptionState state = Settings::optionState(); UMLView * pView = 0; int count = 0; while (!element.isNull()) { QString tag = element.tagName(); if (tag == QLatin1String("diagram") || tag == QLatin1String("UISDiagram")) { pView = new UMLView(0); // 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->umlScene()->setOptionState(state); bool success = false; if (tag == QLatin1String("UISDiagram")) { success = pView->umlScene()->loadUISDiagram(element); } else { success = pView->umlScene()->loadFromXMI1(element); } if (!success) { uWarning() << "failed load on viewdata loadfromXMI"; delete pView; return false; } // Put diagram in default predefined folder. // @todo pass in the parent folder - it might be a user defined one. Uml::ModelType::Enum mt = Model_Utils::convert_DT_MT(pView->umlScene()->type()); pView->umlScene()->setFolder(m_root[mt]); pView->hide(); addView(pView); emit sigSetStatusbarProgress(++count); qApp->processEvents(); // give UI events a chance } node = node.nextSibling(); element = node.toElement(); } return true; } /** * Call to remove all the views (diagrams) in the current file. */ void UMLDoc::removeAllViews() { for (int i = 0; i < Uml::ModelType::N_MODELTYPES; ++i) { m_root[i]->removeAllViews(); } UMLApp::app()->setCurrentView(0); emit sigDiagramChanged(Uml::DiagramType::Undefined); UMLApp::app()->setDiagramMenuItemsState(false); } /** * Call to remove all objects in the current file. */ void UMLDoc::removeAllObjects() { m_root[Uml::ModelType::Logical]->removeObject(m_datatypeRoot); for (int i = 0; i < Uml::ModelType::N_MODELTYPES; ++i) { m_root[i]->removeAllObjects(); } } /** * Returns a list of the packages in this UMLDoc, * * @return List of UMLPackages. */ UMLPackageList UMLDoc::packages(bool includeNested /* = true */) { UMLPackageList packageList; m_root[Uml::ModelType::Logical]->appendPackages(packageList, includeNested); return packageList; } /** * Returns the datatype folder. * * @return Pointer to the predefined folder for datatypes. */ UMLFolder * UMLDoc::datatypeFolder() const { return m_datatypeRoot; } /** * Returns a list of the concepts in this UMLDoc. * * @param includeNested Whether to include the concepts from * nested packages (default: true.) * @return List of UML concepts. */ UMLClassifierList UMLDoc::concepts(bool includeNested /* =true */) { UMLClassifierList conceptList; m_root[Uml::ModelType::Logical]->appendClassifiers(conceptList, includeNested); return conceptList; } /** * Returns a list of the classes and interfaces in this UMLDoc. * * @param includeNested Whether to include the concepts from * nested packages (default: true.) * @return List of UML concepts. */ UMLClassifierList UMLDoc::classesAndInterfaces(bool includeNested /* =true */) { UMLClassifierList conceptList; m_root[Uml::ModelType::Logical]->appendClassesAndInterfaces(conceptList, includeNested); return conceptList; } /** * Returns a list of the entities in this UMLDoc. * * @param includeNested Whether to include the entities from * nested packages (default: true.) * @return List of UML Entities. */ UMLEntityList UMLDoc::entities(bool includeNested /* =true */) { UMLEntityList entityList; m_root[Uml::ModelType::EntityRelationship]->appendEntities(entityList, includeNested); return entityList; } /** * Returns a list of the datatypes in this UMLDoc. * * @return List of datatypes. */ UMLClassifierList UMLDoc::datatypes() { UMLObjectList &objects = m_datatypeRoot->containedObjects(); UMLClassifierList datatypeList; foreach (UMLObject *obj, objects) { uIgnoreZeroPointer(obj); if (obj->isUMLDatatype()) { datatypeList.append(obj->asUMLClassifier()); } } return datatypeList; } /** * Returns a list of the associations in this UMLDoc. * * @return List of UML associations. */ UMLAssociationList UMLDoc::associations() { UMLAssociationList associationList; for (int i = 0; i < Uml::ModelType::N_MODELTYPES; ++i) { UMLAssociationList assocs = m_root[i]->getAssociations(); foreach (UMLAssociation* a, assocs) { associationList.append(a); } } return associationList; } /** * Controls the printing of the program. * * @param pPrinter The printer (object) to use. * @param selectPage The DiagramPrintPage by which diagrams are selected for printing */ void UMLDoc::print(QPrinter * pPrinter, DiagramPrintPage * selectPage) { UMLView * printView = 0; int count = selectPage->printUmlCount(); QPainter painter(pPrinter); for (int i = 0; i < count; ++i) { if (i>0) { pPrinter->newPage(); } QString sID = selectPage->printUmlDiagram(i); Uml::ID::Type id = Uml::ID::fromString(sID); printView = findView(id); if (printView) { printView->umlScene()->print(pPrinter, painter); } printView = 0; } painter.end(); } /** * Return the list of views for this document. * * @return List of UML views. */ UMLViewList UMLDoc::viewIterator() { UMLViewList accumulator; for (int i = 0; i < Uml::ModelType::N_MODELTYPES; ++i) { m_root[i]->appendViews(accumulator, true); } return accumulator; } /** * Sets the modified flag for the document after a modifying * action on the view connected to the document. * * @param modified The value to set the modified flag to. */ void UMLDoc::setModified(bool modified /*=true*/) { if (!m_bLoading) { m_modified = modified; UMLApp::app()->setModified(modified); } } /** * Returns if the document is modified or not. Use this to * determine if your document needs saving by the user on * closing. * * @return True if this UMLDoc is modified. */ bool UMLDoc::isModified() { return m_modified; } /** * Assigns an already created UMLObject a new ID. * If the object is a classifier then the operations/attributes * are also assigned new IDs. * * @param obj Pointer to the UMLObject to add. * @return True if operation successful. */ bool UMLDoc::assignNewIDs(UMLObject* obj) { if (!obj || !m_pChangeLog) { DEBUG(DBG_SRC) << "no obj || Changelog"; return false; } Uml::ID::Type result = assignNewID(obj->id()); obj->setID(result); //If it is a CONCEPT then change the ids of all its operations and attributes if (obj->baseType() == UMLObject::ot_Class) { UMLClassifier *c = obj->asUMLClassifier(); UMLClassifierListItemList attributes = c->getFilteredList(UMLObject::ot_Attribute); foreach (UMLObject* listItem, attributes) { result = assignNewID(listItem->id()); listItem->setID(result); } UMLClassifierListItemList templates = c->getFilteredList(UMLObject::ot_Template); foreach (UMLObject* listItem, templates) { result = assignNewID(listItem->id()); listItem->setID(result); } } if (obj->baseType() == UMLObject::ot_Interface || obj->baseType() == UMLObject::ot_Class) { UMLOperationList operations(((UMLClassifier*)obj)->getOpList()); foreach (UMLObject* listItem, operations) { result = assignNewID(listItem->id()); listItem->setID(result); } } setModified(true); return true; } /** * Return the predefined root folder of the given type. */ UMLFolder *UMLDoc::rootFolder(Uml::ModelType::Enum mt) { if (mt < Uml::ModelType::Logical || mt >= Uml::ModelType::N_MODELTYPES) { uError() << "illegal input value " << Uml::ModelType::toString(mt); return 0; } return m_root[mt]; } /** * Return the corresponding Model_Type if the given object * is one of the root folders. * When the given object is not one of the root folders then * return Uml::ModelType::N_MODELTYPES. */ Uml::ModelType::Enum UMLDoc::rootFolderType(UMLObject *obj) { for (int i = 0; i < Uml::ModelType::N_MODELTYPES; ++i) { const Uml::ModelType::Enum m = Uml::ModelType::fromInt(i); if (obj == m_root[m]) { return m; } } return Uml::ModelType::N_MODELTYPES; } /** * Read property of IDChangeLog* m_pChangeLog. * * @return Pointer to the IDChangeLog object. */ IDChangeLog* UMLDoc::changeLog() { 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. * * @param oldID The present ID of the object. * @return The new ID assigned to the object. */ Uml::ID::Type UMLDoc::assignNewID(Uml::ID::Type oldID) { Uml::ID::Type result = UniqueID::gen(); if (m_pChangeLog) { m_pChangeLog->addIDChange(oldID, result); } return result; } /** * Returns the documentation for the project. * * @return The documentation text of this UMLDoc. */ QString UMLDoc::documentation() const { return m_Doc; } /** * Sets the documentation for the project. * * @param doc The documentation to set for this UMLDoc. */ void UMLDoc::setDocumentation(const QString &doc) { m_Doc = doc; } /** * 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 * * @param pView Pointer to the UMLView to add. * @return True if operation successful. */ bool UMLDoc::addUMLView(UMLView * pView) { if (!pView || !m_pChangeLog) { return false; } Uml::ID::Type oldID = pView->umlScene()->ID(); int i = 0; QString viewName = pView->umlScene()->name(); QString name = viewName; while (findView(pView->umlScene()->type(), name) != 0) { name = viewName + QLatin1Char('_') + QString::number(++i); } if (i) { //If name was modified pView->umlScene()->setName(name); } Uml::ID::Type newID = assignNewID(oldID); pView->umlScene()->setID(newID); pView->umlScene()->activateAfterLoad(true); pView->umlScene()->endPartialWidgetPaste(); pView->umlScene()->setOptionState(Settings::optionState()); addView(pView); emit sigDiagramCreated(pView->umlScene()->ID()); setModified(true); return true; } /** * Activate all the diagrams/views after loading so all their * widgets keep their IDs. */ 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::ModelType::N_MODELTYPES; ++i) { m_root[i]->activateViews(); } m_bLoading = m_bLoading_old; } /** * Sets the default settings to the given settings. * @param optionState settings */ void UMLDoc::settingsChanged(Settings::OptionState &optionState) { for (int i = 0; i < Uml::ModelType::N_MODELTYPES; ++i) { m_root[i]->setViewOptions(optionState); } initSaveTimer(); } /** * Sets up the autosave timer. */ void UMLDoc::initSaveTimer() { if (m_pAutoSaveTimer) { m_pAutoSaveTimer->stop(); disconnect(m_pAutoSaveTimer, SIGNAL(timeout()), this, SLOT(slotAutoSave())); delete m_pAutoSaveTimer; m_pAutoSaveTimer = 0; } Settings::OptionState optionState = Settings::optionState(); if (optionState.generalState.autosave) { m_pAutoSaveTimer = new QTimer(this); connect(m_pAutoSaveTimer, SIGNAL(timeout()), this, SLOT(slotAutoSave())); m_pAutoSaveTimer->setSingleShot(false); m_pAutoSaveTimer->start(optionState.generalState.autosavetime * 60000); } } /** * Called after a specified time to autosave the document. */ void UMLDoc::slotAutoSave() { //Only save if modified. if (!m_modified) { return; } #if QT_VERSION >= 0x050000 QUrl tempUrl = m_doc_url; #else KUrl tempUrl = m_doc_url; #endif if (tempUrl.fileName() == i18n("Untitled")) { #if QT_VERSION >= 0x050000 tempUrl.setScheme(QLatin1String("file")); #endif tempUrl.setPath(QDir::homePath() + i18n("/autosave%1", QLatin1String(".xmi"))); saveDocument(tempUrl); setUrlUntitled(); m_modified = true; UMLApp::app()->setModified(m_modified); } else { // 2004-05-17 Achim Spangler #if QT_VERSION >= 0x050000 QUrl orgDocUrl = m_doc_url; #else KUrl orgDocUrl = m_doc_url; #endif QString orgFileName = m_doc_url.fileName(); // don't overwrite manually saved file with autosave content QString fileName = tempUrl.fileName(); Settings::OptionState optionState = Settings::optionState(); fileName.replace(QLatin1String(".xmi"), optionState.generalState.autosavesuffix); #if QT_VERSION >= 0x050000 tempUrl.setUrl(tempUrl.toString(QUrl::RemoveFilename) + fileName); #else tempUrl.setFileName(fileName); #endif // 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 (optionState.generalState.autosavesuffix != QLatin1String(".xmi")) { 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 } } /** * Signal a view/diagram has been renamed. */ void UMLDoc::signalDiagramRenamed(UMLView* view) { if (view) { Settings::OptionState optionState = Settings::optionState(); if (optionState.generalState.tabdiagrams) { UMLApp::app()->tabWidget()->setTabText(UMLApp::app()->tabWidget()->indexOf(view), view->umlScene()->name()); } emit sigDiagramRenamed(view->umlScene()->ID()); } else { uError() << "Cannot signal diagram renamed - view is NULL!"; } } /** * Calls the active code generator to create its default datatypes. */ void UMLDoc::addDefaultDatatypes() { CodeGenerator *cg = UMLApp::app()->generator(); if (cg == 0) { DEBUG(DBG_SRC) << "CodeGenerator is still NULL"; return; } QStringList entries = cg->defaultDatatypes(); QStringList::Iterator end(entries.end()); for (QStringList::Iterator it = entries.begin(); it != end; ++it) { createDatatype(*it); } UMLApp::app()->listView()->closeDatatypesFolder(); } /** * Add a datatype if it doesn't already exist. * Used by code generators and attribute dialog. */ void UMLDoc::createDatatype(const QString &name) { UMLObjectList &datatypes = m_datatypeRoot->containedObjects(); UMLObject* umlobject = Model_Utils::findUMLObject(datatypes, name, UMLObject::ot_Datatype, m_datatypeRoot); if (!umlobject) { Object_Factory::createUMLObject(UMLObject::ot_Datatype, name, m_datatypeRoot); } } /** * Make a popup menu for the tabs * signalled from tabWidget's contextMenu(). */ void UMLDoc::slotDiagramPopupMenu(QWidget* umlview, const QPoint& point) { UMLView* view = (UMLView*) umlview; UMLListViewItem::ListViewType type = UMLListViewItem::lvt_Unknown; switch (view->umlScene()->type()) { case Uml::DiagramType::Class: type = UMLListViewItem::lvt_Class_Diagram; break; case Uml::DiagramType::UseCase: type = UMLListViewItem::lvt_UseCase_Diagram; break; case Uml::DiagramType::Sequence: type = UMLListViewItem::lvt_Sequence_Diagram; break; case Uml::DiagramType::Collaboration: type = UMLListViewItem::lvt_Collaboration_Diagram; break; case Uml::DiagramType::State: type = UMLListViewItem::lvt_State_Diagram; break; case Uml::DiagramType::Activity: type = UMLListViewItem::lvt_Activity_Diagram; break; case Uml::DiagramType::Component: type = UMLListViewItem::lvt_Component_Diagram; break; case Uml::DiagramType::Deployment: type = UMLListViewItem::lvt_Deployment_Diagram; break; case Uml::DiagramType::EntityRelationship: type = UMLListViewItem::lvt_EntityRelationship_Diagram; break; default: uWarning() << "unknown diagram type " << view->umlScene()->type(); return; }//end switch UMLListViewItem item((UMLListView *)0, QString(), type); UMLListViewPopupMenu popup(UMLApp::app()->mainViewWidget(), &item); QAction *triggered = popup.exec(point); view->umlScene()->slotMenuSelection(triggered); } /** * Function for comparing tags in XMI files. */ bool UMLDoc::tagEq (const QString& inTag, const QString& inPattern) { QString tag = inTag; QString pattern = inPattern; tag.remove(QRegExp(QLatin1String("^\\w+:"))); // remove leading "UML:" or other int patSections = pattern.count(QLatin1Char('.')) + 1; QString tagEnd = tag.section(QLatin1Char('.'), -patSections); return (tagEnd.toLower() == pattern.toLower()); } diff --git a/umbrello/umldoc.h b/umbrello/umldoc.h index b85953639..3476aca3b 100644 --- a/umbrello/umldoc.h +++ b/umbrello/umldoc.h @@ -1,393 +1,401 @@ /*************************************************************************** * 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-2014 * * Umbrello UML Modeller Authors * ***************************************************************************/ #ifndef UMLDOC_H #define UMLDOC_H // app includes #include "basictypes.h" #include "optionstate.h" #include "umlobject.h" #include "umlobjectlist.h" #include "umlassociationlist.h" #include "umlclassifierlist.h" #include "umlentitylist.h" #include "umlviewlist.h" #include "umlstereotypelist.h" #include "umlpackagelist.h" // kde includes #if QT_VERSION < 0x050000 #include #endif // qt includes #if QT_VERSION >= 0x050000 #include #endif // system includes #include #define ENC_UNKNOWN 0 #define ENC_UNICODE 1 #define ENC_WINDOWS 2 #define ENC_OLD_ENC 3 // forward declarations class QDomNode; class QDomElement; class QPrinter; class IDChangeLog; class DiagramsModel; class ObjectsModel; class StereotypesModel; class UMLPackage; class UMLFolder; class DiagramPrintPage; /** * UMLDoc provides a document object for a document-view model. * * The UMLDoc class provides a document object that can be used * in conjunction with the classes UMLApp and UMLView to create * a document-view model for standard KDE applications based on * KApplication and KMainWindow. Thereby, the document object * is created by the UMLApp instance and contains the document * structure with the according methods for manipulation of the * document data by UMLView objects. Also, UMLDoc contains the * methods for serialization of the document data from and to * files. * * @author Paul Hensgen * Bugs and comments to umbrello-devel@kde.org or http://bugs.kde.org */ class UMLDoc : public QObject { Q_OBJECT public: UMLDoc(); ~UMLDoc(); void init(); void addView(UMLView *view); void removeView(UMLView *view, bool enforceOneView = true); void setMainViewID(Uml::ID::Type viewID); void changeCurrentView(Uml::ID::Type id); void activateAllViews(); void removeAllViews(); void removeAllObjects(); void setModified(bool modified = true); bool isModified(); bool saveModified(); bool newDocument(); void closeDocument(); #if QT_VERSION >= 0x050000 bool openDocument(const QUrl& url, const char *format = 0); bool saveDocument(const QUrl& url, const char *format = 0); const QUrl& url() const; void setUrl(const QUrl& url); #else bool openDocument(const KUrl& url, const char *format = 0); bool saveDocument(const KUrl& url, const char *format = 0); const KUrl& url() const; void setUrl(const KUrl& url); #endif void setUrlUntitled(); void setupSignals(); bool isUnique(const QString &name); bool isUnique(const QString &name, UMLPackage *package); UMLAssociation* createUMLAssociation(UMLObject *a, UMLObject *b, Uml::AssociationType::Enum type); void addAssociation(UMLAssociation *assoc); void removeAssociation(UMLAssociation *assoc, bool doSetModified = true); UMLAssociation * findAssociation(Uml::AssociationType::Enum assocType, const UMLObject *roleAObj, const UMLObject *roleBObj, bool *swap = 0); QString createDiagramName(Uml::DiagramType::Enum type, bool askForName = true); UMLView* createDiagram(UMLFolder *folder, Uml::DiagramType::Enum type, const QString& name, Uml::ID::Type id = Uml::ID::None); void removeDiagram(Uml::ID::Type id); void removeDiagramCmd(Uml::ID::Type id); void renameDiagram(Uml::ID::Type id); void removeUMLObject(UMLObject* umlobject, bool deleteObject = false); void renameUMLObject(UMLObject *o); void renameChildUMLObject(UMLObject *o); UMLObject* findObjectById(Uml::ID::Type id); UMLObject* findUMLObject(const QString &name, UMLObject::ObjectType type = UMLObject::ot_UMLObject, UMLObject *currentObj = 0); UMLObject* findUMLObjectRaw(Uml::ModelType::Enum, const QString &name, UMLObject::ObjectType type = UMLObject::ot_UMLObject); UMLObject* findUMLObjectRaw(UMLFolder *folder, const QString &name, UMLObject::ObjectType type = UMLObject::ot_UMLObject); + UMLObject* findUMLObjectRecursive(Uml::ModelType::Enum, + const QString &name, + UMLObject::ObjectType type = UMLObject::ot_UMLObject); + + UMLObject* findUMLObjectRecursive(UMLFolder *folder, + const QString &name, + UMLObject::ObjectType type = UMLObject::ot_UMLObject); + UMLClassifier * findUMLClassifier(const QString &name); UMLView * findView(Uml::ID::Type id); UMLView * findView(Uml::DiagramType::Enum type, const QString &name, bool searchAllScopes = false); void setName(const QString& name); QString name() const; void setResolution(qreal resolution); qreal resolution() const; qreal dpiScale() const; Uml::ID::Type modelID() const; static bool tagEq (const QString& tag, const QString& pattern); virtual void saveToXMI1(QIODevice& file); short encoding(QIODevice & file); virtual bool loadFromXMI1(QIODevice& file, short encode = ENC_UNKNOWN); bool validateXMI1Header(QDomNode& headerNode); bool loadUMLObjectsFromXMI1(QDomElement & element); void loadExtensionsFromXMI1(QDomNode & node); bool loadDiagramsFromXMI1(QDomNode & node); void signalDiagramRenamed(UMLView * view); void signalUMLObjectCreated(UMLObject * o); UMLClassifierList concepts(bool includeNested = true); UMLClassifierList classesAndInterfaces(bool includeNested = true); UMLEntityList entities(bool includeNested = true); UMLFolder * datatypeFolder() const; UMLClassifierList datatypes(); UMLAssociationList associations(); UMLPackageList packages(bool includeNested = true); void print(QPrinter * pPrinter, DiagramPrintPage * selectPage); UMLViewList viewIterator(); bool assignNewIDs(UMLObject* obj); bool addUMLObject(UMLObject * object); bool addUMLView(UMLView * pView); UMLFolder *rootFolder(Uml::ModelType::Enum mt); Uml::ModelType::Enum rootFolderType(UMLObject *obj); UMLFolder *currentRoot(); void setCurrentRoot(Uml::ModelType::Enum rootType); virtual IDChangeLog* changeLog(); void beginPaste(); void endPaste(); Uml::ID::Type assignNewID(Uml::ID::Type oldID); void setDocumentation(const QString &doc); QString documentation() const; void settingsChanged(Settings::OptionState &optionState); QString uniqueViewName(const Uml::DiagramType::Enum type); bool loading() const; void setLoading(bool state = true); bool importing() const; void setImporting(bool state = true); bool closing() const; void addDefaultDatatypes(); void createDatatype(const QString &name); UMLStereotype *createStereotype(const QString &name); UMLStereotype *findStereotype(const QString &name); UMLStereotype *findOrCreateStereotype(const QString &name); UMLStereotype *findStereotypeById(Uml::ID::Type id); void addStereotype(UMLStereotype *s); void removeStereotype(UMLStereotype *s); void addDefaultStereotypes(); const UMLStereotypeList& stereotypes() const; void writeToStatusBar(const QString &text); void resolveTypes(); bool loadDiagrams1(); void addDiagramToLoad(UMLFolder *folder, QDomNode node); DiagramsModel *diagramsModel(); StereotypesModel *stereotypesModel(); ObjectsModel *objectsModel(); private: void initSaveTimer(); void createDatatypeFolder(); /** * Array of predefined root folders. */ UMLFolder *m_root[Uml::ModelType::N_MODELTYPES]; /** * Predefined root folder for datatypes, contained in * m_root[Uml::mt_Logical] */ UMLFolder *m_datatypeRoot; /** * The UMLDoc is the sole owner of all stereotypes. * UMLStereotype instances are reference counted. * When an UMLStereotype is no longer referenced anywhere, * its refcount drops to zero. It is then removed from the * m_stereoList and it is physically deleted. */ UMLStereotypeList m_stereoList; QString m_Name; ///< name of this model as stored in the tag Uml::ID::Type m_modelID; ///< xmi.id of this model in the int m_count; ///< auxiliary counter for the progress bar bool m_modified; #if QT_VERSION >= 0x050000 QUrl m_doc_url; #else KUrl m_doc_url; #endif /** * Contains all the UMLObject id changes of paste session. */ IDChangeLog* m_pChangeLog; /** * true if the we're loading a new document */ bool m_bLoading; /** * true if the we're importing */ bool m_importing; /** * Documentation for the project. */ QString m_Doc; /** * Used for autosave */ QTimer * m_pAutoSaveTimer; /** * Auxiliary to processing */ Uml::ID::Type m_nViewID; /** * True when type resolution pass has been executed. */ bool m_bTypesAreResolved; /** * Auxiliary variable for currentRoot(): * m_pCurrentRoot is only used if UMLApp::app()->currentView() * returns 0. */ UMLFolder * m_pCurrentRoot; /** * True while closeDocument() is executing. */ bool m_bClosing; DiagramsModel *m_diagramsModel; ObjectsModel *m_objectsModel; StereotypesModel *m_stereotypesModel; /** * Holds widgets coordinates resolution. * Unit is dpi. */ qreal m_resolution; /** * Holds diagram xml nodes on loading */ typedef QMap> DiagramsMap; DiagramsMap m_diagramsToLoad; public slots: void slotRemoveUMLObject(UMLObject*o); void slotAutoSave(); void slotDiagramPopupMenu(QWidget* umlview, const QPoint& point); signals: void sigDiagramCreated(Uml::ID::Type id); void sigDiagramRemoved(Uml::ID::Type id); void sigDiagramRenamed(Uml::ID::Type t); void sigDiagramChanged(Uml::DiagramType::Enum); void sigObjectCreated(UMLObject *); void sigObjectRemoved(UMLObject *); /** * Reset the status bar. */ void sigResetStatusbarProgress(); /** * Set the total range of the progressbar. * * @param totalSteps Total range of the progressbar (0..totalSteps) */ void sigSetStatusbarProgressSteps(int totalSteps); /** * Set the progress position of the progressbar. * * @param stepPosition The step position to set. */ void sigSetStatusbarProgress(int stepPosition); /** * Write text to the status bar. */ void sigWriteToStatusBar(const QString &text); /** * The diagram being displayed has changed. * UMLApp uses this to keep its menu items state up to date. */ void sigCurrentViewChanged(); }; #endif // UMLDOC_H