diff --git a/umbrello/clipboard/umldragdata.cpp b/umbrello/clipboard/umldragdata.cpp index 032c8f129..82cddeb69 100644 --- a/umbrello/clipboard/umldragdata.cpp +++ b/umbrello/clipboard/umldragdata.cpp @@ -1,886 +1,902 @@ /*************************************************************************** * 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 "umldragdata.h" // local includes #include "associationwidget.h" #include "classifier.h" #include "cmds.h" #include "debug_utils.h" #include "floatingtextwidget.h" #include "folder.h" #include "idchangelog.h" #include "model_utils.h" #include "object_factory.h" #include "objectwidget.h" #include "notewidget.h" #include "preconditionwidget.h" #include "messagewidget.h" #include "uniqueid.h" #include "uml.h" #include "umldoc.h" #include "umllistview.h" #include "umllistviewitem.h" #include "umlobject.h" #include "umlscene.h" #include "umlview.h" #include "umlwidget.h" // qt includes #include #include #include /** * Constructor. */ UMLDragData::UMLDragData(UMLObjectList& objects, QWidget* dragSource /* = 0 */) { Q_UNUSED(dragSource); setUMLDataClip1(objects); } /** * For use when the user selects UML Object and Diagrams * from the ListView to be copied, Mime type = * "application/x-uml-clip2 */ UMLDragData::UMLDragData(UMLObjectList& objects, UMLViewList& diagrams, QWidget* dragSource /* = 0 */) { Q_UNUSED(dragSource); setUMLDataClip2(objects, diagrams); } /** * Constructor. */ UMLDragData::UMLDragData(UMLListViewItemList& umlListViewItems, QWidget* dragSource /* = 0 */) { Q_UNUSED(dragSource); setUMLDataClip3(umlListViewItems); } /** * For use when the user selects UMLObjects from a * Diagram. The Selected widgets and the relationships * between only selected widgets will be copied and also * its respective ListView Items, Mime type = * "application/x-uml-clip4 */ UMLDragData::UMLDragData(UMLObjectList& objects, UMLWidgetList& widgets, AssociationWidgetList& associationDatas, QPixmap& pngImage, UMLScene* scene, QWidget* dragSource /* = 0 */) { Q_UNUSED(dragSource); setUMLDataClip4(objects, widgets, associationDatas, pngImage, scene); } /** * For use when the user selects only Operations and/or * Attributes from the ListView, Mime type = * "application/x-uml-clip5 */ UMLDragData::UMLDragData(UMLObjectList& objects, int, QWidget* dragSource /* = 0 */) { Q_UNUSED(dragSource); setUMLDataClip5(objects); } /** * Constructor. */ UMLDragData::UMLDragData(QWidget* dragSource /* = 0 */) { Q_UNUSED(dragSource); } /** * Deconstructor. */ UMLDragData::~UMLDragData() { } /** * For use when the user selects only UMLObjects from the * ListView but no diagrams to be copied */ void UMLDragData::setUMLDataClip1(UMLObjectList& objects) { QDomDocument domDoc; QDomElement xmiclip = domDoc.createElement(QLatin1String("xmiclip")); domDoc.appendChild(xmiclip); QDomElement objectsTag = domDoc.createElement(QLatin1String("umlobjects")); xmiclip.appendChild(objectsTag); UMLObjectListIt object_it(objects); UMLObject* obj = 0; while (object_it.hasNext()) { obj = object_it.next(); obj->saveToXMI1(domDoc, objectsTag); } setData(QLatin1String("application/x-uml-clip1"), domDoc.toString().toUtf8()); } /** * For use when the user selects UML Object and Diagrams * from the ListView to be copied */ void UMLDragData::setUMLDataClip2(UMLObjectList& objects, UMLViewList& diagrams) { QDomDocument domDoc; QDomElement xmiclip = domDoc.createElement(QLatin1String("xmiclip")); domDoc.appendChild(xmiclip); QDomElement objectsTag = domDoc.createElement(QLatin1String("umlobjects")); xmiclip.appendChild(objectsTag); UMLObjectListIt object_it(objects); UMLObject* obj = 0; while (object_it.hasNext()) { obj = object_it.next(); obj->saveToXMI1(domDoc, objectsTag); } QDomElement viewsTag = domDoc.createElement(QLatin1String("umlviews")); xmiclip.appendChild(viewsTag); foreach(UMLView* view, diagrams) { view->umlScene()->saveToXMI1(domDoc, viewsTag); } setData(QLatin1String("application/x-uml-clip2"), domDoc.toString().toUtf8()); } /** * For use when the user selects only empty folders from the ListView * to be copied. */ void UMLDragData::setUMLDataClip3(UMLListViewItemList& umlListViewItems) { QDomDocument domDoc; QDomElement xmiclip = domDoc.createElement(QLatin1String("xmiclip")); domDoc.appendChild(xmiclip); QDomElement itemsTag = domDoc.createElement(QLatin1String("umllistviewitems")); xmiclip.appendChild(itemsTag); foreach(UMLListViewItem* item, umlListViewItems) { item->saveToXMI1(domDoc, itemsTag); } setData(QLatin1String("application/x-uml-clip3"), domDoc.toString().toUtf8()); } /** * For use when the user selects UML Objects from a * Diagram. The Selected widgets and the relationships * between only selected widgets will be copied and also * its respective ListView Items */ void UMLDragData::setUMLDataClip4(UMLObjectList& objects, UMLWidgetList& widgets, AssociationWidgetList& associations, QPixmap& pngImage, UMLScene *scene) { QDomDocument domDoc; QDomElement xmiclip = domDoc.createElement(QLatin1String("xmiclip")); xmiclip.setAttribute(QLatin1String("diagramtype"), scene->type()); xmiclip.setAttribute(QLatin1String("diagramid"), Uml::ID::toString(scene->ID())); domDoc.appendChild(xmiclip); QDomElement objectsTag = domDoc.createElement(QLatin1String("umlobjects")); xmiclip.appendChild(objectsTag); foreach (UMLObject* obj, objects) { obj->saveToXMI1(domDoc, objectsTag); } QDomElement widgetsTag = domDoc.createElement(QLatin1String("widgets")); xmiclip.appendChild(widgetsTag); foreach (UMLWidget* widget, widgets) { widget->saveToXMI1(domDoc, widgetsTag); } QDomElement associationWidgetsTag = domDoc.createElement(QLatin1String("associations")); xmiclip.appendChild(associationWidgetsTag); foreach (AssociationWidget* association, associations) { association->saveToXMI1(domDoc, associationWidgetsTag); } setData(QLatin1String("application/x-uml-clip4"), domDoc.toString().toUtf8()); QImage img = pngImage.toImage(); int l_size = img.byteCount(); QByteArray clipdata; clipdata.resize(l_size); QDataStream clipstream(&clipdata, QIODevice::WriteOnly); clipstream << img; setImageData(clipdata); } /** * For use when the user selects only Attirbutes and/or * Operation from the ListView */ void UMLDragData::setUMLDataClip5(UMLObjectList& objects) { QDomDocument domDoc; QDomElement xmiclip = domDoc.createElement(QLatin1String("xmiclip")); domDoc.appendChild(xmiclip); QDomElement objectsTag = domDoc.createElement(QLatin1String("umlobjects")); xmiclip.appendChild(objectsTag); foreach (UMLObject* obj, objects) { obj->saveToXMI1(domDoc, objectsTag); } setData(QLatin1String("application/x-uml-clip5"), domDoc.toString().toUtf8()); } /** * For use when the user selects only UML Objects * from the ListView but no diagrams to be * copied, decodes Mime type = * "application/x-uml-clip1 */ bool UMLDragData::decodeClip1(const QMimeData* mimeData, UMLObjectList& objects) { if (!mimeData->hasFormat(QLatin1String("application/x-uml-clip1"))) { return false; } QByteArray payload = mimeData->data(QLatin1String("application/x-uml-clip1")); if (!payload.size()) { return false; } QString xmiClip = QString::fromUtf8(payload); QString error; int line; QDomDocument domDoc; if(!domDoc.setContent(xmiClip, false, &error, &line)) { uWarning() << "Cannot set content:" << error << " Line:" << line; return false; } QDomNode xmiClipNode = domDoc.firstChild(); QDomElement root = xmiClipNode.toElement(); if (root.isNull()) { return false; } // make sure it is an XMI clip if (root.tagName() != QLatin1String("xmiclip")) { return false; } QDomNode objectsNode = xmiClipNode.firstChild(); if (!UMLDragData::decodeObjects(objectsNode, objects, false)) { return false; } return true; } /** * For use when the user selects UML Object and Diagrams * from the ListView to be copied, decodes Mime type = * "application/x-uml-clip2 */ bool UMLDragData::decodeClip2(const QMimeData* mimeData, UMLObjectList& objects, UMLViewList& diagrams) { if (!mimeData->hasFormat(QLatin1String("application/x-uml-clip2"))) { return false; } QByteArray payload = mimeData->data(QLatin1String("application/x-uml-clip2")); if (!payload.size()) { return false; } QString xmiClip = QString::fromUtf8(payload); QString error; int line; QDomDocument domDoc; if(!domDoc.setContent(xmiClip, false, &error, &line)) { uWarning() << "Cannot set content:" << error << " Line:" << line; return false; } QDomNode xmiClipNode = domDoc.firstChild(); QDomElement root = xmiClipNode.toElement(); if (root.isNull()) { return false; } // make sure it is an XMI clip if (root.tagName() != QLatin1String("xmiclip")) { return false; } // Load UMLObjects QDomNode objectsNode = xmiClipNode.firstChild(); if (NoteWidget::s_pCurrentNote == 0) { if (!UMLDragData::decodeObjects(objectsNode, objects, true)) { return false; } } // Load UMLViews (diagrams) QDomNode umlviewsNode = objectsNode.nextSibling(); if (!UMLDragData::decodeViews(umlviewsNode, diagrams)) { return false; } return true; } /** * Return just the LvTypeAndID of a Clip3. * * @param mimeData The encoded source. * @param typeAndIdList The LvTypeAndID_List decoded from the source. * @return True if decoding was successful. */ bool UMLDragData::getClip3TypeAndID(const QMimeData* mimeData, LvTypeAndID_List& typeAndIdList) { if (!mimeData->hasFormat(QLatin1String("application/x-uml-clip3"))) { return false; } QByteArray payload = mimeData->data(QLatin1String("application/x-uml-clip3")); if (!payload.size()) { return false; } QTextStream clipdata(payload, QIODevice::ReadOnly); QString xmiClip = QString::fromUtf8(payload); QString error; int line; QDomDocument domDoc; if(!domDoc.setContent(xmiClip, false, &error, &line)) { uWarning() << "getClip3Type: Cannot set content:" << error << " Line:" << line; return false; } QDomNode xmiClipNode = domDoc.firstChild(); QDomElement root = xmiClipNode.toElement(); if (root.isNull()) { return false; } // make sure it is an XMI clip if (root.tagName() != QLatin1String("xmiclip")) { return false; } QDomNode listItemNode = xmiClipNode.firstChild(); QDomNode listItems = listItemNode.firstChild(); QDomElement listItemElement = listItems.toElement(); if (listItemElement.isNull()) { uWarning() << "getClip3Type: No listitems in XMI clip."; return false; } while (!listItemElement.isNull()) { QString typeStr = listItemElement.attribute(QLatin1String("type"), QLatin1String("-1")); if (typeStr == QLatin1String("-1")) { uDebug() << "getClip3Type: bad type."; return false; } QString idStr = listItemElement.attribute(QLatin1String("id"), QLatin1String("-1")); if (idStr == QLatin1String("-1")) { uDebug() << "getClip3Type: bad id"; return false; } LvTypeAndID * pData = new LvTypeAndID; pData->type = (UMLListViewItem::ListViewType)(typeStr.toInt()); pData->id = Uml::ID::fromString(idStr); typeAndIdList.append(pData); listItems = listItems.nextSibling(); listItemElement = listItems.toElement(); } return true; } /** * For use when the user selects UMLObjects from * the ListView to be copied, decodes Mime * type = * "application/x-uml-clip3 */ bool UMLDragData::decodeClip3(const QMimeData* mimeData, UMLListViewItemList& umlListViewItems, const UMLListView* parentListView) { if (!mimeData->hasFormat(QLatin1String("application/x-uml-clip3"))) { return false; } QByteArray payload = mimeData->data(QLatin1String("application/x-uml-clip3")); if (!payload.size()) { return false; } QTextStream clipdata(payload, QIODevice::ReadOnly); QString xmiClip = QString::fromUtf8(payload); QString error; int line; QDomDocument domDoc; if(!domDoc.setContent(xmiClip, false, &error, &line)) { uWarning() << "Cannot set content:" << error << " Line:" << line; return false; } QDomNode xmiClipNode = domDoc.firstChild(); QDomElement root = xmiClipNode.toElement(); if (root.isNull()) { return false; } // make sure it is an XMI clip if (root.tagName() != QLatin1String("xmiclip")) { return false; } //listviewitems QDomNode listItemNode = xmiClipNode.firstChild(); QDomNode listItems = listItemNode.firstChild(); QDomElement listItemElement = listItems.toElement(); if (listItemElement.isNull()) { uWarning() << "no listitems in XMI clip"; return false; } while (!listItemElement.isNull()) { // Get the ListViewType beforehand so that we can construct an // UMLListViewItem instance. QString type = listItemElement.attribute(QLatin1String("type"), QLatin1String("-1")); if (type == QLatin1String("-1")) { uDebug() << "Type not found."; listItems = listItems.nextSibling(); listItemElement = listItems.toElement(); continue; } UMLListViewItem::ListViewType t = (UMLListViewItem::ListViewType)(type.toInt()); UMLListViewItem* parent = parentListView->determineParentItem(t); UMLListViewItem* itemData = new UMLListViewItem(parent); if (itemData->loadFromXMI1(listItemElement)) umlListViewItems.append(itemData); else delete itemData; listItems = listItems.nextSibling(); listItemElement = listItems.toElement(); } return true; } /** * For use when the user selects UML Objects from a * Diagram. The Selected widgets and the relationships * between only selected widgets will be copied * * decodes Mime type = "application/x-uml-clip4" */ bool UMLDragData::decodeClip4(const QMimeData* mimeData, UMLObjectList& objects, UMLWidgetList& widgets, AssociationWidgetList& associations, Uml::DiagramType::Enum &dType) { if (!mimeData->hasFormat(QLatin1String("application/x-uml-clip4"))) { return false; } QByteArray payload = mimeData->data(QLatin1String("application/x-uml-clip4")); if (!payload.size()) { return false; } QString xmiClip = QString::fromUtf8(payload); QString error; int line; QDomDocument domDoc; if(!domDoc.setContent(xmiClip, false, &error, &line)) { uWarning() << "Cannot set content:" << error << " Line:" << line; return false; } QDomNode xmiClipNode = domDoc.firstChild(); QDomElement root = xmiClipNode.toElement(); if (root.isNull()) { return false; } // make sure it is an XMI clip if (root.tagName() != QLatin1String("xmiclip")) { return false; } dType = Uml::DiagramType::fromInt(root.attribute(QLatin1String("diagramtype"), QLatin1String("0")).toInt()); QDomNode objectsNode = xmiClipNode.firstChild(); // Load UMLObjects and do not fail if there are none in the clip bool hasObjects = !objectsNode.firstChild().toElement().isNull(); if (hasObjects && !UMLDragData::decodeObjects(objectsNode, objects, true)) { return false; } UMLDoc *doc = UMLApp::app()->document(); UMLView *view = UMLApp::app()->currentView(); UMLScene *scene = view->umlScene(); QString sourceDiagramID = root.attribute(QLatin1String("diagramid"), QLatin1String("")); UMLView *sourceView = doc->findView(Uml::ID::fromString(sourceDiagramID)); - bool pasteToDiagramCopiedFrom = sourceView && sourceView->umlScene()->ID() == scene->ID(); + bool fromSameDiagram = sourceView && sourceView->umlScene()->ID() == scene->ID(); + bool fromAnotherInstance = !sourceView; + bool fromDifferentDiagramType = dType != scene->type(); // Load widgets QDomNode widgetsNode = objectsNode.nextSibling(); QDomNode widgetNode = widgetsNode.firstChild(); QDomElement widgetElement = widgetNode.toElement(); if (widgetElement.isNull()) { uWarning() << "No widgets in XMI clip."; return false; } while (!widgetElement.isNull()) { UMLWidget* widget = scene->loadWidgetFromXMI(widgetElement); if (widget) { - if (pasteToDiagramCopiedFrom && widget->isObjectWidget()) { + if (fromSameDiagram && widget->isObjectWidget()) { delete widget; widgetNode = widgetNode.nextSibling(); widgetElement = widgetNode.toElement(); continue; } // check if widget is pastable - if (!sourceView || sourceView->umlScene()->type() != scene->type()) { + if (fromAnotherInstance || fromDifferentDiagramType) { UMLObject *object = widget->umlObject(); if (object) { if (!Model_Utils::typeIsAllowedInDiagram(object, scene)) { delete widget; widgetNode = widgetNode.nextSibling(); widgetElement = widgetNode.toElement(); continue; } } else if (!Model_Utils::typeIsAllowedInDiagram(widget, scene)) { delete widget; widgetNode = widgetNode.nextSibling(); widgetElement = widgetNode.toElement(); continue; } + } else if (Model_Utils::isCloneable(widget->baseType())) { + if (widget->umlObject()) { + UMLObject *clone = widget->umlObject()->clone(); + widget->setUMLObject(clone); + // we do not want to recreate an additional widget, + // which would be the case if calling scene->addUMLObject() + UMLApp::app()->document()->addUMLObject(clone); + UMLApp::app()->listView()->slotObjectCreated(clone); + if (Model_Utils::hasAssociations(clone->baseType())) + { + scene->createAutoAssociations(widget); + scene->createAutoAttributeAssociations2(widget); + } + } } // Generate a new unique 'local ID' so a second widget for the same // UMLObject can be distinguished from the first widget widget->setLocalID(doc->assignNewID(widget->localID())); if (widget->isMessageWidget()) { MessageWidget *w = widget->asMessageWidget(); if (w && w->floatingTextWidget()) { w->floatingTextWidget()->setLocalID(doc->assignNewID(w->floatingTextWidget()->localID())); w->floatingTextWidget()->setID(doc->assignNewID(w->floatingTextWidget()->id())); } } // Add the widget to the UMLWidgetList for reference in // UMLClipboard widgets.append(widget); } else { uWarning() << "Unable to paste widget" << widgetElement.tagName(); } widgetNode = widgetNode.nextSibling(); widgetElement = widgetNode.toElement(); } if (widgets.size() == 0) return false; IDChangeLog* log = doc->changeLog(); // Make sure all object widgets are loaded before adding messages or // preconditions - if (!pasteToDiagramCopiedFrom) { + if (!fromSameDiagram) { foreach (UMLWidget* widget, widgets) { if (widget->isObjectWidget()) { executeCreateWidgetCommand(widget); } } } // Now add all remaining widgets foreach (UMLWidget* widget, widgets) { - if (!pasteToDiagramCopiedFrom && widget->isMessageWidget()) { + if (!fromSameDiagram && widget->isMessageWidget()) { MessageWidget* message = widget->asMessageWidget(); message->resolveObjectWidget(log); } if (widget->isPreconditionWidget()) { PreconditionWidget* precondition = widget->asPreconditionWidget(); precondition->resolveObjectWidget(log); } if (!widget->isObjectWidget()) { executeCreateWidgetCommand(widget); } } // Load AssociationWidgets QDomNode associationWidgetsNode = widgetsNode.nextSibling(); QDomNode associationWidgetNode = associationWidgetsNode.firstChild(); QDomElement associationWidgetElement = associationWidgetNode.toElement(); while (!associationWidgetElement.isNull()) { AssociationWidget* associationWidget = AssociationWidget::create(view->umlScene()); if (associationWidget->loadFromXMI1(associationWidgetElement, widgets, 0)) associations.append(associationWidget); else { delete associationWidget; } associationWidgetNode = associationWidgetNode.nextSibling(); associationWidgetElement = associationWidgetNode.toElement(); } return true; } /** * For use when the user selects only Attributes and/or * Operations from the ListView * copied, decodes Mime * type = "application/x-uml-clip5 */ bool UMLDragData::decodeClip5(const QMimeData* mimeData, UMLObjectList& objects, UMLClassifier* newParent) { if (!mimeData->hasFormat(QLatin1String("application/x-uml-clip5"))) { return false; } QByteArray payload = mimeData->data(QLatin1String("application/x-uml-clip5")); if (!payload.size()) { return false; } QString xmiClip = QString::fromUtf8(payload); QString error; int line; QDomDocument domDoc; if(!domDoc.setContent(xmiClip, false, &error, &line)) { uWarning() << "Cannot set content:" << error << " Line:" << line; return false; } QDomNode xmiClipNode = domDoc.firstChild(); QDomElement root = xmiClipNode.toElement(); if (root.isNull()) { return false; } // make sure it is an XMI clip if (root.tagName() != QLatin1String("xmiclip")) { return false; } //UMLObjects QDomNode objectsNode = xmiClipNode.firstChild(); QDomNode objectElement = objectsNode.firstChild(); QDomElement element = objectElement.toElement(); if (element.isNull()) { return false;//return ok as it means there is no umlobjects } while (!element.isNull()) { QString type = element.tagName(); UMLClassifierListItem *pObject = newParent->makeChildObject(type); if(!pObject) { uWarning() << "Given wrong type of umlobject to create:" << type; return false; } if(!pObject->loadFromXMI1(element)) { uWarning() << "Failed to load object from XMI."; return false; } pObject->resolveRef(); objects.append(pObject); objectElement = objectElement.nextSibling(); element = objectElement.toElement(); } return true; } /** * Execute the CmdCreateWidget undo command */ void UMLDragData::executeCreateWidgetCommand(UMLWidget* widget) { UMLApp::app()->executeCommand(new Uml::CmdCreateWidget(widget)); } /** * Decode UMLObjects from clip */ bool UMLDragData::decodeObjects(QDomNode& objectsNode, UMLObjectList& objects, bool skipIfObjectExists) { UMLDoc* doc = UMLApp::app()->document(); QDomNode objectElement = objectsNode.firstChild(); QDomElement element = objectElement.toElement(); if (element.isNull()) { return false;//return ok as it means there is no umlobjects } UMLObject* pObject = 0; while (!element.isNull()) { pObject = 0; QString type = element.tagName(); Uml::ID::Type elmId = Uml::ID::fromString(element.attribute(QLatin1String("xmi.id"))); QString stereotype = element.attribute(QLatin1String("stereotype")); bool objectExists = (doc->findObjectById(elmId) != 0); // This happens when pasting clip4 (widgets): pasting widgets must // not duplicate the UMLObjects, unless they don't exists (other // instance of umbrello) if (skipIfObjectExists && objectExists) { objectElement = objectElement.nextSibling(); element = objectElement.toElement(); continue; } // Remove ownedElements from containers: the clip already contains all children // as a flat list (UMLClipboard::insertItemChildren) if (type == QLatin1String("UML:Package") || type == QLatin1String("UML:Class") || type == QLatin1String("UML:Interface") || type == QLatin1String("UML:Component")) { QDomNodeList list = element.childNodes(); for (int i=(list.length() - 1); i>=0; i--) { QDomNode child = list.at(i); QString tagName = child.toElement().tagName(); if (tagName == QLatin1String("UML:Namespace.ownedElement") || tagName == QLatin1String("UML:Namespace.contents")) { element.removeChild(child); } } } pObject = Object_Factory::makeObjectFromXMI(type, stereotype); if(!pObject) { uWarning() << "Given wrong type of umlobject to create: " << type; return false; } Uml::ID::Type oldParentId = Uml::ID::fromString( element.attribute(QLatin1String("namespace"), QLatin1String("-1")) ); // Determine the parent package of the pasted object UMLPackage* newParent = 0; if (oldParentId != Uml::ID::None) { Uml::ID::Type newParentId = doc->changeLog()->findNewID(oldParentId); if (newParentId == Uml::ID::None) { // Fallback to parent ID before paste (folder was not pasted in // this paste operation) newParentId = oldParentId; } newParent = doc->findObjectById(newParentId)->asUMLPackage(); } if (newParent == 0) { // Package is not in this clip, determine the parent based // on the selected tree view item newParent = Model_Utils::treeViewGetPackageFromCurrent(); } pObject->setUMLPackage(newParent); // Note: element should not be used after calling loadFromXMI1() because // it can point to an arbitrary child node if(!pObject->loadFromXMI1(element)) { uWarning() << "Failed to load object of type " << type << " from XMI"; delete pObject; return false; } // Assign a new ID if the object already existed before this paste, // this happens when pasting on listview items in the same document. if (objectExists) { pObject->setID( doc->assignNewID(pObject->id()) ); } UMLApp::app()->executeCommand(new Uml::CmdCreateUMLObject(pObject)); doc->signalUMLObjectCreated(pObject); objects.append(pObject); objectElement = objectElement.nextSibling(); element = objectElement.toElement(); } return true; } /** * Decode views from clip */ bool UMLDragData::decodeViews(QDomNode& umlviewsNode, UMLViewList& diagrams) { QDomNode diagramNode = umlviewsNode.firstChild(); QDomElement diagramElement = diagramNode.toElement(); if (diagramElement.isNull()) { uWarning() << "No diagrams in XMI clip."; return false; } UMLListView *listView = UMLApp::app()->listView(); while (!diagramElement.isNull()) { if (NoteWidget::s_pCurrentNote) { QString idStr = diagramElement.attribute(QLatin1String("xmi.id"), QLatin1String("-1")); Uml::ID::Type id = Uml::ID::fromString(idStr); if (id == Uml::ID::None) { uDebug() << "Cannot paste diagram hyperlink to note because decoding of xmi.id failed"; return false; } NoteWidget::s_pCurrentNote->setDiagramLink(id); return true; } QString type = diagramElement.attribute(QLatin1String("type"), QLatin1String("0")); Uml::DiagramType::Enum dt = Uml::DiagramType::fromInt(type.toInt()); UMLListViewItem *parent = listView->findFolderForDiagram(dt); if (parent == 0) return false; UMLObject *po = parent->umlObject(); if (po == 0 || po->baseType() != UMLObject::ot_Folder) { uError() << "Bad parent for view."; return false; } UMLFolder *f = po->asUMLFolder(); UMLView* view = new UMLView(f); view->umlScene()->loadFromXMI1(diagramElement); diagrams.append(view); diagramNode = diagramNode.nextSibling(); diagramElement = diagramNode.toElement(); } return true; } /** * Converts application/x-uml-clip[1-5] clip type to an integer */ int UMLDragData::getCodingType(const QMimeData* mimeData) { int result = 0; if (mimeData->hasFormat(QLatin1String("application/x-uml-clip1"))) { result = 1; } if (mimeData->hasFormat(QLatin1String("application/x-uml-clip2"))) { result = 2; } if (mimeData->hasFormat(QLatin1String("application/x-uml-clip3"))) { result = 3; } if (mimeData->hasFormat(QLatin1String("application/x-uml-clip4"))) { result = 4; } if (mimeData->hasFormat(QLatin1String("application/x-uml-clip5"))) { result = 5; } if (mimeData->hasFormat(QLatin1String("text/plain"))) { result = 6; } return result; } diff --git a/umbrello/model_utils.cpp b/umbrello/model_utils.cpp index 68f389f79..30b7a8d55 100644 --- a/umbrello/model_utils.cpp +++ b/umbrello/model_utils.cpp @@ -1,2067 +1,2095 @@ /*************************************************************************** * 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; } /** * 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 lvt 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 (ot != UMLObject::ot_Entity && ot != UMLObject::ot_Category) + 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 295723e8c..150098f3b 100644 --- a/umbrello/model_utils.h +++ b/umbrello/model_utils.h @@ -1,138 +1,139 @@ /*************************************************************************** * 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); 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/umlscene.cpp b/umbrello/umlscene.cpp index eda023176..352d56cd8 100644 --- a/umbrello/umlscene.cpp +++ b/umbrello/umlscene.cpp @@ -1,4174 +1,4159 @@ /*************************************************************************** * 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 "umlscene.h" // application specific includes #include "activitywidget.h" #include "actorwidget.h" #include "artifactwidget.h" #include "association.h" #include "associationwidget.h" #include "assocrules.h" #include "attribute.h" #include "boxwidget.h" #include "classifier.h" #include "classifierwidget.h" #include "classoptionspage.h" #include "cmds.h" #include "componentwidget.h" #include "datatype.h" #include "diagram_utils.h" #include "pinportbase.h" #include "datatypewidget.h" #include "debug_utils.h" #include "dialog_utils.h" #include "docwindow.h" #include "entity.h" #include "entitywidget.h" #include "enumwidget.h" #include "floatingtextwidget.h" #include "folder.h" #include "foreignkeyconstraint.h" #include "forkjoinwidget.h" #include "idchangelog.h" #include "import_utils.h" #include "layoutgenerator.h" #include "layoutgrid.h" #include "messagewidget.h" #include "model_utils.h" #include "notewidget.h" #include "object_factory.h" #include "objectnodewidget.h" #include "objectwidget.h" #include "package.h" #include "packagewidget.h" #include "pinwidget.h" #include "seqlinewidget.h" #include "signalwidget.h" #include "statewidget.h" #include "toolbarstate.h" #include "toolbarstatefactory.h" #include "uml.h" #include "umldoc.h" #include "umldragdata.h" #include "umlfiledialog.h" #include "umllistview.h" #include "umllistviewitem.h" #include "umlobject.h" #include "umlobjectlist.h" #include "umlrole.h" #include "umlscenepopupmenu.h" #include "umlview.h" #include "umlviewimageexporter.h" #include "umlwidget.h" #include "uniqueid.h" #include "widget_factory.h" #include "widget_utils.h" #include "widgetlist_utils.h" //kde include files #if QT_VERSION < 0x050000 #include #include #endif #include #include #include // include files for Qt #include #include #include #include #include #include // system includes #include // for ceil // static members const qreal UMLScene::defaultCanvasSize = 5000; bool UMLScene::m_showDocumentationIndicator = false; using namespace Uml; DEBUG_REGISTER(UMLScene) /** * The class UMLScenePrivate is intended to hold private * members/classes to reduce the size of the public class * and to speed up recompiling. * The migration to this class is not complete yet. */ class UMLScenePrivate { public: UMLScenePrivate() {} }; /** * Constructor. */ UMLScene::UMLScene(UMLFolder *parentFolder, UMLView *view) : QGraphicsScene(0, 0, defaultCanvasSize, defaultCanvasSize), m_nLocalID(Uml::ID::None), m_nID(Uml::ID::None), m_Type(Uml::DiagramType::Undefined), m_Name(QString()), m_Documentation(QString()), m_Options(Settings::optionState()), m_bUseSnapToGrid(false), m_bUseSnapComponentSizeToGrid(false), m_isOpen(true), m_nCollaborationId(0), m_bCreateObject(false), m_bDrawSelectedOnly(false), m_bPaste(false), m_bStartedCut(false), m_d(new UMLScenePrivate), m_view(view), m_pFolder(parentFolder), m_pIDChangesLog(0), m_isActivated(false), m_bPopupShowing(false), m_autoIncrementSequence(false) { m_PastePoint = QPointF(0, 0); m_pImageExporter = new UMLViewImageExporter(this); // setup signals connect(UMLApp::app(), SIGNAL(sigCutSuccessful()), this, SLOT(slotCutSuccessful())); // Create the ToolBarState factory. This class is not a singleton, because it // needs a pointer to this object. m_pToolBarStateFactory = new ToolBarStateFactory(); m_pToolBarState = m_pToolBarStateFactory->getState(WorkToolBar::tbb_Arrow, this); m_doc = UMLApp::app()->document(); // // settings for background // setBackgroundBrush(QColor(195, 195, 195)); m_layoutGrid = new LayoutGrid(this); // fix crash caused by Qt stale item issue see https://bugs.kde.org/show_bug.cgi?id=383592 setItemIndexMethod(NoIndex); } /** * Destructor. */ UMLScene::~UMLScene() { delete m_pImageExporter; m_pImageExporter = 0; delete m_pIDChangesLog; m_pIDChangesLog = 0; // before we can delete the QCanvas, all widgets must be explicitly // removed // otherwise the implicit remove of the contained widgets will cause // events which would demand a valid connected QCanvas // ==> this causes umbrello to crash for some - larger?? - projects // first avoid all events, which would cause some update actions // on deletion of each removed widget blockSignals(true); removeAllWidgets(); delete m_pToolBarStateFactory; m_pToolBarStateFactory = 0; delete m_layoutGrid; delete m_d; } /** * Return the UMLFolder in which this diagram lives. */ UMLFolder* UMLScene::folder() const { return m_pFolder; } /** * Set the UMLFolder in which this diagram lives. */ void UMLScene::setFolder(UMLFolder *folder) { m_pFolder = folder; } /** * Returns the active view associated with this scene. */ UMLView* UMLScene::activeView() const { return m_view; } /** * Return the documentation of the diagram. */ QString UMLScene::documentation() const { return m_Documentation; } /** * Set the documentation of the diagram. */ void UMLScene::setDocumentation(const QString &doc) { m_Documentation = doc; } /** * Return the state of the auto increment sequence */ bool UMLScene::autoIncrementSequence() const { return m_autoIncrementSequence; } void UMLScene::setAutoIncrementSequence(bool state) { m_autoIncrementSequence = state; } /** * Return the next auto increment sequence value */ QString UMLScene::autoIncrementSequenceValue() { int sequenceNumber = 0; if (type() == Uml::DiagramType::Sequence) { foreach (MessageWidget* message, messageList()) { bool ok; int value = message->sequenceNumber().toInt(&ok); if (ok && value > sequenceNumber) sequenceNumber = value; } } else if (type() == Uml::DiagramType::Collaboration) { foreach (AssociationWidget* assoc, associationList()) { bool ok; int value = assoc->sequenceNumber().toInt(&ok); if (ok && value > sequenceNumber) sequenceNumber = value; } } return QString::number(sequenceNumber + 1); } /** * Return the name of the diagram. */ QString UMLScene::name() const { return m_Name; } /** * Set the name of the diagram. */ void UMLScene::setName(const QString &name) { m_Name = name; } /** * Returns the type of the diagram. */ DiagramType::Enum UMLScene::type() const { return m_Type; } /** * Set the type of diagram. */ void UMLScene::setType(DiagramType::Enum type) { m_Type = type; } /** * Returns the ID of the diagram. */ Uml::ID::Type UMLScene::ID() const { return m_nID; } /** * Sets the ID of the diagram. */ void UMLScene::setID(Uml::ID::Type id) { m_nID = id; } /** * Returns the position of the diagram. */ QPointF UMLScene::pos() const { return m_Pos; } /** * Sets the position of the diagram. */ void UMLScene::setPos(const QPointF &pos) { m_Pos = pos; } /** * Returns the fill color to use. */ const QColor& UMLScene::fillColor() const { return m_Options.uiState.fillColor; } /** * Set the background color. * * @param color The color to use. */ void UMLScene::setFillColor(const QColor &color) { m_Options.uiState.fillColor = color; emit sigFillColorChanged(ID()); } /** * Returns the line color to use. */ const QColor& UMLScene::lineColor() const { return m_Options.uiState.lineColor; } /** * Sets the line color. * * @param color The color to use. */ void UMLScene::setLineColor(const QColor &color) { m_Options.uiState.lineColor = color; emit sigLineColorChanged(ID()); } /** * Returns the line width to use. */ uint UMLScene::lineWidth() const { return m_Options.uiState.lineWidth; } /** * Sets the line width. * * @param width The width to use. */ void UMLScene::setLineWidth(uint width) { m_Options.uiState.lineWidth = width; emit sigLineWidthChanged(ID()); } /** * Returns the text color to use. */ const QColor& UMLScene::textColor() const { return m_Options.uiState.textColor; } /** * Sets the text color. * * @param color The color to use. */ void UMLScene::setTextColor(const QColor& color) { m_Options.uiState.textColor = color; emit sigTextColorChanged(ID()); } /** * return grid dot color * * @return Color */ const QColor& UMLScene::gridDotColor() const { return m_layoutGrid->gridDotColor(); } /** * set grid dot color * * @param color grid dot color */ void UMLScene::setGridDotColor(const QColor& color) { m_Options.uiState.gridDotColor = color; m_layoutGrid->setGridDotColor(color); } /** * Returns the options being used. */ Settings::OptionState& UMLScene::optionState() { return m_Options; } /** * Sets the options to be used. */ void UMLScene::setOptionState(const Settings::OptionState& options) { m_Options = options; setBackgroundBrush(options.uiState.backgroundColor); setGridDotColor(options.uiState.gridDotColor); } /** * Returns a reference to the association list. */ const AssociationWidgetList UMLScene::associationList() const { AssociationWidgetList result; foreach(QGraphicsItem *item, items()) { AssociationWidget *w = dynamic_cast(item); if (w) result.append(w); } return result; } /** * Returns a reference to the widget list. */ const UMLWidgetList UMLScene::widgetList() const { UMLWidgetList result; foreach(QGraphicsItem *item, items()) { UMLWidget *w = dynamic_cast(item); if (w && !w->isMessageWidget() && !w->isAssociationWidget()) result.append(w); } return result; } void UMLScene::addWidgetCmd(UMLWidget* widget) { Q_ASSERT(0 != widget); addItem(widget); } void UMLScene::addWidgetCmd(AssociationWidget* widget) { Q_ASSERT(0 != widget); addItem(widget); } /** * Returns a reference to the message list. */ const MessageWidgetList UMLScene::messageList() const { MessageWidgetList result; foreach(QGraphicsItem *item, items()) { MessageWidget *w = dynamic_cast(item); if (w) result.append(w); } return result; } /** * Used for creating unique name of collaboration messages. */ int UMLScene::generateCollaborationId() { return ++m_nCollaborationId; } /** * Returns the open state. * @return when true diagram is shown to the user */ bool UMLScene::isOpen() const { return m_isOpen; } /** * Sets the flag 'isOpen'. * @param isOpen flag indicating that the diagram is shown to the user */ void UMLScene::setIsOpen(bool isOpen) { m_isOpen = isOpen; } /** * Contains the implementation for printing functionality. */ void UMLScene::print(QPrinter *pPrinter, QPainter & pPainter) { bool isFooter = optionState().generalState.footerPrinting; // The printer will probably use a different font with different font metrics, // force the widgets to update accordingly on paint forceUpdateWidgetFontMetrics(&pPainter); QRectF source = diagramRect(); QRect paper = pPrinter->paperRect(); QRect page = pPrinter->pageRect(); // use the painter font metrics, not the screen fm! QFontMetrics fm = pPainter.fontMetrics(); int fontHeight = fm.lineSpacing(); if (paper == page) { QSize margin = page.size() * 0.025; page.adjust(margin.width(), margin.height(), -margin.width(), -margin.height()); } if (isFooter) { int margin = 3 + 3 * fontHeight; page.adjust(0, 0, 0, -margin); } getDiagram(pPainter, QRectF(source), QRectF(page)); //draw foot note if (isFooter) { page.adjust(0, 0, 0, fontHeight); QString string = i18n("Diagram: %2 Page %1", 1, name()); QColor textColor(50, 50, 50); pPainter.setPen(textColor); pPainter.drawLine(page.left(), page.bottom() , page.right(), page.bottom()); pPainter.drawText(page.left(), page.bottom() + 3, page.right(), 2*fontHeight, Qt::AlignLeft, string); } // next painting will most probably be to a different device (i.e. the screen) forceUpdateWidgetFontMetrics(0); } /** * Initialize and announce a newly created widget. * Auxiliary to contentsMouseReleaseEvent(). */ void UMLScene::setupNewWidget(UMLWidget *w, bool setPosition) { if (setPosition && (!w->isPinWidget()) && (!w->isPortWidget()) && (!w->isObjectWidget())) { // ObjectWidget's position is handled by the widget w->setX(m_Pos.x()); w->setY(m_Pos.y()); } w->setVisible(true); w->activate(); w->setFontCmd(font()); w->slotFillColorChanged(ID()); w->slotTextColorChanged(ID()); w->slotLineWidthChanged(ID()); resizeSceneToItems(); m_doc->setModified(); if (m_doc->loading()) { // do not emit signals while loading addWidgetCmd(w); // w->activate(); // will be done by UMLDoc::activateAllViews() after loading } else { UMLApp::app()->executeCommand(new CmdCreateWidget(w)); } } /** * Return whether we are currently creating an object. */ bool UMLScene::getCreateObject() const { return m_bCreateObject; } /** * Set whether we are currently creating an object. */ void UMLScene::setCreateObject(bool bCreate) { m_bCreateObject = bCreate; } /** * Overrides the standard operation. */ void UMLScene::showEvent(QShowEvent* /*se*/) { connect(m_doc, SIGNAL(sigObjectCreated(UMLObject*)), this, SLOT(slotObjectCreated(UMLObject*))); connect(this, SIGNAL(sigAssociationRemoved(AssociationWidget*)), UMLApp::app()->docWindow(), SLOT(slotAssociationRemoved(AssociationWidget*))); connect(this, SIGNAL(sigWidgetRemoved(UMLWidget*)), UMLApp::app()->docWindow(), SLOT(slotWidgetRemoved(UMLWidget*))); } /** * Overrides the standard operation. */ void UMLScene::hideEvent(QHideEvent* /*he*/) { disconnect(m_doc, SIGNAL(sigObjectCreated(UMLObject*)), this, SLOT(slotObjectCreated(UMLObject*))); disconnect(this, SIGNAL(sigAssociationRemoved(AssociationWidget*)), UMLApp::app()->docWindow(), SLOT(slotAssociationRemoved(AssociationWidget*))); disconnect(this, SIGNAL(sigWidgetRemoved(UMLWidget*)), UMLApp::app()->docWindow(), SLOT(slotWidgetRemoved(UMLWidget*))); } /** * Changes the current tool to the selected tool. * The current tool is cleaned and the selected tool initialized. */ void UMLScene::slotToolBarChanged(int c) { m_pToolBarState->cleanBeforeChange(); m_pToolBarState = m_pToolBarStateFactory->getState((WorkToolBar::ToolBar_Buttons)c, this); m_pToolBarState->init(); m_bPaste = false; } /** * Slot called when an object is created. * @param o created UML object */ void UMLScene::slotObjectCreated(UMLObject* o) { DEBUG(DBG_SRC) << "scene=" << name() << " / object=" << o->name(); m_bPaste = false; //check to see if we want the message //may be wanted by someone else e.g. list view if (!m_bCreateObject) { return; } UMLWidget* newWidget = Widget_Factory::createWidget(this, o); if (!newWidget) { return; } setupNewWidget(newWidget); m_bCreateObject = false; - switch (o->baseType()) { - 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: - createAutoAssociations(newWidget); - // We need to invoke createAutoAttributeAssociations() - // on all other widgets again because the newly created - // widget might saturate some latent attribute assocs. - createAutoAttributeAssociations2(newWidget); - break; - default: - break; + if (Model_Utils::hasAssociations(o->baseType())) + { + createAutoAssociations(newWidget); + // We need to invoke createAutoAttributeAssociations() + // on all other widgets again because the newly created + // widget might saturate some latent attribute assocs. + createAutoAttributeAssociations2(newWidget); } resizeSceneToItems(); } /** * Slot called when an object is removed. * @param o removed UML object */ void UMLScene::slotObjectRemoved(UMLObject * o) { m_bPaste = false; Uml::ID::Type id = o->id(); foreach(UMLWidget* obj, widgetList()) { if (obj->id() != id) continue; removeWidget(obj); break; } } /** * Override standard method. */ void UMLScene::dragEnterEvent(QGraphicsSceneDragDropEvent *e) { UMLDragData::LvTypeAndID_List tidList; if (!UMLDragData::getClip3TypeAndID(e->mimeData(), tidList)) { DEBUG(DBG_SRC) << "UMLDragData::getClip3TypeAndID returned false"; return; } UMLDragData::LvTypeAndID_It tidIt(tidList); UMLDragData::LvTypeAndID * tid; if (!tidIt.hasNext()) { DEBUG(DBG_SRC) << "UMLDragData::getClip3TypeAndID returned empty list"; return; } tid = tidIt.next(); UMLListViewItem::ListViewType lvtype = tid->type; Uml::ID::Type id = tid->id; DiagramType::Enum diagramType = type(); UMLObject* temp = 0; //if dragging diagram - might be a drag-to-note if (Model_Utils::typeIsDiagram(lvtype)) { e->accept(); return; } //can't drag anything onto state/activity diagrams if (diagramType == DiagramType::State || diagramType == DiagramType::Activity) { e->ignore(); return; } //make sure can find UMLObject if (!(temp = m_doc->findObjectById(id))) { DEBUG(DBG_SRC) << "object " << Uml::ID::toString(id) << " not found"; e->ignore(); return; } bool bAccept = Model_Utils::typeIsAllowedInDiagram(temp, this); if (bAccept) { e->accept(); } else { e->ignore(); } } /** * Override standard method. */ void UMLScene::dragMoveEvent(QGraphicsSceneDragDropEvent* e) { e->accept(); } /** * Override standard method. */ void UMLScene::dropEvent(QGraphicsSceneDragDropEvent *e) { UMLDragData::LvTypeAndID_List tidList; if (!UMLDragData::getClip3TypeAndID(e->mimeData(), tidList)) { DEBUG(DBG_SRC) << "UMLDragData::getClip3TypeAndID returned error"; return; } UMLDragData::LvTypeAndID_It tidIt(tidList); UMLDragData::LvTypeAndID * tid; if (!tidIt.hasNext()) { DEBUG(DBG_SRC) << "UMLDragData::getClip3TypeAndID returned empty list"; return; } tid = tidIt.next(); UMLListViewItem::ListViewType lvtype = tid->type; Uml::ID::Type id = tid->id; if (Model_Utils::typeIsDiagram(lvtype)) { bool breakFlag = false; UMLWidget* w = 0; foreach(w, widgetList()) { if (w->isNoteWidget() && w->onWidget(e->scenePos())) { breakFlag = true; break; } } if (breakFlag) { NoteWidget *note = static_cast(w); note->setDiagramLink(id); } return; } UMLObject* o = m_doc->findObjectById(id); if (!o) { DEBUG(DBG_SRC) << "object id=" << Uml::ID::toString(id) << " not found"; return; } m_Pos = e->scenePos(); UMLWidget* newWidget = Widget_Factory::createWidget(this, o); if (!newWidget) { return; } setupNewWidget(newWidget); createAutoAssociations(newWidget); createAutoAttributeAssociations2(newWidget); } /** * Overrides the standard operation. * Calls the same method in the current tool bar state. */ void UMLScene::mouseMoveEvent(QGraphicsSceneMouseEvent* ome) { m_pToolBarState->mouseMove(ome); } /** * Override standard method. * Calls the same method in the current tool bar state. */ void UMLScene::mousePressEvent(QGraphicsSceneMouseEvent* event) { if (event->button() != Qt::LeftButton) { event->ignore(); return; } m_pToolBarState->mousePress(event); //TODO should be managed by widgets when are selected. Right now also has some //problems, such as clicking on a widget, and clicking to move that widget shows //documentation of the diagram instead of keeping the widget documentation. //When should diagram documentation be shown? When clicking on an empty //space in the diagram with arrow tool? UMLWidget* widget = widgetAt(event->scenePos()); if (widget) { DEBUG(DBG_SRC) << "widget = " << widget->name() << " / type = " << widget->baseTypeStr(); UMLApp::app()->docWindow()->showDocumentation(widget); event->accept(); } else { AssociationWidget* association = associationAt(event->scenePos()); if (association) { DEBUG(DBG_SRC) << "association widget = " << association->name() << " / type = " << association->baseTypeStr(); // the following is done in AssociationWidget::setSelected() // UMLApp::app()->docWindow()->showDocumentation(association, true); // event->accept(); } //:TODO: else if (clicking on other elements with documentation) { //:TODO: UMLApp::app()->docWindow()->showDocumentation(umlObject, true); else { // clicking on an empty space in the diagram with arrow tool UMLApp::app()->docWindow()->showDocumentation(this); event->accept(); } } } /** * Override standard method. * Calls the same method in the current tool bar state. */ void UMLScene::mouseDoubleClickEvent(QGraphicsSceneMouseEvent* event) { if (!m_doc->loading()) m_pToolBarState->mouseDoubleClick(event); if (!event->isAccepted()) { // show properties dialog of the scene if (m_view->showPropertiesDialog() == true) { m_doc->setModified(); } event->accept(); } } /** * Overrides the standard operation. * Calls the same method in the current tool bar state. */ void UMLScene::mouseReleaseEvent(QGraphicsSceneMouseEvent* ome) { m_pToolBarState->mouseRelease(ome); } /** * Determine whether on a sequence diagram we have clicked on a line * of an Object. * * @return The widget owning the line which was clicked. * Returns 0 if no line was clicked on. */ ObjectWidget * UMLScene::onWidgetLine(const QPointF &point) const { foreach(UMLWidget* obj, widgetList()) { ObjectWidget *ow = obj->asObjectWidget(); if (ow == 0) continue; SeqLineWidget *pLine = ow->sequentialLine(); if (pLine == 0) { uError() << "SeqLineWidget of " << ow->name() << " (id=" << Uml::ID::toString(ow->localID()) << ") is NULL"; continue; } if (pLine->onWidget(point)) return ow; } return 0; } /** * Determine whether on a sequence diagram we have clicked on * the destruction box of an Object. * * @return The widget owning the destruction box which was clicked. * Returns 0 if no destruction box was clicked on. */ ObjectWidget * UMLScene::onWidgetDestructionBox(const QPointF &point) const { foreach(UMLWidget* obj, widgetList()) { ObjectWidget *ow = obj->asObjectWidget(); if (ow == 0) continue; SeqLineWidget *pLine = ow->sequentialLine(); if (pLine == 0) { uError() << "SeqLineWidget of " << ow->name() << " (id=" << Uml::ID::toString(ow->localID()) << ") is NULL"; continue; } if (pLine->onDestructionBox(point)) return ow; } return 0; } /** * Return pointer to the first selected widget (for multi-selection) */ UMLWidget* UMLScene::getFirstMultiSelectedWidget() const { if (selectedWidgets().size() == 0) return 0; return selectedWidgets().first(); } /** * Tests the given point against all widgets and returns the * widget for which the point is within its bounding rectangle. * In case of multiple matches, returns the smallest widget. * Returns NULL if the point is not inside any widget. * TODO: What about using QGraphicsScene::items(...)? */ UMLWidget* UMLScene::widgetAt(const QPointF& p) { return dynamic_cast(itemAt(p)); } /** * Tests the given point against all associations and returns the * association widget for which the point is on the line. * Returns NULL if the point is not inside any association. * CHECK: This is the same method as in ToolBarState. */ AssociationWidget* UMLScene::associationAt(const QPointF& p) { foreach (AssociationWidget* association, associationList()) { if (association->onAssociation(p)) { return association; } } return 0; } /** * Tests the given point against all associations and returns the * association widget for which the point is on the line. * Returns NULL if the point is not inside any association. */ MessageWidget* UMLScene::messageAt(const QPointF& p) { foreach(MessageWidget *message, messageList()) { if (message->onWidget(p)) { return message; } } return 0; } /** * Sees if a message is relevant to the given widget. If it does delete it. * @param w The widget to check messages against. */ void UMLScene::checkMessages(ObjectWidget * w) { if (type() != DiagramType::Sequence) { return; } foreach(MessageWidget *obj, messageList()) { if (obj->hasObjectWidget(w)) { removeWidgetCmd(obj); } } } /** * Returns whether a widget is already on the diagram. * * @param id The id of the widget to check for. * * @return Returns pointer to the widget if it is on the diagram, NULL if not. */ UMLWidget* UMLScene::widgetOnDiagram(Uml::ID::Type id) { foreach(UMLWidget *obj, widgetList()) { if (!obj) continue; UMLWidget* w = obj->widgetWithID(id); if (w) return w; } foreach(UMLWidget *obj, messageList()) { // CHECK: Should MessageWidget reimplement widgetWithID() ? // If yes then we should use obj->widgetWithID(id) here too. if (id == obj->id()) return obj; } return 0; } /** * Finds a widget with the given ID. * Search both our UMLWidget AND MessageWidget lists. * @param id The ID of the widget to find. * * @return Returns the widget found, returns 0 if no widget found. */ UMLWidget * UMLScene::findWidget(Uml::ID::Type id) { foreach(UMLWidget* obj, widgetList()) { if (!obj) continue; UMLWidget* w = obj->widgetWithID(id); if (w) { return w; } } foreach(UMLWidget* obj, messageList()) { // CHECK: Should MessageWidget reimplement widgetWithID() ? // If yes then we should use obj->widgetWithID(id) here too. if (obj->localID() == id || obj->id() == id) return obj; } return 0; } /** * Finds an association widget with the given ID. * * @param id The ID of the widget to find. * * @return Returns the widget found, returns 0 if no widget found. */ AssociationWidget * UMLScene::findAssocWidget(Uml::ID::Type id) { foreach(AssociationWidget* obj, associationList()) { UMLAssociation* umlassoc = obj->association(); if (umlassoc && umlassoc->id() == id) { return obj; } } return 0; } /** * Finds an association widget with the given widgets and the given role B name. * Considers the following association types: * at_Association, at_UniAssociation, at_Composition, at_Aggregation * This is used for seeking an attribute association. * * @param pWidgetA Pointer to the UMLWidget of role A. * @param pWidgetB Pointer to the UMLWidget of role B. * @param roleNameB Name at the B side of the association (the attribute name) * * @return Returns the widget found, returns 0 if no widget found. */ AssociationWidget * UMLScene::findAssocWidget(UMLWidget *pWidgetA, UMLWidget *pWidgetB, const QString& roleNameB) { foreach(AssociationWidget* assoc, associationList()) { const Uml::AssociationType::Enum testType = assoc->associationType(); if (testType != Uml::AssociationType::Association && testType != Uml::AssociationType::UniAssociation && testType != Uml::AssociationType::Composition && testType != Uml::AssociationType::Aggregation && testType != Uml::AssociationType::Relationship) { continue; } if (pWidgetA->id() == assoc->widgetIDForRole(Uml::RoleType::A) && pWidgetB->id() == assoc->widgetIDForRole(Uml::RoleType::B) && assoc->roleName(Uml::RoleType::B) == roleNameB) { return assoc; } } return 0; } /** * Finds an association widget with the given type and widgets. * * @param at The AssociationType of the widget to find. * @param pWidgetA Pointer to the UMLWidget of role A. * @param pWidgetB Pointer to the UMLWidget of role B. * * @return Returns the widget found, returns 0 if no widget found. */ AssociationWidget * UMLScene::findAssocWidget(AssociationType::Enum at, UMLWidget *pWidgetA, UMLWidget *pWidgetB) { foreach(AssociationWidget* assoc, associationList()) { Uml::AssociationType::Enum testType = assoc->associationType(); if (testType != at) { continue; } if (pWidgetA->id() == assoc->widgetIDForRole(Uml::RoleType::A) && pWidgetB->id() == assoc->widgetIDForRole(Uml::RoleType::B)) { return assoc; } } return 0; } /** * Remove a widget from view (undo command) * * @param o The widget to remove. */ void UMLScene::removeWidget(UMLWidget * o) { UMLApp::app()->executeCommand(new CmdRemoveWidget(o)); } /** * Remove a widget from view. * * @param o The widget to remove. */ void UMLScene::removeWidgetCmd(UMLWidget * o) { if (!o) return; emit sigWidgetRemoved(o); removeAssociations(o); removeOwnedWidgets(o); WidgetBase::WidgetType t = o->baseType(); if (type() == DiagramType::Sequence && t == WidgetBase::wt_Object) { checkMessages(static_cast(o)); } o->cleanup(); o->setSelectedFlag(false); disconnect(this, SIGNAL(sigFillColorChanged(Uml::ID::Type)), o, SLOT(slotFillColorChanged(Uml::ID::Type))); disconnect(this, SIGNAL(sigLineColorChanged(Uml::ID::Type)), o, SLOT(slotLineColorChanged(Uml::ID::Type))); disconnect(this, SIGNAL(sigTextColorChanged(Uml::ID::Type)), o, SLOT(slotTextColorChanged(Uml::ID::Type))); removeItem(o); o->deleteLater(); m_doc->setModified(true); } /** * Remove all widgets that have given widget as owner. * * @param o The owner widget that will be removed. */ void UMLScene::removeOwnedWidgets(UMLWidget* o) { foreach(QGraphicsItem* item, o->childItems()) { UMLWidget* widget = dynamic_cast(item); if ((widget != 0) && (widget->isPinWidget() || widget->isPortWidget())) { removeWidgetCmd(widget); } } } /** * Returns background color */ const QColor& UMLScene::backgroundColor() const { return backgroundBrush().color(); } /** * Returns whether to use the fill/background color */ bool UMLScene::useFillColor() const { return m_Options.uiState.useFillColor; } /** * Sets whether to use the fill/background color */ void UMLScene::setUseFillColor(bool ufc) { m_Options.uiState.useFillColor = ufc; } /** * Gets the smallest area to print. * * @return Returns the smallest area to print. */ QRectF UMLScene::diagramRect() { return itemsBoundingRect(); } /** * Returns a list of selected widgets * @return list of selected widgets based on class UMLWidget * @note This method returns widgets including message widgets, but no association widgets */ UMLWidgetList UMLScene::selectedWidgets() const { QList items = selectedItems(); UMLWidgetList widgets; foreach(QGraphicsItem *item, items) { UMLWidget *w = dynamic_cast(item); if (w) widgets.append(w); } return widgets; } /** * Returns a list of selected association widgets * @return list of selected widgets based on class AssociationWidget */ AssociationWidgetList UMLScene::selectedAssociationWidgets() const { QList items = selectedItems(); AssociationWidgetList widgets; foreach(QGraphicsItem *item, items) { AssociationWidget *w = dynamic_cast(item); if (w) widgets.append(w); } return widgets; } /** * Returns a list of selected message widgets * @return list of selected widgets based on class MessageWidget */ UMLWidgetList UMLScene::selectedMessageWidgets() const { QList items = selectedItems(); UMLWidgetList widgets; foreach(QGraphicsItem *item, items) { MessageWidget *w = dynamic_cast(item); if (w) widgets.append(w); } return widgets; } /** * Clear the selected widgets list. */ void UMLScene::clearSelected() { clearSelection(); //m_doc->enableCutCopy(false); } /** * Move all the selected widgets by a relative X and Y offset. * TODO: Only used in UMLApp::handleCursorKeyReleaseEvent * * @param dX The distance to move horizontally. * @param dY The distance to move vertically. */ void UMLScene::moveSelectedBy(qreal dX, qreal dY) { // DEBUG(DBG_SRC) << "********** m_selectedList count=" << m_selectedList.count(); foreach(UMLWidget *w, selectedWidgets()) { w->moveByLocal(dX, dY); } } /** * Set the useFillColor variable to all selected widgets * * @param useFC The state to set the widget to. */ void UMLScene::selectionUseFillColor(bool useFC) { if (useFC) { UMLApp::app()->beginMacro(i18n("Use fill color")); } else { UMLApp::app()->beginMacro(i18n("No fill color")); } foreach(UMLWidget* widget, selectedWidgets()) { widget->setUseFillColor(useFC); } UMLApp::app()->endMacro(); } /** * Set the font for all the currently selected items. */ void UMLScene::selectionSetFont(const QFont &font) { UMLApp::app()->beginMacro(i18n("Change font")); foreach(UMLWidget* temp, selectedWidgets()) { temp->setFont(font); } UMLApp::app()->endMacro(); } /** * Set the line color for all the currently selected items. */ void UMLScene::selectionSetLineColor(const QColor &color) { UMLApp::app()->beginMacro(i18n("Change line color")); foreach(UMLWidget *temp, selectedWidgets()) { temp->setLineColor(color); } AssociationWidgetList assoclist = selectedAssocs(); foreach(AssociationWidget *aw, assoclist) { aw->setLineColor(color); } UMLApp::app()->endMacro(); } /** * Set the line width for all the currently selected items. */ void UMLScene::selectionSetLineWidth(uint width) { UMLApp::app()->beginMacro(i18n("Change line width")); foreach(UMLWidget* temp, selectedWidgets()) { temp->setLineWidth(width); temp->setUsesDiagramLineWidth(false); } AssociationWidgetList assoclist = selectedAssocs(); foreach(AssociationWidget *aw, assoclist) { aw->setLineWidth(width); aw->setUsesDiagramLineWidth(false); } UMLApp::app()->endMacro(); } /** * Set the fill color for all the currently selected items. */ void UMLScene::selectionSetFillColor(const QColor &color) { UMLApp::app()->beginMacro(i18n("Change fill color")); foreach(UMLWidget* widget, selectedWidgets()) { widget->setFillColor(color); widget->setUsesDiagramFillColor(false); } UMLApp::app()->endMacro(); } /** * Set or unset the visual property (show ..) setting of all selected items. */ void UMLScene::selectionSetVisualProperty(ClassifierWidget::VisualProperty property, bool value) { UMLApp::app()->beginMacro(i18n("Change visual property")); foreach(UMLWidget *temp, selectedWidgets()) { ClassifierWidget *cw = temp->asClassifierWidget(); cw->setVisualProperty(property, value); } UMLApp::app()->endMacro(); } /** * Unselect child widgets when their owner is already selected. */ void UMLScene::unselectChildrenOfSelectedWidgets() { foreach(UMLWidget* widget, selectedWidgets()) { if (widget->isPinWidget() || widget->isPortWidget()) { foreach(UMLWidget* potentialParentWidget, selectedWidgets()) { if (widget->parentItem() == potentialParentWidget) { widget->setSelectedFlag(false); } } } } } /** * Delete the selected widgets list and the widgets in it. */ void UMLScene::deleteSelection() { AssociationWidgetList selectedAssociations = selectedAssociationWidgets(); int selectionCount = selectedWidgets().count() + selectedAssociations.count(); if (selectionCount == 0) return; // check related associations bool hasAssociations = false; foreach(UMLWidget* widget, selectedWidgets()) { if (widget->isTextWidget() && widget->asFloatingTextWidget()->textRole() != Uml::TextRole::Floating) { continue; } if (widget->isMessageWidget() || widget->associationWidgetList().size() > 0) hasAssociations = true; } if (hasAssociations && !Dialog_Utils::askDeleteAssociation()) return; UMLApp::app()->beginMacro(i18n("Delete widgets")); unselectChildrenOfSelectedWidgets(); foreach(UMLWidget* widget, selectedWidgets()) { // Don't delete text widget that are connect to associations as these will // be cleaned up by the associations. if (widget->isTextWidget() && widget->asFloatingTextWidget()->textRole() != Uml::TextRole::Floating) { widget->setSelectedFlag(false); widget->hide(); // message widgets are handled later } else if (!widget->isMessageWidget()){ removeWidget(widget); } } // Delete any selected associations. foreach(AssociationWidget* assocwidget, selectedAssociations) { removeWidgetCmd(assocwidget); } // we also have to remove selected messages from sequence diagrams foreach(UMLWidget* cur_msgWgt, selectedMessageWidgets()) { removeWidget(cur_msgWgt); } //make sure list empty - it should be anyway, just a check. clearSelected(); UMLApp::app()->endMacro(); } /** * resize selected widgets */ void UMLScene::resizeSelection() { int selectionCount = selectedWidgets().count(); if (selectionCount > 1) { UMLApp::app()->beginMacro(i18n("Resize widgets")); } if (selectedCount() == 0) return; foreach(UMLWidget *w, selectedWidgets()) { w->resize(); } m_doc->setModified(); if (selectionCount > 1) { UMLApp::app()->endMacro(); } } /** * Selects all widgets */ void UMLScene::selectAll() { selectWidgets(sceneRect().left(), sceneRect().top(), sceneRect().right(), sceneRect().bottom()); } /** * Returns true if this diagram resides in an externalized folder. * CHECK: It is probably cleaner to move this to the UMLListViewItem. */ bool UMLScene::isSavedInSeparateFile() { if (optionState().generalState.tabdiagrams) { // Umbrello currently does not support external folders // when tabbed diagrams are enabled. return false; } const QString msgPrefix(QLatin1String("UMLScene::isSavedInSeparateFile(") + name() + QLatin1String("): ")); UMLListView *listView = UMLApp::app()->listView(); UMLListViewItem *lvItem = listView->findItem(m_nID); if (lvItem == 0) { uError() << msgPrefix << "listView->findUMLObject(this) returns false"; return false; } UMLListViewItem *parentItem = dynamic_cast(lvItem->parent()); if (parentItem == 0) { uError() << msgPrefix << "parent item in listview is not a UMLListViewItem (?)"; return false; } const UMLListViewItem::ListViewType lvt = parentItem->type(); if (! Model_Utils::typeIsFolder(lvt)) return false; UMLFolder *modelFolder = parentItem->umlObject()->asUMLFolder(); if (modelFolder == 0) { uError() << msgPrefix << "parent model object is not a UMLFolder (?)"; return false; } QString folderFile = modelFolder->folderFile(); return !folderFile.isEmpty(); } UMLSceneItemList UMLScene::collisions(const QPointF &p, int delta) { QPointF a = p-QPointF(delta, delta); QPointF b = p+QPointF(delta, delta); QList list = items(QRectF(a, b)); return list; } /** * Calls setSelected on the given UMLWidget and enters * it into the m_selectedList while making sure it is * there only once. */ void UMLScene::makeSelected(UMLWidget* uw) { if (uw) { uw->setSelected(true); } } /** * Selects all the widgets of the given association widget. */ void UMLScene::selectWidgetsOfAssoc(AssociationWidget * a) { if (a) { a->setSelected(true); //select the two widgets makeSelected(a->widgetForRole(Uml::RoleType::A)); makeSelected(a->widgetForRole(Uml::RoleType::B)); //select all the text makeSelected(a->multiplicityWidget(Uml::RoleType::A)); makeSelected(a->multiplicityWidget(Uml::RoleType::B)); makeSelected(a->roleWidget(Uml::RoleType::A)); makeSelected(a->roleWidget(Uml::RoleType::B)); makeSelected(a->changeabilityWidget(Uml::RoleType::A)); makeSelected(a->changeabilityWidget(Uml::RoleType::B)); } } /** * Selects all the widgets within an internally kept rectangle. */ void UMLScene::selectWidgets(qreal px, qreal py, qreal qx, qreal qy) { clearSelected(); QRectF rect; if (px <= qx) { rect.setLeft(px); rect.setRight(qx); } else { rect.setLeft(qx); rect.setRight(px); } if (py <= qy) { rect.setTop(py); rect.setBottom(qy); } else { rect.setTop(qy); rect.setBottom(py); } // Select UMLWidgets that fall within the selection rectangle foreach(UMLWidget* temp, widgetList()) { uIgnoreZeroPointer(temp); selectWidget(temp, &rect); } // Select messages that fall within the selection rectangle foreach(MessageWidget* temp, messageList()) { selectWidget(temp->asUMLWidget(), &rect); } // Select associations of selected widgets selectAssociations(true); // Automatically select all messages if two object widgets are selected foreach(MessageWidget *w, messageList()) { if (w->objectWidget(Uml::RoleType::A) && w->objectWidget(Uml::RoleType::B) && w->objectWidget(Uml::RoleType::A)->isSelected() && w->objectWidget(Uml::RoleType::B)->isSelected()) { makeSelected(w); } } } /** * Select a single widget * * If QRectF* rect is provided, the selection is only made if the widget is * visible within the rectangle. */ void UMLScene::selectWidget(UMLWidget* widget, QRectF* rect) { if (rect == 0) { makeSelected(widget); return; } int x = widget->x(); int y = widget->y(); int w = widget->width(); int h = widget->height(); QRectF rect2(x, y, w, h); //see if any part of widget is in the rectangle if (!rect->intersects(rect2)) { return; } //if it is text that is part of an association then select the association //and the objects that are connected to it. if (widget->isTextWidget()) { FloatingTextWidget *ft = widget->asFloatingTextWidget(); Uml::TextRole::Enum t = ft->textRole(); LinkWidget *lw = ft->link(); MessageWidget * mw = dynamic_cast(lw); if (mw) { makeSelected(mw); } else if (t != Uml::TextRole::Floating) { AssociationWidget * a = dynamic_cast(lw); if (a) selectWidgetsOfAssoc(a); } } else if (widget->isMessageWidget()) { MessageWidget *mw = widget->asMessageWidget(); makeSelected(mw); } if (widget->isVisible()) { makeSelected(widget); } } /** * Selects all the widgets from a list. */ void UMLScene::selectWidgets(UMLWidgetList &widgets) { foreach (UMLWidget* widget, widgets) makeSelected(widget); } /** * Returns the PNG picture of the paste operation. * @param diagram the class to store PNG picture of the paste operation. * @param rect the area of the diagram to copy */ void UMLScene::getDiagram(QPixmap &diagram, const QRectF &rect) { DEBUG(DBG_SRC) << "rect=" << rect << ", pixmap=" << diagram.rect(); QPainter painter(&diagram); painter.fillRect(0, 0, rect.width(), rect.height(), Qt::white); getDiagram(painter, rect); } /** * Paint diagram to the paint device * @param painter the QPainter to which the diagram is painted * @param source the area of the diagram to copy * @param target the rect where to paint into */ void UMLScene::getDiagram(QPainter &painter, const QRectF &source, const QRectF &target) { DEBUG(DBG_SRC) << "painter=" << painter.window() << ", source=" << source << ", target=" << target; //TODO unselecting and selecting later doesn't work now as the selection is //cleared in UMLSceneImageExporter. Check if the anything else than the //following is needed and, if it works, remove the clearSelected in //UMLSceneImageExporter and UMLSceneImageExporterModel UMLWidgetList selected = selectedWidgets(); foreach(UMLWidget* widget, selected) { widget->setSelected(false); } AssociationWidgetList selectedAssociationsList = selectedAssocs(); foreach(AssociationWidget* association, selectedAssociationsList) { association->setSelected(false); } // we don't want to get the grid bool showSnapGrid = isSnapGridVisible(); setSnapGridVisible(false); const int sourceMargin = 1; QRectF alignedSource(source); alignedSource.adjust(-sourceMargin, -sourceMargin, sourceMargin, sourceMargin); uDebug() << "TODO: Check if this render method is identical to cavnas()->drawArea()"; // [PORT] render(&painter, target, alignedSource, Qt::KeepAspectRatio); setSnapGridVisible(showSnapGrid); //select again foreach(UMLWidget* widget, selected) { widget->setSelected(true); } foreach(AssociationWidget* association, selectedAssociationsList) { association->setSelected(true); } } /** * Returns the imageExporter used to export the view. * * @return The imageExporter used to export the view. */ UMLViewImageExporter* UMLScene::getImageExporter() { return m_pImageExporter; } /** * makes this view the active view by asking the document to show us */ void UMLScene::slotActivate() { m_doc->changeCurrentView(ID()); } /** * Activate all the objects and associations after a load from the clipboard */ void UMLScene::activate() { //Activate Regular widgets then activate messages foreach(UMLWidget* obj, widgetList()) { uIgnoreZeroPointer(obj); //If this UMLWidget is already activated or is a MessageWidget then skip it if (obj->isActivated() || obj->isMessageWidget()) { continue; } if (obj->activate()) { obj->setVisible(true); } else { removeItem(obj); delete obj; } }//end foreach //Activate Message widgets foreach(UMLWidget* obj, messageList()) { //If this MessageWidget is already activated then skip it if (obj->isActivated()) continue; obj->activate(m_doc->changeLog()); obj->setVisible(true); }//end foreach // Activate all association widgets foreach(AssociationWidget* aw, associationList()) { if (aw->activate()) { if (m_PastePoint.x() != 0) { int x = m_PastePoint.x() - m_Pos.x(); int y = m_PastePoint.y() - m_Pos.y(); aw->moveEntireAssoc(x, y); } } else { removeWidgetCmd(aw); delete aw; } } } /** * Return the amount of widgets selected. * * @param filterText When true, do NOT count floating text widgets that * belong to other widgets (i.e. only count TextRole::Floating.) * Default: Count all widgets. * @return Number of widgets selected. */ int UMLScene::selectedCount(bool filterText) const { if (!filterText) return selectedWidgets().count(); int counter = 0; foreach(UMLWidget* temp, selectedWidgets()) { if (temp->isTextWidget()) { const FloatingTextWidget *ft = static_cast(temp); if (ft->textRole() == TextRole::Floating) counter++; } else { counter++; } } return counter; } /** * Fills the List with all the selected widgets from the diagram * The list can be filled with all the selected widgets, or be filtered to prevent * text widgets other than tr_Floating to be append. * * @param filterText Don't append the text, unless their role is tr_Floating * @return The UMLWidgetList to fill. */ UMLWidgetList UMLScene::selectedWidgetsExt(bool filterText /*= true*/) { UMLWidgetList widgetList; foreach(UMLWidget* widgt, selectedWidgets()) { if (filterText && widgt->isTextWidget()) { FloatingTextWidget *ft = widgt->asFloatingTextWidget(); if (ft->textRole() == Uml::TextRole::Floating) widgetList.append(widgt); } else { widgetList.append(widgt); } } return widgetList; } /** * Returns a list with all the selected associations from the diagram */ AssociationWidgetList UMLScene::selectedAssocs() { AssociationWidgetList assocWidgetList; foreach(AssociationWidget* assocwidget, associationList()) { if (assocwidget->isSelected()) assocWidgetList.append(assocwidget); } return assocWidgetList; } /** * Adds a floating text widget to the view */ void UMLScene::addFloatingTextWidget(FloatingTextWidget* pWidget) { int wX = pWidget->x(); int wY = pWidget->y(); bool xIsOutOfRange = (wX < sceneRect().left() || wX > sceneRect().right()); bool yIsOutOfRange = (wY < sceneRect().top() || wY > sceneRect().bottom()); if (xIsOutOfRange || yIsOutOfRange) { QString name = pWidget->name(); if (name.isEmpty()) { FloatingTextWidget *ft = pWidget->asFloatingTextWidget(); if (ft) name = ft->displayText(); } DEBUG(DBG_SRC) << name << " type=" << pWidget->baseTypeStr() << ": position (" << wX << "," << wY << ") is out of range"; if (xIsOutOfRange) { pWidget->setX(0); wX = 0; } if (yIsOutOfRange) { pWidget->setY(0); wY = 0; } } addWidgetCmd(pWidget); } /** * Adds an association to the view from the given data. * Use this method when pasting. */ bool UMLScene::addAssociation(AssociationWidget* pAssoc, bool isPasteOperation) { if (!pAssoc) { return false; } const Uml::AssociationType::Enum assocType = pAssoc->associationType(); if (isPasteOperation) { IDChangeLog * log = m_doc->changeLog(); if (!log) { return false; } Uml::ID::Type ida = Uml::ID::None, idb = Uml::ID::None; if (type() == DiagramType::Collaboration || type() == DiagramType::Sequence) { //check local log first ida = m_pIDChangesLog->findNewID(pAssoc->widgetIDForRole(Uml::RoleType::A)); idb = m_pIDChangesLog->findNewID(pAssoc->widgetIDForRole(Uml::RoleType::B)); //if either is still not found and assoc type is anchor //we are probably linking to a notewidet - else an error if (ida == Uml::ID::None && assocType == Uml::AssociationType::Anchor) ida = log->findNewID(pAssoc->widgetIDForRole(Uml::RoleType::A)); if (idb == Uml::ID::None && assocType == Uml::AssociationType::Anchor) idb = log->findNewID(pAssoc->widgetIDForRole(Uml::RoleType::B)); } else { Uml::ID::Type oldIdA = pAssoc->widgetIDForRole(Uml::RoleType::A); Uml::ID::Type oldIdB = pAssoc->widgetIDForRole(Uml::RoleType::B); ida = log->findNewID(oldIdA); if (ida == Uml::ID::None) { // happens after a cut if (oldIdA == Uml::ID::None) { return false; } ida = oldIdA; } idb = log->findNewID(oldIdB); if (idb == Uml::ID::None) { // happens after a cut if (oldIdB == Uml::ID::None) { return false; } idb = oldIdB; } } if (ida == Uml::ID::None || idb == Uml::ID::None) { return false; } // cant do this anymore.. may cause problem for pasting // pAssoc->setWidgetID(ida, A); // pAssoc->setWidgetID(idb, B); pAssoc->setWidgetForRole(findWidget(ida), Uml::RoleType::A); pAssoc->setWidgetForRole(findWidget(idb), Uml::RoleType::B); } UMLWidget * pWidgetA = findWidget(pAssoc->widgetIDForRole(Uml::RoleType::A)); UMLWidget * pWidgetB = findWidget(pAssoc->widgetIDForRole(Uml::RoleType::B)); //make sure valid widget ids if (!pWidgetA || !pWidgetB) { return false; } //make sure there isn't already the same assoc foreach(AssociationWidget* assocwidget, associationList()) { if (*pAssoc == *assocwidget) // this is nuts. Paste operation wants to know if 'true' // for duplicate, but loadFromXMI1 needs 'false' value return (isPasteOperation ? true : false); } addWidgetCmd(pAssoc); FloatingTextWidget *ft[5] = { pAssoc->nameWidget(), pAssoc->roleWidget(Uml::RoleType::A), pAssoc->roleWidget(Uml::RoleType::B), pAssoc->multiplicityWidget(Uml::RoleType::A), pAssoc->multiplicityWidget(Uml::RoleType::B) }; for (int i = 0; i < 5; i++) { FloatingTextWidget *flotxt = ft[i]; if (flotxt) { flotxt->updateGeometry(); addFloatingTextWidget(flotxt); } } return true; } /** * Activate the view after a load a new file */ void UMLScene::activateAfterLoad(bool bUseLog) { if (m_isActivated) { return; } if (bUseLog) { beginPartialWidgetPaste(); } //now activate them all activate(); if (bUseLog) { endPartialWidgetPaste(); } m_view->centerOn(0, 0); m_isActivated = true; } void UMLScene::beginPartialWidgetPaste() { delete m_pIDChangesLog; m_pIDChangesLog = 0; m_pIDChangesLog = new IDChangeLog(); m_bPaste = true; } void UMLScene::endPartialWidgetPaste() { delete m_pIDChangesLog; m_pIDChangesLog = 0; m_bPaste = false; } /** * Removes a AssociationWidget from a diagram * Physically deletes the AssociationWidget passed in. * * @param pAssoc Pointer to the AssociationWidget. */ void UMLScene::removeWidgetCmd(AssociationWidget* pAssoc) { if (!pAssoc) return; emit sigAssociationRemoved(pAssoc); pAssoc->cleanup(); removeItem(pAssoc); pAssoc->deleteLater(); m_doc->setModified(); } /** * Removes an AssociationWidget from the association list * and removes the corresponding UMLAssociation from the current UMLDoc. */ void UMLScene::removeAssocInViewAndDoc(AssociationWidget* a) { // For umbrello 1.2, UMLAssociations can only be removed in two ways: // 1. Right click on the assocwidget in the view and select Delete // 2. Go to the Class Properties page, select Associations, right click // on the association and select Delete if (!a) return; if (a->associationType() == Uml::AssociationType::Containment) { UMLObject *objToBeMoved = a->widgetForRole(Uml::RoleType::B)->umlObject(); if (objToBeMoved != 0) { UMLListView *lv = UMLApp::app()->listView(); lv->moveObject(objToBeMoved->id(), Model_Utils::convert_OT_LVT(objToBeMoved), lv->theLogicalView()); // UMLListView::moveObject() will delete the containment // AssociationWidget via UMLScene::updateContainment(). } else { DEBUG(DBG_SRC) << "removeAssocInViewAndDoc(containment): " << "objB is NULL"; } } else { // Remove assoc in doc. m_doc->removeAssociation(a->association()); // Remove assoc in view. removeWidgetCmd(a); } } /** * Removes all the associations related to Widget. * * @param widget Pointer to the widget to remove. */ void UMLScene::removeAssociations(UMLWidget* widget) { foreach(AssociationWidget* assocwidget, associationList()) { if (assocwidget->containsAsEndpoint(widget)) { removeWidgetCmd(assocwidget); } } } /** * Sets each association as selected if the widgets it associates are selected * * @param bSelect True to select, false for unselect */ void UMLScene::selectAssociations(bool bSelect) { foreach(AssociationWidget* assocwidget, associationList()) { UMLWidget *widA = assocwidget->widgetForRole(Uml::RoleType::A); UMLWidget *widB = assocwidget->widgetForRole(Uml::RoleType::B); if (bSelect && widA && widA->isSelected() && widB && widB->isSelected()) { assocwidget->setSelected(true); } else { assocwidget->setSelected(false); } } } /** * Fills Associations with all the associations that includes a widget related to object */ void UMLScene::getWidgetAssocs(UMLObject* Obj, AssociationWidgetList & Associations) { if (! Obj) return; foreach(AssociationWidget* assocwidget, associationList()) { if (assocwidget->widgetForRole(Uml::RoleType::A)->umlObject() == Obj || assocwidget->widgetForRole(Uml::RoleType::B)->umlObject() == Obj) Associations.append(assocwidget); } } /** * Removes All the associations of the diagram */ void UMLScene::removeAllAssociations() { //Remove All association widgets foreach(AssociationWidget* assocwidget, associationList()) { removeWidgetCmd(assocwidget); } } /** * Removes All the widgets of the diagram */ void UMLScene::removeAllWidgets() { // Remove widgets. foreach(UMLWidget* temp, widgetList()) { uIgnoreZeroPointer(temp); // I had to take this condition back in, else umbrello // crashes on exit. Still to be analyzed. --okellogg if (!(temp->isTextWidget() && temp->asFloatingTextWidget()->textRole() != TextRole::Floating)) { removeWidgetCmd(temp); } } } /** * Refreshes containment association, i.e. removes possible old * containment and adds new containment association if applicable. * * @param self Pointer to the contained object for which * the association to the containing object is * recomputed. */ void UMLScene::updateContainment(UMLCanvasObject *self) { if (self == 0) return; // See if the object has a widget representation in this view. // While we're at it, also see if the new parent has a widget here. UMLWidget *selfWidget = 0, *newParentWidget = 0; UMLPackage *newParent = self->umlPackage(); foreach(UMLWidget* w, widgetList()) { UMLObject *o = w->umlObject(); if (o == self) selfWidget = w; else if (newParent != 0 && o == newParent) newParentWidget = w; } if (selfWidget == 0) return; // Remove possibly obsoleted containment association. foreach(AssociationWidget* a, associationList()) { if (a->associationType() != Uml::AssociationType::Containment) continue; // Container is at role A, containee at B. // We only look at association for which we are B. UMLWidget *wB = a->widgetForRole(Uml::RoleType::B); UMLObject *roleBObj = wB->umlObject(); if (roleBObj != self) continue; UMLWidget *wA = a->widgetForRole(Uml::RoleType::A); UMLObject *roleAObj = wA->umlObject(); if (roleAObj == newParent) { // Wow, all done. Great! return; } removeWidgetCmd(a); // It's okay to break out because there can only be a single // containing object. break; } if (newParentWidget == 0) return; // Create the new containment association. AssociationWidget *a = AssociationWidget::create (this, newParentWidget, Uml::AssociationType::Containment, selfWidget); addWidgetCmd(a); } /** * Creates automatically any Associations that the given @ref UMLWidget * may have on any diagram. This method is used when you just add the UMLWidget * to a diagram. */ void UMLScene::createAutoAssociations(UMLWidget * widget) { if (widget == 0 || (m_Type != Uml::DiagramType::Class && m_Type != Uml::DiagramType::Object && m_Type != Uml::DiagramType::Component && m_Type != Uml::DiagramType::Deployment && m_Type != Uml::DiagramType::EntityRelationship)) return; // Recipe: // If this widget has an underlying UMLCanvasObject then // for each of the UMLCanvasObject's UMLAssociations // if umlassoc's "other" role has a widget representation on this view then // if the AssocWidget does not already exist then // if the assoc type is permitted in the current diagram type then // create the AssocWidget // end if // end if // end if // end loop // Do createAutoAttributeAssociations() // if this object is capable of containing nested objects then // for each of the object's containedObjects // if the containedObject has a widget representation on this view then // if the containedWidget is not physically located inside this widget // create the containment AssocWidget // end if // end if // end loop // end if // if the UMLCanvasObject has a parentPackage then // if the parentPackage has a widget representation on this view then // create the containment AssocWidget // end if // end if // end if UMLObject *tmpUmlObj = widget->umlObject(); if (tmpUmlObj == 0) return; UMLCanvasObject *umlObj = tmpUmlObj->asUMLCanvasObject(); if (umlObj == 0) return; const UMLAssociationList& umlAssocs = umlObj->getAssociations(); Uml::ID::Type myID = umlObj->id(); foreach(UMLAssociation* assoc, umlAssocs) { UMLCanvasObject *other = 0; UMLObject *roleAObj = assoc->getObject(Uml::RoleType::A); if (roleAObj == 0) { DEBUG(DBG_SRC) << "roleA object is NULL at UMLAssoc " << Uml::ID::toString(assoc->id()); continue; } UMLObject *roleBObj = assoc->getObject(Uml::RoleType::B); if (roleBObj == 0) { DEBUG(DBG_SRC) << "roleB object is NULL at UMLAssoc " << Uml::ID::toString(assoc->id()); continue; } if (roleAObj->id() == myID) { other = roleBObj->asUMLCanvasObject(); } else if (roleBObj->id() == myID) { other = roleAObj->asUMLCanvasObject(); } else { DEBUG(DBG_SRC) << "Cannot find own object " << Uml::ID::toString(myID) << " in UMLAssoc " << Uml::ID::toString(assoc->id()); continue; } // Now that we have determined the "other" UMLObject, seek it in // this view's UMLWidgets. if (!other) { continue; } Uml::ID::Type otherID = other->id(); bool breakFlag = false; UMLWidget* pOtherWidget = 0; foreach(pOtherWidget, widgetList()) { if (pOtherWidget->id() == otherID) { breakFlag = true; break; } } if (!breakFlag) continue; // Both objects are represented in this view: // Assign widget roles as indicated by the UMLAssociation. UMLWidget *widgetA, *widgetB; if (myID == roleAObj->id()) { widgetA = widget; widgetB = pOtherWidget; } else { widgetA = pOtherWidget; widgetB = widget; } // Check that the assocwidget does not already exist. Uml::AssociationType::Enum assocType = assoc->getAssocType(); AssociationWidget * assocwidget = findAssocWidget(assocType, widgetA, widgetB); if (assocwidget) { assocwidget->calculateEndingPoints(); // recompute assoc lines continue; } // Check that the assoc is allowed. if (!AssocRules::allowAssociation(assocType, widgetA, widgetB)) { DEBUG(DBG_SRC) << "not transferring assoc " << "of type " << assocType; continue; } // Create the AssociationWidget. assocwidget = AssociationWidget::create(this); assocwidget->setWidgetForRole(widgetA, Uml::RoleType::A); assocwidget->setWidgetForRole(widgetB, Uml::RoleType::B); assocwidget->setAssociationType(assocType); assocwidget->setUMLObject(assoc); // Call calculateEndingPoints() before setting the FloatingTexts // because their positions are computed according to the // assocwidget line positions. assocwidget->calculateEndingPoints(); assocwidget->syncToModel(); assocwidget->setActivated(true); if (! addAssociation(assocwidget)) delete assocwidget; } createAutoAttributeAssociations(widget); if (m_Type == Uml::DiagramType::EntityRelationship) { createAutoConstraintAssociations(widget); } // if this object is capable of containing nested objects then UMLObject::ObjectType t = umlObj->baseType(); if (t == UMLObject::ot_Package || t == UMLObject::ot_Class || t == UMLObject::ot_Interface || t == UMLObject::ot_Component) { // for each of the object's containedObjects UMLPackage *umlPkg = umlObj->asUMLPackage(); UMLObjectList lst = umlPkg->containedObjects(); foreach(UMLObject* obj, lst) { uIgnoreZeroPointer(obj); // if the containedObject has a widget representation on this view then Uml::ID::Type id = obj->id(); foreach(UMLWidget *w, widgetList()) { uIgnoreZeroPointer(w); if (w->id() != id) continue; // if the containedWidget is not physically located inside this widget if (widget->rect().contains(w->rect())) continue; // create the containment AssocWidget AssociationWidget *a = AssociationWidget::create(this, widget, Uml::AssociationType::Containment, w); a->calculateEndingPoints(); a->setActivated(true); if (! addAssociation(a)) delete a; } } } // if the UMLCanvasObject has a parentPackage then UMLPackage *parent = umlObj->umlPackage(); if (parent == 0) return; // if the parentPackage has a widget representation on this view then Uml::ID::Type pkgID = parent->id(); bool breakFlag = false; UMLWidget* pWidget = 0; foreach(pWidget, widgetList()) { uIgnoreZeroPointer(pWidget); if (pWidget->id() == pkgID) { breakFlag = true; break; } } if (!breakFlag || pWidget->rect().contains(widget->rect())) return; // create the containment AssocWidget AssociationWidget *a = AssociationWidget::create(this, pWidget, Uml::AssociationType::Containment, widget); if (! addAssociation(a)) delete a; } /** * If the m_Type of the given widget is WidgetBase::wt_Class then * iterate through the class' attributes and create an * association to each attribute type widget that is present * on the current diagram. */ void UMLScene::createAutoAttributeAssociations(UMLWidget *widget) { if (widget == 0 || m_Type != Uml::DiagramType::Class || !m_Options.classState.showAttribAssocs) return; // Pseudocode: // if the underlying model object is really a UMLClassifier then // for each of the UMLClassifier's UMLAttributes // if the attribute type has a widget representation on this view then // if the AssocWidget does not already exist then // if the current diagram type permits compositions then // create a composition AssocWidget // end if // end if // end if // if the attribute type is a Datatype then // if the Datatype is a reference (pointer) type then // if the referenced type has a widget representation on this view then // if the AssocWidget does not already exist then // if the current diagram type permits aggregations then // create an aggregation AssocWidget from the ClassifierWidget to the // widget of the referenced type // end if // end if // end if // end if // end if // end loop // end if // // Implementation: UMLObject *tmpUmlObj = widget->umlObject(); if (tmpUmlObj == 0) return; // if the underlying model object is really a UMLClassifier then if (tmpUmlObj->isUMLDatatype()) { UMLDatatype *dt = tmpUmlObj->asUMLDatatype(); while (dt && dt->originType() != 0) { tmpUmlObj = dt->originType(); if (!tmpUmlObj->isUMLDatatype()) break; dt = tmpUmlObj->asUMLDatatype(); } } if (tmpUmlObj->baseType() != UMLObject::ot_Class) return; UMLClassifier * klass = tmpUmlObj->asUMLClassifier(); // for each of the UMLClassifier's UMLAttributes UMLAttributeList attrList = klass->getAttributeList(); foreach(UMLAttribute* attr, attrList) { createAutoAttributeAssociation(attr->getType(), attr, widget); /* * The following code from attachment 19935 of http://bugs.kde.org/140669 * creates Aggregation/Composition to the template parameters. * The current solution uses Dependency instead, see handling of template * instantiation at Import_Utils::createUMLObject(). UMLClassifierList templateList = attr->getTemplateParams(); for (UMLClassifierListIt it(templateList); it.current(); ++it) { createAutoAttributeAssociation(it, attr, widget); } */ } } /** * Create an association with the attribute attr associated with the UMLWidget * widget if the UMLClassifier type is present on the current diagram. */ void UMLScene::createAutoAttributeAssociation(UMLClassifier *type, UMLAttribute *attr, UMLWidget *widget /*, UMLClassifier * klass*/) { if (type == 0) { // DEBUG(DBG_SRC) << klass->getName() << ": type is NULL for " // << "attribute " << attr->getName(); return; } Uml::AssociationType::Enum assocType = Uml::AssociationType::Composition; UMLWidget *w = findWidget(type->id()); // if the attribute type has a widget representation on this view if (w) { AssociationWidget *a = findAssocWidget(widget, w, attr->name()); if (a == 0 && // if the current diagram type permits compositions AssocRules::allowAssociation(assocType, widget, w)) { // Create a composition AssocWidget, or, if the attribute type is // stereotyped <>, create a UniAssociation widget. if (type->stereotype() == QLatin1String("CORBAInterface")) assocType = Uml::AssociationType::UniAssociation; a = AssociationWidget::create(this, widget, assocType, w, attr); a->setVisibility(attr->visibility(), Uml::RoleType::B); /* if (assocType == Uml::AssociationType::Aggregation || assocType == Uml::AssociationType::UniAssociation) a->setMulti("0..1", Uml::RoleType::B); */ a->setRoleName(attr->name(), Uml::RoleType::B); a->setActivated(true); if (! addAssociation(a)) delete a; } } // if the attribute type is a Datatype then if (type->isUMLDatatype()) { UMLDatatype *dt = type->asUMLDatatype(); // if the Datatype is a reference (pointer) type if (dt && dt->isReference()) { //Uml::AssociationType::Enum assocType = Uml::AssociationType::Composition; UMLClassifier *c = dt->originType(); UMLWidget *w = c ? findWidget(c->id()) : 0; // if the referenced type has a widget representation on this view if (w) { AssociationWidget *a = findAssocWidget(widget, w, attr->name()); if (a == 0 && // if the current diagram type permits aggregations AssocRules::allowAssociation(Uml::AssociationType::Aggregation, widget, w)) { // create an aggregation AssocWidget from the ClassifierWidget // to the widget of the referenced type a = AssociationWidget::create (this, widget, Uml::AssociationType::Aggregation, w, attr); a->setVisibility(attr->visibility(), Uml::RoleType::B); //a->setChangeability(true, Uml::RoleType::B); a->setMultiplicity(QLatin1String("0..1"), Uml::RoleType::B); a->setRoleName(attr->name(), Uml::RoleType::B); a->setActivated(true); if (! addAssociation(a)) delete a; } } } } } void UMLScene::createAutoConstraintAssociations(UMLWidget *widget) { if (widget == 0 || m_Type != Uml::DiagramType::EntityRelationship) return; // Pseudocode: // if the underlying model object is really a UMLEntity then // for each of the UMLEntity's UMLForeignKeyConstraint's // if the attribute type has a widget representation on this view then // if the AssocWidget does not already exist then // if the current diagram type permits relationships then // create a relationship AssocWidget // end if // end if // end if UMLObject *tmpUmlObj = widget->umlObject(); if (tmpUmlObj == 0) return; // check if the underlying model object is really a UMLEntity UMLCanvasObject *umlObj = tmpUmlObj->asUMLCanvasObject(); if (umlObj == 0) return; // finished checking whether this widget has a UMLCanvas Object if (tmpUmlObj->baseType() != UMLObject::ot_Entity) return; UMLEntity *entity = tmpUmlObj->asUMLEntity(); // for each of the UMLEntity's UMLForeignKeyConstraints UMLClassifierListItemList constrList = entity->getFilteredList(UMLObject::ot_ForeignKeyConstraint); foreach(UMLClassifierListItem* cli, constrList) { UMLEntityConstraint *eConstr = cli->asUMLEntityConstraint(); UMLForeignKeyConstraint* fkc = eConstr->asUMLForeignKeyConstraint(); if (fkc == 0) { return; } UMLEntity* refEntity = fkc->getReferencedEntity(); if (refEntity == 0) { return; } createAutoConstraintAssociation(refEntity, fkc, widget); } } void UMLScene::createAutoConstraintAssociation(UMLEntity* refEntity, UMLForeignKeyConstraint* fkConstraint, UMLWidget* widget) { if (refEntity == 0) { return; } Uml::AssociationType::Enum assocType = Uml::AssociationType::Relationship; UMLWidget *w = findWidget(refEntity->id()); AssociationWidget *aw = 0; if (w) { aw = findAssocWidget(w, widget, fkConstraint->name()); if (aw == 0 && // if the current diagram type permits relationships AssocRules::allowAssociation(assocType, w, widget)) { // for foreign key contstraint, we need to create the association type Uml::AssociationType::Relationship. // The referenced entity is the "1" part (Role A) and the entity holding the relationship is the "many" part. (Role B) AssociationWidget *a = AssociationWidget::create(this, w, assocType, widget); a->setUMLObject(fkConstraint); //a->setVisibility(attr->getVisibility(), Uml::RoleType::B); a->setRoleName(fkConstraint->name(), Uml::RoleType::B); a->setActivated(true); if (! addAssociation(a)) delete a; } } } void UMLScene::createAutoAttributeAssociations2(UMLWidget *widget) { foreach(UMLWidget* w, widgetList()) { uIgnoreZeroPointer(w); if (w != widget) { createAutoAttributeAssociations(w); if (widget->umlObject() && widget->umlObject()->baseType() == UMLObject::ot_Entity) createAutoConstraintAssociations(w); } } } /** * Find the maximum bounding rectangle of FloatingTextWidget widgets. * Auxiliary to copyAsImage(). * * @param ft Pointer to the FloatingTextWidget widget to consider. * @param px X coordinate of lower left corner. This value will be * updated if the X coordinate of the lower left corner * of ft is smaller than the px value passed in. * @param py Y coordinate of lower left corner. This value will be * updated if the Y coordinate of the lower left corner * of ft is smaller than the py value passed in. * @param qx X coordinate of upper right corner. This value will be * updated if the X coordinate of the upper right corner * of ft is larger than the qx value passed in. * @param qy Y coordinate of upper right corner. This value will be * updated if the Y coordinate of the upper right corner * of ft is larger than the qy value passed in. */ void UMLScene::findMaxBoundingRectangle(const FloatingTextWidget* ft, qreal& px, qreal& py, qreal& qx, qreal& qy) { if (ft == 0 || !ft->isVisible()) return; qreal x = ft->x(); qreal y = ft->y(); qreal x1 = x + ft->width() - 1; qreal y1 = y + ft->height() - 1; if (px == -1 || x < px) px = x; if (py == -1 || y < py) py = y; if (qx == -1 || x1 > qx) qx = x1; if (qy == -1 || y1 > qy) qy = y1; } /** * Returns the PNG picture of the paste operation. */ void UMLScene::copyAsImage(QPixmap*& pix) { //get the smallest rect holding the diagram QRectF rect = diagramRect(); QPixmap diagram(rect.width(), rect.height()); //only draw what is selected m_bDrawSelectedOnly = true; selectAssociations(true); getDiagram(diagram, rect); //now get the selection cut qreal px = -1, py = -1, qx = -1, qy = -1; //first get the smallest rect holding the widgets foreach(UMLWidget* temp, selectedWidgets()) { qreal x = temp->x(); qreal y = temp->y(); qreal x1 = x + temp->width() - 1; qreal y1 = y + temp->height() - 1; if (px == -1 || x < px) { px = x; } if (py == -1 || y < py) { py = y; } if (qx == -1 || x1 > qx) { qx = x1; } if (qy == -1 || y1 > qy) { qy = y1; } } //also take into account any text lines in assocs or messages //get each type of associations //This needs to be reimplemented to increase the rectangle //if a part of any association is not included foreach(AssociationWidget *a, associationList()) { if (! a->isSelected()) continue; const FloatingTextWidget* multiA = a->multiplicityWidget(Uml::RoleType::A); const FloatingTextWidget* multiB = a->multiplicityWidget(Uml::RoleType::B); const FloatingTextWidget* roleA = a->roleWidget(Uml::RoleType::A); const FloatingTextWidget* roleB = a->roleWidget(Uml::RoleType::B); const FloatingTextWidget* changeA = a->changeabilityWidget(Uml::RoleType::A); const FloatingTextWidget* changeB = a->changeabilityWidget(Uml::RoleType::B); findMaxBoundingRectangle(multiA, px, py, qx, qy); findMaxBoundingRectangle(multiB, px, py, qx, qy); findMaxBoundingRectangle(roleA, px, py, qx, qy); findMaxBoundingRectangle(roleB, px, py, qx, qy); findMaxBoundingRectangle(changeA, px, py, qx, qy); findMaxBoundingRectangle(changeB, px, py, qx, qy); }//end foreach QRectF imageRect; //area with respect to diagramRect() //i.e. all widgets on the scene. Was previously with //respect to whole scene imageRect.setLeft(px - rect.left()); imageRect.setTop(py - rect.top()); imageRect.setRight(qx - rect.left()); imageRect.setBottom(qy - rect.top()); pix = new QPixmap(imageRect.width(), imageRect.height()); QPainter output(pix); output.drawPixmap(QPoint(0, 0), diagram, imageRect); m_bDrawSelectedOnly = false; } /** * Reset the toolbar. */ void UMLScene::resetToolbar() { emit sigResetToolBar(); } /** * Event handler for context menu events. */ void UMLScene::contextMenuEvent(QGraphicsSceneContextMenuEvent* event) { QGraphicsScene::contextMenuEvent(event); if (!event->isAccepted()) { setPos(event->scenePos()); UMLScenePopupMenu popup(m_view, this); QAction *triggered = popup.exec(event->screenPos()); slotMenuSelection(triggered); event->accept(); } } /** * Returns the status on whether in a paste state. * * @return Returns the status on whether in a paste state. */ bool UMLScene::getPaste() const { return m_bPaste; } /** * Sets the status on whether in a paste state. */ void UMLScene::setPaste(bool paste) { m_bPaste = paste; } /** * When a menu selection has been made on the menu * that this view created, this method gets called. */ void UMLScene::slotMenuSelection(QAction* action) { ListPopupMenu::MenuType sel = ListPopupMenu::typeFromAction(action); switch (sel) { case ListPopupMenu::mt_Undo: UMLApp::app()->undo(); break; case ListPopupMenu::mt_Redo: UMLApp::app()->redo(); break; case ListPopupMenu::mt_Clear: clearDiagram(); break; case ListPopupMenu::mt_Export_Image: m_pImageExporter->exportView(); break; case ListPopupMenu::mt_Apply_Layout: case ListPopupMenu::mt_Apply_Layout1: case ListPopupMenu::mt_Apply_Layout2: case ListPopupMenu::mt_Apply_Layout3: case ListPopupMenu::mt_Apply_Layout4: case ListPopupMenu::mt_Apply_Layout5: case ListPopupMenu::mt_Apply_Layout6: case ListPopupMenu::mt_Apply_Layout7: case ListPopupMenu::mt_Apply_Layout8: case ListPopupMenu::mt_Apply_Layout9: { QVariant value = ListPopupMenu::dataFromAction(ListPopupMenu::dt_ApplyLayout, action); applyLayout(value.toString()); } break; case ListPopupMenu::mt_FloatText: { FloatingTextWidget* ft = new FloatingTextWidget(this); ft->showChangeTextDialog(); //if no text entered delete if (!FloatingTextWidget::isTextValid(ft->text())) { delete ft; } else { ft->setID(UniqueID::gen()); setupNewWidget(ft); } } break; case ListPopupMenu::mt_UseCase: m_bCreateObject = true; Object_Factory::createUMLObject(UMLObject::ot_UseCase); break; case ListPopupMenu::mt_Actor: m_bCreateObject = true; Object_Factory::createUMLObject(UMLObject::ot_Actor); break; case ListPopupMenu::mt_Class: case ListPopupMenu::mt_Object: m_bCreateObject = true; Object_Factory::createUMLObject(UMLObject::ot_Class); break; case ListPopupMenu::mt_Package: m_bCreateObject = true; Object_Factory::createUMLObject(UMLObject::ot_Package); break; case ListPopupMenu::mt_Subsystem: { m_bCreateObject = true; UMLPackage *component = UMLApp::app()->document()->rootFolder(Uml::ModelType::Component); QString name = Model_Utils::uniqObjectName(UMLObject::ot_Package, component, i18n("new_subsystem")); if (Dialog_Utils::askName(i18n("Enter Subsystem Name"), i18n("Enter the name of the new subsystem"), name)) { UMLObject *package = Object_Factory::createUMLObject(UMLObject::ot_Package, name, component); if (package) package->setStereotypeCmd(QLatin1String("subsystem")); } break; } case ListPopupMenu::mt_Component: m_bCreateObject = true; Object_Factory::createUMLObject(UMLObject::ot_Component); break; case ListPopupMenu::mt_Node: m_bCreateObject = true; Object_Factory::createUMLObject(UMLObject::ot_Node); break; case ListPopupMenu::mt_Artifact: m_bCreateObject = true; Object_Factory::createUMLObject(UMLObject::ot_Artifact); break; case ListPopupMenu::mt_Interface: m_bCreateObject = true; Object_Factory::createUMLObject(UMLObject::ot_Interface); break; case ListPopupMenu::mt_Enum: m_bCreateObject = true; Object_Factory::createUMLObject(UMLObject::ot_Enum); break; case ListPopupMenu::mt_Entity: m_bCreateObject = true; Object_Factory::createUMLObject(UMLObject::ot_Entity); break; case ListPopupMenu::mt_Category: m_bCreateObject = true; Object_Factory::createUMLObject(UMLObject::ot_Category); break; case ListPopupMenu::mt_Datatype: m_bCreateObject = true; Object_Factory::createUMLObject(UMLObject::ot_Datatype); break; case ListPopupMenu::mt_Instance: m_bCreateObject = true; Object_Factory::createUMLObject(UMLObject::ot_Instance); break; case ListPopupMenu::mt_Note: { m_bCreateObject = true; UMLWidget* widget = new NoteWidget(this); addItem(widget); widget->setPos(pos()); widget->setSize(100, 40); widget->showPropertiesDialog(); QSizeF size = widget->minimumSize(); widget->setSize(size); break; } case ListPopupMenu::mt_Cut: //FIXME make this work for diagram's right click menu if (selectedWidgets().count() && UMLApp::app()->editCutCopy(true)) { deleteSelection(); m_doc->setModified(true); } break; case ListPopupMenu::mt_Copy: //FIXME make this work for diagram's right click menu selectedWidgets().count() && UMLApp::app()->editCutCopy(true); break; case ListPopupMenu::mt_Paste: m_PastePoint = m_Pos; m_Pos.setX(2000); m_Pos.setY(2000); UMLApp::app()->slotEditPaste(); m_PastePoint.setX(0); m_PastePoint.setY(0); break; case ListPopupMenu::mt_Initial_State: { StateWidget* state = new StateWidget(this, StateWidget::Initial); setupNewWidget(state); } break; case ListPopupMenu::mt_End_State: { StateWidget* state = new StateWidget(this, StateWidget::End); setupNewWidget(state); } break; case ListPopupMenu::mt_Junction: { StateWidget* state = new StateWidget(this, StateWidget::Junction); setupNewWidget(state); } break; case ListPopupMenu::mt_DeepHistory: { StateWidget* state = new StateWidget(this, StateWidget::DeepHistory); setupNewWidget(state); } break; case ListPopupMenu::mt_ShallowHistory: { StateWidget* state = new StateWidget(this, StateWidget::ShallowHistory); setupNewWidget(state); } break; case ListPopupMenu::mt_Choice: { StateWidget* state = new StateWidget(this, StateWidget::Choice); setupNewWidget(state); } break; case ListPopupMenu::mt_StateFork: { StateWidget* state = new StateWidget(this, StateWidget::Fork); setupNewWidget(state); } break; case ListPopupMenu::mt_StateJoin: { StateWidget* state = new StateWidget(this, StateWidget::Join); setupNewWidget(state); } break; case ListPopupMenu::mt_State: { QString name = i18n("new state"); bool ok = Dialog_Utils::askName(i18n("Enter State Name"), i18n("Enter the name of the new state:"), name); if (ok) { StateWidget* state = new StateWidget(this); state->setName(name); setupNewWidget(state); } } break; case ListPopupMenu::mt_Initial_Activity: { ActivityWidget* activity = new ActivityWidget(this, ActivityWidget::Initial); setupNewWidget(activity); } break; case ListPopupMenu::mt_End_Activity: { ActivityWidget* activity = new ActivityWidget(this, ActivityWidget::End); setupNewWidget(activity); } break; case ListPopupMenu::mt_Branch: { ActivityWidget* activity = new ActivityWidget(this, ActivityWidget::Branch); setupNewWidget(activity); } break; case ListPopupMenu::mt_Activity: { QString name = i18n("new activity"); bool ok = Dialog_Utils::askName(i18n("Enter Activity Name"), i18n("Enter the name of the new activity:"), name); if (ok) { ActivityWidget* activity = new ActivityWidget(this, ActivityWidget::Normal); activity->setName(name); setupNewWidget(activity); } } break; case ListPopupMenu::mt_SnapToGrid: toggleSnapToGrid(); m_doc->setModified(); break; case ListPopupMenu::mt_SnapComponentSizeToGrid: toggleSnapComponentSizeToGrid(); m_doc->setModified(); break; case ListPopupMenu::mt_ShowSnapGrid: toggleShowGrid(); m_doc->setModified(); break; case ListPopupMenu::mt_ShowDocumentationIndicator: setShowDocumentationIndicator(!isShowDocumentationIndicator()); update(); break; case ListPopupMenu::mt_Properties: if (m_view->showPropertiesDialog() == true) m_doc->setModified(); break; case ListPopupMenu::mt_Delete: m_doc->removeDiagram(ID()); break; case ListPopupMenu::mt_Rename: { QString newName = name(); bool ok = Dialog_Utils::askName(i18n("Enter Diagram Name"), i18n("Enter the new name of the diagram:"), newName); if (ok) { setName(newName); m_doc->signalDiagramRenamed(activeView()); } } break; case ListPopupMenu::mt_Import_from_File: { QPointer dialog = new UMLFileDialog(QUrl(), QString(), UMLApp::app()); dialog->exec(); QUrl url = dialog->selectedUrl(); if (!url.isEmpty()) if (!Diagram_Utils::importGraph(url.toLocalFile(), this)) UMLApp::app()->slotStatusMsg(i18n("Failed to import from file.")); break; } default: uWarning() << "unknown ListPopupMenu::MenuType " << ListPopupMenu::toString(sel); break; } } /** * Connects to the signal that @ref UMLApp emits when a cut operation * is successful. * If the view or a child started the operation the flag m_bStartedCut will * be set and we can carry out any operation that is needed, like deleting the selected * widgets for the cut operation. */ void UMLScene::slotCutSuccessful() { if (m_bStartedCut) { deleteSelection(); m_bStartedCut = false; } } /** * Called by menu when to show the instance of the view. */ void UMLScene::slotShowView() { m_doc->changeCurrentView(ID()); } /** * Returns the offset point at which to place the paste from clipboard. * Just add the amount to your co-ords. * Only call this straight after the event, the value won't stay valid. * Should only be called by Assoc widgets at the moment. no one else needs it. */ QPointF UMLScene::getPastePoint() { QPointF point = m_PastePoint; point.setX(point.x() - m_Pos.x()); point.setY(point.y() - m_Pos.y()); return point; } /** * Reset the paste point. */ void UMLScene::resetPastePoint() { m_PastePoint = m_Pos; } /** * Called by the view or any of its children when they start a cut * operation. */ void UMLScene::setStartedCut() { m_bStartedCut = true; } /** * Returns the font to use */ QFont UMLScene::font() const { return m_Options.uiState.font; } /** * Sets the font for the view and optionally all the widgets on the view. */ void UMLScene::setFont(QFont font, bool changeAllWidgets /* = false */) { m_Options.uiState.font = font; if (!changeAllWidgets) return; foreach(UMLWidget* w, widgetList()) { uIgnoreZeroPointer(w); w->setFont(font); } } /** * Sets some options for all the @ref ClassifierWidget on the view. */ void UMLScene::setClassWidgetOptions(ClassOptionsPage * page) { foreach(UMLWidget* pWidget, widgetList()) { uIgnoreZeroPointer(pWidget); WidgetBase::WidgetType wt = pWidget->baseType(); if (wt == WidgetBase::wt_Class || wt == WidgetBase::wt_Interface) { page->setWidget(static_cast(pWidget)); page->apply(); } } } /** * Returns the type of the selected widget or widgets. * * If multiple widgets of different types are selected. WidgetType::UMLWidget * is returned. */ WidgetBase::WidgetType UMLScene::getUniqueSelectionType() { if (selectedWidgets().isEmpty()) { return WidgetBase::wt_UMLWidget; } // Get the first item and its base type UMLWidget * pTemp = (UMLWidget *) selectedWidgets().first(); WidgetBase::WidgetType tmpType = pTemp->baseType(); // Check all selected items, if they have the same BaseType foreach(pTemp, selectedWidgets()) { if (pTemp->baseType() != tmpType) { return WidgetBase::wt_UMLWidget; } } return tmpType; } /** * Asks for confirmation and clears everything on the diagram. * Called from menus. */ void UMLScene::clearDiagram() { if (Dialog_Utils::askDeleteDiagram()) { removeAllWidgets(); } } /** * Apply an automatic layout. */ void UMLScene::applyLayout(const QString &variant) { DEBUG(DBG_SRC) << "layout = " << variant; LayoutGenerator r; r.generate(this, variant); r.apply(this); UMLApp::app()->slotZoomFit(); } /** * Changes snap to grid boolean. * Called from menus. */ void UMLScene::toggleSnapToGrid() { setSnapToGrid(!snapToGrid()); } /** * Changes snap to grid for component size boolean. * Called from menus. */ void UMLScene::toggleSnapComponentSizeToGrid() { setSnapComponentSizeToGrid(!snapComponentSizeToGrid()); } /** * Changes show grid boolean. * Called from menus. */ void UMLScene::toggleShowGrid() { setSnapGridVisible(!isSnapGridVisible()); } /** * Return whether to use snap to grid. */ bool UMLScene::snapToGrid() const { return m_bUseSnapToGrid; } /** * Sets whether to snap to grid. */ void UMLScene::setSnapToGrid(bool bSnap) { m_bUseSnapToGrid = bSnap; emit sigSnapToGridToggled(snapToGrid()); } /** * Return whether to use snap to grid for component size. */ bool UMLScene::snapComponentSizeToGrid() const { return m_bUseSnapComponentSizeToGrid; } /** * Sets whether to snap to grid for component size. */ void UMLScene::setSnapComponentSizeToGrid(bool bSnap) { m_bUseSnapComponentSizeToGrid = bSnap; updateComponentSizes(); emit sigSnapComponentSizeToGridToggled(snapComponentSizeToGrid()); } /** * Returns the x grid size. */ int UMLScene::snapX() const { return m_layoutGrid->gridSpacingX(); } /** * Returns the y grid size. */ int UMLScene::snapY() const { return m_layoutGrid->gridSpacingY(); } /** * Sets the grid size in x and y. */ void UMLScene::setSnapSpacing(int x, int y) { m_layoutGrid->setGridSpacing(x, y); } /** * Returns the input coordinate with possible grid-snap applied. */ qreal UMLScene::snappedX(qreal _x) { if (snapToGrid()) { int x = (int)_x; int gridX = snapX(); int modX = x % gridX; x -= modX; if (modX >= gridX / 2) x += gridX; return x; } else return _x; } /** * Returns the input coordinate with possible grid-snap applied. */ qreal UMLScene::snappedY(qreal _y) { if (snapToGrid()) { int y = (int)_y; int gridY = snapY(); int modY = y % gridY; y -= modY; if (modY >= gridY / 2) y += gridY; return y; } else return _y; } /** * Returns whether to show snap grid or not. */ bool UMLScene::isSnapGridVisible() const { return m_layoutGrid->isVisible(); } /** * Sets whether to show snap grid. */ void UMLScene::setSnapGridVisible(bool bShow) { m_layoutGrid->setVisible(bShow); emit sigShowGridToggled(bShow); } /** * Returns whether to show documentation indicator. */ bool UMLScene::isShowDocumentationIndicator() const { return m_showDocumentationIndicator; } /** * sets whether to show documentation indicator. */ void UMLScene::setShowDocumentationIndicator(bool bShow) { m_showDocumentationIndicator = bShow; } /** * Returns whether to show operation signatures. */ bool UMLScene::showOpSig() const { return m_Options.classState.showOpSig; } /** * Sets whether to show operation signatures. */ void UMLScene::setShowOpSig(bool bShowOpSig) { m_Options.classState.showOpSig = bShowOpSig; } /** * Changes the zoom to the currently set level (now loaded from file) * Called from UMLApp::slotUpdateViews() */ void UMLScene::fileLoaded() { m_view->setZoom(m_view->zoom()); resizeSceneToItems(); } /** * Sets the size of the scene to just fit on all the items */ void UMLScene::resizeSceneToItems() { // let QGraphicsScene handle scene size by itself setSceneRect(QRectF()); } /** * Updates the size of all components in this view. */ void UMLScene::updateComponentSizes() { // update sizes of all components foreach(UMLWidget *obj, widgetList()) { uIgnoreZeroPointer(obj); obj->updateGeometry(); } } /** * Force the widget font metrics to be updated next time * the widgets are drawn. * This is necessary because the widget size might depend on the * font metrics and the font metrics might change for different * QPainter, i.e. font metrics for Display font and Printer font are * usually different. * Call this when you change the QPainter. */ void UMLScene::forceUpdateWidgetFontMetrics(QPainter * painter) { foreach(UMLWidget *obj, widgetList()) { uIgnoreZeroPointer(obj); obj->forceUpdateFontMetrics(painter); } } /** * Overrides standard method from QGraphicsScene drawing the background. */ void UMLScene::drawBackground(QPainter *painter, const QRectF &rect) { QGraphicsScene::drawBackground(painter, rect); m_layoutGrid->paint(painter, rect); } /** * Creates the "diagram" tag and fills it with the contents of the diagram. */ void UMLScene::saveToXMI1(QDomDocument & qDoc, QDomElement & qElement) { resizeSceneToItems(); QDomElement viewElement = qDoc.createElement(QLatin1String("diagram")); viewElement.setAttribute(QLatin1String("xmi.id"), Uml::ID::toString(m_nID)); viewElement.setAttribute(QLatin1String("name"), name()); viewElement.setAttribute(QLatin1String("type"), m_Type); viewElement.setAttribute(QLatin1String("documentation"), m_Documentation); //option state m_Options.saveToXMI1(viewElement); //misc viewElement.setAttribute(QLatin1String("localid"), Uml::ID::toString(m_nLocalID)); viewElement.setAttribute(QLatin1String("showgrid"), m_layoutGrid->isVisible()); viewElement.setAttribute(QLatin1String("snapgrid"), m_bUseSnapToGrid); viewElement.setAttribute(QLatin1String("snapcsgrid"), m_bUseSnapComponentSizeToGrid); viewElement.setAttribute(QLatin1String("snapx"), m_layoutGrid->gridSpacingX()); viewElement.setAttribute(QLatin1String("snapy"), m_layoutGrid->gridSpacingY()); // FIXME: move to UMLView viewElement.setAttribute(QLatin1String("zoom"), activeView()->zoom()); viewElement.setAttribute(QLatin1String("canvasheight"), QString::number(height())); viewElement.setAttribute(QLatin1String("canvaswidth"), QString::number(width())); viewElement.setAttribute(QLatin1String("isopen"), isOpen()); if (type() == Uml::DiagramType::Sequence || type() == Uml::DiagramType::Collaboration) viewElement.setAttribute(QLatin1String("autoincrementsequence"), autoIncrementSequence()); //now save all the widgets QDomElement widgetElement = qDoc.createElement(QLatin1String("widgets")); foreach(UMLWidget *widget, widgetList()) { uIgnoreZeroPointer(widget); // Having an exception is bad I know, but gotta work with // system we are given. // We DON'T want to record any text widgets which are belonging // to associations as they are recorded later in the "associations" // section when each owning association is dumped. -b.t. if ((!widget->isTextWidget() && !widget->isFloatingDashLineWidget()) || (widget->asFloatingTextWidget() && widget->asFloatingTextWidget()->link() == 0)) widget->saveToXMI1(qDoc, widgetElement); } viewElement.appendChild(widgetElement); //now save the message widgets QDomElement messageElement = qDoc.createElement(QLatin1String("messages")); foreach(UMLWidget* widget, messageList()) { widget->saveToXMI1(qDoc, messageElement); } viewElement.appendChild(messageElement); //now save the associations QDomElement assocElement = qDoc.createElement(QLatin1String("associations")); if (associationList().count()) { // We guard against (associationList().count() == 0) because // this code could be reached as follows: // ^ UMLScene::saveToXMI1() // ^ UMLDoc::saveToXMI1() // ^ UMLDoc::addToUndoStack() // ^ UMLDoc::setModified() // ^ UMLDoc::createDiagram() // ^ UMLDoc::newDocument() // ^ UMLApp::newDocument() // ^ main() // AssociationWidget * assoc = 0; foreach(assoc, associationList()) { assoc->saveToXMI1(qDoc, assocElement); } } viewElement.appendChild(assocElement); qElement.appendChild(viewElement); } /** * Loads the "diagram" tag. */ bool UMLScene::loadFromXMI1(QDomElement & qElement) { QString id = qElement.attribute(QLatin1String("xmi.id"), QLatin1String("-1")); m_nID = Uml::ID::fromString(id); if (m_nID == Uml::ID::None) return false; setName(qElement.attribute(QLatin1String("name"))); QString type = qElement.attribute(QLatin1String("type"), QLatin1String("0")); m_Documentation = qElement.attribute(QLatin1String("documentation")); QString localid = qElement.attribute(QLatin1String("localid"), QLatin1String("0")); // option state m_Options.loadFromXMI1(qElement); setBackgroundBrush(m_Options.uiState.backgroundColor); setGridDotColor(m_Options.uiState.gridDotColor); //misc QString showgrid = qElement.attribute(QLatin1String("showgrid"), QLatin1String("0")); m_layoutGrid->setVisible((bool)showgrid.toInt()); QString snapgrid = qElement.attribute(QLatin1String("snapgrid"), QLatin1String("0")); m_bUseSnapToGrid = (bool)snapgrid.toInt(); QString snapcsgrid = qElement.attribute(QLatin1String("snapcsgrid"), QLatin1String("0")); m_bUseSnapComponentSizeToGrid = (bool)snapcsgrid.toInt(); QString snapx = qElement.attribute(QLatin1String("snapx"), QLatin1String("10")); QString snapy = qElement.attribute(QLatin1String("snapy"), QLatin1String("10")); m_layoutGrid->setGridSpacing(snapx.toInt(), snapy.toInt()); QString zoom = qElement.attribute(QLatin1String("zoom"), QLatin1String("100")); activeView()->setZoom(zoom.toInt()); resizeSceneToItems(); QString isOpen = qElement.attribute(QLatin1String("isopen"), QLatin1String("1")); m_isOpen = (bool)isOpen.toInt(); int nType = type.toInt(); if (nType == -1 || nType >= 400) { // Pre 1.5.5 numeric values // Values of "type" were changed in 1.5.5 to merge with Settings::Diagram switch (nType) { case 400: m_Type = Uml::DiagramType::UseCase; break; case 401: m_Type = Uml::DiagramType::Collaboration; break; case 402: m_Type = Uml::DiagramType::Class; break; case 403: m_Type = Uml::DiagramType::Sequence; break; case 404: m_Type = Uml::DiagramType::State; break; case 405: m_Type = Uml::DiagramType::Activity; break; case 406: m_Type = Uml::DiagramType::Component; break; case 407: m_Type = Uml::DiagramType::Deployment; break; case 408: m_Type = Uml::DiagramType::EntityRelationship; break; case 409: m_Type = Uml::DiagramType::Object; break; default: m_Type = Uml::DiagramType::Undefined; break; } } else { m_Type = Uml::DiagramType::fromInt(nType); } m_nLocalID = Uml::ID::fromString(localid); if (m_Type == Uml::DiagramType::Sequence || m_Type == Uml::DiagramType::Collaboration) { QString autoIncrementSequence = qElement.attribute(QLatin1String("autoincrementsequence"), QLatin1String("0")); m_autoIncrementSequence = (bool)autoIncrementSequence.toInt(); } QDomNode node = qElement.firstChild(); bool widgetsLoaded = false, messagesLoaded = false, associationsLoaded = false; while (!node.isNull()) { QDomElement element = node.toElement(); if (!element.isNull()) { if (element.tagName() == QLatin1String("widgets")) widgetsLoaded = loadWidgetsFromXMI(element); else if (element.tagName() == QLatin1String("messages")) messagesLoaded = loadMessagesFromXMI(element); else if (element.tagName() == QLatin1String("associations")) associationsLoaded = loadAssociationsFromXMI(element); } node = node.nextSibling(); } if (!widgetsLoaded) { uWarning() << "failed UMLScene load on widgets"; return false; } if (!messagesLoaded) { uWarning() << "failed UMLScene load on messages"; return false; } if (!associationsLoaded) { uWarning() << "failed UMLScene load on associations"; return false; } return true; } bool UMLScene::loadWidgetsFromXMI(QDomElement & qElement) { UMLWidget* widget = 0; QDomNode node = qElement.firstChild(); QDomElement widgetElement = node.toElement(); while (!widgetElement.isNull()) { widget = loadWidgetFromXMI(widgetElement); if (widget) { addWidgetCmd(widget); widget->clipSize(); // In the interest of best-effort loading, in case of a // (widget == 0) we still go on. // The individual widget's loadFromXMI1 method should // already have generated an error message to tell the // user that something went wrong. } node = widgetElement.nextSibling(); widgetElement = node.toElement(); } return true; } /** * Loads a "widget" element from XMI, used by loadFromXMI1() and the clipboard. */ UMLWidget* UMLScene::loadWidgetFromXMI(QDomElement& widgetElement) { if (!m_doc) { uWarning() << "m_doc is NULL"; return 0L; } QString tag = widgetElement.tagName(); QString idstr = widgetElement.attribute(QLatin1String("xmi.id"), QLatin1String("-1")); UMLWidget* widget = Widget_Factory::makeWidgetFromXMI(tag, idstr, this); if (widget == 0) return 0; if (!widget->loadFromXMI1(widgetElement)) { widget->cleanup(); delete widget; return 0; } return widget; } bool UMLScene::loadMessagesFromXMI(QDomElement & qElement) { MessageWidget * message = 0; QDomNode node = qElement.firstChild(); QDomElement messageElement = node.toElement(); while (!messageElement.isNull()) { QString tag = messageElement.tagName(); DEBUG(DBG_SRC) << "tag = " << tag; if (tag == QLatin1String("messagewidget") || tag == QLatin1String("UML:MessageWidget")) { // for bkwd compatibility message = new MessageWidget(this, SequenceMessage::Asynchronous, Uml::ID::Reserved); if (!message->loadFromXMI1(messageElement)) { delete message; return false; } addWidgetCmd(message); FloatingTextWidget *ft = message->floatingTextWidget(); if (!ft && message->sequenceMessageType() != SequenceMessage::Creation) DEBUG(DBG_SRC) << "floating text is NULL for message " << Uml::ID::toString(message->id()); } node = messageElement.nextSibling(); messageElement = node.toElement(); } return true; } bool UMLScene::loadAssociationsFromXMI(QDomElement & qElement) { QDomNode node = qElement.firstChild(); QDomElement assocElement = node.toElement(); int countr = 0; while (!assocElement.isNull()) { QString tag = assocElement.tagName(); if (tag == QLatin1String("assocwidget") || tag == QLatin1String("UML:AssocWidget")) { // for bkwd compatibility countr++; AssociationWidget *assoc = AssociationWidget::create(this); if (!assoc->loadFromXMI1(assocElement)) { uError() << "could not loadFromXMI1 association widget:" << assoc << ", bad XMI file? Deleting from UMLScene."; delete assoc; /* return false; Returning false here is a little harsh when the rest of the diagram might load okay. */ } else { assoc->clipSize(); if (!addAssociation(assoc, false)) { uError() << "Could not addAssociation(" << assoc << ") to UMLScene, deleting."; delete assoc; //return false; // soften error.. may not be that bad } } } node = assocElement.nextSibling(); assocElement = node.toElement(); } return true; } /** * Add an object to the application, and update the view. */ void UMLScene::addObject(UMLObject *object) { m_bCreateObject = true; if (m_doc->addUMLObject(object)) m_doc->signalUMLObjectCreated(object); // m_bCreateObject is reset by slotObjectCreated() else m_bCreateObject = false; } bool UMLScene::loadUisDiagramPresentation(QDomElement & qElement) { for (QDomNode node = qElement.firstChild(); !node.isNull(); node = node.nextSibling()) { QDomElement elem = node.toElement(); QString tag = elem.tagName(); if (! UMLDoc::tagEq(tag, QLatin1String("Presentation"))) { uError() << "ignoring unknown UisDiagramPresentation tag " << tag; continue; } QDomNode n = elem.firstChild(); QDomElement e = n.toElement(); QString idStr; int x = 0, y = 0, w = 0, h = 0; while (!e.isNull()) { tag = e.tagName(); DEBUG(DBG_SRC) << "Presentation: tag = " << tag; if (UMLDoc::tagEq(tag, QLatin1String("Presentation.geometry"))) { QDomNode gnode = e.firstChild(); QDomElement gelem = gnode.toElement(); QString csv = gelem.text(); QStringList dim = csv.split(QLatin1Char(',')); x = dim[0].toInt(); y = dim[1].toInt(); w = dim[2].toInt(); h = dim[3].toInt(); } else if (UMLDoc::tagEq(tag, QLatin1String("Presentation.style"))) { // TBD } else if (UMLDoc::tagEq(tag, QLatin1String("Presentation.model"))) { QDomNode mnode = e.firstChild(); QDomElement melem = mnode.toElement(); idStr = melem.attribute(QLatin1String("xmi.idref")); } else { DEBUG(DBG_SRC) << "ignoring tag " << tag; } n = n.nextSibling(); e = n.toElement(); } Uml::ID::Type id = Uml::ID::fromString(idStr); UMLObject *o = m_doc->findObjectById(id); if (o == 0) { uError() << "Cannot find object for id " << idStr; } else { UMLObject::ObjectType ot = o->baseType(); DEBUG(DBG_SRC) << "Create widget for model object of type " << UMLObject::toString(ot); UMLWidget *widget = 0; switch (ot) { case UMLObject::ot_Class: widget = new ClassifierWidget(this, o->asUMLClassifier()); break; case UMLObject::ot_Association: { UMLAssociation *umla = o->asUMLAssociation(); Uml::AssociationType::Enum at = umla->getAssocType(); UMLObject* objA = umla->getObject(Uml::RoleType::A); UMLObject* objB = umla->getObject(Uml::RoleType::B); if (objA == 0 || objB == 0) { uError() << "intern err 1"; return false; } UMLWidget *wA = findWidget(objA->id()); UMLWidget *wB = findWidget(objB->id()); if (wA != 0 && wB != 0) { AssociationWidget *aw = AssociationWidget::create(this, wA, at, wB, umla); aw->syncToModel(); addWidgetCmd(aw); } else { uError() << "cannot create assocwidget from (" ; //<< wA << ", " << wB << ")"; } break; } case UMLObject::ot_Role: { //UMLRole *robj = o->asUMLRole(); //UMLAssociation *umla = robj->getParentAssociation(); // @todo properly display role names. // For now, in order to get the role names displayed // simply delete the participating diagram objects // and drag them from the list view to the diagram. break; } default: uError() << "Cannot create widget of type " << ot; } if (widget) { DEBUG(DBG_SRC) << "Widget: x=" << x << ", y=" << y << ", w=" << w << ", h=" << h; widget->setX(x); widget->setY(y); widget->setSize(w, h); addWidgetCmd(widget); } } } return true; } /** * Loads the "UISDiagram" tag of Unisys.IntegratePlus.2 generated files. */ bool UMLScene::loadUISDiagram(QDomElement & qElement) { QString idStr = qElement.attribute(QLatin1String("xmi.id")); if (idStr.isEmpty()) return false; m_nID = Uml::ID::fromString(idStr); UMLListViewItem *ulvi = 0; for (QDomNode node = qElement.firstChild(); !node.isNull(); node = node.nextSibling()) { if (node.isComment()) continue; QDomElement elem = node.toElement(); QString tag = elem.tagName(); if (tag == QLatin1String("uisDiagramName")) { setName(elem.text()); if (ulvi) ulvi->setText(name()); } else if (tag == QLatin1String("uisDiagramStyle")) { QString diagramStyle = elem.text(); if (diagramStyle != QLatin1String("ClassDiagram")) { uError() << "diagram style " << diagramStyle << " is not yet implemented"; continue; } m_doc->setMainViewID(m_nID); m_Type = Uml::DiagramType::Class; UMLListView *lv = UMLApp::app()->listView(); ulvi = new UMLListViewItem(lv->theLogicalView(), name(), UMLListViewItem::lvt_Class_Diagram, m_nID); } else if (tag == QLatin1String("uisDiagramPresentation")) { loadUisDiagramPresentation(elem); } else if (tag != QLatin1String("uisToolName")) { DEBUG(DBG_SRC) << "ignoring tag " << tag; } } return true; } /** * Left Alignment */ void UMLScene::alignLeft() { UMLWidgetList widgetList = selectedWidgetsExt(); if (widgetList.isEmpty()) return; qreal smallestX = WidgetList_Utils::getSmallestX(widgetList); foreach(UMLWidget *widget, widgetList) { widget->setX(smallestX); widget->adjustAssocs(widget->x(), widget->y()); } //TODO: Push stored cmds to stack. } /** * Right Alignment */ void UMLScene::alignRight() { UMLWidgetList widgetList = selectedWidgetsExt(); if (widgetList.isEmpty()) return; qreal biggestX = WidgetList_Utils::getBiggestX(widgetList); foreach(UMLWidget *widget, widgetList) { widget->setX(biggestX - widget->width()); widget->adjustAssocs(widget->x(), widget->y()); } //TODO: Push stored cmds to stack. } /** * Top Alignment */ void UMLScene::alignTop() { UMLWidgetList widgetList = selectedWidgetsExt(); if (widgetList.isEmpty()) return; qreal smallestY = WidgetList_Utils::getSmallestY(widgetList); foreach(UMLWidget *widget, widgetList) { widget->setY(smallestY); widget->adjustAssocs(widget->x(), widget->y()); } //TODO: Push stored cmds to stack. } /** * Bottom Alignment */ void UMLScene::alignBottom() { UMLWidgetList widgetList = selectedWidgetsExt(); if (widgetList.isEmpty()) return; qreal biggestY = WidgetList_Utils::getBiggestY(widgetList); foreach(UMLWidget *widget, widgetList) { widget->setY(biggestY - widget->height()); widget->adjustAssocs(widget->x(), widget->y()); } //TODO: Push stored cmds to stack. } /** * Vertical Middle Alignment */ void UMLScene::alignVerticalMiddle() { UMLWidgetList widgetList = selectedWidgetsExt(); if (widgetList.isEmpty()) return; qreal smallestY = WidgetList_Utils::getSmallestY(widgetList); qreal biggestY = WidgetList_Utils::getBiggestY(widgetList); qreal middle = int((biggestY - smallestY) / 2) + smallestY; foreach(UMLWidget *widget, widgetList) { widget->setY(middle - widget->height() / 2); widget->adjustAssocs(widget->x(), widget->y()); } AssociationWidgetList assocList = selectedAssocs(); if (!assocList.isEmpty()) { foreach (AssociationWidget *widget, assocList) { widget->setYEntireAssoc(middle); } } //TODO: Push stored cmds to stack. } /** * Horizontal Middle Alignment */ void UMLScene::alignHorizontalMiddle() { UMLWidgetList widgetList = selectedWidgetsExt(); if (widgetList.isEmpty()) return; qreal smallestX = WidgetList_Utils::getSmallestX(widgetList); qreal biggestX = WidgetList_Utils::getBiggestX(widgetList); qreal middle = int((biggestX - smallestX) / 2) + smallestX; foreach(UMLWidget *widget, widgetList) { widget->setX(middle - widget->width() / 2); widget->adjustAssocs(widget->x(), widget->y()); } AssociationWidgetList assocList = selectedAssocs(); if (!assocList.isEmpty()) { foreach (AssociationWidget *widget, assocList) { widget->setXEntireAssoc(middle); } } //TODO: Push stored cmds to stack. } /** * Vertical Distribute Alignment */ void UMLScene::alignVerticalDistribute() { UMLWidgetList widgetList = selectedWidgetsExt(); if (widgetList.isEmpty()) return; qreal smallestY = WidgetList_Utils::getSmallestY(widgetList); qreal biggestY = WidgetList_Utils::getBiggestY(widgetList); qreal heightsSum = WidgetList_Utils::getHeightsSum(widgetList); qreal distance = int(((biggestY - smallestY) - heightsSum) / (widgetList.count() - 1.0) + 0.5); qSort(widgetList.begin(), widgetList.end(), Widget_Utils::hasSmallerY); int i = 1; UMLWidget* widgetPrev = 0; foreach(UMLWidget *widget, widgetList) { if (i == 1) { widgetPrev = widget; } else { widget->setY(widgetPrev->y() + widgetPrev->height() + distance); widget->adjustAssocs(widget->x(), widget->y()); widgetPrev = widget; } i++; } //TODO: Push stored cmds to stack. } /** * Horizontal Distribute Alignment */ void UMLScene::alignHorizontalDistribute() { UMLWidgetList widgetList = selectedWidgetsExt(); if (widgetList.isEmpty()) return; qreal smallestX = WidgetList_Utils::getSmallestX(widgetList); qreal biggestX = WidgetList_Utils::getBiggestX(widgetList); qreal widthsSum = WidgetList_Utils::getWidthsSum(widgetList); qreal distance = int(((biggestX - smallestX) - widthsSum) / (widgetList.count() - 1.0) + 0.5); qSort(widgetList.begin(), widgetList.end(), Widget_Utils::hasSmallerX); int i = 1; UMLWidget* widgetPrev = 0; foreach(UMLWidget *widget, widgetList) { if (i == 1) { widgetPrev = widget; } else { widget->setX(widgetPrev->x() + widgetPrev->width() + distance); widget->adjustAssocs(widget->x(), widget->y()); widgetPrev = widget; } i++; } //TODO: Push stored cmds to stack. } /** * Overloading operator for debugging output. */ QDebug operator<<(QDebug dbg, UMLScene *item) { dbg.nospace() << "UMLScene: " << item->name() << " / type=" << DiagramType::toString(item->type()) << " / id=" << Uml::ID::toString(item->ID()) << " / isOpen=" << item->isOpen(); return dbg.space(); }