diff --git a/umbrello/basictypes.h b/umbrello/basictypes.h index 5f422261e..00749eb3d 100644 --- a/umbrello/basictypes.h +++ b/umbrello/basictypes.h @@ -1,376 +1,377 @@ /*************************************************************************** * Copyright (C) 2011 by Andi Fischer * * * * This 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, or (at your option) * * any later version. * * * * This software is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this package; see the file COPYING. If not, write to * * the Free Software Foundation, Inc., 51 Franklin Street - Fifth Floor, * * Boston, MA 02110-1301, USA. * ***************************************************************************/ #ifndef BASICTYPES_H #define BASICTYPES_H // qt includes #include #include #include /** * This namespace contains all the enums used all over the code base. * The enums are embedded into namespaces and useful functionality is added. */ namespace Uml { /** * The model type enum is used to identify the folder the diagrams belong to. */ namespace ModelType { enum Enum { Logical, UseCase, Component, Deployment, EntityRelationship, N_MODELTYPES // must remain last }; QString toString(Enum item); Enum fromString(const QString& item); Enum fromInt(int item); } /** * The visibility enum defines the visibility of attributes and operations. */ namespace Visibility { enum Enum { Public, Private, Protected, Implementation, // objects marked with this are declared in the implementation file. FromParent = 3, // alias for Implementation, used by CodeGenerationPolicy Unknown }; QString toString(Enum item, bool mnemonic = false); Enum fromString(const QString& item, bool checkUnknown = false); Enum fromInt(int item); } /** * Supported diagram types. */ namespace DiagramType { enum Enum { //the values in this enum are saved out to the file //for file compatibility, only add new values to the end Undefined = 0, Class, UseCase, Sequence, Collaboration, State, Activity, Component, Deployment, EntityRelationship, Object, N_DIAGRAMTYPES // must remain last }; QString toString(Enum item); QString toStringI18n(Enum item); Enum fromString(const QString& item); Enum fromInt(int item); } /** * Association types. */ namespace AssociationType { // only append entries to this type // it is used as xmi file attribute enum Enum { Generalization = 500, Aggregation, Dependency, Association, Association_Self, Coll_Message_Asynchronous, Seq_Message, Coll_Message_Self, Seq_Message_Self, Containment, Composition, Realization, UniAssociation, Anchor, State, Activity, Exception, Category2Parent, Child2Category, Relationship, Coll_Message_Synchronous, // enter new entries before this line Reserved, Unknown = - 1 }; QString toString(Enum item); QString toStringI18n(Enum item); Enum fromString(const QString& item); Enum fromInt(int item); bool hasUMLRepresentation(Enum item); } /** * Layout types. */ namespace LayoutType { enum Enum { Undefined = 0, Direct, Orthogonal, Polyline, Spline, N_LAYOUTTYPES // must remain last }; QString toString(Enum item); Enum fromString(const QString& item); Enum fromInt(int item); } /** * Signature types. */ namespace SignatureType { enum Enum { NoSig = 600, ShowSig, SigNoVis, NoSigNoVis }; QString toString(Enum item); Enum fromString(const QString& item); Enum fromInt(int item); } /** * TextRole types. */ namespace TextRole { enum Enum { Floating = 700, //text widget on diagrams MultiA, //Text for Multiple A MultiB, //Text for Multiple B Name, //middle text on most associations Seq_Message, //message on seq diagram between two objects Seq_Message_Self, //message to self on seq diagram - feature not implemented yet Coll_Message, //message between two objects on a collab diagram Coll_Message_Self, //message to object self on collab diagram State, RoleAName, //RoleA text on associations RoleBName, //RoleB text on associations ChangeA, //Changeability A text on associations ChangeB, //Changeability B text on associations Reserved //Enter new entries before this line }; QString toString(Enum item); Enum fromString(const QString& item); Enum fromInt(int item); } /** * Changeability types. */ namespace Changeability { enum Enum { Changeable = 900, Frozen, AddOnly }; QString toString(Enum item); Enum fromString(const QString& item); Enum fromInt(int item); } /** * SequenceMessage type */ namespace SequenceMessage { enum Enum { //This is saved out to the file so only add new entries at the end Synchronous = 1000, Asynchronous, Creation, Lost, - Found + Found, + Destroy, }; QString toString(Enum item); Enum fromString(const QString& item); Enum fromInt(int item); } /** * Constants used for indexing the roles of associations. */ namespace RoleType { enum Enum { A, B }; QString toString(Enum item); Enum fromString(const QString& item); Enum fromInt(int item); } /** * Direction of operation parameters: * in = operation uses the parameter as an input value * out = operation fills the parameter as a return value * inout = operation both reads and writes the parameter * The numeric values of this enum are not currently saved to file. */ namespace ParameterDirection { enum Enum { In, InOut, Out }; QString toString(Enum item); Enum fromString(const QString& item); Enum fromInt(int item); } /** * Supported programming languages. */ namespace ProgrammingLanguage { enum Enum { ActionScript, Ada, Cpp, CSharp, D, IDL, Java, JavaScript, MySQL, Pascal, Perl, PHP, PHP5, PostgreSQL, Python, Ruby, SQL, Tcl, Vala, XMLSchema, Reserved }; QString toString(Enum item); Enum fromString(const QString& item); Enum fromInt(int item); QStringList toExtensions(Enum item); QString toExtensionsDescription(Enum item); bool isCaseSensitive(Enum item); QString scopeSeparator(Enum item); } /** * Enumeration used for stating where a line is on a widget. * @note Do not change this ordering, as we use these values in for loop. * @note See also associationwidget.h. */ namespace Region { enum Enum { Error = 0, West, North, East, South, NorthWest, NorthEast, SouthEast, SouthWest, Center }; QString toString(Enum item); Enum fromString(const QString& item); Enum fromInt(int item); } /** * Corner class with special operators. */ class Corner { public: enum Enum { TopLeft = 0x1, TopRight = 0x2, BottomRight = 0x4, BottomLeft = 0x8 }; static QString toString(Enum item); static Enum fromString(const QString& item); static Enum fromInt(int item); }; Q_DECLARE_FLAGS(Corners, Corner::Enum) Q_DECLARE_OPERATORS_FOR_FLAGS(Corners) /** * The data type used for unique IDs. */ namespace ID { typedef std::string Type; const Type None = "-1"; ///< special value for uninitialized ID const Type Reserved = "0"; ///< special value for illegal ID QString toString(const ID::Type &id); ID::Type fromString(const QString &id); QDebug operator<<(QDebug out, ID::Type &type); } QFont systemFont(); } // end namespace Uml static inline QString toString(Uml::ProgrammingLanguage::Enum lang) { return Uml::ProgrammingLanguage::toString(lang); } static inline QString toString(Uml::Visibility::Enum visibility) { return Uml::Visibility::toString(visibility); } qreal toDoubleFromAnyLocale(const QString &s); #endif diff --git a/umbrello/icon_utils.cpp b/umbrello/icon_utils.cpp index 83788b6f4..aeacc3d13 100644 --- a/umbrello/icon_utils.cpp +++ b/umbrello/icon_utils.cpp @@ -1,396 +1,397 @@ /* Copyright 2008 Andreas Fischer Copyright (C) 2009-2020 * Umbrello UML Modeller Authors * 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) version 3 or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 14 of version 3 of the license. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "icon_utils.h" #include "basictypes.h" #include "debug_utils.h" #include "optionstate.h" #include #include namespace Icon_Utils { #define ICON_PREFIX QLatin1String(":/pics/") /** * Returns the pixmap for the given type as small icon. * @param type the identification of the icon * @return the wanted pixmap */ QPixmap SmallIcon(IconType type) { QString icon = toString(type); if (QFile::exists(ICON_PREFIX + icon + QLatin1String(".png"))) return QPixmap(ICON_PREFIX + icon); else return SmallIcon(icon); } /** * Returns the pixmap for the given type as bar icon. * @param type the identification of the icon * @return the wanted pixmap */ QPixmap BarIcon(IconType type) { QString icon = toString(type); if (QFile::exists(ICON_PREFIX + icon + QLatin1String(".png"))) return QPixmap(ICON_PREFIX + icon); else return BarIcon(icon); } /** * Returns the pixmap for the given type as main bar icon. * @param type the identification of the icon * @return the wanted pixmap */ QPixmap MainBarIcon(IconType type) { QString icon = toString(type); if (QFile::exists(ICON_PREFIX + icon + QLatin1String(".png"))) return QPixmap(ICON_PREFIX + icon); else return MainBarIcon(icon); } /** * Returns the pixmap for the given type as user icon. * @param type the identification of the icon * @return the wanted pixmap */ QPixmap UserIcon(IconType type) { QString icon = toString(type); if (QFile::exists(ICON_PREFIX + icon + QLatin1String(".png"))) return QPixmap(ICON_PREFIX + icon); else return UserIcon(icon); } /** * Returns the pixmap for the given type as desktop icon. * @param type the identification of the icon * @return the wanted icon */ QPixmap DesktopIcon(IconType type) { QString icon = toString(type); if (QFile::exists(ICON_PREFIX + icon + QLatin1String(".png"))) return QPixmap(ICON_PREFIX + icon); else return DesktopIcon(icon); } /** * Returns the pixmap for the given type as user icon. * This is used in worktoolbar to create cursors. * @param type the identification of the icon * @return the wanted cursor */ QCursor Cursor(IconType type) { // TODO: generate from a 32x32 cursor template and place requested icon into QString icon = QLatin1String("cursor-") + toString(type); if (QFile::exists(ICON_PREFIX + icon + QLatin1String(".png"))) return QCursor(QPixmap(ICON_PREFIX + icon), 9, 9); else return QCursor(UserIcon(icon), 9, 9); } /** * Return the icon corresponding to the given Diagram_Type. * @param dt the diagram type * @return the wanted icon */ QPixmap iconSet(Uml::DiagramType::Enum dt) { switch (dt) { case Uml::DiagramType::UseCase: return DesktopIcon(it_Diagram_Usecase); case Uml::DiagramType::Collaboration: return DesktopIcon(it_Diagram_Collaboration); case Uml::DiagramType::Class: return DesktopIcon(it_Diagram_Class); case Uml::DiagramType::Object: return DesktopIcon(it_Diagram_Object); case Uml::DiagramType::Sequence: return DesktopIcon(it_Diagram_Sequence); case Uml::DiagramType::State: return DesktopIcon(it_Diagram_State); case Uml::DiagramType::Activity: return DesktopIcon(it_Diagram_Activity); case Uml::DiagramType::Component: return DesktopIcon(it_Diagram_Component); case Uml::DiagramType::Deployment: return DesktopIcon(it_Diagram_Deployment); case Uml::DiagramType::EntityRelationship: return DesktopIcon(it_Diagram_EntityRelationship); default: uDebug() << "Widget_Utils::iconSet: unknown diagram type " << Uml::DiagramType::toString(dt); return QPixmap(); } } /** * Return the icon corresponding to the given Diagram_Type. * @param dt the diagram type * @return the wanted icon */ QPixmap smallIcon(Uml::DiagramType::Enum dt) { switch (dt) { case Uml::DiagramType::UseCase: return SmallIcon(it_Diagram_Usecase); case Uml::DiagramType::Collaboration: return SmallIcon(it_Diagram_Collaboration); case Uml::DiagramType::Class: return SmallIcon(it_Diagram_Class); case Uml::DiagramType::Object: return SmallIcon(it_Diagram_Object); case Uml::DiagramType::Sequence: return SmallIcon(it_Diagram_Sequence); case Uml::DiagramType::State: return SmallIcon(it_Diagram_State); case Uml::DiagramType::Activity: return SmallIcon(it_Diagram_Activity); case Uml::DiagramType::Component: return SmallIcon(it_Diagram_Component); case Uml::DiagramType::Deployment: return SmallIcon(it_Diagram_Deployment); case Uml::DiagramType::EntityRelationship: return SmallIcon(it_Diagram_EntityRelationship); default: uDebug() << "Widget_Utils::smallIcon: unknown diagram type " << Uml::DiagramType::toString(dt); return QPixmap(); } } /** * Conversion from icon type to its string name. * @param type the identification of the icon * @return the string representation of the type */ QString toString(IconType type) { switch (type) { case it_Accept_Signal: return QLatin1String("accept_signal"); case it_Accept_TimeEvent: return QLatin1String("accept_time_event"); case it_Activity: return QLatin1String("activity"); case it_Activity_End: return QLatin1String("end_state"); case it_Activity_Final: return QLatin1String("final_activity"); case it_Activity_Initial: return QLatin1String("initial_state"); case it_Activity_Transition: return QLatin1String("uniassociation"); case it_Actor: return QLatin1String("actor"); case it_Add_Point: return QLatin1String("format-add-node"); case it_Aggregation: return QLatin1String("aggregation"); case it_Align_Bottom: return QLatin1String("align-vertical-bottom"); case it_Align_HorizontalDistribute: return QLatin1String("distribute-horizontal"); case it_Align_HorizontalMiddle: return QLatin1String("align-horizontal-center"); case it_Align_Left: return QLatin1String("align-horizontal-left"); case it_Align_Right: return QLatin1String("align-horizontal-right"); case it_Align_Top: return QLatin1String("align-vertical-top"); case it_Align_VerticalDistribute: return QLatin1String("distribute-vertical"); case it_Align_VerticalMiddle: return QLatin1String("align-vertical-center"); case it_Anchor: return QLatin1String("anchor"); case it_And_Line: return QLatin1String("andline"); case it_Arrow: return QLatin1String("arrow"); case it_Arrow_Down: return QLatin1String("arrow-down"); case it_Arrow_Up: return QLatin1String("arrow-up"); case it_Artifact: return QLatin1String("artifact"); case it_Association: return QLatin1String("association"); case it_Attribute_New: return QLatin1String("CVpublic_var"); case it_Box: return QLatin1String("box"); case it_Branch: return QLatin1String("branch"); case it_Category: return QLatin1String("category"); case it_Category_Child: return QLatin1String("child2category"); case it_Category_Parent: return QLatin1String("category2parent"); case it_Change_Font: return QLatin1String("preferences-desktop-font"); case it_Check_Constraint: return QLatin1String("check_constraint"); case it_Choice_Rhomb: return QLatin1String("choice-rhomb"); case it_Choice_Round: return QLatin1String("choice-round"); case it_Class: return QLatin1String("class"); case it_ClassOrPackage: return QLatin1String("class-or-package"); case it_Clear: return QLatin1String("edit-clear"); case it_Code_Gen_Wizard: return QLatin1String("umbrello"); case it_Color_Fill: return QLatin1String("fill-color"); case it_Color_Line: return QLatin1String("draw-brush"); case it_Combined_Fragment: return QLatin1String("combined_fragment"); case it_Component: return Settings::optionState().generalState.uml2 ? QLatin1String("component") : QLatin1String("component1"); case it_Composition: return QLatin1String("composition"); case it_Condition_PrePost: return QLatin1String("PrePostCondition"); case it_Constraint_Check: return QLatin1String("check_constraint"); case it_Constraint_ForeignKey: return QLatin1String("foreignkey_constraint"); case it_Constraint_PrimaryKey: return QLatin1String("primarykey_constraint"); case it_Constraint_Unique: return QLatin1String("unique_constraint"); case it_Containment: return QLatin1String("containment"); case it_Copy: return QLatin1String("edit-copy"); case it_Cut: return QLatin1String("edit-cut"); case it_Datatype: return QLatin1String("datatype"); case it_Delete: return QLatin1String("edit-delete"); case it_Delete_Point: return QLatin1String("format-remove-node"); case it_Dependency: return QLatin1String("dependency"); case it_Diagram: return QLatin1String("CVnamespace"); case it_Diagram_Activity: return QLatin1String("umbrello_diagram_activity"); case it_Diagram_Class: return QLatin1String("umbrello_diagram_class"); case it_Diagram_Collaboration: return QLatin1String("umbrello_diagram_collaboration"); case it_Diagram_Component: return QLatin1String("umbrello_diagram_component"); case it_Diagram_Deployment: return QLatin1String("umbrello_diagram_deployment"); case it_Diagram_EntityRelationship: return QLatin1String("umbrello_diagram_deployment"); case it_Diagram_Object: return QLatin1String("umbrello_diagram_object"); case it_Diagram_Sequence: return QLatin1String("umbrello_diagram_sequence"); case it_Diagram_State: return QLatin1String("umbrello_diagram_state"); case it_Diagram_Usecase: return QLatin1String("umbrello_diagram_usecase"); case it_Directional_Association: return QLatin1String("uniassociation"); case it_Document_Edit: return QLatin1String("document-edit"); case it_Duplicate: return QLatin1String("duplicate"); case it_EndState: return QLatin1String("end_state"); case it_Entity: return QLatin1String("entity"); case it_Entity_Attribute: return QLatin1String("text-x-generic"); case it_Entity_Attribute_New: return QLatin1String("text-x-generic"); case it_Enum: return QLatin1String("enum"); case it_Enum_Literal: return QLatin1String("text-x-generic"); case it_Exception: return QLatin1String("exception"); case it_Export_Files: return QLatin1String("document-export"); case it_Export_Picture: return QLatin1String("image-x-generic"); case it_File_Open: return QLatin1String("document-open"); case it_Folder: return QLatin1String("folder-new"); case it_Folder_Cyan: return QLatin1String("folder"); case it_Folder_Cyan_Open: return QLatin1String("folder-open"); case it_Folder_Green: return QLatin1String("folder-green"); case it_Folder_Green_Open: return QLatin1String("folder-green"); //FIXME was folder_green_open case it_Folder_Grey: return QLatin1String("folder-grey"); case it_Folder_Grey_Open: return QLatin1String("folder-grey"); //FIXME was folder_grey_open case it_Folder_Orange: return QLatin1String("folder-orange"); case it_Folder_Orange_Open: return QLatin1String("folder-orange"); //FIXME was folder_orange_open case it_Folder_Red: return QLatin1String("folder-red"); case it_Folder_Red_Open: return QLatin1String("folder-red"); //FIXME was folder_red_open case it_Folder_Violet: return QLatin1String("folder-violet"); case it_Folder_Violet_Open: return QLatin1String("folder-violet"); //FIXME was folder_violet_open case it_ForeignKey_Constraint: return QLatin1String("foreignkey_constraint"); case it_Fork_Join: return QLatin1String("activity-fork"); case it_Fork_State: return QLatin1String("state-fork"); case it_Generalisation: return QLatin1String("generalisation"); case it_Go_Next: return QLatin1String("go-next"); case it_Go_Previous: return QLatin1String("go-previous"); case it_History_Deep: return QLatin1String("deep-history"); case it_History_Shallow: return QLatin1String("shallow-history"); case it_Home: return QLatin1String("user-home"); case it_Implementation_Attribute: return QLatin1String("CVimplementation_var"); case it_Implementation_Method: return QLatin1String("CVimplementation_meth"); case it_Implements: return QLatin1String("generalisation"); case it_Import_File: return QLatin1String("document-import"); case it_Import_Files: return QLatin1String("document-import"); case it_Import_Project: return QLatin1String("document-import"); case it_InitialState: return QLatin1String("initial_state"); case it_Instance: return QLatin1String("instance"); case it_Interface: return QLatin1String("interface"); case it_Interface_Requirement: return QLatin1String("interface-requirement"); case it_Interface_Provider: return QLatin1String("interface-provider"); case it_Join: return QLatin1String("join"); case it_Junction: return QLatin1String("junction"); case it_Literal_New: return QLatin1String("text-x-generic"); case it_Message_Async: return QLatin1String("umbr-message-asynchronous"); case it_Message_Asynchronous: return QLatin1String("umbr-coll-message-asynchronous"); case it_Message_Creation: return QLatin1String("umbr-message-creation"); + case it_Message_Destroy: return QLatin1String("umbr-message-destroy"); case it_Message_Found: return QLatin1String("umbr-message-found"); case it_Message_Lost: return QLatin1String("umbr-message-lost"); case it_Message_Sync: return QLatin1String("umbr-message-synchronous"); case it_Message_Synchronous: return QLatin1String("umbr-coll-message-synchronous"); case it_New: return QLatin1String("document-new"); case it_Node: return QLatin1String("node"); case it_Note: return QLatin1String("note"); case it_Object: return QLatin1String("object"); case it_Object_Node: return QLatin1String("object_node"); case it_Operation_New: return QLatin1String("document-new"); case it_Operation_Public_New: return QLatin1String("CVpublic_meth"); case it_Package: return QLatin1String("package"); case it_Parameter_New: return QLatin1String("text-x-generic"); case it_Paste: return QLatin1String("edit-paste"); case it_Pin: return QLatin1String("pin"); case it_Port: return QLatin1String("port"); case it_Precondition: return QLatin1String("precondition"); case it_PrimaryKey_Constraint: return QLatin1String("primarykey_constraint"); case it_Private_Attribute: return QLatin1String("CVprivate_var"); case it_Private_Method: return QLatin1String("CVprivate_meth"); case it_Properties: return QLatin1String("preferences-system"); case it_Properties_Activities: return QLatin1String("text-x-generic"); case it_Properties_Associations: return QLatin1String("preferences-other"); case it_Properties_Attributes: return QLatin1String("preferences-other"); case it_Properties_AutoLayout: return QLatin1String("code-class"); case it_Properties_Class: return QLatin1String("document-properties"); case it_Properties_CodeGeneration: return QLatin1String("document-export"); case it_Properties_CodeImport: return QLatin1String("document-import"); case it_Properties_CodeViewer: return QLatin1String("package_graphics_viewer"); case it_Properties_Color: return QLatin1String("preferences-desktop-color"); case it_Properties_Columns: return QLatin1String("preferences-other"); case it_Properties_Contents: return QLatin1String("preferences-other"); case it_Properties_Display: return QLatin1String("preferences-desktop-theme"); case it_Properties_EntityAttributes: return QLatin1String("preferences-other"); case it_Properties_EntityConstraints: return QLatin1String("preferences-other"); case it_Properties_EnumLiterals: return QLatin1String("preferences-other"); case it_Properties_Font: return QLatin1String("preferences-desktop-font"); case it_Properties_General: return QLatin1String("preferences-other"); case it_Properties_Operations: return QLatin1String("preferences-other"); case it_Properties_Roles: return QLatin1String("preferences-other"); case it_Properties_Templates: return QLatin1String("preferences-other"); case it_Properties_UserInterface: return QLatin1String("preferences-desktop-theme"); case it_Protected_Attribute: return QLatin1String("CVprotected_var"); case it_Protected_Method: return QLatin1String("CVprotected_meth"); case it_Public_Attribute: return QLatin1String("CVpublic_var"); case it_Public_Method: return QLatin1String("CVpublic_meth"); case it_Redo: return QLatin1String("edit-redo"); case it_Refactor: return QLatin1String("refactor"); case it_Region: return QLatin1String("region"); case it_Relationship: return QLatin1String("relationship"); case it_Remove: return QLatin1String("remove"); case it_Rename: return QLatin1String("edit-rename"); case it_Send_Signal: return QLatin1String("send_signal"); case it_Show: return QLatin1String("document-preview"); case it_State: return QLatin1String("state"); case it_State_Activity: return QLatin1String("text-x-generic"); case it_State_Transition: return QLatin1String("uniassociation"); case it_Subsystem: return QLatin1String("subsystem"); case it_Tab_Close: return QLatin1String("tab-close"); case it_Tab_New: return QLatin1String("tab-new"); case it_Template: return QLatin1String("template"); case it_Template_Class: return QLatin1String("format-justify-fill"); case it_Template_Interface: return QLatin1String("text-x-generic-template"); case it_Template_New: return QLatin1String("text-x-generic-template"); case it_Text: return QLatin1String("text"); case it_Undo: return QLatin1String("edit-undo"); case it_UndoView: return QLatin1String("document-save"); case it_Uniassociation: return QLatin1String("uniassociation"); case it_Unique_Constraint: return QLatin1String("unique_constraint"); case it_UseCase: return QLatin1String("usecase"); case it_View_Code: return QLatin1String("text-x-generic"); case it_Zoom_100: return QLatin1String("zoom-original"); case it_Zoom_Slider: return QLatin1String("zoom-original"); default: return QString(); } } } // namespace diff --git a/umbrello/icon_utils.h b/umbrello/icon_utils.h index db1af0b68..886fcd733 100644 --- a/umbrello/icon_utils.h +++ b/umbrello/icon_utils.h @@ -1,259 +1,260 @@ /* Copyright 2008 Andreas Fischer 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) version 3 or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 14 of version 3 of the license. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef ICON_UTILS_H #define ICON_UTILS_H // app includes #include "basictypes.h" // kde includes #if QT_VERSION < 0x050000 #include #endif // qt includes #include #if QT_VERSION >= 0x050000 #include #endif #include /** * This class is a gateway to KIconLoader for for handling all the * used icons in umbrello. Identification is done with an enum and * then a translation to the icon name. * @author Andreas Fischer */ namespace Icon_Utils { enum IconType { it_Home = 0, it_Arrow, it_File_Open, it_Folder, it_Folder_Cyan, it_Folder_Cyan_Open, it_Folder_Green, it_Folder_Green_Open, it_Folder_Grey, it_Folder_Grey_Open, it_Folder_Red, it_Folder_Red_Open, it_Folder_Violet, it_Folder_Violet_Open, it_Folder_Orange, it_Folder_Orange_Open, it_Properties_General, it_Properties_Color, it_Properties_Display, it_Properties_Attributes, it_Properties_Operations, it_Properties_Templates, it_Properties_EnumLiterals, it_Properties_EntityAttributes, it_Properties_EntityConstraints, it_Properties_Contents, it_Properties_Associations, it_Properties_Font, it_Properties_Activities, it_Properties_Roles, it_Properties_UserInterface, it_Properties_Class, it_Properties_CodeImport, it_Properties_CodeGeneration, it_Properties_CodeViewer, it_Properties_Columns, it_Diagram, //change to have different one for each type of diagram it_Class, it_Object, it_Template, it_Template_Class, it_Template_Interface, it_Package, it_Subsystem, it_Component, it_Port, it_Node, it_Artifact, it_Interface, it_Interface_Provider, it_Interface_Requirement, it_Datatype, it_Enum, it_Entity, it_Actor, it_UseCase, it_Generalisation, it_Association, it_Uniassociation, it_Text, it_Note, it_Box, it_Anchor, it_Containment, it_Public_Method, it_Private_Method, it_Protected_Method, it_Implementation_Method, it_Public_Attribute, it_Private_Attribute, it_Protected_Attribute, it_Implementation_Attribute, it_Unique_Constraint, it_PrimaryKey_Constraint, it_ForeignKey_Constraint, it_Check_Constraint, it_Diagram_Activity, it_Diagram_Class, it_Diagram_Collaboration, it_Diagram_Component, it_Diagram_Deployment, it_Diagram_EntityRelationship, it_Diagram_Sequence, it_Diagram_State, it_Diagram_Usecase, it_Diagram_Object, it_New, it_Delete, it_Add_Point, it_Delete_Point, it_Rename, it_Cut, it_Copy, it_Paste, it_Undo, it_Redo, it_UndoView, it_Go_Next, it_Go_Previous, it_Properties, it_Show, it_Refactor, it_View_Code, it_Tab_New, it_Tab_Close, it_Change_Font, it_Arrow_Up, it_Arrow_Down, it_Clear, it_Operation_New, it_Operation_Public_New, it_Attribute_New, it_Template_New, it_Literal_New, it_Entity_Attribute_New, it_Parameter_New, it_Color_Line, it_Color_Fill, it_Import_File, it_Import_Project, it_Import_Files, it_Export_Files, it_Export_Picture, it_InitialState, it_EndState, it_Branch, it_Entity_Attribute, it_Constraint_PrimaryKey, it_Constraint_ForeignKey, it_Constraint_Check, it_Constraint_Unique, it_Enum_Literal, it_State_Activity, it_Message_Creation, + it_Message_Destroy, it_Message_Sync, it_Message_Async, it_Message_Found, it_Message_Lost, it_Combined_Fragment, it_Precondition, it_Dependency, it_Aggregation, it_Relationship, it_Directional_Association, it_Implements, it_Composition, it_Region, it_Send_Signal, it_Accept_Signal, it_Accept_TimeEvent, it_Fork_Join, it_History_Deep, it_History_Shallow, it_Join, it_Fork_State, it_Junction, it_Choice_Round, it_Choice_Rhomb, it_And_Line, it_State_Transition, it_Activity_Transition, it_Activity, it_State, it_Activity_End, it_Activity_Final, it_Pin, it_Activity_Initial, it_Message_Synchronous, it_Message_Asynchronous, it_Exception, it_Object_Node, it_Condition_PrePost, it_Category, it_Category_Parent, it_Category_Child, it_Zoom_Slider, it_Zoom_100, it_Align_Right, it_Align_Left, it_Align_Top, it_Align_Bottom, it_Align_VerticalMiddle, it_Align_HorizontalMiddle, it_Align_VerticalDistribute, it_Align_HorizontalDistribute, it_Code_Gen_Wizard, it_Properties_AutoLayout, it_Document_Edit, it_ClassOrPackage, it_Instance, it_Remove, it_Duplicate, N_ICONTYPES // must remain last }; QPixmap SmallIcon(IconType type); QPixmap BarIcon(IconType type); QPixmap MainBarIcon(IconType type); QPixmap UserIcon(IconType type); QPixmap DesktopIcon(IconType type); QCursor Cursor(IconType type); QString toString(IconType type); QPixmap iconSet(Uml::DiagramType::Enum dt); QPixmap smallIcon(Uml::DiagramType::Enum dt); } // namespace #endif // ICONCONTAINER_H diff --git a/umbrello/icons.qrc b/umbrello/icons.qrc index 725690419..ba7e0406e 100644 --- a/umbrello/icons.qrc +++ b/umbrello/icons.qrc @@ -1,185 +1,187 @@ pics/accept_signal.png pics/accept_time_event.png pics/activity-fork.png pics/actor.png pics/aggregation.png pics/align-horizontal-center.png pics/align-horizontal-left.png pics/align-horizontal-right.png pics/align-vertical-bottom.png pics/align-vertical-center.png pics/align-vertical-top.png pics/anchor.png pics/andline.png pics/arrow.png pics/artifact.png pics/association.png pics/box.png pics/branch.png pics/category2parent.png pics/category.png pics/check_constraint.png pics/child2category.png pics/choice-rhomb.png pics/choice-round.png pics/class-or-package.png pics/class.png pics/combined_fragment.png pics/component.png pics/composition.png pics/containment.png pics/cursor-accept_signal.png pics/cursor-accept_time_event.png pics/cursor-activity-fork.png pics/cursor-actor.png pics/cursor-aggregation.png pics/cursor-anchor.png pics/cursor-andline.png pics/cursor-artifact.png pics/cursor-association.png pics/cursor-box.png pics/cursor-branch.png pics/cursor-category2parent.png pics/cursor-category.png pics/cursor-child2category.png pics/cursor-choice-rhomb.png pics/cursor-choice-round.png pics/cursor-class.png pics/cursor-combined_fragment.png pics/cursor-component.png pics/cursor-composition.png pics/cursor-containment.png pics/cursor-datatype.png pics/cursor-deep-history.png pics/cursor-dependency.png pics/cursor-end_state.png pics/cursor-entity.png pics/cursor-enum.png pics/cursor-exception.png pics/cursor-final_activity.png pics/cursor-generalisation.png pics/cursor-initial_state.png pics/cursor-instance.png pics/cursor-interface.png pics/cursor-join.png pics/cursor-junction.png pics/cursor-node.png pics/cursor-note.png pics/cursor-object_node.png pics/cursor-object.png pics/cursor-package.png pics/cursor-pin.png pics/cursor-precondition.png pics/cursor-PrePostCondition.png pics/cursor-region.png pics/cursor-relationship.png pics/cursor-send_signal.png pics/cursor-shallow-history.png pics/cursor-state-fork.png pics/cursor-text.png pics/cursor-umbr-coll-message-asynchronous.png pics/cursor-umbr-coll-message-synchronous.png pics/cursor-umbr-message-asynchronous.png pics/cursor-umbr-message-found.png pics/cursor-umbr-message-lost.png pics/cursor-umbr-message-synchronous.png pics/cursor-uniassociation.png pics/cursor-usecase.png pics/CVglobal_meth.png pics/CVglobal_var.png pics/CVimplementation_meth.png pics/CVimplementation_signal.png pics/CVimplementation_slot.png pics/CVimplementation_var.png pics/CVprivate_meth.png pics/CVprivate_signal.png pics/CVprivate_slot.png pics/CVprivate_var.png pics/CVprotected_meth.png pics/CVprotected_signal.png pics/CVprotected_slot.png pics/CVprotected_var.png pics/CVpublic_meth.png pics/CVpublic_signal.png pics/CVpublic_slot.png pics/CVpublic_var.png pics/CVstruct.png pics/datatype.png pics/deep-history.png pics/dependency.png pics/distribute-horizontal.png pics/distribute-vertical.png pics/end_state.png pics/entity.png pics/enum.png pics/exception.png pics/final_activity.png pics/foreignkey_constraint.png pics/generalisation.png pics/global/128-apps-umbrello.png pics/global/16-apps-umbrello.png pics/global/16-mimetypes-application-x-uml.png pics/global/22-apps-umbrello.png pics/global/32-apps-umbrello.png pics/global/32-mimetypes-application-x-uml.png pics/global/48-apps-umbrello.png pics/global/64-apps-umbrello.png pics/hi22-actions-umbrello_diagram_activity.png pics/hi22-actions-umbrello_diagram_class.png pics/hi22-actions-umbrello_diagram_collaboration.png pics/hi22-actions-umbrello_diagram_component.png pics/hi22-actions-umbrello_diagram_deployment.png pics/diag_object.png pics/hi22-actions-umbrello_diagram_sequence.png pics/hi22-actions-umbrello_diagram_state.png pics/hi22-actions-umbrello_diagram_usecase.png pics/initial_state.png pics/instance.png pics/interface.png pics/join.png pics/junction.png pics/node.png pics/note.png pics/object_node.png pics/object.png pics/package.png pics/pin.png pics/port.png pics/precondition.png pics/PrePostCondition.png pics/primarykey_constraint.png pics/realization.png pics/refactor.png pics/region.png pics/relationship.png pics/send_signal.png pics/shallow-history.png pics/startlogo.png pics/state-fork.png pics/subsystem.png pics/template.png pics/text.png pics/umbr-coll-message-asynchronous.png pics/umbr-coll-message-synchronous.png pics/umbr-message-asynchronous.png pics/umbr-message-found.png pics/umbr-message-lost.png pics/umbr-message-synchronous.png pics/uniassociation.png pics/unique_constraint.png pics/usecase.png pics/state.png pics/cursor-state.png pics/component1.png pics/cursor-component1.png pics/interface-provider.png pics/interface-requirement.png pics/cursor-interface-provider.png pics/cursor-interface-requirement.png pics/cursor-subsystem.png pics/cursor-umbr-message-creation.png pics/umbr-message-creation.png + pics/cursor-umbr-message-destroy.png + pics/umbr-message-destroy.png diff --git a/umbrello/menus/listpopupmenu.cpp b/umbrello/menus/listpopupmenu.cpp index 82ee7a4b6..6eb7b0e9c 100644 --- a/umbrello/menus/listpopupmenu.cpp +++ b/umbrello/menus/listpopupmenu.cpp @@ -1,637 +1,640 @@ /*************************************************************************** * 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-2020 * * Umbrello UML Modeller Authors * ***************************************************************************/ // own header #include "listpopupmenu.h" // app includes #include "activitywidget.h" #include "associationline.h" #include "associationwidget.h" #include "category.h" #include "classifier.h" #include "classifierwidget.h" #include "combinedfragmentwidget.h" #include "debug_utils.h" #include "floatingtextwidget.h" #include "folder.h" #include "forkjoinwidget.h" #include "layoutgenerator.h" #include "model_utils.h" #include "objectnodewidget.h" #include "objectwidget.h" #include "notewidget.h" #include "pinportbase.h" #include "preconditionwidget.h" #include "signalwidget.h" #include "statewidget.h" #include "uml.h" #include "umldoc.h" #include "umlscene.h" #include "umlview.h" #include "umllistview.h" #include "umllistviewitem.h" #include "widget_utils.h" #include "widgetbase.h" // kde includes #include #include DEBUG_REGISTER_DISABLED(ListPopupMenu) static const bool CHECKABLE = true; // uncomment to see not handled switch cases //#define CHECK_SWITCH class DebugMenu { public: DebugMenu(ListPopupMenu::MenuType _m) : m(_m) {} DebugMenu(const QString & _m) : menu(_m) {} ListPopupMenu::MenuType m; QString menu; }; class ListPopupMenuPrivate { public: QList debugActions; ~ListPopupMenuPrivate() { debugActions.clear(); } }; #define DEBUG_AddAction(m) d->debugActions.append(DebugMenu(m)) #define DEBUG_StartMenu(m) d->debugActions.append(DebugMenu(m->title() + QLatin1String(" - start"))) #define DEBUG_EndMenu(m) d->debugActions.append(DebugMenu(m->title() + QLatin1String(" - end"))) /** * Constructs the popup menu * * @param parent The parent to ListPopupMenu. */ ListPopupMenu::ListPopupMenu(QWidget *parent) : KMenu(parent), d(new ListPopupMenuPrivate) { } /** * Standard destructor. */ ListPopupMenu::~ListPopupMenu() { foreach (QAction* action, m_actions) { delete action; } m_actions.clear(); delete d; } KMenu *ListPopupMenu::newMenu(const QString &title, QWidget *widget) { KMenu *menu = new KMenu(title, widget); DEBUG_StartMenu(menu); return menu; } void ListPopupMenu::addMenu(KMenu *menu) { KMenu::addMenu(menu); DEBUG_EndMenu(menu); } /** * Shortcut for the frequently used addAction() calls. * * @param m The MenuType for which to insert a menu item. */ void ListPopupMenu::insert(MenuType m) { insert(m, this); } /** * Shortcut for the frequently used addAction() calls. * * @param m The MenuType for which to insert a menu item. * @param menu The KMenu for which to insert a menu item. * @param s The entry to be inserted from the action collection */ void ListPopupMenu::insertFromActionKey(const MenuType m, KMenu *menu, const QString &s) { QAction* action = UMLApp::app()->actionCollection()->action(s); insert(m, menu, action->icon(), action->text()); } /** * Shortcut for the frequently used addAction() calls. * * @param m The MenuType for which to insert a menu item. * @param menu The KMenu for which to insert a menu item. */ void ListPopupMenu::insert(const MenuType m, KMenu* menu) { DEBUG_AddAction(m); Q_ASSERT(menu != 0); switch (m) { case mt_Accept_Signal: m_actions[m] = menu->addAction(Icon_Utils::SmallIcon(Icon_Utils::it_Accept_Signal), i18n("Accept Signal")); break; case mt_Accept_Time_Event: m_actions[m] = menu->addAction(Icon_Utils::SmallIcon(Icon_Utils::it_Accept_TimeEvent), i18n("Accept Time Event")); break; case mt_Activity: m_actions[m] = menu->addAction(Icon_Utils::SmallIcon(Icon_Utils::it_UseCase), i18n("Activity...")); break; case mt_Activity_Transition: m_actions[m] = menu->addAction(Icon_Utils::SmallIcon(Icon_Utils::it_Activity_Transition), i18n("Activity Transition")); break; case mt_Actor: m_actions[m] = menu->addAction(Icon_Utils::SmallIcon(Icon_Utils::it_Actor), i18n("Actor")); break; //case mt_Actor: m_actions[m] = menu->addAction(Icon_Utils::SmallIcon(Icon_Utils::it_Actor), i18n("Actor...")); break; case mt_Artifact: m_actions[m] = menu->addAction(Icon_Utils::SmallIcon(Icon_Utils::it_Artifact), i18n("Artifact")); break; //case mt_Artifact: m_actions[m] = menu->addAction(Icon_Utils::SmallIcon(Icon_Utils::it_Artifact), i18n("Artifact...")); break; case mt_Attribute: m_actions[m] = menu->addAction(Icon_Utils::SmallIcon(Icon_Utils::it_Public_Attribute), i18n("Attribute")); break; //case mt_Attribute: m_actions[m] = menu->addAction(Icon_Utils::SmallIcon(Icon_Utils::it_Public_Attribute), i18n("Attribute...")); break; case mt_Branch: m_actions[m] = menu->addAction(Icon_Utils::SmallIcon(Icon_Utils::it_Branch), i18n("Branch/Merge")); break; case mt_Category: m_actions[m] = menu->addAction(Icon_Utils::SmallIcon(Icon_Utils::it_Category), i18n("Category")); break; //case mt_Category: m_actions[m] = menu->addAction(Icon_Utils::SmallIcon(Icon_Utils::it_Category), i18n("Category...")); break; case mt_Change_Font: m_actions[m] = menu->addAction(Icon_Utils::SmallIcon(Icon_Utils::it_Change_Font), i18n("Change Font...")); break; case mt_CheckConstraint: m_actions[m] = menu->addAction(Icon_Utils::SmallIcon(Icon_Utils::it_Constraint_Check), i18n("Check Constraint...")); break; case mt_Choice: m_actions[m] = menu->addAction(Icon_Utils::SmallIcon(Icon_Utils::it_Choice_Rhomb), i18n("Choice")); break; case mt_Class: m_actions[m] = menu->addAction(Icon_Utils::SmallIcon(Icon_Utils::it_Class), i18nc("new class menu item", "Class...")); break; case mt_Clone: m_actions[m] = menu->addAction(Icon_Utils::BarIcon(Icon_Utils::it_Duplicate), i18nc("duplicate action", "Duplicate")); break; case mt_Collapse_All: m_actions[m] = menu->addAction(i18n("Collapse All")); break; case mt_CombinedState: m_actions[m] = menu->addAction(Icon_Utils::SmallIcon(Icon_Utils::it_State), i18nc("add new combined state", "Combined state...")); break; case mt_Component: m_actions[m] = menu->addAction(Icon_Utils::SmallIcon(Icon_Utils::it_Component), i18n("Component")); break; //case mt_Component: m_actions[m] = menu->addAction(Icon_Utils::SmallIcon(Icon_Utils::it_Component), i18n("Component...")); break; case mt_Component_Diagram: insertFromActionKey(m, menu, QLatin1String("new_component_diagram")); break; case mt_Component_Folder: m_actions[m] = menu->addAction(Icon_Utils::BarIcon(Icon_Utils::it_Folder), i18n("Folder")); break; case mt_Copy: m_actions[m] = menu->addAction(Icon_Utils::SmallIcon(Icon_Utils::it_Copy), i18n("Copy")); break; case mt_Cut: m_actions[m] = menu->addAction(Icon_Utils::SmallIcon(Icon_Utils::it_Cut), i18n("Cut")); break; case mt_Datatype: m_actions[m] = menu->addAction(Icon_Utils::SmallIcon(Icon_Utils::it_Datatype), i18n("Datatype...")); break; case mt_DeepHistory: m_actions[m] = menu->addAction(Icon_Utils::SmallIcon(Icon_Utils::it_History_Deep), i18n("Deep History")); break; case mt_Delete: m_actions[m] = menu->addAction(Icon_Utils::SmallIcon(Icon_Utils::it_Delete), i18n("Delete")); break; case mt_Deployment_Diagram: insertFromActionKey(m, menu, QLatin1String("new_deployment_diagram")); break; case mt_Deployment_Folder: m_actions[m] = menu->addAction(Icon_Utils::BarIcon(Icon_Utils::it_Folder), i18n("Folder")); break; case mt_EditCombinedState: m_actions[m] = menu->addAction(Icon_Utils::SmallIcon(Icon_Utils::it_State), i18n("Edit combined state")); break; case mt_End_Activity: m_actions[m] = menu->addAction(Icon_Utils::SmallIcon(Icon_Utils::it_EndState), i18n("End Activity")); break; case mt_End_State: m_actions[m] = menu->addAction(Icon_Utils::SmallIcon(Icon_Utils::it_EndState), i18n("End State")); break; case mt_Entity: m_actions[m] = menu->addAction(Icon_Utils::SmallIcon(Icon_Utils::it_Entity), i18n("Entity")); break; //case mt_Entity: m_actions[m] = menu->addAction(Icon_Utils::SmallIcon(Icon_Utils::it_Entity), i18n("Entity...")); break; case mt_EntityAttribute: m_actions[m] = menu->addAction(Icon_Utils::SmallIcon(Icon_Utils::it_Entity_Attribute), i18n("Entity Attribute...")); break; case mt_EntityRelationship_Diagram: insertFromActionKey(m, menu, QLatin1String("new_entityrelationship_diagram")); break; case mt_EntityRelationship_Folder: m_actions[m] = menu->addAction(Icon_Utils::BarIcon(Icon_Utils::it_Folder), i18n("Folder")); break; case mt_Enum: m_actions[m] = menu->addAction(Icon_Utils::SmallIcon(Icon_Utils::it_Enum), i18n("Enum...")); break; case mt_EnumLiteral: m_actions[m] = menu->addAction(Icon_Utils::SmallIcon(Icon_Utils::it_Enum_Literal), i18n("Enum Literal...")); break; case mt_Exception: m_actions[m] = menu->addAction(Icon_Utils::SmallIcon(Icon_Utils::it_Exception), i18n("Exception")); break; case mt_Expand_All: m_actions[m] = menu->addAction(i18n("Expand All")); break; case mt_Export_Image: m_actions[m] = menu->addAction(Icon_Utils::SmallIcon(Icon_Utils::it_Export_Picture), i18n("Export as Picture...")); break; case mt_Externalize_Folder: m_actions[m] = menu->addAction(i18n("Externalize Folder...")); break; case mt_Fill_Color: m_actions[m] = menu->addAction(Icon_Utils::SmallIcon(Icon_Utils::it_Color_Fill), i18n("Fill Color...")); break; case mt_Final_Activity: m_actions[m] = menu->addAction(Icon_Utils::SmallIcon(Icon_Utils::it_Activity_Final), i18n("Final Activity")); break; case mt_FlipHorizontal: m_actions[m] = menu->addAction(i18n("Flip Horizontal")); break; case mt_FlipVertical: m_actions[m] = menu->addAction(i18n("Flip Vertical")); break; case mt_FloatText: m_actions[m] = menu->addAction(Icon_Utils::SmallIcon(Icon_Utils::it_Text), i18n("Text Line...")); break; case mt_ForeignKeyConstraint: m_actions[m] = menu->addAction(Icon_Utils::SmallIcon(Icon_Utils::it_Constraint_ForeignKey), i18n("Foreign Key Constraint...")); break; case mt_Fork: m_actions[m] = menu->addAction(Icon_Utils::SmallIcon(Icon_Utils::it_Fork_Join), i18n("Fork")); break; case mt_GoToStateDiagram: m_actions[m] = menu->addAction(Icon_Utils::SmallIcon(Icon_Utils::it_Remove), i18n("Go to state diagram")); break; + case mt_Hide_Destruction_Box: m_actions[m] = menu->addAction(Icon_Utils::SmallIcon(Icon_Utils::it_Message_Destroy), i18n("Hide destruction box")); break; case mt_Import_Class: m_actions[m] = menu->addAction(Icon_Utils::BarIcon(Icon_Utils::it_Import_File), i18n("Import File(s)...")); break; case mt_Import_Project: m_actions[m] = menu->addAction(Icon_Utils::BarIcon(Icon_Utils::it_Import_Project), i18n("Import from Directory...")); break; case mt_Import_from_File: m_actions[m] = menu->addAction(Icon_Utils::SmallIcon(Icon_Utils::it_Import_File), i18n("from file...")); break; case mt_Initial_Activity: m_actions[m] = menu->addAction(Icon_Utils::SmallIcon(Icon_Utils::it_InitialState), i18n("Initial Activity")); break; case mt_Initial_State: m_actions[m] = menu->addAction(Icon_Utils::SmallIcon(Icon_Utils::it_InitialState), i18n("Initial State")); break; case mt_Instance: m_actions[m] = menu->addAction(Icon_Utils::SmallIcon(Icon_Utils::it_Instance), i18nc("new instance menu item", "Instance...")); break; case mt_InstanceAttribute: m_actions[m] = menu->addAction(Icon_Utils::SmallIcon(Icon_Utils::it_Attribute_New), i18n("New Attribute...")); break; case mt_Interface: m_actions[m] = menu->addAction(Icon_Utils::SmallIcon(Icon_Utils::it_Interface), i18n("Interface")); break; case mt_InterfaceComponent: m_actions[m] = menu->addAction(Icon_Utils::SmallIcon(Icon_Utils::it_Interface_Provider), i18n("Interface")); break; case mt_InterfaceProvided: m_actions[m] = menu->addAction(Icon_Utils::SmallIcon(Icon_Utils::it_Interface_Provider), i18n("Provided interface")); break; case mt_InterfaceRequired: m_actions[m] = menu->addAction(Icon_Utils::SmallIcon(Icon_Utils::it_Interface_Requirement), i18n("Required interface")); break; case mt_Internalize_Folder: m_actions[m] = menu->addAction(i18n("Internalize Folder")); break; case mt_Junction: m_actions[m] = menu->addAction(Icon_Utils::SmallIcon(Icon_Utils::it_Junction), i18n("Junction")); break; case mt_Line_Color: m_actions[m] = menu->addAction(Icon_Utils::SmallIcon(Icon_Utils::it_Color_Line), i18n("Line Color...")); break; case mt_Logical_Folder: m_actions[m] = menu->addAction(Icon_Utils::BarIcon(Icon_Utils::it_Folder), i18n("Folder")); break; case mt_MessageAsynchronous: m_actions[m] = menu->addAction(Icon_Utils::SmallIcon(Icon_Utils::it_Message_Async), i18n("Asynchronous Message")); break; case mt_MessageCreation: m_actions[m] = menu->addAction(Icon_Utils::SmallIcon(Icon_Utils::it_Message_Creation), i18n("Creation Message")); break; + case mt_MessageDestroy: m_actions[m] = menu->addAction(Icon_Utils::SmallIcon(Icon_Utils::it_Message_Destroy), i18n("Destroy Message")); break; case mt_MessageFound: m_actions[m] = menu->addAction(Icon_Utils::SmallIcon(Icon_Utils::it_Message_Found), i18n("Found Message")); break; case mt_MessageLost: m_actions[m] = menu->addAction(Icon_Utils::SmallIcon(Icon_Utils::it_Message_Lost), i18n("Lost Message")); break; case mt_MessageSynchronous: m_actions[m] = menu->addAction(Icon_Utils::SmallIcon(Icon_Utils::it_Message_Sync), i18n("Synchronous Message")); break; case mt_New_Activity: m_actions[m] = menu->addAction(Icon_Utils::SmallIcon(Icon_Utils::it_State_Activity), i18n("Activity...")); break; case mt_New_Attribute: m_actions[m] = menu->addAction(Icon_Utils::SmallIcon(Icon_Utils::it_Attribute_New), i18n("New Attribute...")); break; case mt_New_EntityAttribute: m_actions[m] = menu->addAction(Icon_Utils::SmallIcon(Icon_Utils::it_Entity_Attribute_New), i18n("New Entity Attribute...")); break; case mt_New_EnumLiteral: m_actions[m] = menu->addAction(Icon_Utils::SmallIcon(Icon_Utils::it_Literal_New), i18n("New Literal...")); break; case mt_New_InstanceAttribute: m_actions[m] = menu->addAction(Icon_Utils::SmallIcon(Icon_Utils::it_Attribute_New), i18n("New Attribute...")); break; case mt_New_Operation: m_actions[m] = menu->addAction(Icon_Utils::SmallIcon(Icon_Utils::it_Operation_Public_New), i18n("New Operation...")); break; case mt_New_Parameter: m_actions[m] = menu->addAction(Icon_Utils::SmallIcon(Icon_Utils::it_Parameter_New), i18n("New Parameter...")); break; case mt_New_Template: m_actions[m] = menu->addAction(Icon_Utils::SmallIcon(Icon_Utils::it_Template_New), i18n("New Template...")); break; case mt_Node: m_actions[m] = menu->addAction(Icon_Utils::SmallIcon(Icon_Utils::it_Node), i18n("Node")); break; //case mt_Node: m_actions[m] = menu->addAction(Icon_Utils::SmallIcon(Icon_Utils::it_Node), i18n("Node...")); break; case mt_Note: m_actions[m] = menu->addAction(Icon_Utils::SmallIcon(Icon_Utils::it_Note), i18n("Note...")); break; //case mt_Note: m_actions[m] = menu->addAction(Icon_Utils::SmallIcon(Icon_Utils::it_Note), i18n("Note...")); break; case mt_Object: m_actions[m] = menu->addAction(Icon_Utils::SmallIcon(Icon_Utils::it_Object), i18n("Object...")); break; case mt_Object_Node: m_actions[m] = menu->addAction(Icon_Utils::SmallIcon(Icon_Utils::it_Object_Node), i18n("Object Node")); break; case mt_Open_File: m_actions[m] = menu->addAction(Icon_Utils::SmallIcon(Icon_Utils::it_File_Open), i18n("Open file")); break; case mt_Operation: m_actions[m] = menu->addAction(Icon_Utils::SmallIcon(Icon_Utils::it_Public_Method), i18n("Operation")); break; //case mt_Operation: m_actions[m] = menu->addAction(Icon_Utils::SmallIcon(Icon_Utils::it_Public_Method), i18n("Operation...")); break; case mt_Package: m_actions[m] = menu->addAction(Icon_Utils::SmallIcon(Icon_Utils::it_Package), i18n("Package...")); break; case mt_Paste: m_actions[m] = menu->addAction(Icon_Utils::SmallIcon(Icon_Utils::it_Paste), i18n("Paste")); break; case mt_Pin: m_actions[m] = menu->addAction(Icon_Utils::SmallIcon(Icon_Utils::it_Pin), i18n("Pin")); break; case mt_Port: m_actions[m] = menu->addAction(Icon_Utils::SmallIcon(Icon_Utils::it_Port), i18n("Port")); break; //case mt_Port: m_actions[m] = menu->addAction(Icon_Utils::SmallIcon(Icon_Utils::it_Port), i18n("Port...")); break; case mt_PrePostCondition: m_actions[m] = menu->addAction(Icon_Utils::SmallIcon(Icon_Utils::it_Condition_PrePost), i18n("Pre Post Condition")); break; case mt_PrimaryKeyConstraint: m_actions[m] = menu->addAction(Icon_Utils::SmallIcon(Icon_Utils::it_Constraint_PrimaryKey), i18n("Primary Key Constraint...")); break; case mt_Properties: m_actions[m] = menu->addAction(Icon_Utils::SmallIcon(Icon_Utils::it_Properties), i18n("Properties")); break; case mt_Redo: m_actions[m] = menu->addAction(Icon_Utils::SmallIcon(Icon_Utils::it_Redo), i18n("Redo")); break; case mt_Region: m_actions[m] = menu->addAction(Icon_Utils::SmallIcon(Icon_Utils::it_Region), i18n("Region")); break; case mt_Remove: m_actions[m] = menu->addAction(Icon_Utils::SmallIcon(Icon_Utils::it_Remove), i18n("Remove")); break; case mt_RemoveStateDiagram: m_actions[m] = menu->addAction(Icon_Utils::SmallIcon(Icon_Utils::it_Remove), i18n("Remove state diagram")); break; case mt_Rename: m_actions[m] = menu->addAction(Icon_Utils::SmallIcon(Icon_Utils::it_Rename), i18n("Rename...")); break; case mt_Rename_Object: insert(m, menu, i18n("Rename Object...")); break; case mt_ReturnToCombinedState: m_actions[m] = menu->addAction(Icon_Utils::SmallIcon(Icon_Utils::it_Redo), i18n("Return to combined state")); break; case mt_ReturnToClass: m_actions[m] = menu->addAction(Icon_Utils::SmallIcon(Icon_Utils::it_Redo), i18n("Return to class")); break; case mt_Reset_Label_Positions: m_actions[m] = menu->addAction(i18n("Reset Label Positions")); break; case mt_Resize: insert(m, menu, i18n("Resize")); break; case mt_SelectStateDiagram: m_actions[m] = menu->addAction(Icon_Utils::SmallIcon(Icon_Utils::it_Remove), i18n("Select state diagram")); break; case mt_Send_Signal: m_actions[m] = menu->addAction(Icon_Utils::SmallIcon(Icon_Utils::it_Send_Signal), i18n("Send Signal")); break; case mt_ShallowHistory: m_actions[m] = menu->addAction(Icon_Utils::SmallIcon(Icon_Utils::it_History_Shallow), i18n("Shallow History")); break; case mt_Show: m_actions[m] = menu->addAction(Icon_Utils::SmallIcon(Icon_Utils::it_Show), i18n("Show")); break; + case mt_Show_Destruction_Box: m_actions[m] = menu->addAction(Icon_Utils::SmallIcon(Icon_Utils::it_Message_Destroy), i18n("Show destruction box")); break; case mt_State: m_actions[m] = menu->addAction(Icon_Utils::SmallIcon(Icon_Utils::it_State), i18nc("add new state", "State...")); break; case mt_StateFork: m_actions[m] = menu->addAction(Icon_Utils::SmallIcon(Icon_Utils::it_Fork_State), i18n("Fork")); break; case mt_StateJoin: m_actions[m] = menu->addAction(Icon_Utils::SmallIcon(Icon_Utils::it_Join), i18n("Join")); break; case mt_StateTransition: m_actions[m] = menu->addAction(Icon_Utils::SmallIcon(Icon_Utils::it_State_Transition), i18n("State Transition")); break; case mt_State_Diagram: insertFromActionKey(m, menu, QLatin1String("new_state_diagram")); break; case mt_Subsystem: m_actions[m] = menu->addAction(Icon_Utils::SmallIcon(Icon_Utils::it_Subsystem), i18n("Subsystem")); break; //case mt_Subsystem: m_actions[m] = menu->addAction(Icon_Utils::SmallIcon(Icon_Utils::it_Subsystem), i18n("Subsystem...")); break; case mt_Template: m_actions[m] = menu->addAction(Icon_Utils::SmallIcon(Icon_Utils::it_Template_Class), i18n("Template")); break; //case mt_Template: m_actions[m] = menu->addAction(Icon_Utils::SmallIcon(Icon_Utils::it_Template_New), i18n("Template...")); break; case mt_Undo: m_actions[m] = menu->addAction(Icon_Utils::SmallIcon(Icon_Utils::it_Undo), i18n("Undo")); break; case mt_UniqueConstraint: m_actions[m] = menu->addAction(Icon_Utils::SmallIcon(Icon_Utils::it_Constraint_Unique), i18n("Unique Constraint...")); break; case mt_UseCase: m_actions[m] = menu->addAction(Icon_Utils::SmallIcon(Icon_Utils::it_UseCase), i18n("Use Case")); break; //case mt_UseCase: m_actions[m] = menu->addAction(Icon_Utils::SmallIcon(Icon_Utils::it_UseCase), i18n("Use Case...")); break; case mt_UseCase_Diagram: insertFromActionKey(m, menu, QLatin1String("new_use_case_diagram")); break; case mt_UseCase_Folder: m_actions[m] = menu->addAction(Icon_Utils::BarIcon(Icon_Utils::it_Folder), i18n("Folder")); break; default: uWarning() << "called on unimplemented MenuType " << toString(m); break; } } /** * Shortcut for the frequently used addAction() calls. * * @param m The MenuType for which to insert a menu item. * @param icon The icon for this action. * @param text The text for this action. */ void ListPopupMenu::insert(const MenuType m, const QIcon & icon, const QString & text) { DEBUG_AddAction(m); m_actions[m] = addAction(icon, text); } /** * Shortcut for the frequently used addAction() calls. * * @param m The MenuType for which to insert a menu item. * @param text The text for this action. * @param checkable Sets the action to checkable. */ void ListPopupMenu::insert(const MenuType m, const QString & text, const bool checkable) { insert(m, this, text, checkable); } /** * Shortcut for the frequently used addAction() calls. * * @param m The MenuType for which to insert a menu item. * @param menu The KMenu for which to insert a menu item. * @param icon The icon for this action. * @param text The text for this action. */ void ListPopupMenu::insert(const MenuType m, KMenu* menu, const QIcon & icon, const QString & text) { DEBUG_AddAction(m); m_actions[m] = menu->addAction(icon, text); } /** * Shortcut for the frequently used addAction() calls. * * @param m The MenuType for which to insert a menu item. * @param menu The KMenu for which to insert a menu item. * @param text The text for this action. * @param checkable Sets the action to checkable. */ void ListPopupMenu::insert(const MenuType m, KMenu* menu, const QString & text, const bool checkable) { DEBUG_AddAction(m); m_actions[m] = menu->addAction(text); if (checkable) { QAction* action = getAction(m); if (action) action->setCheckable(checkable); } } /** * Shortcut for inserting standard model items (Class, Interface, * Datatype, Enum, Package) as well as diagram choices. * * @param folderAndDiagrams Set this true if folders and diagram * types shall be included as choices. * @param packages Set this true if packages * shall be included as choices. */ void ListPopupMenu::insertContainerItems(bool folderAndDiagrams, bool packages) { KMenu* menu = newMenu(i18nc("new container menu", "New"), this); menu->setIcon(Icon_Utils::SmallIcon(Icon_Utils::it_New)); insertContainerItems(menu, folderAndDiagrams, packages); addMenu(menu); } /** * Shortcut for inserting standard model items (Class, Interface, * Datatype, Enum, Package) as well as diagram choices. * * @param menu Menu to add the menu entries * @param folderAndDiagrams Set this true if folders and diagram * types shall be included as choices. * @param packages Set this true if packages * shall be included as choices. */ void ListPopupMenu::insertContainerItems(KMenu* menu, bool folderAndDiagrams, bool packages) { if (folderAndDiagrams) insert(mt_Logical_Folder, menu, Icon_Utils::BarIcon(Icon_Utils::it_Folder), i18n("Folder")); insert(mt_Class, menu); insert(mt_Interface, menu); insert(mt_Datatype, menu); insert(mt_Enum, menu); if (packages) insert(mt_Package, menu); if (folderAndDiagrams) { insertFromActionKey(mt_Class_Diagram, menu, QLatin1String("new_class_diagram")); insertFromActionKey(mt_Sequence_Diagram, menu, QLatin1String("new_sequence_diagram")); insertFromActionKey(mt_Collaboration_Diagram, menu, QLatin1String("new_collaboration_diagram")); insertFromActionKey(mt_State_Diagram, menu, QLatin1String("new_state_diagram")); insertFromActionKey(mt_Activity_Diagram, menu, QLatin1String("new_activity_diagram")); } } /** * Inserts a menu item for an association related text * (such as name, role, multiplicity etc.) * * @param label The menu text. * @param mt The menu type. */ void ListPopupMenu::insertAssociationTextItem(const QString &label, MenuType mt) { insert(mt, label); insert(mt_Change_Font); insert(mt_Reset_Label_Positions); insert(mt_Properties); } /** * Convenience method to extract the ListPopupMenu type from an action. * @param action the action which was called * @return menu type enum value */ ListPopupMenu::MenuType ListPopupMenu::typeFromAction(QAction *action) { ListPopupMenu *menu = ListPopupMenu::menuFromAction(action); if (menu) { return menu->getMenuType(action); } else { uError() << "Action's data field does not contain ListPopupMenu pointer!"; return mt_Undefined; } } /** * Utility: Convert a MenuType value to an ObjectType value. */ UMLObject::ObjectType ListPopupMenu::convert_MT_OT(MenuType mt) { UMLObject::ObjectType type = UMLObject::ot_UMLObject; switch (mt) { case mt_UseCase: type = UMLObject::ot_UseCase; break; case mt_Actor: type = UMLObject::ot_Actor; break; case mt_Class: type = UMLObject::ot_Class; break; case mt_Datatype: type = UMLObject::ot_Datatype; break; case mt_Attribute: type = UMLObject::ot_Attribute; break; case mt_Interface: type = UMLObject::ot_Interface; break; case mt_Template: type = UMLObject::ot_Template; break; case mt_Enum: type = UMLObject::ot_Enum; break; case mt_EnumLiteral: type = UMLObject::ot_EnumLiteral; break; case mt_EntityAttribute: type = UMLObject::ot_EntityAttribute; break; case mt_Operation: type = UMLObject::ot_Operation; break; case mt_Category: type = UMLObject::ot_Category; break; case mt_InstanceAttribute: type = UMLObject::ot_InstanceAttribute; break; default: break; } return type; } /** * Returns the data from the given action to the given key. */ QVariant ListPopupMenu::dataFromAction(DataType key, QAction* action) { QVariant data = action->data(); QMap map = data.toMap(); return map[ListPopupMenu::toString(key)]; } /** * Convenience method to extract the ListPopupMenu pointer stored in QAction * objects belonging to ListPopupMenu. */ ListPopupMenu* ListPopupMenu::menuFromAction(QAction *action) { if (action) { QVariant value = dataFromAction(dt_MenuPointer, action); if (value.canConvert()) { return qvariant_cast(value); } } return 0; } /** * Create the 'new' menu * @return menu instance */ KMenu *ListPopupMenu::makeNewMenu() { KMenu *menu = newMenu(i18nc("new sub menu", "New"), this); menu->setIcon(Icon_Utils::SmallIcon(Icon_Utils::it_New)); return menu; } /** * Creates a popup menu for a single category Object * @param category The UMLCategory for which the category menu is created */ void ListPopupMenu::insertSubMenuCategoryType(UMLCategory* category) { KMenu* menu = newMenu(i18nc("category type sub menu", "Category Type"), this); insert(mt_DisjointSpecialisation, menu, i18n("Disjoint(Specialisation)"), CHECKABLE); insert(mt_OverlappingSpecialisation, menu, i18n("Overlapping(Specialisation)"), CHECKABLE); insert(mt_Union, menu, i18n("Union"), CHECKABLE); setActionChecked(mt_DisjointSpecialisation, category->getType()==UMLCategory::ct_Disjoint_Specialisation); setActionChecked(mt_OverlappingSpecialisation, category->getType()==UMLCategory::ct_Overlapping_Specialisation); setActionChecked(mt_Union, category->getType()==UMLCategory::ct_Union); addMenu(menu); } /** * Get the action from the menu type as index. */ QAction* ListPopupMenu::getAction(MenuType idx) { return m_actions.value(idx, 0); } // /** // * Get the MenuType from the action. // */ // ListPopupMenu::MenuType ListPopupMenu::getMenuType(KAction* action) // { // return m_actions.key(action); // } /** * Get the MenuType from the action. */ ListPopupMenu::MenuType ListPopupMenu::getMenuType(QAction* action) { QList keyList = m_actions.keys(action); if (keyList.empty() || /* all key-value pairs are unique*/ keyList.count() > 1) { return mt_Undefined; } else { // we return the first (only) value return keyList.first(); } } /** * Checks the action item. * * @param idx The MenuType for which to check the menu item. * @param value The value. */ void ListPopupMenu::setActionChecked(MenuType idx, bool value) { QAction* action = getAction(idx); if (action && action->isCheckable()) { action->setChecked(value); } else { DEBUG(DBG_SRC) << "called on unknown MenuType " << toString(idx); } } /** * Enables the action item. * * @param idx The MenuType for which to enable the menu item. * @param value The value. */ void ListPopupMenu::setActionEnabled(MenuType idx, bool value) { QAction* action = getAction(idx); if (action) { action->setEnabled(value); } else { DEBUG(DBG_SRC) << "called on unknown MenuType " << toString(idx); } } /** * Sets up actions added to the ListPopupMenu to have their data field set to * pointer to this ListPopupMenu object, so that this menu pointer can be * retrieved in UMLWidget::slotMenuSelection * * @note This might seem like an ugly hack, but this is the only solution which * helped in avoiding storage of ListPopupMenu pointer in each UMLWidget. */ void ListPopupMenu::setupActionsData() { foreach (QAction *action, m_actions) { QMap map = action->data().toMap(); map[toString(dt_MenuPointer)] = qVariantFromValue(this); action->setData(QVariant(map)); } } /** * Convert enum MenuType to string. */ QString ListPopupMenu::toString(MenuType menu) { return QLatin1String(ENUM_NAME(ListPopupMenu, MenuType, menu)); } /** * Convert enum DataType to string. */ QString ListPopupMenu::toString(DataType data) { return QLatin1String(ENUM_NAME(ListPopupMenu, DataType, data)); } //QList &ListPopupMenu::debugActions() //{ // return d->debugActions; //} /** * dump collected actions * @param title optional menu title */ void ListPopupMenu::dumpActions(const QString &title) { qDebug().nospace() << title; foreach(DebugMenu e, d->debugActions) { if (!e.menu.isEmpty()) qDebug().nospace() << " " << e.menu; else qDebug().nospace() << " " << toString(e.m); } } diff --git a/umbrello/menus/listpopupmenu.h b/umbrello/menus/listpopupmenu.h index 36f9cc794..657358633 100644 --- a/umbrello/menus/listpopupmenu.h +++ b/umbrello/menus/listpopupmenu.h @@ -1,318 +1,321 @@ /*************************************************************************** * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * copyright (C) 2003-2020 * * Umbrello UML Modeller Authors * ***************************************************************************/ #ifndef LISTPOPUPMENU_H #define LISTPOPUPMENU_H #include "basictypes.h" #include "umllistviewitem.h" #include "umlobject.h" #include "widgetbase.h" #include #include class UMLCategory; class ListPopupMenuPrivate; /** * A popup menu that depending on what type is set to will * display a different menu. * * The data field of actions is used to carry user data * between objects. Because different types of data are used, a map is loaded * with an enum as key (see @ref DataType). * * @short Displays a popup menu. * @author Paul Hensgen * Bugs and comments to umbrello-devel@kde.org or https://bugs.kde.org */ class ListPopupMenu : public KMenu { Q_OBJECT Q_ENUMS(MenuType) Q_ENUMS(DataType) public: enum MenuType ///< This type hosts all possible menu entry types { mt_Model, // diagrams mt_Activity_Diagram, mt_Class_Diagram, mt_Collaboration_Diagram, mt_Component_Diagram, mt_Deployment_Diagram, mt_EntityRelationship_Diagram, mt_Sequence_Diagram, mt_State_Diagram, mt_UseCase_Diagram, mt_Logical_Folder, mt_UseCase_Folder, mt_Component_Folder, mt_Deployment_Folder, mt_EntityRelationship_Folder, // widgets, uml objects mt_Accept_Signal, mt_Accept_Time_Event, mt_Activity, mt_Activity_Transition, mt_Actor, mt_Artifact, mt_Attribute, mt_Branch, mt_Category, mt_CheckConstraint, mt_Choice, mt_Class, mt_CombinedState, mt_Component, mt_Datatype, mt_DeepHistory, mt_DisjointSpecialisation, mt_EditCombinedState, mt_End_Activity, mt_End_State, mt_Entity, mt_EntityAttribute, mt_Enum, mt_EnumLiteral, mt_Exception, mt_FloatText, mt_Final_Activity, mt_ForeignKeyConstraint, mt_Fork, mt_GoToStateDiagram, mt_Initial_Activity, mt_Initial_State, mt_Invoke_Activity, mt_Instance, mt_InstanceAttribute, mt_Interface, mt_InterfaceComponent, mt_InterfaceProvided, mt_InterfaceRequired, mt_Junction, mt_MessageAsynchronous, mt_MessageCreation, + mt_MessageDestroy, mt_MessageFound, mt_MessageLost, mt_MessageSynchronous, mt_Node, mt_Note, mt_Object, mt_Object_Node, mt_Operation, mt_OverlappingSpecialisation, mt_Package, mt_Param_Activity, mt_Pin, mt_Port, mt_PrePostCondition, mt_PrimaryKeyConstraint, mt_Region, mt_SelectStateDiagram, mt_Send_Signal, mt_ShallowHistory, mt_State, mt_StateFork, mt_StateJoin, mt_StateTransition, mt_Subsystem, mt_Template, mt_Union, mt_UniqueConstraint, mt_UseCase, // new from dialogs mt_New_Activity, mt_New_Attribute, mt_New_EntityAttribute, mt_New_EnumLiteral, mt_New_InstanceAttribute, mt_New_Operation, mt_New_Parameter, mt_New_Template, mt_RemoveStateDiagram, mt_ReturnToClass, mt_ReturnToCombinedState, // selection // visual properties mt_Hide_Attribute_Signature_Selection, mt_Hide_Attributes_Selection, // Unset visual property on multiple widgets + mt_Hide_Destruction_Box, mt_Hide_NonPublic_Selection, // Could be named "show public only" mt_Hide_Operation_Signature_Selection, mt_Hide_Operations_Selection, // Hide operations mt_Hide_Packages_Selection, mt_Hide_Stereotypes_Selection, mt_Hide_Visibility_Selection, mt_Show_Attribute_Signature, mt_Show_Attribute_Signature_Selection, mt_Show_Attributes_Selection, // Set visual property on multiple widgets mt_Show_Attributes, // Toggle visual property on a widget + mt_Show_Destruction_Box, mt_Show_Documentation, mt_Show_NonPublic_Selection, // Could be named "hide public only" (crazy!) mt_Show_Operation_Signature, mt_Show_Operation_Signature_Selection, mt_Show_Operations_Selection, // Show operations mt_Show_Operations, // Toggle 'show operations' mt_Show_Packages, // etc... mt_Show_Packages_Selection, mt_Show_Public_Only, mt_Show_Stereotypes, mt_Show_Stereotypes_Selection, mt_Show_Visibility_Selection, mt_Visibility, // other mt_DrawAsCircle, mt_ChangeToClass, mt_ChangeToInterface, mt_ChangeToPackage, mt_Open_File, mt_Rename_Object, mt_Select_Operation, mt_Properties, mt_Rename, mt_NameAsTooltip, mt_Show, mt_Delete, mt_Export_Image, mt_Import_Class, mt_Import_Project, mt_Cut, mt_Copy, mt_Paste, mt_Clear, mt_Redo, mt_Undo, mt_Reset_Label_Positions, mt_Line_Color, mt_Line_Color_Selection, mt_Fill_Color, mt_Fill_Color_Selection, mt_Use_Fill_Color, mt_Set_Use_Fill_Color_Selection, mt_Unset_Use_Fill_Color_Selection, mt_Rename_MultiA, mt_Rename_MultiB, mt_Rename_Name, mt_Rename_RoleAName, mt_Rename_RoleBName, mt_Change_Font, mt_Change_Font_Selection, mt_SnapToGrid, mt_SnapComponentSizeToGrid, mt_ShowDocumentationIndicator, mt_ShowSnapGrid, mt_AutoResize, mt_Resize, mt_Up, mt_Down, mt_FlipHorizontal, mt_FlipVertical, mt_Add_Point, mt_Delete_Point, mt_Auto_Layout_Spline, mt_Expand_All, // Expand all items in the list mt_Collapse_All, // Collapse all items in the list mt_Refactoring, mt_ViewCode, // view code document contents mt_Clone, // Create a deep copy of the object. mt_Externalize_Folder, // Mark folder for saving as separate submodel mt_Internalize_Folder, // Reintegrate separate submodel into main model mt_AddInteractionOperand, // add a dash line to an alternative or a parallel combined fragment mt_Apply_Layout, // apply automatically created layout mt_Apply_Layout1, // apply automatically created layout mt_Apply_Layout2, // apply automatically created layout mt_Apply_Layout3, // apply automatically created layout mt_Apply_Layout4, // apply automatically created layout mt_Apply_Layout5, // apply automatically created layout mt_Apply_Layout6, // apply automatically created layout mt_Apply_Layout7, // apply automatically created layout mt_Apply_Layout8, // apply automatically created layout mt_Apply_Layout9, // apply automatically created layout mt_LayoutDirect, // associations with direct lines mt_LayoutSpline, // associations with slines mt_LayoutOrthogonal, // associations with orthogonal lines mt_LayoutPolyline, // associations with polylines mt_Align_Right, mt_Align_Left, mt_Align_Top, mt_Align_Bottom, mt_Align_VerticalMiddle, mt_Align_HorizontalMiddle, mt_Align_VerticalDistribute, mt_Align_HorizontalDistribute, mt_Import_from_File, mt_Remove, // add new entries above mt_Undefined = - 1 }; static QString toString(MenuType menu); enum DataType ///< Key value of the data map used in actions. { dt_MenuPointer, dt_ApplyLayout }; static QString toString(DataType data); static QVariant dataFromAction(DataType key, QAction* action); ListPopupMenu(QWidget* parent = 0); virtual ~ListPopupMenu(); static UMLObject::ObjectType convert_MT_OT(MenuType mt); static ListPopupMenu* menuFromAction(QAction *action); static MenuType typeFromAction(QAction *action); QAction* getAction(MenuType idx); void setActionEnabled(MenuType idx, bool value); MenuType getMenuType(QAction* action); void dumpActions(const QString &title); KMenu *newMenu(const QString &title, QWidget *widget); void addMenu(KMenu *menu); protected: void insert(MenuType m); void insertFromActionKey(const MenuType m, KMenu *menu, const QString &action); void insert(const MenuType m, KMenu* menu); void insert(const MenuType m, KMenu* menu, const QIcon & icon, const QString & text); void insert(const MenuType m, KMenu* menu, const QString & text, const bool checkable = false); void insert(const MenuType m, const QIcon & icon, const QString & text); void insert(const MenuType m, const QString & text, const bool checkable = false); void insertContainerItems(bool folderAndDiagrams, bool packages=true); void insertContainerItems(KMenu* menu, bool folderAndDiagrams, bool packages); void insertAssociationTextItem(const QString &label, MenuType mt); KMenu *makeNewMenu(); void insertSubMenuCategoryType(UMLCategory *category); void setActionChecked(MenuType idx, bool value); void setupActionsData(); QHash m_actions; ListPopupMenuPrivate *d; }; /// Need this for ability to store ListPopupMenu* in a QVariant Q_DECLARE_METATYPE(ListPopupMenu*) #endif diff --git a/umbrello/menus/umlscenepopupmenu.cpp b/umbrello/menus/umlscenepopupmenu.cpp index 4199f6bfa..4219984f0 100644 --- a/umbrello/menus/umlscenepopupmenu.cpp +++ b/umbrello/menus/umlscenepopupmenu.cpp @@ -1,212 +1,213 @@ /*************************************************************************** * 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) 2018-2020 * * Umbrello UML Modeller Authors * ***************************************************************************/ #include "umlscenepopupmenu.h" // app includes #include "debug_utils.h" #include "layoutgenerator.h" #include "uml.h" #include "umlscene.h" // kde includes #include UMLScenePopupMenu::UMLScenePopupMenu(QWidget *parent, UMLScene *scene) : ListPopupMenu(parent), m_scene(scene) { const bool CHECKABLE = true; Uml::DiagramType::Enum type = scene->type(); switch(type) { case Uml::DiagramType::UseCase: case Uml::DiagramType::Class: case Uml::DiagramType::Object: case Uml::DiagramType::State: case Uml::DiagramType::Activity: case Uml::DiagramType::Component: case Uml::DiagramType::Deployment: case Uml::DiagramType::EntityRelationship: case Uml::DiagramType::Sequence: case Uml::DiagramType::Collaboration: if (type == Uml::DiagramType::State && scene->widgetLink()) { if (scene->widgetLink()->isStateWidget()) { insert(mt_ReturnToCombinedState); addSeparator(); } else if (scene->widgetLink()->isClassWidget()) { insert(mt_ReturnToClass); addSeparator(); } } insertSubMenuNew(type); addSeparator(); insert(mt_Undo); insert(mt_Redo); addSeparator(); insert(mt_Cut); insert(mt_Copy); insert(mt_Paste); addSeparator(); insert(mt_Clear, Icon_Utils::SmallIcon(Icon_Utils::it_Clear), i18n("Clear Diagram")); insert(mt_Export_Image); addSeparator(); insertLayoutItems(); insert(mt_SnapToGrid, i18n("Snap to Grid"), CHECKABLE); setActionChecked(mt_SnapToGrid, scene->snapToGrid()); insert(mt_SnapComponentSizeToGrid, i18n("Snap Component Size to Grid"), CHECKABLE); setActionChecked(mt_SnapComponentSizeToGrid, scene->snapComponentSizeToGrid()); insert(mt_ShowSnapGrid, i18n("Show Grid"), CHECKABLE); setActionChecked(mt_ShowSnapGrid, scene->isSnapGridVisible()); insert(mt_ShowDocumentationIndicator, i18n("Show Documentation Indicator"), CHECKABLE); setActionChecked(mt_ShowDocumentationIndicator, scene->isShowDocumentationIndicator()); insert(mt_Properties); break; #ifndef CHECK_SWITCH default: break; #endif } bool bCutState = UMLApp::app()->isCutCopyState(); setActionEnabled(mt_Undo, UMLApp::app()->isUndoActionEnabled()); setActionEnabled(mt_Redo, UMLApp::app()->isRedoActionEnabled()); setActionEnabled(mt_Cut, bCutState); setActionEnabled(mt_Copy, bCutState); setActionEnabled(mt_Paste, UMLApp::app()->isPasteState()); setupActionsData(); if (IS_DEBUG_ENABLED(DBG_SRC)) dumpActions(Uml::DiagramType::toString(type)); } void UMLScenePopupMenu::insertLayoutItems() { QList types; types << mt_Apply_Layout << mt_Apply_Layout1 << mt_Apply_Layout2 << mt_Apply_Layout3 << mt_Apply_Layout4 << mt_Apply_Layout5 << mt_Apply_Layout6 << mt_Apply_Layout7 << mt_Apply_Layout8 << mt_Apply_Layout9; LayoutGenerator generator; if (generator.isEnabled()) { QHash configFiles; if (LayoutGenerator::availableConfigFiles(m_scene, configFiles)) { int i = 0; foreach(const QString &key, configFiles.keys()) { // krazy:exclude=foreach if (i >= types.size()) break; if (key == QLatin1String("export") && !Settings::optionState().autoLayoutState.showExportLayout) continue; insert(types[i], QPixmap(), i18n("apply '%1'", configFiles[key])); QAction* action = getAction(types[i]); QMap map = action->data().toMap(); map[toString(dt_ApplyLayout)] = QVariant(key); action->setData(QVariant(map)); i++; } addSeparator(); } } else { uWarning() << "Could not add autolayout entries because graphviz installation has not been found."; } } void UMLScenePopupMenu::insertSubMenuNew(Uml::DiagramType::Enum type, KMenu *menu) { if (!menu) { menu = makeNewMenu(); } switch(type) { case Uml::DiagramType::UseCase: insert(mt_Actor, menu); insert(mt_UseCase, menu); break; case Uml::DiagramType::Class: insert(mt_Import_from_File, menu); insert(mt_Class, menu); insert(mt_Interface, menu); insert(mt_Datatype, menu); insert(mt_Enum, menu); insert(mt_Package, menu); break; case Uml::DiagramType::Object: insert(mt_Instance, menu); break; case Uml::DiagramType::State: insert(mt_Initial_State, menu); insert(mt_State, menu); insert(mt_End_State, menu); insert(mt_Junction, menu); insert(mt_DeepHistory, menu); insert(mt_ShallowHistory, menu); insert(mt_Choice, menu); insert(mt_StateFork, menu); insert(mt_StateJoin, menu); insert(mt_CombinedState, menu); break; case Uml::DiagramType::Activity: insert(mt_Initial_Activity, menu); insert(mt_Activity, menu); insert(mt_End_Activity, menu); insert(mt_Final_Activity, menu); insert(mt_Branch, menu); insert(mt_Fork, menu); insert(mt_Invoke_Activity, menu); insert(mt_Param_Activity, menu); insert(mt_Activity_Transition, menu); insert(mt_Exception, menu); insert(mt_PrePostCondition, menu); insert(mt_Send_Signal, menu); insert(mt_Accept_Signal, menu); insert(mt_Accept_Time_Event, menu); insert(mt_Region, menu); insert(mt_Pin, menu); insert(mt_Object_Node, menu); break; case Uml::DiagramType::Component: insert(mt_Subsystem, menu); insert(mt_Component, menu); insert(mt_InterfaceComponent, menu); insert(mt_Artifact, menu); break; case Uml::DiagramType::Deployment: insert(mt_Node, menu); break; case Uml::DiagramType::EntityRelationship: insert(mt_Entity, menu); insert(mt_Category, menu); break; case Uml::DiagramType::Sequence: insert(mt_Import_from_File, menu); insert(mt_Object, menu); if (m_scene->onWidgetLine(m_scene->pos())) { insert(mt_MessageCreation, menu); + insert(mt_MessageDestroy, menu); insert(mt_MessageSynchronous, menu); insert(mt_MessageAsynchronous, menu); insert(mt_MessageLost, menu); } else if (m_scene->widgetOnDiagram(WidgetBase::wt_Object)){ insert(mt_MessageFound, menu); } break; case Uml::DiagramType::Collaboration: insert(mt_Object, menu); break; default: delete menu; return; } insert(mt_Note, menu); insert(mt_FloatText, menu); addMenu(menu); } diff --git a/umbrello/menus/widgetbasepopupmenu.cpp b/umbrello/menus/widgetbasepopupmenu.cpp index be788f189..f11b3f99e 100644 --- a/umbrello/menus/widgetbasepopupmenu.cpp +++ b/umbrello/menus/widgetbasepopupmenu.cpp @@ -1,732 +1,737 @@ /*************************************************************************** * 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) 2018-2020 * * Umbrello UML Modeller Authors * ***************************************************************************/ #include "widgetbasepopupmenu.h" // app includes #include "activitywidget.h" #include "category.h" #include "classifier.h" #include "combinedfragmentwidget.h" #include "debug_utils.h" #include "entitywidget.h" #include "floatingtextwidget.h" #include "forkjoinwidget.h" #include "interfacewidget.h" #include "notewidget.h" #include "objectwidget.h" #include "objectnodewidget.h" #include "pinportbase.h" #include "statewidget.h" #include "uml.h" #include "umldoc.h" #include "umllistview.h" #include "umlscene.h" // kde includes #include static const bool CHECKABLE = true; /** * Constructs the popup menu for a scene widget. * * @param parent The parent to ListPopupMenu. * @param widget The WidgetBase to represent a menu for. * @param multi True if multiple items are selected. * @param uniqueType The type of widget shared by all selected widgets */ WidgetBasePopupMenu::WidgetBasePopupMenu(QWidget * parent, WidgetBase * widget, bool multi, WidgetBase::WidgetType uniqueType) : ListPopupMenu(parent) { if (!widget) return; if (multi) { insertMultiSelectionMenu(uniqueType); } else { insertSingleSelectionMenu(widget); } bool bCutState = UMLApp::app()->isCutCopyState(); setActionEnabled(mt_Cut, bCutState); setActionEnabled(mt_Copy, bCutState); bool pasteAvailable = false; if (widget->isNoteWidget() && UMLApp::app()->listView()->startedCopy()) { NoteWidget::s_pCurrentNote = widget->asNoteWidget(); pasteAvailable = true; } setActionEnabled(mt_Paste, pasteAvailable); setActionChecked(mt_AutoResize, widget->autoResize()); setupActionsData(); } /** * Creates the "Show" submenu in the context menu of one classifier widget */ void WidgetBasePopupMenu::makeClassifierShowPopup(ClassifierWidget *c) { WidgetBase::WidgetType type = c->baseType(); KMenu* show = newMenu(i18n("Show"), this); show->setIcon(Icon_Utils::SmallIcon(Icon_Utils::it_Show)); #ifdef ENABLE_WIDGET_SHOW_DOC insert(mt_Show_Documentation, show, i18n("Documentation"), CHECKABLE); setActionChecked(mt_Show_Documentation, c->visualProperty(ClassifierWidget::ShowDocumentation)); #endif if (type == WidgetBase::wt_Class) { insert(mt_Show_Attributes, show, i18n("Attributes"), CHECKABLE); setActionChecked(mt_Show_Attributes, c->visualProperty(ClassifierWidget::ShowAttributes)); } insert(mt_Show_Operations, show, i18n("Operations"), CHECKABLE); setActionChecked(mt_Show_Operations, c->visualProperty(ClassifierWidget::ShowOperations)); insert(mt_Show_Public_Only, show, i18n("Public Only"), CHECKABLE); setActionChecked(mt_Show_Public_Only, c->visualProperty(ClassifierWidget::ShowPublicOnly)); insert(mt_Visibility, show, i18n("Visibility"), CHECKABLE); setActionChecked(mt_Visibility, c->visualProperty(ClassifierWidget::ShowVisibility)); insert(mt_Show_Operation_Signature, show, i18n("Operation Signature"), CHECKABLE); bool sig = (c->operationSignature() == Uml::SignatureType::SigNoVis || c->operationSignature() == Uml::SignatureType::ShowSig); setActionChecked(mt_Show_Operation_Signature, sig); if (type == WidgetBase::wt_Class) { insert(mt_Show_Attribute_Signature, show, i18n("Attribute Signature"), CHECKABLE); sig = (c->attributeSignature() == Uml::SignatureType::SigNoVis || c->attributeSignature() == Uml::SignatureType::ShowSig); setActionChecked(mt_Show_Attribute_Signature, sig); } insert(mt_Show_Packages, show, i18n("Package"), CHECKABLE); setActionChecked(mt_Show_Packages, c->visualProperty(ClassifierWidget::ShowPackage)); insert(mt_Show_Stereotypes, show, i18n("Stereotype"), CHECKABLE); setActionChecked(mt_Show_Stereotypes, c->visualProperty(ClassifierWidget::ShowStereotype)); addMenu(show); } /** * Creates the "Show" submenu the context menu of multiple classifier widgets */ void WidgetBasePopupMenu::makeMultiClassifierShowPopup(WidgetBase::WidgetType type) { KMenu* show = newMenu(i18n("Show"), this); show->setIcon(Icon_Utils::SmallIcon(Icon_Utils::it_Show)); KMenu* attributes = newMenu(i18n("Attributes"), this); if (type == WidgetBase::wt_Class) { insert(mt_Show_Attributes_Selection, attributes, i18n("Show")); insert(mt_Hide_Attributes_Selection, attributes, i18n("Hide")); insert(mt_Show_Attribute_Signature_Selection, attributes, i18n("Show Signatures")); insert(mt_Hide_Attribute_Signature_Selection, attributes, i18n("Hide Signatures")); } show->addMenu(attributes); KMenu* operations = newMenu(i18n("Operations"), this); insert(mt_Show_Operations_Selection, operations, i18n("Show")); insert(mt_Hide_Operations_Selection, operations, i18n("Hide")); insert(mt_Show_Operation_Signature_Selection, operations, i18n("Show Signatures")); insert(mt_Hide_Operation_Signature_Selection, operations, i18n("Hide Signatures")); show->addMenu(operations); KMenu* visibility = newMenu(i18n("Visibility"), this); insert(mt_Show_Visibility_Selection, visibility, i18n("Show")); insert(mt_Hide_Visibility_Selection, visibility, i18n("Hide")); insert(mt_Hide_NonPublic_Selection, visibility, i18n("Hide Non-public members")); insert(mt_Show_NonPublic_Selection, visibility, i18n("Show Non-public members")); show->addMenu(visibility); KMenu* packages = newMenu(i18n("Packages"), this); insert(mt_Show_Packages_Selection, packages, i18n("Show")); insert(mt_Hide_Packages_Selection, packages, i18n("Hide")); show->addMenu(packages); if (type == WidgetBase::wt_Class) { KMenu* stereotypes = newMenu(i18n("Stereotypes"), this); insert(mt_Show_Stereotypes_Selection, stereotypes, i18n("Show")); insert(mt_Hide_Stereotypes_Selection, stereotypes, i18n("Hide")); show->addMenu(stereotypes); } addMenu(show); } /** * Inserts the menu actions for a widget * * @param widget widget to generate the menu for */ void WidgetBasePopupMenu::insertSingleSelectionMenu(WidgetBase* widget) { WidgetBase::WidgetType type = widget->baseType(); switch (type) { case WidgetBase::wt_Actor: case WidgetBase::wt_UseCase: insertSubMenuNew(type); insertSubMenuColor(widget->useFillColor()); insertStdItems(true, type); insert(mt_Rename); insert(mt_Change_Font); insert(mt_Properties); break; case WidgetBase::wt_Category: { insertSubMenuNew(type); insertSubMenuCategoryType(widget->umlObject()->asUMLCategory()); insertSubMenuColor(widget->useFillColor()); insertStdItems(true, type); insert(mt_Rename); insert(mt_Change_Font); break; } case WidgetBase::wt_Class: { ClassifierWidget* c = widget->asClassifierWidget(); if (!c) break; insertSubMenuNew(type); makeClassifierShowPopup(c); insertSubMenuColor(c->useFillColor()); addSeparator(); if (c->linkedDiagram()) insert(mt_GoToStateDiagram); if (UMLApp::app()->document()->views(Uml::DiagramType::State).size() > 0) insert(mt_SelectStateDiagram); if (c->linkedDiagram()) insert(mt_RemoveStateDiagram); insertStdItems(true, type); insert(mt_Rename); insert(mt_Change_Font); if (c->umlObject() && c->umlObject()->stereotype() == QLatin1String("class-or-package")) { insert(mt_ChangeToClass, i18n("Change into Class")); insert(mt_ChangeToPackage, i18n("Change into Package")); } else { insert(mt_Refactoring, Icon_Utils::SmallIcon(Icon_Utils::it_Refactor), i18n("Refactor")); insert(mt_ViewCode, Icon_Utils::SmallIcon(Icon_Utils::it_View_Code), i18n("View Code")); UMLClassifier *umlc = c->classifier(); if (umlc->isAbstract() && umlc->getAttributeList().size() == 0) insert(mt_ChangeToInterface, i18n("Change into Interface")); } insert(mt_Properties); } break; case WidgetBase::wt_Interface: { InterfaceWidget* c = widget->asInterfaceWidget(); if (!c) break; insertSubMenuNew(type); makeClassifierShowPopup(c); insertSubMenuColor(c->useFillColor()); insertStdItems(true, type); insert(mt_Rename); insert(mt_Change_Font); insert(mt_DrawAsCircle, i18n("Draw as Circle"), CHECKABLE); setActionChecked(mt_DrawAsCircle, c->visualProperty(ClassifierWidget::DrawAsCircle)); insert(mt_ChangeToClass, i18n("Change into Class")); insert(mt_Properties); } break; case WidgetBase::wt_Instance: insertSubMenuNew(type); insert(mt_InstanceAttribute); insert(mt_Rename_Object); insert(mt_Rename, i18n("Rename Class...")); insertStdItems(true, type); insert(mt_Change_Font); insert(mt_Properties); break; case WidgetBase::wt_Enum: insertSubMenuNew(type); insertSubMenuColor(widget->useFillColor()); insertStdItems(true, type); insert(mt_Rename); insert(mt_Change_Font); insert(mt_Properties); break; case WidgetBase::wt_Entity: insertSubMenuNew(type); insertSubMenuShowEntity(widget->asEntityWidget()); insertSubMenuColor(widget->useFillColor()); insertStdItems(true, type); insert(mt_Rename); insert(mt_Change_Font); insert(mt_Properties); break; case WidgetBase::wt_Datatype: case WidgetBase::wt_Package: case WidgetBase::wt_Component: case WidgetBase::wt_Node: case WidgetBase::wt_Artifact: insertSubMenuNew(type); insertSubMenuColor(widget->useFillColor()); insertStdItems(false, type); insert(mt_Rename); insert(mt_Change_Font); insert(mt_Properties); break; case WidgetBase::wt_Port: insertSubMenuNew(type); insertSubMenuColor(widget->useFillColor()); insertStdItems(false); insert(mt_NameAsTooltip, i18n("Name as Tooltip"), true); { PinPortBase *pW = static_cast(widget); FloatingTextWidget *ft = pW->floatingTextWidget(); if (ft == 0) m_actions[mt_NameAsTooltip]->setChecked(true); } insert(mt_Delete); insert(mt_Properties); break; case WidgetBase::wt_Object: //Used for sequence diagram and collaboration diagram widgets insertSubMenuNew(type); insertSubMenuColor(widget->useFillColor()); if (widget->umlScene() && widget->umlScene()->isSequenceDiagram()) { addSeparator(); MenuType tabUp = mt_Up; insert(mt_Up, Icon_Utils::SmallIcon(Icon_Utils::it_Arrow_Up), i18n("Move Up")); insert(mt_Down, Icon_Utils::SmallIcon(Icon_Utils::it_Arrow_Down), i18n("Move Down")); if (!(static_cast(widget))->canTabUp()) { setActionEnabled(tabUp, false); } } insertStdItems(true, type); insert(mt_Rename_Object); insert(mt_Rename, i18n("Rename Class...")); + if (widget->asObjectWidget() && + widget->umlScene() && + widget->umlScene()->isSequenceDiagram()) { + insert(widget->asObjectWidget()->showDestruction() ? mt_Hide_Destruction_Box : mt_Show_Destruction_Box); + } insert(mt_Change_Font); insert(mt_Properties); break; case WidgetBase::wt_Message: insertSubMenuNew(type); insertSubMenuColor(widget->useFillColor()); insertStdItems(false, type); //insert(mt_Change_Font); //insert(mt_Operation, Icon_Utils::SmallIcon(Icon_Utils::it_Operation_New), i18n("New Operation...")); //insert(mt_Select_Operation, i18n("Select Operation...")); break; case WidgetBase::wt_Note: insertSubMenuNew(type); insertSubMenuColor(widget->useFillColor()); addSeparator(); insert(mt_Cut); insert(mt_Copy); insert(mt_Paste); insert(mt_Clear, Icon_Utils::SmallIcon(Icon_Utils::it_Clear), i18nc("clear note", "Clear")); addSeparator(); insert(mt_Rename, i18n("Change Text...")); insert(mt_Resize); insert(mt_AutoResize, i18n("Auto resize"), CHECKABLE); insert(mt_Delete); insert(mt_Change_Font); insert(mt_Properties); break; case WidgetBase::wt_Box: insertSubMenuNew(type); insertStdItems(false, type); insert(mt_Line_Color); break; case WidgetBase::wt_State: { StateWidget* pState = static_cast< StateWidget *>(widget); switch (pState->stateType()) { case StateWidget::Combined: insert(mt_EditCombinedState); addSeparator(); break; default: break; } insertSubMenuNew(type); insertSubMenuColor(widget->useFillColor()); insertStdItems(true, type); switch (pState->stateType()) { case StateWidget::Combined: insert(mt_Change_Font); insert(mt_Properties); break; case StateWidget::Normal: insert(mt_Rename, i18n("Change State Name...")); insert(mt_Change_Font); insert(mt_Properties); break; case StateWidget::Fork: case StateWidget::Join: insert(pState->drawVertical() ? mt_FlipHorizontal : mt_FlipVertical); break; default: break; } } break; case WidgetBase::wt_ForkJoin: insertSubMenuNew(type); { ForkJoinWidget *pForkJoin = static_cast(widget); insert(pForkJoin->orientation() == Qt::Vertical ? mt_FlipHorizontal : mt_FlipVertical); insert(mt_Fill_Color); } break; case WidgetBase::wt_Activity: insertSubMenuNew(type); { ActivityWidget* pActivity = static_cast(widget); if(pActivity->activityType() == ActivityWidget::Normal || pActivity->activityType() == ActivityWidget::Invok || pActivity->activityType() == ActivityWidget::Param) { insertSubMenuColor(widget->useFillColor()); } insertStdItems(false, type); if(pActivity->activityType() == ActivityWidget::Normal || pActivity->activityType() == ActivityWidget::Invok || pActivity->activityType() == ActivityWidget::Param) { insert(mt_Rename, i18n("Change Activity Name...")); insert(mt_Change_Font); insert(mt_Properties); } } break; case WidgetBase::wt_ObjectNode: insertSubMenuNew(type); { ObjectNodeWidget* objWidget = static_cast(widget); if (objWidget->objectNodeType() == ObjectNodeWidget::Buffer || objWidget->objectNodeType() == ObjectNodeWidget::Data || objWidget->objectNodeType() == ObjectNodeWidget::Flow) { insertSubMenuColor(widget->useFillColor()); } insertStdItems(false, type); if (objWidget->objectNodeType() == ObjectNodeWidget::Buffer || objWidget->objectNodeType() == ObjectNodeWidget::Data || objWidget->objectNodeType() == ObjectNodeWidget::Flow) { insert(mt_Rename, i18n("Change Object Node Name...")); insert(mt_Change_Font); insert(mt_Properties); } } break; case WidgetBase::wt_Pin: case WidgetBase::wt_Signal: case WidgetBase::wt_FloatingDashLine: case WidgetBase::wt_Precondition: insertSubMenuNew(type); insertSubMenuColor(widget->useFillColor()); addSeparator(); insert(mt_Cut); insert(mt_Copy); insert(mt_Paste); insert(mt_Clear, Icon_Utils::SmallIcon(Icon_Utils::it_Clear), i18nc("clear precondition", "Clear")); addSeparator(); insert(mt_Rename, i18n("Change Text...")); if (type == WidgetBase::wt_Pin) { insert(mt_NameAsTooltip, i18n("Name as Tooltip"), true); PinPortBase *pW = static_cast(widget); FloatingTextWidget *ft = pW->floatingTextWidget(); if (ft == 0) m_actions[mt_NameAsTooltip]->setChecked(true); } insert(mt_Delete); insert(mt_Change_Font); if (type == WidgetBase::wt_Precondition) insert(mt_Properties); break; case WidgetBase::wt_CombinedFragment: insertSubMenuNew(type); // for alternative and parallel combined fragments if ((static_cast(widget))->combinedFragmentType() == CombinedFragmentWidget::Alt || (static_cast(widget))->combinedFragmentType() == CombinedFragmentWidget::Par) { insert(mt_AddInteractionOperand, i18n("Add Interaction Operand")); addSeparator(); } insertSubMenuColor(widget->useFillColor()); addSeparator(); insert(mt_Cut); insert(mt_Copy); insert(mt_Paste); insert(mt_Clear, Icon_Utils::SmallIcon(Icon_Utils::it_Clear), i18nc("clear combined fragment", "Clear")); addSeparator(); insert(mt_Rename, i18n("Change Text...")); insert(mt_Delete); insert(mt_Change_Font); insert(mt_Properties); break; case WidgetBase::wt_Text: insertSubMenuNew(type); switch((static_cast(widget))->textRole()) { case Uml::TextRole::MultiB: insertAssociationTextItem(i18n("Change Multiplicity..."), mt_Rename_MultiB); break; case Uml::TextRole::MultiA: insertAssociationTextItem(i18n("Change Multiplicity..."), mt_Rename_MultiA); break; case Uml::TextRole::Name: insertAssociationTextItem(i18n("Change Name"), mt_Rename_Name); break; case Uml::TextRole::RoleAName: insertAssociationTextItem(i18n("Change Role A Name..."), mt_Rename_RoleAName); break; case Uml::TextRole::RoleBName: insertAssociationTextItem(i18n("Change Role B Name..."), mt_Rename_RoleBName); break; case Uml::TextRole::ChangeA: case Uml::TextRole::ChangeB: insert(mt_Change_Font); insert(mt_Reset_Label_Positions); insert(mt_Properties); break; case Uml::TextRole::Coll_Message_Self: case Uml::TextRole::Coll_Message: case Uml::TextRole::Seq_Message_Self: case Uml::TextRole::Seq_Message: insert(mt_Change_Font); insert(mt_Operation, Icon_Utils::SmallIcon(Icon_Utils::it_Operation_New), i18n("New Operation...")); insert(mt_Select_Operation, i18n("Select Operation...")); break; case Uml::TextRole::Floating: default: insertStdItems(false, type); insert(mt_Rename, i18n("Change Text...")); insert(mt_Change_Font); break; } break; default: uWarning() << "unhandled WidgetType " << WidgetBase::toString(type); break; }//end switch } /** * Inserts the menu actions that work on the whole selection of widgets */ void WidgetBasePopupMenu::insertMultiSelectionMenu(WidgetBase::WidgetType uniqueType) { insertSubMenuAlign(); KMenu* color = newMenu(i18nc("color menu", "Color"), this); insert(mt_Line_Color_Selection, color, Icon_Utils::SmallIcon(Icon_Utils::it_Color_Line), i18n("Line Color...")); insert(mt_Fill_Color_Selection, color, Icon_Utils::SmallIcon(Icon_Utils::it_Color_Fill), i18n("Fill Color...")); insert(mt_Set_Use_Fill_Color_Selection, color, i18n("Use Fill Color")); insert(mt_Unset_Use_Fill_Color_Selection, color, i18n("No Fill Color")); // Add menu actions specific to classifiers if (uniqueType == WidgetBase::wt_Class || uniqueType == WidgetBase::wt_Interface) { makeMultiClassifierShowPopup(uniqueType); } addMenu(color); addSeparator(); insert(mt_Cut); insert(mt_Copy); addSeparator(); insert(mt_Clone); insert(mt_Delete); insert(mt_Resize); addSeparator(); insert(mt_Change_Font_Selection, Icon_Utils::SmallIcon(Icon_Utils::it_Change_Font), i18n("Change Font...")); } /** * Shortcut for the frequently used insert() calls. * * @param insertLeadingSeparator Set this true if the group shall * start with a separator. * @param type The WidgetType for which to insert the menu items. * If no argument is supplied then a Rename item will be * included. */ void WidgetBasePopupMenu::insertStdItems(bool insertLeadingSeparator, WidgetBase::WidgetType type) { if (insertLeadingSeparator) addSeparator(); insert(mt_Cut); insert(mt_Copy); insert(mt_Paste); addSeparator(); if (type == WidgetBase::wt_UMLWidget) insert(mt_Rename); else if (Model_Utils::isCloneable(type)) { insert(mt_Clone); insert(mt_Remove); } else insert(mt_Delete); insert(mt_Resize); insert(mt_AutoResize, i18n("Auto resize"), CHECKABLE); } /** * Add the align actions submenu */ void WidgetBasePopupMenu::insertSubMenuAlign() { KMenu* alignment = newMenu(i18nc("align menu", "Align"), this); insert(mt_Align_Right, alignment, Icon_Utils::SmallIcon(Icon_Utils::it_Align_Right), i18n("Align Right")); insert(mt_Align_Left, alignment, Icon_Utils::SmallIcon(Icon_Utils::it_Align_Left), i18n("Align Left")); insert(mt_Align_Top, alignment, Icon_Utils::SmallIcon(Icon_Utils::it_Align_Top), i18n("Align Top")); insert(mt_Align_Bottom, alignment, Icon_Utils::SmallIcon(Icon_Utils::it_Align_Bottom), i18n("Align Bottom")); insert(mt_Align_VerticalMiddle, alignment, Icon_Utils::SmallIcon(Icon_Utils::it_Align_VerticalMiddle), i18n("Align Vertical Middle")); insert(mt_Align_HorizontalMiddle, alignment, Icon_Utils::SmallIcon(Icon_Utils::it_Align_HorizontalMiddle), i18n("Align Horizontal Middle")); insert(mt_Align_VerticalDistribute, alignment, Icon_Utils::SmallIcon(Icon_Utils::it_Align_VerticalDistribute), i18n("Align Vertical Distribute")); insert(mt_Align_HorizontalDistribute, alignment, Icon_Utils::SmallIcon(Icon_Utils::it_Align_HorizontalDistribute), i18n("Align Horizontal Distribute")); addMenu(alignment); } /** * Shortcut for commonly used sub menu initializations. * * @param fc The "Use Fill Color" is checked. */ void WidgetBasePopupMenu::insertSubMenuColor(bool fc) { KMenu* color = newMenu(i18nc("color menu", "Color"), this); insert(mt_Line_Color, color); insert(mt_Fill_Color, color); insert(mt_Use_Fill_Color, color, i18n("Use Fill Color"), CHECKABLE); setActionChecked(mt_Use_Fill_Color, fc); addMenu(color); } /** * Shortcut for commonly used sub menu initializations. * * @param type The widget type for which to set up the menu. */ void WidgetBasePopupMenu::insertSubMenuNew(WidgetBase::WidgetType type, KMenu *menu) { if (!menu) menu = makeNewMenu(); switch (type) { case WidgetBase::wt_Activity: insert(mt_Initial_Activity, menu); insert(mt_Activity, menu); insert(mt_End_Activity, menu); insert(mt_Final_Activity, menu); insert(mt_Branch, menu); insert(mt_Fork, menu); insert(mt_Invoke_Activity, menu); insert(mt_Param_Activity, menu); insert(mt_Activity_Transition, menu); insert(mt_Exception, menu); insert(mt_PrePostCondition, menu); insert(mt_Send_Signal, menu); insert(mt_Accept_Signal, menu); insert(mt_Accept_Time_Event, menu); insert(mt_Region, menu); insert(mt_Pin, menu); insert(mt_Object_Node, menu); break; case WidgetBase::wt_Actor: case WidgetBase::wt_UseCase: insert(mt_Actor, menu); insert(mt_UseCase, menu); break; case WidgetBase::wt_Component: insert(mt_Subsystem, menu); insert(mt_Component, menu); if (Settings::optionState().generalState.uml2) { insert(mt_Port, menu); insert(mt_InterfaceProvided, menu); insert(mt_InterfaceRequired, menu); } else { insert(mt_InterfaceComponent, menu); } insert(mt_Artifact, menu); break; case WidgetBase::wt_Class: insert(mt_Attribute, menu); insert(mt_Operation, menu); insert(mt_Template, menu); insert(mt_Class, menu); insert(mt_Interface, menu); insert(mt_Datatype, menu); insert(mt_Enum, menu); insert(mt_State_Diagram, menu); break; case WidgetBase::wt_Interface: insert(mt_Operation, menu); insert(mt_Template, menu); insert(mt_Class, menu); insert(mt_Interface, menu); insert(mt_Datatype, menu); insert(mt_Enum, menu); break; case WidgetBase::wt_Entity: insert(mt_EntityAttribute, menu); insert(mt_PrimaryKeyConstraint, menu); insert(mt_UniqueConstraint, menu); insert(mt_ForeignKeyConstraint, menu); insert(mt_CheckConstraint, menu); break; case WidgetBase::wt_Enum: insert(mt_EnumLiteral, menu); break; case WidgetBase::wt_Port: insert(mt_InterfaceProvided, menu); insert(mt_InterfaceRequired, menu); break; case WidgetBase::wt_State: insert(mt_State, menu); insert(mt_End_State, menu); insert(mt_StateTransition, menu); insert(mt_Junction, menu); insert(mt_DeepHistory, menu); insert(mt_ShallowHistory, menu); insert(mt_Choice, menu); insert(mt_StateFork, menu); insert(mt_StateJoin, menu); insert(mt_CombinedState, menu); break; default: break; } insert(mt_Note, menu); insert(mt_FloatText, menu); addMenu(menu); } void WidgetBasePopupMenu::insertSubMenuShowEntity(EntityWidget *widget) { KMenu* show = newMenu(i18n("Show"), this); show->setIcon(Icon_Utils::SmallIcon(Icon_Utils::it_Show)); insert(mt_Show_Attribute_Signature, show, i18n("Attribute Signature"), CHECKABLE); setActionChecked(mt_Show_Attribute_Signature, widget->showAttributeSignature()); insert(mt_Show_Stereotypes, show, i18n("Stereotype"), CHECKABLE); setActionChecked(mt_Show_Stereotypes, widget->showStereotype()); addMenu(show); } diff --git a/umbrello/pics/CMakeLists.txt b/umbrello/pics/CMakeLists.txt index ef0a415c8..a93c0f32a 100644 --- a/umbrello/pics/CMakeLists.txt +++ b/umbrello/pics/CMakeLists.txt @@ -1,190 +1,192 @@ add_subdirectory(global) set(ICONS accept_signal accept_time_event activity activity-fork actor aggregation align-horizontal-center align-horizontal-left align-horizontal-right align-vertical-bottom align-vertical-center align-vertical-top anchor andline arrow artifact association box branch category category2parent check_constraint child2category choice-rhomb choice-round class class-or-package combined_fragment component component1 composition containment CVglobal_meth CVglobal_var CVimplementation_meth CVimplementation_signal CVimplementation_slot CVimplementation_var CVnamespace CVprivate_meth CVprivate_signal CVprivate_slot CVprivate_var CVprotected_meth CVprotected_signal CVprotected_slot CVprotected_var CVpublic_meth CVpublic_signal CVpublic_slot CVpublic_var CVstruct datatype deep-history dependency #unused #diag_activity #diagbase #diag_class #diag_collaboration #diag_component #diag_deployment #diag_entityrelationship #diag_sequence #diag_state diag_object #diag_usecase distribute-horizontal distribute-vertical end_state entity enum foreignkey_constraint generalisation initial_state instance interface interface-provider interface-requirement join junction node note object package port primarykey_constraint realization refactor relationship send_signal shallow-history state state-fork subsystem template text umbr-coll-message-asynchronous umbr-coll-message-synchronous umbr-message-asynchronous umbr-message-creation + umbr-message-destroy umbr-message-found umbr-message-lost umbr-message-synchronous uniassociation unique_constraint usecase ) set(CURSOR_ICONS accept_signal accept_time_event activity activity-fork actor aggregation anchor andline artifact association box branch category2parent category child2category choice-rhomb choice-round class combined_fragment component component1 composition containment datatype deep-history dependency end_state entity enum exception final_activity generalisation initial_state instance interface interface-provider interface-requirement join junction node note object_node object package pin precondition PrePostCondition region relationship send_signal shallow-history state state-fork subsystem text umbr-coll-message-asynchronous umbr-coll-message-synchronous umbr-message-asynchronous umbr-message-creation + umbr-message-destroy umbr-message-found umbr-message-lost umbr-message-synchronous uniassociation usecase ) if(BUILD_ICONS) generate_icons("${ICONS}" 22) # for visual inspecting #generate_icons("${ICONS}" 128) endif() if(BUILD_CURSOR_ICONS) generate_cursor_icons("${CURSOR_ICONS}" 32) endif() diff --git a/umbrello/pics/cursor-umbr-message-destroy.png b/umbrello/pics/cursor-umbr-message-destroy.png new file mode 100644 index 000000000..eb35321f3 Binary files /dev/null and b/umbrello/pics/cursor-umbr-message-destroy.png differ diff --git a/umbrello/pics/sources/umbr-message-destroy.svg b/umbrello/pics/sources/umbr-message-destroy.svg new file mode 100644 index 000000000..093677f7f --- /dev/null +++ b/umbrello/pics/sources/umbr-message-destroy.svg @@ -0,0 +1,134 @@ + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/umbrello/pics/umbr-message-destroy.png b/umbrello/pics/umbr-message-destroy.png new file mode 100644 index 000000000..6f1b1d0a3 Binary files /dev/null and b/umbrello/pics/umbr-message-destroy.png differ diff --git a/umbrello/toolbarstateassociation.cpp b/umbrello/toolbarstateassociation.cpp index 6d6c9d7e1..f172e75b5 100644 --- a/umbrello/toolbarstateassociation.cpp +++ b/umbrello/toolbarstateassociation.cpp @@ -1,351 +1,352 @@ /*************************************************************************** * 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-2020 * * Umbrello UML Modeller Authors * ***************************************************************************/ // own header #include "toolbarstateassociation.h" // app includes #include "assocrules.h" #include "association.h" #include "associationline.h" #include "associationwidget.h" #include "classifier.h" #include "classifierwidget.h" #include "cmds/widget/cmdcreatewidget.h" #include "floatingtextwidget.h" #include "debug_utils.h" #include "folder.h" #include "model_utils.h" #include "uml.h" #include "umldoc.h" #include "umlobject.h" #include "umlscene.h" #include "umlview.h" #include "umlwidget.h" // kde includes #include #include /** * Creates a new ToolBarStateAssociation. * * @param umlScene The UMLScene to use. */ ToolBarStateAssociation::ToolBarStateAssociation(UMLScene *umlScene) : ToolBarStatePool(umlScene), m_firstWidget(0), m_associationLine(0) { } /** * Destroys this ToolBarStateAssociation. * Deletes the association line. */ ToolBarStateAssociation::~ToolBarStateAssociation() { cleanAssociation(); } /** * Goes back to the initial state. */ void ToolBarStateAssociation::init() { ToolBarStatePool::init(); cleanAssociation(); } /** * Called when the current tool is changed to use another tool. * Executes base method and cleans the association. */ void ToolBarStateAssociation::cleanBeforeChange() { ToolBarStatePool::cleanBeforeChange(); cleanAssociation(); } /** * Called when a mouse event happened. * It executes the base method and then updates the position of the * association line, if any. */ void ToolBarStateAssociation::mouseMove(QGraphicsSceneMouseEvent* ome) { ToolBarStatePool::mouseMove(ome); if (m_associationLine) { QPointF sp = m_associationLine->line().p1(); m_associationLine->setLine(sp.x(), sp.y(), m_pMouseEvent->scenePos().x(), m_pMouseEvent->scenePos().y()); } } /** * A widget was removed from the UMLScene. * If the widget removed was the current widget, the current widget is set * to 0. * Also, if it was the first widget, the association is cleaned. */ void ToolBarStateAssociation::slotWidgetRemoved(UMLWidget* widget) { ToolBarState::slotWidgetRemoved(widget); if (widget == m_firstWidget) { cleanAssociation(); } } /** * Called when the release event happened on an association. * If the button pressed isn't left button, the association being created is * cleaned. If it is left button, and the first widget is set and is a * classifier widget, it creates an association class. Otherwise, the * association being created is cleaned. */ void ToolBarStateAssociation::mouseReleaseAssociation() { if (m_pMouseEvent->button() != Qt::LeftButton || !m_firstWidget || !m_firstWidget->isClassWidget()) { cleanAssociation(); return; } currentAssociation()->createAssocClassLine( static_cast(m_firstWidget), currentAssociation()->associationLine()->closestSegmentIndex(m_pMouseEvent->scenePos())); m_firstWidget->addAssoc(currentAssociation()); cleanAssociation(); } /** * Called when the release event happened on a widget. * If the button pressed isn't left button, the association is cleaned. If * it is left button, sets the first widget or the second, depending on * whether the first widget is already set or not. */ void ToolBarStateAssociation::mouseReleaseWidget() { if (m_pMouseEvent->button() != Qt::LeftButton) { cleanAssociation(); return; } // TODO In old code in ToolBarState there was a TODO that said: Should not //be called by a Sequence message Association. Here's the check for that, //although I don't know why it is needed, but it seems that it's not needed, //as the old code worked fine without it... if (getAssociationType() == Uml::AssociationType::Seq_Message) { return; } if (!m_firstWidget) { setFirstWidget(); } else { setSecondWidget(); } } /** * Called when the release event happened on an empty space. * Cleans the association. */ void ToolBarStateAssociation::mouseReleaseEmpty() { cleanAssociation(); } /** * Sets the first widget in the association using the current widget. * If the widget can't be associated using the current type of association, * an error is shown and the widget isn't set. * Otherwise, the temporal visual association is created and the mouse * tracking is enabled, so move events will be delivered. */ void ToolBarStateAssociation::setFirstWidget() { UMLWidget* widget = currentWidget(); Uml::AssociationType::Enum type = getAssociationType(); if (!AssocRules::allowAssociation(type, widget)) { //TODO improve error feedback: tell the user what are the valid type of associations for //that widget KMessageBox::error(0, i18n("Incorrect use of associations."), i18n("Association Error")); return; } //set up position QPoint pos; pos.setX(widget->scenePos().x() + (widget->width() / 2)); pos.setY(widget->scenePos().y() + (widget->height() / 2)); //TODO why is this needed? m_pUMLScene->setPos(pos); cleanAssociation(); m_firstWidget = widget; // preliminary line m_associationLine = new QGraphicsLineItem(); m_pUMLScene->addItem(m_associationLine); m_associationLine->setLine(pos.x(), pos.y(), pos.x(), pos.y()); m_associationLine->setPen(QPen(m_pUMLScene->lineColor(), m_pUMLScene->lineWidth(), Qt::DashLine)); m_associationLine->setVisible(true); m_pUMLScene->activeView()->viewport()->setMouseTracking(true); } /** * Sets the second widget in the association using the current widget and * creates the association. * If the association between the two widgets using the current type of * association is illegitimate, an error is shown and the association cancelled. * Otherwise, the association is created and added to the scene, and the tool * is changed to the default tool. * * @todo Why change to the default tool? Shouldn't it better to stay on * association and let the user change with a right click? The tool to * create widgets doesn't change to default after creating a widget */ void ToolBarStateAssociation::setSecondWidget() { Uml::AssociationType::Enum type = getAssociationType(); UMLWidget* widgetA = m_firstWidget; UMLWidget* widgetB = currentWidget(); WidgetBase::WidgetType at = widgetA->baseType(); bool valid = true; if (type == Uml::AssociationType::Generalization) { type = AssocRules::isGeneralisationOrRealisation(widgetA, widgetB); } if (widgetA == widgetB) { valid = AssocRules::allowSelf(type, at); if (valid && type == Uml::AssociationType::Association) { type = Uml::AssociationType::Association_Self; } } else { valid = AssocRules::allowAssociation(type, widgetA, widgetB); } if (valid) { if (widgetA->changesShape()) widgetA->updateGeometry(); if (widgetB->changesShape()) widgetB->updateGeometry(); if (widgetA->isClassWidget() && widgetB->isClassWidget()) { if (type == Uml::AssociationType::Composition) { UMLClassifier *c = widgetA->umlObject()->asUMLClassifier(); UMLAttribute *attr = new UMLAttribute(c, c->uniqChildName(UMLObject::ot_Attribute)); attr->setType(widgetB->umlObject()); c->addAttribute(attr); cleanAssociation(); emit finished(); return; } else if (type == Uml::AssociationType::Aggregation) { UMLClassifier *c = widgetA->umlObject()->asUMLClassifier(); UMLAttribute *attr = new UMLAttribute(c, c->uniqChildName(UMLObject::ot_Attribute)); attr->setTypeName(QString(QLatin1String("%1*")).arg(widgetB->umlObject()->name())); c->addAttribute(attr); cleanAssociation(); emit finished(); return; } } AssociationWidget *temp = AssociationWidget::create(m_pUMLScene, widgetA, type, widgetB); FloatingTextWidget *wt = temp->textWidgetByRole(Uml::TextRole::Coll_Message); if (wt) wt->showOperationDialog(); UMLApp::app()->executeCommand(new Uml::CmdCreateWidget(temp)); } else { //TODO improve error feedback: tell the user what are the valid type of associations for //the second widget using the first widget KMessageBox::error(0, i18n("Incorrect use of associations."), i18n("Association Error")); } cleanAssociation(); emit finished(); } /** * Returns the association type of this tool. * * @return The association type of this tool. */ Uml::AssociationType::Enum ToolBarStateAssociation::getAssociationType() { Uml::AssociationType::Enum at; switch(getButton()) { case WorkToolBar::tbb_Anchor: at = Uml::AssociationType::Anchor; break; case WorkToolBar::tbb_Association: at = Uml::AssociationType::Association; break; case WorkToolBar::tbb_UniAssociation: at = Uml::AssociationType::UniAssociation; break; case WorkToolBar::tbb_Generalization: at = Uml::AssociationType::Generalization; break; case WorkToolBar::tbb_Composition: at = Uml::AssociationType::Composition; break; case WorkToolBar::tbb_Aggregation: at = Uml::AssociationType::Aggregation; break; case WorkToolBar::tbb_Relationship: at = Uml::AssociationType::Relationship; break; case WorkToolBar::tbb_Dependency: at = Uml::AssociationType::Dependency; break; case WorkToolBar::tbb_Containment: at = Uml::AssociationType::Containment; break; case WorkToolBar::tbb_Seq_Message_Creation: + case WorkToolBar::tbb_Seq_Message_Destroy: case WorkToolBar::tbb_Seq_Message_Synchronous: case WorkToolBar::tbb_Seq_Combined_Fragment: case WorkToolBar::tbb_Seq_Precondition: case WorkToolBar::tbb_Seq_Message_Asynchronous: at = Uml::AssociationType::Seq_Message; break; case WorkToolBar::tbb_Coll_Message_Synchronous: at = Uml::AssociationType::Coll_Message_Synchronous; break; case WorkToolBar::tbb_Coll_Message_Asynchronous: at = Uml::AssociationType::Coll_Message_Asynchronous; break; case WorkToolBar::tbb_State_Transition: at = Uml::AssociationType::State; break; case WorkToolBar::tbb_Activity_Transition: at = Uml::AssociationType::Activity; break; case WorkToolBar::tbb_Exception: at = Uml::AssociationType::Exception; break; case WorkToolBar::tbb_Category2Parent: at = Uml::AssociationType::Category2Parent; break; case WorkToolBar::tbb_Child2Category: at = Uml::AssociationType::Child2Category; break; default: at = Uml::AssociationType::Unknown; break; } return at; } /** * Adds an AssociationWidget to the association list and creates the * corresponding UMLAssociation in the current UMLDoc. * If the association can't be added, is deleted. * * @param assoc The AssociationWidget to add. * @return True on success */ bool ToolBarStateAssociation::addAssociationInViewAndDoc(AssociationWidget* assoc) { // append in view if (m_pUMLScene->addAssociation(assoc, false)) { // if view went ok, then append in document UMLAssociation *umla = assoc->association(); if (umla) { // association with model representation in UMLDoc Uml::ModelType::Enum m = Model_Utils::convert_DT_MT(m_pUMLScene->type()); UMLDoc *umldoc = UMLApp::app()->document(); umla->setUMLPackage(umldoc->rootFolder(m)); umldoc->addAssociation(umla); } return true; } else { uError() << "cannot addAssocInViewAndDoc(), deleting"; delete assoc; return false; } } /** * Cleans the first widget and the temporal association line, if any. * Both are set to null, and the association line is also deleted. */ void ToolBarStateAssociation::cleanAssociation() { m_firstWidget = 0; delete m_associationLine; m_associationLine = 0; } diff --git a/umbrello/toolbarstatefactory.cpp b/umbrello/toolbarstatefactory.cpp index 8cda52f15..44b51df09 100644 --- a/umbrello/toolbarstatefactory.cpp +++ b/umbrello/toolbarstatefactory.cpp @@ -1,107 +1,108 @@ /*************************************************************************** * 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-2020 * * Umbrello UML Modeller Authors * ***************************************************************************/ #include "toolbarstatefactory.h" #include "toolbarstate.h" #include "toolbarstatepool.h" #include "toolbarstateother.h" #include "toolbarstatearrow.h" #include "toolbarstatemessages.h" #include "toolbarstateassociation.h" #include "toolbarstateonewidget.h" #include "umlview.h" ToolBarStateFactory::ToolBarStateFactory() { for (int i = 0; i < NR_OF_TOOLBAR_STATES; ++i) { m_states[i] = 0; } } ToolBarStateFactory::~ToolBarStateFactory() { for (int i = 0; i < NR_OF_TOOLBAR_STATES; ++i) { if (m_states[i]) delete m_states[i]; } } ToolBarState* ToolBarStateFactory::getState(const WorkToolBar::ToolBar_Buttons &toolbarButton, UMLScene *umlScene) { int key = getKey(toolbarButton); if (m_states[key] == 0) { switch (key) { // When you add a new state, make sure you also increase the // NR_OF_TOOLBAR_STATES case 0: m_states[0] = new ToolBarStateOther(umlScene); break; case 1: m_states[1] = new ToolBarStateAssociation(umlScene); break; case 2: m_states[2] = new ToolBarStateMessages(umlScene); break; // This case has no pool. case 3: m_states[3] = new ToolBarStateArrow(umlScene); break; case 4: m_states[4] = new ToolBarStateOneWidget(umlScene); break; } } // Make explicit the selected button. This is only necessary for states with a pool. if (key != 3) ((ToolBarStatePool*) m_states[key].data())->setButton(toolbarButton); return m_states[key]; } int ToolBarStateFactory::getKey(const WorkToolBar::ToolBar_Buttons &toolbarButton) const { switch (toolbarButton) { // Associations case WorkToolBar::tbb_Dependency: return 1; case WorkToolBar::tbb_Aggregation: return 1; case WorkToolBar::tbb_Relationship: return 1; case WorkToolBar::tbb_Generalization: return 1; case WorkToolBar::tbb_Association: return 1; case WorkToolBar::tbb_UniAssociation: return 1; case WorkToolBar::tbb_Composition: return 1; case WorkToolBar::tbb_Containment: return 1; case WorkToolBar::tbb_Anchor: return 1; case WorkToolBar::tbb_Coll_Message_Synchronous: return 1; case WorkToolBar::tbb_Coll_Message_Asynchronous: return 1; case WorkToolBar::tbb_State_Transition: return 1; case WorkToolBar::tbb_Activity_Transition: return 1; case WorkToolBar::tbb_Exception: return 1; case WorkToolBar::tbb_Category2Parent: return 1; case WorkToolBar::tbb_Child2Category: return 1; // Messages case WorkToolBar::tbb_Seq_Message_Creation: return 2; + case WorkToolBar::tbb_Seq_Message_Destroy: return 2; case WorkToolBar::tbb_Seq_Message_Synchronous: return 2; case WorkToolBar::tbb_Seq_Message_Asynchronous: return 2; case WorkToolBar::tbb_Seq_Message_Found: return 2; case WorkToolBar::tbb_Seq_Message_Lost: return 2; case WorkToolBar::tbb_Seq_Precondition: return 4; case WorkToolBar::tbb_Pin: return 4; case WorkToolBar::tbb_Port: return 4; // Arrow pointer case WorkToolBar::tbb_Arrow: return 3; // Other. default: return 0; } } diff --git a/umbrello/toolbarstatemessages.cpp b/umbrello/toolbarstatemessages.cpp index a4779c0e0..e69222497 100644 --- a/umbrello/toolbarstatemessages.cpp +++ b/umbrello/toolbarstatemessages.cpp @@ -1,345 +1,349 @@ /*************************************************************************** * 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-2020 * * Umbrello UML Modeller Authors * ***************************************************************************/ // own header #include "toolbarstatemessages.h" // local includes #include "cmds.h" #include "debug_utils.h" #include "floatingtextwidget.h" #include "messagewidget.h" #include "objectwidget.h" #include "object_factory.h" #include "uml.h" #include "umldoc.h" #include "umlview.h" #include "umlscene.h" #include "widget_factory.h" // kde includes #include #include /** * Creates a new ToolBarStateMessages. * * @param umlScene The UMLScene to use. */ ToolBarStateMessages::ToolBarStateMessages(UMLScene *umlScene) : ToolBarStatePool(umlScene), m_firstObject(0), m_messageLine(0), m_isObjectWidgetLine(false), xclick(0), yclick(0) { } /** * Destroys this ToolBarStateMessages. */ ToolBarStateMessages::~ToolBarStateMessages() { cleanMessage(); } /** * Goes back to the initial state. */ void ToolBarStateMessages::init() { ToolBarStatePool::init(); cleanMessage(); } /** * Called when the current tool is changed to use another tool. * Executes base method and cleans the message. */ void ToolBarStateMessages::cleanBeforeChange() { ToolBarStatePool::cleanBeforeChange(); cleanMessage(); } /** * Called when a mouse event happened. * It executes the base method and then updates the position of the * message line, if any. */ void ToolBarStateMessages::mouseMove(QGraphicsSceneMouseEvent* ome) { ToolBarStatePool::mouseMove(ome); if (m_messageLine) { QPointF sp = m_messageLine->line().p1(); m_messageLine->setLine(sp.x(), sp.y(), m_pMouseEvent->scenePos().x(), m_pMouseEvent->scenePos().y()); } } /** * A widget was removed from the UMLView. * If the widget removed was the current widget, the current widget is set * to 0. * Also, if it was the first object, the message is cleaned. */ void ToolBarStateMessages::slotWidgetRemoved(UMLWidget* widget) { ToolBarState::slotWidgetRemoved(widget); if (widget == m_firstObject) { cleanMessage(); } } /** * Selects only widgets, but no associations. * Overrides base class method. * If the press event happened on the line of an object, the object is set * as current widget. If the press event happened on a widget, the widget is * set as current widget. */ void ToolBarStateMessages::setCurrentElement() { m_isObjectWidgetLine = false; ObjectWidget* objectWidgetLine = m_pUMLScene->onWidgetLine(m_pMouseEvent->scenePos()); if (objectWidgetLine) { uDebug() << Q_FUNC_INFO << "Object detected"; setCurrentWidget(objectWidgetLine); m_isObjectWidgetLine = true; return; } uDebug() << Q_FUNC_INFO << "Object NOT detected"; //commit 515177 fixed a setting creation messages only working properly at 100% zoom //However, the applied patch doesn't seem to be necessary no more, so it was removed //The widgets weren't got from UMLView, but from a method in this class similarto the //one in UMLView but containing special code to handle the zoom UMLWidget *widget = m_pUMLScene->widgetAt(m_pMouseEvent->scenePos()); if (widget) { setCurrentWidget(widget); return; } } /** * Called when the release event happened on a widget. * If the button pressed isn't left button or the widget isn't an object * widget, the message is cleaned. * If the release event didn't happen on the line of an object and the first * object wasn't selected, nothing is done. If the first object was already * selected, a creation message is made. * If the event happened on the line of an object, the first object or the * second are set, depending on whether the first object was already set or * not. */ void ToolBarStateMessages::mouseReleaseWidget() { //TODO When an association between UMLObjects of invalid types is made, an error message //is shown. Shouldn't also a message be used here? if (m_pMouseEvent->button() != Qt::LeftButton || !currentWidget()->isObjectWidget()) { cleanMessage(); return; } if (!m_isObjectWidgetLine && !m_firstObject) { return; } if (!m_isObjectWidgetLine) { setSecondWidget(static_cast(currentWidget()), CreationMessage); return; } if (!m_firstObject) { setFirstWidget(static_cast(currentWidget())); } else { setSecondWidget(static_cast(currentWidget()), NormalMessage); } } /** * Called when the release event happened on an empty space. * Cleans the message. * Empty spaces are not only actual empty spaces, but also associations. */ void ToolBarStateMessages::mouseReleaseEmpty() { Uml::SequenceMessage::Enum msgType = getMessageType(); if (m_firstObject && msgType == Uml::SequenceMessage::Creation) { xclick = m_pMouseEvent->scenePos().x(); yclick = m_pMouseEvent->scenePos().y(); bool state = m_pUMLScene->getCreateObject(); m_pUMLScene->setCreateObject(false); UMLObject *object = Object_Factory::createUMLObject(UMLObject::ot_Class); m_pUMLScene->setCreateObject(state); ObjectWidget *widget = (ObjectWidget *)Widget_Factory::createWidget(m_pUMLScene, object); widget->setX(xclick); widget->activate(); m_pUMLScene->addWidgetCmd(widget); MessageWidget* message = new MessageWidget(m_pUMLScene, m_firstObject, widget, yclick, msgType); setupMessageWidget(message, false); cleanMessage(); xclick = 0; yclick = 0; } else if (m_firstObject && msgType == Uml::SequenceMessage::Lost) { xclick = m_pMouseEvent->scenePos().x(); yclick = m_pMouseEvent->scenePos().y(); MessageWidget* message = new MessageWidget(m_pUMLScene, m_firstObject, xclick, yclick, msgType); setupMessageWidget(message); cleanMessage(); xclick = 0; yclick = 0; } else if (!m_firstObject && msgType == Uml::SequenceMessage::Found && xclick == 0 && yclick == 0) { xclick = m_pMouseEvent->scenePos().x(); yclick = m_pMouseEvent->scenePos().y(); cleanMessage(); m_messageLine = new QGraphicsLineItem(); m_pUMLScene->addItem(m_messageLine); qreal x = m_pMouseEvent->scenePos().x(); qreal y = m_pMouseEvent->scenePos().y(); m_messageLine->setLine(x, y, x, y); m_messageLine->setPen(QPen(m_pUMLScene->lineColor(), m_pUMLScene->lineWidth(), Qt::DashLine)); m_messageLine->setVisible(true); m_pUMLScene->activeView()->viewport()->setMouseTracking(true); } else { cleanMessage(); } } /** * Sets the first object of the message using the specified object. * The temporal visual message is created and mouse tracking enabled, so * mouse events will be delivered. * * @param firstObject The first object of the message. */ void ToolBarStateMessages::setFirstWidget(ObjectWidget* firstObject) { m_firstObject = firstObject; Uml::SequenceMessage::Enum msgType = getMessageType(); if (msgType == Uml::SequenceMessage::Found && xclick!=0 && yclick!=0) { MessageWidget* message = new MessageWidget(m_pUMLScene, m_firstObject, xclick, yclick, msgType); setupMessageWidget(message); cleanMessage(); xclick = 0; yclick = 0; } else { // TODO use cleanMessage() if (m_messageLine) delete m_messageLine; m_messageLine = new QGraphicsLineItem(); m_pUMLScene->addItem(m_messageLine); qreal x = m_pMouseEvent->scenePos().x(); qreal y = m_pMouseEvent->scenePos().y(); m_messageLine->setLine(x, y, x, y); m_messageLine->setPen(QPen(m_pUMLScene->lineColor(), m_pUMLScene->lineWidth(), Qt::DashLine)); m_messageLine->setVisible(true); m_pUMLScene->activeView()->viewport()->setMouseTracking(true); } } /** * Sets the second object of the message using the specified widget and * creates the message. * The association is created and added to the view. The dialog to select * the operation of the message is shown. * * @param secondObject The second object of the message. * @param messageType The type of the message to create. */ void ToolBarStateMessages::setSecondWidget(ObjectWidget* secondObject, MessageType messageType) { Uml::SequenceMessage::Enum msgType = getMessageType(); //There shouldn't be second widget for a lost or a found message if (msgType == Uml::SequenceMessage::Lost || msgType == Uml::SequenceMessage::Found) { cleanMessage(); xclick = 0; yclick = 0; return; } qreal y = m_messageLine->line().p1().y(); if (messageType == CreationMessage) { msgType = Uml::SequenceMessage::Creation; } MessageWidget* message = new MessageWidget(m_pUMLScene, m_firstObject, secondObject, y, msgType); setupMessageWidget(message); cleanMessage(); } /** * Returns the message type of this tool. * * @return The message type of this tool. */ Uml::SequenceMessage::Enum ToolBarStateMessages::getMessageType() { if (getButton() == WorkToolBar::tbb_Seq_Message_Creation) { return Uml::SequenceMessage::Creation; } - if (getButton() == WorkToolBar::tbb_Seq_Message_Synchronous) { + else if (getButton() == WorkToolBar::tbb_Seq_Message_Destroy) { + return Uml::SequenceMessage::Destroy; + } + else if (getButton() == WorkToolBar::tbb_Seq_Message_Synchronous) { return Uml::SequenceMessage::Synchronous; } else if (getButton() == WorkToolBar::tbb_Seq_Message_Found) { return Uml::SequenceMessage::Found; } else if (getButton() == WorkToolBar::tbb_Seq_Message_Lost) { return Uml::SequenceMessage::Lost; } - else if (getButton() == WorkToolBar::tbb_Seq_Message_Creation) { - return Uml::SequenceMessage::Creation; - } return Uml::SequenceMessage::Asynchronous; } /** * Cleans the first widget and the temporal message line, if any. * Both are set to null, and the message line is also deleted. */ void ToolBarStateMessages::cleanMessage() { m_firstObject = 0; delete m_messageLine; m_messageLine = 0; } void ToolBarStateMessages::setupMessageWidget(MessageWidget *message, bool showOperationDialog) { if (showOperationDialog) { FloatingTextWidget *ft = message->floatingTextWidget(); //TODO cancel doesn't cancel the creation of the message, only cancels setting an operation. //Shouldn't it cancel also the whole creation? - ft->showOperationDialog(); - m_pUMLScene->addWidgetCmd(ft); + if (message->sequenceMessageType() == Uml::SequenceMessage::Destroy) { + message->setOperationText(i18n("destroy")); + } else { + ft->showOperationDialog(); + m_pUMLScene->addWidgetCmd(ft); + } message->setTextPosition(); } UMLApp::app()->executeCommand(new Uml::CmdCreateWidget(message)); emit finished(); } diff --git a/umbrello/umlscene.cpp b/umbrello/umlscene.cpp index 6f5055646..2f54e7b1f 100644 --- a/umbrello/umlscene.cpp +++ b/umbrello/umlscene.cpp @@ -1,4391 +1,4395 @@ /*************************************************************************** * 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-2020 * * 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 "component.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 "interfacewidget.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 "portwidget.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 #include // system includes #include // for ceil // static members const qreal UMLScene::defaultCanvasSize = 5000; bool UMLScene::m_showDocumentationIndicator = false; using namespace Uml; DEBUG_REGISTER_DISABLED(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(UMLScene *parent) : p(parent) , toolBarState(nullptr) , inMouseMoveEvent(false) { toolBarStateFactory = new ToolBarStateFactory; } ~UMLScenePrivate() { delete toolBarState; delete toolBarStateFactory; } /** * Check if there is a corresponding port widget * for all UMLPort instances and add if not. */ void addMissingPorts() { UMLWidgetList ports; UMLWidgetList components; foreach(UMLWidget *w, p->widgetList()) { if (w->isPortWidget()) ports.append(w); else if (w->isComponentWidget()) components.append(w); } foreach(UMLWidget *cw, components) { UMLComponent *c = cw->umlObject()->asUMLComponent(); if (!c) continue; // iterate through related ports for this component widget foreach(UMLObject *o, c->containedObjects()) { UMLPort *up = o->asUMLPort(); if (!up) continue; Uml::ID::Type id = o->id(); bool found = false; foreach(UMLWidget *p, ports) { if (p->id() == id) { found = true; break; } } if (!found) new PortWidget(p, up, cw); } } } /** * Check if port are located equally on the border of a component * and fix position if not. */ void fixPortPositions() { foreach(UMLWidget *w, p->widgetList()) { if (w->isPortWidget()) { QGraphicsItem *g = w->parentItem(); ComponentWidget *c = dynamic_cast(g); Q_ASSERT(c); qreal w2 = w->width()/2; qreal h2 = w->height()/2; if (w->x() <= -w2 || w->y() <= -h2 || w->x() >= c->width() - w2 || w->y() >= c->height() - h2) continue; if (w->x() >= c->width() - 3 * w2) { // right w->setX(c->width() - w2); } else if (w->y() >= c->height() - 3 * h2) { // bottom w->setY(c->height() - h2); } else if (w->x() < 3 * w2) { // left w->setX(-w2); } else if (w->y() < 3 * h2) { // top w->setY(-h2); } else uWarning() << "unhandled widget position of" << w->name(); } } } /** * Check if duplicated floating text labels are in the scene and remove them */ void removeDuplicatedFloatingTextInstances() { UMLWidgetList labelsWithoutParents; UMLWidgetList labelsWithParent; uDebug() << "checking diagram" << p->name() << "id" << Uml::ID::toString(p->ID()); foreach(UMLWidget *w, p->widgetList()) { if (!w->isTextWidget()) continue; if (w->parentItem()) labelsWithParent.append(w); else labelsWithoutParents.append(w); } foreach(UMLWidget *w, labelsWithoutParents) { foreach(UMLWidget *wp, labelsWithParent) { if (w->id() == wp->id() && w->localID() == wp->localID() && w->name() == wp->name()) { p->removeWidgetCmd(w); uDebug() << "removed duplicated text label" << w->name() << "id:" << Uml::ID::toString(w->id()); break; } } } } void setToolBarChanged(WorkToolBar::ToolBar_Buttons button) { if (toolBarState) toolBarState->cleanBeforeChange(); toolBarState = toolBarStateFactory->getState(button, p); toolBarState->init(); p->setPaste(false); } void triggerToolBarButton(WorkToolBar::ToolBar_Buttons button) { UMLApp::app()->workToolBar()->buttonChanged(button); setToolBarChanged(button); QGraphicsSceneMouseEvent event; event.setScenePos(p->pos()); event.setButton(Qt::LeftButton); toolBarState->mousePress(&event); toolBarState->mouseRelease(&event); p->connect(toolBarState, SIGNAL(finished()), UMLApp::app()->workToolBar(), SLOT(slotResetToolBar())); } UMLScene *p; ToolBarStateFactory *toolBarStateFactory; ToolBarState *toolBarState; QPointer widgetLink; bool inMouseMoveEvent; }; /** * 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(this)), 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())); m_d->setToolBarChanged(WorkToolBar::tbb_Arrow); 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_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 (isSequenceDiagram()) { foreach (MessageWidget* message, messageList()) { bool ok; int value = message->sequenceNumber().toInt(&ok); if (ok && value > sequenceNumber) sequenceNumber = value; } } else if (isCollaborationDiagram()) { 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_d->setToolBarChanged((WorkToolBar::ToolBar_Buttons)c); } /** * 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; 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; } for(UMLDragData::LvTypeAndID_List::const_iterator it = tidList.begin(); it != tidList.end(); it++) { UMLListViewItem::ListViewType lvtype = (*it)->type; Uml::ID::Type id = (*it)->id; DiagramType::Enum diagramType = type(); UMLObject* temp = 0; //if dragging diagram - might be a drag-to-note if (Model_Utils::typeIsDiagram(lvtype)) { e->accept(); continue; } //can't drag anything onto state/activity diagrams if (diagramType == DiagramType::State || diagramType == DiagramType::Activity) { e->ignore(); continue; } //make sure can find UMLObject if (!(temp = m_doc->findObjectById(id))) { DEBUG(DBG_SRC) << "object " << Uml::ID::toString(id) << " not found"; e->ignore(); continue; } 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; } m_pos = e->scenePos(); for(UMLDragData::LvTypeAndID_List::const_iterator it = tidList.begin(); it != tidList.end(); it++) { UMLListViewItem::ListViewType lvtype = (*it)->type; Uml::ID::Type id = (*it)->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); } continue; } UMLObject* o = m_doc->findObjectById(id); if (!o) { DEBUG(DBG_SRC) << "object id=" << Uml::ID::toString(id) << " not found"; continue; } UMLWidget* newWidget = Widget_Factory::createWidget(this, o); if (!newWidget) { uWarning() << "could not create widget for uml object" << o->name(); continue; } setupNewWidget(newWidget); m_pos += QPointF(UMLWidget::DefaultMinimumSize.width(), UMLWidget::DefaultMinimumSize.height()); createAutoAssociations(newWidget); createAutoAttributeAssociations2(newWidget); } } /** * Overrides the standard operation. * Calls the same method in the current tool bar state. */ void UMLScene::mouseMoveEvent(QGraphicsSceneMouseEvent* ome) { #if QT_VERSION < 0x050000 if (m_d->inMouseMoveEvent) return; m_d->inMouseMoveEvent = true; m_d->toolBarState->mouseMove(ome); m_d->inMouseMoveEvent = false; #else m_d->toolBarState->mouseMove(ome); #endif } /** * 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_d->toolBarState->mousePress(event); // setup document if (selectedItems().count() == 0) 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_d->toolBarState->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_d->toolBarState->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(); } /** * Checks the specified point against all widgets and returns the widget * for which the point is within its bounding box. * @param p Point in scene coordinates to search for * @return Returns the first widget of type UMLWidget returned by QGraphicsScene::items() for multiple matches * @return Returns NULL if the point is not inside any widget. */ UMLWidget* UMLScene::widgetAt(const QPointF& p) { foreach(QGraphicsItem *item, items(p)) { UMLWidget *w = dynamic_cast(item); if (w) return w; } return nullptr; } /** * 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; } /** * Returns whether a widget is already on the diagram. * * @param type The type of the widget to check for. * * @return Returns pointer to the widget if it is on the diagram, NULL if not. */ UMLWidget* UMLScene::widgetOnDiagram(WidgetBase::WidgetType type) { foreach(UMLWidget *widget, widgetList()) { if (!widget) continue; if (widget->baseType() == type) return widget; } return nullptr; } /** * 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 (undo command) * * @param o The widget to remove. */ void UMLScene::removeWidget(AssociationWidget* w) { UMLApp::app()->executeCommand(new CmdRemoveWidget(w)); } /** * 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(); } else if (widget->isPortWidget()) { UMLObject *o = widget->umlObject(); removeWidget(widget); if (o) UMLApp::app()->executeCommand(new CmdRemoveUMLObject(o)); // message widgets are handled later } else if (!widget->isMessageWidget()){ removeWidget(widget); } } // Delete any selected associations. foreach(AssociationWidget* assocwidget, selectedAssociations) { removeWidget(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 an 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 https://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) { a->setAssociationType(assocType); } else if (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()) { UMLClassifier *c = dt->originType(); UMLWidget *w = c ? findWidget(c->id()) : 0; // if the referenced type has a widget representation on this view if (w) { Uml::AssociationType::Enum assocType = Uml::AssociationType::Aggregation; AssociationWidget *a = findAssocWidget(widget, w, attr->name()); if (a) { a->setAssociationType(assocType); } else if (AssocRules::allowAssociation(assocType, widget, w)) { // create an aggregation AssocWidget from the ClassifierWidget // to the widget of the referenced type a = AssociationWidget::create (this, widget, assocType, 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 constraint, 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(); } void UMLScene::triggerToolbarButton(WorkToolBar::ToolBar_Buttons button) { m_d->triggerToolBarButton(button); } /** * 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; Object_Factory::createUMLObject(UMLObject::ot_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: case ListPopupMenu::mt_InterfaceComponent: 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 = Widget_Utils::defaultWidgetName(WidgetBase::WidgetType::wt_State); bool ok = Dialog_Utils::askNewName(WidgetBase::WidgetType::wt_State, name); if (ok) { StateWidget* state = new StateWidget(this); state->setName(name); setupNewWidget(state); } } break; case ListPopupMenu::mt_CombinedState: { QString name = Widget_Utils::defaultWidgetName(WidgetBase::WidgetType::wt_State); bool ok; do { if (!Diagram_Utils::isUniqueDiagramName(Uml::DiagramType::State, name)) name.append(QLatin1String("_1")); ok = Dialog_Utils::askNewName(WidgetBase::WidgetType::wt_State, name); } while(ok && !Diagram_Utils::isUniqueDiagramName(Uml::DiagramType::State, name)); if (ok) { StateWidget* state = new StateWidget(this); state->setName(name); setupNewWidget(state); Uml::CmdCreateDiagram* d = new Uml::CmdCreateDiagram(m_doc, Uml::DiagramType::State, name); UMLApp::app()->executeCommand(d); state->setDiagramLink(d->view()->umlScene()->ID()); d->view()->umlScene()->setWidgetLink(state); state->setStateType(StateWidget::Combined); } } break; case ListPopupMenu::mt_ReturnToClass: case ListPopupMenu::mt_ReturnToCombinedState: if (widgetLink()) { UMLApp::app()->document()->changeCurrentView(widgetLink()->umlScene()->ID()); widgetLink()->update(); widgetLink()->umlScene()->update(); setWidgetLink(nullptr); } 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; bool ok = Dialog_Utils::askDefaultNewName(WidgetBase::wt_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; } case ListPopupMenu::mt_MessageCreation: m_d->triggerToolBarButton(WorkToolBar::tbb_Seq_Message_Creation); break; + case ListPopupMenu::mt_MessageDestroy: + m_d->triggerToolBarButton(WorkToolBar::tbb_Seq_Message_Destroy); + break; + case ListPopupMenu::mt_MessageSynchronous: m_d->triggerToolBarButton(WorkToolBar::tbb_Seq_Message_Synchronous); break; case ListPopupMenu::mt_MessageAsynchronous: m_d->triggerToolBarButton(WorkToolBar::tbb_Seq_Message_Asynchronous); break; case ListPopupMenu::mt_MessageFound: m_d->triggerToolBarButton(WorkToolBar::tbb_Seq_Message_Found); break; case ListPopupMenu::mt_MessageLost: m_d->triggerToolBarButton(WorkToolBar::tbb_Seq_Message_Lost); 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) { page->setWidget(pWidget->asClassifierWidget()); page->apply(); } else if (wt == WidgetBase::wt_Interface) { page->setWidget(pWidget->asInterfaceWidget()); 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); resizeSceneToItems(); 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(itemsBoundingRect()); } /** * 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); // debug info if (Tracer::instance()->isEnabled(QLatin1String(metaObject()->className()))) { painter->setPen(Qt::green); painter->drawRect(sceneRect()); QVector origin; origin << QLineF(QPointF(-5,0), QPointF(5,0)) << QLineF(QPointF(0,-5), QPointF(0,5)); painter->drawLines(origin); painter->setPen(Qt::blue); painter->drawRect(itemsBoundingRect()); QVector lines; lines << QLineF(m_pos + QPointF(-5,0), m_pos + QPointF(5,0)) << QLineF(m_pos + QPointF(0,-5), m_pos + QPointF(0,5)); painter->setPen(Qt::gray); painter->drawLines(lines); } } /** * 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 (isSequenceDiagram() || isCollaborationDiagram()) viewElement.setAttribute(QLatin1String("autoincrementsequence"), autoIncrementSequence()); //now save all the widgets QDomElement widgetElement = qDoc.createElement(QLatin1String("widgets")); foreach(UMLWidget *widget, widgetList()) { uIgnoreZeroPointer(widget); // do not save floating text widgets having a parent widget; they are saved as part of the parent if (widget->isTextWidget() && widget->parentItem()) continue; // 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; } if (this->isComponentDiagram()) { m_d->addMissingPorts(); m_d->fixPortPositions(); } m_d->removeDuplicatedFloatingTextInstances(); 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(); } void UMLScene::setWidgetLink(WidgetBase *w) { m_d->widgetLink = w; } WidgetBase *UMLScene::widgetLink() { return m_d->widgetLink; } diff --git a/umbrello/umlwidgets/messagewidget.cpp b/umbrello/umlwidgets/messagewidget.cpp index 29cee3db6..2e5ec78b9 100644 --- a/umbrello/umlwidgets/messagewidget.cpp +++ b/umbrello/umlwidgets/messagewidget.cpp @@ -1,1431 +1,1452 @@ /*************************************************************************** * 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-2020 * * Umbrello UML Modeller Authors * ***************************************************************************/ // onw header #include "messagewidget.h" //app includes #include "classifier.h" #include "debug_utils.h" #include "dialog_utils.h" #include "docwindow.h" #include "floatingtextwidget.h" #include "listpopupmenu.h" #include "objectwidget.h" #include "operation.h" #include "uml.h" #include "umldoc.h" #include "messagewidgetpropertiesdialog.h" #include "umlview.h" #include "uniqueid.h" #include "idchangelog.h" //qt includes #include #include #include #include //kde includes #include DEBUG_REGISTER_DISABLED(MessageWidget) static const int circleWidth = 10; /** * Constructs a MessageWidget. * * This method is used for creation, synchronous and synchronous message types. * * @param scene The parent to this class. * @param a The role A widget for this message. * @param b The role B widget for this message. * @param y The vertical position to display this message. * @param sequenceMessageType Whether synchronous or asynchronous * @param id A unique id used for deleting this object cleanly. * The default (-1) will prompt generation of a new ID. */ MessageWidget::MessageWidget(UMLScene * scene, ObjectWidget* a, ObjectWidget* b, int y, Uml::SequenceMessage::Enum sequenceMessageType, Uml::ID::Type id /* = Uml::id_None */) : UMLWidget(scene, WidgetBase::wt_Message, id) { init(); m_pOw[Uml::RoleType::A] = a; m_pOw[Uml::RoleType::B] = b; m_sequenceMessageType = sequenceMessageType; if (m_sequenceMessageType == Uml::SequenceMessage::Creation) { y -= m_pOw[Uml::RoleType::B]->height() / 2; m_pOw[Uml::RoleType::B]->setY(y); - } + } else if (m_sequenceMessageType == Uml::SequenceMessage::Destroy) + m_pOw[Uml::RoleType::B]->setShowDestruction(true); updateResizability(); calculateWidget(); y = y < getMinY() ? getMinY() : y; if (y > b->getEndLineY()) b->setEndLine(y); setY(y); this->activate(); } /** * Constructs a MessageWidget. * * @param scene The parent to this class. * @param seqMsgType The Uml::SequenceMessage::Enum of this message widget * @param id The ID to assign (-1 will prompt a new ID.) */ MessageWidget::MessageWidget(UMLScene * scene, Uml::SequenceMessage::Enum seqMsgType, Uml::ID::Type id) : UMLWidget(scene, WidgetBase::wt_Message, id) { init(); m_sequenceMessageType = seqMsgType; } /** * Constructs a Lost or Found MessageWidget. * * @param scene The parent to this class. * @param a The role A widget for this message. * @param xclick The horizontal position clicked by the user * @param yclick The vertical position clicked by the user * @param sequenceMessageType Whether lost or found * @param id The ID to assign (-1 will prompt a new ID.) */ MessageWidget::MessageWidget(UMLScene * scene, ObjectWidget* a, int xclick, int yclick, Uml::SequenceMessage::Enum sequenceMessageType, Uml::ID::Type id /*= Uml::id_None*/) : UMLWidget(scene, WidgetBase::wt_Message, id) { init(); m_pOw[Uml::RoleType::A] = a; m_pOw[Uml::RoleType::B] = a; m_sequenceMessageType = sequenceMessageType; m_xclicked = xclick; m_yclicked = yclick; updateResizability(); calculateWidget(); yclick = yclick < getMinY() ? getMinY() : yclick; yclick = yclick > getMaxY() ? getMaxY() : yclick; setY(yclick); m_yclicked = yclick; this->activate(); } /** * Initializes key variables of the class. */ void MessageWidget::init() { m_xclicked = -1; m_yclicked = -1; m_ignoreSnapToGrid = true; m_ignoreSnapComponentSizeToGrid = true; m_pOw[Uml::RoleType::A] = m_pOw[Uml::RoleType::B] = 0; m_pFText = 0; } /** * Standard destructor. */ MessageWidget::~MessageWidget() { + if (m_pOw[Uml::RoleType::B] && m_sequenceMessageType == Uml::SequenceMessage::Destroy) + m_pOw[Uml::RoleType::B]->setShowDestruction(false); } /** * Sets the y-coordinate. * Reimplemented from UMLWidget. * * @param y The y-coordinate to be set. */ void MessageWidget::setY(qreal y) { if (y < getMinY()) { DEBUG(DBG_SRC) << "got out of bounds y position, check the reason" << this->y() << getMinY(); return; } UMLWidget::setY(y); if (m_sequenceMessageType == Uml::SequenceMessage::Creation) { const qreal objWidgetHalfHeight = m_pOw[Uml::RoleType::B]->height() / 2; m_pOw[Uml::RoleType::B]->setY(y - objWidgetHalfHeight); } if (m_pFText && !UMLApp::app()->document()->loading()) { setTextPosition(); emit sigMessageMoved(); } } /** * Update the UMLWidget::m_resizable flag according to the * charactersitics of this message. */ void MessageWidget::updateResizability() { if (m_sequenceMessageType == Uml::SequenceMessage::Synchronous) UMLWidget::m_resizable = true; else UMLWidget::m_resizable = false; } /** * Overridden from UMLWidget. * Returns the cursor to be shown when resizing the widget. * The cursor shown is KCursor::sizeVerCursor(). * * @return The cursor to be shown when resizing the widget. */ QCursor MessageWidget::resizeCursor() const { return Qt::SizeVerCursor; } /** * Overridden from UMLWidget. * Resizes the height of the message widget and emits the message moved signal. * Message widgets can only be resized vertically, so width isn't modified. * * @param newW The new width for the widget (isn't used). * @param newH The new height for the widget. */ void MessageWidget::resizeWidget(qreal newW, qreal newH) { if (sequenceMessageType() == Uml::SequenceMessage::Creation) setSize(width(), newH); else { qreal x1 = m_pOw[Uml::RoleType::A]->x(); qreal x2 = getxclicked(); qreal diffX = 0; if (x1 < x2) { diffX = x2 + (newW - width()); } else { diffX = x2 - (newW - width()); } if (diffX <= 0 ) diffX = 10; setxclicked (diffX); setSize(newW, newH); calculateWidget(); } emit sigMessageMoved(); } /** * Constrains the vertical position of the message widget so it doesn't go * upper than the bottom side of the lower object. * The height of the floating text widget in the message is taken in account * if there is any and isn't empty. * * @param diffY The difference between current Y position and new Y position. * @return The new Y position, constrained. */ qreal MessageWidget::constrainPositionY(qreal diffY) { qreal newY = y() + diffY; qreal minY = getMinY(); if (m_pFText && !m_pFText->displayText().isEmpty()) { minY += m_pFText->height(); } if (newY < minY) { newY = minY; } return newY; } /** * Overridden from UMLWidget. * Moves the widget to a new position using the difference between the * current position and the new position. X position is ignored, and widget * is only moved along Y axis. If message goes upper than the object, it's * kept at this position until it should be lowered again (the unconstrained * Y position is saved to know when it's the time to lower it again). * If the message is a creation message, the object created is also moved to * the new vertical position. * @see constrainPositionY * * @param diffX The difference between current X position and new X position * (isn't used). * @param diffY The difference between current Y position and new Y position. */ void MessageWidget::moveWidgetBy(qreal diffX, qreal diffY) { Q_UNUSED(diffX); qreal newY = constrainPositionY(diffY); setY(newY); } /** * Overridden from UMLWidget. * Modifies the value of the diffX and diffY variables used to move the widgets. * All the widgets are constrained to be moved only in Y axis (diffX is set to 0). * @see constrainPositionY * * @param diffX The difference between current X position and new X position. * @param diffY The difference between current Y position and new Y position. */ void MessageWidget::constrainMovementForAllWidgets(qreal &diffX, qreal &diffY) { diffX = 0; diffY = constrainPositionY(diffY) - y(); } /** * Reimplemented from UMLWidget and calls other paint...() methods * depending on the message type. */ void MessageWidget::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) { Q_UNUSED(option); Q_UNUSED(widget); if(!m_pOw[Uml::RoleType::A] || !m_pOw[Uml::RoleType::B]) { return; } setPenFromSettings(painter); if (m_sequenceMessageType == Uml::SequenceMessage::Synchronous) { paintSynchronous(painter, option); } else if (m_sequenceMessageType == Uml::SequenceMessage::Asynchronous) { paintAsynchronous(painter, option); } else if (m_sequenceMessageType == Uml::SequenceMessage::Creation) { paintCreation(painter, option); + } else if (m_sequenceMessageType == Uml::SequenceMessage::Destroy) { + paintDestroy(painter, option); } else if (m_sequenceMessageType == Uml::SequenceMessage::Lost) { paintLost(painter, option); } else if (m_sequenceMessageType == Uml::SequenceMessage::Found) { paintFound(painter, option); } else { uWarning() << "Unknown message type"; } } /** * Draw a solid (triangular) arrowhead pointing in the given direction. * The direction can be either Qt::LeftArrow or Qt::RightArrow. */ void MessageWidget::paintSolidArrowhead(QPainter *p, int x, int y, Qt::ArrowType direction) { int arrowheadExtentX = 4; if (direction == Qt::RightArrow) { arrowheadExtentX = -arrowheadExtentX; } QPolygon points; points.putPoints(0, 3, x, y, x + arrowheadExtentX, y - 3, x + arrowheadExtentX, y + 3); p->setBrush(QBrush(p->pen().color())); p->drawPolygon(points); } /** * Draw an arrow pointing in the given direction. * The arrow head is not solid, i.e. it is made up of two lines * like so: ---> * The direction can be either Qt::LeftArrow or Qt::RightArrow. */ void MessageWidget::paintArrow(QPainter *p, int x, int y, int w, Qt::ArrowType direction, bool useDottedLine /* = false */) { if (w > 3) { int arrowheadStartX = x; int arrowheadExtentX = 4; if (direction == Qt::RightArrow) { arrowheadStartX += w; arrowheadExtentX = -arrowheadExtentX; } // draw upper half of arrowhead p->drawLine(arrowheadStartX, y, arrowheadStartX + arrowheadExtentX, y - 3); // draw lower half of arrowhead p->drawLine(arrowheadStartX, y, arrowheadStartX + arrowheadExtentX, y + 3); } // draw arrow line if (useDottedLine) { QPen pen = p->pen(); pen.setStyle(Qt::DotLine); p->setPen(pen); } p->drawLine(x, y, x + w, y); } /** * Draws the calling arrow with filled in arrowhead, the * timeline box and the returning arrow with a dashed line and * stick arrowhead. */ void MessageWidget::paintSynchronous(QPainter *painter, const QStyleOptionGraphicsItem *option) { int x1 = m_pOw[Uml::RoleType::A]->x(); int x2 = m_pOw[Uml::RoleType::B]->x(); int w = width() - 1; int h = height(); int offsetX = 0; int offsetY = 0; bool messageOverlaps = m_pOw[Uml::RoleType::A]->messageOverlap(y(), this); const int boxWidth = 17; const int wr = w < boxWidth ? w : boxWidth; const int arrowWidth = 4; if (UMLWidget::useFillColor()) painter->setBrush(UMLWidget::fillColor()); else painter->setBrush(m_scene->backgroundColor()); if(isSelf()) { painter->fillRect(offsetX, offsetY, wr, h, QBrush(Qt::white)); //box painter->drawRect(offsetX, offsetY, wr, h); //box offsetX += wr; w -= wr; offsetY += 3; const int lowerLineY = offsetY + h - 6; // draw upper line segment (leaving the life line) painter->drawLine(offsetX, offsetY, offsetX + w, offsetY); // draw line segment parallel to (and at the right of) the life line painter->drawLine(offsetX + w, offsetY, offsetX + w, lowerLineY); // draw lower line segment (back to the life line) paintArrow(painter, offsetX, lowerLineY, w, Qt::LeftArrow); offsetX -= wr; offsetY -= 3; } else if(x1 < x2) { if (messageOverlaps) { offsetX += 8; w -= 8; } QPen pen = painter->pen(); int startX = offsetX + w - wr + 1; painter->fillRect(startX, offsetY, wr, h, QBrush(Qt::white)); //box painter->drawRect(startX, offsetY, wr, h); //box painter->drawLine(offsetX, offsetY + arrowWidth, startX, offsetY + arrowWidth); //arrow line if (w > boxWidth + arrowWidth) paintSolidArrowhead(painter, startX - 1, offsetY + arrowWidth, Qt::RightArrow); paintArrow(painter, offsetX, offsetY + h - arrowWidth + 1, w - wr + 1, Qt::LeftArrow, true); // return arrow if (messageOverlaps) { offsetX -= 8; //reset for drawSelected() } } else { if (messageOverlaps) { w -=8; } QPen pen = painter->pen(); painter->fillRect(offsetX, offsetY, wr, h, QBrush(Qt::white)); //box painter->drawRect(offsetX, offsetY, wr, h); //box painter->drawLine(offsetX + wr + 1, offsetY + arrowWidth, offsetX + w, offsetY + arrowWidth); //arrow line if (w > boxWidth + arrowWidth) paintSolidArrowhead(painter, offsetX + wr, offsetY + arrowWidth, Qt::LeftArrow); paintArrow(painter, offsetX + wr + 1, offsetY + h - arrowWidth + 1, w - wr - 1, Qt::RightArrow, true); // return arrow } UMLWidget::paint(painter, option); } /** * Draws a solid arrow line and a stick arrow head. */ void MessageWidget::paintAsynchronous(QPainter *painter, const QStyleOptionGraphicsItem *option) { int x1 = m_pOw[Uml::RoleType::A]->x(); int x2 = m_pOw[Uml::RoleType::B]->x(); int w = width() - 1; int h = height() - 1; int offsetX = 0; int offsetY = 0; bool messageOverlapsA = m_pOw[Uml::RoleType::A]->messageOverlap(y(), this); //bool messageOverlapsB = m_pOw[Uml::RoleType::B]->messageOverlap(y(), this); if(isSelf()) { if (messageOverlapsA) { offsetX += 7; w -= 7; } const int lowerLineY = offsetY + h - 3; // draw upper line segment (leaving the life line) painter->drawLine(offsetX, offsetY, offsetX + w, offsetY); // draw line segment parallel to (and at the right of) the life line painter->drawLine(offsetX + w, offsetY, offsetX + w, lowerLineY); // draw lower line segment (back to the life line) paintArrow(painter, offsetX, lowerLineY, w, Qt::LeftArrow); if (messageOverlapsA) { offsetX -= 7; //reset for drawSelected() } } else if(x1 < x2) { if (messageOverlapsA) { offsetX += 7; w -= 7; } paintArrow(painter, offsetX, offsetY + 4, w, Qt::RightArrow); if (messageOverlapsA) { offsetX -= 7; } } else { if (messageOverlapsA) { w -= 7; } paintArrow(painter, offsetX, offsetY + 4, w, Qt::LeftArrow); } UMLWidget::paint(painter, option); } /** * Draws a solid arrow line and a stick arrow head to the * edge of the target object widget instead of to the * sequence line. */ void MessageWidget::paintCreation(QPainter *painter, const QStyleOptionGraphicsItem *option) { int x1 = m_pOw[Uml::RoleType::A]->x(); int x2 = m_pOw[Uml::RoleType::B]->x(); int w = width(); //int h = height() - 1; int offsetX = 0; int offsetY = 0; bool messageOverlapsA = m_pOw[Uml::RoleType::A]->messageOverlap(y(), this); //bool messageOverlapsB = m_pOw[Uml::RoleType::B]->messageOverlap(y(), this); const int lineY = offsetY + 4; if (x1 < x2) { if (messageOverlapsA) { offsetX += 7; w -= 7; } paintArrow(painter, offsetX, lineY, w, Qt::RightArrow, true); if (messageOverlapsA) { offsetX -= 7; } } else { if (messageOverlapsA) { w -= 7; } paintArrow(painter, offsetX, lineY, w, Qt::LeftArrow, true); } UMLWidget::paint(painter, option); } +void MessageWidget::paintDestroy(QPainter *painter, const QStyleOptionGraphicsItem *option) +{ + paintSynchronous(painter, option); +} /** * Draws a solid arrow line and a stick arrow head * and a circle */ void MessageWidget::paintLost(QPainter *painter, const QStyleOptionGraphicsItem *option) { int x1 = m_pOw[Uml::RoleType::A]->centerX(); int x2 = m_xclicked; int w = width(); int h = height(); int offsetX = 0; int offsetY = 0; bool messageOverlapsA = m_pOw[Uml::RoleType::A]->messageOverlap(y(), this); //bool messageOverlapsB = m_pOw[Uml::RoleType::B]->messageOverlap(y(), this); if(x1 < x2) { if (messageOverlapsA) { offsetX += 7; w -= 7; } setPenFromSettings(painter); painter->setBrush(WidgetBase::lineColor()); painter->drawEllipse(offsetX + w - h, offsetY, h, h); paintArrow(painter, offsetX, offsetY + h/2, w - h, Qt::RightArrow); if (messageOverlapsA) { offsetX -= 7; } } else { setPenFromSettings(painter); painter->setBrush(WidgetBase::lineColor()); painter->drawEllipse(offsetX, offsetY, h, h); paintArrow(painter, offsetX + h, offsetY + h/2, w - h, Qt::LeftArrow); } UMLWidget::paint(painter, option); } /** * Draws a circle and a solid arrow line and a stick arrow head. */ void MessageWidget::paintFound(QPainter *painter, const QStyleOptionGraphicsItem *option) { int x1 = m_pOw[Uml::RoleType::A]->centerX(); int x2 = m_xclicked; int w = width(); int h = height(); int offsetX = 0; int offsetY = 0; bool messageOverlapsA = m_pOw[Uml::RoleType::A]->messageOverlap(y(), this); //bool messageOverlapsB = m_pOw[Uml::RoleType::B]->messageOverlap(y(), this); if(x1 < x2) { if (messageOverlapsA) { offsetX += 7; w -= 7; } setPenFromSettings(painter); painter->setBrush(WidgetBase::lineColor()); painter->drawEllipse(offsetX + w - h, offsetY, h, h); paintArrow(painter, offsetX, offsetY + h/2, w, Qt::LeftArrow); if (messageOverlapsA) { offsetX -= 7; } } else { if (messageOverlapsA) { w -= 7; } setPenFromSettings(painter); painter->setBrush(WidgetBase::lineColor()); painter->drawEllipse(offsetX, offsetY, h, h); paintArrow(painter, offsetX, offsetY + h/2, w, Qt::RightArrow); } UMLWidget::paint(painter, option); } /** * Overrides operation from UMLWidget. * * @param p Point to be checked. * * @return 'this' if the point is on a part of the MessageWidget. * NB In case of a synchronous message, the empty space * between call line and return line does not count, i.e. if * the point is located in that space the function returns NULL. */ UMLWidget* MessageWidget::onWidget(const QPointF& p) { if (m_sequenceMessageType != Uml::SequenceMessage::Synchronous) { return UMLWidget::onWidget(p); } // Synchronous message: // Consists of top arrow (call) and bottom arrow (return.) if (p.x() < x() || p.x() > x() + width()) return 0; const int tolerance = 5; // pixels const int pY = p.y(); const int topArrowY = y() + 3; const int bottomArrowY = y() + height() - 3; if (pY < topArrowY - tolerance || pY > bottomArrowY + tolerance) return 0; if (height() <= 2 * tolerance) return this; if (pY > topArrowY + tolerance && pY < bottomArrowY - tolerance) return 0; return this; } /** * Sets the text position relative to the sequence message. */ void MessageWidget::setTextPosition() { if (m_pFText == 0) { DEBUG(DBG_SRC) << "m_pFText is NULL"; return; } if (m_pFText->displayText().isEmpty()) { return; } m_pFText->updateGeometry(); int ftX = constrainX(m_pFText->x(), m_pFText->width(), m_pFText->textRole()); int ftY = y() - m_pFText->height(); m_pFText->setX(ftX); m_pFText->setY(ftY); } /** * Returns the textX arg with constraints applied. * Auxiliary to setTextPosition() and constrainTextPos(). */ int MessageWidget::constrainX(int textX, int textWidth, Uml::TextRole::Enum tr) { int result = textX; const int minTextX = x() + 5; if (textX < minTextX || tr == Uml::TextRole::Seq_Message_Self) { result = minTextX; } else { ObjectWidget *objectAtRight = 0; if (m_pOw[Uml::RoleType::B]->x() > m_pOw[Uml::RoleType::A]->x()) objectAtRight = m_pOw[Uml::RoleType::B]; else objectAtRight = m_pOw[Uml::RoleType::A]; const int objRight_seqLineX = objectAtRight->centerX(); const int maxTextX = objRight_seqLineX - textWidth - 5; if (maxTextX <= minTextX) result = minTextX; else if (textX > maxTextX) result = maxTextX; } return result; } /** * Constrains the FloatingTextWidget X and Y values supplied. * Overrides operation from LinkWidget. * * @param textX candidate X value (may be modified by the constraint) * @param textY candidate Y value (may be modified by the constraint) * @param textWidth width of the text * @param textHeight height of the text * @param tr Uml::TextRole::Enum of the text */ void MessageWidget::constrainTextPos(qreal &textX, qreal &textY, qreal textWidth, qreal textHeight, Uml::TextRole::Enum tr) { textX = constrainX(textX, textWidth, tr); // Constrain Y. const qreal minTextY = getMinY(); const qreal maxTextY = getMaxY() - textHeight - 5; if (textY < minTextY) textY = minTextY; else if (textY > maxTextY) textY = maxTextY; // setY(textY + textHeight); // NB: side effect } /** * Shortcut for calling m_pFText->setLink() followed by * this->setTextPosition(). */ void MessageWidget::setLinkAndTextPos() { if (m_pFText) { m_pFText->setLink(this); setTextPosition(); } } void MessageWidget::resizeEvent(QResizeEvent* /*re*/) { } /** * Calculate the geometry of the widget. */ void MessageWidget::calculateWidget() { setMessageText(m_pFText); calculateDimensions(); setVisible(true); } void MessageWidget::slotWidgetMoved(Uml::ID::Type id) { const Uml::ID::Type idA = m_pOw[Uml::RoleType::A]->localID(); const Uml::ID::Type idB = m_pOw[Uml::RoleType::B]->localID(); if (idA != id && idB != id) { DEBUG(DBG_SRC) << "id=" << Uml::ID::toString(id) << ": ignoring for idA=" << Uml::ID::toString(idA) << ", idB=" << Uml::ID::toString(idB); return; } qreal y = this->y(); if (y < getMinY()) y = getMinY(); if (y > getMaxY()) y = getMaxY(); setPos(x(), y); calculateWidget(); if(!m_pFText) return; if (m_scene->selectedCount(true) > 1) return; setTextPosition(); } /** * Check to see if the given ObjectWidget is involved in the message. * * @param w The ObjectWidget to check for. * @return true - if is contained, false - not contained. */ bool MessageWidget::hasObjectWidget(ObjectWidget * w) { if(m_pOw[Uml::RoleType::A] == w || m_pOw[Uml::RoleType::B] == w) return true; else return false; } /** * This method determines whether the message is for "Self" for * an ObjectWidget. * * @retval True If both ObjectWidgets for this widget exists and * are same. */ bool MessageWidget::isSelf() const { return (m_pOw[Uml::RoleType::A] && m_pOw[Uml::RoleType::B] && m_pOw[Uml::RoleType::A] == m_pOw[Uml::RoleType::B]); } void MessageWidget::slotMenuSelection(QAction* action) { ListPopupMenu::MenuType sel = ListPopupMenu::typeFromAction(action); if (sel == ListPopupMenu::mt_Delete) { if (Dialog_Utils::askDeleteAssociation()) { // This will clean up this widget and the text widget: m_scene->removeWidget(this); } } else { UMLWidget::slotMenuSelection(action); } } /** * Activates a MessageWidget. Connects its m_pOw[] pointers * to UMLObjects and also send signals about its FloatingTextWidget. */ bool MessageWidget::activate(IDChangeLog * /*Log = 0*/) { m_scene->resetPastePoint(); // UMLWidget::activate(Log); CHECK: I don't think we need this ? if (m_pOw[Uml::RoleType::A] == 0) { UMLWidget *pWA = m_scene->findWidget(m_widgetAId); if (pWA == 0) { DEBUG(DBG_SRC) << "role A object " << Uml::ID::toString(m_widgetAId) << " not found"; return false; } m_pOw[Uml::RoleType::A] = pWA->asObjectWidget(); if (m_pOw[Uml::RoleType::A] == 0) { DEBUG(DBG_SRC) << "role A widget " << Uml::ID::toString(m_widgetAId) << " is not an ObjectWidget"; return false; } } if (m_pOw[Uml::RoleType::B] == 0) { UMLWidget *pWB = m_scene->findWidget(m_widgetBId); if (pWB == 0) { DEBUG(DBG_SRC) << "role B object " << Uml::ID::toString(m_widgetBId) << " not found"; return false; } m_pOw[Uml::RoleType::B] = pWB->asObjectWidget(); if (m_pOw[Uml::RoleType::B] == 0) { DEBUG(DBG_SRC) << "role B widget " << Uml::ID::toString(m_widgetBId) << " is not an ObjectWidget"; return false; } } updateResizability(); UMLClassifier *c = m_pOw[Uml::RoleType::B]->umlObject()->asUMLClassifier(); UMLOperation *op = 0; if (c && !m_CustomOp.isEmpty()) { Uml::ID::Type opId = Uml::ID::fromString(m_CustomOp); op = c->findChildObjectById(opId, true)->asUMLOperation(); if (op) { // If the UMLOperation is set, m_CustomOp isn't used anyway. // Just setting it empty for the sake of sanity. m_CustomOp.clear(); } } if(!m_pFText) { Uml::TextRole::Enum tr = Uml::TextRole::Seq_Message; if (isSelf()) tr = Uml::TextRole::Seq_Message_Self; m_pFText = new FloatingTextWidget(m_scene, tr, operationText(m_scene)); m_scene->addFloatingTextWidget(m_pFText); m_pFText->setFontCmd(UMLWidget::font()); } if (op) setOperation(op); // This requires a valid m_pFText. setLinkAndTextPos(); m_pFText->setText(QString()); m_pFText->setActivated(); QString messageText = m_pFText->text(); m_pFText->setVisible(messageText.length() > 1); connect(m_pOw[Uml::RoleType::A], SIGNAL(sigWidgetMoved(Uml::ID::Type)), this, SLOT(slotWidgetMoved(Uml::ID::Type))); connect(m_pOw[Uml::RoleType::B], SIGNAL(sigWidgetMoved(Uml::ID::Type)), this, SLOT(slotWidgetMoved(Uml::ID::Type))); connect(this, SIGNAL(sigMessageMoved()), m_pOw[Uml::RoleType::A], SLOT(slotMessageMoved())); connect(this, SIGNAL(sigMessageMoved()), m_pOw[Uml::RoleType::B], SLOT(slotMessageMoved())); m_pOw[Uml::RoleType::A]->messageAdded(this); if (!isSelf()) m_pOw[Uml::RoleType::B]->messageAdded(this); // Calculate the size and position of the message widget calculateDimensions(); // Position the floating text accordingly setTextPosition(); emit sigMessageMoved(); return true; } /** * Resolve references of this message so they reference the correct * new object widgets after paste. */ void MessageWidget::resolveObjectWidget(IDChangeLog* log) { m_widgetAId = log->findNewID(m_widgetAId); m_widgetBId = log->findNewID(m_widgetBId); } /** * Overrides operation from LinkWidget. * Required by FloatingTextWidget. * * @param ft The text widget which to update. */ void MessageWidget::setMessageText(FloatingTextWidget *ft) { if (ft == 0) return; ft->setSequenceNumber(m_SequenceNumber); ft->setText(operationText(m_scene)); setTextPosition(); } /** * Overrides operation from LinkWidget. * Required by FloatingTextWidget. * * @param ft The text widget which to update. * @param newText The new text to set. */ void MessageWidget::setText(FloatingTextWidget *ft, const QString &newText) { ft->setText(newText); UMLApp::app()->document()->setModified(true); } /** * Overrides operation from LinkWidget. * Required by FloatingTextWidget. * * @param op The new operation string to set. */ void MessageWidget::setOperationText(const QString &op) { m_CustomOp = op; ///FIXME m_pOperation } /** * Implements operation from LinkWidget. * Required by FloatingTextWidget. */ void MessageWidget::lwSetFont (QFont font) { UMLWidget::setFont(font); } /** * Overrides operation from LinkWidget. * Required by FloatingTextWidget. * @todo Move to LinkWidget. */ UMLClassifier *MessageWidget::operationOwner() { UMLObject *pObject = m_pOw[Uml::RoleType::B]->umlObject(); if (pObject == 0) return 0; UMLClassifier *c = pObject->asUMLClassifier(); return c; } /** * Implements operation from LinkWidget. * Motivated by FloatingTextWidget. */ UMLOperation *MessageWidget::operation() { return m_umlObject->asUMLOperation(); } /** * Implements operation from LinkWidget. * Motivated by FloatingTextWidget. */ void MessageWidget::setOperation(UMLOperation *op) { if (m_umlObject && m_pFText) disconnect(m_umlObject, SIGNAL(modified()), m_pFText, SLOT(setMessageText())); m_umlObject = op; if (m_umlObject && m_pFText) { connect(m_umlObject, SIGNAL(modified()), m_pFText, SLOT(setMessageText())); m_pFText->setMessageText(); } } /** * Overrides operation from LinkWidget. * Required by FloatingTextWidget. */ QString MessageWidget::customOpText() { return m_CustomOp; } /** * Overrides operation from LinkWidget. * Required by FloatingTextWidget. */ void MessageWidget::setCustomOpText(const QString &opText) { m_CustomOp = opText; m_pFText->setMessageText(); } /** * Overrides operation from LinkWidget. * Required by FloatingTextWidget. */ QString MessageWidget::lwOperationText() { UMLOperation *pOperation = operation(); if (pOperation != 0) { return pOperation->toString(Uml::SignatureType::SigNoVis); } else { return customOpText(); } } /** * Overrides operation from LinkWidget. * Required by FloatingTextWidget. */ UMLClassifier *MessageWidget::lwClassifier() { UMLObject *o = m_pOw[Uml::RoleType::B]->umlObject(); UMLClassifier *c = o->asUMLClassifier(); return c; } /** * Calculates the size of the widget by calling * calculateDimensionsSynchronous(), * calculateDimensionsAsynchronous(), or * calculateDimensionsCreation() */ void MessageWidget::calculateDimensions() { if (m_sequenceMessageType == Uml::SequenceMessage::Synchronous) { calculateDimensionsSynchronous(); } else if (m_sequenceMessageType == Uml::SequenceMessage::Asynchronous) { calculateDimensionsAsynchronous(); } else if (m_sequenceMessageType == Uml::SequenceMessage::Creation) { calculateDimensionsCreation(); + } else if (m_sequenceMessageType == Uml::SequenceMessage::Destroy) { + calculateDimensionsDestroy(); } else if (m_sequenceMessageType == Uml::SequenceMessage::Lost) { calculateDimensionsLost(); } else if (m_sequenceMessageType == Uml::SequenceMessage::Found) { calculateDimensionsFound(); } else { uWarning() << "Unknown message type"; } if (! UMLApp::app()->document()->loading()) { adjustAssocs(x(), y()); // adjust assoc lines } } /** * Calculates and sets the size of the widget for a synchronous message. */ void MessageWidget::calculateDimensionsSynchronous() { int x = 0; int x1 = m_pOw[Uml::RoleType::A]->centerX(); int x2 = m_pOw[Uml::RoleType::B]->centerX(); int widgetWidth = 0; if(isSelf()) { widgetWidth = 50; x = x1 - 2; } else if(x1 < x2) { x = x1; widgetWidth = x2 - x1 + 8; } else { x = x2 - 8; widgetWidth = x1 - x2 + 8; } QSizeF minSize = minimumSize(); int widgetHeight = 0; if (height() < minSize.height()) { widgetHeight = minSize.height(); } else { widgetHeight = height(); } setX(x); setSize(widgetWidth, widgetHeight); } /** * Calculates and sets the size of the widget for an asynchronous message. */ void MessageWidget::calculateDimensionsAsynchronous() { int x = 0; int x1 = m_pOw[Uml::RoleType::A]->centerX(); int x2 = m_pOw[Uml::RoleType::B]->centerX(); int widgetWidth = 0; if(isSelf()) { widgetWidth = 50; x = x1; } else if(x1 < x2) { x = x1; widgetWidth = x2 - x1; } else { x = x2; widgetWidth = x1 - x2; } x += 1; widgetWidth -= 2; QSizeF minSize = minimumSize(); int widgetHeight = 0; if (height() < minSize.height()) { widgetHeight = minSize.height(); } else { widgetHeight = height(); } setX(x); setSize(widgetWidth, widgetHeight); } /** * Calculates and sets the size of the widget for a creation message. */ void MessageWidget::calculateDimensionsCreation() { int x = 0; int x1 = m_pOw[Uml::RoleType::A]->centerX(); int x2 = m_pOw[Uml::RoleType::B]->x(); int w2 = m_pOw[Uml::RoleType::B]->width(); if (x1 > x2) x2 += w2; int widgetWidth = 0; if (x1 < x2) { x = x1; widgetWidth = x2 - x1; } else { x = x2; widgetWidth = x1 - x2; } x += 1; widgetWidth -= 2; int widgetHeight = minimumSize().height(); setPos(x, m_pOw[Uml::RoleType::B]->y() + m_pOw[Uml::RoleType::B]->height() / 2); setSize(widgetWidth, widgetHeight); } +/** + * Calculates and sets the size of the widget for a destroy message. + */ +void MessageWidget::calculateDimensionsDestroy() +{ + calculateDimensionsSynchronous(); +} + /** * Calculates and sets the size of the widget for a lost message. */ void MessageWidget::calculateDimensionsLost() { int x = 0; int x1 = m_pOw[Uml::RoleType::A]->centerX(); int x2 = m_xclicked; int widgetWidth = 0; if(x1 < x2) { x = x1; widgetWidth = x2 - x1 + circleWidth/2; } else { x = x2 - circleWidth/2; widgetWidth = x1 - x2 + circleWidth/2; } int widgetHeight = minimumSize().height(); setX(x); setSize(widgetWidth, widgetHeight); } /** * Calculates and sets the size of the widget for a found message. */ void MessageWidget::calculateDimensionsFound() { int x = 0; int x1 = m_pOw[Uml::RoleType::A]->centerX(); int x2 = m_xclicked; int widgetWidth = 0; if(x1 < x2) { x = x1; widgetWidth = x2 - x1 + circleWidth/2; } else { x = x2 - circleWidth/2; widgetWidth = x1 - x2 + circleWidth/2; } int widgetHeight = minimumSize().height(); setX(x); setSize(widgetWidth, widgetHeight); } /** * Used to cleanup any other widget it may need to delete. */ void MessageWidget::cleanup() { if (m_pOw[Uml::RoleType::A]) { disconnect(this, SIGNAL(sigMessageMoved()), m_pOw[Uml::RoleType::A], SLOT(slotMessageMoved())); m_pOw[Uml::RoleType::A]->messageRemoved(this); } if (m_pOw[Uml::RoleType::B]) { disconnect(this, SIGNAL(sigMessageMoved()), m_pOw[Uml::RoleType::B], SLOT(slotMessageMoved())); m_pOw[Uml::RoleType::B]->messageRemoved(this); } UMLWidget::cleanup(); if (m_pFText) { m_scene->removeWidgetCmd(m_pFText); m_pFText = 0; } } /** * Sets the state of whether the widget is selected. * * @param _select True if the widget is selected. */ void MessageWidget::setSelected(bool _select) { UMLWidget::setSelected(_select); if(!m_pFText || m_pFText->displayText().isEmpty()) return; if(isSelected() && m_pFText->isSelected()) return; if(!isSelected() && !m_pFText->isSelected()) return; m_pFText->setSelected(isSelected()); } /** * Returns the minimum height this widget should be set at on * a sequence diagrams. Takes into account the widget positions * it is related to. */ int MessageWidget::getMinY() { if (!m_pOw[Uml::RoleType::A] || !m_pOw[Uml::RoleType::B]) { return 0; } if (m_sequenceMessageType == Uml::SequenceMessage::Creation) { return m_pOw[Uml::RoleType::A]->y() + m_pOw[Uml::RoleType::A]->height(); } int heightA = m_pOw[Uml::RoleType::A]->y() + m_pOw[Uml::RoleType::A]->height(); int heightB = m_pOw[Uml::RoleType::B]->y() + m_pOw[Uml::RoleType::B]->height(); int height = heightA; if(heightA < heightB) { height = heightB; } return height; } /** * Returns the maximum height this widget should be set at on * a sequence diagrams. Takes into account the widget positions * it is related to. */ int MessageWidget::getMaxY() { if(!m_pOw[Uml::RoleType::A] || !m_pOw[Uml::RoleType::B]) { return 0; } int heightA = (int)((ObjectWidget*)m_pOw[Uml::RoleType::A])->getEndLineY(); int heightB = (int)((ObjectWidget*)m_pOw[Uml::RoleType::B])->getEndLineY(); int height = heightA; if(heightA > heightB) { height = heightB; } return (height - this->height()); } /** * Overrides method from UMLWidget. */ QSizeF MessageWidget::minimumSize() const { if (m_sequenceMessageType == Uml::SequenceMessage::Synchronous) { return QSizeF(width(), 20); } else if (m_sequenceMessageType == Uml::SequenceMessage::Asynchronous) { return isSelf() ? QSizeF(width(), 20) : QSizeF(width(), 8); } else if (m_sequenceMessageType == Uml::SequenceMessage::Creation) { return QSizeF(width(), 8); + } else if (m_sequenceMessageType == Uml::SequenceMessage::Destroy) { + return QSizeF(width(), 8); } else if (m_sequenceMessageType == Uml::SequenceMessage::Lost) { return QSizeF(width(), 10); } else if (m_sequenceMessageType == Uml::SequenceMessage::Found) { return QSizeF(width(), 10); } else { uWarning() << "Unknown message type"; } return QSize(width(), height()); } /** * Sets the related widget on the given side. * * @param ow The ObjectWidget we are related to. * @param role The Uml::RoleType::Enum to be set for the ObjectWidget */ void MessageWidget::setObjectWidget(ObjectWidget * ow, Uml::RoleType::Enum role) { m_pOw[role] = ow; updateResizability(); } /** * Returns the related widget on the given side. * * @return The ObjectWidget we are related to. */ ObjectWidget* MessageWidget::objectWidget(Uml::RoleType::Enum role) { return m_pOw[role]; } /** * Set the xclicked */ void MessageWidget::setxclicked(int xclick) { m_xclicked = xclick; } /** * Set the yclicked */ void MessageWidget::setyclicked(int yclick) { m_yclicked = yclick; } /** * Show a properties dialog for an ObjectWidget. */ bool MessageWidget::showPropertiesDialog() { if (!lwClassifier()) { uError() << "lwClassifier() returns a NULL classifier"; return false; } bool result = false; UMLApp::app()->docWindow()->updateDocumentation(false); QPointer dlg = new MessageWidgetPropertiesDialog(0, this); if (dlg->exec()) { m_pFText->setMessageText(); UMLApp::app()->docWindow()->showDocumentation(this, true); UMLApp::app()->document()->setModified(true); result = true; } delete dlg; return result; } /** * Saves to the "messagewidget" XMI element. */ void MessageWidget::saveToXMI1(QDomDocument & qDoc, QDomElement & qElement) { QDomElement messageElement = qDoc.createElement(QLatin1String("messagewidget")); UMLWidget::saveToXMI1(qDoc, messageElement); LinkWidget::saveToXMI1(qDoc, messageElement); if (m_pOw[Uml::RoleType::A]) messageElement.setAttribute(QLatin1String("widgetaid"), Uml::ID::toString(m_pOw[Uml::RoleType::A]->localID())); if (m_pOw[Uml::RoleType::B]) messageElement.setAttribute(QLatin1String("widgetbid"), Uml::ID::toString(m_pOw[Uml::RoleType::B]->localID())); UMLOperation *pOperation = operation(); if (pOperation) messageElement.setAttribute(QLatin1String("operation"), Uml::ID::toString(pOperation->id())); else messageElement.setAttribute(QLatin1String("operation"), m_CustomOp); messageElement.setAttribute(QLatin1String("sequencemessagetype"), m_sequenceMessageType); if (m_sequenceMessageType == Uml::SequenceMessage::Lost || m_sequenceMessageType == Uml::SequenceMessage::Found) { messageElement.setAttribute(QLatin1String("xclicked"), m_xclicked); messageElement.setAttribute(QLatin1String("yclicked"), m_yclicked); } // save the corresponding message text if (m_pFText && !m_pFText->text().isEmpty()) { messageElement.setAttribute(QLatin1String("textid"), Uml::ID::toString(m_pFText->id())); m_pFText->saveToXMI1(qDoc, messageElement); } qElement.appendChild(messageElement); } /** * Loads from the "messagewidget" XMI element. */ bool MessageWidget::loadFromXMI1(QDomElement& qElement) { if (!UMLWidget::loadFromXMI1(qElement)) { return false; } if (!LinkWidget::loadFromXMI1(qElement)) { return false; } QString textid = qElement.attribute(QLatin1String("textid"), QLatin1String("-1")); QString widgetaid = qElement.attribute(QLatin1String("widgetaid"), QLatin1String("-1")); QString widgetbid = qElement.attribute(QLatin1String("widgetbid"), QLatin1String("-1")); m_CustomOp = qElement.attribute(QLatin1String("operation")); QString sequenceMessageType = qElement.attribute(QLatin1String("sequencemessagetype"), QLatin1String("1001")); m_sequenceMessageType = Uml::SequenceMessage::fromInt(sequenceMessageType.toInt()); if (m_sequenceMessageType == Uml::SequenceMessage::Lost || m_sequenceMessageType == Uml::SequenceMessage::Found) { m_xclicked = qElement.attribute(QLatin1String("xclicked"), QLatin1String("-1")).toInt(); m_yclicked = qElement.attribute(QLatin1String("yclicked"), QLatin1String("-1")).toInt(); } m_widgetAId = Uml::ID::fromString(widgetaid); m_widgetBId = Uml::ID::fromString(widgetbid); m_textId = Uml::ID::fromString(textid); Uml::TextRole::Enum tr = Uml::TextRole::Seq_Message; if (m_widgetAId == m_widgetBId) tr = Uml::TextRole::Seq_Message_Self; //now load child elements QDomNode node = qElement.firstChild(); QDomElement element = node.toElement(); if (!element.isNull()) { QString tag = element.tagName(); if (tag == QLatin1String("floatingtext") || tag == QLatin1String("UML::FloatingTextWidget")) { m_pFText = new FloatingTextWidget(m_scene, tr, operationText(m_scene), m_textId); m_scene->addFloatingTextWidget(m_pFText); if(! m_pFText->loadFromXMI1(element)) { // Most likely cause: The FloatingTextWidget is empty. delete m_pFText; m_pFText = 0; } else m_pFText->setSequenceNumber(m_SequenceNumber); } else { uError() << "unknown tag " << tag; } } return true; } diff --git a/umbrello/umlwidgets/messagewidget.h b/umbrello/umlwidgets/messagewidget.h index f3cf624e9..dcf10a3ff 100644 --- a/umbrello/umlwidgets/messagewidget.h +++ b/umbrello/umlwidgets/messagewidget.h @@ -1,214 +1,216 @@ /*************************************************************************** * 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-2020 * * Umbrello UML Modeller Authors * ***************************************************************************/ #ifndef MESSAGEWIDGET_H #define MESSAGEWIDGET_H #include "umlwidget.h" #include "linkwidget.h" // forward declarations class FloatingTextWidget; class ObjectWidget; class QResizeEvent; class UMLOperation; /** * Used to display a message on a sequence diagram. The message * could be between two objects or a message that calls itself on * an object. This class will only display the line that is * required and the text will be setup by the @ref FloatingTextWidget * widget that is passed in the constructor. A message can be * synchronous (calls a method and gains control back on return, * as happens in most programming languages) or asynchronous * (calls a method and gains back control immediately). * * @short Displays a message. * @author Paul Hensgen * @see UMLWidget * @see ObjectWidget * @see FloatingTextWidget * Bugs and comments to umbrello-devel@kde.org or https://bugs.kde.org */ class MessageWidget : public UMLWidget, public LinkWidget { Q_OBJECT public: MessageWidget(UMLScene * scene, ObjectWidget* a, ObjectWidget* b, int y, Uml::SequenceMessage::Enum sequenceMessageType, Uml::ID::Type id = Uml::ID::None); MessageWidget(UMLScene * scene, Uml::SequenceMessage::Enum sequenceMessageType, Uml::ID::Type id = Uml::ID::None); MessageWidget(UMLScene * scene, ObjectWidget* a, int xclick, int yclick, Uml::SequenceMessage::Enum sequenceMessageType, Uml::ID::Type id = Uml::ID::None); virtual ~MessageWidget(); virtual void setY(qreal y); //---------- LinkWidget Interface methods implemementation from now on. virtual void lwSetFont (QFont font); virtual UMLClassifier *operationOwner(); virtual UMLOperation *operation(); virtual void setOperation(UMLOperation *op); virtual QString customOpText(); virtual void setCustomOpText(const QString &opText); virtual void setMessageText(FloatingTextWidget *ft); virtual void setText(FloatingTextWidget *ft, const QString &newText); virtual QString lwOperationText(); virtual UMLClassifier *lwClassifier(); virtual void setOperationText(const QString &op); virtual void constrainTextPos(qreal &textX, qreal &textY, qreal textWidth, qreal textHeight, Uml::TextRole::Enum tr); //---------- End LinkWidget Interface methods implemementation. /// @return Whether the message is synchronous or asynchronous Uml::SequenceMessage::Enum sequenceMessageType() const { return m_sequenceMessageType; } bool hasObjectWidget(ObjectWidget * w); ObjectWidget* objectWidget(Uml::RoleType::Enum role); void setObjectWidget(ObjectWidget * ow, Uml::RoleType::Enum role) ; bool isSelf() const; /** * Returns the text widget it is related to. * * @return The text widget we are related to. */ FloatingTextWidget * floatingTextWidget() { return m_pFText; } /** * Sets the text widget it is related to. * * @param f The text widget we are related to. */ void setFloatingTextWidget(FloatingTextWidget * f) { m_pFText = f; } void calculateWidget(); virtual bool activate(IDChangeLog * Log = 0); void resolveObjectWidget(IDChangeLog* log); void calculateDimensions(); void calculateDimensionsSynchronous(); void calculateDimensionsAsynchronous(); void calculateDimensionsCreation(); + void calculateDimensionsDestroy(); void calculateDimensionsLost(); void calculateDimensionsFound(); virtual void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = 0); void setTextPosition(); void cleanup(); void setSelected(bool _select); int getMinY(); int getMaxY(); virtual QSizeF minimumSize() const; UMLWidget* onWidget(const QPointF& p); virtual void resizeWidget(qreal newW, qreal newH); virtual void saveToXMI1(QDomDocument & qDoc, QDomElement & qElement); virtual bool loadFromXMI1(QDomElement & qElement); void setxclicked(int xclick); void setyclicked(int yclick); /** * Return the xclicked */ int getxclicked() const { return m_xclicked; } virtual bool showPropertiesDialog(); protected: virtual void moveWidgetBy(qreal diffX, qreal diffY); virtual void constrainMovementForAllWidgets(qreal &diffX, qreal &diffY); virtual QCursor resizeCursor() const; void setLinkAndTextPos(); int constrainX(int textX, int textWidth, Uml::TextRole::Enum tr); static void paintArrow(QPainter *p, int x, int y, int w, Qt::ArrowType direction, bool useDottedLine = false); static void paintSolidArrowhead(QPainter *p, int x, int y, Qt::ArrowType direction); void updateResizability(); void paintSynchronous(QPainter *painter, const QStyleOptionGraphicsItem *option); void paintAsynchronous(QPainter *painter, const QStyleOptionGraphicsItem *option); void paintCreation(QPainter *painter, const QStyleOptionGraphicsItem *option); + void paintDestroy(QPainter *painter, const QStyleOptionGraphicsItem *option); void paintLost(QPainter *painter, const QStyleOptionGraphicsItem *option); void paintFound(QPainter *painter, const QStyleOptionGraphicsItem *option); // Data loaded/saved QString m_CustomOp; /** * Whether the message is synchronous or asynchronous */ Uml::SequenceMessage::Enum m_sequenceMessageType; private: void resizeEvent(QResizeEvent *re); qreal constrainPositionY(qreal diffY); void init(); QPointer m_pOw[2]; FloatingTextWidget * m_pFText; int m_xclicked; int m_yclicked; /** * The following variables are used by loadFromXMI1() as an intermediate * store. activate() resolves the IDs, i.e. after activate() the variables * m_pOw[] and m_pFText can be used. */ Uml::ID::Type m_widgetAId, m_widgetBId, m_textId; public slots: void slotWidgetMoved(Uml::ID::Type id); void slotMenuSelection(QAction* action); signals: /** * emitted when the message widget is moved up or down * slots into ObjectWidget::slotMessageMoved() */ void sigMessageMoved(); }; #endif diff --git a/umbrello/umlwidgets/objectwidget.cpp b/umbrello/umlwidgets/objectwidget.cpp index 46d48e843..763f24b76 100644 --- a/umbrello/umlwidgets/objectwidget.cpp +++ b/umbrello/umlwidgets/objectwidget.cpp @@ -1,705 +1,703 @@ /*************************************************************************** * 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-2020 * * Umbrello UML Modeller Authors * ***************************************************************************/ // own header file #include "objectwidget.h" // local includes #include "classpropertiesdialog.h" #include "debug_utils.h" #include "dialog_utils.h" #include "docwindow.h" #include "listpopupmenu.h" #include "messagewidget.h" #include "seqlinewidget.h" #include "uml.h" #include "umldoc.h" #include "umlobject.h" #include "umlscene.h" #include "umlview.h" // kde includes #include // qt includes #include #include #include #define O_MARGIN 5 #define O_WIDTH 40 #define A_WIDTH 20 #define A_HEIGHT 40 #define A_MARGIN 5 DEBUG_REGISTER_DISABLED(ObjectWidget) /** * The number of pixels margin between the lowest message * and the bottom of the vertical line */ static const int sequenceLineMargin = 20; /** * Creates an ObjectWidget. * * @param scene The parent to this object. * @param o The object it will be representing. */ ObjectWidget::ObjectWidget(UMLScene * scene, UMLObject *o) : UMLWidget(scene, WidgetBase::wt_Object, o), m_multipleInstance(false), m_drawAsActor(false), m_showDestruction(false), m_isOnDestructionBox(false) { if (m_scene && m_scene->isSequenceDiagram()) { m_pLine = new SeqLineWidget( m_scene, this ); m_pLine->setStartPoint(x() + width() / 2, y() + height()); } else { m_pLine = 0; } } /** * Destructor. */ ObjectWidget::~ObjectWidget() { cleanup(); } /** * Sets whether representing a multi-instance object. * * @param multiple Object state. true- multi, false - single. */ void ObjectWidget::setMultipleInstance(bool multiple) { //make sure only calling this in relation to an object on a collab. diagram if (m_scene && m_scene->isCollaborationDiagram()) { m_multipleInstance = multiple; updateGeometry(); update(); } } /** * Returns whether object is representing a multi-object. * * @return True if object is representing a multi-object. */ bool ObjectWidget::multipleInstance() const { return m_multipleInstance; } void ObjectWidget::setSelected(bool state) { UMLWidget::setSelected(state); if (m_pLine) { QPen pen = m_pLine->pen(); int lineWidth = (int)UMLWidget::lineWidth(); if (state) lineWidth = lineWidth ? lineWidth * 2 : 2; pen.setWidth(lineWidth); m_pLine->setPen(pen); } } /** * Overridden from UMLWidget. * Moves the widget to a new position using the difference between the * current position and the new position. * Y position is ignored, and widget is only moved along X axis. * * @param diffX The difference between current X position and new X position. * @param diffY The difference between current Y position and new Y position * (isn't used). */ void ObjectWidget::moveWidgetBy(qreal diffX, qreal diffY) { setX(x() + diffX); if (m_scene && (m_scene->type() != Uml::DiagramType::Sequence)) { setY(y() + diffY); } } /** * Overridden from UMLWidget. * Modifies the value of the diffX and diffY variables used to move the widgets. * All the widgets are constrained to be moved only in X axis (diffY is set to 0). * * @param diffX The difference between current X position and new X position. * @param diffY The difference between current Y position and new Y position. */ void ObjectWidget::constrainMovementForAllWidgets(qreal &diffX, qreal &diffY) { Q_UNUSED(diffX); if (m_scene && m_scene->isSequenceDiagram()) { diffY = 0; } } /** * Override default method. */ void ObjectWidget::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) { Q_UNUSED(option); Q_UNUSED(widget); if (m_drawAsActor) paintActor(painter); else paintObject(painter); setPenFromSettings(painter); UMLWidget::paint(painter, option, widget); } /** * Handles a popup menu selection. */ void ObjectWidget::slotMenuSelection(QAction* action) { ListPopupMenu::MenuType sel = ListPopupMenu::typeFromAction(action); switch(sel) { case ListPopupMenu::mt_Properties: showPropertiesDialog(); updateGeometry(); moveEvent(0); update(); break; case ListPopupMenu::mt_Up: tabUp(); break; case ListPopupMenu::mt_Down: tabDown(); break; default: UMLWidget::slotMenuSelection(action); break; } } /** * Overrides method from UMLWidget */ QSizeF ObjectWidget::minimumSize() const { int width, height; const QFontMetrics &fm = getFontMetrics(FT_UNDERLINE); const int fontHeight = fm.lineSpacing(); const QString t = m_instanceName + QLatin1String(" : ") + name(); const int textWidth = fm.width(t); if (m_drawAsActor) { width = textWidth > A_WIDTH?textWidth:A_WIDTH; height = A_HEIGHT + fontHeight + A_MARGIN; width += A_MARGIN * 2; } else { width = textWidth > O_WIDTH?textWidth:O_WIDTH; height = fontHeight + O_MARGIN * 2; width += O_MARGIN * 2; if (m_multipleInstance) { width += 10; height += 10; } }//end else drawasactor return QSizeF(width, height); } /** * Sets whether to draw as an Actor. * * @param drawAsActor True if widget shall be drawn as an actor. */ void ObjectWidget::setDrawAsActor(bool drawAsActor) { m_drawAsActor = drawAsActor; updateGeometry(); } /** * Returns whether to draw as an Actor or not. * * @return True if widget is drawn as an actor. */ bool ObjectWidget::drawAsActor() const { return m_drawAsActor; } /** * Activate the object after serializing it from a QDataStream */ bool ObjectWidget::activate(IDChangeLog* ChangeLog /*= 0*/) { if (! UMLWidget::activate(ChangeLog)) return false; if (m_showDestruction && m_pLine) m_pLine->setupDestructionBox(); moveEvent(0); return true; } /** * Sets the x-coordinate. * Reimplements the method from UMLWidget. * * @param x The x-coordinate to be set. */ void ObjectWidget::setX(qreal x) { UMLWidget::setX(x); moveEvent(0); } /** * Sets the y-coordinate. * Reimplements the method from UMLWidget. * * @param y The y-coordinate to be set. */ void ObjectWidget::setY(qreal y) { UMLWidget::setY(y); if (!UMLApp::app()->document()->loading()) moveEvent(0); } /** * Return the x coordinate of the widgets center. * @return The x-coordinate of the widget center. */ qreal ObjectWidget::centerX() { return x() + width()/2; } /** * Overrides the standard operation. */ void ObjectWidget::moveEvent(QGraphicsSceneMouseEvent *event) { Q_UNUSED(event) emit sigWidgetMoved(m_nLocalID); if (m_pLine) { m_pLine->setStartPoint(x() + width() / 2, y() + height()); } } /** * Overrides the standard operation. */ void ObjectWidget::mousePressEvent(QGraphicsSceneMouseEvent *me) { UMLWidget::mousePressEvent(me); m_isOnDestructionBox = false; if (m_pLine && m_pLine->onDestructionBox(me->scenePos())) { m_isOnDestructionBox = true; qreal oldX = x() + width() / 2; qreal oldY = getEndLineY() - 10; m_oldPos = QPointF(oldX, oldY); } } /** * Overrides the standard operation. */ void ObjectWidget::mouseMoveEvent(QGraphicsSceneMouseEvent* me) { if (m_inResizeArea) { DEBUG(DBG_SRC) << "resizing..."; resize(me); moveEvent(0); return; } if (m_isOnDestructionBox) { qreal diffY = me->scenePos().y() - m_oldPos.y(); moveDestructionBy(diffY); } else { UMLWidget::mouseMoveEvent(me); } } /** * Moves the destruction Box to a new position using the difference between the * current position and the new position. * The destruction box is only moved along Y axis. * * @param diffY The difference between current Y position and new Y position */ void ObjectWidget::moveDestructionBy(qreal diffY) { // endLine = length of the life line + diffY - 10 to center on the destruction box qreal endLine = getEndLineY() + diffY - 10; SeqLineWidget *pLine = sequentialLine(); pLine->setEndOfLine(endLine); m_oldPos.setY(endLine); } /** * Handles a color change signal. */ void ObjectWidget::slotFillColorChanged(Uml::ID::Type /*viewID*/) { UMLWidget::setFillColor(m_scene->fillColor()); UMLWidget::setLineColor(m_scene->lineColor()); if(m_pLine) m_pLine->setPen(QPen(UMLWidget::lineColor(), UMLWidget::lineWidth(), Qt::DashLine)); } /** * Used to cleanup any other widget it may need to delete. */ void ObjectWidget::cleanup() { UMLWidget::cleanup(); if(m_pLine) { m_pLine->cleanup(); delete m_pLine; m_pLine = 0; } } /** * Show a properties dialog for an ObjectWidget. */ bool ObjectWidget::showPropertiesDialog() { bool result = false; UMLApp::app()->docWindow()->updateDocumentation(false); QPointer dlg = new ClassPropertiesDialog((QWidget*)UMLApp::app(), this); if (dlg->exec()) { UMLApp::app()->docWindow()->showDocumentation(this, true); UMLApp::app()->document()->setModified(true); result = true; } dlg->close(); delete dlg; return result; } /** * Draw the object as an object (default). */ void ObjectWidget::paintObject(QPainter *painter) { QFont oldFont = painter->font(); QFont font = UMLWidget::font(); font.setUnderline(true); painter->setFont(font); setPenFromSettings(painter); if(UMLWidget::useFillColor()) painter->setBrush(UMLWidget::fillColor()); else painter->setBrush(m_scene->backgroundColor()); const int w = width(); const int h = height(); const QString t = m_instanceName + QLatin1String(" : ") + name(); int multiInstOfst = 0; if (m_multipleInstance) { painter->drawRect(10, 10, w - 10, h - 10); painter->drawRect(5, 5, w - 10, h - 10); multiInstOfst = 10; } painter->drawRect(0, 0, w - multiInstOfst, h - multiInstOfst); painter->setPen(textColor()); painter->drawText(O_MARGIN, O_MARGIN, w - O_MARGIN * 2 - multiInstOfst, h - O_MARGIN * 2 - multiInstOfst, Qt::AlignCenter, t); painter->setFont(oldFont); } /** * Draw the object as an actor. */ void ObjectWidget::paintActor(QPainter *painter) { const QFontMetrics &fm = getFontMetrics(FT_UNDERLINE); setPenFromSettings(painter); if (UMLWidget::useFillColor()) painter->setBrush(UMLWidget::fillColor()); const int w = width(); const int textStartY = A_HEIGHT + A_MARGIN; const int fontHeight = fm.lineSpacing(); const int middleX = w / 2; const int thirdH = A_HEIGHT / 3; //draw actor painter->drawEllipse(middleX - A_WIDTH / 2, 0, A_WIDTH, thirdH);//head painter->drawLine(middleX, thirdH, middleX, thirdH * 2);//body painter->drawLine(middleX, 2 * thirdH, middleX - A_WIDTH / 2, A_HEIGHT);//left leg painter->drawLine(middleX, 2 * thirdH, middleX + A_WIDTH / 2, A_HEIGHT);//right leg painter->drawLine(middleX - A_WIDTH / 2, thirdH + thirdH / 2, middleX + A_WIDTH / 2, thirdH + thirdH / 2);//arms //draw text painter->setPen(textColor()); QString t = m_instanceName + QLatin1String(" : ") + name(); painter->drawText(A_MARGIN, textStartY, w - A_MARGIN * 2, fontHeight, Qt::AlignCenter, t); } /** * Move the object up on a sequence diagram. */ void ObjectWidget::tabUp() { int newY = y() - height(); if (newY < topMargin()) newY = topMargin(); setY(newY); adjustAssocs(x(), newY); } /** * Move the object down on a sequence diagram. */ void ObjectWidget::tabDown() { int newY = y() + height(); setY(newY); adjustAssocs(x(), newY); } /** * Returns the top margin constant (Y axis value) * * @return Y coordinate of the space between the diagram top * and the upper edge of the ObjectWidget. */ int ObjectWidget::topMargin() { return 80 - height(); } /** * Returns whether or not the widget can be moved vertically up. * * @return True if widget can be moved upwards vertically. */ bool ObjectWidget::canTabUp() { return (y() > topMargin()); } /** * Sets whether to show deconstruction on sequence line. * * @param bShow True if destruction on line shall be shown. */ void ObjectWidget::setShowDestruction(bool bShow) { m_showDestruction = bShow; if(m_pLine) m_pLine->setupDestructionBox(); } /** * Returns whether to show deconstruction on sequence line. * * @return True if destruction on sequence line is shown. */ bool ObjectWidget::showDestruction() const { return m_showDestruction; } /** * Sets the y position of the bottom of the vertical line. * * @param yPosition The y coordinate for the bottom of the line. */ void ObjectWidget::setEndLine(int yPosition) { if (m_pLine) { m_pLine->setEndOfLine(yPosition); } } /** * Returns the end Y co-ord of the sequence line. * * @return Y coordinate of the endpoint of the sequence line. */ int ObjectWidget::getEndLineY() { int y = this->y() + height(); if(m_pLine) y += m_pLine->getLineLength(); if (m_showDestruction) y += 10; return y; } /** * Add a message widget to the list. * * @param message Pointer to the MessageWidget to add. */ void ObjectWidget::messageAdded(MessageWidget* message) { if (m_messages.count(message)) { uError() << message->name() << ": duplicate entry !"; return ; } m_messages.append(message); } /** * Remove a message widget from the list. * * @param message Pointer to the MessageWidget to remove. */ void ObjectWidget::messageRemoved(MessageWidget* message) { if (m_messages.removeAll(message) == false) { uError() << message->name() << ": missing entry !"; return ; } } /** * Called when a message widget with an end on this object has * moved up or down. * Sets the bottom of the line to a nice position. */ void ObjectWidget::slotMessageMoved() { if (m_pLine) { int lowestMessage = 0; foreach (MessageWidget* message, m_messages) { int messageHeight = message->y() + message->height(); if (lowestMessage < messageHeight) { lowestMessage = messageHeight; } } m_pLine->setEndOfLine(lowestMessage + sequenceLineMargin); } } /** * Returns whether a message is overlapping with another message. * Used by MessageWidget::paint() methods. * * @param y top of your message * @param messageWidget pointer to your message so it doesn't check against itself */ bool ObjectWidget::messageOverlap(qreal y, MessageWidget* messageWidget) { foreach (MessageWidget* message, m_messages) { if (message == messageWidget) { continue; } const qreal msgY = message->y(); const qreal msgHeight = msgY + message->height(); if (y >= msgY && y <= msgHeight) { return true; } } return false; } /** * Overridden from UMLWidget * Set color of object widget and sequence line on sequence diagrams. */ void ObjectWidget::setLineColorCmd(const QColor &color) { UMLWidget::setLineColorCmd(color); if (m_pLine) { - QPen pen = m_pLine->pen(); - pen.setColor(color); - m_pLine->setPen(pen); + m_pLine->setLineColorCmd(color); } } /** * Return the SeqLineWidget. * Returns a non NULL pointer if this ObjectWidget is part of a * sequence diagram. */ SeqLineWidget *ObjectWidget::sequentialLine() const { return m_pLine; } /** * Overridden from UMLWidget. * Returns the cursor to be shown when resizing the widget. * The cursor shown is KCursor::sizeHorCursor(). * * @return The cursor to be shown when resizing the widget. */ QCursor ObjectWidget::resizeCursor() const { return Qt::SizeHorCursor; } /** * Overridden from UMLWidget. * Resizes the width of the object widget. * Object widgets can only be resized horizontally, so height isn't modified. * * @param newW The new width for the widget. * @param newH The new height for the widget (isn't used). */ void ObjectWidget::resizeWidget(qreal newW, qreal newH) { Q_UNUSED(newH); setSize(newW, height()); } /** * Saves to the "objectwidget" XMI element. */ void ObjectWidget::saveToXMI1(QDomDocument& qDoc, QDomElement& qElement) { QDomElement objectElement = qDoc.createElement(QLatin1String("objectwidget")); UMLWidget::saveToXMI1(qDoc, objectElement); objectElement.setAttribute(QLatin1String("drawasactor"), m_drawAsActor); objectElement.setAttribute(QLatin1String("multipleinstance"), m_multipleInstance); objectElement.setAttribute(QLatin1String("decon"), m_showDestruction); qElement.appendChild(objectElement); } /** * Loads from a "objectwidget" XMI element. */ bool ObjectWidget::loadFromXMI1(QDomElement& qElement) { if(!UMLWidget::loadFromXMI1(qElement)) return false; QString draw = qElement.attribute(QLatin1String("drawasactor"), QLatin1String("0")); QString multi = qElement.attribute(QLatin1String("multipleinstance"), QLatin1String("0")); QString decon = qElement.attribute(QLatin1String("decon"), QLatin1String("0")); m_drawAsActor = (bool)draw.toInt(); m_multipleInstance = (bool)multi.toInt(); m_showDestruction = (bool)decon.toInt(); return true; } diff --git a/umbrello/umlwidgets/objectwidget.h b/umbrello/umlwidgets/objectwidget.h index 08bf8f7bf..6088e00d5 100644 --- a/umbrello/umlwidgets/objectwidget.h +++ b/umbrello/umlwidgets/objectwidget.h @@ -1,119 +1,120 @@ /*************************************************************************** * 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-2020 * * Umbrello UML Modeller Authors * ***************************************************************************/ #ifndef OBJECTWIDGET_H #define OBJECTWIDGET_H #include "messagewidgetlist.h" #include "umlwidget.h" class MessageWidget; class SeqLineWidget; class UMLScene; /** * Displays an instance UMLObject of a concept. * * The local ID is needed as it can represent a class * that has many objects representing it. * * @short Displays an instance of a Concept. * @author Paul Hensgen * @see UMLWidget * Bugs and comments to umbrello-devel@kde.org or https://bugs.kde.org */ class ObjectWidget : public UMLWidget { Q_OBJECT public: ObjectWidget(UMLScene *scene, UMLObject *o); virtual ~ObjectWidget(); virtual void setX(qreal x); virtual void setY(qreal y); qreal centerX(); void setMultipleInstance(bool multiple); bool multipleInstance() const; void setSelected(bool state); virtual void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = 0); bool activate(IDChangeLog* ChangeLog = 0); void cleanup(); virtual bool showPropertiesDialog(); void setDrawAsActor(bool drawAsActor); bool drawAsActor() const; void setShowDestruction(bool bShow); bool showDestruction() const; int topMargin(); void setEndLine(int yPosition); int getEndLineY(); void messageAdded(MessageWidget* message); void messageRemoved(MessageWidget* message); bool canTabUp(); bool messageOverlap(qreal y, MessageWidget* messageWidget); virtual void setLineColorCmd(const QColor &color); SeqLineWidget *sequentialLine() const; virtual void resizeWidget(qreal newW, qreal newH); virtual void saveToXMI1(QDomDocument& qDoc, QDomElement& qElement); virtual bool loadFromXMI1(QDomElement& qElement); public slots: void slotMenuSelection(QAction* action); virtual void slotFillColorChanged(Uml::ID::Type viewID); void slotMessageMoved(); protected: virtual void mousePressEvent(QGraphicsSceneMouseEvent *me); virtual void mouseMoveEvent(QGraphicsSceneMouseEvent *me); QSizeF minimumSize() const; virtual void moveEvent(QGraphicsSceneMouseEvent *event); virtual void moveWidgetBy(qreal diffX, qreal diffY); virtual void constrainMovementForAllWidgets(qreal &diffX, qreal &diffY); virtual QCursor resizeCursor() const; void paintActor(QPainter *p); void paintObject(QPainter *p); private: void tabUp(); void tabDown(); void moveDestructionBy(qreal diffY); SeqLineWidget* m_pLine; bool m_multipleInstance; ///< draw an object as a multiple object bool m_drawAsActor; ///< object should be drawn as an Actor or an Object bool m_showDestruction; ///< show object destruction on sequence diagram line bool m_isOnDestructionBox; ///< true when a click occurred on the destruction box MessageWidgetList m_messages; ///< message widgets with an end on this widget + friend class SeqLineWidget; }; #endif diff --git a/umbrello/umlwidgets/seqlinewidget.cpp b/umbrello/umlwidgets/seqlinewidget.cpp index c8ce63c1e..d6b227478 100644 --- a/umbrello/umlwidgets/seqlinewidget.cpp +++ b/umbrello/umlwidgets/seqlinewidget.cpp @@ -1,201 +1,214 @@ /*************************************************************************** * 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-2020 * * Umbrello UML Modeller Authors * ***************************************************************************/ // own header #include "seqlinewidget.h" //app includes #include "debug_utils.h" #include "messagewidget.h" #include "objectwidget.h" #include "umlscene.h" #include "umlview.h" +#include "widgetbasepopupmenu.h" //qt includes #include DEBUG_REGISTER_DISABLED(SeqLineWidget) // class members int const SeqLineWidget::m_nMouseDownEpsilonX = 20; /** * Constructor. */ SeqLineWidget::SeqLineWidget(UMLScene *scene, ObjectWidget * pObject) : QGraphicsLineItem(), m_scene(scene) { scene->addItem(this); m_pObject = pObject; setPen(QPen(m_pObject->lineColor(), 0, Qt::DashLine)); setZValue(0); setVisible(true); - m_DestructionBox.line1 = 0; m_nLengthY = 250; setupDestructionBox(); } /** * Destructor. */ SeqLineWidget::~SeqLineWidget() { cleanupDestructionBox(); } /** * Return whether point is on sequence line. * Takes into account destruction box if shown. * * @param p The point to investigate. * @return True if point is on this sequence line. */ bool SeqLineWidget::onWidget(const QPointF & p) { bool isOnWidget = false; QPointF sp = line().p1(); QPointF ep = line().p2(); //see if on widget (for message creation) if (sp.x() - m_nMouseDownEpsilonX < p.x() && ep.x() + m_nMouseDownEpsilonX > p.x() && sp.y() < p.y() && ep.y() + 3 > p.y()) { isOnWidget = true; } return isOnWidget; } /** * Return whether point is on the destruction box. * * @param p The point to investigate. * @return True if point is on the destruction box of this sequence line. */ bool SeqLineWidget::onDestructionBox(const QPointF & p) { bool isOnDestructionBox = false; int x = m_pObject->x() + m_pObject->width() / 2; int y = m_pObject->y() + m_pObject->height() + m_nLengthY; //see if on destruction box if (!m_pObject->showDestruction()) { return false; } if (x - 10 < p.x() && x + 10 > p.x() && y - 10 < p.y() && y + 10 > p.y()) { isOnDestructionBox = true; } return isOnDestructionBox; } /** * Clean up anything before deletion. */ void SeqLineWidget::cleanup() { cleanupDestructionBox(); } /** * Set the start point of the line. * * @param startX X coordinate of the start point. * @param startY Y coordinate of the start point. */ void SeqLineWidget::setStartPoint(int startX, int startY) { int endX = startX; int endY = startY + m_nLengthY; QGraphicsLineItem::setLine(startX, startY, endX, endY); moveDestructionBox(); } /** * Clean up destruction box. */ void SeqLineWidget::cleanupDestructionBox() { if (m_DestructionBox.line1) { delete m_DestructionBox.line1; m_DestructionBox.line1 = 0; delete m_DestructionBox.line2; m_DestructionBox.line2 = 0; } } /** * Set up destruction box. */ void SeqLineWidget::setupDestructionBox() { cleanupDestructionBox(); if(!m_pObject->showDestruction()) { return; } QRect rect; rect.setX(m_pObject->x() + m_pObject->width() / 2 - 7); rect.setY(m_pObject->y() + m_pObject->height() + m_nLengthY - 7); rect.setWidth(14); rect.setHeight(14); m_DestructionBox.line1 = new QGraphicsLineItem; m_scene->addItem(m_DestructionBox.line1); m_DestructionBox.setLine1Points(rect); m_DestructionBox.line1->setVisible(true); m_DestructionBox.line1->setPen(QPen(m_pObject->lineColor(), 2)); m_DestructionBox.line1->setZValue(3); m_DestructionBox.line2 = new QGraphicsLineItem; m_scene->addItem(m_DestructionBox.line2); m_DestructionBox.setLine2Points(rect); m_DestructionBox.line2->setVisible(true); m_DestructionBox.line2->setPen(QPen(m_pObject->lineColor(), 2)); m_DestructionBox.line2->setZValue(3); } /** * Move destruction box. */ void SeqLineWidget::moveDestructionBox() { if(!m_DestructionBox.line1) { return; } QRect rect; rect.setX(m_pObject->x() + m_pObject->width() / 2 - 7); rect.setY(m_pObject->y() + m_pObject->height() + m_nLengthY - 7); rect.setWidth(14); rect.setHeight(14); m_DestructionBox.setLine1Points(rect); m_DestructionBox.setLine2Points(rect); } /** * Sets the y position of the bottom of the vertical line. * * @param yPosition The y coordinate for the bottom of the line. */ void SeqLineWidget::setEndOfLine(int yPosition) { QPointF sp = line().p1(); int newY = yPosition; m_nLengthY = yPosition - m_pObject->y() - m_pObject->height(); // normally the managing Objectwidget is responsible for the call of this function // but to be sure - make a double check _against current position_ if (m_nLengthY < 0) { m_nLengthY = 0; newY = m_pObject->y() + m_pObject->height(); } setLine(sp.x(), sp.y(), sp.x(), newY); moveDestructionBox(); m_scene->resizeSceneToItems(); } + +void SeqLineWidget::setLineColorCmd(const QColor &color) +{ + QPen pen = this->pen(); + pen.setColor(color); + setPen(pen); + m_DestructionBox.setLineColorCmd(color); +} + +void SeqLineWidget::contextMenuEvent(QGraphicsSceneContextMenuEvent *event) +{ + m_pObject->contextMenuEvent(event); +} diff --git a/umbrello/umlwidgets/seqlinewidget.h b/umbrello/umlwidgets/seqlinewidget.h index 5e5c0aefe..a1859b0c1 100644 --- a/umbrello/umlwidgets/seqlinewidget.h +++ b/umbrello/umlwidgets/seqlinewidget.h @@ -1,86 +1,100 @@ /*************************************************************************** * 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-2020 * * Umbrello UML Modeller Authors * ***************************************************************************/ #ifndef SEQLINEWIDGET_H #define SEQLINEWIDGET_H #include +#include + class ObjectWidget; class UMLScene; /** * @short Widget class for graphical representation of sequence lines * @author Paul Hensgen * Bugs and comments to umbrello-devel@kde.org or https://bugs.kde.org */ class SeqLineWidget : public QGraphicsLineItem { public: SeqLineWidget(UMLScene *scene, ObjectWidget * pObject); virtual ~SeqLineWidget(); bool onWidget(const QPointF& p); bool onDestructionBox(const QPointF& p); void cleanup(); void setupDestructionBox(); void setStartPoint(int startX, int startY); /** * Gets the length of the line. * * @return Length of the line. */ int getLineLength() { return m_nLengthY; } /** * Returns the @ref ObjectWidget associated with this sequence line. * * @return Pointer to the associated ObjectWidget. */ ObjectWidget * getObjectWidget() { return m_pObject; } void setEndOfLine(int yPosition); + void setLineColorCmd(const QColor &color); protected: void cleanupDestructionBox(); void moveDestructionBox(); ObjectWidget* m_pObject; ///< ObjectWidget associated with this sequence line UMLScene* m_scene; ///< scene displayed on struct DestructionBox { - QGraphicsLineItem * line1; - QGraphicsLineItem * line2; + QGraphicsLineItem * line1{nullptr}; + QGraphicsLineItem * line2{nullptr}; + void setLineColorCmd(const QColor &color) + { + if (!line1) + return; + QPen pen = line1->pen(); + pen.setColor(color); + line1->setPen(pen); + line2->setPen(pen); + } + void setLine1Points(QRect rect) { line1->setLine(rect.x(), rect.y(), rect.x() + rect.width(), rect.y() + rect.height()); } void setLine2Points(QRect rect) { line2->setLine(rect.x(), rect.y() + rect.height(), rect.x() + rect.width(), rect.y()); } } m_DestructionBox; ///< the destruction box int m_nLengthY; ///< the length of the line static int const m_nMouseDownEpsilonX; ///< margin used for mouse clicks + void contextMenuEvent(QGraphicsSceneContextMenuEvent* event); }; #endif diff --git a/umbrello/umlwidgets/umlwidget.cpp b/umbrello/umlwidgets/umlwidget.cpp index ad3c1da53..4666d6c21 100644 --- a/umbrello/umlwidgets/umlwidget.cpp +++ b/umbrello/umlwidgets/umlwidget.cpp @@ -1,2127 +1,2143 @@ /*************************************************************************** * 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-2020 * * Umbrello UML Modeller Authors * ***************************************************************************/ #include "umlwidget.h" // local includes #include "artifact.h" #include "artifactwidget.h" #include "activitywidget.h" #include "actor.h" #include "actorwidget.h" #include "associationwidget.h" #include "classifier.h" #include "classpropertiesdialog.h" #include "cmds.h" #include "component.h" #include "componentwidget.h" #include "debug_utils.h" #include "dialog_utils.h" #include "docwindow.h" #include "floatingtextwidget.h" #include "forkjoinwidget.h" #include "interfacewidget.h" #include "notewidget.h" #include "messagewidget.h" +#include "objectwidget.h" #include "object_factory.h" #include "idchangelog.h" #include "menus/listpopupmenu.h" #include "objectnodewidget.h" #include "pinwidget.h" #include "port.h" #include "portwidget.h" #include "regionwidget.h" #include "signalwidget.h" #include "settingsdialog.h" #include "statewidget.h" #include "uml.h" #include "umldoc.h" #include "umllistview.h" #include "umlobject.h" #include "umlscene.h" #include "umlview.h" #include "usecase.h" #include "usecasewidget.h" #include "uniqueid.h" #include "widget_factory.h" #include "widget_utils.h" // kde includes #include #include // qt includes #include #include #include #include using namespace Uml; DEBUG_REGISTER_DISABLED(UMLWidget) #define I18N_NEXT_RELEASE(a,b) QString(QLatin1String(a)).arg(b)) const QSizeF UMLWidget::DefaultMinimumSize(50, 20); const QSizeF UMLWidget::DefaultMaximumSize(1000, 5000); const int UMLWidget::defaultMargin = 5; const int UMLWidget::selectionMarkerSize = 4; const int UMLWidget::resizeMarkerLineCount = 3; /** * Creates a UMLWidget object. * * @param scene The view to be displayed on. * @param type The WidgetType to construct. * This must be set to the appropriate value by the constructors of inheriting classes. * @param o The UMLObject to represent. * @note Although a pointer to the scene is required, the widget is not added to the scene by default. */ UMLWidget::UMLWidget(UMLScene * scene, WidgetType type, UMLObject * o) : WidgetBase(scene, type, o ? o->id() : Uml::ID::None) , DiagramProxyWidget(this) { init(); m_umlObject = o; if (m_umlObject) { // TODO: calling WidgetBase::setUMLObject does not add this connection connect(m_umlObject, SIGNAL(modified()), this, SLOT(updateWidget())); } } /** * Creates a UMLWidget object. * * @param scene The view to be displayed on. * @param type The WidgetType to construct. * This must be set to the appropriate value by the constructors of inheriting classes. * @param id The id of the widget. * The default value (id_None) will prompt generation of a new ID. */ UMLWidget::UMLWidget(UMLScene *scene, WidgetType type, Uml::ID::Type id) : WidgetBase(scene, type, id) , DiagramProxyWidget(this) { init(); } /** * Destructor. */ UMLWidget::~UMLWidget() { cleanup(); } /** * Assignment operator */ UMLWidget& UMLWidget::operator=(const UMLWidget & other) { if (this == &other) return *this; WidgetBase::operator=(other); DiagramProxyWidget::operator=(other); // assign members loaded/saved m_useFillColor = other.m_useFillColor; m_usesDiagramFillColor = other.m_usesDiagramFillColor; m_usesDiagramUseFillColor = other.m_usesDiagramUseFillColor; m_fillColor = other.m_fillColor; m_Assocs = other.m_Assocs; m_isInstance = other.m_isInstance; m_instanceName = other.m_instanceName; m_instanceName = other.m_instanceName; m_showStereotype = other.m_showStereotype; setX(other.x()); setY(other.y()); setRect(rect().x(), rect().y(), other.width(), other.height()); // assign volatile (non-saved) members m_startMove = other.m_startMove; m_nPosX = other.m_nPosX; m_doc = other.m_doc; //new m_resizable = other.m_resizable; for (unsigned i = 0; i < FT_INVALID; ++i) m_pFontMetrics[i] = other.m_pFontMetrics[i]; m_activated = other.m_activated; m_ignoreSnapToGrid = other.m_ignoreSnapToGrid; m_ignoreSnapComponentSizeToGrid = other.m_ignoreSnapComponentSizeToGrid; return *this; } /** * Overload '==' operator */ bool UMLWidget::operator==(const UMLWidget& other) const { if (this == &other) return true; if (baseType() != other.baseType()) { return false; } if (id() != other.id()) return false; /* Testing the associations is already an exaggeration, no? The type and ID should uniquely identify an UMLWidget. */ if (m_Assocs.count() != other.m_Assocs.count()) { return false; } // if(getBaseType() != wt_Text) // DON'T do this for floatingtext widgets, an infinite loop will result // { AssociationWidgetListIt assoc_it(m_Assocs); AssociationWidgetListIt assoc_it2(other.m_Assocs); AssociationWidget * assoc = 0, *assoc2 = 0; while (assoc_it.hasNext() && assoc_it2.hasNext()) { assoc = assoc_it.next(); assoc2 = assoc_it2.next(); if (!(*assoc == *assoc2)) { return false; } } // } return true; // NOTE: In the comparison tests we are going to do, we don't need these values. // They will actually stop things functioning correctly so if you change these, be aware of that. /* if(m_useFillColor != other.m_useFillColor) return false; if(m_nId != other.m_nId) return false; if(m_nX != other.m_nX) return false; if(m_nY != other.m_nY) return false; */ } /** * Compute the minimum possible width and height. * * @return QSizeF(mininum_width, minimum_height) */ QSizeF UMLWidget::minimumSize() const { return m_minimumSize; } /** * This method is used to set the minimum size variable for this * widget. * * @param newSize The size being set as minimum. */ void UMLWidget::setMinimumSize(const QSizeF& newSize) { m_minimumSize = newSize; } /** * Compute the maximum possible width and height. * * @return maximum size */ QSizeF UMLWidget::maximumSize() { return m_maximumSize; } /** * This method is used to set the maximum size variable for this * widget. * * @param newSize The size being set as maximum. */ void UMLWidget::setMaximumSize(const QSizeF& newSize) { m_maximumSize = newSize; } /** * Event handler for context menu events. */ void UMLWidget::contextMenuEvent(QGraphicsSceneContextMenuEvent *event) { WidgetBase::contextMenuEvent(event); } /** * Moves the widget to a new position using the difference between the * current position and the new position. * This method doesn't adjust associations. It only moves the widget. * * It can be overridden to constrain movement only in one axis even when * the user isn't constraining the movement with shift or control buttons, for example. * The movement policy set here is applied whenever the widget is moved, being it * moving it explicitly, or as a part of a selection but not receiving directly the * mouse events. * * Default behaviour is move the widget to the new position using the diffs. * @see constrainMovementForAllWidgets * * @param diffX The difference between current X position and new X position. * @param diffY The difference between current Y position and new Y position. */ void UMLWidget::moveWidgetBy(qreal diffX, qreal diffY) { setX(x() + diffX); setY(y() + diffY); } /** * Modifies the value of the diffX and diffY variables used to move the widgets. * * It can be overridden to constrain movement of all the selected widgets only in one * axis even when the user isn't constraining the movement with shift or control * buttons, for example. * The difference with moveWidgetBy is that the diff positions used here are * applied to all the selected widgets instead of only to m_widget, and that * moveWidgetBy, in fact, moves the widget, and here simply the diff positions * are modified. * * Default behaviour is do nothing. * @see moveWidgetBy * * @param diffX The difference between current X position and new X position. * @param diffY The difference between current Y position and new Y position. */ void UMLWidget::constrainMovementForAllWidgets(qreal &diffX, qreal &diffY) { Q_UNUSED(diffX) Q_UNUSED(diffY) } /** * Bring the widget at the pressed position to the foreground. */ void UMLWidget::toForeground() { QRectF rect = QRectF(scenePos(), QSizeF(width(), height())); QList items = scene()->items(rect, Qt::IntersectsItemShape, Qt::DescendingOrder); DEBUG(DBG_SRC) << "items at " << rect << " = " << items.count(); if (items.count() > 1) { foreach(QGraphicsItem* i, items) { UMLWidget* w = dynamic_cast(i); if (w) { DEBUG(DBG_SRC) << "item=" << w->name() << " with zValue=" << w->zValue(); if (w->name() != name()) { if (w->zValue() >= zValue()) { setZValue(w->zValue() + 1.0); DEBUG(DBG_SRC) << "bring to foreground with zValue: " << zValue(); } } } } } else { setZValue(0.0); } DEBUG(DBG_SRC) << "zValue is " << zValue(); } /** * Handles a mouse press event. * It'll select the widget (or mark it to be deselected) and prepare it to * be moved or resized. Go on reading for more info about this. * * Widget values and message bar status are saved. * * If shift or control buttons are pressed, we're in move area no matter * where the button was pressed in the widget. Moreover, if the widget * wasn't already selected, it's added to the selection. If already selected, * it's marked to be deselected when releasing the button (provided it isn't * moved). * Also, if the widget is already selected with other widgets but shift nor * control buttons are pressed, we're in move area. If finally we don't move * the widget, it's selected and the other widgets deselected when releasing * the left button. * * If shift nor control buttons are pressed, we're facing a single selection. * Depending on the position of the cursor, we're in move or in resize area. * If the widget wasn't selected (both when there are no widgets selected, or * when there're other widgets selected but not the one receiving the press * event) it's selected and the others deselected, if any. If already selected, * it's marked to be deselected when releasing the button (provided it wasn't * moved or resized). * * @param event The QGraphicsSceneMouseEvent event. */ void UMLWidget::mousePressEvent(QGraphicsSceneMouseEvent *event) { if (event->button() != Qt::LeftButton) { event->ignore(); return; } event->accept(); DEBUG(DBG_SRC) << "widget = " << name() << " / type = " << baseTypeStr(); toForeground(); m_startMovePostion = pos(); m_startResizeSize = QSizeF(width(), height()); // saving the values of the widget m_pressOffset = event->scenePos() - pos(); DEBUG(DBG_SRC) << "press offset=" << m_pressOffset; m_oldStatusBarMsg = UMLApp::app()->statusBarMsg(); if (event->modifiers() == Qt::ShiftModifier || event->modifiers() == Qt::ControlModifier) { m_shiftPressed = true; if (event->button() == Qt::LeftButton) { m_inMoveArea = true; } if (!isSelected()) { selectMultiple(event); } return; } m_shiftPressed = false; int count = m_scene->selectedCount(true); if (event->button() == Qt::LeftButton) { if (isSelected() && count > 1) { // single selection is made in release event if the widget wasn't moved m_inMoveArea = true; m_oldPos = pos(); return; } if (isInResizeArea(event)) { m_inResizeArea = true; m_oldW = width(); m_oldH = height(); } else { m_inMoveArea = true; } } // if widget wasn't selected, or it was selected but with other widgets also selected if (!isSelected() || count > 1) { selectSingle(event); } } /** * Handles a mouse move event. * It resizes or moves the widget, depending on where the cursor is pressed * on the widget. Go on reading for more info about this. * * If resizing, the widget is resized using UMLWidget::resizeWidget (where specific * widget resize constraint can be applied), and then the associations are * adjusted. * The resizing can be constrained also to a specific axis using control * and shift buttons. If on or another is pressed, it's constrained to X axis. * If both are pressed, it's constrained to Y axis. * * If not resizing, the widget is being moved. If the move is being started, * the selection bounds are set (which includes updating the list of selected * widgets). * The difference between the previous position of the selection and the new * one is got (taking in account the selection bounds so widgets don't go * beyond the scene limits). Then, it's constrained to X or Y axis depending * on shift and control buttons. * A further constraint is made using constrainMovementForAllWidgets (for example, * if the widget that receives the event can only be moved in Y axis, with this * method the movement of all the widgets in the selection can be constrained to * be moved only in Y axis). * Then, all the selected widgets are moved using moveWidgetBy (where specific * widget movement constraint can be applied) and, if a certain amount of time * passed from the last move event, the associations are also updated (they're * not updated always to be easy on the CPU). Finally, the scene is resized, * and selection bounds updated. * * @param event The QGraphicsSceneMouseEvent event. */ void UMLWidget::mouseMoveEvent(QGraphicsSceneMouseEvent* event) { if (m_inResizeArea) { resize(event); return; } if (!m_moved) { UMLApp::app()->document()->writeToStatusBar(i18n("Hold shift or ctrl to move in X axis. Hold shift and control to move in Y axis. Right button click to cancel move.")); m_moved = true; //Maybe needed by AssociationWidget m_startMove = true; setSelectionBounds(); } QPointF position = event->scenePos() - m_pressOffset; qreal diffX = position.x() - x(); qreal diffY = position.y() - y(); if ((event->modifiers() & Qt::ShiftModifier) && (event->modifiers() & Qt::ControlModifier)) { // move only in Y axis diffX = 0; } else if ((event->modifiers() & Qt::ShiftModifier) || (event->modifiers() & Qt::ControlModifier)) { // move only in X axis diffY = 0; } constrainMovementForAllWidgets(diffX, diffY); // nothing to move if (diffX == 0 && diffY == 0) { return; } QPointF delta = event->scenePos() - event->lastScenePos(); DEBUG(DBG_SRC) << "diffX=" << diffX << " / diffY=" << diffY; foreach(UMLWidget* widget, umlScene()->selectedWidgets()) { if ((widget->parentItem() == 0) || (!widget->parentItem()->isSelected())) { widget->moveWidgetBy(diffX, diffY); widget->adjustUnselectedAssocs(delta.x(), delta.y()); widget->slotSnapToGrid(); } } // Move any selected associations. foreach(AssociationWidget* aw, m_scene->selectedAssocs()) { if (aw->isSelected()) { aw->moveEntireAssoc(diffX, diffY); } } umlScene()->resizeSceneToItems(); } /** * Handles a mouse release event. * It selects or deselects the widget and cancels or confirms the move or * resize. Go on reading for more info about this. * No matter which tool is selected, Z position of widget is updated. * * Middle button release resets the selection. * Left button release, if it wasn't moved nor resized, selects the widget * and deselect the others if it wasn't selected and there were other widgets * selected. If the widget was marked to be deselected, deselects it. * If it was moved or resized, the document is set to modified if position * or size changed. Also, if moved, all the associations are adjusted because * the timer could have prevented the adjustment in the last move event before * the release. * If mouse was pressed in resize area, cursor is set again to normal cursor * Right button release if right button was pressed shows the pop up menu for * the widget. * If left button was pressed, it cancels the move or resize with a mouse move * event at the same position than the cursor was when pressed. Another left * button release is also sent. * * @param event The QGraphicsSceneMouseEvent event. */ void UMLWidget::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) { if (!m_moved && !m_resized) { if (!m_shiftPressed && (m_scene->selectedCount(true) > 1)) { selectSingle(event); } else if (!isSelected()) { deselect(event); } } else { // Commands if (m_moved) { int selectionCount = umlScene()->selectedWidgets().count(); if (selectionCount > 1) { UMLApp::app()->beginMacro(i18n("Move widgets")); } foreach(UMLWidget* widget, umlScene()->selectedWidgets()) { UMLApp::app()->executeCommand(new Uml::CmdMoveWidget(widget)); } if (selectionCount > 1) { UMLApp::app()->endMacro(); } m_moved = false; } else { UMLApp::app()->executeCommand(new Uml::CmdResizeWidget(this)); m_autoResize = false; m_resized = false; } if ((m_inMoveArea && wasPositionChanged()) || (m_inResizeArea && wasSizeChanged())) { umlDoc()->setModified(true); umlScene()->invalidate(); } umlScene()->resizeSceneToItems(); UMLApp::app()->document()->writeToStatusBar(m_oldStatusBarMsg); } if (m_inResizeArea) { m_inResizeArea = false; m_scene->activeView()->setCursor(Qt::ArrowCursor); } else { m_inMoveArea = false; } m_startMove = false; } /** * Event handler for mouse double click events. * @param event the QGraphicsSceneMouseEvent event. */ void UMLWidget::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event) { if (event->button() == Qt::LeftButton) { DEBUG(DBG_SRC) << "widget = " << name() << " / type = " << baseTypeStr(); showPropertiesDialog(); event->accept(); } } /** * Return the start position of the move action. * @return point where the move began */ QPointF UMLWidget::startMovePosition() const { return m_startMovePostion; } /** * Set the start position of the move action. * @param position point where the move began */ void UMLWidget::setStartMovePosition(const QPointF &position) { m_startMovePostion = position; } /** * Return the start size of the resize action. * @return size where the resize began */ QSizeF UMLWidget::startResizeSize() const { return m_startResizeSize; } /** * Resizes the widget. * It's called from resize, after the values are constrained and before * the associations are adjusted. * * Default behaviour is resize the widget using the new size values. * @see resize * * @param newW The new width for the widget. * @param newH The new height for the widget. */ void UMLWidget::resizeWidget(qreal newW, qreal newH) { setSize(newW, newH); } /** * Notify child widget about parent resizes. * Child widgets can override this function to move when their parent is resized. */ void UMLWidget::notifyParentResize() { } /** * When a widget changes this slot captures that signal. */ void UMLWidget::updateWidget() { updateGeometry(); switch (baseType()) { case WidgetBase::wt_Class: m_scene->createAutoAttributeAssociations(this); break; case WidgetBase::wt_Entity: m_scene->createAutoConstraintAssociations(this); break; default: break; } if (isVisible()) update(); } /** * Apply possible constraints to the given candidate width and height. * The default implementation limits input values to the bounds returned * by minimumSize()/maximumSize(). * * @param width input value, may be modified by the constraint * @param height input value, may be modified by the constraint */ void UMLWidget::constrain(qreal& width, qreal& height) { QSizeF minSize = minimumSize(); if (width < minSize.width()) width = minSize.width(); if (height < minSize.height()) height = minSize.height(); QSizeF maxSize = maximumSize(); if (width > maxSize.width()) width = maxSize.width(); if (height > maxSize.height()) height = maxSize.height(); if (fixedAspectRatio()) { QSizeF size = rect().size(); float aspectRatio = size.width() > 0 ? (float)size.height()/size.width() : 1; height = width * aspectRatio; } } /** * Initializes key attributes of the class. */ void UMLWidget::init() { m_isInstance = false; setMinimumSize(DefaultMinimumSize); setMaximumSize(DefaultMaximumSize); m_font = QApplication::font(); for (int i = (int)FT_INVALID - 1; i >= 0; --i) { FontType fontType = (FontType)i; setupFontType(m_font, fontType); m_pFontMetrics[fontType] = new QFontMetrics(m_font); } if (m_scene) { m_useFillColor = true; m_usesDiagramFillColor = true; m_usesDiagramUseFillColor = true; const Settings::OptionState& optionState = m_scene->optionState(); m_fillColor = optionState.uiState.fillColor; m_showStereotype = optionState.classState.showStereoType; } else { uError() << "SERIOUS PROBLEM - m_scene is NULL"; m_useFillColor = false; m_usesDiagramFillColor = false; m_usesDiagramUseFillColor = false; m_showStereotype = false; } m_resizable = true; m_fixedAspectRatio = false; m_startMove = false; m_activated = false; m_ignoreSnapToGrid = false; m_ignoreSnapComponentSizeToGrid = false; m_doc = UMLApp::app()->document(); m_nPosX = 0; connect(m_scene, SIGNAL(sigFillColorChanged(Uml::ID::Type)), this, SLOT(slotFillColorChanged(Uml::ID::Type))); connect(m_scene, SIGNAL(sigLineColorChanged(Uml::ID::Type)), this, SLOT(slotLineColorChanged(Uml::ID::Type))); connect(m_scene, SIGNAL(sigTextColorChanged(Uml::ID::Type)), this, SLOT(slotTextColorChanged(Uml::ID::Type))); connect(m_scene, SIGNAL(sigLineWidthChanged(Uml::ID::Type)), this, SLOT(slotLineWidthChanged(Uml::ID::Type))); m_umlObject = 0; m_oldPos = QPointF(); m_pressOffset = QPointF(); m_oldW = 0; m_oldH = 0; m_shiftPressed = false; m_inMoveArea = false; m_inResizeArea = false; m_moved = false; m_resized = false; // propagate line color set by base class constructor // which does not call the virtual methods from this class. setLineColor(lineColor()); setZValue(2.0); // default for most widgets } /** * This is usually called synchronously after menu.exec() and \a * trigger's parent is always the ListPopupMenu which can be used to * get the type of action of \a trigger. * * @note Subclasses can reimplement to handle specific actions and * leave the rest to WidgetBase::slotMenuSelection. */ void UMLWidget::slotMenuSelection(QAction *trigger) { if (!trigger) { return; } ListPopupMenu::MenuType sel = ListPopupMenu::typeFromAction(trigger); switch (sel) { case ListPopupMenu::mt_Resize: umlScene()->resizeSelection(); break; case ListPopupMenu::mt_AutoResize: setAutoResize(trigger->isChecked()); updateGeometry(); break; case ListPopupMenu::mt_Rename_Object: { QString name = m_instanceName; bool ok = Dialog_Utils::askName(i18n("Rename Object"), i18n("Enter object name:"), name); if (ok) { m_instanceName = name; updateGeometry(); moveEvent(0); update(); UMLApp::app()->document()->setModified(true); } break; } case ListPopupMenu::mt_FloatText: { FloatingTextWidget* ft = new FloatingTextWidget(umlScene()); ft->showChangeTextDialog(); //if no text entered delete if (!FloatingTextWidget::isTextValid(ft->text())) { delete ft; } else { ft->setID(UniqueID::gen()); addWidget(ft, false); } break; } case ListPopupMenu::mt_Actor: { UMLActor *actor = new UMLActor; UMLWidget *widget = new ActorWidget(umlScene(), actor); addConnectedWidget(widget, Uml::AssociationType::Association); break; } case ListPopupMenu::mt_Artifact: { UMLArtifact *a = new UMLArtifact(); ArtifactWidget *widget = new ArtifactWidget(umlScene(), a); addConnectedWidget(widget, Uml::AssociationType::Association); break; } case ListPopupMenu::mt_Component: { UMLComponent *c = new UMLComponent(); ComponentWidget *widget = new ComponentWidget(umlScene(), c); addConnectedWidget(widget, Uml::AssociationType::Association, SetupSize); break; } + case ListPopupMenu::mt_Hide_Destruction_Box: { + ObjectWidget *w = asObjectWidget(); + if (w) + w->setShowDestruction(false); + break; + } + + case ListPopupMenu::mt_Show_Destruction_Box: { + ObjectWidget *w = asObjectWidget(); + if (w) + w->setShowDestruction(true); + break; + } + case ListPopupMenu::mt_Interface: { UMLPackage* component = umlObject()->asUMLPackage(); QString name = Model_Utils::uniqObjectName(UMLObject::ot_Interface, component); if (Dialog_Utils::askNewName(WidgetBase::wt_Interface, name)) { UMLClassifier *c = new UMLClassifier(); c->setBaseType(UMLObject::ot_Interface); ClassifierWidget *widget = new ClassifierWidget(umlScene(), c); addConnectedWidget(widget, Uml::AssociationType::Association); } break; } case ListPopupMenu::mt_InterfaceComponent: case ListPopupMenu::mt_InterfaceProvided: { UMLObject *o = Object_Factory::createUMLObject(UMLObject::ot_Interface); InterfaceWidget *w = new InterfaceWidget(umlScene(), o->asUMLClassifier()); w->setDrawAsCircle(true); addConnectedWidget(w, Uml::AssociationType::Association, SetupSize); break; } case ListPopupMenu::mt_InterfaceRequired: { UMLObject *o = Object_Factory::createUMLObject(UMLObject::ot_Interface); InterfaceWidget *w = new InterfaceWidget(umlScene(), o->asUMLClassifier()); w->setDrawAsCircle(true); addConnectedWidget(w, Uml::AssociationType::Association, SetupSize | SwitchDirection); break; } case ListPopupMenu::mt_Note: { NoteWidget *widget = new NoteWidget(umlScene()); addConnectedWidget(widget, Uml::AssociationType::Anchor); break; } case ListPopupMenu::mt_Port: { // TODO: merge with ToolbarStateOneWidget::setWidget() UMLPackage* component = umlObject()->asUMLPackage(); QString name = Model_Utils::uniqObjectName(UMLObject::ot_Port, component); if (Dialog_Utils::askNewName(WidgetBase::wt_Port, name)) { UMLPort *port = Object_Factory::createUMLObject(UMLObject::ot_Port, name, component)->asUMLPort(); UMLWidget *umlWidget = Widget_Factory::createWidget(umlScene(), port); umlWidget->setParentItem(this); QPointF p = mapFromScene(umlScene()->pos()); umlWidget->setPos(p); umlScene()->setupNewWidget(umlWidget, false); } break; } case ListPopupMenu::mt_UseCase: { UMLUseCase *useCase = new UMLUseCase; UMLWidget *widget = new UseCaseWidget(umlScene(), useCase); addConnectedWidget(widget, Uml::AssociationType::Association); break; } case ListPopupMenu::mt_MessageCreation: + case ListPopupMenu::mt_MessageDestroy: case ListPopupMenu::mt_MessageSynchronous: // MessageWidget *widget = new MessageWidget(umlScene(), this); // addConnectedWidget(widget, Uml::AssociationType::Coll_Message_Synchronous); case ListPopupMenu::mt_MessageAsynchronous: case ListPopupMenu::mt_MessageFound: case ListPopupMenu::mt_MessageLost: break; // activity diagrams case ListPopupMenu::mt_Accept_Signal: addConnectedWidget(new SignalWidget(umlScene(), SignalWidget::Accept), Uml::AssociationType::Activity, NoOption); break; case ListPopupMenu::mt_Accept_Time_Event: addConnectedWidget(new SignalWidget(umlScene(), SignalWidget::Time), Uml::AssociationType::Activity, NoOption); break; case ListPopupMenu::mt_Activity: addConnectedWidget(new ActivityWidget(umlScene(), ActivityWidget::Normal), Uml::AssociationType::Activity, NoOption); break; case ListPopupMenu::mt_Activity_Transition: addConnectedWidget(new ActivityWidget(umlScene(), ActivityWidget::Final), Uml::AssociationType::Activity, NoOption); break; case ListPopupMenu::mt_Branch: addConnectedWidget(new ActivityWidget(umlScene(), ActivityWidget::Branch), Uml::AssociationType::Activity, NoOption); break; case ListPopupMenu::mt_Exception: umlScene()->triggerToolbarButton(WorkToolBar::tbb_Exception); break; case ListPopupMenu::mt_Final_Activity: addConnectedWidget(new ActivityWidget(umlScene(), ActivityWidget::Final), Uml::AssociationType::Activity, NoOption); break; case ListPopupMenu::mt_Fork: addConnectedWidget(new ForkJoinWidget(umlScene()), Uml::AssociationType::Activity, NoOption); break; case ListPopupMenu::mt_End_Activity: addConnectedWidget(new ActivityWidget(umlScene(), ActivityWidget::End), Uml::AssociationType::Activity, NoOption); break; case ListPopupMenu::mt_Initial_Activity: addConnectedWidget(new ActivityWidget(umlScene(), ActivityWidget::Initial), Uml::AssociationType::Activity, NoOption); break; case ListPopupMenu::mt_Invoke_Activity: addConnectedWidget(new ActivityWidget(umlScene(), ActivityWidget::Invok), Uml::AssociationType::Activity, NoOption); break; case ListPopupMenu::mt_Object_Node: addConnectedWidget(new ObjectNodeWidget(umlScene(), ObjectNodeWidget::Data), Uml::AssociationType::Activity, NoOption); break; case ListPopupMenu::mt_Pin: umlScene()->triggerToolbarButton(WorkToolBar::tbb_Pin); break; case ListPopupMenu::mt_Param_Activity: addConnectedWidget(new ActivityWidget(umlScene(), ActivityWidget::Param), Uml::AssociationType::Activity, NoOption); break; case ListPopupMenu::mt_PrePostCondition: addConnectedWidget(new NoteWidget(umlScene(), NoteWidget::Normal), Uml::AssociationType::Activity, NoOption); break; case ListPopupMenu::mt_Region: addConnectedWidget(new RegionWidget(umlScene()), Uml::AssociationType::Activity, NoOption); break; case ListPopupMenu::mt_Send_Signal: addConnectedWidget(new SignalWidget(umlScene(), SignalWidget::Send), Uml::AssociationType::Activity, NoOption); break; // state diagrams case ListPopupMenu::mt_Choice: addConnectedWidget(new StateWidget(umlScene(), StateWidget::Choice), Uml::AssociationType::State, NoOption); break; case ListPopupMenu::mt_DeepHistory: addConnectedWidget(new StateWidget(umlScene(), StateWidget::DeepHistory), Uml::AssociationType::State, NoOption); break; case ListPopupMenu::mt_End_State: addConnectedWidget(new StateWidget(umlScene(), StateWidget::End), Uml::AssociationType::State, NoOption); break; case ListPopupMenu::mt_Junction: addConnectedWidget(new StateWidget(umlScene(), StateWidget::Junction), Uml::AssociationType::State, NoOption); break; case ListPopupMenu::mt_ShallowHistory: addConnectedWidget(new StateWidget(umlScene(), StateWidget::ShallowHistory), Uml::AssociationType::State, NoOption); break; case ListPopupMenu::mt_State: addConnectedWidget(new StateWidget(umlScene(), StateWidget::Normal), Uml::AssociationType::State, ShowProperties); break; case ListPopupMenu::mt_StateFork: addConnectedWidget(new StateWidget(umlScene(), StateWidget::Fork), Uml::AssociationType::State, NoOption); break; case ListPopupMenu::mt_StateJoin: addConnectedWidget(new StateWidget(umlScene(), StateWidget::Join), Uml::AssociationType::State, NoOption); break; case ListPopupMenu::mt_StateTransition: umlScene()->triggerToolbarButton(WorkToolBar::tbb_State_Transition); break; default: WidgetBase::slotMenuSelection(trigger); break; } } /** * Captures when another widget moves if this widget is linked to it. * @see sigWidgetMoved * * @param id The id of object behind the widget. */ void UMLWidget::slotWidgetMoved(Uml::ID::Type /*id*/) { } /** * Captures a color change signal. * * @param viewID The id of the UMLScene behind the widget. */ void UMLWidget::slotFillColorChanged(Uml::ID::Type viewID) { //only change if on the diagram concerned if (m_scene->ID() != viewID) { return; } if (m_usesDiagramFillColor) { WidgetBase::setFillColor(m_scene->fillColor()); } if (m_usesDiagramUseFillColor) { WidgetBase::setUseFillColor(m_scene->useFillColor()); } update(); } /** * Captures a text color change signal. * * @param viewID The id of the UMLScene behind the widget. */ void UMLWidget::slotTextColorChanged(Uml::ID::Type viewID) { //only change if on the diagram concerned if (m_scene->ID() != viewID) return; WidgetBase::setTextColor(m_scene->textColor()); update(); } /** * Captures a line color change signal. * * @param viewID The id of the UMLScene behind the widget. */ void UMLWidget::slotLineColorChanged(Uml::ID::Type viewID) { //only change if on the diagram concerned if (m_scene->ID() != viewID) return; if (m_usesDiagramLineColor) { WidgetBase::setLineColor(m_scene->lineColor()); } update(); } /** * Captures a linewidth change signal. * * @param viewID The id of the UMLScene behind the widget. */ void UMLWidget::slotLineWidthChanged(Uml::ID::Type viewID) { //only change if on the diagram concerned if (m_scene->ID() != viewID) { return; } if (m_usesDiagramLineWidth) { WidgetBase::setLineWidth(m_scene->lineWidth()); } update(); } /** * Set the status of using fill color (undo action) * * @param fc the status of using fill color. */ void UMLWidget::setUseFillColor(bool fc) { if (useFillColor() != fc) { UMLApp::app()->executeCommand(new CmdChangeUseFillColor(this, fc)); } } /** * Set the status of using fill color. * * @param fc the status of using fill color. */ void UMLWidget::setUseFillColorCmd(bool fc) { WidgetBase::setUseFillColor(fc); update(); } /** * Overrides the method from WidgetBase. */ void UMLWidget::setTextColorCmd(const QColor &color) { WidgetBase::setTextColor(color); update(); } /** * Overrides the method from WidgetBase. */ void UMLWidget::setTextColor(const QColor &color) { if (textColor() != color) { UMLApp::app()->executeCommand(new CmdChangeTextColor(this, color)); update(); } } /** * Overrides the method from WidgetBase. */ void UMLWidget::setLineColorCmd(const QColor &color) { WidgetBase::setLineColor(color); update(); } /** * Overrides the method from WidgetBase. */ void UMLWidget::setLineColor(const QColor &color) { if (lineColor() != color) { UMLApp::app()->executeCommand(new CmdChangeLineColor(this, color)); } } /** * Overrides the method from WidgetBase, execute CmdChangeLineWidth */ void UMLWidget::setLineWidth(uint width) { if (lineWidth() != width) { UMLApp::app()->executeCommand(new CmdChangeLineWidth(this, width)); } } /** * Overrides the method from WidgetBase. */ void UMLWidget::setLineWidthCmd(uint width) { WidgetBase::setLineWidth(width); update(); } /** * Sets the background fill color * * @param color the new fill color */ void UMLWidget::setFillColor(const QColor &color) { if (fillColor() != color) { UMLApp::app()->executeCommand(new CmdChangeFillColor(this, color)); } } /** * Sets the background fill color * * @param color the new fill color */ void UMLWidget::setFillColorCmd(const QColor &color) { WidgetBase::setFillColor(color); update(); } /** * Reimplemented from class WidgetBase * * @param ChangeLog * @return true for success */ bool UMLWidget::activate(IDChangeLog* changeLog) { if (!WidgetBase::activate(changeLog)) return false; DiagramProxyWidget::activate(changeLog); setFontCmd(m_font); setSize(width(), height()); m_activated = true; updateGeometry(); if (m_scene->getPaste()) { FloatingTextWidget * ft = 0; QPointF point = m_scene->getPastePoint(); int x = point.x() + this->x(); int y = point.y() + this->y(); if (m_scene->isSequenceDiagram()) { switch (baseType()) { case WidgetBase::wt_Object: case WidgetBase::wt_Precondition : setY(this->y()); setX(x); break; case WidgetBase::wt_Message: setY(this->y()); setX(x); break; case WidgetBase::wt_Text: ft = static_cast(this); if (ft->textRole() == Uml::TextRole::Seq_Message) { setX(x); setY(this->y()); } else { setX(this->x()); setY(this->y()); } break; default: setY(y); break; }//end switch base type }//end if sequence else { setX(x); setY(y); } }//end if pastepoint else { setX(this->x()); setY(this->y()); } if (m_scene->getPaste()) m_scene->createAutoAssociations(this); updateGeometry(); return true; } /** * Returns true if the Activate method has been called for this instance * * @return The activate status. */ bool UMLWidget::isActivated() const { return m_activated; } /** * Set the m_activated flag of a widget but does not perform the Activate method * * @param active Status of activation is to be set. */ void UMLWidget::setActivated(bool active /*=true*/) { m_activated = active; } /** * Reimplemented from class WidgetBase */ void UMLWidget::addAssoc(AssociationWidget* pAssoc) { if (pAssoc && !associationWidgetList().contains(pAssoc)) { associationWidgetList().append(pAssoc); } } /** * Returns the list of associations connected to this widget. */ AssociationWidgetList &UMLWidget::associationWidgetList() const { m_Assocs.removeAll(0); return m_Assocs; } /** * Reimplemented from class WidgetBase */ void UMLWidget::removeAssoc(AssociationWidget* pAssoc) { if (pAssoc) { associationWidgetList().removeAll(pAssoc); } if (changesShape()) { updateGeometry(); } } /** * Adjusts associations with the given co-ordinates * * @param dx The amount by which the widget moved in X direction. * @param dy The amount by which the widget moved in Y direction. */ void UMLWidget::adjustAssocs(qreal dx, qreal dy) { qDebug() << this; // don't adjust Assocs on file load, as // the original positions, which are stored in XMI // should be reproduced exactly // (don't try to reposition assocs as long // as file is only partly loaded -> reposition // could be misguided) /// @todo avoid trigger of this event during load if (m_doc->loading()) { // don't recalculate the assocs during load of XMI // -> return immediately without action return; } foreach(AssociationWidget* assocwidget, associationWidgetList()) { assocwidget->saveIdealTextPositions(); } foreach(AssociationWidget* assocwidget, associationWidgetList()) { assocwidget->widgetMoved(this, dx, dy); } } /** * Adjusts all unselected associations with the given co-ordinates * * @param dx The amount by which the widget moved in X direction. * @param dy The amount by which the widget moved in Y direction. */ void UMLWidget::adjustUnselectedAssocs(qreal dx, qreal dy) { foreach(AssociationWidget* assocwidget, associationWidgetList()) { if (!assocwidget->isSelected()) assocwidget->saveIdealTextPositions(); } foreach(AssociationWidget* assocwidget, associationWidgetList()) { if (!assocwidget->isSelected()) { assocwidget->widgetMoved(this, dx, dy); } } } /** * Show a properties dialog for a UMLWidget. */ bool UMLWidget::showPropertiesDialog() { bool result = false; // will already be selected so make sure docWindow updates the doc // back it the widget UMLApp::app()->docWindow()->updateDocumentation(false); QPointer dlg = new ClassPropertiesDialog((QWidget*)UMLApp::app(), this); if (dlg->exec()) { UMLApp::app()->docWindow()->showDocumentation(umlObject(), true); m_doc->setModified(true); result = true; } dlg->close(); //wipe from memory delete dlg; return result; } /** * Move the widget by an X and Y offset relative to * the current position. */ void UMLWidget::moveByLocal(qreal dx, qreal dy) { qreal newX = x() + dx; qreal newY = y() + dy; setX(newX); setY(newY); adjustAssocs(dx, dy); } /** * Set the pen. */ void UMLWidget::setPenFromSettings(QPainter & p) { p.setPen(QPen(m_lineColor, m_lineWidth)); } /** * Set the pen. */ void UMLWidget::setPenFromSettings(QPainter *p) { p->setPen(QPen(m_lineColor, m_lineWidth)); } /** * Returns the cursor to be shown when resizing the widget. * Default cursor is KCursor::sizeFDiagCursor(). * * @return The cursor to be shown when resizing the widget. */ QCursor UMLWidget::resizeCursor() const { return Qt::SizeFDiagCursor; } /** * Checks if the mouse is in resize area (right bottom corner), and sets * the cursor depending on that. * The cursor used when resizing is gotten from resizeCursor(). * * @param me The QMouseEVent to check. * @return true if the mouse is in resize area, false otherwise. */ bool UMLWidget::isInResizeArea(QGraphicsSceneMouseEvent *me) { qreal m = 10.0; const qreal w = width(); const qreal h = height(); // If the widget itself is very small then make the resize area small, too. // Reason: Else it becomes impossible to do a move instead of resize. if (w - m < m || h - m < m) { m = 2.0; } if (m_resizable && me->scenePos().x() >= (x() + w - m) && me->scenePos().y() >= (y() + h - m)) { m_scene->activeView()->setCursor(resizeCursor()); return true; } else { m_scene->activeView()->setCursor(Qt::ArrowCursor); return false; } } /** * calculate content related size of widget. * * @return calculated widget size */ QSizeF UMLWidget::calculateSize(bool withExtensions /* = true */) const { Q_UNUSED(withExtensions) const QFontMetrics &fm = getFontMetrics(UMLWidget::FT_NORMAL); const int fontHeight = fm.lineSpacing(); if (m_umlObject) { qreal width = 0, height = defaultMargin; if (!m_umlObject->stereotype().isEmpty()) { height += fontHeight; const QFontMetrics &bfm = UMLWidget::getFontMetrics(UMLWidget::FT_BOLD); const int stereoWidth = bfm.size(0, m_umlObject->stereotype(true)).width(); if (stereoWidth > width) width = stereoWidth; } height += fontHeight; const QFontMetrics &bfm = UMLWidget::getFontMetrics(UMLWidget::FT_BOLD); const int nameWidth = bfm.size(0, m_umlObject->name()).width(); if (nameWidth > width) width = nameWidth; return QSizeF(width + 2*defaultMargin, height); } else return QSizeF(width(), height()); } /** * Resize widget to minimum size. */ void UMLWidget::resize() { qreal oldW = width(); qreal oldH = height(); // @TODO minimumSize() do not work in all cases, we need a dedicated autoResize() method QSizeF size = minimumSize(); setSize(size.width(), size.height()); DEBUG(DBG_SRC) << "size=" << size; adjustAssocs(size.width()-oldW, size.height()-oldH); } /** * Resizes the widget and adjusts the associations. * It's called when a mouse move event happens and the cursor was * in resize area when pressed. * Resizing can be constrained to an specific axis using control and shift buttons. * * @param me The QGraphicsSceneMouseEvent to get the values from. */ void UMLWidget::resize(QGraphicsSceneMouseEvent *me) { QString msgX = i18n("Hold shift or control to move in X axis."); QString msgY = i18n("Hold shift and control to move in Y axis."); QString msg; if (isMessageWidget()) msg = msgY; else if (isObjectWidget()) msg = msgX; else msg = QString(QLatin1String("%1 %2")).arg(msgX, msgY); UMLApp::app()->document()->writeToStatusBar(msg); m_resized = true; qreal newW = m_oldW + me->scenePos().x() - x() - m_pressOffset.x(); qreal newH = m_oldH + me->scenePos().y() - y() - m_pressOffset.y(); if ((me->modifiers() & Qt::ShiftModifier) && (me->modifiers() & Qt::ControlModifier)) { //Move in Y axis newW = m_oldW; } else if ((me->modifiers() & Qt::ShiftModifier) || (me->modifiers() & Qt::ControlModifier)) { //Move in X axis newH = m_oldH; } constrain(newW, newH); resizeWidget(newW, newH); DEBUG(DBG_SRC) << "event=" << me->scenePos() << "/ pos=" << pos() << " / newW=" << newW << " / newH=" << newH; QPointF delta = me->scenePos() - me->lastScenePos(); adjustAssocs(delta.x(), delta.y()); m_scene->resizeSceneToItems(); } /** * Checks if the size of the widget changed respect to the size that * it had when press event was fired. * * @return true if was resized, false otherwise. */ bool UMLWidget::wasSizeChanged() { return m_oldW != width() || m_oldH != height(); } /** * Checks if the position of the widget changed respect to the position that * it had when press event was fired. * * @return true if was moved, false otherwise. */ bool UMLWidget::wasPositionChanged() { return m_oldPos != pos(); } /** * Fills m_selectedWidgetsList and sets the selection bounds ((m_min/m_max)X/Y attributes). */ void UMLWidget::setSelectionBounds() { } void UMLWidget::setSelectedFlag(bool _select) { WidgetBase::setSelected(_select); } /** * Sets the state of whether the widget is selected. * * @param _select The state of whether the widget is selected. */ void UMLWidget::setSelected(bool _select) { const WidgetBase::WidgetType wt = baseType(); if (_select) { if (m_scene->selectedCount() == 0) { if (widgetHasUMLObject(wt)) { UMLApp::app()->docWindow()->showDocumentation(m_umlObject, false); } else { UMLApp::app()->docWindow()->showDocumentation(this, false); } }//end if /* if (wt != wt_Text && wt != wt_Box) { setZ(9);//keep text on top and boxes behind so don't touch Z value } */ } else { /* if (wt != wt_Text && wt != wt_Box) { setZ(m_origZ); } */ if (isSelected()) UMLApp::app()->docWindow()->updateDocumentation(true); } WidgetBase::setSelected(_select); update(); // selection changed, we have to make sure the copy and paste items // are correctly enabled/disabled UMLApp::app()->slotCopyChanged(); // select in tree view as done for diagrams if (_select) { UMLListViewItem * item = UMLApp::app()->listView()->findItem(id()); if (item) UMLApp::app()->listView()->setCurrentItem(item); else UMLApp::app()->listView()->clearSelection(); } } /** * Selects the widget and clears the other selected widgets, if any. * * @param me The QGraphicsSceneMouseEvent which made the selection. */ void UMLWidget::selectSingle(QGraphicsSceneMouseEvent *me) { m_scene->clearSelected(); // Adds the widget to the selected widgets list, but as it has been cleared // only the current widget is selected. selectMultiple(me); } /** * Selects the widget and adds it to the list of selected widgets. * * @param me The QGraphicsSceneMouseEvent which made the selection. */ void UMLWidget::selectMultiple(QGraphicsSceneMouseEvent *me) { Q_UNUSED(me); setSelected(true); } /** * Deselects the widget and removes it from the list of selected widgets. * * @param me The QGraphicsSceneMouseEvent which made the selection. */ void UMLWidget::deselect(QGraphicsSceneMouseEvent *me) { Q_UNUSED(me); setSelected(false); } /** * Clears the selection, resets the toolbar and deselects the widget. */ //void UMLWidget::resetSelection() //{ // m_scene->clearSelected(); // m_scene->resetToolbar(); // setSelected(false); //} /** * Sets the view the widget is on. * * @param scene The UMLScene the widget is on. */ void UMLWidget::setScene(UMLScene *scene) { //remove signals from old view - was probably 0 anyway disconnect(m_scene, SIGNAL(sigFillColorChanged(Uml::ID::Type)), this, SLOT(slotFillColorChanged(Uml::ID::Type))); disconnect(m_scene, SIGNAL(sigTextColorChanged(Uml::ID::Type)), this, SLOT(slotTextColorChanged(Uml::ID::Type))); disconnect(m_scene, SIGNAL(sigLineWidthChanged(Uml::ID::Type)), this, SLOT(slotLineWidthChanged(Uml::ID::Type))); m_scene = scene; connect(m_scene, SIGNAL(sigFillColorChanged(Uml::ID::Type)), this, SLOT(slotFillColorChanged(Uml::ID::Type))); connect(m_scene, SIGNAL(sigTextColorChanged(Uml::ID::Type)), this, SLOT(slotTextColorChanged(Uml::ID::Type))); connect(m_scene, SIGNAL(sigLineWidthChanged(Uml::ID::Type)), this, SLOT(slotLineWidthChanged(Uml::ID::Type))); } /** * Sets the x-coordinate. * Currently, the only class that reimplements this method is * ObjectWidget. * * @param x The x-coordinate to be set. */ void UMLWidget::setX(qreal x) { QGraphicsObjectWrapper::setX(x); } /** * Sets the y-coordinate. * Currently, the only class that reimplements this method is * ObjectWidget. * * @param y The y-coordinate to be set. */ void UMLWidget::setY(qreal y) { QGraphicsObjectWrapper::setY(y); } /** * Used to cleanup any other widget it may need to delete. * Used by child classes. This should be called before deleting a widget of a diagram. */ void UMLWidget::cleanup() { } /** * Tells the widget to snap to grid. * Will use the grid settings of the @ref UMLView it belongs to. */ void UMLWidget::slotSnapToGrid() { if (!m_ignoreSnapToGrid) { qreal newX = m_scene->snappedX(x()); setX(newX); qreal newY = m_scene->snappedY(y()); setY(newY); } } /** * Set m_ignoreSnapToGrid. */ void UMLWidget::setIgnoreSnapToGrid(bool to) { m_ignoreSnapToGrid = to; } /** * Return the value of m_ignoreSnapToGrid. */ bool UMLWidget::getIgnoreSnapToGrid() const { return m_ignoreSnapToGrid; } /** * Sets the size. * If m_scene->snapComponentSizeToGrid() is true, then * set the next larger size that snaps to the grid. */ void UMLWidget::setSize(qreal width, qreal height) { // snap to the next larger size that is a multiple of the grid if (!m_ignoreSnapComponentSizeToGrid && m_scene->snapComponentSizeToGrid()) { // integer divisions int numX = width / m_scene->snapX(); int numY = height / m_scene->snapY(); // snap to the next larger valid value if (width > numX * m_scene->snapX()) width = (numX + 1) * m_scene->snapX(); if (height > numY * m_scene->snapY()) height = (numY + 1) * m_scene->snapY(); } const QRectF newRect(rect().x(), rect().y(), width, height); setRect(newRect); foreach(QGraphicsItem* child, childItems()) { UMLWidget* umlChild = static_cast(child); umlChild->notifyParentResize(); } } /** * Sets the size with another size. */ void UMLWidget::setSize(const QSizeF& size) { setSize(size.width(), size.height()); } /** * Update the size of this widget. * * @param withAssocs true - update associations too */ void UMLWidget::updateGeometry(bool withAssocs) { if (m_doc->loading()) { return; } if (!m_autoResize) return; qreal oldW = width(); qreal oldH = height(); QSizeF size = calculateSize(); qreal clipWidth = size.width(); qreal clipHeight = size.height(); constrain(clipWidth, clipHeight); setSize(clipWidth, clipHeight); slotSnapToGrid(); if (withAssocs) adjustAssocs(size.width()-oldW, size.height()-oldH); update(); } /** * clip the size of this widget against the * minimal and maximal limits. */ void UMLWidget::clipSize() { qreal clipWidth = width(); qreal clipHeight = height(); constrain(clipWidth, clipHeight); setSize(clipWidth, clipHeight); } /** * Template Method, override this to set the default font metric. */ void UMLWidget::setDefaultFontMetrics(QFont &font, UMLWidget::FontType fontType) { setupFontType(font, fontType); setFontMetrics(fontType, QFontMetrics(font)); } void UMLWidget::setupFontType(QFont &font, UMLWidget::FontType fontType) { switch (fontType) { case FT_NORMAL: font.setBold(false); font.setItalic(false); font.setUnderline(false); break; case FT_BOLD: font.setBold(true); font.setItalic(false); font.setUnderline(false); break; case FT_ITALIC: font.setBold(false); font.setItalic(true); font.setUnderline(false); break; case FT_UNDERLINE: font.setBold(false); font.setItalic(false); font.setUnderline(true); break; case FT_BOLD_ITALIC: font.setBold(true); font.setItalic(true); font.setUnderline(false); break; case FT_BOLD_UNDERLINE: font.setBold(true); font.setItalic(false); font.setUnderline(true); break; case FT_ITALIC_UNDERLINE: font.setBold(false); font.setItalic(true); font.setUnderline(true); break; case FT_BOLD_ITALIC_UNDERLINE: font.setBold(true); font.setItalic(true); font.setUnderline(true); break; default: return; } } void UMLWidget::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) { Q_UNUSED(option); Q_UNUSED(widget); if (option->state & QStyle::State_Selected) { const qreal w = width(); const qreal h = height(); const qreal s = selectionMarkerSize; QBrush brush(Qt::blue); painter->fillRect(0, 0, s, s, brush); painter->fillRect(0, 0 + h - s, s, s, brush); painter->fillRect(0 + w - s, 0, s, s, brush); // Draw the resize anchor in the lower right corner. // Don't draw it if the widget is so small that the // resize anchor would cover up most of the widget. if (m_resizable && w >= s+8 && h >= s+8) { brush.setColor(Qt::red); const int right = 0 + w; const int bottom = 0 + h; painter->drawLine(right - s, 0 + h - 1, 0 + w - 1, 0 + h - s); painter->drawLine(right - (s*2), bottom - 1, right - 1, bottom - (s*2)); painter->drawLine(right - (s*3), bottom - 1, right - 1, bottom - (s*3)); } else { painter->fillRect(0 + w - s, 0 + h - s, s, s, brush); } // debug info if (Tracer::instance()->isEnabled(QLatin1String(metaObject()->className()))) { QPen p(Qt::green); p.setWidthF(1.0); painter->setPen(p); painter->setBrush(Qt::NoBrush); painter->drawPath(shape()); painter->setPen(Qt::blue); painter->drawRect(boundingRect()); // origin painter->drawLine(-10, 0, 10, 0); painter->drawLine(0, -10, 0, 10); } } if (umlScene()->isShowDocumentationIndicator() && hasDocumentation()) { const qreal h = height(); const qreal d = 8; QPolygonF p; p << QPointF(0, h - d) << QPointF(d, h) << QPointF(0, h); painter->setPen(Qt::blue); painter->setBrush(Qt::red); painter->drawPolygon(p); } } /** * Template Method, override this to set the default font metric. */ void UMLWidget::setDefaultFontMetrics(QFont &font, UMLWidget::FontType fontType, QPainter &painter) { setupFontType(font, fontType); painter.setFont(font); setFontMetrics(fontType, painter.fontMetrics()); } /** * Returns the font metric used by this object for Text * which uses bold/italic fonts. */ QFontMetrics &UMLWidget::getFontMetrics(UMLWidget::FontType fontType) const { return *m_pFontMetrics[fontType]; } /** * Set the font metric to use. */ void UMLWidget::setFontMetrics(UMLWidget::FontType fontType, QFontMetrics fm) { delete m_pFontMetrics[fontType]; m_pFontMetrics[fontType] = new QFontMetrics(fm); } /** * Sets the font the widget is to use. * * @param font Font to be set. */ void UMLWidget::setFont(const QFont &font) { QFont newFont = font; forceUpdateFontMetrics(newFont, 0); if (m_font != newFont) { UMLApp::app()->executeCommand(new CmdChangeFont(this, font)); } } /** * Sets the font the widget is to use. * * @param font Font to be set. */ void UMLWidget::setFontCmd(const QFont &font) { WidgetBase::setFont(font); forceUpdateFontMetrics(0); if (m_doc->loading()) return; update(); } /** * Updates font metrics for widgets current m_font */ void UMLWidget::forceUpdateFontMetrics(QPainter *painter) { forceUpdateFontMetrics(m_font, painter); } /** * @note For performance Reasons, only FontMetrics for already used * font types are updated. Not yet used font types will not get a font metric * and will get the same font metric as if painter was zero. * This behaviour is acceptable, because diagrams will always be shown on Display * first before a special painter like a printer device is used. */ void UMLWidget::forceUpdateFontMetrics(QFont& font, QPainter *painter) { if (painter == 0) { for (int i = (int)FT_INVALID - 1; i >= 0; --i) { if (m_pFontMetrics[(UMLWidget::FontType)i] != 0) setDefaultFontMetrics(font, (UMLWidget::FontType)i); } } else { for (int i2 = (int)FT_INVALID - 1; i2 >= 0; --i2) { if (m_pFontMetrics[(UMLWidget::FontType)i2] != 0) setDefaultFontMetrics(font, (UMLWidget::FontType)i2, *painter); } } if (m_doc->loading()) return; // calculate the size, based on the new font metric updateGeometry(); } /** * Set the status of whether to show Stereotype. * * @param flag True if stereotype shall be shown. */ void UMLWidget::setShowStereotype(bool flag) { m_showStereotype = flag; updateGeometry(); update(); } /** * Returns the status of whether to show Stereotype. * * @return True if stereotype is shown. */ bool UMLWidget::showStereotype() const { return m_showStereotype; } /** * Overrides the standard operation. * * @param me The move event. */ void UMLWidget::moveEvent(QGraphicsSceneMouseEvent* me) { Q_UNUSED(me) } void UMLWidget::saveToXMI1(QDomDocument & qDoc, QDomElement & qElement) { /* Call after required actions in child class. Type must be set in the child class. */ WidgetBase::saveToXMI1(qDoc, qElement); DiagramProxyWidget::saveToXMI1(qDoc, qElement); qreal dpiScale = UMLApp::app()->document()->dpiScale(); qElement.setAttribute(QLatin1String("x"), QString::number(x() / dpiScale)); qElement.setAttribute(QLatin1String("y"), QString::number(y() / dpiScale)); qElement.setAttribute(QLatin1String("width"), QString::number(width() / dpiScale)); qElement.setAttribute(QLatin1String("height"), QString::number(height() / dpiScale)); qElement.setAttribute(QLatin1String("isinstance"), m_isInstance); if (!m_instanceName.isEmpty()) qElement.setAttribute(QLatin1String("instancename"), m_instanceName); if (m_showStereotype) qElement.setAttribute(QLatin1String("showstereotype"), m_showStereotype); } bool UMLWidget::loadFromXMI1(QDomElement & qElement) { WidgetBase::loadFromXMI1(qElement); DiagramProxyWidget::loadFromXMI1(qElement); QString x = qElement.attribute(QLatin1String("x"), QLatin1String("0")); QString y = qElement.attribute(QLatin1String("y"), QLatin1String("0")); QString h = qElement.attribute(QLatin1String("height"), QLatin1String("0")); QString w = qElement.attribute(QLatin1String("width"), QLatin1String("0")); qreal dpiScale = UMLApp::app()->document()->dpiScale(); setSize(toDoubleFromAnyLocale(w) * dpiScale, toDoubleFromAnyLocale(h) * dpiScale); setX(toDoubleFromAnyLocale(x) * dpiScale); setY(toDoubleFromAnyLocale(y) * dpiScale); QString isinstance = qElement.attribute(QLatin1String("isinstance"), QLatin1String("0")); m_isInstance = (bool)isinstance.toInt(); m_instanceName = qElement.attribute(QLatin1String("instancename")); QString showstereo = qElement.attribute(QLatin1String("showstereotype"), QLatin1String("0")); m_showStereotype = (bool)showstereo.toInt(); return true; } /** * Adds a widget to the diagram, which is connected to the current widget * @param widget widget instance to add to diagram * @param type association type * @param options widget options */ void UMLWidget::addConnectedWidget(UMLWidget *widget, Uml::AssociationType::Enum type, AddWidgetOptions options) { QString name = Widget_Utils::defaultWidgetName(widget->baseType()); widget->setName(name); if (options & ShowProperties) { if (!widget->showPropertiesDialog()) { delete widget; return; } } umlScene()->addItem(widget); widget->setX(x() + rect().width() + 100); widget->setY(y()); if (options & SetupSize) { widget->setSize(100, 40); QSizeF size = widget->minimumSize(); widget->setSize(size); } AssociationWidget* assoc = options & SwitchDirection ? AssociationWidget::create(umlScene(), widget, type, this) : AssociationWidget::create(umlScene(), this, type, widget); umlScene()->addAssociation(assoc); umlScene()->clearSelected(); umlScene()->selectWidget(widget); UMLApp::app()->beginMacro(I18N_NEXT_RELEASE("Adding connected '%1'", widget->baseTypeStrWithoutPrefix()); UMLApp::app()->executeCommand(new CmdCreateWidget(widget)); UMLApp::app()->executeCommand(new CmdCreateWidget(assoc)); UMLApp::app()->endMacro(); m_doc->setModified(); } /** * Adds a widget to the diagram, which is connected to the current widget * @param widget widget instance to add to diagram * @param showProperties whether to show properties of the widget */ void UMLWidget::addWidget(UMLWidget *widget, bool showProperties) { umlScene()->addItem(widget); widget->setX(x() + rect().width() + 100); widget->setY(y()); widget->setSize(100, 40); if (showProperties) widget->showPropertiesDialog(); QSizeF size = widget->minimumSize(); widget->setSize(size); } diff --git a/umbrello/version.h b/umbrello/version.h index 0fcf2b22c..864dafcc6 100644 --- a/umbrello/version.h +++ b/umbrello/version.h @@ -1,38 +1,38 @@ /*************************************************************************** * 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-2020 * * Umbrello UML Modeller Authors * ***************************************************************************/ #ifndef UMBRELLO_VERSION_H #define UMBRELLO_VERSION_H #if QT_VERSION < 0x050000 #include #endif inline QByteArray umbrelloVersion() { #ifdef UMBRELLO_VERSION_STRING QString versionStr = QString::fromLatin1(UMBRELLO_VERSION_STRING); #else QString versionStr = QString::fromLatin1("%1.%2.%3") .arg(KDE::versionMajor()-2) .arg(KDE::versionMinor()) .arg(KDE::versionRelease()); #endif return versionStr.toLatin1(); } // Update this version and dtd's in doc/xml when changing the XMI file format #if defined(ENABLE_WIDGET_SHOW_DOC) || defined(ENABLE_XMIRESOLUTION) -#define XMI_FILE_VERSION "1.6.18" +#define XMI_FILE_VERSION "1.6.19" #else -#define XMI_FILE_VERSION "1.6.17" +#define XMI_FILE_VERSION "1.6.18" #endif #endif diff --git a/umbrello/worktoolbar.cpp b/umbrello/worktoolbar.cpp index 2ae4a572a..6cf3c5064 100644 --- a/umbrello/worktoolbar.cpp +++ b/umbrello/worktoolbar.cpp @@ -1,509 +1,512 @@ /*************************************************************************** * 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-2020 * * Umbrello UML Modeller Authors * ***************************************************************************/ // own header #include "worktoolbar.h" // application specific includes #include "debug_utils.h" #include "icon_utils.h" #include "optionstate.h" #include "uml.h" #include "umldoc.h" #include "umlview.h" // kde include files #include #if QT_VERSION < 0x050000 #include #endif #include // qt include files #include /** * Creates a work tool bar. * * @param parentWindow The parent of the toolbar. */ WorkToolBar::WorkToolBar(QMainWindow *parentWindow) : KToolBar(QLatin1String("worktoolbar"), parentWindow, Qt::TopToolBarArea, true, true, true) { m_CurrentButtonID = tbb_Undefined; loadPixmaps(); m_Type = Uml::DiagramType::Class; // first time in just want it to load arrow, // needs anything but Uml::DiagramType::Undefined setOrientation(Qt::Vertical); // setVerticalStretchable(true); // initialize old tool map, everything starts with select tool (arrow) m_map.insert(Uml::DiagramType::UseCase, tbb_Arrow); m_map.insert(Uml::DiagramType::Collaboration, tbb_Arrow); m_map.insert(Uml::DiagramType::Class, tbb_Arrow); m_map.insert(Uml::DiagramType::Object, tbb_Arrow); m_map.insert(Uml::DiagramType::Sequence, tbb_Arrow); m_map.insert(Uml::DiagramType::State, tbb_Arrow); m_map.insert(Uml::DiagramType::Activity, tbb_Arrow); m_map.insert(Uml::DiagramType::EntityRelationship, tbb_Arrow); m_map.insert(Uml::DiagramType::Undefined, tbb_Arrow); slotCheckToolBar(Uml::DiagramType::Undefined); } /** * Standard destructor. */ WorkToolBar::~WorkToolBar() { } /** * Inserts the button corresponding to the tbb value given * and activates the toggle. */ QAction* WorkToolBar::insertHotBtn(ToolBar_Buttons tbb) { #if QT_VERSION >= 0x050000 QAction *action = m_actions[tbb]; #else KAction *action = m_actions[tbb]; #endif addAction(action); action->setChecked(true); return action; } /** * Inserts most associations, just reduces some string * duplication (nice to translators) */ void WorkToolBar::insertBasicAssociations() { insertHotBtn(tbb_Association); if (m_Type == Uml::DiagramType::Class || m_Type == Uml::DiagramType::UseCase || m_Type == Uml::DiagramType::Component || m_Type == Uml::DiagramType::Deployment) { insertHotBtn(tbb_UniAssociation); } if(m_Type != Uml::DiagramType::Object){ insertHotBtn(tbb_Dependency); insertHotBtn(tbb_Generalization); } } void WorkToolBar::slotCheckToolBar(Uml::DiagramType::Enum dt) { if (dt == m_Type) return; clear(); m_Type = dt; if (m_Type == Uml::DiagramType::Undefined) return; // insert note, anchor and lines of text on all diagrams QAction* action = insertHotBtn(tbb_Arrow); action->setChecked(true); m_CurrentButtonID = tbb_Arrow; insertHotBtn(tbb_Note); insertHotBtn(tbb_Anchor); insertHotBtn(tbb_Text); insertHotBtn(tbb_Box); // insert diagram specific tools switch (m_Type) { case Uml::DiagramType::UseCase: insertHotBtn(tbb_Actor); insertHotBtn(tbb_UseCase); insertBasicAssociations(); break; case Uml::DiagramType::Class: insertHotBtn(tbb_Class); insertHotBtn(tbb_Interface); insertHotBtn(tbb_Datatype); insertHotBtn(tbb_Enum); insertHotBtn(tbb_Package); insertBasicAssociations(); insertHotBtn(tbb_Composition); insertHotBtn(tbb_Aggregation); insertHotBtn(tbb_Containment); break; case Uml::DiagramType::Object: insertHotBtn(tbb_Instance); insertHotBtn(tbb_Association); break; case Uml::DiagramType::Sequence: insertHotBtn(tbb_Object); insertHotBtn(tbb_Seq_Message_Creation); + insertHotBtn(tbb_Seq_Message_Destroy); insertHotBtn(tbb_Seq_Message_Synchronous); insertHotBtn(tbb_Seq_Message_Asynchronous); insertHotBtn(tbb_Seq_Message_Found); insertHotBtn(tbb_Seq_Message_Lost); insertHotBtn(tbb_Seq_Combined_Fragment); insertHotBtn(tbb_Seq_Precondition); break; case Uml::DiagramType::Collaboration: insertHotBtn(tbb_Object); insertHotBtn(tbb_Coll_Message_Asynchronous); insertHotBtn(tbb_Coll_Message_Synchronous); break; case Uml::DiagramType::State: insertHotBtn(tbb_Initial_State); insertHotBtn(tbb_State); insertHotBtn(tbb_End_State); insertHotBtn(tbb_State_Transition); insertHotBtn(tbb_DeepHistory); insertHotBtn(tbb_ShallowHistory); insertHotBtn(tbb_StateJoin); insertHotBtn(tbb_StateFork); insertHotBtn(tbb_Junction); insertHotBtn(tbb_Choice); //insertHotBtn(tbb_Andline); //NotYetImplemented break; case Uml::DiagramType::Activity: insertHotBtn(tbb_Initial_Activity); insertHotBtn(tbb_Activity); insertHotBtn(tbb_End_Activity); insertHotBtn(tbb_Final_Activity); insertHotBtn(tbb_Branch); insertHotBtn(tbb_Fork); insertHotBtn(tbb_Activity_Transition); insertHotBtn(tbb_Exception); insertHotBtn(tbb_PrePostCondition); insertHotBtn(tbb_Send_Signal); insertHotBtn(tbb_Accept_Signal); insertHotBtn(tbb_Accept_Time_Event); insertHotBtn(tbb_Region); insertHotBtn(tbb_Pin); insertHotBtn(tbb_Object_Node); break; case Uml::DiagramType::Component: insertHotBtn(tbb_SubSystem); if (Settings::optionState().generalState.uml2) insertHotBtn(tbb_Interface_Requirement); insertHotBtn(tbb_Component); if (Settings::optionState().generalState.uml2) insertHotBtn(tbb_Port); insertHotBtn(tbb_Interface_Provider); insertHotBtn(tbb_Artifact); insertBasicAssociations(); break; case Uml::DiagramType::Deployment: insertHotBtn(tbb_Object); insertHotBtn(tbb_Interface); insertHotBtn(tbb_Component); insertHotBtn(tbb_Node); insertBasicAssociations(); break; case Uml::DiagramType::EntityRelationship: insertHotBtn(tbb_Entity); insertHotBtn(tbb_Category); insertHotBtn(tbb_Relationship); insertHotBtn(tbb_Category2Parent); insertHotBtn(tbb_Child2Category); break; default: uWarning() << "slotCheckToolBar() on unknown diagram type:" << Uml::DiagramType::toString(m_Type); break; } } void WorkToolBar::buttonChanged(int b) { UMLView* view = UMLApp::app()->currentView(); // if trying to turn off arrow - stop it ToolBar_Buttons tbb = (ToolBar_Buttons)b; if (tbb == tbb_Arrow && m_CurrentButtonID == tbb_Arrow) { m_actions[tbb_Arrow]->toggle(); // signal needed, in the case (when switching diagrams) that // Arrow Button gets activated, but the toolBarState of the Views may be different emit sigButtonChanged(m_CurrentButtonID); view->setCursor(currentCursor()); return; } // if toggling off a button set to arrow if (tbb == m_CurrentButtonID) { m_map[m_Type] = m_CurrentButtonID; // store old tool for this diagram type m_actions[tbb_Arrow]->toggle(); m_CurrentButtonID = tbb_Arrow; emit sigButtonChanged(m_CurrentButtonID); view->setCursor(currentCursor()); return; } m_map[m_Type] = m_CurrentButtonID; m_actions[m_CurrentButtonID]->toggle(); m_CurrentButtonID = tbb; emit sigButtonChanged(m_CurrentButtonID); view->setCursor(currentCursor()); } /** * Returns the current cursor depending on m_CurrentButtonID */ QCursor WorkToolBar::currentCursor() { return m_cursors[m_CurrentButtonID]; } /** * Returns the default cursor */ QCursor WorkToolBar::defaultCursor() { return Qt::ArrowCursor; } void WorkToolBar::slotResetToolBar() { if (m_CurrentButtonID == tbb_Undefined) return; if (m_CurrentButtonID == tbb_Arrow) return; // really shouldn't occur m_actions[m_CurrentButtonID]->toggle(); m_CurrentButtonID = tbb_Arrow; m_actions[m_CurrentButtonID]->toggle(); emit sigButtonChanged(m_CurrentButtonID); UMLView* view = UMLApp::app()->currentView(); if (view != 0) { view->setCursor(defaultCursor()); } } /** * Sets the current tool to the previously used Tool. This is just * as if the user had pressed the button for the other tool. */ void WorkToolBar::setOldTool() { QToolButton *b = (QToolButton*) widgetForAction(m_actions[m_map[m_Type]]); if (b) b->animateClick(); } /** * Sets the current tool to the default tool. (select tool) * Calling this function is as if the user had pressed the "arrow" * button on the toolbar. */ void WorkToolBar::setDefaultTool() { QToolButton *b = (QToolButton*) widgetForAction(m_actions[tbb_Arrow]); if (b) b->animateClick(); } /** * Loads toolbar icon and mouse cursor images from disk */ void WorkToolBar::loadPixmaps() { const struct ButtonInfo { const ToolBar_Buttons tbb; const QString btnName; const Icon_Utils::IconType icon; const char *slotName; } buttonInfo[] = { { tbb_Arrow, i18nc("selection arrow", "Select"), Icon_Utils::it_Arrow, SLOT(slotArrow()) }, { tbb_Object, i18n("Object"), Icon_Utils::it_Object, SLOT(slotObject()) }, { tbb_Seq_Message_Creation, i18n("Creation"), Icon_Utils::it_Message_Creation, SLOT(slotSeq_Message_Creation()) }, + { tbb_Seq_Message_Destroy, i18n("Destroy"), Icon_Utils::it_Message_Destroy, SLOT(slotSeq_Message_Destroy()) }, { tbb_Seq_Message_Synchronous, i18n("Synchronous Message"), Icon_Utils::it_Message_Sync, SLOT(slotSeq_Message_Synchronous()) }, { tbb_Seq_Message_Asynchronous, i18n("Asynchronous Message"), Icon_Utils::it_Message_Async, SLOT(slotSeq_Message_Asynchronous()) }, { tbb_Seq_Message_Found, i18n("Found Message"), Icon_Utils::it_Message_Found, SLOT(slotSeq_Message_Found()) }, { tbb_Seq_Message_Lost, i18n("Lost Message"), Icon_Utils::it_Message_Lost, SLOT(slotSeq_Message_Lost()) }, { tbb_Seq_Combined_Fragment, i18n("Combined Fragment"), Icon_Utils::it_Combined_Fragment, SLOT(slotSeq_Combined_Fragment()) }, { tbb_Seq_Precondition, i18n("Precondition"), Icon_Utils::it_Precondition, SLOT(slotSeq_Precondition()) }, { tbb_Association, i18n("Association"), Icon_Utils::it_Association, SLOT(slotAssociation()) }, { tbb_Containment, i18n("Containment"), Icon_Utils::it_Containment, SLOT(slotContainment()) }, { tbb_Anchor, i18n("Anchor"), Icon_Utils::it_Anchor, SLOT(slotAnchor()) }, { tbb_Text, i18n("Label"), Icon_Utils::it_Text, SLOT(slotText()) }, { tbb_Note, i18n("Note"), Icon_Utils::it_Note, SLOT(slotNote()) }, { tbb_Box, i18n("Box"), Icon_Utils::it_Box, SLOT(slotBox()) }, { tbb_Actor, i18n("Actor"), Icon_Utils::it_Actor, SLOT(slotActor()) }, { tbb_Dependency, i18n("Dependency"), Icon_Utils::it_Dependency, SLOT(slotDependency()) }, { tbb_Aggregation, i18n("Aggregation"), Icon_Utils::it_Aggregation, SLOT(slotAggregation()) }, { tbb_Relationship, i18n("Relationship"), Icon_Utils::it_Relationship, SLOT(slotRelationship()) }, { tbb_UniAssociation, i18n("Directional Association"), Icon_Utils::it_Directional_Association, SLOT(slotUniAssociation()) }, { tbb_Generalization, i18n("Generalization"), Icon_Utils::it_Generalisation, SLOT(slotGeneralization()) }, { tbb_Composition, i18n("Composition"), Icon_Utils::it_Composition, SLOT(slotComposition()) }, { tbb_UseCase, i18n("Use Case"), Icon_Utils::it_UseCase, SLOT(slotUseCase()) }, { tbb_Class, i18nc("UML class", "Class"), Icon_Utils::it_Class, SLOT(slotClass()) }, { tbb_Initial_State, i18n("Initial State"), Icon_Utils::it_InitialState, SLOT(slotInitial_State()) }, { tbb_Region, i18n("Region"), Icon_Utils::it_Region, SLOT(slotRegion()) }, { tbb_End_State, i18n("End State"), Icon_Utils::it_EndState, SLOT(slotEnd_State()) }, { tbb_Branch, i18n("Branch/Merge"), Icon_Utils::it_Branch, SLOT(slotBranch()) }, { tbb_Send_Signal, i18n("Send signal"), Icon_Utils::it_Send_Signal, SLOT(slotSend_Signal()) }, { tbb_Accept_Signal, i18n("Accept signal"), Icon_Utils::it_Accept_Signal, SLOT(slotAccept_Signal()) }, { tbb_Accept_Time_Event, i18n("Accept time event"), Icon_Utils::it_Accept_TimeEvent, SLOT(slotAccept_Time_Event()) }, { tbb_Fork, i18n("Fork/Join"), Icon_Utils::it_Fork_Join, SLOT(slotFork()) }, { tbb_Package, i18n("Package"), Icon_Utils::it_Package, SLOT(slotPackage()) }, { tbb_Component, i18n("Component"), Icon_Utils::it_Component, SLOT(slotComponent()) }, { tbb_Node, i18n("Node"), Icon_Utils::it_Node, SLOT(slotNode()) }, { tbb_Artifact, i18n("Artifact"), Icon_Utils::it_Artifact, SLOT(slotArtifact()) }, { tbb_Interface, i18n("Interface"), Icon_Utils::it_Interface, SLOT(slotInterface()) }, { tbb_Interface_Provider, i18n("Interface Provider"), Icon_Utils::it_Interface_Provider, SLOT(slotInterfaceProvider()) }, { tbb_Interface_Requirement, i18n("Interface required"), Icon_Utils::it_Interface_Requirement, SLOT(slotInterfaceRequired()) }, { tbb_Datatype, i18n("Datatype"), Icon_Utils::it_Datatype, SLOT(slotDatatype()) }, { tbb_Enum, i18n("Enum"), Icon_Utils::it_Enum, SLOT(slotEnum()) }, { tbb_Entity, i18n("Entity"), Icon_Utils::it_Entity, SLOT(slotEntity()) }, { tbb_DeepHistory, i18n("Deep History"), Icon_Utils::it_History_Deep, SLOT(slotDeepHistory()) }, { tbb_ShallowHistory, i18n("Shallow History"), Icon_Utils::it_History_Shallow, SLOT(slotShallowHistory()) }, { tbb_StateJoin, i18nc("join states", "Join"), Icon_Utils::it_Join, SLOT(slotStateJoin()) }, { tbb_StateFork, i18n("Fork"), Icon_Utils::it_Fork_State, SLOT(slotStateFork()) }, { tbb_SubSystem, i18n("Subsystem"), Icon_Utils::it_Subsystem, SLOT(slotSubsystem()) }, { tbb_Junction, i18n("Junction"), Icon_Utils::it_Junction, SLOT(slotJunction()) }, { tbb_Choice, i18nc("state choice", "Choice"), Icon_Utils::it_Choice_Rhomb, SLOT(slotChoice()) }, //:TODO: let the user decide which symbol he wants (setting an option) //{ tbb_Choice, i18n("Choice"), Icon_Utils::it_Choice_Round, SLOT(slotChoice()) }, //NotYetImplemented { tbb_Andline, i18n("And Line"), Icon_Utils::it_And_Line, SLOT(slotAndline()) }, //NotYetImplemented { tbb_State_Transition, i18n("State Transition"), Icon_Utils::it_State_Transition, SLOT(slotState_Transition()) }, { tbb_Activity_Transition, i18n("Activity Transition"), Icon_Utils::it_Activity_Transition, SLOT(slotActivity_Transition()) }, { tbb_Activity, i18n("Activity"), Icon_Utils::it_Activity, SLOT(slotActivity()) }, { tbb_State, i18nc("state diagram", "State"), Icon_Utils::it_State, SLOT(slotState()) }, { tbb_End_Activity, i18n("End Activity"), Icon_Utils::it_Activity_End, SLOT(slotEnd_Activity()) }, { tbb_Final_Activity, i18n("Final Activity"), Icon_Utils::it_Activity_Final, SLOT(slotFinal_Activity()) }, { tbb_Pin, i18n("Pin"), Icon_Utils::it_Pin, SLOT(slotPin()) }, { tbb_Port, i18n("Port"), Icon_Utils::it_Pin, SLOT(slotPort()) }, { tbb_Initial_Activity, i18n("Initial Activity"), Icon_Utils::it_Activity_Initial, SLOT(slotInitial_Activity()) }, { tbb_Coll_Message_Synchronous, i18n("Synchronous Message"), Icon_Utils::it_Message_Synchronous, SLOT(slotColl_Message_Synchronous()) }, { tbb_Coll_Message_Asynchronous,i18n("Asynchronous Message"), Icon_Utils::it_Message_Asynchronous, SLOT(slotColl_Message_Asynchronous()) }, { tbb_Exception, i18n("Exception"), Icon_Utils::it_Exception, SLOT(slotException()) }, { tbb_Object_Node, i18n("Object Node"), Icon_Utils::it_Object_Node, SLOT(slotObject_Node()) }, { tbb_PrePostCondition, i18n("Pre/Post condition"), Icon_Utils::it_Condition_PrePost, SLOT(slotPrePostCondition()) }, { tbb_Category, i18n("Category"), Icon_Utils::it_Category, SLOT(slotCategory()) }, { tbb_Category2Parent, i18n("Category to Parent"), Icon_Utils::it_Category_Parent, SLOT(slotCategory2Parent()) }, { tbb_Child2Category, i18n("Child to Category"), Icon_Utils::it_Category_Child, SLOT(slotChild2Category()) }, { tbb_Instance, i18n("Instance"), Icon_Utils::it_Instance, SLOT(slotInstance()) } }; const size_t n_buttonInfos = sizeof(buttonInfo) / sizeof(ButtonInfo); KActionCollection *collection = UMLApp::app()->actionCollection(); for (uint i = 0; i < n_buttonInfos; ++i) { const ButtonInfo& info = buttonInfo[i]; QString key = QLatin1String(ENUM_NAME(WorkToolBar, ToolBar_Buttons, info.tbb)); #if QT_VERSION >= 0x050000 QAction *action = collection->addAction(key, this, info.slotName); action->setIcon(Icon_Utils::BarIcon(info.icon)); #else KAction *action = collection->addAction(key, this, info.slotName); action->setIcon(KIcon(Icon_Utils::BarIcon(info.icon))); #endif action->setText(info.btnName); m_actions[info.tbb] = action; m_cursors[info.tbb] = Icon_Utils::Cursor(info.icon); } // umbrello code use qt provided arrow cursor m_cursors[tbb_Arrow] = defaultCursor(); setupActions(); } /** * These slots are triggered by the buttons. They call buttonChanged with * the button id */ void WorkToolBar::slotArrow() { buttonChanged(tbb_Arrow); } void WorkToolBar::slotGeneralization() { buttonChanged(tbb_Generalization); } void WorkToolBar::slotAggregation() { buttonChanged(tbb_Aggregation); } void WorkToolBar::slotDependency() { buttonChanged(tbb_Dependency); } void WorkToolBar::slotAssociation() { buttonChanged(tbb_Association); } void WorkToolBar::slotContainment() { buttonChanged(tbb_Containment); } void WorkToolBar::slotColl_Message_Synchronous() { buttonChanged(tbb_Coll_Message_Synchronous); } void WorkToolBar::slotColl_Message_Asynchronous(){ buttonChanged(tbb_Coll_Message_Asynchronous);} void WorkToolBar::slotSeq_Message_Creation() { buttonChanged(tbb_Seq_Message_Creation); } +void WorkToolBar::slotSeq_Message_Destroy() { buttonChanged(tbb_Seq_Message_Destroy); } void WorkToolBar::slotSeq_Message_Synchronous() { buttonChanged(tbb_Seq_Message_Synchronous); } void WorkToolBar::slotSeq_Message_Asynchronous() { buttonChanged(tbb_Seq_Message_Asynchronous); } void WorkToolBar::slotSeq_Message_Found() { buttonChanged(tbb_Seq_Message_Found); } void WorkToolBar::slotSeq_Message_Lost() { buttonChanged(tbb_Seq_Message_Lost); } void WorkToolBar::slotSeq_Combined_Fragment() { buttonChanged(tbb_Seq_Combined_Fragment); } void WorkToolBar::slotSeq_Precondition() { buttonChanged(tbb_Seq_Precondition); } void WorkToolBar::slotComposition() { buttonChanged(tbb_Composition); } void WorkToolBar::slotRelationship() { buttonChanged(tbb_Relationship); } void WorkToolBar::slotUniAssociation() { buttonChanged(tbb_UniAssociation); } void WorkToolBar::slotState_Transition() { buttonChanged(tbb_State_Transition); } void WorkToolBar::slotActivity_Transition() { buttonChanged(tbb_Activity_Transition); } void WorkToolBar::slotAnchor() { buttonChanged(tbb_Anchor); } void WorkToolBar::slotNote() { buttonChanged(tbb_Note); } void WorkToolBar::slotBox() { buttonChanged(tbb_Box); } void WorkToolBar::slotText() { buttonChanged(tbb_Text); } void WorkToolBar::slotActor() { buttonChanged(tbb_Actor); } void WorkToolBar::slotUseCase() { buttonChanged(tbb_UseCase); } void WorkToolBar::slotClass() { buttonChanged(tbb_Class); } void WorkToolBar::slotInterface() { buttonChanged(tbb_Interface); } void WorkToolBar::slotInterfaceProvider() { buttonChanged(tbb_Interface_Provider); } void WorkToolBar::slotInterfaceRequired() { buttonChanged(tbb_Interface_Requirement); } void WorkToolBar::slotDatatype() { buttonChanged(tbb_Datatype); } void WorkToolBar::slotEnum() { buttonChanged(tbb_Enum); } void WorkToolBar::slotEntity() { buttonChanged(tbb_Entity); } void WorkToolBar::slotPackage() { buttonChanged(tbb_Package); } void WorkToolBar::slotComponent() { buttonChanged(tbb_Component); } void WorkToolBar::slotNode() { buttonChanged(tbb_Node); } void WorkToolBar::slotArtifact() { buttonChanged(tbb_Artifact); } void WorkToolBar::slotObject() { buttonChanged(tbb_Object); } void WorkToolBar::slotInitial_State() { buttonChanged(tbb_Initial_State); } void WorkToolBar::slotState() { buttonChanged(tbb_State); } void WorkToolBar::slotSend_Signal() { buttonChanged(tbb_Send_Signal); } void WorkToolBar::slotAccept_Signal() { buttonChanged(tbb_Accept_Signal); } void WorkToolBar::slotAccept_Time_Event() { buttonChanged(tbb_Accept_Time_Event); } void WorkToolBar::slotEnd_State() { buttonChanged(tbb_End_State); } void WorkToolBar::slotRegion() { buttonChanged(tbb_Region); } void WorkToolBar::slotInitial_Activity() { buttonChanged(tbb_Initial_Activity); } void WorkToolBar::slotActivity() { buttonChanged(tbb_Activity); } void WorkToolBar::slotEnd_Activity() { buttonChanged(tbb_End_Activity); } void WorkToolBar::slotFinal_Activity() { buttonChanged(tbb_Final_Activity); } void WorkToolBar::slotBranch() { buttonChanged(tbb_Branch); } void WorkToolBar::slotFork() { buttonChanged(tbb_Fork); } void WorkToolBar::slotDeepHistory() { buttonChanged(tbb_DeepHistory); } void WorkToolBar::slotShallowHistory() { buttonChanged(tbb_ShallowHistory); } void WorkToolBar::slotStateJoin() { buttonChanged(tbb_StateJoin); } void WorkToolBar::slotPin() { buttonChanged(tbb_Pin); } void WorkToolBar::slotPort() { buttonChanged(tbb_Port); } void WorkToolBar::slotStateFork() { buttonChanged(tbb_StateFork); } void WorkToolBar::slotJunction() { buttonChanged(tbb_Junction); } void WorkToolBar::slotChoice() { buttonChanged(tbb_Choice); } void WorkToolBar::slotAndline() { buttonChanged(tbb_Andline); } void WorkToolBar::slotException() { buttonChanged(tbb_Exception); } void WorkToolBar::slotObject_Node() { buttonChanged(tbb_Object_Node); } void WorkToolBar::slotPrePostCondition() { buttonChanged(tbb_PrePostCondition); } void WorkToolBar::slotCategory() { buttonChanged(tbb_Category); } void WorkToolBar::slotCategory2Parent() { buttonChanged(tbb_Category2Parent); } void WorkToolBar::slotChild2Category() { buttonChanged(tbb_Child2Category); } void WorkToolBar::slotInstance() { buttonChanged(tbb_Instance); } void WorkToolBar::slotSubsystem() { buttonChanged(tbb_SubSystem); } /** * Setup actions after reading shortcuts from settings */ void WorkToolBar::setupActions() { foreach(QAction *action, m_actions) { if (!action->shortcut().isEmpty()) { action->setToolTip(action->text() + QLatin1String("\t[") + action->shortcut().toString() + QLatin1String("]")); } else action->setToolTip(action->text()); } } diff --git a/umbrello/worktoolbar.h b/umbrello/worktoolbar.h index db5732cb2..af543011a 100644 --- a/umbrello/worktoolbar.h +++ b/umbrello/worktoolbar.h @@ -1,236 +1,238 @@ /*************************************************************************** * 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-2020 * * Umbrello UML Modeller Authors * ***************************************************************************/ #ifndef WORKTOOLBAR_H #define WORKTOOLBAR_H #include "basictypes.h" #include #include #include #include class QMainWindow; class KAction; /** * This is the toolbar that is displayed on the right-hand side of the program * window. For each type of diagram it will change to suit that document. * * To add a new tool button do the following: * - create a button pixmap (symbol) * - create a cursor pixmap * - add an element to the ToolBar_Buttons enum * - adjust function loadPixmaps * - adjust function slotCheckToolBar * * @short The toolbar that is different for each type of diagram. * @author Paul Hensgen * Bugs and comments to umbrello-devel@kde.org or https://bugs.kde.org */ class WorkToolBar : public KToolBar { Q_OBJECT Q_ENUMS(ToolBar_Buttons) public: explicit WorkToolBar(QMainWindow *parentWindow); ~WorkToolBar(); void setOldTool(); void setDefaultTool(); void setupActions(); /** * Enumeration of all available toolbar buttons. */ enum ToolBar_Buttons { tbb_Undefined = -1, tbb_Arrow, tbb_Generalization, tbb_Aggregation, tbb_Dependency, tbb_Association, tbb_Containment, tbb_Coll_Message_Synchronous, tbb_Coll_Message_Asynchronous, tbb_Seq_Message_Creation, + tbb_Seq_Message_Destroy, tbb_Seq_Message_Synchronous, tbb_Seq_Message_Asynchronous, tbb_Seq_Message_Found, tbb_Seq_Message_Lost, tbb_Seq_Combined_Fragment, tbb_Seq_Precondition, tbb_Composition, tbb_Relationship, tbb_UniAssociation, tbb_State_Transition, tbb_Activity_Transition, tbb_Send_Signal, tbb_Accept_Signal, tbb_Accept_Time_Event, tbb_Anchor, //keep anchor as last association until code uses better algorithm for testing tbb_Note, tbb_Box, tbb_Text, tbb_Actor, tbb_UseCase, tbb_Class, tbb_Interface, tbb_Interface_Provider, tbb_Interface_Requirement, tbb_Datatype, tbb_Enum, tbb_Entity, tbb_Package, tbb_Component, tbb_Node, tbb_Artifact, tbb_Object, tbb_Initial_State, tbb_State, tbb_Region, tbb_End_State, tbb_Initial_Activity, tbb_Activity, tbb_End_Activity, tbb_Final_Activity, tbb_Pin, tbb_Port, tbb_Branch, tbb_Fork, tbb_DeepHistory, tbb_ShallowHistory, tbb_StateFork, tbb_StateJoin, tbb_Junction, tbb_Choice, tbb_Andline, tbb_Exception, tbb_Object_Node, tbb_PrePostCondition, tbb_Category, tbb_Category2Parent, tbb_Child2Category, tbb_Instance, tbb_SubSystem }; private: typedef QMap OldToolMap; typedef QMap CursorMap; #if QT_VERSION >= 0x050000 typedef QMap ActionsMap; #else typedef QMap ActionsMap; #endif ToolBar_Buttons m_CurrentButtonID; OldToolMap m_map; Uml::DiagramType::Enum m_Type; CursorMap m_cursors; ActionsMap m_actions; void loadPixmaps(); QCursor currentCursor(); QCursor defaultCursor(); QAction* insertHotBtn(ToolBar_Buttons tbb); void insertBasicAssociations(); signals: void sigButtonChanged(int); public slots: void slotCheckToolBar(Uml::DiagramType::Enum dt); void buttonChanged(int b); void slotResetToolBar(); /** * These slots are triggered by the buttons. They call buttonChanged with * the button id */ void slotArrow(); void slotGeneralization(); void slotAggregation(); void slotDependency(); void slotAssociation(); void slotContainment(); void slotColl_Message_Synchronous(); void slotColl_Message_Asynchronous(); void slotSeq_Message_Creation(); + void slotSeq_Message_Destroy(); void slotSeq_Message_Synchronous(); void slotSeq_Message_Asynchronous(); void slotSeq_Message_Found(); void slotSeq_Message_Lost(); void slotSeq_Combined_Fragment(); void slotSeq_Precondition(); void slotComposition(); void slotRelationship(); void slotUniAssociation(); void slotState_Transition(); void slotActivity_Transition(); void slotAnchor(); // keep anchor as last association until code uses better algorithm for testing void slotNote(); void slotBox(); void slotText(); void slotActor(); void slotUseCase(); void slotClass(); void slotInterface(); void slotInterfaceProvider(); void slotInterfaceRequired(); void slotDatatype(); void slotEnum(); void slotEntity(); void slotPackage(); void slotComponent(); void slotNode(); void slotArtifact(); void slotObject(); void slotRegion(); void slotInitial_State(); void slotState(); void slotEnd_State(); void slotInitial_Activity(); void slotActivity(); void slotEnd_Activity(); void slotFinal_Activity(); void slotBranch(); void slotSend_Signal(); void slotAccept_Signal(); void slotAccept_Time_Event(); void slotFork(); void slotDeepHistory(); void slotShallowHistory(); void slotStateJoin(); void slotStateFork(); void slotJunction(); void slotChoice(); void slotAndline(); void slotException(); void slotPrePostCondition(); void slotPin(); void slotPort(); void slotObject_Node(); void slotCategory(); void slotCategory2Parent(); void slotChild2Category(); void slotInstance(); void slotSubsystem(); }; #endif