diff options
Diffstat (limited to 'src/libs/modelinglib/qmt')
93 files changed, 2736 insertions, 592 deletions
diff --git a/src/libs/modelinglib/qmt/config/configcontroller.cpp b/src/libs/modelinglib/qmt/config/configcontroller.cpp index 4bb9b806c4..c584bce1c2 100644 --- a/src/libs/modelinglib/qmt/config/configcontroller.cpp +++ b/src/libs/modelinglib/qmt/config/configcontroller.cpp @@ -9,12 +9,11 @@ #include "qmt/stereotype/stereotypecontroller.h" -#include <QDir> -#include <QFileInfo> -#include <QFile> - #include <QDebug> +using Utils::FilePath; +using Utils::FilePaths; + namespace qmt { class ConfigController::ConfigControllerPrivate @@ -39,7 +38,7 @@ void ConfigController::setStereotypeController(StereotypeController *stereotypeC d->m_stereotypeController = stereotypeController; } -void ConfigController::readStereotypeDefinitions(const QString &path) +void ConfigController::readStereotypeDefinitions(const FilePath &path) { if (path.isEmpty()) { // TODO add error handling @@ -54,25 +53,19 @@ void ConfigController::readStereotypeDefinitions(const QString &path) connect(&parser, &StereotypeDefinitionParser::toolbarParsed, this, &ConfigController::onToolbarParsed); - QStringList fileNames; - QDir dir; - QFileInfo fileInfo(path); - if (fileInfo.isFile()) { - dir.setPath(fileInfo.path()); - fileNames.append(fileInfo.fileName()); - } else if (fileInfo.isDir()) { - dir.setPath(path); - dir.setNameFilters(QStringList("*.def")); - fileNames = dir.entryList(QDir::Files); + FilePaths paths; + if (path.isFile()) { + paths.append(path); + } else if (path.isDir()) { + paths = path.dirEntries({ { "*.def" } }); } else { // TODO add error handling return; } - for (const QString &fileName : std::as_const(fileNames)) { - QFile file(QFileInfo(dir, fileName).absoluteFilePath()); - if (file.open(QIODevice::ReadOnly)) { - QString text = QString::fromUtf8(file.readAll()); - file.close(); + for (const FilePath &filePath : std::as_const(paths)) { + auto data = filePath.fileContents(); + if (data.has_value()) { + QString text = QString::fromUtf8(data.value()); StringTextSource source; source.setSourceId(1); source.setText(text); diff --git a/src/libs/modelinglib/qmt/config/configcontroller.h b/src/libs/modelinglib/qmt/config/configcontroller.h index 28e6d65afc..7d8af82f6e 100644 --- a/src/libs/modelinglib/qmt/config/configcontroller.h +++ b/src/libs/modelinglib/qmt/config/configcontroller.h @@ -5,6 +5,8 @@ #include "qmt/infrastructure/qmt_global.h" +#include <utils/filepath.h> + #include <QObject> namespace qmt { @@ -24,7 +26,7 @@ public: void setStereotypeController(StereotypeController *stereotypeController); - void readStereotypeDefinitions(const QString &path); + void readStereotypeDefinitions(const Utils::FilePath &path); private: void onStereotypeIconParsed(const StereotypeIcon &stereotypeIcon); diff --git a/src/libs/modelinglib/qmt/config/stereotypedefinitionparser.cpp b/src/libs/modelinglib/qmt/config/stereotypedefinitionparser.cpp index b3caebb164..d81f71c500 100644 --- a/src/libs/modelinglib/qmt/config/stereotypedefinitionparser.cpp +++ b/src/libs/modelinglib/qmt/config/stereotypedefinitionparser.cpp @@ -28,12 +28,15 @@ static const int KEYWORD_WIDTH = 6; static const int KEYWORD_HEIGHT = 7; static const int KEYWORD_MINWIDTH = 8; static const int KEYWORD_MINHEIGHT = 9; -static const int KEYWORD_LOCK_SIZE = 10; -static const int KEYWORD_DISPLAY = 11; -static const int KEYWORD_TEXTALIGN = 12; -static const int KEYWORD_BASECOLOR = 13; -static const int KEYWORD_SHAPE = 14; -static const int KEYWORD_OUTLINE = 15; +static const int KEYWORD_ICONWIDTH = 10; +static const int KEYWORD_ICONHEIGHT = 11; +static const int KEYWORD_LOCK_SIZE = 12; +static const int KEYWORD_DISPLAY = 13; +static const int KEYWORD_TEXTALIGN = 14; +static const int KEYWORD_BASECOLOR = 15; +static const int KEYWORD_SHAPE = 16; +static const int KEYWORD_OUTLINE = 17; +static const int KEYWORD_Z = 18; // Shape items static const int KEYWORD_CIRCLE = 30; @@ -102,8 +105,18 @@ static const int KEYWORD_DASH = 136; static const int KEYWORD_DASHDOT = 137; static const int KEYWORD_DASHDOTDOT = 138; static const int KEYWORD_COLOR = 139; - -// Operatoren +static const int KEYWORD_EMPHASIZED = 140; + +// Shape Value Units and Origin +static const int KEYWORD_ABS = 150; +static const int KEYWORD_FIX = 151; +static const int KEYWORD_SCALE = 152; +static const int KEYWORD_FRACT = 153; +static const int KEYWORD_ORIGIN = 154; +static const int KEYWORD_CENTER = 155; +static const int KEYWORD_SIZE = 156; + +// Operators static const int OPERATOR_SEMICOLON = 1; static const int OPERATOR_BRACE_OPEN = 2; static const int OPERATOR_BRACE_CLOSE = 3; @@ -111,6 +124,7 @@ static const int OPERATOR_COLON = 4; static const int OPERATOR_COMMA = 5; static const int OPERATOR_PERIOD = 6; static const int OPERATOR_MINUS = 7; +static const int OPERATOR_PERCENTAGE = 8; template <typename T, typename U> QHash<T, U> operator<<(QHash<T, U> hash, QPair<T, U> pair) { @@ -213,12 +227,15 @@ void StereotypeDefinitionParser::parse(ITextSource *source) {"height", KEYWORD_HEIGHT}, {"minwidth", KEYWORD_MINWIDTH}, {"minheight", KEYWORD_MINHEIGHT}, + {"iconwidth", KEYWORD_ICONWIDTH}, + {"iconheight", KEYWORD_ICONHEIGHT}, {"locksize", KEYWORD_LOCK_SIZE}, {"display", KEYWORD_DISPLAY}, {"textalignment", KEYWORD_TEXTALIGN}, {"basecolor", KEYWORD_BASECOLOR}, {"shape", KEYWORD_SHAPE}, {"outline", KEYWORD_OUTLINE}, + {"z", KEYWORD_Z}, {"circle", KEYWORD_CIRCLE}, {"ellipse", KEYWORD_ELLIPSE}, {"line", KEYWORD_LINE}, @@ -276,7 +293,15 @@ void StereotypeDefinitionParser::parse(ITextSource *source) {"dash", KEYWORD_DASH}, {"dashdot", KEYWORD_DASHDOT}, {"dashdotdot", KEYWORD_DASHDOTDOT}, - {"color", KEYWORD_COLOR}}); + {"color", KEYWORD_COLOR}, + {"emphasized", KEYWORD_EMPHASIZED}, + {"abs", KEYWORD_ABS}, + {"fix", KEYWORD_FIX}, + {"scale", KEYWORD_SCALE}, + {"fract", KEYWORD_FRACT}, + {"origin", KEYWORD_ORIGIN}, + {"center", KEYWORD_CENTER}, + {"size", KEYWORD_SIZE}}); textScanner.setOperators({{";", OPERATOR_SEMICOLON}, {"{", OPERATOR_BRACE_OPEN}, @@ -284,7 +309,8 @@ void StereotypeDefinitionParser::parse(ITextSource *source) {":", OPERATOR_COLON}, {",", OPERATOR_COMMA}, {".", OPERATOR_PERIOD}, - {"-", OPERATOR_MINUS}}); + {"-", OPERATOR_MINUS}, + {"%", OPERATOR_PERCENTAGE}}); textScanner.setSource(source); @@ -364,6 +390,23 @@ void StereotypeDefinitionParser::parseIcon() case KEYWORD_MINHEIGHT: stereotypeIcon.setMinHeight(parseFloatProperty()); break; + case KEYWORD_ICONWIDTH: + stereotypeIcon.setIconWith(parseFloatProperty()); + break; + case KEYWORD_ICONHEIGHT: + stereotypeIcon.setIconHeight(parseFloatProperty()); + break; + case KEYWORD_Z: + { + const static QHash<QString, StereotypeIcon::DepthLayer> zNames = QHash<QString, StereotypeIcon::DepthLayer>() + << qMakePair(QString("behinditems"), StereotypeIcon::DepthBehindItems) + << qMakePair(QString("amongitems"), StereotypeIcon::DepthAmongItems) + << qMakePair(QString("beforeitems"), StereotypeIcon::DepthBeforeItems); + parseEnum<StereotypeIcon::DepthLayer>( + parseIdentifierProperty(), zNames, token.sourcePos(), + [&](StereotypeIcon::DepthLayer layer) { stereotypeIcon.setDepthLayer(layer); }); + break; + } case KEYWORD_LOCK_SIZE: { const static QHash<QString, StereotypeIcon::SizeLock> lockNames @@ -577,7 +620,7 @@ QHash<int, StereotypeDefinitionParser::IconCommandParameter> StereotypeDefinitio throw StereotypeDefinitionParserError("Property given twice.", token.sourcePos()); IconCommandParameter parameter = parameters.value(token.subtype()); if (parameter.type() == IconCommandParameter::ShapeValue) - parameter.setShapeValue(ShapeValueF(parseFloatProperty(), parameter.unit(), parameter.origin())); + parameter.setShapeValue(parseShapeValueProperty(parameter.unit(), parameter.origin())); else if (parameter.type() == IconCommandParameter::Boolean) parameter.setBoolean(parseBoolProperty()); else @@ -650,8 +693,6 @@ void StereotypeDefinitionParser::parseRelation(CustomRelation::Element element) } case KEYWORD_COLOR: { - if (element != CustomRelation::Element::Relation) - throwUnknownPropertyError(token); Value expression = parseProperty(); if (expression.type() == Color) { relation.setColorType(CustomRelation::ColorType::Custom); @@ -663,6 +704,10 @@ void StereotypeDefinitionParser::parseRelation(CustomRelation::Element element) relation.setColorType(CustomRelation::ColorType::EndA); } else if (colorName == "b") { relation.setColorType(CustomRelation::ColorType::EndB); + } else if (colorName == "warning") { + relation.setColorType(CustomRelation::ColorType::Warning); + } else if (colorName == "error") { + relation.setColorType(CustomRelation::ColorType::Error); } else if (QColor::isValidColor(colorName)) { relation.setColorType(CustomRelation::ColorType::Custom); relation.setColor(QColor(colorName)); @@ -674,6 +719,9 @@ void StereotypeDefinitionParser::parseRelation(CustomRelation::Element element) } break; } + case KEYWORD_EMPHASIZED: + relation.setEmphasized(parseBoolProperty()); + break; case KEYWORD_END: parseRelationEnd(&relation); break; @@ -976,6 +1024,65 @@ StereotypeDefinitionParser::Value StereotypeDefinitionParser::parseProperty() return parseExpression(); } +ShapeValueF StereotypeDefinitionParser::parseShapeValueProperty(ShapeValueF::Unit unit, ShapeValueF::Origin origin) +{ + expectColon(); + qreal value = parseFloatExpression(); + // unit + Token token = d->m_scanner->read(); + if (token.type() == Token::TokenOperator) { + switch (token.subtype()) { + case OPERATOR_PERCENTAGE: + unit = ShapeValueF::UnitPercentage; + break; + default: + d->m_scanner->unread(token); + break; + } + } else if (token.type() == Token::TokenKeyword) { + switch (token.subtype()) { + case KEYWORD_ABS: + unit = ShapeValueF::UnitAbsolute; + break; + case KEYWORD_FIX: + unit = ShapeValueF::UnitRelative; + break; + case KEYWORD_SCALE: + unit = ShapeValueF::UnitScaled; + break; + case KEYWORD_FRACT: + unit = ShapeValueF::UnitPercentage; + break; + default: + d->m_scanner->unread(token); + break; + } + } else { + d->m_scanner->unread(token); + } + // origin + token = d->m_scanner->read(); + if (token.type() == Token::TokenKeyword) { + switch (token.subtype()) { + case KEYWORD_ORIGIN: + origin = ShapeValueF::OriginTopOrLeft; + break; + case KEYWORD_CENTER: + origin = ShapeValueF::OriginCenter; + break; + case KEYWORD_SIZE: + origin = ShapeValueF::OriginBottomOrRight; + break; + default: + d->m_scanner->unread(token); + break; + } + } else { + d->m_scanner->unread(token); + } + return ShapeValueF(value, unit, origin); +} + QString StereotypeDefinitionParser::parseStringExpression() { Token token = d->m_scanner->read(); @@ -1050,11 +1157,8 @@ QColor StereotypeDefinitionParser::parseColorExpression() Token token = d->m_scanner->read(); if (token.type() == Token::TokenIdentifier || token.type() == Token::TokenColor) { QString value = token.text().toLower(); - QColor color; - if (QColor::isValidColor(value)) { - color.setNamedColor(value); - return color; - } + if (QColor::isValidColorName(value)) + return QColor::fromString(value); } throw StereotypeDefinitionParserError("Expected color name.", token.sourcePos()); } @@ -1084,9 +1188,8 @@ StereotypeDefinitionParser::Value StereotypeDefinitionParser::parseExpression() return Value(Float, QVariant(value)); } else if (token.type() == Token::TokenColor) { QString value = token.text().toLower(); - QColor color; - if (QColor::isValidColor(value)) { - color.setNamedColor(value); + if (QColor::isValidColorName(value)) { + const QColor color = QColor::fromString(value); return Value(Color, QVariant(color)); } else { throw StereotypeDefinitionParserError("Invalid color.", token.sourcePos()); diff --git a/src/libs/modelinglib/qmt/config/stereotypedefinitionparser.h b/src/libs/modelinglib/qmt/config/stereotypedefinitionparser.h index e32126c730..97b14211ca 100644 --- a/src/libs/modelinglib/qmt/config/stereotypedefinitionparser.h +++ b/src/libs/modelinglib/qmt/config/stereotypedefinitionparser.h @@ -6,6 +6,7 @@ #include <QObject> #include "qmt/infrastructure/exceptions.h" #include "qmt/stereotype/customrelation.h" +#include "qmt/stereotype/shapevalue.h" #include "qmt/stereotype/toolbar.h" #include "sourcepos.h" @@ -20,7 +21,6 @@ class ITextSource; class Token; class StereotypeIcon; class Toolbar; -class ShapeValueF; class QMT_EXPORT StereotypeDefinitionParserError : public Exception { @@ -97,6 +97,7 @@ private: bool parseBoolProperty(); QColor parseColorProperty(); Value parseProperty(); + ShapeValueF parseShapeValueProperty(ShapeValueF::Unit unit, ShapeValueF::Origin origin); QString parseStringExpression(); qreal parseFloatExpression(); diff --git a/src/libs/modelinglib/qmt/controller/namecontroller.cpp b/src/libs/modelinglib/qmt/controller/namecontroller.cpp index d910be4b12..2f9506d3fe 100644 --- a/src/libs/modelinglib/qmt/controller/namecontroller.cpp +++ b/src/libs/modelinglib/qmt/controller/namecontroller.cpp @@ -3,9 +3,10 @@ #include "namecontroller.h" -#include <QFileInfo> #include <QDebug> +using Utils::FilePath; + namespace qmt { NameController::NameController(QObject *parent) @@ -17,10 +18,9 @@ NameController::~NameController() { } -QString NameController::convertFileNameToElementName(const QString &fileName) +QString NameController::convertFileNameToElementName(const FilePath &fileName) { - QFileInfo fileInfo(fileName); - QString baseName = fileInfo.baseName().trimmed(); + QString baseName = fileName.baseName().trimmed(); QString elementName; bool makeTitlecase = true; bool insertSpace = false; @@ -65,13 +65,19 @@ QString NameController::convertElementNameToBaseFileName(const QString &elementN return baseFileName; } -QString NameController::calcRelativePath(const QString &absoluteFileName, const QString &anchorPath) +FilePath NameController::calcRelativePath(const FilePath &absoluteFileName, + const FilePath &anchorPath) { + // TODO try using Utils::FilePath::relativePath + QString absoluteFilePath = absoluteFileName.path(); + QString anchorPathString = anchorPath.path(); int secondLastSlashIndex = -1; int slashIndex = -1; int i = 0; - while (i < absoluteFileName.size() && i < anchorPath.size() && absoluteFileName.at(i) == anchorPath.at(i)) { - if (absoluteFileName.at(i) == QLatin1Char('/')) { + while (i < absoluteFilePath.size() && i < anchorPathString.size() + && absoluteFilePath.at(i) == anchorPathString.at(i)) + { + if (absoluteFilePath.at(i) == QLatin1Char('/')) { secondLastSlashIndex = slashIndex; slashIndex = i; } @@ -81,22 +87,22 @@ QString NameController::calcRelativePath(const QString &absoluteFileName, const QString relativePath; if (slashIndex < 0) { - relativePath = absoluteFileName; - } else if (i >= absoluteFileName.size()) { + relativePath = absoluteFilePath; + } else if (i >= absoluteFilePath.size()) { // absoluteFileName is a substring of anchor path. if (slashIndex == i - 1) { if (secondLastSlashIndex < 0) - relativePath = absoluteFileName; + relativePath = absoluteFilePath; else - relativePath = absoluteFileName.mid(secondLastSlashIndex + 1); + relativePath = absoluteFilePath.mid(secondLastSlashIndex + 1); } else { - relativePath = absoluteFileName.mid(slashIndex + 1); + relativePath = absoluteFilePath.mid(slashIndex + 1); } } else { - relativePath = absoluteFileName.mid(slashIndex + 1); + relativePath = absoluteFilePath.mid(slashIndex + 1); } - return relativePath; + return FilePath::fromString(relativePath); } QString NameController::calcElementNameSearchId(const QString &elementName) @@ -109,16 +115,17 @@ QString NameController::calcElementNameSearchId(const QString &elementName) return searchId; } -QStringList NameController::buildElementsPath(const QString &filePath, bool ignoreLastFilePathPart) +QStringList NameController::buildElementsPath(const FilePath &filePath, + bool ignoreLastFilePathPart) { QList<QString> relativeElements; - QStringList split = filePath.split("/"); + QStringList split = filePath.toString().split("/"); QStringList::const_iterator splitEnd = split.constEnd(); if (ignoreLastFilePathPart || split.last().isEmpty()) --splitEnd; for (auto it = split.constBegin(); it != splitEnd; ++it) { - QString packageName = qmt::NameController::convertFileNameToElementName(*it); + QString packageName = qmt::NameController::convertFileNameToElementName(FilePath::fromString(*it)); relativeElements.append(packageName); } return relativeElements; diff --git a/src/libs/modelinglib/qmt/controller/namecontroller.h b/src/libs/modelinglib/qmt/controller/namecontroller.h index 03e3be30bb..bb1ff0c3e1 100644 --- a/src/libs/modelinglib/qmt/controller/namecontroller.h +++ b/src/libs/modelinglib/qmt/controller/namecontroller.h @@ -6,6 +6,8 @@ #include <QObject> #include "qmt/infrastructure/qmt_global.h" +#include <utils/filepath.h> + #include <QString> #include <QStringList> @@ -18,12 +20,12 @@ private: ~NameController() override; public: - static QString convertFileNameToElementName(const QString &fileName); + static QString convertFileNameToElementName(const Utils::FilePath &fileName); static QString convertElementNameToBaseFileName(const QString &elementName); - // TODO use Utils::FilePath instead - static QString calcRelativePath(const QString &absoluteFileName, const QString &anchorPath); + static Utils::FilePath calcRelativePath(const Utils::FilePath &absoluteFileName, + const Utils::FilePath &anchorPath); static QString calcElementNameSearchId(const QString &elementName); - static QStringList buildElementsPath(const QString &filePath, bool ignoreLastFilePathPart); + static QStringList buildElementsPath(const Utils::FilePath &filePath, bool ignoreLastFilePathPart); static bool parseClassName(const QString &fullClassName, QString *umlNamespace, QString *className, QStringList *templateParameters); }; diff --git a/src/libs/modelinglib/qmt/diagram/dobject.cpp b/src/libs/modelinglib/qmt/diagram/dobject.cpp index 4e0e5cb642..543e6af30f 100644 --- a/src/libs/modelinglib/qmt/diagram/dobject.cpp +++ b/src/libs/modelinglib/qmt/diagram/dobject.cpp @@ -6,11 +6,11 @@ #include "dvisitor.h" #include "dconstvisitor.h" +using Utils::FilePath; + namespace qmt { -DObject::DObject() -{ -} +DObject::DObject() {} DObject::DObject(const DObject &rhs) : DElement(rhs), @@ -25,7 +25,10 @@ DObject::DObject(const DObject &rhs) m_visualSecondaryRole(rhs.m_visualSecondaryRole), m_stereotypeDisplay(rhs.m_stereotypeDisplay), m_isAutoSized(rhs.m_isAutoSized), - m_isVisualEmphasized(rhs.m_isVisualEmphasized) + m_isVisualEmphasized(rhs.m_isVisualEmphasized), + m_hasLinkedFile(rhs.m_hasLinkedFile), + m_imagePath(rhs.m_imagePath), + m_image(rhs.m_image) { } @@ -49,6 +52,9 @@ DObject &DObject::operator =(const DObject &rhs) m_stereotypeDisplay = rhs.m_stereotypeDisplay; m_isAutoSized = rhs.m_isAutoSized; m_isVisualEmphasized = rhs.m_isVisualEmphasized; + m_hasLinkedFile = rhs.m_hasLinkedFile; + m_imagePath = rhs.m_imagePath; + m_image = rhs.m_image; } return *this; } @@ -113,6 +119,26 @@ void DObject::setVisualEmphasized(bool visualEmphasized) m_isVisualEmphasized = visualEmphasized; } +void DObject::setLinkedFile(bool linkedFile) +{ + m_hasLinkedFile = linkedFile; +} + +bool DObject::hasImage() const +{ + return !m_image.isNull(); +} + +void DObject::setImagePath(const FilePath &path) +{ + m_imagePath = path; +} + +void DObject::setImage(const QImage &image) +{ + m_image = image; +} + void DObject::accept(DVisitor *visitor) { visitor->visitDObject(this); diff --git a/src/libs/modelinglib/qmt/diagram/dobject.h b/src/libs/modelinglib/qmt/diagram/dobject.h index c8cf2b0e4d..7364f15749 100644 --- a/src/libs/modelinglib/qmt/diagram/dobject.h +++ b/src/libs/modelinglib/qmt/diagram/dobject.h @@ -7,6 +7,9 @@ #include "qmt/infrastructure/uid.h" +#include <utils/filepath.h> + +#include <QImage> #include <QList> #include <QPointF> #include <QRectF> @@ -78,6 +81,13 @@ public: void setAutoSized(bool autoSized); bool isVisualEmphasized() const { return m_isVisualEmphasized; } void setVisualEmphasized(bool visualEmphasized); + bool hasLinkedFile() const { return m_hasLinkedFile; } + void setLinkedFile(bool linkedFile); + Utils::FilePath imagePath() const { return m_imagePath; } + void setImagePath(const Utils::FilePath &path); + bool hasImage() const; + QImage image() const { return m_image; } + void setImage(const QImage &image); void accept(DVisitor *visitor) override; void accept(DConstVisitor *visitor) const override; @@ -95,6 +105,9 @@ private: StereotypeDisplay m_stereotypeDisplay = StereotypeSmart; bool m_isAutoSized = true; bool m_isVisualEmphasized = false; + bool m_hasLinkedFile = false; + Utils::FilePath m_imagePath; + QImage m_image; }; } // namespace qmt diff --git a/src/libs/modelinglib/qmt/diagram/drelation.cpp b/src/libs/modelinglib/qmt/diagram/drelation.cpp index a21f61c707..890bd275e4 100644 --- a/src/libs/modelinglib/qmt/diagram/drelation.cpp +++ b/src/libs/modelinglib/qmt/diagram/drelation.cpp @@ -65,6 +65,31 @@ void DRelation::setIntermediatePoints(const QList<DRelation::IntermediatePoint> m_intermediatePoints = intermediatePoints; } +void DRelation::setVisualPrimaryRole(VisualPrimaryRole visualPrimaryRole) +{ + m_visualPrimaryRole = visualPrimaryRole; +} + +void DRelation::setVisualSecondaryRole(VisualSecondaryRole visualSecondaryRole) +{ + m_visualSecondaryRole = visualSecondaryRole; +} + +void DRelation::setVisualEmphasized(bool visualEmphasized) +{ + m_isVisualEmphasized = visualEmphasized; +} + +void DRelation::setColor(const QColor &color) +{ + m_color = color; +} + +void DRelation::setThickness(qreal thickness) +{ + m_thickness = thickness; +} + void DRelation::accept(DVisitor *visitor) { visitor->visitDRelation(this); diff --git a/src/libs/modelinglib/qmt/diagram/drelation.h b/src/libs/modelinglib/qmt/diagram/drelation.h index 34bdaf1986..ed5624ad9f 100644 --- a/src/libs/modelinglib/qmt/diagram/drelation.h +++ b/src/libs/modelinglib/qmt/diagram/drelation.h @@ -5,6 +5,7 @@ #include "delement.h" +#include <QColor> #include <QList> #include <QPointF> @@ -15,6 +16,22 @@ class DObject; class QMT_EXPORT DRelation : public DElement { public: + enum VisualPrimaryRole { + PrimaryRoleNormal, + PrimaryRoleCustom1, + PrimaryRoleCustom2, + PrimaryRoleCustom3, + PrimaryRoleCustom4, + PrimaryRoleCustom5 + }; + + enum VisualSecondaryRole { + SecondaryRoleNone, + SecondaryRoleWarning, + SecondaryRoleError, + SecondaryRoleSoften + }; + class IntermediatePoint { public: @@ -42,6 +59,16 @@ public: void setName(const QString &name); const QList<IntermediatePoint> intermediatePoints() const { return m_intermediatePoints; } void setIntermediatePoints(const QList<IntermediatePoint> &intermediatePoints); + VisualPrimaryRole visualPrimaryRole() const { return m_visualPrimaryRole; } + void setVisualPrimaryRole(VisualPrimaryRole visualPrimaryRole); + VisualSecondaryRole visualSecondaryRole() const { return m_visualSecondaryRole; } + void setVisualSecondaryRole(VisualSecondaryRole visualSecondaryRole); + bool isVisualEmphasized() const { return m_isVisualEmphasized; } + void setVisualEmphasized(bool visualEmphasized); + QColor color() const { return m_color; } + void setColor(const QColor &color); + qreal thickness() const { return m_thickness; } + void setThickness(qreal thickness); void accept(DVisitor *visitor) override; void accept(DConstVisitor *visitor) const override; @@ -53,6 +80,11 @@ private: Uid m_endBUid; QString m_name; QList<IntermediatePoint> m_intermediatePoints; + VisualPrimaryRole m_visualPrimaryRole = PrimaryRoleNormal; + VisualSecondaryRole m_visualSecondaryRole = SecondaryRoleNone; + bool m_isVisualEmphasized = false; + QColor m_color; + qreal m_thickness = 0; }; bool operator==(const DRelation::IntermediatePoint &lhs, const DRelation::IntermediatePoint &rhs); diff --git a/src/libs/modelinglib/qmt/diagram_controller/dflatassignmentvisitor.cpp b/src/libs/modelinglib/qmt/diagram_controller/dflatassignmentvisitor.cpp index 5e88175812..5e4c072588 100644 --- a/src/libs/modelinglib/qmt/diagram_controller/dflatassignmentvisitor.cpp +++ b/src/libs/modelinglib/qmt/diagram_controller/dflatassignmentvisitor.cpp @@ -49,7 +49,10 @@ void DFlatAssignmentVisitor::visitDObject(const DObject *object) target->setVisualPrimaryRole(object->visualPrimaryRole()); target->setVisualSecondaryRole(object->visualSecondaryRole()); target->setVisualEmphasized(object->isVisualEmphasized()); + target->setLinkedFile(object->hasLinkedFile()); target->setStereotypeDisplay(object->stereotypeDisplay()); + target->setImagePath(object->imagePath()); + target->setImage(object->image()); } void DFlatAssignmentVisitor::visitDPackage(const DPackage *package) @@ -100,6 +103,8 @@ void DFlatAssignmentVisitor::visitDRelation(const DRelation *relation) QMT_ASSERT(target, return); target->setStereotypes(relation->stereotypes()); target->setIntermediatePoints(relation->intermediatePoints()); + target->setVisualPrimaryRole(relation->visualPrimaryRole()); + target->setThickness(relation->thickness()); } void DFlatAssignmentVisitor::visitDInheritance(const DInheritance *inheritance) diff --git a/src/libs/modelinglib/qmt/diagram_controller/dupdatevisitor.cpp b/src/libs/modelinglib/qmt/diagram_controller/dupdatevisitor.cpp index 91a95438ad..2dd079e1bc 100644 --- a/src/libs/modelinglib/qmt/diagram_controller/dupdatevisitor.cpp +++ b/src/libs/modelinglib/qmt/diagram_controller/dupdatevisitor.cpp @@ -65,13 +65,16 @@ void DUpdateVisitor::visitMObject(const MObject *object) } if (isUpdating(object->name() != dobject->name())) dobject->setName(object->name()); + bool hasLinkedFile = !object->linkedFileName().isEmpty(); + if (isUpdating(hasLinkedFile != dobject->hasLinkedFile())) + dobject->setLinkedFile(hasLinkedFile); // TODO unlikely that this is called for all objects if hierarchy is modified // PERFORM remove loop int depth = 1; const MObject *owner = object->owner(); while (owner) { owner = owner->owner(); - depth += 1; + depth += 3; } if (isUpdating(depth != dobject->depth())) dobject->setDepth(depth); diff --git a/src/libs/modelinglib/qmt/diagram_scene/capabilities/windable.h b/src/libs/modelinglib/qmt/diagram_scene/capabilities/windable.h index a21f1caea3..c31249e5c1 100644 --- a/src/libs/modelinglib/qmt/diagram_scene/capabilities/windable.h +++ b/src/libs/modelinglib/qmt/diagram_scene/capabilities/windable.h @@ -20,7 +20,7 @@ public: virtual void insertHandle(int beforeIndex, const QPointF &pos, double rasterWidth, double rasterHeight) = 0; virtual void deleteHandle(int index) = 0; virtual void setHandlePos(int index, const QPointF &pos) = 0; - virtual void dropHandle(int index, double rasterWidth, double rasterHeight) = 0; + virtual void dropHandle(int index, bool extraSnap, double rasterWidth, double rasterHeight) = 0; }; } // namespace qmt diff --git a/src/libs/modelinglib/qmt/diagram_scene/diagramscenemodel.cpp b/src/libs/modelinglib/qmt/diagram_scene/diagramscenemodel.cpp index de8b82de20..9366a39503 100644 --- a/src/libs/modelinglib/qmt/diagram_scene/diagramscenemodel.cpp +++ b/src/libs/modelinglib/qmt/diagram_scene/diagramscenemodel.cpp @@ -209,15 +209,26 @@ DSelection DiagramSceneModel::selectedElements() const return selection; } -DElement *DiagramSceneModel::findTopmostElement(const QPointF &scenePos) const +QGraphicsItem *DiagramSceneModel::findTopmostItem(const QPointF &scenePos) const { // fetch affected items from scene in correct drawing order to find topmost element const QList<QGraphicsItem *> items = m_graphicsScene->items(scenePos); + QGraphicsItem *candidate = NULL; for (QGraphicsItem *item : items) { - if (m_graphicsItems.contains(item)) - return m_itemToElementMap.value(item); + if (m_graphicsItems.contains(item)) { + if (!candidate || item->zValue() > candidate->zValue()) + candidate = item; + } } - return nullptr; + return candidate; +} + +DElement *DiagramSceneModel::findTopmostElement(const QPointF &scenePos) const +{ + QGraphicsItem *item = findTopmostItem(scenePos); + if (!item) + return nullptr; + return m_itemToElementMap.value(item); } DObject *DiagramSceneModel::findTopmostObject(const QPointF &scenePos) const @@ -230,16 +241,13 @@ DObject *DiagramSceneModel::findTopmostObject(const QPointF &scenePos) const ObjectItem *DiagramSceneModel::findTopmostObjectItem(const QPointF &scenePos) const { - // fetch affected items from scene in correct drawing order to find topmost element - const QList<QGraphicsItem *> items = m_graphicsScene->items(scenePos); - for (QGraphicsItem *item : std::as_const(items)) { - if (m_graphicsItems.contains(item)) { - DObject *object = dynamic_cast<DObject *>(m_itemToElementMap.value(item)); - if (object) - return dynamic_cast<ObjectItem *>(item); - } - } - return nullptr; + QGraphicsItem *item = findTopmostItem(scenePos); + if (!item) + return nullptr; + DObject *object = dynamic_cast<DObject *>(m_itemToElementMap.value(item)); + if (!object) + return nullptr; + return dynamic_cast<ObjectItem *>(item); } QGraphicsItem *DiagramSceneModel::graphicsItem(DElement *element) const @@ -353,7 +361,8 @@ void DiagramSceneModel::selectAllElements() void DiagramSceneModel::selectElement(DElement *element) { QGraphicsItem *selectItem = m_elementToItemMap.value(element); - for (QGraphicsItem *item : std::as_const(m_selectedItems)) { + auto selectedItems = m_selectedItems; + for (QGraphicsItem *item : std::as_const(selectedItems)) { if (item != selectItem) item->setSelected(false); } @@ -363,9 +372,17 @@ void DiagramSceneModel::selectElement(DElement *element) void DiagramSceneModel::editElement(DElement *element) { - auto editable = dynamic_cast<IEditable *>(m_elementToItemMap.value(element)); - if (editable && editable->isEditable()) + QGraphicsItem *item = m_elementToItemMap.value(element); + auto editable = dynamic_cast<IEditable *>(item); + if (editable && editable->isEditable()) { + auto selectable = dynamic_cast<ISelectable *>(item); + if (selectable && item != m_focusItem) { + unsetFocusItem(); + selectable->setFocusSelected(true); + m_focusItem = item; + } editable->edit(); + } } void DiagramSceneModel::copyToClipboard() @@ -507,9 +524,8 @@ void DiagramSceneModel::selectItem(QGraphicsItem *item, bool multiSelect) { if (!multiSelect) { if (!item->isSelected()) { - // We have to create a copy since "setSelected" may modify m_selectedItems - const QSet<QGraphicsItem *> copy = m_selectedItems; - for (QGraphicsItem *selectedItem : copy) { + auto selectedItems = m_selectedItems; + for (QGraphicsItem *selectedItem : std::as_const(selectedItems)) { if (selectedItem != item) selectedItem->setSelected(false); } @@ -624,6 +640,7 @@ void DiagramSceneModel::keyReleaseEvent(QKeyEvent *event) void DiagramSceneModel::mousePressEvent(QGraphicsSceneMouseEvent *event) { + m_mousePressed = true; updateFocusItem(Utils::toSet(m_graphicsScene->selectedItems())); m_latchController->mousePressEventLatching(event); mousePressEventReparenting(event); @@ -658,8 +675,10 @@ void DiagramSceneModel::mouseMoveEventReparenting(QGraphicsSceneMouseEvent *even void DiagramSceneModel::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) { + m_mousePressed = false; m_latchController->mouseReleaseEventLatching(event); mouseReleaseEventReparenting(event); + recalcSceneRectSize(); } void DiagramSceneModel::mouseReleaseEventReparenting(QGraphicsSceneMouseEvent *event) @@ -851,7 +870,7 @@ void DiagramSceneModel::onSelectionChanged() QSet<QGraphicsItem *> newSecondarySelectedItems; // select all contained objects secondarily - for (QGraphicsItem *selectedItem : std::as_const(m_secondarySelectedItems)) { + for (QGraphicsItem *selectedItem : std::as_const(m_selectedItems)) { const QList<QGraphicsItem *> items = collectCollidingObjectItems(selectedItem, CollidingInnerItems); for (QGraphicsItem *item : items) { if (!item->isSelected() && dynamic_cast<ISelectable *>(item) @@ -908,7 +927,8 @@ void DiagramSceneModel::onSelectionChanged() } } - for (QGraphicsItem *item : std::as_const(m_secondarySelectedItems)) { + auto secondarySelectedItems = m_secondarySelectedItems; + for (QGraphicsItem *item : std::as_const(secondarySelectedItems)) { if (!newSecondarySelectedItems.contains(item)) { auto selectable = dynamic_cast<ISelectable *>(item); QMT_ASSERT(selectable, return); @@ -1039,8 +1059,19 @@ void DiagramSceneModel::recalcSceneRectSize() if (!dynamic_cast<SwimlaneItem *>(item)) sceneRect |= item->mapRectToScene(item->boundingRect()); } - m_sceneRect = sceneRect; - emit sceneRectChanged(sceneRect); + if (m_mousePressed) { + if (sceneRect.left() < m_sceneRect.left()) + m_sceneRect.setLeft(sceneRect.left()); + if (sceneRect.right() > m_sceneRect.right()) + m_sceneRect.setRight(sceneRect.right()); + if (sceneRect.top() < m_sceneRect.top()) + m_sceneRect.setTop(sceneRect.top()); + if (sceneRect.bottom() > m_sceneRect.bottom()) + m_sceneRect.setBottom(sceneRect.bottom()); + } else { + m_sceneRect = sceneRect; + } + emit sceneRectChanged(m_sceneRect); } QGraphicsItem *DiagramSceneModel::createGraphicsItem(DElement *element) diff --git a/src/libs/modelinglib/qmt/diagram_scene/diagramscenemodel.h b/src/libs/modelinglib/qmt/diagram_scene/diagramscenemodel.h index 0021faf459..b976f421f9 100644 --- a/src/libs/modelinglib/qmt/diagram_scene/diagramscenemodel.h +++ b/src/libs/modelinglib/qmt/diagram_scene/diagramscenemodel.h @@ -79,6 +79,7 @@ public: bool hasSelection() const; bool hasMultiObjectsSelection() const; DSelection selectedElements() const; + QGraphicsItem *findTopmostItem(const QPointF &scenePos) const; DElement *findTopmostElement(const QPointF &scenePos) const; DObject *findTopmostObject(const QPointF &scenePos) const; ObjectItem *findTopmostObjectItem(const QPointF &scenePos) const; @@ -169,6 +170,7 @@ private: QGraphicsItem *m_focusItem = nullptr; QRectF m_sceneRect; QList<Uid> m_relationEndsUid; + bool m_mousePressed = false; }; } // namespace qmt diff --git a/src/libs/modelinglib/qmt/diagram_scene/items/classitem.cpp b/src/libs/modelinglib/qmt/diagram_scene/items/classitem.cpp index 14ad25b2c1..23a9c524a2 100644 --- a/src/libs/modelinglib/qmt/diagram_scene/items/classitem.cpp +++ b/src/libs/modelinglib/qmt/diagram_scene/items/classitem.cpp @@ -85,29 +85,16 @@ void ClassItem::update() m_methodsText.clear(); } - // custom icon - if (stereotypeIconDisplay() == StereotypeIcon::DisplayIcon) { - if (!m_customIcon) - m_customIcon = new CustomIconItem(diagramSceneModel(), this); - m_customIcon->setStereotypeIconId(stereotypeIconId()); - m_customIcon->setBaseSize(stereotypeIconMinimumSize(m_customIcon->stereotypeIcon(), CUSTOM_ICON_MINIMUM_AUTO_WIDTH, CUSTOM_ICON_MINIMUM_AUTO_HEIGHT)); - m_customIcon->setBrush(style->fillBrush()); - m_customIcon->setPen(style->outerLinePen()); - m_customIcon->setZValue(SHAPE_ZVALUE); - } else if (m_customIcon) { - m_customIcon->scene()->removeItem(m_customIcon); - delete m_customIcon; - m_customIcon = nullptr; - } + updateCustomIcon(style); // shape - if (!m_customIcon) { + if (!customIconItem()) { if (!m_shape) m_shape = new QGraphicsRectItem(this); m_shape->setBrush(style->fillBrush()); m_shape->setPen(style->outerLinePen()); m_shape->setZValue(SHAPE_ZVALUE); - } else if (m_shape){ + } else if (m_shape) { m_shape->scene()->removeItem(m_shape); delete m_shape; m_shape = nullptr; @@ -259,7 +246,7 @@ void ClassItem::update() m_templateParameterBox = nullptr; } - updateSelectionMarker(m_customIcon); + updateSelectionMarker(customIconItem()); updateRelationStarter(); updateAlignmentButtons(); updateGeometry(); @@ -267,8 +254,8 @@ void ClassItem::update() bool ClassItem::intersectShapeWithLine(const QLineF &line, QPointF *intersectionPoint, QLineF *intersectionLine) const { - if (m_customIcon) { - QList<QPolygonF> polygons = m_customIcon->outline(); + if (customIconItem()) { + QList<QPolygonF> polygons = customIconItem()->outline(); for (int i = 0; i < polygons.size(); ++i) polygons[i].translate(object()->pos() + object()->rect().topLeft()); if (shapeIcon().textAlignment() == qmt::StereotypeIcon::TextalignBelow) { @@ -508,7 +495,7 @@ DClass::TemplateDisplay ClassItem::templateDisplay() const DClass::TemplateDisplay templateDisplay = diagramClass->templateDisplay(); if (templateDisplay == DClass::TemplateSmart) { - if (m_customIcon) + if (customIconItem()) templateDisplay = DClass::TemplateName; else templateDisplay = DClass::TemplateBox; @@ -521,8 +508,8 @@ QSizeF ClassItem::calcMinimumGeometry() const double width = 0.0; double height = 0.0; - if (m_customIcon) { - QSizeF sz = stereotypeIconMinimumSize(m_customIcon->stereotypeIcon(), + if (customIconItem()) { + QSizeF sz = customIconItemMinimumSize(customIconItem(), CUSTOM_ICON_MINIMUM_AUTO_WIDTH, CUSTOM_ICON_MINIMUM_AUTO_HEIGHT); if (shapeIcon().textAlignment() != qmt::StereotypeIcon::TextalignTop && shapeIcon().textAlignment() != qmt::StereotypeIcon::TextalignCenter) @@ -589,12 +576,7 @@ void ClassItem::updateGeometry() height = geometry.height(); if (object()->isAutoSized()) { - if (!m_customIcon) { - if (width < MINIMUM_AUTO_WIDTH) - width = MINIMUM_AUTO_WIDTH; - if (height < MINIMUM_AUTO_HEIGHT) - height = MINIMUM_AUTO_HEIGHT; - } + correctAutoSize(customIconItem(), width, height, MINIMUM_AUTO_WIDTH, MINIMUM_AUTO_HEIGHT); } else { QRectF rect = object()->rect(); if (rect.width() > width) @@ -619,15 +601,15 @@ void ClassItem::updateGeometry() // a backup for the graphics item used for manual resized and persistency. object()->setRect(rect); - if (m_customIcon) { - m_customIcon->setPos(left, top); - m_customIcon->setActualSize(QSizeF(width, height)); + if (customIconItem()) { + customIconItem()->setPos(left, top); + customIconItem()->setActualSize(QSizeF(width, height)); } if (m_shape) m_shape->setRect(rect); - if (m_customIcon) { + if (customIconItem()) { switch (shapeIcon().textAlignment()) { case qmt::StereotypeIcon::TextalignBelow: y += height + BODY_VERT_BORDER; @@ -679,7 +661,7 @@ void ClassItem::updateGeometry() y += nameItem()->boundingRect().height(); } if (m_contextLabel) { - if (m_customIcon) + if (customIconItem()) m_contextLabel->resetMaxWidth(); else m_contextLabel->setMaxWidth(width - 2 * BODY_HORIZ_BORDER); @@ -692,7 +674,7 @@ void ClassItem::updateGeometry() y += 8.0; } if (m_attributes) { - if (m_customIcon) + if (customIconItem()) m_attributes->setPos(-m_attributes->boundingRect().width() / 2.0, y); else m_attributes->setPos(left + BODY_HORIZ_BORDER, y); @@ -704,7 +686,7 @@ void ClassItem::updateGeometry() y += 8.0; } if (m_methods) { - if (m_customIcon) + if (customIconItem()) m_methods->setPos(-m_methods->boundingRect().width() / 2.0, y); else m_methods->setPos(left + BODY_HORIZ_BORDER, y); @@ -765,21 +747,22 @@ void ClassItem::updateMembers(const Style *style) break; } + bool needBr = false; if (text && !text->isEmpty()) - *text += "<br/>"; + needBr = true; - bool addNewline = false; - bool addSpace = false; if (currentVisibility) *currentVisibility = member.visibility(); if (currentGroup && member.group() != *currentGroup) { - *text += QString("[%1]").arg(member.group()); - addNewline = true; + needBr = false; + *text += QString("<p style=\"padding:0;margin-top:6;margin-bottom:0;\"><b>[%1]</b></p>").arg(member.group()); *currentGroup = member.group(); } - if (addNewline) + + if (needBr) *text += "<br/>"; + bool addSpace = false; bool haveSignal = false; bool haveSlot = false; if (member.visibility() != MClassMember::VisibilityUndefined) { diff --git a/src/libs/modelinglib/qmt/diagram_scene/items/classitem.h b/src/libs/modelinglib/qmt/diagram_scene/items/classitem.h index 83ce0a9a5a..cc92ab1ff5 100644 --- a/src/libs/modelinglib/qmt/diagram_scene/items/classitem.h +++ b/src/libs/modelinglib/qmt/diagram_scene/items/classitem.h @@ -53,7 +53,6 @@ private: void updateGeometry(); void updateMembers(const Style *style); - CustomIconItem *m_customIcon = nullptr; QGraphicsRectItem *m_shape = nullptr; QGraphicsSimpleTextItem *m_baseClasses = nullptr; QGraphicsSimpleTextItem *m_namespace = nullptr; diff --git a/src/libs/modelinglib/qmt/diagram_scene/items/componentitem.cpp b/src/libs/modelinglib/qmt/diagram_scene/items/componentitem.cpp index 6a54a001e5..e7fffcbf9a 100644 --- a/src/libs/modelinglib/qmt/diagram_scene/items/componentitem.cpp +++ b/src/libs/modelinglib/qmt/diagram_scene/items/componentitem.cpp @@ -53,24 +53,11 @@ void ComponentItem::update() const Style *style = adaptedStyle(stereotypeIconId()); - // custom icon - if (stereotypeIconDisplay() == StereotypeIcon::DisplayIcon) { - if (!m_customIcon) - m_customIcon = new CustomIconItem(diagramSceneModel(), this); - m_customIcon->setStereotypeIconId(stereotypeIconId()); - m_customIcon->setBaseSize(stereotypeIconMinimumSize(m_customIcon->stereotypeIcon(), CUSTOM_ICON_MINIMUM_AUTO_WIDTH, CUSTOM_ICON_MINIMUM_AUTO_HEIGHT)); - m_customIcon->setBrush(style->fillBrush()); - m_customIcon->setPen(style->outerLinePen()); - m_customIcon->setZValue(SHAPE_ZVALUE); - } else if (m_customIcon) { - m_customIcon->scene()->removeItem(m_customIcon); - delete m_customIcon; - m_customIcon = nullptr; - } + updateCustomIcon(style); // shape bool deleteRects = false; - if (!m_customIcon) { + if (!customIconItem()) { if (!m_shape) m_shape = new QGraphicsRectItem(this); m_shape->setBrush(style->fillBrush()); @@ -130,7 +117,7 @@ void ComponentItem::update() m_contextLabel = nullptr; } - updateSelectionMarker(m_customIcon); + updateSelectionMarker(customIconItem()); updateRelationStarter(); updateAlignmentButtons(); updateGeometry(); @@ -138,8 +125,8 @@ void ComponentItem::update() bool ComponentItem::intersectShapeWithLine(const QLineF &line, QPointF *intersectionPoint, QLineF *intersectionLine) const { - if (m_customIcon) { - QList<QPolygonF> polygons = m_customIcon->outline(); + if (customIconItem()) { + QList<QPolygonF> polygons = customIconItem()->outline(); for (int i = 0; i < polygons.size(); ++i) polygons[i].translate(object()->pos() + object()->rect().topLeft()); if (shapeIcon().textAlignment() == qmt::StereotypeIcon::TextalignBelow) { @@ -207,14 +194,16 @@ QSizeF ComponentItem::calcMinimumGeometry() const { double width = 0.0; double height = 0.0; + double customMinHeight = 0.0; - if (m_customIcon) { - QSizeF sz = stereotypeIconMinimumSize(m_customIcon->stereotypeIcon(), + if (customIconItem()) { + QSizeF sz = customIconItemMinimumSize(customIconItem(), CUSTOM_ICON_MINIMUM_AUTO_WIDTH, CUSTOM_ICON_MINIMUM_AUTO_HEIGHT); if (shapeIcon().textAlignment() != qmt::StereotypeIcon::TextalignTop && shapeIcon().textAlignment() != qmt::StereotypeIcon::TextalignCenter) return sz; width = sz.width(); + customMinHeight = sz.height(); } height += BODY_VERT_BORDER; @@ -234,7 +223,7 @@ QSizeF ComponentItem::calcMinimumGeometry() const height += m_contextLabel->height(); height += BODY_VERT_BORDER; - if (!hasPlainShape()) { + if (!customIconItem() && !hasPlainShape()) { width = RECT_WIDTH * 0.5 + BODY_HORIZ_BORDER + width + BODY_HORIZ_BORDER + RECT_WIDTH * 0.5; double minHeight = UPPER_RECT_Y + RECT_HEIGHT + RECT_Y_DISTANCE + RECT_HEIGHT + LOWER_RECT_MIN_Y; if (height < minHeight) @@ -243,6 +232,9 @@ QSizeF ComponentItem::calcMinimumGeometry() const width = BODY_HORIZ_BORDER + width + BODY_HORIZ_BORDER; } + if (height < customMinHeight) + height = customMinHeight; + return GeometryUtilities::ensureMinimumRasterSize(QSizeF(width, height), 2 * RASTER_WIDTH, 2 * RASTER_HEIGHT); } @@ -259,7 +251,7 @@ void ComponentItem::updateGeometry() height = geometry.height(); if (object()->isAutoSized()) { - // nothing + correctAutoSize(customIconItem(), width, height, 0, 0); } else { QRectF rect = object()->rect(); if (rect.width() > width) @@ -283,9 +275,9 @@ void ComponentItem::updateGeometry() // a backup for the graphics item used for manual resized and persistency. object()->setRect(rect); - if (m_customIcon) { - m_customIcon->setPos(left, top); - m_customIcon->setActualSize(QSizeF(width, height)); + if (customIconItem()) { + customIconItem()->setPos(left, top); + customIconItem()->setActualSize(QSizeF(width, height)); } if (m_shape) @@ -303,7 +295,7 @@ void ComponentItem::updateGeometry() m_lowerRect->setPos(left - RECT_WIDTH * 0.5, top + UPPER_RECT_Y + RECT_HEIGHT + RECT_Y_DISTANCE); } - if (m_customIcon) { + if (customIconItem()) { switch (shapeIcon().textAlignment()) { case qmt::StereotypeIcon::TextalignBelow: y += height + BODY_VERT_BORDER; @@ -345,7 +337,7 @@ void ComponentItem::updateGeometry() y += nameItem()->boundingRect().height(); } if (m_contextLabel) { - if (m_customIcon) { + if (customIconItem()) { m_contextLabel->resetMaxWidth(); } else { double maxContextWidth = width - 2 * BODY_HORIZ_BORDER - (hasPlainShape() ? 0 : RECT_WIDTH); diff --git a/src/libs/modelinglib/qmt/diagram_scene/items/componentitem.h b/src/libs/modelinglib/qmt/diagram_scene/items/componentitem.h index f00fbf30eb..f4910f53ce 100644 --- a/src/libs/modelinglib/qmt/diagram_scene/items/componentitem.h +++ b/src/libs/modelinglib/qmt/diagram_scene/items/componentitem.h @@ -41,7 +41,6 @@ private: QSizeF calcMinimumGeometry() const; void updateGeometry(); - CustomIconItem *m_customIcon = nullptr; QGraphicsRectItem *m_shape = nullptr; QGraphicsRectItem *m_upperRect = nullptr; QGraphicsRectItem *m_lowerRect = nullptr; diff --git a/src/libs/modelinglib/qmt/diagram_scene/items/diagramitem.cpp b/src/libs/modelinglib/qmt/diagram_scene/items/diagramitem.cpp index c37c38b3bb..951d6f3b1a 100644 --- a/src/libs/modelinglib/qmt/diagram_scene/items/diagramitem.cpp +++ b/src/libs/modelinglib/qmt/diagram_scene/items/diagramitem.cpp @@ -47,23 +47,10 @@ void DiagramItem::update() const Style *style = adaptedStyle(stereotypeIconId()); - // custom icon - if (stereotypeIconDisplay() == StereotypeIcon::DisplayIcon) { - if (!m_customIcon) - m_customIcon = new CustomIconItem(diagramSceneModel(), this); - m_customIcon->setStereotypeIconId(stereotypeIconId()); - m_customIcon->setBaseSize(stereotypeIconMinimumSize(m_customIcon->stereotypeIcon(), CUSTOM_ICON_MINIMUM_AUTO_WIDTH, CUSTOM_ICON_MINIMUM_AUTO_HEIGHT)); - m_customIcon->setBrush(style->fillBrush()); - m_customIcon->setPen(style->outerLinePen()); - m_customIcon->setZValue(SHAPE_ZVALUE); - } else if (m_customIcon) { - m_customIcon->scene()->removeItem(m_customIcon); - delete m_customIcon; - m_customIcon = nullptr; - } + updateCustomIcon(style); // shape - if (!m_customIcon) { + if (!customIconItem()) { if (!m_body) m_body = new QGraphicsPolygonItem(this); m_body->setBrush(style->fillBrush()); @@ -93,15 +80,15 @@ void DiagramItem::update() // diagram name updateNameItem(style); - updateSelectionMarker(m_customIcon); + updateSelectionMarker(customIconItem()); updateAlignmentButtons(); updateGeometry(); } bool DiagramItem::intersectShapeWithLine(const QLineF &line, QPointF *intersectionPoint, QLineF *intersectionLine) const { - if (m_customIcon) { - QList<QPolygonF> polygons = m_customIcon->outline(); + if (customIconItem()) { + QList<QPolygonF> polygons = customIconItem()->outline(); for (int i = 0; i < polygons.size(); ++i) polygons[i].translate(object()->pos() + object()->rect().topLeft()); if (shapeIcon().textAlignment() == qmt::StereotypeIcon::TextalignBelow) { @@ -131,8 +118,8 @@ QSizeF DiagramItem::calcMinimumGeometry() const double width = MINIMUM_WIDTH; double height = 0.0; - if (m_customIcon) { - QSizeF sz = stereotypeIconMinimumSize(m_customIcon->stereotypeIcon(), + if (customIconItem()) { + QSizeF sz = customIconItemMinimumSize(customIconItem(), CUSTOM_ICON_MINIMUM_AUTO_WIDTH, CUSTOM_ICON_MINIMUM_AUTO_HEIGHT); if (shapeIcon().textAlignment() != qmt::StereotypeIcon::TextalignTop && shapeIcon().textAlignment() != qmt::StereotypeIcon::TextalignCenter) @@ -175,12 +162,7 @@ void DiagramItem::updateGeometry() height = geometry.height(); if (object()->isAutoSized()) { - if (!m_customIcon) { - if (width < MINIMUM_AUTO_WIDTH) - width = MINIMUM_AUTO_WIDTH; - if (height < MINIMUM_AUTO_HEIGHT) - height = MINIMUM_AUTO_HEIGHT; - } + correctAutoSize(customIconItem(), width, height, MINIMUM_AUTO_WIDTH, MINIMUM_AUTO_HEIGHT); } else { QRectF rect = object()->rect(); if (rect.width() > width) @@ -203,9 +185,9 @@ void DiagramItem::updateGeometry() // a backup for the graphics item used for manual resized and persistency. object()->setRect(rect); - if (m_customIcon) { - m_customIcon->setPos(left, top); - m_customIcon->setActualSize(QSizeF(width, height)); + if (customIconItem()) { + customIconItem()->setPos(left, top); + customIconItem()->setActualSize(QSizeF(width, height)); } if (m_body) { @@ -227,7 +209,7 @@ void DiagramItem::updateGeometry() m_fold->setPolygon(foldPolygon); } - if (m_customIcon) { + if (customIconItem()) { switch (shapeIcon().textAlignment()) { case qmt::StereotypeIcon::TextalignBelow: y += height + BODY_VERT_BORDER; diff --git a/src/libs/modelinglib/qmt/diagram_scene/items/diagramitem.h b/src/libs/modelinglib/qmt/diagram_scene/items/diagramitem.h index 81012b04a1..88d9f9a7a8 100644 --- a/src/libs/modelinglib/qmt/diagram_scene/items/diagramitem.h +++ b/src/libs/modelinglib/qmt/diagram_scene/items/diagramitem.h @@ -29,7 +29,6 @@ private: QSizeF calcMinimumGeometry() const; void updateGeometry(); - CustomIconItem *m_customIcon = nullptr; QGraphicsPolygonItem *m_body = nullptr; QGraphicsPolygonItem *m_fold = nullptr; }; diff --git a/src/libs/modelinglib/qmt/diagram_scene/items/itemitem.cpp b/src/libs/modelinglib/qmt/diagram_scene/items/itemitem.cpp index 97a1b337fc..9855889b08 100644 --- a/src/libs/modelinglib/qmt/diagram_scene/items/itemitem.cpp +++ b/src/libs/modelinglib/qmt/diagram_scene/items/itemitem.cpp @@ -52,22 +52,10 @@ void ItemItem::update() const Style *style = adaptedStyle(shapeIconId()); - if (!shapeIconId().isEmpty()) { - if (!m_customIcon) - m_customIcon = new CustomIconItem(diagramSceneModel(), this); - m_customIcon->setStereotypeIconId(shapeIconId()); - m_customIcon->setBaseSize(stereotypeIconMinimumSize(m_customIcon->stereotypeIcon(), CUSTOM_ICON_MINIMUM_AUTO_WIDTH, CUSTOM_ICON_MINIMUM_AUTO_HEIGHT)); - m_customIcon->setBrush(style->fillBrush()); - m_customIcon->setPen(style->outerLinePen()); - m_customIcon->setZValue(SHAPE_ZVALUE); - } else if (m_customIcon) { - m_customIcon->scene()->removeItem(m_customIcon); - delete m_customIcon; - m_customIcon = nullptr; - } + updateCustomIcon(style); // shape - if (!m_customIcon) { + if (!customIconItem()) { if (!m_shape) m_shape = new QGraphicsRectItem(this); m_shape->setBrush(style->fillBrush()); @@ -100,7 +88,7 @@ void ItemItem::update() m_contextLabel = nullptr; } - updateSelectionMarker(m_customIcon); + updateSelectionMarker(customIconItem()); updateRelationStarter(); updateAlignmentButtons(); updateGeometry(); @@ -108,8 +96,8 @@ void ItemItem::update() bool ItemItem::intersectShapeWithLine(const QLineF &line, QPointF *intersectionPoint, QLineF *intersectionLine) const { - if (m_customIcon) { - QList<QPolygonF> polygons = m_customIcon->outline(); + if (customIconItem()) { + QList<QPolygonF> polygons = customIconItem()->outline(); for (int i = 0; i < polygons.size(); ++i) polygons[i].translate(object()->pos() + object()->rect().topLeft()); if (shapeIcon().textAlignment() == qmt::StereotypeIcon::TextalignBelow) { @@ -152,14 +140,16 @@ QSizeF ItemItem::calcMinimumGeometry() const { double width = 0.0; double height = 0.0; + double customMinHeight = 0.0; - if (m_customIcon) { - QSizeF sz = stereotypeIconMinimumSize(m_customIcon->stereotypeIcon(), + if (customIconItem()) { + QSizeF sz = customIconItemMinimumSize(customIconItem(), CUSTOM_ICON_MINIMUM_AUTO_WIDTH, CUSTOM_ICON_MINIMUM_AUTO_HEIGHT); if (shapeIcon().textAlignment() != qmt::StereotypeIcon::TextalignTop && shapeIcon().textAlignment() != qmt::StereotypeIcon::TextalignCenter) return sz; width = sz.width(); + customMinHeight = sz.height(); } height += BODY_VERT_BORDER; @@ -181,6 +171,9 @@ QSizeF ItemItem::calcMinimumGeometry() const width = BODY_HORIZ_BORDER + width + BODY_HORIZ_BORDER; + if (height < customMinHeight) + height = customMinHeight; + return GeometryUtilities::ensureMinimumRasterSize(QSizeF(width, height), 2 * RASTER_WIDTH, 2 * RASTER_HEIGHT); } @@ -197,7 +190,7 @@ void ItemItem::updateGeometry() height = geometry.height(); if (object()->isAutoSized()) { - // nothing + correctAutoSize(customIconItem(), width, height, 0, 0); } else { QRectF rect = object()->rect(); if (rect.width() > width) @@ -221,15 +214,15 @@ void ItemItem::updateGeometry() // a backup for the graphics item used for manual resized and persistency. object()->setRect(rect); - if (m_customIcon) { - m_customIcon->setPos(left, top); - m_customIcon->setActualSize(QSizeF(width, height)); + if (customIconItem()) { + customIconItem()->setPos(left, top); + customIconItem()->setActualSize(QSizeF(width, height)); } if (m_shape) m_shape->setRect(rect); - if (m_customIcon) { + if (customIconItem()) { switch (shapeIcon().textAlignment()) { case qmt::StereotypeIcon::TextalignBelow: y += height + BODY_VERT_BORDER; @@ -271,7 +264,7 @@ void ItemItem::updateGeometry() y += nameItem()->boundingRect().height(); } if (m_contextLabel) { - if (m_customIcon) { + if (customIconItem()) { m_contextLabel->resetMaxWidth(); } else { double maxContextWidth = width - 2 * BODY_HORIZ_BORDER; diff --git a/src/libs/modelinglib/qmt/diagram_scene/items/itemitem.h b/src/libs/modelinglib/qmt/diagram_scene/items/itemitem.h index 327b3799ac..de091a2bc7 100644 --- a/src/libs/modelinglib/qmt/diagram_scene/items/itemitem.h +++ b/src/libs/modelinglib/qmt/diagram_scene/items/itemitem.h @@ -39,7 +39,6 @@ private: QSizeF calcMinimumGeometry() const; void updateGeometry(); - CustomIconItem *m_customIcon = nullptr; QGraphicsRectItem *m_shape = nullptr; ContextLabelItem *m_contextLabel = nullptr; }; diff --git a/src/libs/modelinglib/qmt/diagram_scene/items/objectitem.cpp b/src/libs/modelinglib/qmt/diagram_scene/items/objectitem.cpp index 0fe32080cf..d4f6bd386b 100644 --- a/src/libs/modelinglib/qmt/diagram_scene/items/objectitem.cpp +++ b/src/libs/modelinglib/qmt/diagram_scene/items/objectitem.cpp @@ -185,6 +185,8 @@ void ObjectItem::setFocusSelected(bool focusSelected) if (m_isFocusSelected != focusSelected) { m_isFocusSelected = focusSelected; update(); + if (m_nameItem) + m_nameItem->setEditable(focusSelected); } } @@ -353,9 +355,9 @@ void ObjectItem::relationDrawn(const QString &id, ObjectItem *targetItem, const elementType = targetItem->stereotypeIconId(); else if (!targetItem->shapeIconId().isEmpty()) elementType = targetItem->shapeIconId(); - else - elementType = targetItem->elementType(); - if (!endItems.contains(elementType)) { + if ((!elementType.isEmpty() && !endItems.contains(elementType)) + && !endItems.contains(targetItem->elementType())) + { return; } // create relation @@ -498,6 +500,7 @@ void ObjectItem::updateStereotypes(const QString &stereotypeIconId, StereotypeIc m_stereotypeIcon = new CustomIconItem(m_diagramSceneModel, this); m_stereotypeIcon->setStereotypeIconId(stereotypeIconId); m_stereotypeIcon->setBaseSize(QSizeF(m_stereotypeIcon->shapeWidth(), m_stereotypeIcon->shapeHeight())); + m_stereotypeIcon->setActualSize(QSizeF(m_stereotypeIcon->shapeWidth(), m_stereotypeIcon->shapeHeight())); m_stereotypeIcon->setBrush(style->fillBrush()); m_stereotypeIcon->setPen(style->innerLinePen()); } else if (m_stereotypeIcon) { @@ -518,45 +521,92 @@ void ObjectItem::updateStereotypes(const QString &stereotypeIconId, StereotypeIc } } -QSizeF ObjectItem::stereotypeIconMinimumSize(const StereotypeIcon &stereotypeIcon, +QSizeF ObjectItem::customIconItemMinimumSize(const CustomIconItem *customIconItem, qreal minimumWidth, qreal minimumHeight) const { Q_UNUSED(minimumWidth) qreal width = 0.0; qreal height = 0.0; - if (stereotypeIcon.hasMinWidth() && !stereotypeIcon.hasMinHeight()) { - width = stereotypeIcon.minWidth(); - if (stereotypeIcon.sizeLock() == StereotypeIcon::LockHeight || stereotypeIcon.sizeLock() == StereotypeIcon::LockSize) - height = stereotypeIcon.minHeight(); - else - height = width * stereotypeIcon.height() / stereotypeIcon.width(); - } else if (!stereotypeIcon.hasMinWidth() && stereotypeIcon.hasMinHeight()) { - height = stereotypeIcon.minHeight(); - if (stereotypeIcon.sizeLock() == StereotypeIcon::LockWidth || stereotypeIcon.sizeLock() == StereotypeIcon::LockSize) - width = stereotypeIcon.minWidth(); - else - width = height * stereotypeIcon.width() / stereotypeIcon.height(); - } else if (stereotypeIcon.hasMinWidth() && stereotypeIcon.hasMinHeight()) { - if (stereotypeIcon.sizeLock() == StereotypeIcon::LockRatio) { + if (customIconItem->hasImage()) { + const QImage &image = customIconItem->image(); + width = minimumWidth; + height = image.height() * width / image.width(); + if (height < minimumHeight) { + height = minimumHeight; + width = image.width() * height / image.height(); + } + } else { + const StereotypeIcon &stereotypeIcon = customIconItem->stereotypeIcon(); + if (stereotypeIcon.hasMinWidth() && !stereotypeIcon.hasMinHeight()) { width = stereotypeIcon.minWidth(); - height = width * stereotypeIcon.height() / stereotypeIcon.width(); - if (height < stereotypeIcon.minHeight()) { + if (stereotypeIcon.sizeLock() == StereotypeIcon::LockHeight || stereotypeIcon.sizeLock() == StereotypeIcon::LockSize) height = stereotypeIcon.minHeight(); + else + height = width * stereotypeIcon.height() / stereotypeIcon.width(); + } else if (!stereotypeIcon.hasMinWidth() && stereotypeIcon.hasMinHeight()) { + height = stereotypeIcon.minHeight(); + if (stereotypeIcon.sizeLock() == StereotypeIcon::LockWidth || stereotypeIcon.sizeLock() == StereotypeIcon::LockSize) + width = stereotypeIcon.minWidth(); + else width = height * stereotypeIcon.width() / stereotypeIcon.height(); - QMT_CHECK(width <= stereotypeIcon.minWidth()); + } else if (stereotypeIcon.hasMinWidth() && stereotypeIcon.hasMinHeight()) { + if (stereotypeIcon.sizeLock() == StereotypeIcon::LockRatio) { + width = stereotypeIcon.minWidth(); + height = width * stereotypeIcon.height() / stereotypeIcon.width(); + if (height < stereotypeIcon.minHeight()) { + height = stereotypeIcon.minHeight(); + width = height * stereotypeIcon.width() / stereotypeIcon.height(); + QMT_CHECK(width <= stereotypeIcon.minWidth()); + } + } else { + width = stereotypeIcon.minWidth(); + height = stereotypeIcon.minHeight(); } } else { - width = stereotypeIcon.minWidth(); - height = stereotypeIcon.minHeight(); + height = minimumHeight; + width = height * stereotypeIcon.width() / stereotypeIcon.height(); } - } else { - height = minimumHeight; - width = height * stereotypeIcon.width() / stereotypeIcon.height(); } return QSizeF(width, height); } +void ObjectItem::correctAutoSize(const CustomIconItem* customIconItem, qreal &width, qreal &height, qreal minimumWidth, qreal minimumHeight) const +{ + if (customIconItem) { + if (customIconItem->hasImage()) { + width = customIconItem->image().width(); + height = customIconItem->image().height(); + } + } else { + if (width < minimumWidth) + width = minimumWidth; + if (height < minimumHeight) + height = minimumHeight; + } +} + +void ObjectItem::updateCustomIcon(const Style *style) +{ + if (object()->hasImage()) { + if (!m_customIcon) + m_customIcon = new CustomIconItem(diagramSceneModel(), this); + m_customIcon->setImage(object()->image()); + m_customIcon->setZValue(SHAPE_ZVALUE); + } else if (!shapeIconId().isEmpty()) { + if (!m_customIcon) + m_customIcon = new CustomIconItem(diagramSceneModel(), this); + m_customIcon->setStereotypeIconId(shapeIconId()); + m_customIcon->setBrush(style->fillBrush()); + m_customIcon->setPen(style->outerLinePen()); + m_customIcon->setZValue(SHAPE_ZVALUE); + } else if (m_customIcon) { + m_customIcon->scene()->removeItem(m_customIcon); + delete m_customIcon; + m_customIcon = nullptr; + } +} + bool ObjectItem::suppressTextDisplay() const { return m_shapeIcon.textAlignment() == StereotypeIcon::TextalignNone; @@ -564,6 +614,7 @@ bool ObjectItem::suppressTextDisplay() const void ObjectItem::updateNameItem(const Style *style) { + QString display_name = buildDisplayName(); if (!suppressTextDisplay()) { if (!m_nameItem) { m_nameItem = new EditableTextItem(this); @@ -582,16 +633,18 @@ void ObjectItem::updateNameItem(const Style *style) QObject::connect(m_nameItem, &EditableTextItem::returnKeyPressed, m_nameItem, [this] { this->m_nameItem->clearFocus(); }); } - if (style->headerFont() != m_nameItem->font()) - m_nameItem->setFont(style->headerFont()); + QFont font = style->headerFont(); + if (object()->hasLinkedFile()) + font.setUnderline(true); + if (font != m_nameItem->font()) + m_nameItem->setFont(font); if (style->textBrush().color() != m_nameItem->defaultTextColor()) m_nameItem->setDefaultTextColor(style->textBrush().color()); if (!m_nameItem->hasFocus()) { - QString name = buildDisplayName(); - if (name != m_nameItem->toPlainText()) - m_nameItem->setPlainText(name); + if (display_name != m_nameItem->toPlainText()) + m_nameItem->setPlainText(display_name); } - } else if (m_nameItem ){ + } else if (m_nameItem) { m_nameItem->scene()->removeItem(m_nameItem); delete m_nameItem; m_nameItem = nullptr; @@ -621,30 +674,45 @@ void ObjectItem::setObjectName(const QString &objectName) void ObjectItem::updateDepth() { - setZValue(m_object->depth()); + int depth_delta = 0; + switch (m_shapeIcon.depthLayer()) { + case StereotypeIcon::DepthBehindItems: + depth_delta = -1; + break; + case StereotypeIcon::DepthAmongItems: + depth_delta = 0; + break; + case StereotypeIcon::DepthBeforeItems: + depth_delta = 1; + break; + } + setZValue(m_object->depth() + depth_delta); } -void ObjectItem::updateSelectionMarker(CustomIconItem *customIconItem) +void ObjectItem::updateSelectionMarker(const CustomIconItem *customIconItem) { if (customIconItem) { - StereotypeIcon stereotypeIcon = customIconItem->stereotypeIcon(); ResizeFlags resizeFlags = ResizeUnlocked; - switch (stereotypeIcon.sizeLock()) { - case StereotypeIcon::LockNone: - resizeFlags = ResizeUnlocked; - break; - case StereotypeIcon::LockWidth: - resizeFlags = ResizeLockedWidth; - break; - case StereotypeIcon::LockHeight: - resizeFlags = ResizeLockedHeight; - break; - case StereotypeIcon::LockSize: - resizeFlags = ResizeLockedSize; - break; - case StereotypeIcon::LockRatio: + if (customIconItem->hasImage()) { resizeFlags = ResizeLockedRatio; - break; + } else { + switch (customIconItem->stereotypeIcon().sizeLock()) { + case StereotypeIcon::LockNone: + resizeFlags = ResizeUnlocked; + break; + case StereotypeIcon::LockWidth: + resizeFlags = ResizeLockedWidth; + break; + case StereotypeIcon::LockHeight: + resizeFlags = ResizeLockedHeight; + break; + case StereotypeIcon::LockSize: + resizeFlags = ResizeLockedSize; + break; + case StereotypeIcon::LockRatio: + resizeFlags = ResizeLockedRatio; + break; + } } updateSelectionMarker(resizeFlags); } else { @@ -990,6 +1058,10 @@ void ObjectItem::contextMenuEvent(QGraphicsSceneContextMenuEvent *event) addSeparator = true; if (element_tasks->extendContextMenu(object(), diagramSceneModel()->diagram(), &menu)) addSeparator = true; + if (element_tasks->hasLinkedFile(m_object, m_diagramSceneModel->diagram())) { + menu.addAction(new ContextMenuAction(Tr::tr("Open Linked File"), "openLinkedFile", &menu)); + addSeparator = true; + } if (addSeparator) menu.addSeparator(); menu.addAction(new ContextMenuAction(Tr::tr("Remove"), "remove", @@ -1000,12 +1072,12 @@ void ObjectItem::contextMenuEvent(QGraphicsSceneContextMenuEvent *event) QMenu alignMenu; alignMenu.setTitle(Tr::tr("Align Objects")); alignMenu.addAction(new ContextMenuAction(Tr::tr("Align Left"), "alignLeft", &alignMenu)); - alignMenu.addAction(new ContextMenuAction(Tr::tr("Center Vertically"), "centerVertically", + alignMenu.addAction(new ContextMenuAction(Tr::tr("Center Horizontally"), "centerHorizontally", &alignMenu)); alignMenu.addAction(new ContextMenuAction(Tr::tr("Align Right"), "alignRight", &alignMenu)); alignMenu.addSeparator(); alignMenu.addAction(new ContextMenuAction(Tr::tr("Align Top"), "alignTop", &alignMenu)); - alignMenu.addAction(new ContextMenuAction(Tr::tr("Center Horizontally"), "centerHorizontally", + alignMenu.addAction(new ContextMenuAction(Tr::tr("Center Vertically"), "centerVertically", &alignMenu)); alignMenu.addAction(new ContextMenuAction(Tr::tr("Align Bottom"), "alignBottom", &alignMenu)); alignMenu.addSeparator(); @@ -1022,7 +1094,6 @@ void ObjectItem::contextMenuEvent(QGraphicsSceneContextMenuEvent *event) layoutMenu.addAction(new ContextMenuAction(Tr::tr("Equal Vertical Space"), "sameVBorderDistance", &alignMenu)); layoutMenu.setEnabled(m_diagramSceneModel->hasMultiObjectsSelection()); menu.addMenu(&layoutMenu); - menu.addAction(new ContextMenuAction(Tr::tr("Add Related Elements"), "addRelatedElements", &menu)); QAction *selectedAction = menu.exec(event->screenPos()); if (selectedAction) { @@ -1035,6 +1106,8 @@ void ObjectItem::contextMenuEvent(QGraphicsSceneContextMenuEvent *event) m_diagramSceneModel->diagramSceneController()->elementTasks()->openDiagram(m_object, m_diagramSceneModel->diagram()); } else if (action->id() == "createDiagram") { m_diagramSceneModel->diagramSceneController()->elementTasks()->createAndOpenDiagram(m_object, m_diagramSceneModel->diagram()); + } else if (action->id() == "openLinkedFile") { + m_diagramSceneModel->diagramSceneController()->elementTasks()->openLinkedFile(m_object, m_diagramSceneModel->diagram()); } else if (action->id() == "remove") { DSelection selection = m_diagramSceneModel->selectedElements(); if (selection.isEmpty()) @@ -1073,11 +1146,6 @@ void ObjectItem::contextMenuEvent(QGraphicsSceneContextMenuEvent *event) align(IAlignable::AlignHeight, "height"); } else if (action->id() == "sameSize") { align(IAlignable::AlignSize, "size"); - } else if (action->id() == "addRelatedElements") { - DSelection selection = m_diagramSceneModel->selectedElements(); - if (selection.isEmpty()) - selection.append(m_object->uid(), m_diagramSceneModel->diagram()->uid()); - m_diagramSceneModel->diagramSceneController()->addRelatedElements(selection, m_diagramSceneModel->diagram()); } } } diff --git a/src/libs/modelinglib/qmt/diagram_scene/items/objectitem.h b/src/libs/modelinglib/qmt/diagram_scene/items/objectitem.h index e7acd5705c..254ef215ca 100644 --- a/src/libs/modelinglib/qmt/diagram_scene/items/objectitem.h +++ b/src/libs/modelinglib/qmt/diagram_scene/items/objectitem.h @@ -120,8 +120,12 @@ protected: StereotypeIcon::Display stereotypeDisplay, const Style *style); StereotypesItem *stereotypesItem() const { return m_stereotypesItem; } CustomIconItem *stereotypeIconItem() const { return m_stereotypeIcon; } - QSizeF stereotypeIconMinimumSize(const StereotypeIcon &stereotypeIcon, qreal minimumWidth, + CustomIconItem *customIconItem() const { return m_customIcon; } + QSizeF customIconItemMinimumSize(const CustomIconItem* customIconItem, qreal minimumWidth, qreal minimumHeight) const; + void correctAutoSize(const CustomIconItem *customIconItem, qreal& width, qreal& height, + qreal minimumWidth, qreal minimumHeight) const; + void updateCustomIcon(const Style* style); bool suppressTextDisplay() const; void updateNameItem(const Style *style); EditableTextItem *nameItem() const { return m_nameItem; } @@ -130,7 +134,7 @@ protected: void setObjectName(const QString &objectName); void updateDepth(); - void updateSelectionMarker(CustomIconItem *customIconItem); + void updateSelectionMarker(const CustomIconItem* customIconItem); void updateSelectionMarker(ResizeFlags resizeFlags); void updateSelectionMarkerGeometry(const QRectF &objectRect); void updateRelationStarter(); @@ -169,6 +173,7 @@ private: StereotypeIcon::Display m_stereotypeIconDisplay = StereotypeIcon::DisplayLabel; StereotypesItem *m_stereotypesItem = nullptr; CustomIconItem *m_stereotypeIcon = nullptr; + CustomIconItem *m_customIcon = nullptr; EditableTextItem *m_nameItem = nullptr; RectangularSelectionItem *m_selectionMarker = nullptr; RelationStarter *m_relationStarter = nullptr; diff --git a/src/libs/modelinglib/qmt/diagram_scene/items/packageitem.cpp b/src/libs/modelinglib/qmt/diagram_scene/items/packageitem.cpp index b473d17ec8..03e6d8d0f5 100644 --- a/src/libs/modelinglib/qmt/diagram_scene/items/packageitem.cpp +++ b/src/libs/modelinglib/qmt/diagram_scene/items/packageitem.cpp @@ -64,23 +64,10 @@ void PackageItem::update() const Style *style = adaptedStyle(stereotypeIconId()); - // custom icon - if (stereotypeIconDisplay() == StereotypeIcon::DisplayIcon) { - if (!m_customIcon) - m_customIcon = new CustomIconItem(diagramSceneModel(), this); - m_customIcon->setStereotypeIconId(stereotypeIconId()); - m_customIcon->setBaseSize(stereotypeIconMinimumSize(m_customIcon->stereotypeIcon(), CUSTOM_ICON_MINIMUM_AUTO_WIDTH, CUSTOM_ICON_MINIMUM_AUTO_HEIGHT)); - m_customIcon->setBrush(style->fillBrush()); - m_customIcon->setPen(style->outerLinePen()); - m_customIcon->setZValue(SHAPE_ZVALUE); - } else if (m_customIcon) { - m_customIcon->scene()->removeItem(m_customIcon); - delete m_customIcon; - m_customIcon = nullptr; - } + updateCustomIcon(style); // shape - if (!m_customIcon) { + if (!customIconItem()) { if (!m_shape) m_shape = new QGraphicsPolygonItem(this); m_shape->setBrush(style->fillBrush()); @@ -111,7 +98,7 @@ void PackageItem::update() m_contextLabel = nullptr; } - updateSelectionMarker(m_customIcon); + updateSelectionMarker(customIconItem()); updateRelationStarter(); updateAlignmentButtons(); updateGeometry(); @@ -119,8 +106,8 @@ void PackageItem::update() bool PackageItem::intersectShapeWithLine(const QLineF &line, QPointF *intersectionPoint, QLineF *intersectionLine) const { - if (m_customIcon) { - QList<QPolygonF> polygons = m_customIcon->outline(); + if (customIconItem()) { + QList<QPolygonF> polygons = customIconItem()->outline(); for (int i = 0; i < polygons.size(); ++i) polygons[i].translate(object()->pos() + object()->rect().topLeft()); if (shapeIcon().textAlignment() == qmt::StereotypeIcon::TextalignBelow) { @@ -169,8 +156,8 @@ PackageItem::ShapeGeometry PackageItem::calcMinimumGeometry() const double width = 0.0; double height = 0.0; - if (m_customIcon) { - QSizeF sz = stereotypeIconMinimumSize(m_customIcon->stereotypeIcon(), + if (customIconItem()) { + QSizeF sz = customIconItemMinimumSize(customIconItem(), CUSTOM_ICON_MINIMUM_AUTO_WIDTH, CUSTOM_ICON_MINIMUM_AUTO_HEIGHT); if (shapeIcon().textAlignment() != qmt::StereotypeIcon::TextalignTop && shapeIcon().textAlignment() != qmt::StereotypeIcon::TextalignCenter) @@ -244,12 +231,7 @@ void PackageItem::updateGeometry() // calc width and height if (object()->isAutoSized()) { - if (!m_customIcon) { - if (width < MINIMUM_AUTO_WIDTH) - width = MINIMUM_AUTO_WIDTH; - if (height < MINIMUM_AUTO_HEIGHT) - height = MINIMUM_AUTO_HEIGHT; - } + correctAutoSize(customIconItem(), width, height, MINIMUM_AUTO_WIDTH, MINIMUM_AUTO_HEIGHT); } else { QRectF rect = object()->rect(); if (rect.width() > width) @@ -273,9 +255,9 @@ void PackageItem::updateGeometry() // a backup for the graphics item used for manual resized and persistency. object()->setRect(rect); - if (m_customIcon) { - m_customIcon->setPos(left, top); - m_customIcon->setActualSize(QSizeF(width, height)); + if (customIconItem()) { + customIconItem()->setPos(left, top); + customIconItem()->setActualSize(QSizeF(width, height)); switch (shapeIcon().textAlignment()) { case qmt::StereotypeIcon::TextalignBelow: diff --git a/src/libs/modelinglib/qmt/diagram_scene/items/packageitem.h b/src/libs/modelinglib/qmt/diagram_scene/items/packageitem.h index 19c2fe54ab..f87af2fa35 100644 --- a/src/libs/modelinglib/qmt/diagram_scene/items/packageitem.h +++ b/src/libs/modelinglib/qmt/diagram_scene/items/packageitem.h @@ -40,7 +40,6 @@ private: ShapeGeometry calcMinimumGeometry() const; void updateGeometry(); - CustomIconItem *m_customIcon = nullptr; QGraphicsPolygonItem *m_shape = nullptr; ContextLabelItem *m_contextLabel = nullptr; }; diff --git a/src/libs/modelinglib/qmt/diagram_scene/items/relationitem.cpp b/src/libs/modelinglib/qmt/diagram_scene/items/relationitem.cpp index a2f444f59e..ca9dd8c3a2 100644 --- a/src/libs/modelinglib/qmt/diagram_scene/items/relationitem.cpp +++ b/src/libs/modelinglib/qmt/diagram_scene/items/relationitem.cpp @@ -380,7 +380,12 @@ void RelationItem::setHandlePos(int index, const QPointF &pos) } } -void RelationItem::dropHandle(int index, double rasterWidth, double rasterHeight) +static inline bool between(double v, double mid, double range) +{ + return v >= mid-range && v <= mid+range; +} + +void RelationItem::dropHandle(int index, bool extraSnap, double rasterWidth, double rasterHeight) { if (index == 0) { m_grabbedEndA = false; @@ -398,9 +403,40 @@ void RelationItem::dropHandle(int index, double rasterWidth, double rasterHeight QMT_ASSERT(index >= 0 && index < intermediatePoints.size(), return); QPointF pos = intermediatePoints.at(index).pos(); - double x = qRound(pos.x() / rasterWidth) * rasterWidth; - double y = qRound(pos.y() / rasterHeight) * rasterHeight; - intermediatePoints[index].setPos(QPointF(x, y)); + static constexpr int ANGLE = 20.0; + bool roundX = true; + bool roundY = true; + if (extraSnap) { + if (index >= 1) { + double angle = GeometryUtilities::calcAngle(QLineF(intermediatePoints.at(index).pos(), + intermediatePoints.at(index - 1).pos())); + if (between(qAbs(angle), 90, ANGLE)) { + pos.setX(intermediatePoints.at(index - 1).pos().x()); + roundX = false; + } + if (between(angle, 0, ANGLE) || between(qAbs(angle), 180, ANGLE)) { + pos.setY(intermediatePoints.at(index - 1).pos().y()); + roundY = false; + } + } + if (index < intermediatePoints.size() - 1) { + double angle = GeometryUtilities::calcAngle(QLineF(intermediatePoints.at(index).pos(), + intermediatePoints.at(index + 1).pos())); + if (between(qAbs(angle), 90, ANGLE)) { + pos.setX(intermediatePoints.at(index + 1).pos().x()); + roundX = false; + } + if (between(angle, 0, ANGLE) || between(qAbs(angle), 180, ANGLE)) { + pos.setY(intermediatePoints.at(index + 1).pos().y()); + roundY = false; + } + } + } + if (roundX) + pos.setX(qRound(pos.x() / rasterWidth) * rasterWidth); + if (roundY) + pos.setY(qRound(pos.y() / rasterHeight) * rasterHeight); + intermediatePoints[index].setPos(pos); m_diagramSceneModel->diagramController()->startUpdateElement(m_relation, m_diagramSceneModel->diagram(), DiagramController::UpdateMinor); m_relation->setIntermediatePoints(intermediatePoints); @@ -507,9 +543,11 @@ void RelationItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) const Style *RelationItem::adaptedStyle() { - DObject *endAObject = m_diagramSceneModel->diagramController()->findElement<DObject>(m_relation->endAUid(), m_diagramSceneModel->diagram()); - DObject *endBObject = m_diagramSceneModel->diagramController()->findElement<DObject>(m_relation->endBUid(), m_diagramSceneModel->diagram()); - StyledRelation styledRelation(m_relation, endAObject, endBObject); + const DObject *endAObject = m_diagramSceneModel->diagramController()->findElement<DObject>(m_relation->endAUid(), m_diagramSceneModel->diagram()); + const DObject *endBObject = m_diagramSceneModel->diagramController()->findElement<DObject>(m_relation->endBUid(), m_diagramSceneModel->diagram()); + const CustomRelation customRelation = m_diagramSceneModel->stereotypeController() + ->findCustomRelationByStereotype(m_relation->stereotypes().value(0)); + StyledRelation styledRelation(m_relation, endAObject, endBObject, &customRelation); return m_diagramSceneModel->styleController()->adaptRelationStyle(styledRelation); } diff --git a/src/libs/modelinglib/qmt/diagram_scene/items/relationitem.h b/src/libs/modelinglib/qmt/diagram_scene/items/relationitem.h index a43b8b2749..0980d232e4 100644 --- a/src/libs/modelinglib/qmt/diagram_scene/items/relationitem.h +++ b/src/libs/modelinglib/qmt/diagram_scene/items/relationitem.h @@ -56,7 +56,7 @@ public: void insertHandle(int beforeIndex, const QPointF &pos, double rasterWidth, double rasterHeight) override; void deleteHandle(int index) override; void setHandlePos(int index, const QPointF &pos) override; - void dropHandle(int index, double rasterWidth, double rasterHeight) override; + void dropHandle(int index, bool extraSnap, double rasterWidth, double rasterHeight) override; virtual void update(); diff --git a/src/libs/modelinglib/qmt/diagram_scene/items/stereotypedisplayvisitor.cpp b/src/libs/modelinglib/qmt/diagram_scene/items/stereotypedisplayvisitor.cpp index ad668f6bee..9313327c87 100644 --- a/src/libs/modelinglib/qmt/diagram_scene/items/stereotypedisplayvisitor.cpp +++ b/src/libs/modelinglib/qmt/diagram_scene/items/stereotypedisplayvisitor.cpp @@ -57,7 +57,9 @@ void StereotypeDisplayVisitor::visitDObject(const DObject *object) DObject::StereotypeDisplay stereotypeDisplay = object->stereotypeDisplay(); m_stereotypeIconId = m_stereotypeController->findStereotypeIconId(m_stereotypeIconElement, object->stereotypes()); - if (m_stereotypeIconId.isEmpty() && stereotypeDisplay == DObject::StereotypeIcon) { + if (object->hasImage() && stereotypeDisplay == DObject::StereotypeSmart) { + stereotypeDisplay = DObject::StereotypeLabel; + } else if (m_stereotypeIconId.isEmpty() && stereotypeDisplay == DObject::StereotypeIcon) { stereotypeDisplay = DObject::StereotypeLabel; } else if (!m_stereotypeIconId.isEmpty() && stereotypeDisplay == DObject::StereotypeSmart) { StereotypeIcon stereotypeIcon = m_stereotypeController->findStereotypeIcon(m_stereotypeIconId); diff --git a/src/libs/modelinglib/qmt/diagram_scene/parts/arrowitem.cpp b/src/libs/modelinglib/qmt/diagram_scene/parts/arrowitem.cpp index 3a14eb10d9..632da76f05 100644 --- a/src/libs/modelinglib/qmt/diagram_scene/parts/arrowitem.cpp +++ b/src/libs/modelinglib/qmt/diagram_scene/parts/arrowitem.cpp @@ -384,27 +384,29 @@ void ArrowItem::updateShaft(const Style *style) QMT_ASSERT(m_shaftItem, return); QPen pen(style->linePen()); + pen.setCapStyle(Qt::FlatCap); + + auto scale = [&pen](qreal v) { + return v / ((pen.widthF() - 1.0) / 2.0 + 1.0); + }; + switch (m_shaft) { case ShaftSolid: break; case ShaftDashed: - pen.setDashPattern(QVector<qreal>() - << (4.0 / pen.widthF()) << (4.0 / pen.widthF())); + pen.setDashPattern(QVector<qreal>() << scale(5.0) << scale(3.0)); break; case ShaftDot: - pen.setDashPattern(QVector<qreal>() - << (2.0 / pen.widthF()) << (2.0 / pen.widthF())); + pen.setDashPattern(QVector<qreal>() << scale(3.0) << scale(3.0)); break; case ShaftDashDot: - pen.setDashPattern(QVector<qreal>() - << (4.0 / pen.widthF()) << (2.0 / pen.widthF()) - << (2.0 / pen.widthF()) << (2.0 / pen.widthF())); + pen.setDashPattern(QVector<qreal>() << scale(5.0) << scale(3.0) + << scale(3.0) << scale(3.0)); break; case ShaftDashDotDot: - pen.setDashPattern(QVector<qreal>() - << (4.0 / pen.widthF()) << (2.0 / pen.widthF()) - << (2.0 / pen.widthF()) << (2.0 / pen.widthF()) - << (2.0 / pen.widthF()) << (2.0 / pen.widthF())); + pen.setDashPattern(QVector<qreal>() << scale(5.0) << scale(3.0) + << scale(3.0) << scale(3.0) + << scale(3.0) << scale(3.0)); break; } m_shaftItem->setPen(pen); diff --git a/src/libs/modelinglib/qmt/diagram_scene/parts/customiconitem.cpp b/src/libs/modelinglib/qmt/diagram_scene/parts/customiconitem.cpp index 066f9b7875..36e4c4f789 100644 --- a/src/libs/modelinglib/qmt/diagram_scene/parts/customiconitem.cpp +++ b/src/libs/modelinglib/qmt/diagram_scene/parts/customiconitem.cpp @@ -57,55 +57,97 @@ void CustomIconItem::setPen(const QPen &pen) m_pen = pen; } +void CustomIconItem::setImage(const QImage &image) +{ + m_image = image; +} + double CustomIconItem::shapeWidth() const { - return m_stereotypeIcon.width(); + if (!m_image.isNull()) + return m_image.width(); + return m_stereotypeIcon.hasIconWidth() ? m_stereotypeIcon.iconWidth() + : m_stereotypeIcon.width(); } double CustomIconItem::shapeHeight() const { - return m_stereotypeIcon.height(); + if (!m_image.isNull()) + return m_image.height(); + return m_stereotypeIcon.hasIconHeight() ? m_stereotypeIcon.iconHeight() + : m_stereotypeIcon.height(); } QRectF CustomIconItem::boundingRect() const { - ShapeSizeVisitor visitor(QPointF(0.0, 0.0), QSizeF(m_stereotypeIcon.width(), m_stereotypeIcon.height()), m_baseSize, m_actualSize); + if (!m_image.isNull()) + return QRectF(QPointF(0.0, 0.0), m_actualSize) | childrenBoundingRect(); + ShapeSizeVisitor visitor(QPointF(0.0, 0.0), + QSizeF(m_stereotypeIcon.width(), m_stereotypeIcon.height()), + m_baseSize, + m_actualSize); m_stereotypeIcon.iconShape().visitShapes(&visitor); return visitor.boundingRect() | childrenBoundingRect(); } -void CustomIconItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) +void CustomIconItem::paint(QPainter *painter, + const QStyleOptionGraphicsItem *option, + QWidget *widget) { Q_UNUSED(option) Q_UNUSED(widget) painter->save(); - painter->setBrush(m_brush); - painter->setPen(m_pen); + if (!m_image.isNull()) { + painter->drawImage(QRectF(QPointF(0.0, 0.0), m_actualSize), m_image); + } else { + painter->setBrush(m_brush); + painter->setPen(m_pen); #ifdef DEBUG_OUTLINE - ShapePolygonVisitor visitor(QPointF(0.0, 0.0), QSizeF(m_stereotypeIcon.width(), m_stereotypeIcon.height()), m_baseSize, m_actualSize); - IconShape shape = m_stereotypeIcon.outlineShape(); - if (shape.isEmpty()) - shape = m_stereotypeIcon.iconShape(); - shape.visitShapes(&visitor); - painter->drawPath(visitor.path()); - ShapePaintVisitor visitor1(painter, QPointF(0.0, 0.0), QSizeF(m_stereotypeIcon.width(), m_stereotypeIcon.height()), m_baseSize, m_actualSize); - m_stereotypeIcon.iconShape().visitShapes(&visitor1); + ShapePolygonVisitor visitor(QPointF(0.0, 0.0), + QSizeF(m_stereotypeIcon.width(), m_stereotypeIcon.height()), + m_baseSize, + m_actualSize); + IconShape shape = m_stereotypeIcon.outlineShape(); + if (shape.isEmpty()) + shape = m_stereotypeIcon.iconShape(); + shape.visitShapes(&visitor); + painter->drawPath(visitor.path()); + ShapePaintVisitor visitor1(painter, + QPointF(0.0, 0.0), + QSizeF(m_stereotypeIcon.width(), m_stereotypeIcon.height()), + m_baseSize, + m_actualSize); + m_stereotypeIcon.iconShape().visitShapes(&visitor1); #else - ShapePaintVisitor visitor(painter, QPointF(0.0, 0.0), QSizeF(m_stereotypeIcon.width(), m_stereotypeIcon.height()), m_baseSize, m_actualSize); - m_stereotypeIcon.iconShape().visitShapes(&visitor); + ShapePaintVisitor visitor(painter, + QPointF(0.0, 0.0), + QSizeF(m_stereotypeIcon.width(), m_stereotypeIcon.height()), + m_baseSize, + m_actualSize); + m_stereotypeIcon.iconShape().visitShapes(&visitor); #endif + } painter->restore(); } QList<QPolygonF> CustomIconItem::outline() const { - ShapePolygonVisitor visitor(QPointF(0.0, 0.0), QSizeF(m_stereotypeIcon.width(), m_stereotypeIcon.height()), m_baseSize, m_actualSize); - IconShape shape = m_stereotypeIcon.outlineShape(); - if (shape.isEmpty()) - shape = m_stereotypeIcon.iconShape(); - shape.visitShapes(&visitor); - return visitor.toPolygons(); + if (!m_image.isNull()) { + QList<QPolygonF> polygons; + polygons.append(QPolygonF(QRectF(QPointF(0.0, 0.0), m_actualSize))); + return polygons; + } else { + ShapePolygonVisitor visitor(QPointF(0.0, 0.0), + QSizeF(m_stereotypeIcon.width(), m_stereotypeIcon.height()), + m_baseSize, + m_actualSize); + IconShape shape = m_stereotypeIcon.outlineShape(); + if (shape.isEmpty()) + shape = m_stereotypeIcon.iconShape(); + shape.visitShapes(&visitor); + return visitor.toPolygons(); + } } } // namespace qmt diff --git a/src/libs/modelinglib/qmt/diagram_scene/parts/customiconitem.h b/src/libs/modelinglib/qmt/diagram_scene/parts/customiconitem.h index 7f85e87516..f61db552ac 100644 --- a/src/libs/modelinglib/qmt/diagram_scene/parts/customiconitem.h +++ b/src/libs/modelinglib/qmt/diagram_scene/parts/customiconitem.h @@ -9,6 +9,7 @@ #include "qmt/stereotype/stereotypeicon.h" #include <QBrush> +#include <QImage> #include <QPen> namespace qmt { @@ -26,6 +27,9 @@ public: void setActualSize(const QSizeF &actualSize); void setBrush(const QBrush &brush); void setPen(const QPen &pen); + QImage image() const { return m_image; } + bool hasImage() const { return !m_image.isNull(); } + void setImage(const QImage &image); StereotypeIcon stereotypeIcon() const { return m_stereotypeIcon; } double shapeWidth() const; double shapeHeight() const; @@ -43,6 +47,7 @@ private: QSizeF m_actualSize; QBrush m_brush; QPen m_pen; + QImage m_image; }; } // namespace qmt diff --git a/src/libs/modelinglib/qmt/diagram_scene/parts/editabletextitem.cpp b/src/libs/modelinglib/qmt/diagram_scene/parts/editabletextitem.cpp index 60275cb5ec..185321d84c 100644 --- a/src/libs/modelinglib/qmt/diagram_scene/parts/editabletextitem.cpp +++ b/src/libs/modelinglib/qmt/diagram_scene/parts/editabletextitem.cpp @@ -13,13 +13,18 @@ namespace qmt { EditableTextItem::EditableTextItem(QGraphicsItem *parent) : QGraphicsTextItem(parent) { - setTextInteractionFlags(Qt::TextEditorInteraction); + setTextInteractionFlags(Qt::NoTextInteraction); } EditableTextItem::~EditableTextItem() { } +void EditableTextItem::setEditable(bool editable) +{ + setTextInteractionFlags(editable ? Qt::TextEditorInteraction : Qt::NoTextInteraction); +} + void EditableTextItem::setShowFocus(bool showFocus) { m_showFocus = showFocus; @@ -72,13 +77,6 @@ void EditableTextItem::keyReleaseEvent(QKeyEvent *event) QGraphicsTextItem::keyReleaseEvent(event); } -void EditableTextItem::mousePressEvent(QGraphicsSceneMouseEvent *event) -{ - scene()->clearSelection(); - parentItem()->setSelected(true); - QGraphicsTextItem::mousePressEvent(event); -} - void EditableTextItem::focusOutEvent(QFocusEvent *event) { QGraphicsTextItem::focusOutEvent(event); diff --git a/src/libs/modelinglib/qmt/diagram_scene/parts/editabletextitem.h b/src/libs/modelinglib/qmt/diagram_scene/parts/editabletextitem.h index 7c361b72fd..b18bf85cfc 100644 --- a/src/libs/modelinglib/qmt/diagram_scene/parts/editabletextitem.h +++ b/src/libs/modelinglib/qmt/diagram_scene/parts/editabletextitem.h @@ -19,6 +19,7 @@ signals: void returnKeyPressed(); public: + void setEditable(bool editable); void setShowFocus(bool showFocus); void setFilterReturnKey(bool filterReturnKey); void setFilterTabKey(bool filterTabKey); @@ -30,7 +31,6 @@ public: protected: void keyPressEvent(QKeyEvent *event) override; void keyReleaseEvent(QKeyEvent *event) override; - void mousePressEvent(QGraphicsSceneMouseEvent *event) override; void focusOutEvent(QFocusEvent *event) override; private: diff --git a/src/libs/modelinglib/qmt/diagram_scene/parts/pathselectionitem.cpp b/src/libs/modelinglib/qmt/diagram_scene/parts/pathselectionitem.cpp index 8206a7fe9a..231b78a3a0 100644 --- a/src/libs/modelinglib/qmt/diagram_scene/parts/pathselectionitem.cpp +++ b/src/libs/modelinglib/qmt/diagram_scene/parts/pathselectionitem.cpp @@ -79,7 +79,10 @@ protected: { m_lastPos = event->scenePos(); QPointF delta = m_lastPos - m_startPos; - m_owner->moveHandle(m_pointIndex, delta, Release, m_qualifier); + HandleQualifier qualifier = m_qualifier; + if (qualifier == None && (event->modifiers() & Qt::ShiftModifier) != 0) + qualifier = SnapHandle; + m_owner->moveHandle(m_pointIndex, delta, Release, qualifier); clearFocus(); } @@ -267,6 +270,7 @@ void PathSelectionItem::moveHandle(int pointIndex, const QPointF &deltaMove, Han { switch (handleQualifier) { case None: + case SnapHandle: { if (handleStatus == Press) { m_focusHandleItem = m_handles.at(pointIndex); @@ -275,13 +279,13 @@ void PathSelectionItem::moveHandle(int pointIndex, const QPointF &deltaMove, Han QPointF newPos = m_originalHandlePos + deltaMove; m_windable->setHandlePos(pointIndex, newPos); if (handleStatus == Release) { - m_windable->dropHandle(pointIndex, RASTER_WIDTH, RASTER_HEIGHT); + m_windable->dropHandle(pointIndex, handleQualifier == SnapHandle, RASTER_WIDTH, RASTER_HEIGHT); m_focusHandleItem = nullptr; } break; } case DeleteHandle: - if (handleStatus == Press) + if (handleStatus == Release) m_windable->deleteHandle(pointIndex); break; } diff --git a/src/libs/modelinglib/qmt/diagram_scene/parts/pathselectionitem.h b/src/libs/modelinglib/qmt/diagram_scene/parts/pathselectionitem.h index 0c4891948a..06a2d36c49 100644 --- a/src/libs/modelinglib/qmt/diagram_scene/parts/pathselectionitem.h +++ b/src/libs/modelinglib/qmt/diagram_scene/parts/pathselectionitem.h @@ -25,7 +25,8 @@ class PathSelectionItem : public QGraphicsItem enum HandleQualifier { None, - DeleteHandle + DeleteHandle, + SnapHandle }; public: diff --git a/src/libs/modelinglib/qmt/diagram_scene/parts/relationstarter.cpp b/src/libs/modelinglib/qmt/diagram_scene/parts/relationstarter.cpp index 26b8ebc1c4..49bca85162 100644 --- a/src/libs/modelinglib/qmt/diagram_scene/parts/relationstarter.cpp +++ b/src/libs/modelinglib/qmt/diagram_scene/parts/relationstarter.cpp @@ -121,11 +121,13 @@ void RelationStarter::keyPressEvent(QKeyEvent *event) m_currentPreviewArrowIntermediatePoints.append(p); // Do not update the preview arrow here because last two points are now identical which looks wired } + event->accept(); } else if (event->key() == Qt::Key_Control) { if (!m_currentPreviewArrowIntermediatePoints.isEmpty()) { m_currentPreviewArrowIntermediatePoints.removeLast(); updateCurrentPreviewArrow(m_currentPreviewArrow->lastLineSegment().p1()); } + event->accept(); } } diff --git a/src/libs/modelinglib/qmt/diagram_widgets_ui/diagramview.cpp b/src/libs/modelinglib/qmt/diagram_widgets_ui/diagramview.cpp index 5634c4eb7f..555dc812e6 100644 --- a/src/libs/modelinglib/qmt/diagram_widgets_ui/diagramview.cpp +++ b/src/libs/modelinglib/qmt/diagram_widgets_ui/diagramview.cpp @@ -14,9 +14,14 @@ #include <QDragLeaveEvent> #include <QDropEvent> #include <QMimeData> +#include <QScrollBar> namespace qmt { +namespace { +const qreal ADJUSTMENT = 80; +}; + DiagramView::DiagramView(QWidget *parent) : QGraphicsView(parent) { @@ -25,6 +30,9 @@ DiagramView::DiagramView(QWidget *parent) setDragMode(QGraphicsView::RubberBandDrag); setFrameShape(QFrame::NoFrame); setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform); + m_panTimer.setInterval(200); + m_panTimer.setSingleShot(false); + connect(&m_panTimer, &QTimer::timeout, this, &DiagramView::onPanTimeout); } DiagramView::~DiagramView() @@ -104,7 +112,7 @@ void DiagramView::dropEvent(QDropEvent *event) if (diagramSceneController->isAddingAllowed(Uid(QUuid(key)), m_diagramSceneModel->diagram())) { diagramSceneController->addExistingModelElement(Uid(QUuid(key)), - mapToScene(event->pos()), + mapToScene(event->position().toPoint()), m_diagramSceneModel->diagram()); } } @@ -118,10 +126,10 @@ void DiagramView::dropEvent(QDropEvent *event) QString stereotype; dataStream >> newElementId >> name >> stereotype; if (!newElementId.isEmpty()) { - QPointF pos = mapToScene(event->pos()); + QPointF pos = mapToScene(event->position().toPoint()); diagramSceneController->dropNewElement( newElementId, name, stereotype, m_diagramSceneModel->findTopmostElement(pos), - pos, m_diagramSceneModel->diagram(), event->pos(), size()); + pos, m_diagramSceneModel->diagram(), event->position().toPoint(), size()); } } event->accept(); @@ -130,12 +138,41 @@ void DiagramView::dropEvent(QDropEvent *event) } } +void DiagramView::mousePressEvent(QMouseEvent *event) +{ + m_panTimer.start(); + QGraphicsView::mousePressEvent(event); +} + +void DiagramView::mouseReleaseEvent(QMouseEvent *event) +{ + m_panTimer.stop(); + QGraphicsView::mouseReleaseEvent(event); +} + +void DiagramView::mouseMoveEvent(QMouseEvent *event) +{ + QGraphicsView::mouseMoveEvent(event); + m_lastMouse = event->pos(); +} + void DiagramView::onSceneRectChanged(const QRectF &sceneRect) { // add some adjustment to all 4 sides - static const qreal ADJUSTMENT = 80; QRectF rect = sceneRect.adjusted(-ADJUSTMENT, -ADJUSTMENT, ADJUSTMENT, ADJUSTMENT); setSceneRect(rect); } +void DiagramView::onPanTimeout() +{ + if (m_lastMouse.x() < ADJUSTMENT) + horizontalScrollBar()->triggerAction(QScrollBar::SliderSingleStepSub); + else if (m_lastMouse.x() > viewport()->size().width() - ADJUSTMENT) + horizontalScrollBar()->triggerAction(QScrollBar::SliderSingleStepAdd); + if (m_lastMouse.y() < ADJUSTMENT) + verticalScrollBar()->triggerAction(QScrollBar::SliderSingleStepSub); + else if (m_lastMouse.y() > viewport()->size().height() - ADJUSTMENT) + verticalScrollBar()->triggerAction(QScrollBar::SliderSingleStepAdd); +} + } // namespace qmt diff --git a/src/libs/modelinglib/qmt/diagram_widgets_ui/diagramview.h b/src/libs/modelinglib/qmt/diagram_widgets_ui/diagramview.h index 41386f09bd..4eee60ff44 100644 --- a/src/libs/modelinglib/qmt/diagram_widgets_ui/diagramview.h +++ b/src/libs/modelinglib/qmt/diagram_widgets_ui/diagramview.h @@ -7,6 +7,7 @@ #include "qmt/infrastructure/qmt_global.h" #include <QPointer> +#include <QTimer> namespace qmt { @@ -27,11 +28,17 @@ protected: void dragLeaveEvent(QDragLeaveEvent *event) override; void dragMoveEvent(QDragMoveEvent *event) override; void dropEvent(QDropEvent *event) override; + void mousePressEvent(QMouseEvent *event) override; + void mouseReleaseEvent(QMouseEvent *event) override; + void mouseMoveEvent(QMouseEvent *event) override; private: void onSceneRectChanged(const QRectF &sceneRect); + void onPanTimeout(); QPointer<DiagramSceneModel> m_diagramSceneModel; + QPointF m_lastMouse; + QTimer m_panTimer; }; } // namespace qmt diff --git a/src/libs/modelinglib/qmt/document_controller/documentcontroller.cpp b/src/libs/modelinglib/qmt/document_controller/documentcontroller.cpp index 68184d2fb5..696fc30558 100644 --- a/src/libs/modelinglib/qmt/document_controller/documentcontroller.cpp +++ b/src/libs/modelinglib/qmt/document_controller/documentcontroller.cpp @@ -32,7 +32,7 @@ #include "../../modelinglibtr.h" -#include <QFileInfo> +using Utils::FilePath; namespace qmt { @@ -233,7 +233,7 @@ MDiagram *DocumentController::findOrCreateRootDiagram() return rootDiagram; } -void DocumentController::createNewProject(const QString &fileName) +void DocumentController::createNewProject(const FilePath &fileName) { m_diagramsManager->removeAllDiagrams(); m_treeModel->setModelController(nullptr); @@ -246,7 +246,7 @@ void DocumentController::createNewProject(const QString &fileName) m_modelController->setRootPackage(m_projectController->project()->rootPackage()); } -void DocumentController::loadProject(const QString &fileName) +void DocumentController::loadProject(const FilePath &fileName) { m_diagramsManager->removeAllDiagrams(); m_treeModel->setModelController(nullptr); diff --git a/src/libs/modelinglib/qmt/document_controller/documentcontroller.h b/src/libs/modelinglib/qmt/document_controller/documentcontroller.h index 8053d189e0..9c5188e3f7 100644 --- a/src/libs/modelinglib/qmt/document_controller/documentcontroller.h +++ b/src/libs/modelinglib/qmt/document_controller/documentcontroller.h @@ -7,6 +7,8 @@ #include "qmt/infrastructure/qmt_global.h" #include "qmt/model_controller/modelcontroller.h" +#include <utils/filepath.h> + namespace qmt { class ProjectController; @@ -77,8 +79,8 @@ public: MDiagram *findRootDiagram(); MDiagram *findOrCreateRootDiagram(); - void createNewProject(const QString &fileName); - void loadProject(const QString &fileName); + void createNewProject(const Utils::FilePath &fileName); + void loadProject(const Utils::FilePath &fileName); private: ProjectController *m_projectController = nullptr; diff --git a/src/libs/modelinglib/qmt/infrastructure/ioexceptions.cpp b/src/libs/modelinglib/qmt/infrastructure/ioexceptions.cpp index 4af8df9236..bb11a4d566 100644 --- a/src/libs/modelinglib/qmt/infrastructure/ioexceptions.cpp +++ b/src/libs/modelinglib/qmt/infrastructure/ioexceptions.cpp @@ -7,6 +7,8 @@ #include <QObject> +using Utils::FilePath; + namespace qmt { IOException::IOException(const QString &errorMsg) @@ -14,39 +16,39 @@ IOException::IOException(const QString &errorMsg) { } -FileIOException::FileIOException(const QString &errorMsg, const QString &fileName, int lineNumber) +FileIOException::FileIOException(const QString &errorMsg, const FilePath &fileName, int lineNumber) : IOException(errorMsg), m_fileName(fileName), m_lineNumber(lineNumber) { } -FileNotFoundException::FileNotFoundException(const QString &fileName) +FileNotFoundException::FileNotFoundException(const FilePath &fileName) : FileIOException(Tr::tr("File not found."), fileName) { } -FileCreationException::FileCreationException(const QString &fileName) +FileCreationException::FileCreationException(const FilePath &fileName) : FileIOException(Tr::tr("Unable to create file."), fileName) { } -FileWriteError::FileWriteError(const QString &fileName, int lineNumber) +FileWriteError::FileWriteError(const FilePath &fileName, int lineNumber) : FileIOException(Tr::tr("Writing to file failed."), fileName, lineNumber) { } -FileReadError::FileReadError(const QString &fileName, int lineNumber) +FileReadError::FileReadError(const FilePath &fileName, int lineNumber) : FileIOException(Tr::tr("Reading from file failed."), fileName, lineNumber) { } -IllegalXmlFile::IllegalXmlFile(const QString &fileName, int lineNumber) +IllegalXmlFile::IllegalXmlFile(const FilePath &fileName, int lineNumber) : FileIOException(Tr::tr("Illegal XML file."), fileName, lineNumber) { } -UnknownFileVersion::UnknownFileVersion(int version, const QString &fileName, int lineNumber) +UnknownFileVersion::UnknownFileVersion(int version, const FilePath &fileName, int lineNumber) : FileIOException(Tr::tr("Unable to handle file version %1.") .arg(version), fileName, lineNumber) { diff --git a/src/libs/modelinglib/qmt/infrastructure/ioexceptions.h b/src/libs/modelinglib/qmt/infrastructure/ioexceptions.h index aece890529..7dc91ff4ba 100644 --- a/src/libs/modelinglib/qmt/infrastructure/ioexceptions.h +++ b/src/libs/modelinglib/qmt/infrastructure/ioexceptions.h @@ -5,6 +5,8 @@ #include "exceptions.h" +#include <utils/filepath.h> + namespace qmt { class IOException : public Exception @@ -16,51 +18,51 @@ public: class FileIOException : public IOException { public: - explicit FileIOException(const QString &errorMsg, const QString &fileName = QString(), + explicit FileIOException(const QString &errorMsg, const Utils::FilePath &fileName = {}, int lineNumber = -1); - QString fileName() const { return m_fileName; } + Utils::FilePath fileName() const { return m_fileName; } int lineNumber() const { return m_lineNumber; } private: - QString m_fileName; + Utils::FilePath m_fileName; int m_lineNumber = -1; }; class FileNotFoundException : public FileIOException { public: - explicit FileNotFoundException(const QString &fileName); + explicit FileNotFoundException(const Utils::FilePath &fileName); }; class FileCreationException : public FileIOException { public: - explicit FileCreationException(const QString &fileName); + explicit FileCreationException(const Utils::FilePath &fileName); }; class FileWriteError : public FileIOException { public: - explicit FileWriteError(const QString &fileName, int lineNumber = -1); + explicit FileWriteError(const Utils::FilePath &fileName, int lineNumber = -1); }; class FileReadError : public FileIOException { public: - explicit FileReadError(const QString &fileName, int lineNumber = -1); + explicit FileReadError(const Utils::FilePath &fileName, int lineNumber = -1); }; class IllegalXmlFile : public FileIOException { public: - explicit IllegalXmlFile(const QString &fileName, int lineNumber = -1); + explicit IllegalXmlFile(const Utils::FilePath &fileName, int lineNumber = -1); }; class UnknownFileVersion : public FileIOException { public: - UnknownFileVersion(int version, const QString &fileName, int lineNumber = -1); + UnknownFileVersion(int version, const Utils::FilePath &fileName, int lineNumber = -1); }; } // namespace qmt diff --git a/src/libs/modelinglib/qmt/model/mdiagram.h b/src/libs/modelinglib/qmt/model/mdiagram.h index 8f327daabc..bebe17cb50 100644 --- a/src/libs/modelinglib/qmt/model/mdiagram.h +++ b/src/libs/modelinglib/qmt/model/mdiagram.h @@ -21,7 +21,7 @@ public: MDiagram &operator=(const MDiagram &rhs); - const QList<DElement *> &diagramElements() const { return m_elements; } + const QList<DElement *> diagramElements() const { return m_elements; } DElement *findDiagramElement(const Uid &key) const; DElement *findDelegate(const Uid &modelUid) const; void setDiagramElements(const QList<DElement *> &elements); diff --git a/src/libs/modelinglib/qmt/model/mobject.cpp b/src/libs/modelinglib/qmt/model/mobject.cpp index 734be13567..f5b4e2b36f 100644 --- a/src/libs/modelinglib/qmt/model/mobject.cpp +++ b/src/libs/modelinglib/qmt/model/mobject.cpp @@ -8,6 +8,8 @@ #include "mvisitor.h" #include "mconstvisitor.h" +using Utils::FilePath; + namespace qmt { MObject::MObject() @@ -20,6 +22,7 @@ MObject::MObject() MObject::MObject(const MObject &rhs) : MElement(rhs), m_name(rhs.m_name), + m_linkedfilename(rhs.m_linkedfilename), m_children(true), m_relations(true) { @@ -34,6 +37,7 @@ MObject &MObject::operator =(const MObject &rhs) if (this != &rhs) { MElement::operator=(rhs); m_name = rhs.m_name; + m_linkedfilename = rhs.m_linkedfilename; // no deep copy; list of children remains unchanged } return *this; @@ -44,6 +48,11 @@ void MObject::setName(const QString &name) m_name = name; } +void MObject::setLinkedFileName(const FilePath &linkedfilename) +{ + m_linkedfilename = linkedfilename; +} + void MObject::setChildren(const Handles<MObject> &children) { m_children = children; diff --git a/src/libs/modelinglib/qmt/model/mobject.h b/src/libs/modelinglib/qmt/model/mobject.h index 1dc95887d9..29d10da4ba 100644 --- a/src/libs/modelinglib/qmt/model/mobject.h +++ b/src/libs/modelinglib/qmt/model/mobject.h @@ -6,6 +6,8 @@ #include "melement.h" #include "qmt/infrastructure/handles.h" +#include <utils/filepath.h> + #include <QString> namespace qmt { @@ -23,6 +25,8 @@ public: QString name() const { return m_name; } void setName(const QString &name); + Utils::FilePath linkedFileName() const { return m_linkedfilename; } + void setLinkedFileName(const Utils::FilePath &linkedfilename); const Handles<MObject> &children() const { return m_children; } void setChildren(const Handles<MObject> &children); @@ -48,6 +52,7 @@ public: private: QString m_name; + Utils::FilePath m_linkedfilename; Handles<MObject> m_children; Handles<MRelation> m_relations; }; diff --git a/src/libs/modelinglib/qmt/model_controller/mflatassignmentvisitor.cpp b/src/libs/modelinglib/qmt/model_controller/mflatassignmentvisitor.cpp index deb458e4bc..66cfb4576f 100644 --- a/src/libs/modelinglib/qmt/model_controller/mflatassignmentvisitor.cpp +++ b/src/libs/modelinglib/qmt/model_controller/mflatassignmentvisitor.cpp @@ -35,6 +35,7 @@ void MFlatAssignmentVisitor::visitMObject(const MObject *object) auto targetObject = dynamic_cast<MObject *>(m_target); QMT_ASSERT(targetObject, return); targetObject->setName(object->name()); + targetObject->setLinkedFileName(object->linkedFileName()); } void MFlatAssignmentVisitor::visitMPackage(const MPackage *package) diff --git a/src/libs/modelinglib/qmt/model_ui/modeltreefilterdata.cpp b/src/libs/modelinglib/qmt/model_ui/modeltreefilterdata.cpp new file mode 100644 index 0000000000..ba411690f5 --- /dev/null +++ b/src/libs/modelinglib/qmt/model_ui/modeltreefilterdata.cpp @@ -0,0 +1,43 @@ +// Copyright (C) 2018 Jochen Becher +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include "modeltreefilterdata.h" + +namespace qmt { + +void ModelTreeViewData::setShowRelations(bool newShowRelations) +{ + m_showRelations = newShowRelations; +} + +void ModelTreeViewData::setShowDiagramElements(bool newShowDiagramElements) +{ + m_showDiagramElements = newShowDiagramElements; +} + +void ModelTreeFilterData::setType(Type newType) +{ + m_type = newType; +} + +void ModelTreeFilterData::setCustomId(const QString &newCustomId) +{ + m_customId = newCustomId; +} + +void ModelTreeFilterData::setStereotypes(const QStringList &newStereotypes) +{ + m_stereotypes = newStereotypes; +} + +void ModelTreeFilterData::setName(const QString &newName) +{ + m_name = newName; +} + +void ModelTreeFilterData::setDirection(Direction newDirection) +{ + m_direction = newDirection; +} + +} // namespace qmt diff --git a/src/libs/modelinglib/qmt/model_ui/modeltreefilterdata.h b/src/libs/modelinglib/qmt/model_ui/modeltreefilterdata.h new file mode 100644 index 0000000000..3af018fefd --- /dev/null +++ b/src/libs/modelinglib/qmt/model_ui/modeltreefilterdata.h @@ -0,0 +1,72 @@ +// Copyright (C) 2018 Jochen Becher +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#pragma once + +#include <QString> +#include <QStringList> + +namespace qmt { + +class ModelTreeViewData +{ +public: + + bool showRelations() const { return m_showRelations; } + void setShowRelations(bool newShowRelations); + + bool showDiagramElements() const { return m_showDiagramElements; } + void setShowDiagramElements(bool newShowDiagramElements); + +private: + bool m_showRelations = true; + bool m_showDiagramElements = false; +}; + +class ModelTreeFilterData +{ +public: + enum class Type { + Any, + Package, + Component, + Class, + Diagram, + Item, + Dependency, + Association, + Inheritance, + Connection + }; + + enum class Direction { + Any, + Outgoing, + Incoming, + Bidirectional + }; + + Type type() const { return m_type; } + void setType(Type newType); + + QString customId() const { return m_customId; } + void setCustomId(const QString &newCustomId); + + QStringList stereotypes() const { return m_stereotypes; } + void setStereotypes(const QStringList &newStereotypes); + + QString name() const { return m_name; } + void setName(const QString &newName); + + Direction direction() const { return m_direction; } + void setDirection(Direction newDirection); + +private: + Type m_type = Type::Any; + QString m_customId; + QStringList m_stereotypes; + QString m_name; + Direction m_direction = Direction::Any; +}; + +} // namespace qmt diff --git a/src/libs/modelinglib/qmt/model_ui/sortedtreemodel.cpp b/src/libs/modelinglib/qmt/model_ui/sortedtreemodel.cpp index 972c2f9c00..e69e1635a7 100644 --- a/src/libs/modelinglib/qmt/model_ui/sortedtreemodel.cpp +++ b/src/libs/modelinglib/qmt/model_ui/sortedtreemodel.cpp @@ -3,14 +3,203 @@ #include "sortedtreemodel.h" +#include "qmt/model/massociation.h" +#include "qmt/model/mcanvasdiagram.h" +#include "qmt/model/mclass.h" +#include "qmt/model/mcomponent.h" +#include "qmt/model/mconnection.h" +#include "qmt/model/mdependency.h" +#include "qmt/model/minheritance.h" +#include "qmt/model/mitem.h" +#include "qmt/model/mpackage.h" +#include "qmt/model/mconstvisitor.h" #include "treemodel.h" namespace qmt { +namespace { + +class Filter : public MConstVisitor { +public: + + void setModelTreeViewData(const ModelTreeViewData *viewData) + { + m_viewData = viewData; + } + + void setModelTreeFilterData(const ModelTreeFilterData *filterData) + { + m_filterData = filterData; + } + + bool keep() const { return m_keep; } + + void visitMElement(const MElement *element) override + { + if (!m_filterData->stereotypes().isEmpty()) { + const QStringList stereotypes = element->stereotypes(); + bool containsElementStereotype = std::any_of( + stereotypes.constBegin(), stereotypes.constEnd(), + [&](const QString &s) { return m_filterData->stereotypes().contains(s); }); + if (!containsElementStereotype) { + m_keep = false; + return; + } + } + } + + void visitMObject(const MObject *object) override + { + if (!m_filterData->name().isEmpty() && m_filterData->name() != object->name()) + m_keep = false; + else + visitMElement(object); + } + + void visitMPackage(const MPackage *package) override + { + if (m_filterData->type() != ModelTreeFilterData::Type::Any + && m_filterData->type() != ModelTreeFilterData::Type::Package) + { + m_keep = false; + } else { + visitMObject(package); + } + } + + void visitMClass(const MClass *klass) override + { + if (m_filterData->type() != ModelTreeFilterData::Type::Any + && m_filterData->type() != ModelTreeFilterData::Type::Class) + { + m_keep = false; + } else { + visitMObject(klass); + } + } + + void visitMComponent(const MComponent *component) override + { + if (m_filterData->type() != ModelTreeFilterData::Type::Any + && m_filterData->type() != ModelTreeFilterData::Type::Component) + { + m_keep = false; + } else { + visitMObject(component); + } + } + + void visitMDiagram(const MDiagram *diagram) override + { + if (m_filterData->type() != ModelTreeFilterData::Type::Any + && m_filterData->type() != ModelTreeFilterData::Type::Diagram) + { + m_keep = false; + } else { + visitMObject(diagram); + } + } + + void visitMCanvasDiagram(const MCanvasDiagram *diagram) override + { + visitMDiagram(diagram); + } + + void visitMItem(const MItem *item) override + { + if (m_filterData->type() != ModelTreeFilterData::Type::Any + && m_filterData->type() != ModelTreeFilterData::Type::Item) + { + m_keep = false; + } else { + visitMObject(item); + } + } + + void visitMRelation(const MRelation *relation) override + { + if (!m_viewData->showRelations()) + m_keep = false; + else if (!m_filterData->name().isEmpty() && m_filterData->name() != relation->name()) + m_keep = false; + else + visitMElement(relation); + } + + void visitMDependency(const MDependency *dependency) override + { + if (m_filterData->type() != ModelTreeFilterData::Type::Any + && m_filterData->type() != ModelTreeFilterData::Type::Dependency) + { + m_keep = false; + } else { + switch (m_filterData->direction()) { + case ModelTreeFilterData::Direction::Any: + break; + case ModelTreeFilterData::Direction::Outgoing: + if (dependency->direction() != MDependency::AToB) + m_keep = false; + break; + case ModelTreeFilterData::Direction::Incoming: + if (dependency->direction() != MDependency::BToA) + m_keep = false; + break; + case ModelTreeFilterData::Direction::Bidirectional: + if (dependency->direction() != MDependency::Bidirectional) + m_keep = false; + break; + } + if (m_keep) + visitMRelation(dependency); + } + } + + void visitMInheritance(const MInheritance *inheritance) override + { + if (m_filterData->type() != ModelTreeFilterData::Type::Any + && m_filterData->type() != ModelTreeFilterData::Type::Inheritance) + { + m_keep = false; + } else { + visitMRelation(inheritance); + } + } + + void visitMAssociation(const MAssociation *association) override + { + if (m_filterData->type() != ModelTreeFilterData::Type::Any + && m_filterData->type() != ModelTreeFilterData::Type::Association) + { + m_keep = false; + } else { + visitMRelation(association); + } + } + + void visitMConnection(const MConnection *connection) override + { + if (m_filterData->type() != ModelTreeFilterData::Type::Any + && m_filterData->type() != ModelTreeFilterData::Type::Connection) + { + m_keep = false; + } else { + visitMRelation(connection); + } + } + +private: + const ModelTreeViewData *m_viewData = nullptr; + const ModelTreeFilterData *m_filterData = nullptr; + bool m_keep = true; +}; + +} + SortedTreeModel::SortedTreeModel(QObject *parent) : QSortFilterProxyModel(parent) { setDynamicSortFilter(false); + setRecursiveFilteringEnabled(true); setSortCaseSensitivity(Qt::CaseInsensitive); m_delayedSortTimer.setSingleShot(true); @@ -33,6 +222,37 @@ void SortedTreeModel::setTreeModel(TreeModel *treeModel) startDelayedSortTimer(); } +void SortedTreeModel::setModelTreeViewData(const ModelTreeViewData &viewData) +{ + m_modelTreeViewData = viewData; + beginResetModel(); + endResetModel(); + startDelayedSortTimer(); +} + +void SortedTreeModel::setModelTreeFilterData(const ModelTreeFilterData &filterData) +{ + m_modelTreeViewFilterData = filterData; + beginResetModel(); + endResetModel(); + startDelayedSortTimer(); +} + +bool SortedTreeModel::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const +{ + QModelIndex index = sourceModel()->index(source_row, 0, source_parent); + MElement *element = m_treeModel->element(index); + if (element) { + Filter filter; + filter.setModelTreeViewData(&m_modelTreeViewData); + filter.setModelTreeFilterData(&m_modelTreeViewFilterData); + element->accept(&filter); + if (!filter.keep()) + return false; + } + return true; +} + bool SortedTreeModel::lessThan(const QModelIndex &left, const QModelIndex &right) const { TreeModel::ItemType leftItemType = TreeModel::ItemType(sourceModel()->data(left, TreeModel::RoleItemType).toInt()); diff --git a/src/libs/modelinglib/qmt/model_ui/sortedtreemodel.h b/src/libs/modelinglib/qmt/model_ui/sortedtreemodel.h index 0c01cf7de7..797a321132 100644 --- a/src/libs/modelinglib/qmt/model_ui/sortedtreemodel.h +++ b/src/libs/modelinglib/qmt/model_ui/sortedtreemodel.h @@ -4,6 +4,7 @@ #pragma once #include "qmt/infrastructure/qmt_global.h" +#include "qmt/model_ui/modeltreefilterdata.h" #include <QSortFilterProxyModel> #include <QTimer> @@ -21,7 +22,11 @@ public: TreeModel *treeModel() const { return m_treeModel; } void setTreeModel(TreeModel *treeModel); + void setModelTreeViewData(const ModelTreeViewData &viewData); + void setModelTreeFilterData(const ModelTreeFilterData &filterData); + protected: + bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const override; bool lessThan(const QModelIndex &left, const QModelIndex &right) const override; private: @@ -32,6 +37,8 @@ private: void startDelayedSortTimer(); TreeModel *m_treeModel = nullptr; + ModelTreeViewData m_modelTreeViewData; + ModelTreeFilterData m_modelTreeViewFilterData; QTimer m_delayedSortTimer; }; diff --git a/src/libs/modelinglib/qmt/model_ui/treemodel.cpp b/src/libs/modelinglib/qmt/model_ui/treemodel.cpp index 3e8d5a59b6..23d589a654 100644 --- a/src/libs/modelinglib/qmt/model_ui/treemodel.cpp +++ b/src/libs/modelinglib/qmt/model_ui/treemodel.cpp @@ -30,6 +30,8 @@ #include <QStandardItem> +using Utils::FilePath; + namespace qmt { class TreeModel::ModelItem : public QStandardItem @@ -807,7 +809,7 @@ QString TreeModel::createRelationLabel(const MRelation *relation) } QIcon TreeModel::createIcon(StereotypeIcon::Element stereotypeIconElement, StyleEngine::ElementType styleElementType, - const QStringList &stereotypes, const QString &defaultIconPath) + const QStringList &stereotypes, const FilePath &defaultIconPath) { const Style *style = m_styleController->adaptStyle(styleElementType); return m_stereotypeController->createIcon(stereotypeIconElement, stereotypes, defaultIconPath, style, diff --git a/src/libs/modelinglib/qmt/model_ui/treemodel.h b/src/libs/modelinglib/qmt/model_ui/treemodel.h index add325a94a..5ea3596348 100644 --- a/src/libs/modelinglib/qmt/model_ui/treemodel.h +++ b/src/libs/modelinglib/qmt/model_ui/treemodel.h @@ -9,6 +9,8 @@ #include <qmt/stereotype/stereotypeicon.h> #include <qmt/style/styleengine.h> +#include <utils/filepath.h> + #include <QScopedPointer> #include <QHash> @@ -93,8 +95,9 @@ private: QString createObjectLabel(const MObject *object); QString createRelationLabel(const MRelation *relation); QIcon createIcon(StereotypeIcon::Element stereotypeIconElement, - StyleEngine::ElementType styleElementType, const QStringList &stereotypes, - const QString &defaultIconPath); + StyleEngine::ElementType styleElementType, + const QStringList &stereotypes, + const Utils::FilePath &defaultIconPath); enum Busy { NotBusy, diff --git a/src/libs/modelinglib/qmt/model_widgets_ui/addrelatedelementsdialog.cpp b/src/libs/modelinglib/qmt/model_widgets_ui/addrelatedelementsdialog.cpp new file mode 100644 index 0000000000..40499e2839 --- /dev/null +++ b/src/libs/modelinglib/qmt/model_widgets_ui/addrelatedelementsdialog.cpp @@ -0,0 +1,426 @@ +// Copyright (C) 2018 Jochen Becher +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include "addrelatedelementsdialog.h" + +#include <qmt/tasks/diagramscenecontroller.h> +#include <qmt/diagram_controller/dselection.h> +#include <qmt/model/mdiagram.h> +#include <qmt/model/mrelation.h> +#include <qmt/model/mdependency.h> +#include <qmt/model/minheritance.h> +#include <qmt/model/massociation.h> +#include <qmt/model/mconnection.h> +#include <qmt/model_controller/modelcontroller.h> +#include <qmt/model_controller/mvoidvisitor.h> + +#include "../../modelinglibtr.h" + +#include <utils/layoutbuilder.h> + +#include <QComboBox> +#include <QDialogButtonBox> +#include <QLabel> +#include <QStringListModel> + +namespace qmt { + +namespace { + +enum class RelationType { + Any, + Dependency, + Association, + Inheritance, + Connection +}; + +enum class RelationDirection { + Any, + Outgoing, + Incoming, + Bidirectional +}; + +enum class ElementType { + Any, + Package, + Component, + Class, + Diagram, + Item, +}; + +class Filter : public qmt::MVoidConstVisitor { +public: + + void setRelationType(RelationType newRelationType) + { + m_relationType = newRelationType; + } + + void setRelationTypeId(const QString &newRelationTypeId) + { + m_relationTypeId = newRelationTypeId; + } + + void setRelationDirection(RelationDirection newRelationDirection) + { + m_relationDirection = newRelationDirection; + } + + void setRelationStereotypes(const QStringList &newRelationStereotypes) + { + m_relationStereotypes = newRelationStereotypes; + } + + void setElementType(ElementType newElementType) + { + m_elementType = newElementType; + } + + void setElementStereotypes(const QStringList &newElementStereotypes) + { + m_elementStereotypes = newElementStereotypes; + } + + void setObject(const qmt::DObject *dobject, const qmt::MObject *mobject) + { + m_dobject = dobject; + m_mobject = mobject; + } + + void setRelation(const qmt::MRelation *relation) + { + m_relation = relation; + } + + bool keep() const { return m_keep; } + + void reset() + { + m_dobject = nullptr; + m_mobject = nullptr; + m_relation = nullptr; + m_keep = true; + } + + // MConstVisitor interface + void visitMObject(const qmt::MObject *object) override + { + if (!m_elementStereotypes.isEmpty()) { + const QStringList stereotypes = object->stereotypes(); + bool containsElementStereotype = std::any_of( + stereotypes.constBegin(), stereotypes.constEnd(), + [&](const QString &s) { return m_elementStereotypes.contains(s); }); + if (!containsElementStereotype) { + m_keep = false; + return; + } + } + } + + void visitMPackage(const qmt::MPackage *package) override + { + if (m_elementType == ElementType::Any || m_elementType == ElementType::Package) + qmt::MVoidConstVisitor::visitMPackage(package); + else + m_keep = false; + } + + void visitMClass(const qmt::MClass *klass) override + { + if (m_elementType == ElementType::Any || m_elementType == ElementType::Class) + qmt::MVoidConstVisitor::visitMClass(klass); + else + m_keep = false; + } + + void visitMComponent(const qmt::MComponent *component) override + { + if (m_elementType == ElementType::Any || m_elementType == ElementType::Component) + qmt::MVoidConstVisitor::visitMComponent(component); + else + m_keep = false; + } + + void visitMDiagram(const qmt::MDiagram *diagram) override + { + if (m_elementType == ElementType::Any || m_elementType == ElementType::Diagram) + qmt::MVoidConstVisitor::visitMDiagram(diagram); + else + m_keep = false; + } + + void visitMItem(const qmt::MItem *item) override + { + if (m_elementType == ElementType::Any || m_elementType == ElementType::Item) + qmt::MVoidConstVisitor::visitMItem(item); + else + m_keep = false; + } + + void visitMRelation(const qmt::MRelation *relation) override + { + if (!m_relationStereotypes.isEmpty()) { + const QStringList relationStereotypes = relation->stereotypes(); + bool containsRelationStereotype = std::any_of( + relationStereotypes.constBegin(), relationStereotypes.constEnd(), + [&](const QString &s) { return m_relationStereotypes.contains(s); }); + if (!containsRelationStereotype) { + m_keep = false; + return; + } + } + } + + void visitMDependency(const qmt::MDependency *dependency) override + { + if (m_relationDirection != RelationDirection::Any) { + bool keep = false; + if (m_relationDirection == RelationDirection::Outgoing) { + if (dependency->direction() == qmt::MDependency::AToB && dependency->endAUid() == m_mobject->uid()) + keep = true; + else if (dependency->direction() == qmt::MDependency::BToA && dependency->endBUid() == m_mobject->uid()) + keep = true; + } else if (m_relationDirection == RelationDirection::Incoming) { + if (dependency->direction() == qmt::MDependency::AToB && dependency->endBUid() == m_mobject->uid()) + keep = true; + else if (dependency->direction() == qmt::MDependency::BToA && dependency->endAUid() == m_mobject->uid()) + keep = true; + } else if (m_relationDirection == RelationDirection::Bidirectional) { + if (dependency->direction() == qmt::MDependency::Bidirectional) + keep = true; + } + m_keep = keep; + if (!keep) + return; + } + if (m_relationType == RelationType::Any || m_relationType == RelationType::Dependency) + qmt::MVoidConstVisitor::visitMDependency(dependency); + else + m_keep = false; + } + + bool testDirection(const qmt::MRelation *relation) + { + if (m_relationDirection != RelationDirection::Any) { + bool keep = false; + if (m_relationDirection == RelationDirection::Outgoing) { + if (relation->endAUid() == m_mobject->uid()) + keep = true; + } else if (m_relationDirection == RelationDirection::Incoming) { + if (relation->endBUid() == m_mobject->uid()) + keep = true; + } + m_keep = keep; + if (!keep) + return false; + } + return true; + } + + void visitMInheritance(const qmt::MInheritance *inheritance) override + { + if (!testDirection(inheritance)) + return; + if (m_relationType == RelationType::Any || m_relationType == RelationType::Inheritance) + qmt::MVoidConstVisitor::visitMInheritance(inheritance); + else + m_keep = false; + } + + void visitMAssociation(const qmt::MAssociation *association) override + { + if (!testDirection(association)) + return; + if (m_relationType == RelationType::Any || m_relationType == RelationType::Association) + qmt::MVoidConstVisitor::visitMAssociation(association); + else + m_keep = false; + } + + void visitMConnection(const qmt::MConnection *connection) override + { + if (!testDirection(connection)) + return; + if (m_relationType == RelationType::Any || m_relationType == RelationType::Connection) + qmt::MVoidConstVisitor::visitMConnection(connection); + else + m_keep = false; + } + +private: + RelationType m_relationType = RelationType::Any; + QString m_relationTypeId; + RelationDirection m_relationDirection = RelationDirection::Any; + QStringList m_relationStereotypes; + ElementType m_elementType = ElementType::Any; + QStringList m_elementStereotypes; + const qmt::DObject *m_dobject = nullptr; + const qmt::MObject *m_mobject = nullptr; + const qmt::MRelation *m_relation = nullptr; + bool m_keep = true; +}; +} // namespace + +class AddRelatedElementsDialog::Private { +public: + qmt::DiagramSceneController *m_diagramSceneController = nullptr; + qmt::DSelection m_selection; + qmt::Uid m_diagramUid; + QStringListModel m_relationTypeModel; + QStringListModel m_relationDirectionModel; + QStringListModel m_relationStereotypesModel; + QStringListModel m_elementTypeModel; + QStringListModel m_elementStereotypesModel; + Filter m_filter; + + QComboBox *RelationTypeCombobox; + QComboBox *DirectionCombobox; + QComboBox *StereotypesCombobox; + QComboBox *ElementStereotypesCombobox; + QComboBox *ElementTypeComboBox; + QLabel *NumberOfMatchingElementsValue; + QDialogButtonBox *buttonBox; +}; + +AddRelatedElementsDialog::AddRelatedElementsDialog(QWidget *parent) : + QDialog(parent), + d(new Private) +{ + setMinimumWidth(500); + + d->RelationTypeCombobox = new QComboBox; + d->DirectionCombobox = new QComboBox; + d->StereotypesCombobox = new QComboBox; + d->StereotypesCombobox->setEditable(true); + d->ElementTypeComboBox = new QComboBox; + d->ElementStereotypesCombobox = new QComboBox; + d->ElementStereotypesCombobox->setEditable(true); + d->NumberOfMatchingElementsValue = new QLabel; + d->buttonBox = new QDialogButtonBox(QDialogButtonBox::Cancel|QDialogButtonBox::Ok); + + using namespace Layouting; + Column { + Group { + title(Tr::tr("Relation Attributes")), + Form { + Tr::tr("Type"), d->RelationTypeCombobox, br, + Tr::tr("Direction"), d->DirectionCombobox, br, + Tr::tr("Stereotypes"), d->StereotypesCombobox, br, + }, + }, + Group { + title(Tr::tr("Other Element Attributes")), + Form { + Tr::tr("Type"), d->ElementTypeComboBox, br, + Tr::tr("Stereotypes"), d->ElementStereotypesCombobox, br, + }, + }, + Row { + Tr::tr("Number of matching elements: "), d->NumberOfMatchingElementsValue, st, + }, + st, + d->buttonBox, + }.attachTo(this); + + connect(d->RelationTypeCombobox, &QComboBox::currentIndexChanged, this, &AddRelatedElementsDialog::updateNumberOfElements); + connect(d->DirectionCombobox, &QComboBox::currentIndexChanged, this, &AddRelatedElementsDialog::updateNumberOfElements); + connect(d->StereotypesCombobox, &QComboBox::currentTextChanged, this, &AddRelatedElementsDialog::updateNumberOfElements); + connect(d->ElementTypeComboBox, &QComboBox::currentIndexChanged, this, &AddRelatedElementsDialog::updateNumberOfElements); + connect(d->ElementStereotypesCombobox, &QComboBox::currentTextChanged, this, &AddRelatedElementsDialog::updateNumberOfElements); + connect(d->buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept); + connect(d->buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject); + connect(this, &QDialog::accepted, this, &AddRelatedElementsDialog::onAccepted); +} + +AddRelatedElementsDialog::~AddRelatedElementsDialog() +{ + delete d; +} + +void AddRelatedElementsDialog::setDiagramSceneController(qmt::DiagramSceneController *diagramSceneController) +{ + d->m_diagramSceneController = diagramSceneController; +} + +void AddRelatedElementsDialog::setElements(const qmt::DSelection &selection, qmt::MDiagram *diagram) +{ + d->m_selection = selection; + d->m_diagramUid = diagram->uid(); + QStringList relationTypes = {"Any", "Dependency", "Association", "Inheritance"}; + d->m_relationTypeModel.setStringList(relationTypes); + d->RelationTypeCombobox->setModel(&d->m_relationTypeModel); + QStringList relationDirections = {"Any", "Outgoing (->)", "Incoming (<-)", "Bidirectional (<->)"}; + d->m_relationDirectionModel.setStringList(relationDirections); + d->DirectionCombobox->setModel(&d->m_relationDirectionModel); + QStringList relationStereotypes = { }; + d->m_relationStereotypesModel.setStringList(relationStereotypes); + d->StereotypesCombobox->setModel(&d->m_relationStereotypesModel); + QStringList elementTypes = {"Any", "Package", "Component", "Class", "Diagram", "Item"}; + d->m_elementTypeModel.setStringList(elementTypes); + d->ElementTypeComboBox->setModel(&d->m_elementTypeModel); + QStringList elementStereotypes = { }; + d->m_elementStereotypesModel.setStringList(elementStereotypes); + d->ElementStereotypesCombobox->setModel(&d->m_elementStereotypesModel); + updateNumberOfElements(); +} + +void AddRelatedElementsDialog::onAccepted() +{ + qmt::MDiagram *diagram = d->m_diagramSceneController->modelController()->findElement<qmt::MDiagram>(d->m_diagramUid); + if (diagram) { + updateFilter(); + d->m_diagramSceneController->addRelatedElements( + d->m_selection, diagram, + [this](qmt::DObject *dobject, qmt::MObject *mobject, qmt::MRelation *relation) -> bool + { + return this->filter(dobject, mobject, relation); + }); + } +} + +void AddRelatedElementsDialog::updateFilter() +{ + d->m_filter.setRelationType((RelationType) d->RelationTypeCombobox->currentIndex()); + d->m_filter.setRelationDirection((RelationDirection) d->DirectionCombobox->currentIndex()); + d->m_filter.setRelationStereotypes(d->StereotypesCombobox->currentText().split(',', Qt::SkipEmptyParts)); + d->m_filter.setElementType((ElementType) d->ElementTypeComboBox->currentIndex()); + d->m_filter.setElementStereotypes(d->ElementStereotypesCombobox->currentText().split(',', Qt::SkipEmptyParts)); +} + +bool AddRelatedElementsDialog::filter(qmt::DObject *dobject, qmt::MObject *mobject, qmt::MRelation *relation) +{ + d->m_filter.reset(); + d->m_filter.setObject(dobject, mobject); + d->m_filter.setRelation(relation); + relation->accept(&d->m_filter); + if (!d->m_filter.keep()) + return false; + qmt::MObject *targetObject = nullptr; + if (relation->endAUid() != mobject->uid()) + targetObject = d->m_diagramSceneController->modelController()->findObject(relation->endAUid()); + else if (relation->endBUid() != mobject->uid()) + targetObject = d->m_diagramSceneController->modelController()->findObject(relation->endBUid()); + if (!targetObject) + return false; + targetObject->accept(&d->m_filter); + return d->m_filter.keep(); +} + +void AddRelatedElementsDialog::updateNumberOfElements() +{ + qmt::MDiagram *diagram = d->m_diagramSceneController->modelController()->findElement<qmt::MDiagram>(d->m_diagramUid); + if (diagram) { + updateFilter(); + d->NumberOfMatchingElementsValue->setText(QString::number(d->m_diagramSceneController->countRelatedElements( + d->m_selection, diagram, + [this](qmt::DObject *dobject, qmt::MObject *mobject, qmt::MRelation *relation) -> bool + { + return this->filter(dobject, mobject, relation); + }))); + } +} + +} // namespace qmt diff --git a/src/libs/modelinglib/qmt/model_widgets_ui/addrelatedelementsdialog.h b/src/libs/modelinglib/qmt/model_widgets_ui/addrelatedelementsdialog.h new file mode 100644 index 0000000000..e0fefa1c6c --- /dev/null +++ b/src/libs/modelinglib/qmt/model_widgets_ui/addrelatedelementsdialog.h @@ -0,0 +1,43 @@ +// Copyright (C) 2018 Jochen Becher +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#pragma once + +#include "qmt/infrastructure/qmt_global.h" + +#include <QDialog> + +namespace qmt { + +class DSelection; +class DObject; +class MObject; +class MDiagram; +class MRelation; +class DiagramSceneController; +} + +namespace qmt { + +class QMT_EXPORT AddRelatedElementsDialog : public QDialog +{ + Q_OBJECT + class Private; + +public: + explicit AddRelatedElementsDialog(QWidget *parent = nullptr); + ~AddRelatedElementsDialog(); + + void setDiagramSceneController(qmt::DiagramSceneController *diagramSceneController); + void setElements(const qmt::DSelection &selection, qmt::MDiagram *diagram); + +private: + void onAccepted(); + void updateFilter(); + bool filter(qmt::DObject *dobject, qmt::MObject *mobject, qmt::MRelation *relation); + void updateNumberOfElements(); + + Private *d = nullptr; +}; + +} // namespace qmt diff --git a/src/libs/modelinglib/qmt/model_widgets_ui/modeltreefilter.cpp b/src/libs/modelinglib/qmt/model_widgets_ui/modeltreefilter.cpp new file mode 100644 index 0000000000..f02b466830 --- /dev/null +++ b/src/libs/modelinglib/qmt/model_widgets_ui/modeltreefilter.cpp @@ -0,0 +1,173 @@ +// Copyright (C) 2018 Jochen Becher +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include "modeltreefilter.h" + +#include "../../modelinglibtr.h" + +#include <utils/layoutbuilder.h> + +#include <QCheckBox> +#include <QComboBox> +#include <QFrame> +#include <QLabel> +#include <QLineEdit> +#include <QPushButton> +#include <QStringListModel> + +namespace qmt { + +class ModelTreeFilter::Private { +public: + QStringListModel m_typeModel; + QStringListModel m_stereotypesModel; + QStringListModel m_directionModel; + + QPushButton *resetViewButton; + QCheckBox *relationsCheckBox; + QCheckBox *diagramElementsCheckBox; + QPushButton *clearFilterButton; + QComboBox *typeComboBox; + QComboBox *stereotypesComboBox; + QLineEdit *nameLineEdit; + QComboBox *directionComboBox; +}; + +ModelTreeFilter::ModelTreeFilter(QWidget *parent) : + QWidget(parent), + d(new Private) +{ + d->resetViewButton = new QPushButton(Tr::tr("Reset")); + d->relationsCheckBox = new QCheckBox(Tr::tr("Relations")); + d->diagramElementsCheckBox = new QCheckBox(Tr::tr("Diagram Elements")); + d->diagramElementsCheckBox->setMaximumHeight(0); + d->clearFilterButton = new QPushButton(Tr::tr("Clear")); + d->typeComboBox = new QComboBox; + d->stereotypesComboBox = new QComboBox; + d->stereotypesComboBox->setEditable(true); + d->nameLineEdit = new QLineEdit; + d->directionComboBox = new QComboBox; + + const auto boldLabel = [] (const QString &title) -> QLabel * { + auto label = new QLabel(title); + QFont boldFont = label->font(); + boldFont.setWeight(QFont::Bold); + label->setFont(boldFont); + return label; + }; + + const auto line = [] () -> QFrame * { + auto line = new QFrame; + line->setFrameShadow(QFrame::Plain); + line->setFrameShape(QFrame::HLine); + return line; + }; + + const int margin = 9; + + using namespace Layouting; + Column { + Column { + Row { + boldLabel(Tr::tr("View")), st, d->resetViewButton + }, + d->relationsCheckBox, + d->diagramElementsCheckBox, + customMargins(margin, 0, margin, 0), + }, + Space(10), + line(), + Column { + Row { + boldLabel(Tr::tr("Filter")), st, d->clearFilterButton + }, + Space(10), + Form { + Tr::tr("Type:"), d->typeComboBox, br, + Tr::tr("Stereotypes:"), d->stereotypesComboBox, br, + Tr::tr("Name:"), d->nameLineEdit, br, + Tr::tr("Direction:"), d->directionComboBox, br, + }, + customMargins(margin, 0, margin, 0), + }, + st, + line(), + customMargins(0, margin, 0, 0), + }.attachTo(this); + + connect(d->resetViewButton, &QPushButton::clicked, this, &ModelTreeFilter::resetView); + connect(d->relationsCheckBox, &QCheckBox::clicked, this, &ModelTreeFilter::onViewChanged); + connect(d->diagramElementsCheckBox, &QCheckBox::clicked, this, &ModelTreeFilter::onViewChanged); + connect(d->clearFilterButton, &QPushButton::clicked, this, &ModelTreeFilter::clearFilter); + connect(d->typeComboBox, &QComboBox::currentIndexChanged, this, [this](int index) { + d->directionComboBox->setDisabled(index != (int) ModelTreeFilterData::Type::Dependency); + }); + connect(d->typeComboBox, &QComboBox::currentIndexChanged, this, &ModelTreeFilter::onFilterChanged); + connect(d->stereotypesComboBox, &QComboBox::currentTextChanged, this, &ModelTreeFilter::onFilterChanged); + connect(d->nameLineEdit, &QLineEdit::textChanged, this, &ModelTreeFilter::onFilterChanged); + connect(d->directionComboBox, &QComboBox::currentTextChanged, this, &ModelTreeFilter::onFilterChanged); + setupFilter(); + resetView(); + clearFilter(); +} + +ModelTreeFilter::~ModelTreeFilter() +{ + delete d; +} + +void ModelTreeFilter::setupFilter() +{ + QStringList types = {"Any", "Package", "Component", "Class", "Diagram", "Item", + "Dependency", "Association", "Inheritance", "Connection"}; + d->m_typeModel.setStringList(types); + d->typeComboBox->setModel(&d->m_typeModel); + + QStringList stereotypes = { }; + d->m_stereotypesModel.setStringList(stereotypes); + d->stereotypesComboBox->setModel(&d->m_stereotypesModel); + + QStringList directions = {"Any", "Outgoing (->)", "Incoming (<-)", "Bidirectional (<->)"}; + d->m_directionModel.setStringList(directions); + d->directionComboBox->setModel(&d->m_directionModel); +} + +void ModelTreeFilter::resetView() +{ + d->relationsCheckBox->setChecked(true); + d->diagramElementsCheckBox->setChecked(false); + onViewChanged(); +} + +void ModelTreeFilter::clearFilter() +{ + d->typeComboBox->setCurrentIndex(0); + d->stereotypesComboBox->clearEditText(); + d->nameLineEdit->clear(); + d->directionComboBox->setCurrentIndex(0); + onFilterChanged(); +} + +void ModelTreeFilter::onViewChanged() +{ + ModelTreeViewData modelTreeViewData; + modelTreeViewData.setShowRelations(d->relationsCheckBox->isChecked()); + modelTreeViewData.setShowDiagramElements(d->diagramElementsCheckBox->isChecked()); + emit viewDataChanged(modelTreeViewData); +} + +void ModelTreeFilter::onFilterChanged() +{ + ModelTreeFilterData modelTreeFilterData; + modelTreeFilterData.setType((ModelTreeFilterData::Type) d->typeComboBox->currentIndex()); + modelTreeFilterData.setCustomId(QString()); + modelTreeFilterData.setStereotypes(d->stereotypesComboBox->currentText().split(',', Qt::SkipEmptyParts)); + modelTreeFilterData.setName(d->nameLineEdit->text().trimmed()); + if (modelTreeFilterData.type() == ModelTreeFilterData::Type::Dependency) + modelTreeFilterData.setDirection((ModelTreeFilterData::Direction) d->directionComboBox->currentIndex()); + else + modelTreeFilterData.setDirection(ModelTreeFilterData::Direction::Any); + emit filterDataChanged(modelTreeFilterData); +} + +} // namespace qmt diff --git a/src/libs/modelinglib/qmt/model_widgets_ui/modeltreefilter.h b/src/libs/modelinglib/qmt/model_widgets_ui/modeltreefilter.h new file mode 100644 index 0000000000..1de7237c6e --- /dev/null +++ b/src/libs/modelinglib/qmt/model_widgets_ui/modeltreefilter.h @@ -0,0 +1,39 @@ +// Copyright (C) 2018 Jochen Becher +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#pragma once + +#include "qmt/infrastructure/qmt_global.h" +#include "qmt/model_ui/modeltreefilterdata.h" + +#include <QWidget> + +namespace qmt { + +class QMT_EXPORT ModelTreeFilter : public QWidget +{ + Q_OBJECT + class Private; + +public: + explicit ModelTreeFilter(QWidget *parent = nullptr); + ~ModelTreeFilter(); + +signals: + void viewDataChanged(const ModelTreeViewData &modelTreeViewData); + void filterDataChanged(const ModelTreeFilterData &modelTreeFilterData); + +private: + + void setupFilter(); + void resetView(); + void clearFilter(); + void onViewChanged(); + void onFilterChanged(); + +private: + Private *d = nullptr; +}; + + +} // namespace qmt diff --git a/src/libs/modelinglib/qmt/model_widgets_ui/modeltreeview.cpp b/src/libs/modelinglib/qmt/model_widgets_ui/modeltreeview.cpp index 68c22ce6df..e2006a37b5 100644 --- a/src/libs/modelinglib/qmt/model_widgets_ui/modeltreeview.cpp +++ b/src/libs/modelinglib/qmt/model_widgets_ui/modeltreeview.cpp @@ -71,6 +71,46 @@ void ModelTreeView::setElementTasks(IElementTasks *elementTasks) m_elementTasks = elementTasks; } +static void StoreStatus(QTreeView *view, QSortFilterProxyModel *model, const QModelIndex &parent, QVector<QModelIndex> &expanded_items) +{ + for (int index = 0; index < model->rowCount(parent); ++index) { + auto proxy_index = model->index(index, 0, parent); + if (view->isExpanded(proxy_index)) { + auto source_index = model->mapToSource(proxy_index); + expanded_items.append(source_index); + } + StoreStatus(view, model, proxy_index, expanded_items); + } +} + +static void ApplyStatus(QTreeView *view, QSortFilterProxyModel *model, QVector<QModelIndex> &expanded_items) +{ + for (auto source_index : expanded_items) { + auto proxy_index = model->mapFromSource(source_index); + view->setExpanded(proxy_index, true); + } +} + +void ModelTreeView::setModelTreeViewData(const ModelTreeViewData &viewData) +{ + if (m_sortedTreeModel) { + QVector<QModelIndex> expanded_items; + StoreStatus(this, m_sortedTreeModel, QModelIndex(), expanded_items); + m_sortedTreeModel->setModelTreeViewData(viewData); + ApplyStatus(this, m_sortedTreeModel, expanded_items); + } +} + +void ModelTreeView::setModelTreeFilterData(const ModelTreeFilterData &filterData) +{ + if (m_sortedTreeModel) { + QVector<QModelIndex> expanded_items; + StoreStatus(this, m_sortedTreeModel, QModelIndex(), expanded_items); + m_sortedTreeModel->setModelTreeFilterData(filterData); + ApplyStatus(this, m_sortedTreeModel, expanded_items); + } +} + QModelIndex ModelTreeView::mapToSourceModelIndex(const QModelIndex &index) const { return m_sortedTreeModel->mapToSource(index); @@ -144,7 +184,7 @@ void ModelTreeView::dragMoveEvent(QDragMoveEvent *event) { QTreeView::dragMoveEvent(event); bool accept = false; - QModelIndex dropIndex = indexAt(event->pos()); + QModelIndex dropIndex = indexAt(event->position().toPoint()); QModelIndex dropSourceModelIndex = m_sortedTreeModel->mapToSource(dropIndex); if (dropSourceModelIndex.isValid()) { TreeModel *treeModel = m_sortedTreeModel->treeModel(); @@ -175,7 +215,7 @@ void ModelTreeView::dropEvent(QDropEvent *event) bool accept = false; event->setDropAction(Qt::MoveAction); if (event->mimeData()->hasFormat("text/model-elements")) { - QModelIndex dropIndex = indexAt(event->pos()); + QModelIndex dropIndex = indexAt(event->position().toPoint()); QModelIndex dropSourceModelIndex = m_sortedTreeModel->mapToSource(dropIndex); if (dropSourceModelIndex.isValid()) { TreeModel *treeModel = m_sortedTreeModel->treeModel(); @@ -237,6 +277,10 @@ void ModelTreeView::contextMenuEvent(QContextMenuEvent *event) menu.addAction(new ContextMenuAction(Tr::tr("Open Diagram"), "openDiagram", &menu)); addSeparator = true; } + if (m_elementTasks->hasLinkedFile(melement)) { + menu.addAction(new ContextMenuAction(Tr::tr("Open Linked File"), "openLinkedFile", &menu)); + addSeparator = true; + } if (melement->owner()) { if (addSeparator) menu.addSeparator(); @@ -253,6 +297,8 @@ void ModelTreeView::contextMenuEvent(QContextMenuEvent *event) m_elementTasks->openClassDefinition(melement); } else if (action->id() == "openDiagram") { m_elementTasks->openDiagram(melement); + } else if (action->id() == "openLinkedFile") { + m_elementTasks->openLinkedFile(melement); } else if (action->id() == "delete") { MSelection selection; selection.append(melement->uid(), melement->owner()->uid()); diff --git a/src/libs/modelinglib/qmt/model_widgets_ui/modeltreeview.h b/src/libs/modelinglib/qmt/model_widgets_ui/modeltreeview.h index 0c07cb0372..e5537644a3 100644 --- a/src/libs/modelinglib/qmt/model_widgets_ui/modeltreeview.h +++ b/src/libs/modelinglib/qmt/model_widgets_ui/modeltreeview.h @@ -5,6 +5,7 @@ #include "qmt/infrastructure/qmt_global.h" #include "qmt/model_ui/modeltreeviewinterface.h" +#include "qmt/model_ui/modeltreefilterdata.h" #include <QElapsedTimer> #include <QTreeView> @@ -32,6 +33,9 @@ public: void setTreeModel(SortedTreeModel *model); void setElementTasks(IElementTasks *elementTasks); + void setModelTreeViewData(const ModelTreeViewData &viewData); + void setModelTreeFilterData(const ModelTreeFilterData &filterData); + QModelIndex mapToSourceModelIndex(const QModelIndex &index) const; void selectFromSourceModelIndex(const QModelIndex &index); diff --git a/src/libs/modelinglib/qmt/model_widgets_ui/propertiesviewmview.cpp b/src/libs/modelinglib/qmt/model_widgets_ui/propertiesviewmview.cpp index 969313ca33..11df24188d 100644 --- a/src/libs/modelinglib/qmt/model_widgets_ui/propertiesviewmview.cpp +++ b/src/libs/modelinglib/qmt/model_widgets_ui/propertiesviewmview.cpp @@ -43,6 +43,7 @@ #include "qmt/diagram_scene/items/stereotypedisplayvisitor.h" #include "qmt/stereotype/stereotypecontroller.h" #include "qmt/stereotype/customrelation.h" +#include "qmt/style/relationvisuals.h" #include "qmt/style/stylecontroller.h" #include "qmt/style/style.h" #include "qmt/style/objectvisuals.h" @@ -237,6 +238,61 @@ static DClass::TemplateDisplay translateIndexToTemplateDisplay(int index) return map[index]; } +static int translateRelationVisualPrimaryRoleToIndex(DRelation::VisualPrimaryRole visualRole) +{ + switch (visualRole) { + case DRelation::PrimaryRoleNormal: + return 0; + case DRelation::PrimaryRoleCustom1: + return 1; + case DRelation::PrimaryRoleCustom2: + return 2; + case DRelation::PrimaryRoleCustom3: + return 3; + case DRelation::PrimaryRoleCustom4: + return 4; + case DRelation::PrimaryRoleCustom5: + return 5; + } + return 0; +} + +static DRelation::VisualPrimaryRole translateIndexToRelationVisualPrimaryRole(int index) +{ + static const DRelation::VisualPrimaryRole map[] = { + DRelation::PrimaryRoleNormal, + DRelation::PrimaryRoleCustom1, DRelation::PrimaryRoleCustom2, DRelation::PrimaryRoleCustom3, + DRelation::PrimaryRoleCustom4, DRelation::PrimaryRoleCustom5 + }; + QMT_ASSERT(index >= 0 && index <= 5, return DRelation::PrimaryRoleNormal); + return map[index]; +} + +static int translateRelationVisualSecondaryRoleToIndex(DRelation::VisualSecondaryRole visualRole) +{ + switch (visualRole) { + case DRelation::SecondaryRoleNone: + return 0; + case DRelation::SecondaryRoleWarning: + return 1; + case DRelation::SecondaryRoleError: + return 2; + case DRelation::SecondaryRoleSoften: + return 3; + } + return 0; +} + +static DRelation::VisualSecondaryRole translateIndexToRelationVisualSecondaryRole(int index) +{ + static const DRelation::VisualSecondaryRole map[] = { + DRelation::SecondaryRoleNone, + DRelation::SecondaryRoleWarning, DRelation::SecondaryRoleError, DRelation::SecondaryRoleSoften + }; + QMT_ASSERT(index >= 0 && index <= 5, return DRelation::SecondaryRoleNone); + return map[index]; +} + static int translateAnnotationVisualRoleToIndex(DAnnotation::VisualRole visualRole) { switch (visualRole) { @@ -398,6 +454,7 @@ void PropertiesView::MView::visitMPackage(const MPackage *package) else setTitle<MPackage>(m_modelElements, Tr::tr("Package"), Tr::tr("Packages")); visitMObject(package); + visitMObjectBehind(package); } void PropertiesView::MView::visitMClass(const MClass *klass) @@ -472,12 +529,14 @@ void PropertiesView::MView::visitMClass(const MClass *klass) } if (m_classMembersEdit->isEnabled() != isSingleSelection) m_classMembersEdit->setEnabled(isSingleSelection); + visitMObjectBehind(klass); } void PropertiesView::MView::visitMComponent(const MComponent *component) { setTitle<MComponent>(m_modelElements, Tr::tr("Component"), Tr::tr("Components")); visitMObject(component); + visitMObjectBehind(component); } void PropertiesView::MView::visitMDiagram(const MDiagram *diagram) @@ -497,6 +556,7 @@ void PropertiesView::MView::visitMCanvasDiagram(const MCanvasDiagram *diagram) { setTitle<MCanvasDiagram>(m_modelElements, Tr::tr("Canvas Diagram"), Tr::tr("Canvas Diagrams")); visitMDiagram(diagram); + visitMDiagramBehind(diagram); } void PropertiesView::MView::visitMItem(const MItem *item) @@ -521,6 +581,7 @@ void PropertiesView::MView::visitMItem(const MItem *item) if (m_itemVarietyEdit->isEnabled() != isSingleSelection) m_itemVarietyEdit->setEnabled(isSingleSelection); } + visitMObjectBehind(item); } void PropertiesView::MView::visitMRelation(const MRelation *relation) @@ -850,6 +911,7 @@ void PropertiesView::MView::visitDElement(const DElement *element) void PropertiesView::MView::visitDObject(const DObject *object) { visitDElement(object); + visitDObjectBefore(object); #ifdef SHOW_DEBUG_PROPERTIES if (!m_posRectLabel) { m_posRectLabel = new QLabel(m_topWidget); @@ -1057,6 +1119,52 @@ void PropertiesView::MView::visitDItem(const DItem *item) void PropertiesView::MView::visitDRelation(const DRelation *relation) { visitDElement(relation); + if (!m_relationVisualPrimaryRoleSelector) { + m_relationVisualPrimaryRoleSelector = new PaletteBox(m_topWidget); + setRelationPrimaryRolePalette(m_styleElementType, DRelation::PrimaryRoleNormal); + setRelationPrimaryRolePalette(m_styleElementType, DRelation::PrimaryRoleCustom1); + setRelationPrimaryRolePalette(m_styleElementType, DRelation::PrimaryRoleCustom2); + setRelationPrimaryRolePalette(m_styleElementType, DRelation::PrimaryRoleCustom3); + setRelationPrimaryRolePalette(m_styleElementType, DRelation::PrimaryRoleCustom4); + setRelationPrimaryRolePalette(m_styleElementType, DRelation::PrimaryRoleCustom5); + addRow(Tr::tr("Color:"), m_relationVisualPrimaryRoleSelector, "color"); + connect(m_relationVisualPrimaryRoleSelector, &PaletteBox::activated, + this, &PropertiesView::MView::onRelationVisualPrimaryRoleChanged); + } + if (!m_relationVisualPrimaryRoleSelector->hasFocus()) { + DRelation::VisualPrimaryRole visualPrimaryRole; + if (haveSameValue(m_diagramElements, &DRelation::visualPrimaryRole, &visualPrimaryRole)) + m_relationVisualPrimaryRoleSelector->setCurrentIndex(translateRelationVisualPrimaryRoleToIndex(visualPrimaryRole)); + else + m_relationVisualPrimaryRoleSelector->setCurrentIndex(-1); + } + if (!m_relationVisualSecondaryRoleSelector) { + m_relationVisualSecondaryRoleSelector = new QComboBox(m_topWidget); + m_relationVisualSecondaryRoleSelector->addItems({ Tr::tr("Normal"), Tr::tr("Warning"), Tr::tr("Error"), Tr::tr("Soften") }); + addRow(Tr::tr("Role:"), m_relationVisualSecondaryRoleSelector, "role"); + connect(m_relationVisualSecondaryRoleSelector, QOverload<int>::of(&QComboBox::activated), + this, &PropertiesView::MView::onRelationVisualSecondaryRoleChanged); + } + if (!m_relationVisualSecondaryRoleSelector->hasFocus()) { + DRelation::VisualSecondaryRole visualSecondaryRole; + if (haveSameValue(m_diagramElements, &DRelation::visualSecondaryRole, &visualSecondaryRole)) + m_relationVisualSecondaryRoleSelector->setCurrentIndex(translateRelationVisualSecondaryRoleToIndex(visualSecondaryRole)); + else + m_relationVisualSecondaryRoleSelector->setCurrentIndex(-1); + } + if (!m_relationVisualEmphasizedCheckbox) { + m_relationVisualEmphasizedCheckbox = new QCheckBox(Tr::tr("Emphasized"), m_topWidget); + addRow(QString(), m_relationVisualEmphasizedCheckbox, "emphasized"); + connect(m_relationVisualEmphasizedCheckbox, &QAbstractButton::clicked, + this, &PropertiesView::MView::onRelationVisualEmphasizedChanged); + } + if (!m_relationVisualEmphasizedCheckbox->hasFocus()) { + bool emphasized; + if (haveSameValue(m_diagramElements, &DRelation::isVisualEmphasized, &emphasized)) + m_relationVisualEmphasizedCheckbox->setChecked(emphasized); + else + m_relationVisualEmphasizedCheckbox->setChecked(false); + } #ifdef SHOW_DEBUG_PROPERTIES if (!m_pointsLabel) { m_pointsLabel = new QLabel(m_topWidget); @@ -1145,6 +1253,26 @@ void PropertiesView::MView::visitDSwimlane(const DSwimlane *swimlane) visitDElement(swimlane); } +void PropertiesView::MView::visitMElementBehind(const MElement *element) +{ + Q_UNUSED(element) +} + +void PropertiesView::MView::visitMObjectBehind(const MObject *object) +{ + visitMElementBehind(object); +} + +void PropertiesView::MView::visitMDiagramBehind(const MDiagram *diagram) +{ + visitMObjectBehind(diagram); +} + +void PropertiesView::MView::visitDObjectBefore(const DObject *object) +{ + Q_UNUSED(object); +} + void PropertiesView::MView::onStereotypesChanged(const QString &stereotypes) { QList<QString> set = m_stereotypesController->fromString(stereotypes); @@ -1392,7 +1520,42 @@ void PropertiesView::MView::onAnnotationVisualRoleChanged(int visualRoleIndex) { DAnnotation::VisualRole visualRole = translateIndexToAnnotationVisualRole((visualRoleIndex)); assignModelElement<DAnnotation, DAnnotation::VisualRole>( - m_diagramElements, SelectionMulti, visualRole, &DAnnotation::visualRole, &DAnnotation::setVisualRole); + m_diagramElements, SelectionMulti, visualRole, &DAnnotation::visualRole, &DAnnotation::setVisualRole); +} + + +void PropertiesView::MView::onRelationVisualPrimaryRoleChanged(int visualRoleIndex) +{ + DRelation::VisualPrimaryRole visualRole = translateIndexToRelationVisualPrimaryRole(visualRoleIndex); + assignModelElement<DRelation, DRelation::VisualPrimaryRole>( + m_diagramElements, SelectionMulti, visualRole, + &DRelation::visualPrimaryRole, &DRelation::setVisualPrimaryRole); +} + +void PropertiesView::MView::onRelationVisualSecondaryRoleChanged(int visualRoleIndex) +{ + DRelation::VisualSecondaryRole visualRole = translateIndexToRelationVisualSecondaryRole(visualRoleIndex); + assignModelElement<DRelation, DRelation::VisualSecondaryRole>( + m_diagramElements, SelectionMulti, visualRole, + &DRelation::visualSecondaryRole, &DRelation::setVisualSecondaryRole); +} + +void PropertiesView::MView::onRelationVisualEmphasizedChanged(bool visualEmphasized) +{ + assignModelElement<DRelation, bool>(m_diagramElements, SelectionMulti, visualEmphasized, + &DRelation::isVisualEmphasized, &DRelation::setVisualEmphasized); +} + +void PropertiesView::MView::onRelationColorChanged(const QColor &color) +{ + assignModelElement<DRelation, QColor>(m_diagramElements, SelectionMulti, color, + &DRelation::color, &DRelation::setColor); +} + +void PropertiesView::MView::onRelationThicknessChanged(qreal thickness) +{ + assignModelElement<DRelation, qreal>(m_diagramElements, SelectionMulti, thickness, + &DRelation::thickness, &DRelation::setThickness); } void PropertiesView::MView::prepare() @@ -1553,7 +1716,8 @@ void PropertiesView::MView::setPrimaryRolePalette(StyleEngine::ElementType eleme DObject::VisualPrimaryRole visualPrimaryRole, const QColor &baseColor) { int index = translateVisualPrimaryRoleToIndex(visualPrimaryRole); - const Style *style = m_propertiesView->styleController()->adaptObjectStyle(elementType, ObjectVisuals(visualPrimaryRole, DObject::SecondaryRoleNone, false, baseColor, 0)); + const Style *style = m_propertiesView->styleController()->adaptObjectStyle( + elementType, ObjectVisuals(visualPrimaryRole, DObject::SecondaryRoleNone, false, baseColor, 0)); m_visualPrimaryRoleSelector->setBrush(index, style->fillBrush()); m_visualPrimaryRoleSelector->setLinePen(index, style->linePen()); } @@ -1595,112 +1759,15 @@ QString PropertiesView::MView::formatTemplateParameters(const QList<QString> &te return templateParamters; } -template<class T, class V> -QList<T *> PropertiesView::MView::filter(const QList<V *> &elements) -{ - QList<T *> filtered; - for (V *element : elements) { - auto t = dynamic_cast<T *>(element); - if (t) - filtered.append(t); - } - return filtered; -} - -template<class T, class V, class BASE> -bool PropertiesView::MView::haveSameValue(const QList<BASE *> &baseElements, V (T::*getter)() const, V *value) +void PropertiesView::MView::setRelationPrimaryRolePalette(StyleEngine::ElementType elementType, + DRelation::VisualPrimaryRole visualPrimaryRole) { - const QList<T *> elements = filter<T>(baseElements); - QMT_CHECK(!elements.isEmpty()); - V candidate = V(); // avoid warning of reading uninitialized variable - bool haveCandidate = false; - for (T *element : elements) { - if (!haveCandidate) { - candidate = ((*element).*getter)(); - haveCandidate = true; - } else { - if (candidate != ((*element).*getter)()) - return false; - } - } - QMT_CHECK(haveCandidate); - if (!haveCandidate) - return false; - if (value) - *value = candidate; - return true; -} - -template<class T, class V, class BASE> -void PropertiesView::MView::assignModelElement(const QList<BASE *> &baseElements, SelectionType selectionType, - const V &value, V (T::*getter)() const, void (T::*setter)(const V &)) -{ - const QList<T *> elements = filter<T>(baseElements); - if ((selectionType == SelectionSingle && elements.size() == 1) || selectionType == SelectionMulti) { - for (T *element : elements) { - if (value != ((*element).*getter)()) { - m_propertiesView->beginUpdate(element); - ((*element).*setter)(value); - m_propertiesView->endUpdate(element, false); - } - } - } -} - -template<class T, class V, class BASE> -void PropertiesView::MView::assignModelElement(const QList<BASE *> &baseElements, SelectionType selectionType, - const V &value, V (T::*getter)() const, void (T::*setter)(V)) -{ - const QList<T *> elements = filter<T>(baseElements); - if ((selectionType == SelectionSingle && elements.size() == 1) || selectionType == SelectionMulti) { - for (T *element : elements) { - if (value != ((*element).*getter)()) { - m_propertiesView->beginUpdate(element); - ((*element).*setter)(value); - m_propertiesView->endUpdate(element, false); - } - } - } -} - -template<class T, class E, class V, class BASE> -void PropertiesView::MView::assignEmbeddedModelElement(const QList<BASE *> &baseElements, SelectionType selectionType, - const V &value, E (T::*getter)() const, - void (T::*setter)(const E &), - V (E::*vGetter)() const, void (E::*vSetter)(const V &)) -{ - const QList<T *> elements = filter<T>(baseElements); - if ((selectionType == SelectionSingle && elements.size() == 1) || selectionType == SelectionMulti) { - for (T *element : elements) { - E embedded = ((*element).*getter)(); - if (value != (embedded.*vGetter)()) { - m_propertiesView->beginUpdate(element); - (embedded.*vSetter)(value); - ((*element).*setter)(embedded); - m_propertiesView->endUpdate(element, false); - } - } - } -} - -template<class T, class E, class V, class BASE> -void PropertiesView::MView::assignEmbeddedModelElement(const QList<BASE *> &baseElements, SelectionType selectionType, - const V &value, E (T::*getter)() const, - void (T::*setter)(const E &), - V (E::*vGetter)() const, void (E::*vSetter)(V)) -{ - const QList<T *> elements = filter<T>(baseElements); - if ((selectionType == SelectionSingle && elements.size() == 1) || selectionType == SelectionMulti) { - for (T *element : elements) { - E embedded = ((*element).*getter)(); - if (value != (embedded.*vGetter)()) { - m_propertiesView->beginUpdate(element); - (embedded.*vSetter)(value); - ((*element).*setter)(embedded); - m_propertiesView->endUpdate(element, false); - } - } - } + int index = translateRelationVisualPrimaryRoleToIndex(visualPrimaryRole); + const Style *style = m_propertiesView->styleController()->adaptRelationStyle( + elementType, RelationVisuals(DObject::PrimaryRoleNormal, visualPrimaryRole, + DRelation::SecondaryRoleNone, false)); + m_relationVisualPrimaryRoleSelector->setBrush(index, style->fillBrush()); + m_relationVisualPrimaryRoleSelector->setLinePen(index, style->linePen()); } } // namespace qmt diff --git a/src/libs/modelinglib/qmt/model_widgets_ui/propertiesviewmview.h b/src/libs/modelinglib/qmt/model_widgets_ui/propertiesviewmview.h index 4762803a20..8fa9f0d137 100644 --- a/src/libs/modelinglib/qmt/model_widgets_ui/propertiesviewmview.h +++ b/src/libs/modelinglib/qmt/model_widgets_ui/propertiesviewmview.h @@ -8,6 +8,8 @@ #include "qmt/model/mconstvisitor.h" #include "qmt/diagram/dconstvisitor.h" #include "qmt/diagram/dobject.h" +#include "qmt/diagram/drelation.h" +#include "qmt/infrastructure/qmtassert.h" #include "qmt/stereotype/stereotypeicon.h" #include "qmt/style/styleengine.h" @@ -76,6 +78,11 @@ public: void edit(); protected: + virtual void visitMElementBehind(const MElement *element); + virtual void visitMObjectBehind(const MObject *object); + virtual void visitMDiagramBehind(const MDiagram *diagram); + virtual void visitDObjectBefore(const DObject *object); + void onStereotypesChanged(const QString &stereotypes); void onObjectNameChanged(const QString &name); void onNamespaceChanged(const QString ¨Namespace); @@ -110,6 +117,11 @@ protected: void onPlainShapeChanged(bool plainShape); void onItemShapeChanged(const QString &shape); void onAutoWidthChanged(bool autoWidthed); + void onRelationVisualPrimaryRoleChanged(int visualRoleIndex); + void onRelationVisualSecondaryRoleChanged(int visualRoleIndex); + void onRelationVisualEmphasizedChanged(bool visualEmphasized); + void onRelationColorChanged(const QColor &color); + void onRelationThicknessChanged(qreal thickness); void onAnnotationVisualRoleChanged(int visualRoleIndex); void prepare(); @@ -139,6 +151,9 @@ protected: QList<QString> splitTemplateParameters(const QString &templateParameters); QString formatTemplateParameters(const QList<QString> &templateParametersList); + void setRelationPrimaryRolePalette(StyleEngine::ElementType elementType, + DRelation::VisualPrimaryRole visualPrimaryRole); + enum SelectionType { SelectionSingle, SelectionMulti @@ -149,6 +164,9 @@ protected: template<class T, class V, class BASE> bool haveSameValue(const QList<BASE *> &baseElements, V (T::*getter)() const, V *value); template<class T, class V, class BASE> + bool isValueChanged(const QList<BASE *> &baseElements, SelectionType selectionType, + const V &value, V (T::*getter)() const); + template<class T, class V, class BASE> void assignModelElement(const QList<BASE *> &baseElements, SelectionType selectionType, const V &value, V (T::*getter)() const, void (T::*setter)(const V &)); template<class T, class V, class BASE> @@ -232,7 +250,132 @@ protected: QCheckBox *m_annotationAutoWidthCheckbox = nullptr; QComboBox *m_annotationVisualRoleSelector = nullptr; // DRelation + PaletteBox *m_relationVisualPrimaryRoleSelector = nullptr; + QComboBox *m_relationVisualSecondaryRoleSelector = nullptr; + QCheckBox *m_relationVisualEmphasizedCheckbox = nullptr; QLabel *m_pointsLabel = nullptr; }; +template<class T, class V> +QList<T *> PropertiesView::MView::filter(const QList<V *> &elements) +{ + QList<T *> filtered; + for (auto *element : elements) { + auto t = dynamic_cast<T *>(element); + if (t) + filtered.append(t); + } + return filtered; +} + +template<class T, class V, class BASE> +inline bool PropertiesView::MView::haveSameValue(const QList<BASE *> &baseElements, V (T::*getter)() const, V *value) +{ + QList<T *> elements = filter<T>(baseElements); + QMT_CHECK(!elements.isEmpty()); + V candidate = V(); // avoid warning of reading uninitialized variable + bool haveCandidate = false; + for (const auto *element : elements) { + if (!haveCandidate) { + candidate = ((*element).*getter)(); + haveCandidate = true; + } else { + if (candidate != ((*element).*getter)()) + return false; + } + } + QMT_CHECK(haveCandidate); + if (!haveCandidate) + return false; + if (value) + *value = candidate; + return true; +} + +template<class T, class V, class BASE> +inline bool PropertiesView::MView::isValueChanged(const QList<BASE *> &baseElements, SelectionType selectionType, + const V &value, V (T::*getter)() const) +{ + QList<T *> elements = filter<T>(baseElements); + if ((selectionType == SelectionSingle && elements.size() == 1) || selectionType == SelectionMulti) { + for (const auto *element : elements) { + if (value != ((*element).*getter)()) + return true; + } + } + return false; +} + +template<class T, class V, class BASE> +inline void PropertiesView::MView::assignModelElement(const QList<BASE *> &baseElements, SelectionType selectionType, + const V &value, V (T::*getter)() const, void (T::*setter)(const V &)) +{ + QList<T *> elements = filter<T>(baseElements); + if ((selectionType == SelectionSingle && elements.size() == 1) || selectionType == SelectionMulti) { + for (auto *element : elements) { + if (value != ((*element).*getter)()) { + m_propertiesView->beginUpdate(element); + ((*element).*setter)(value); + m_propertiesView->endUpdate(element, false); + } + } + } +} + +template<class T, class V, class BASE> +inline void PropertiesView::MView::assignModelElement(const QList<BASE *> &baseElements, SelectionType selectionType, + const V &value, V (T::*getter)() const, void (T::*setter)(V)) +{ + QList<T *> elements = filter<T>(baseElements); + if ((selectionType == SelectionSingle && elements.size() == 1) || selectionType == SelectionMulti) { + for (auto *element : elements) { + if (value != ((*element).*getter)()) { + m_propertiesView->beginUpdate(element); + ((*element).*setter)(value); + m_propertiesView->endUpdate(element, false); + } + } + } +} + +template<class T, class E, class V, class BASE> +inline void PropertiesView::MView::assignEmbeddedModelElement(const QList<BASE *> &baseElements, SelectionType selectionType, + const V &value, E (T::*getter)() const, + void (T::*setter)(const E &), + V (E::*vGetter)() const, void (E::*vSetter)(const V &)) +{ + QList<T *> elements = filter<T>(baseElements); + if ((selectionType == SelectionSingle && elements.size() == 1) || selectionType == SelectionMulti) { + for (auto *element : elements) { + E embedded = ((*element).*getter)(); + if (value != (embedded.*vGetter)()) { + m_propertiesView->beginUpdate(element); + (embedded.*vSetter)(value); + ((*element).*setter)(embedded); + m_propertiesView->endUpdate(element, false); + } + } + } +} + +template<class T, class E, class V, class BASE> +inline void PropertiesView::MView::assignEmbeddedModelElement(const QList<BASE *> &baseElements, SelectionType selectionType, + const V &value, E (T::*getter)() const, + void (T::*setter)(const E &), + V (E::*vGetter)() const, void (E::*vSetter)(V)) +{ + QList<T *> elements = filter<T>(baseElements); + if ((selectionType == SelectionSingle && elements.size() == 1) || selectionType == SelectionMulti) { + for (auto *element : elements) { + E embedded = ((*element).*getter)(); + if (value != (embedded.*vGetter)()) { + m_propertiesView->beginUpdate(element); + (embedded.*vSetter)(value); + ((*element).*setter)(embedded); + m_propertiesView->endUpdate(element, false); + } + } + } +} + } // namespace qmt diff --git a/src/libs/modelinglib/qmt/project/project.cpp b/src/libs/modelinglib/qmt/project/project.cpp index 098db684ef..2e08adc6df 100644 --- a/src/libs/modelinglib/qmt/project/project.cpp +++ b/src/libs/modelinglib/qmt/project/project.cpp @@ -3,6 +3,8 @@ #include "project.h" +using Utils::FilePath; + namespace qmt { Project::Project() @@ -23,7 +25,7 @@ bool Project::hasFileName() const return !m_fileName.isEmpty(); } -void Project::setFileName(const QString &fileName) +void Project::setFileName(const FilePath &fileName) { m_fileName = fileName; } @@ -33,7 +35,7 @@ void Project::setRootPackage(MPackage *rootPackage) m_rootPackage = rootPackage; } -void Project::setConfigPath(const QString &configPath) +void Project::setConfigPath(const FilePath &configPath) { m_configPath = configPath; } diff --git a/src/libs/modelinglib/qmt/project/project.h b/src/libs/modelinglib/qmt/project/project.h index fab6bb4228..556471e9ed 100644 --- a/src/libs/modelinglib/qmt/project/project.h +++ b/src/libs/modelinglib/qmt/project/project.h @@ -5,6 +5,8 @@ #include "qmt/infrastructure/uid.h" +#include <utils/filepath.h> + #include <QString> namespace qmt { @@ -20,18 +22,18 @@ public: Uid uid() const { return m_uid; } void setUid(const Uid &uid); bool hasFileName() const; - QString fileName() const { return m_fileName; } - void setFileName(const QString &fileName); + Utils::FilePath fileName() const { return m_fileName; } + void setFileName(const Utils::FilePath &fileName); MPackage *rootPackage() const { return m_rootPackage; } void setRootPackage(MPackage *rootPackage); - QString configPath() const { return m_configPath; } - void setConfigPath(const QString &configPath); + Utils::FilePath configPath() const { return m_configPath; } + void setConfigPath(const Utils::FilePath &configPath); private: Uid m_uid; - QString m_fileName; + Utils::FilePath m_fileName; MPackage *m_rootPackage = nullptr; - QString m_configPath; + Utils::FilePath m_configPath; }; } // namespace qmt diff --git a/src/libs/modelinglib/qmt/project_controller/projectcontroller.cpp b/src/libs/modelinglib/qmt/project_controller/projectcontroller.cpp index f7a7e6d2c9..47eb01138e 100644 --- a/src/libs/modelinglib/qmt/project_controller/projectcontroller.cpp +++ b/src/libs/modelinglib/qmt/project_controller/projectcontroller.cpp @@ -10,6 +10,8 @@ #include "../../modelinglibtr.h" +using Utils::FilePath; + namespace qmt { NoFileNameException::NoFileNameException() @@ -31,7 +33,7 @@ ProjectController::~ProjectController() { } -void ProjectController::newProject(const QString &fileName) +void ProjectController::newProject(const FilePath &fileName) { m_project.reset(new Project()); auto rootPackage = new MPackage(); @@ -43,7 +45,7 @@ void ProjectController::newProject(const QString &fileName) emit changed(); } -void ProjectController::setFileName(const QString &fileName) +void ProjectController::setFileName(const FilePath &fileName) { if (fileName != m_project->fileName()) { m_project->setFileName(fileName); @@ -82,7 +84,7 @@ void ProjectController::save() emit changed(); } -void ProjectController::saveAs(const QString &fileName) +void ProjectController::saveAs(const FilePath &fileName) { setFileName(fileName); save(); diff --git a/src/libs/modelinglib/qmt/project_controller/projectcontroller.h b/src/libs/modelinglib/qmt/project_controller/projectcontroller.h index 1992c72448..09cf65074a 100644 --- a/src/libs/modelinglib/qmt/project_controller/projectcontroller.h +++ b/src/libs/modelinglib/qmt/project_controller/projectcontroller.h @@ -6,6 +6,8 @@ #include "qmt/infrastructure/exceptions.h" #include "qmt/infrastructure/qmt_global.h" +#include <utils/filepath.h> + #include <QObject> #include <QString> @@ -35,19 +37,19 @@ public: signals: void changed(); - void fileNameChanged(const QString &fileName); + void fileNameChanged(const Utils::FilePath &fileName); public: Project *project() const { return m_project.data(); } bool isModified() const { return m_isModified; } - void newProject(const QString &fileName); - void setFileName(const QString &fileName); + void newProject(const Utils::FilePath &fileName); + void setFileName(const Utils::FilePath &fileName); void setModified(); void load(); void save(); - void saveAs(const QString &fileName); + void saveAs(const Utils::FilePath &fileName); private: QScopedPointer<Project> m_project; diff --git a/src/libs/modelinglib/qmt/serializer/diagramserializer.cpp b/src/libs/modelinglib/qmt/serializer/diagramserializer.cpp index 5170d4fdc7..2d68862c32 100644 --- a/src/libs/modelinglib/qmt/serializer/diagramserializer.cpp +++ b/src/libs/modelinglib/qmt/serializer/diagramserializer.cpp @@ -29,10 +29,35 @@ #include "qark/qxmlinarchive.h" #include "qark/serialize.h" +#include <QBuffer> + using namespace qmt; namespace qark { +template<class Archive> +inline void save(Archive &archive, const QImage &image) +{ + QByteArray a; + QBuffer buffer(&a); + buffer.open(QIODevice::WriteOnly); + image.save(&buffer, "PNG"); + // TODO add write(const QByteArray &) + archive.write(QString::fromLatin1(a.toBase64())); +} + +template<class Archive> +inline void load(Archive &archive, QImage &image) +{ + QString s; + // TODO add read(QByteArray &) + archive.read(&s); + QByteArray a = QByteArray::fromBase64(s.toLatin1()); + QBuffer buffer(&a); + buffer.open(QIODevice::ReadOnly); + image.load(&buffer, "PNG"); +} + // DElement QARK_REGISTER_TYPE_NAME(DElement, "DElement") @@ -102,7 +127,10 @@ inline void Access<Archive, DObject>::serialize(Archive &archive, DObject &objec || attr("visual-role", object, &visualRole, &setVisualRole) || attr("visual-role2", object, &DObject::visualSecondaryRole, &DObject::setVisualSecondaryRole) || attr("visual-emphasized", object, &DObject::isVisualEmphasized, &DObject::setVisualEmphasized) + || attr("linkedfile", object, &DObject::hasLinkedFile, &DObject::setLinkedFile) || attr("stereotype-display", object, &DObject::stereotypeDisplay, &DObject::setStereotypeDisplay) + || attr("image-path", object, &DObject::imagePath, &DObject::setImagePath) + || attr("image", object, &DObject::image, &DObject::setImage) // depth is not persistent || end; } @@ -220,6 +248,9 @@ inline void Access<Archive, DRelation>::serialize(Archive &archive, DRelation &r || attr("b", relation, &DRelation::endBUid, &DRelation::setEndBUid) || attr("name", relation, &DRelation::name, &DRelation::setName) || attr("points", relation, &DRelation::intermediatePoints, &DRelation::setIntermediatePoints) + || attr("visualPrimaryRole", relation, &DRelation::visualPrimaryRole, &DRelation::setVisualPrimaryRole) + || attr("visualSecondaryRole", relation, &DRelation::visualSecondaryRole, &DRelation::setVisualSecondaryRole) + || attr("thickness", relation, &DRelation::thickness, &DRelation::setThickness) || end; } diff --git a/src/libs/modelinglib/qmt/serializer/diagramserializer.h b/src/libs/modelinglib/qmt/serializer/diagramserializer.h index a571ff94ef..aa5d1adb52 100644 --- a/src/libs/modelinglib/qmt/serializer/diagramserializer.h +++ b/src/libs/modelinglib/qmt/serializer/diagramserializer.h @@ -6,6 +6,7 @@ #include "qmt/diagram/dannotation.h" #include "qmt/diagram/dobject.h" #include "qmt/diagram/dclass.h" +#include "qmt/diagram/drelation.h" #include "qark/serialize_enum.h" @@ -16,5 +17,7 @@ QARK_SERIALIZE_ENUM(qmt::DObject::VisualSecondaryRole) QARK_SERIALIZE_ENUM(qmt::DObject::StereotypeDisplay) QARK_SERIALIZE_ENUM(qmt::DClass::TemplateDisplay) QARK_SERIALIZE_ENUM(qmt::DAnnotation::VisualRole) +QARK_SERIALIZE_ENUM(qmt::DRelation::VisualPrimaryRole) +QARK_SERIALIZE_ENUM(qmt::DRelation::VisualSecondaryRole) } // namespace qark diff --git a/src/libs/modelinglib/qmt/serializer/infrastructureserializer.h b/src/libs/modelinglib/qmt/serializer/infrastructureserializer.h index e67e5af4eb..59103b3fa9 100644 --- a/src/libs/modelinglib/qmt/serializer/infrastructureserializer.h +++ b/src/libs/modelinglib/qmt/serializer/infrastructureserializer.h @@ -3,6 +3,8 @@ #pragma once +#include <utils/filepath.h> + #include "qmt/infrastructure/handle.h" #include "qmt/infrastructure/handles.h" #include "qmt/infrastructure/uid.h" @@ -51,4 +53,20 @@ inline void serialize(Archive &archive, qmt::Handles<T> &handles) || end; } +// Utils::FilePath + +template<class Archive> +inline void save(Archive &archive, const Utils::FilePath &filePath) +{ + archive.write(filePath.toFSPathString()); +} + +template<class Archive> +inline void load(Archive &archive, Utils::FilePath &filePath) +{ + QString s; + archive.read(&s); + filePath = Utils::FilePath::fromUserInput(s); +} + } // namespace qark diff --git a/src/libs/modelinglib/qmt/serializer/modelserializer.cpp b/src/libs/modelinglib/qmt/serializer/modelserializer.cpp index 115eba08d0..935a825ab2 100644 --- a/src/libs/modelinglib/qmt/serializer/modelserializer.cpp +++ b/src/libs/modelinglib/qmt/serializer/modelserializer.cpp @@ -95,6 +95,7 @@ inline void Access<Archive, MObject>::serialize(Archive &archive, MObject &objec archive || tag(object) || base<MElement>(object) || attr("name", object, &MObject::name, &MObject::setName) + || attr("linkedfilename", object, &MObject::linkedFileName, &MObject::setLinkedFileName) || attr("children", object, &MObject::children, &MObject::setChildren) || attr("relations", object, &MObject::relations, &MObject::setRelations) || end; diff --git a/src/libs/modelinglib/qmt/serializer/projectserializer.cpp b/src/libs/modelinglib/qmt/serializer/projectserializer.cpp index 77f231b358..832d47309b 100644 --- a/src/libs/modelinglib/qmt/serializer/projectserializer.cpp +++ b/src/libs/modelinglib/qmt/serializer/projectserializer.cpp @@ -20,6 +20,8 @@ #include <QFile> +using Utils::FilePath; + namespace qark { using namespace qmt; @@ -48,11 +50,11 @@ ProjectSerializer::~ProjectSerializer() { } -void ProjectSerializer::save(const QString &fileName, const Project *project) +void ProjectSerializer::save(const FilePath &fileName, const Project *project) { QMT_ASSERT(project, return); - QFile file(fileName); + QFile file(fileName.toFSPathString()); if (!file.open(QIODevice::WriteOnly)) throw FileCreationException(fileName); @@ -84,11 +86,11 @@ QByteArray ProjectSerializer::save(const Project *project) return buffer; } -void ProjectSerializer::load(const QString &fileName, Project *project) +void ProjectSerializer::load(const FilePath &fileName, Project *project) { QMT_ASSERT(project, return); - QFile file(fileName); + QFile file(fileName.toFSPathString()); if (!file.open(QIODevice::ReadOnly)) throw FileNotFoundException(fileName); diff --git a/src/libs/modelinglib/qmt/serializer/projectserializer.h b/src/libs/modelinglib/qmt/serializer/projectserializer.h index c6dd74bcd8..bafa407071 100644 --- a/src/libs/modelinglib/qmt/serializer/projectserializer.h +++ b/src/libs/modelinglib/qmt/serializer/projectserializer.h @@ -5,6 +5,8 @@ #include "qmt/infrastructure/qmt_global.h" +#include <utils/filepath.h> + #include <QString> QT_BEGIN_NAMESPACE @@ -21,9 +23,9 @@ public: ProjectSerializer(); ~ProjectSerializer(); - void save(const QString &fileName, const Project *project); + void save(const Utils::FilePath &fileName, const Project *project); QByteArray save(const Project *project); - void load(const QString &fileName, Project *project); + void load(const Utils::FilePath &fileName, Project *project); private: void write(QXmlStreamWriter *writer, const Project *project); diff --git a/src/libs/modelinglib/qmt/stereotype/customrelation.cpp b/src/libs/modelinglib/qmt/stereotype/customrelation.cpp index fa9c80a1a0..cc80da1b80 100644 --- a/src/libs/modelinglib/qmt/stereotype/customrelation.cpp +++ b/src/libs/modelinglib/qmt/stereotype/customrelation.cpp @@ -114,4 +114,9 @@ void CustomRelation::setColor(const QColor &color) m_color = color; } +void CustomRelation::setEmphasized(bool emphasized) +{ + m_emphasized = emphasized; +} + } // namespace qmt diff --git a/src/libs/modelinglib/qmt/stereotype/customrelation.h b/src/libs/modelinglib/qmt/stereotype/customrelation.h index 7125865ade..dd32c542c0 100644 --- a/src/libs/modelinglib/qmt/stereotype/customrelation.h +++ b/src/libs/modelinglib/qmt/stereotype/customrelation.h @@ -55,7 +55,10 @@ public: enum class ColorType { EndA, EndB, - Custom + Custom, + Warning, + Error, + Soften, }; class End { @@ -113,6 +116,8 @@ public: void setColorType(ColorType colorType); QColor color() const { return m_color; } void setColor(const QColor &color); + bool emphasized() const { return m_emphasized; } + void setEmphasized(bool emphasized); friend auto qHash(CustomRelation::Relationship relationship) { return ::qHash(static_cast<int>(relationship)); @@ -139,6 +144,7 @@ private: ShaftPattern m_shaftPattern = ShaftPattern::Solid; ColorType m_colorType = ColorType::EndA; QColor m_color; + bool m_emphasized = false; }; } // namespace qmt diff --git a/src/libs/modelinglib/qmt/stereotype/shapevalue.cpp b/src/libs/modelinglib/qmt/stereotype/shapevalue.cpp index 9e1ee46c15..cf8ea5ef65 100644 --- a/src/libs/modelinglib/qmt/stereotype/shapevalue.cpp +++ b/src/libs/modelinglib/qmt/stereotype/shapevalue.cpp @@ -29,7 +29,7 @@ qreal ShapeValueF::mapScaledTo(qreal scaledOrigin, qreal originalSize, qreal bas v = originalSize != 0 ? (m_value * actualSize / originalSize) : m_value; break; case UnitPercentage: - v = m_value * actualSize; + v = m_value * actualSize / 100.0; break; } switch (m_origin) { diff --git a/src/libs/modelinglib/qmt/stereotype/stereotypecontroller.cpp b/src/libs/modelinglib/qmt/stereotype/stereotypecontroller.cpp index 030c9e12a5..2f679df117 100644 --- a/src/libs/modelinglib/qmt/stereotype/stereotypecontroller.cpp +++ b/src/libs/modelinglib/qmt/stereotype/stereotypecontroller.cpp @@ -10,6 +10,8 @@ #include "qmt/infrastructure/qmtassert.h" #include "qmt/style/style.h" + +#include <utils/filepath.h> #include <utils/algorithm.h> #include <QHash> @@ -19,12 +21,14 @@ #include <algorithm> +using Utils::FilePath; + namespace qmt { namespace { struct IconKey { - IconKey(StereotypeIcon::Element element, const QList<QString> &stereotypes, const QString &defaultIconPath, + IconKey(StereotypeIcon::Element element, const QList<QString> &stereotypes, const FilePath &defaultIconPath, const Uid &styleUid, const QSize &size, const QMarginsF &margins, qreal lineWidth) : m_element(element), m_stereotypes(stereotypes), @@ -53,7 +57,7 @@ struct IconKey { const StereotypeIcon::Element m_element; const QList<QString> m_stereotypes; - const QString m_defaultIconPath; + const FilePath m_defaultIconPath; const Uid m_styleUid; const QSize m_size; const QMarginsF m_margins; @@ -68,6 +72,7 @@ public: QHash<QPair<StereotypeIcon::Element, QString>, QString> m_stereotypeToIconIdMap; QHash<QString, StereotypeIcon> m_iconIdToStereotypeIconsMap; QHash<QString, CustomRelation> m_relationIdToCustomRelationMap; + QHash<QString, CustomRelation> m_stereotypeToCustomRelationMap; QList<Toolbar> m_toolbars; QList<Toolbar> m_elementToolbars; QHash<IconKey, QIcon> m_iconMap; @@ -150,8 +155,13 @@ CustomRelation StereotypeController::findCustomRelation(const QString &customRel return d->m_relationIdToCustomRelationMap.value(customRelationId); } +CustomRelation StereotypeController::findCustomRelationByStereotype(const QString &steoreotype) const +{ + return d->m_stereotypeToCustomRelationMap.value(steoreotype); +} + QIcon StereotypeController::createIcon(StereotypeIcon::Element element, const QList<QString> &stereotypes, - const QString &defaultIconPath, const Style *style, const QSize &size, + const FilePath &defaultIconPath, const Style *style, const QSize &size, const QMarginsF &margins, qreal lineWidth) { IconKey key(element, stereotypes, defaultIconPath, style->uid(), size, margins, lineWidth); @@ -225,7 +235,7 @@ QIcon StereotypeController::createIcon(StereotypeIcon::Element element, const QL icon = QIcon(pixmap); } if (icon.isNull() && !defaultIconPath.isEmpty()) - icon = QIcon(defaultIconPath); + icon = QIcon(defaultIconPath.toFSPathString()); d->m_iconMap.insert(key, icon); return icon; @@ -251,6 +261,9 @@ void StereotypeController::addStereotypeIcon(const StereotypeIcon &stereotypeIco void StereotypeController::addCustomRelation(const CustomRelation &customRelation) { d->m_relationIdToCustomRelationMap.insert(customRelation.id(), customRelation); + QString stereotype = Utils::toList(customRelation.stereotypes()).value(0); + if (!stereotype.isEmpty()) + d->m_stereotypeToCustomRelationMap.insert(stereotype, customRelation); } void StereotypeController::addToolbar(const Toolbar &toolbar) diff --git a/src/libs/modelinglib/qmt/stereotype/stereotypecontroller.h b/src/libs/modelinglib/qmt/stereotype/stereotypecontroller.h index 676f9a275a..49acc5f937 100644 --- a/src/libs/modelinglib/qmt/stereotype/stereotypecontroller.h +++ b/src/libs/modelinglib/qmt/stereotype/stereotypecontroller.h @@ -5,6 +5,8 @@ #include "stereotypeicon.h" +#include <utils/filepath.h> + #include <QMarginsF> #include <QObject> @@ -33,9 +35,14 @@ public: const QList<QString> &stereotypes) const; StereotypeIcon findStereotypeIcon(const QString &stereotypeIconId) const; CustomRelation findCustomRelation(const QString &customRelationId) const; - QIcon createIcon(StereotypeIcon::Element element, const QList<QString> &stereotypes, - const QString &defaultIconPath, const Style *style, - const QSize &size, const QMarginsF &margins, qreal lineWidth); + CustomRelation findCustomRelationByStereotype(const QString &steoreotype) const; + QIcon createIcon(StereotypeIcon::Element element, + const QList<QString> &stereotypes, + const Utils::FilePath &defaultIconPath, + const Style *style, + const QSize &size, + const QMarginsF &margins, + qreal lineWidth); void addStereotypeIcon(const StereotypeIcon &stereotypeIcon); void addCustomRelation(const CustomRelation &customRelation); diff --git a/src/libs/modelinglib/qmt/stereotype/stereotypeicon.cpp b/src/libs/modelinglib/qmt/stereotype/stereotypeicon.cpp index 8bcf5df170..19e466c509 100644 --- a/src/libs/modelinglib/qmt/stereotype/stereotypeicon.cpp +++ b/src/libs/modelinglib/qmt/stereotype/stereotypeicon.cpp @@ -62,6 +62,21 @@ void StereotypeIcon::setMinHeight(qreal minHeight) m_minHeight = minHeight; } +void StereotypeIcon::setIconWith(qreal iconWidth) +{ + m_iconWidth = iconWidth; +} + +void StereotypeIcon::setIconHeight(qreal iconHeight) +{ + m_iconHeight = iconHeight; +} + +void StereotypeIcon::setDepthLayer(DepthLayer depthLayer) +{ + m_depthLayer = depthLayer; +} + void StereotypeIcon::setSizeLock(StereotypeIcon::SizeLock sizeLock) { m_sizeLock = sizeLock; diff --git a/src/libs/modelinglib/qmt/stereotype/stereotypeicon.h b/src/libs/modelinglib/qmt/stereotype/stereotypeicon.h index be45245da5..fcb5f1a15c 100644 --- a/src/libs/modelinglib/qmt/stereotype/stereotypeicon.h +++ b/src/libs/modelinglib/qmt/stereotype/stereotypeicon.h @@ -31,6 +31,12 @@ public: DisplaySmart }; + enum DepthLayer { + DepthBehindItems, + DepthAmongItems, + DepthBeforeItems, + }; + enum SizeLock { LockNone, LockWidth, @@ -62,12 +68,20 @@ public: void setWidth(qreal width); qreal height() const { return m_height; } void setHeight(qreal height); - bool hasMinWidth() const { return m_minWidth > 0; } + bool hasMinWidth() const { return m_minWidth > 0.0; } qreal minWidth() const { return m_minWidth; } void setMinWidth(qreal minWidth); - bool hasMinHeight() const { return m_minHeight > 0; } + bool hasMinHeight() const { return m_minHeight > 0.0; } qreal minHeight() const { return m_minHeight; } void setMinHeight(qreal minHeight); + bool hasIconWidth() const { return m_iconWidth > 0.0; } + qreal iconWidth() const { return m_iconWidth; } + void setIconWith(qreal iconWidth); + bool hasIconHeight() const { return m_iconHeight > 0.0; } + qreal iconHeight() const { return m_iconHeight; } + void setIconHeight(qreal iconHeight); + DepthLayer depthLayer() const { return m_depthLayer; } + void setDepthLayer(DepthLayer depthLayer); SizeLock sizeLock() const { return m_sizeLock; } void setSizeLock(SizeLock sizeLock); Display display() const { return m_display; } @@ -90,8 +104,11 @@ private: QString m_name; qreal m_width = 100.0; qreal m_height = 100.0; - qreal m_minWidth = -1; - qreal m_minHeight = -1; + qreal m_minWidth = -1.0; + qreal m_minHeight = -1.0; + qreal m_iconWidth = -1.0; + qreal m_iconHeight = -1.0; + DepthLayer m_depthLayer = DepthAmongItems; SizeLock m_sizeLock = LockNone; Display m_display = DisplaySmart; TextAlignment m_textAlignment = TextalignBelow; diff --git a/src/libs/modelinglib/qmt/style/defaultstyleengine.cpp b/src/libs/modelinglib/qmt/style/defaultstyleengine.cpp index 74e91837dc..bce1804892 100644 --- a/src/libs/modelinglib/qmt/style/defaultstyleengine.cpp +++ b/src/libs/modelinglib/qmt/style/defaultstyleengine.cpp @@ -5,6 +5,7 @@ #include "defaultstyle.h" #include "objectvisuals.h" +#include "relationvisuals.h" #include "styledobject.h" #include "styledrelation.h" @@ -14,6 +15,7 @@ #include "qmt/diagram/ditem.h" #include "qmt/diagram/dannotation.h" #include "qmt/infrastructure/qmtassert.h" +#include "qmt/stereotype/customrelation.h" #include <utils/algorithm.h> @@ -73,25 +75,29 @@ bool operator==(const ObjectStyleKey &lhs, const ObjectStyleKey &rhs) class RelationStyleKey { public: - RelationStyleKey(StyleEngine::ElementType elementType = StyleEngine::TypeOther, - DObject::VisualPrimaryRole visualPrimaryRole = DObject::PrimaryRoleNormal) + RelationStyleKey(StyleEngine::ElementType elementType, const RelationVisuals &relationVisuals, + bool withObject = false) : m_elementType(elementType), - m_visualPrimaryRole(visualPrimaryRole) + m_relationVisuals(relationVisuals), + m_withObject(withObject) { } StyleEngine::ElementType m_elementType = StyleEngine::TypeOther; - DObject::VisualPrimaryRole m_visualPrimaryRole = DObject::PrimaryRoleNormal; + RelationVisuals m_relationVisuals; + bool m_withObject = false; }; size_t qHash(const RelationStyleKey &styleKey) { - return ::qHash(styleKey.m_elementType) ^ ::qHash(styleKey.m_visualPrimaryRole); + return ::qHash(styleKey.m_elementType) ^ qHash(styleKey.m_relationVisuals); } bool operator==(const RelationStyleKey &lhs, const RelationStyleKey &rhs) { - return lhs.m_elementType == rhs.m_elementType && lhs.m_visualPrimaryRole == rhs.m_visualPrimaryRole; + return lhs.m_elementType == rhs.m_elementType + && lhs.m_relationVisuals == rhs.m_relationVisuals + && lhs.m_withObject == rhs.m_withObject; } class AnnotationStyleKey @@ -253,34 +259,6 @@ const Style *DefaultStyleEngine::applyObjectStyle(const Style *baseStyle, StyleE return derivedStyle; } -static bool areStackingRoles(DObject::VisualPrimaryRole rhsPrimaryRole, - DObject::VisualSecondaryRole rhsSecondaryRole, - DObject::VisualPrimaryRole lhsPrimaryRole, - DObject::VisualSecondaryRole lhsSecondaryRols) -{ - switch (rhsSecondaryRole) { - case DObject::SecondaryRoleNone: - case DObject::SecondaryRoleLighter: - case DObject::SecondaryRoleDarker: - case DObject::SecondaryRoleFlat: - switch (lhsSecondaryRols) { - case DObject::SecondaryRoleNone: - case DObject::SecondaryRoleLighter: - case DObject::SecondaryRoleDarker: - case DObject::SecondaryRoleFlat: - return lhsPrimaryRole == rhsPrimaryRole; - case DObject::SecondaryRoleSoften: - case DObject::SecondaryRoleOutline: - return false; - } - break; - case DObject::SecondaryRoleSoften: - case DObject::SecondaryRoleOutline: - return false; - } - return true; -} - const Style *DefaultStyleEngine::applyObjectStyle(const Style *baseStyle, const StyledObject &styledObject, const Parameters *parameters) { @@ -325,10 +303,12 @@ const Style *DefaultStyleEngine::applyObjectStyle(const Style *baseStyle, const } int depth = 0; if (!depths.isEmpty()) { - for (auto it = depths.cbegin(); it != depths.cend(); ++it) { - if (it->m_elementType == elementType - && areStackingRoles(it->m_visualPrimaryRole, it->m_visualSecondaryRole, - styledVisualPrimaryRole, styledVisualSecondaryRole)) { + const QList<int> keys = Utils::sorted(depths.keys()); + for (int d : keys) { + DepthProperties properties = depths.value(d); + if (properties.m_elementType == elementType + && areStackingRoles(properties.m_visualPrimaryRole, properties.m_visualSecondaryRole, + styledVisualPrimaryRole, styledVisualSecondaryRole)) { ++depth; } else { depth = 0; @@ -345,13 +325,100 @@ const Style *DefaultStyleEngine::applyObjectStyle(const Style *baseStyle, const parameters); } +const Style *DefaultStyleEngine::applyRelationStyle(const Style *baseStyle, ElementType elementType, const RelationVisuals &relationVisuals, const Parameters *parameters) +{ + Q_UNUSED(parameters); + + RelationStyleKey key(elementType, relationVisuals); + const Style *derivedStyle = m_relationStyleMap.value(key); + if (!derivedStyle) { + auto style = new Style(baseStyle->type()); + static QColor customColors[] = { + QColor(0xEE, 0x8E, 0x99).darker(110), // ROLE_CUSTOM1, + QColor(0x80, 0xAF, 0x47).lighter(130), // ROLE_CUSTOM2, + QColor(0xFF, 0xA1, 0x5B).lighter(100), // ROLE_CUSTOM3, + QColor(0x55, 0xC4, 0xCF).lighter(120), // ROLE_CUSTOM4, + QColor(0xFF, 0xE1, 0x4B) // ROLE_CUSTOM5, + }; + + int index = static_cast<int>(relationVisuals.visualPrimaryRole()) - static_cast<int>(DRelation::PrimaryRoleCustom1); + QColor lineColor = index >= 0 && index <= 4 ? customColors[index] : Qt::black; + switch (relationVisuals.visualSecondaryRole()) { + case DRelation::SecondaryRoleNone: + break; + case DRelation::SecondaryRoleWarning: + lineColor = Qt::yellow; + break; + case DRelation::SecondaryRoleError: + lineColor = Qt::red; + break; + case DRelation::SecondaryRoleSoften: + lineColor = Qt::gray; + break; + } + + QColor fillColor = lineColor == Qt::black ? Qt::darkGray : lineColor.lighter(150); + QPen linePen = baseStyle->linePen(); + linePen.setWidth(1); + linePen.setColor(lineColor); + style->setLinePen(linePen); + QBrush textBrush = baseStyle->textBrush(); + textBrush.setColor(Qt::black); + style->setTextBrush(textBrush); + QBrush brush = baseStyle->fillBrush(); + brush.setColor(fillColor); + brush.setStyle(Qt::SolidPattern); + style->setFillBrush(brush); + style->setNormalFont(baseStyle->normalFont()); + style->setSmallFont(baseStyle->smallFont()); + style->setHeaderFont(baseStyle->headerFont()); + m_relationStyleMap.insert(key, style); + derivedStyle = style; + } + return derivedStyle; +} + const Style *DefaultStyleEngine::applyRelationStyle(const Style *baseStyle, const StyledRelation &styledRelation, const Parameters *parameters) { Q_UNUSED(parameters) ElementType elementType = objectType(styledRelation.endA()); - RelationStyleKey key(elementType, styledRelation.endA() ? styledRelation.endA()->visualPrimaryRole() : DObject::PrimaryRoleNormal); + RelationVisuals relationVisuals; + if (styledRelation.customRelation()) { + switch (styledRelation.customRelation()->colorType()) { + case CustomRelation::ColorType::EndA: + // TODO implement + break; + case CustomRelation::ColorType::EndB: + // TODO implement + break; + case CustomRelation::ColorType::Custom: + // TODO implement + break; + case CustomRelation::ColorType::Warning: + relationVisuals.setVisualSecondaryRole(DRelation::VisualSecondaryRole::SecondaryRoleWarning); + break; + case CustomRelation::ColorType::Error: + relationVisuals.setVisualSecondaryRole(DRelation::VisualSecondaryRole::SecondaryRoleError); + break; + case CustomRelation::ColorType::Soften: + relationVisuals.setVisualSecondaryRole(DRelation::VisualSecondaryRole::SecondaryRoleSoften); + break; + } + relationVisuals.setEmphasized(styledRelation.customRelation()->emphasized()); + } + if (styledRelation.endA()) + relationVisuals.setVisualObjectPrimaryRole(styledRelation.endA()->visualPrimaryRole()); + if (styledRelation.relation()) { + if (styledRelation.relation()->visualPrimaryRole() != DRelation::VisualPrimaryRole::PrimaryRoleNormal) + relationVisuals.setVisualPrimaryRole(styledRelation.relation()->visualPrimaryRole()); + if (styledRelation.relation()->visualSecondaryRole() != DRelation::VisualSecondaryRole::SecondaryRoleNone) + relationVisuals.setVisualSecondaryRole(styledRelation.relation()->visualSecondaryRole()); + if (styledRelation.relation()->isVisualEmphasized()) + relationVisuals.setEmphasized(styledRelation.relation()->isVisualEmphasized()); + } + RelationStyleKey key(elementType, relationVisuals, true); const Style *derivedStyle = m_relationStyleMap.value(key); if (!derivedStyle) { auto style = new Style(baseStyle->type()); @@ -365,12 +432,36 @@ const Style *DefaultStyleEngine::applyRelationStyle(const Style *baseStyle, cons QColor lineColor = DefaultStyleEngine::lineColor(objectType(object), objectVisuals); QColor fillColor = lineColor; + static QColor customColors[] = { + QColor(0xEE, 0x8E, 0x99).darker(110), // ROLE_CUSTOM1, + QColor(0x80, 0xAF, 0x47).lighter(130), // ROLE_CUSTOM2, + QColor(0xFF, 0xA1, 0x5B).lighter(100), // ROLE_CUSTOM3, + QColor(0x55, 0xC4, 0xCF).lighter(120), // ROLE_CUSTOM4, + QColor(0xFF, 0xE1, 0x4B) // ROLE_CUSTOM5, + }; + + int index = static_cast<int>(relationVisuals.visualPrimaryRole()) - static_cast<int>(DRelation::PrimaryRoleCustom1); + lineColor = (index >= 0 && index <= 4) ? customColors[index] : lineColor; + switch (relationVisuals.visualSecondaryRole()) { + case DRelation::SecondaryRoleNone: + break; + case DRelation::SecondaryRoleWarning: + lineColor = QColor(0xffc800); + break; + case DRelation::SecondaryRoleError: + lineColor = Qt::red; + break; + case DRelation::SecondaryRoleSoften: + lineColor = Qt::gray; + break; + } + QPen linePen = baseStyle->linePen(); - linePen.setWidth(1); + linePen.setWidth(relationVisuals.isEmphasized() ? 3 : 1); linePen.setColor(lineColor); style->setLinePen(linePen); QBrush textBrush = baseStyle->textBrush(); - textBrush.setColor(QColor("black")); + textBrush.setColor(Qt::black); style->setTextBrush(textBrush); QBrush brush = baseStyle->fillBrush(); brush.setColor(fillColor); @@ -497,6 +588,34 @@ DefaultStyleEngine::ElementType DefaultStyleEngine::objectType(const DObject *ob return elementType; } +bool DefaultStyleEngine::areStackingRoles(DObject::VisualPrimaryRole rhsPrimaryRole, + DObject::VisualSecondaryRole rhsSecondaryRole, + DObject::VisualPrimaryRole lhsPrimaryRole, + DObject::VisualSecondaryRole lhsSecondaryRols) +{ + switch (rhsSecondaryRole) { + case DObject::SecondaryRoleNone: + case DObject::SecondaryRoleLighter: + case DObject::SecondaryRoleDarker: + case DObject::SecondaryRoleFlat: + switch (lhsSecondaryRols) { + case DObject::SecondaryRoleNone: + case DObject::SecondaryRoleLighter: + case DObject::SecondaryRoleDarker: + case DObject::SecondaryRoleFlat: + return lhsPrimaryRole == rhsPrimaryRole; + case DObject::SecondaryRoleSoften: + case DObject::SecondaryRoleOutline: + return false; + } + break; + case DObject::SecondaryRoleSoften: + case DObject::SecondaryRoleOutline: + return false; + } + return true; +} + QColor DefaultStyleEngine::baseColor(ElementType elementType, ObjectVisuals objectVisuals) { if (objectVisuals.visualSecondaryRole() == DObject::SecondaryRoleOutline) diff --git a/src/libs/modelinglib/qmt/style/defaultstyleengine.h b/src/libs/modelinglib/qmt/style/defaultstyleengine.h index 67cdf9ed85..b5ebe557e0 100644 --- a/src/libs/modelinglib/qmt/style/defaultstyleengine.h +++ b/src/libs/modelinglib/qmt/style/defaultstyleengine.h @@ -37,6 +37,9 @@ public: const Parameters *parameters) override; const Style *applyObjectStyle(const Style *baseStyle, const StyledObject &styledObject, const Parameters *parameters) override; + const Style *applyRelationStyle(const Style *baseStyle, ElementType elementType, + const RelationVisuals &relationVisuals, + const Parameters *parameters) override; const Style *applyRelationStyle(const Style *baseStyle, const StyledRelation &styledRelation, const Parameters *parameters) override; const Style *applyAnnotationStyle(const Style *baseStyle, const DAnnotation *annotation, @@ -54,6 +57,11 @@ private: ElementType objectType(const DObject *object); + bool areStackingRoles(DObject::VisualPrimaryRole rhsPrimaryRole, + DObject::VisualSecondaryRole rhsSecondaryRole, + DObject::VisualPrimaryRole lhsPrimaryRole, + DObject::VisualSecondaryRole lhsSecondaryRols); + QColor baseColor(ElementType elementType, ObjectVisuals objectVisuals); QColor lineColor(ElementType elementType, const ObjectVisuals &objectVisuals); QColor fillColor(ElementType elementType, const ObjectVisuals &objectVisuals); diff --git a/src/libs/modelinglib/qmt/style/relationvisuals.cpp b/src/libs/modelinglib/qmt/style/relationvisuals.cpp new file mode 100644 index 0000000000..d297047ae1 --- /dev/null +++ b/src/libs/modelinglib/qmt/style/relationvisuals.cpp @@ -0,0 +1,58 @@ +// Copyright (C) 2024 Jochen Becher +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include "relationvisuals.h" + +namespace qmt { + +RelationVisuals::RelationVisuals() {} + +RelationVisuals::RelationVisuals(DObject::VisualPrimaryRole visualObjectPrimaryRole, + DRelation::VisualPrimaryRole visualPrimaryRole, + DRelation::VisualSecondaryRole visualSecondaryRole, + bool emphasized) + : m_visualObjectPrimaryRole(visualObjectPrimaryRole) + , m_visualPrimaryRole(visualPrimaryRole) + , m_visualSecondaryRole(visualSecondaryRole) + , m_isEmphasized(emphasized) +{} + +RelationVisuals::~RelationVisuals() {} + +void RelationVisuals::setVisualPrimaryRole(DRelation::VisualPrimaryRole VisualPrimaryRole) +{ + m_visualPrimaryRole = VisualPrimaryRole; +} + +void RelationVisuals::setVisualObjectPrimaryRole(DObject::VisualPrimaryRole visualPrimaryRole) +{ + m_visualObjectPrimaryRole = visualPrimaryRole; +} + +void RelationVisuals::setVisualSecondaryRole(DRelation::VisualSecondaryRole visualSecondaryRole) +{ + m_visualSecondaryRole = visualSecondaryRole; +} + +void RelationVisuals::setEmphasized(bool emphasized) +{ + m_isEmphasized = emphasized; +} + +bool operator==(const RelationVisuals &lhs, const RelationVisuals &rhs) +{ + return lhs.visualObjectPrimaryRole() == rhs.visualObjectPrimaryRole() + && lhs.visualPrimaryRole() == rhs.visualPrimaryRole() + && lhs.visualSecondaryRole() == rhs.visualSecondaryRole() + && lhs.isEmphasized() == rhs.isEmphasized(); +} + +size_t qHash(const RelationVisuals &relationVisuals) +{ + return ::qHash(static_cast<int>(relationVisuals.visualObjectPrimaryRole())) + ^ ::qHash(static_cast<int>(relationVisuals.visualPrimaryRole())) + ^ ::qHash(static_cast<int>(relationVisuals.visualSecondaryRole())) + ^ ::qHash(relationVisuals.isEmphasized()); +} + +} // namespace qmt diff --git a/src/libs/modelinglib/qmt/style/relationvisuals.h b/src/libs/modelinglib/qmt/style/relationvisuals.h new file mode 100644 index 0000000000..be87a7d291 --- /dev/null +++ b/src/libs/modelinglib/qmt/style/relationvisuals.h @@ -0,0 +1,42 @@ +// Copyright (C) 2024 Jochen Becher +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#pragma once + +#include "qmt/diagram/drelation.h" +#include "qmt/diagram/dobject.h" + +#include <QColor> + +namespace qmt { + +class QMT_EXPORT RelationVisuals +{ +public: + RelationVisuals(); + RelationVisuals(DObject::VisualPrimaryRole visualObjectPrimaryRole, + DRelation::VisualPrimaryRole visualPrimaryRole, + DRelation::VisualSecondaryRole visualSecondaryRole, + bool emphasized); + ~RelationVisuals(); + + DObject::VisualPrimaryRole visualObjectPrimaryRole() const { return m_visualObjectPrimaryRole; } + void setVisualObjectPrimaryRole(DObject::VisualPrimaryRole visualPrimaryRole); + DRelation::VisualPrimaryRole visualPrimaryRole() const { return m_visualPrimaryRole; } + void setVisualPrimaryRole(DRelation::VisualPrimaryRole visualPrimaryRole); + DRelation::VisualSecondaryRole visualSecondaryRole() const { return m_visualSecondaryRole; } + void setVisualSecondaryRole(DRelation::VisualSecondaryRole visualSecondaryRole); + bool isEmphasized() const { return m_isEmphasized; } + void setEmphasized(bool emphasized); + +private: + DObject::VisualPrimaryRole m_visualObjectPrimaryRole = DObject::PrimaryRoleNormal; + DRelation::VisualPrimaryRole m_visualPrimaryRole = DRelation::PrimaryRoleNormal; + DRelation::VisualSecondaryRole m_visualSecondaryRole = DRelation::SecondaryRoleNone; + bool m_isEmphasized = false; +}; + +bool operator==(const RelationVisuals &lhs, const RelationVisuals &rhs); +size_t qHash(const RelationVisuals &relationVisuals); + +} // namespace qmt diff --git a/src/libs/modelinglib/qmt/style/stylecontroller.cpp b/src/libs/modelinglib/qmt/style/stylecontroller.cpp index 5cd50c1738..bbfc28622e 100644 --- a/src/libs/modelinglib/qmt/style/stylecontroller.cpp +++ b/src/libs/modelinglib/qmt/style/stylecontroller.cpp @@ -63,6 +63,12 @@ const Style *StyleController::adaptObjectStyle(const StyledObject &object) return m_defaultStyleEngine->applyObjectStyle(m_defaultStyle.data(), object, ¶meters); } +const Style *StyleController::adaptRelationStyle(StyleEngine::ElementType elementType, const RelationVisuals &relationVisuals) +{ + Parameters parameters(this); + return m_defaultStyleEngine->applyRelationStyle(m_defaultStyle.data(), elementType, relationVisuals, ¶meters); +} + const Style *StyleController::adaptRelationStyle(const StyledRelation &relation) { Parameters parameters(this); diff --git a/src/libs/modelinglib/qmt/style/stylecontroller.h b/src/libs/modelinglib/qmt/style/stylecontroller.h index 2627bb2caa..afa566460f 100644 --- a/src/libs/modelinglib/qmt/style/stylecontroller.h +++ b/src/libs/modelinglib/qmt/style/stylecontroller.h @@ -4,7 +4,6 @@ #pragma once #include "styleengine.h" -#include "qmt/diagram/dobject.h" #include <QObject> #include <QScopedPointer> @@ -14,6 +13,7 @@ namespace qmt { class Style; class ObjectVisuals; class StyledObject; +class RelationVisuals; class StyledRelation; class DAnnotation; class DBoundary; @@ -33,6 +33,8 @@ public: const Style *adaptObjectStyle(StyleEngine::ElementType elementType, const ObjectVisuals &objectVisuals); const Style *adaptObjectStyle(const StyledObject &object); + const Style *adaptRelationStyle(StyleEngine::ElementType elementType, + const RelationVisuals &relationVisuals); const Style *adaptRelationStyle(const StyledRelation &relation); const Style *adaptAnnotationStyle(const DAnnotation *annotation); const Style *adaptBoundaryStyle(const DBoundary *boundary); diff --git a/src/libs/modelinglib/qmt/style/styledrelation.cpp b/src/libs/modelinglib/qmt/style/styledrelation.cpp index 3c78abf96d..144e5f22ac 100644 --- a/src/libs/modelinglib/qmt/style/styledrelation.cpp +++ b/src/libs/modelinglib/qmt/style/styledrelation.cpp @@ -5,10 +5,12 @@ namespace qmt { -StyledRelation::StyledRelation(const DRelation *relation, const DObject *endA, const DObject *endB) +StyledRelation::StyledRelation(const DRelation *relation, const DObject *endA, const DObject *endB, + const CustomRelation *customRelation) : m_relation(relation), m_endA(endA), - m_endB(endB) + m_endB(endB), + m_customRelation(customRelation) { } diff --git a/src/libs/modelinglib/qmt/style/styledrelation.h b/src/libs/modelinglib/qmt/style/styledrelation.h index 422947908e..27e8735463 100644 --- a/src/libs/modelinglib/qmt/style/styledrelation.h +++ b/src/libs/modelinglib/qmt/style/styledrelation.h @@ -9,21 +9,25 @@ namespace qmt { class DRelation; class DObject; +class CustomRelation; class QMT_EXPORT StyledRelation { public: - StyledRelation(const DRelation *relation, const DObject *endA, const DObject *endB); + StyledRelation(const DRelation *relation, const DObject *endA, const DObject *endB, + const CustomRelation *customRelation); ~StyledRelation(); const DRelation *relation() const { return m_relation; } const DObject *endA() const { return m_endA; } const DObject *endB() const { return m_endB; } + const CustomRelation *customRelation() const { return m_customRelation; } private: const DRelation *m_relation = nullptr; const DObject *m_endA = nullptr; const DObject *m_endB = nullptr; + const CustomRelation *m_customRelation = nullptr; }; } // namespace qmt diff --git a/src/libs/modelinglib/qmt/style/styleengine.h b/src/libs/modelinglib/qmt/style/styleengine.h index e78f4303d9..463a62a194 100644 --- a/src/libs/modelinglib/qmt/style/styleengine.h +++ b/src/libs/modelinglib/qmt/style/styleengine.h @@ -14,6 +14,7 @@ namespace qmt { class Style; class ObjectVisuals; class StyledObject; +class RelationVisuals; class StyledRelation; class DAnnotation; @@ -51,6 +52,9 @@ public: const Parameters *parameters) = 0; virtual const Style *applyObjectStyle(const Style *baseStyle, const StyledObject &, const Parameters *) = 0; + virtual const Style *applyRelationStyle(const Style *baseStyle, ElementType elementType, + const RelationVisuals &relationVisuals, + const Parameters *) = 0; virtual const Style *applyRelationStyle(const Style *baseStyle, const StyledRelation &, const Parameters *) = 0; virtual const Style *applyAnnotationStyle(const Style *baseStyle, const DAnnotation *, diff --git a/src/libs/modelinglib/qmt/tasks/diagramscenecontroller.cpp b/src/libs/modelinglib/qmt/tasks/diagramscenecontroller.cpp index 00aacf9c82..23f6ddc297 100644 --- a/src/libs/modelinglib/qmt/tasks/diagramscenecontroller.cpp +++ b/src/libs/modelinglib/qmt/tasks/diagramscenecontroller.cpp @@ -42,8 +42,6 @@ #include "../../modelinglibtr.h" #include <QMenu> -#include <QFileInfo> -#include <QDir> #include <QQueue> #include <QPair> @@ -421,7 +419,44 @@ void DiagramSceneController::dropNewModelElement(MObject *modelObject, MPackage emit newElementCreated(element, diagram); } -void DiagramSceneController::addRelatedElements(const DSelection &selection, MDiagram *diagram) +int DiagramSceneController::countRelatedElements(const DSelection &selection, MDiagram *diagram, std::function<bool (qmt::DObject *, qmt::MObject *, qmt::MRelation *)> filter) +{ + int counter = 0; + const QList<DSelection::Index> indices = selection.indices(); + for (const DSelection::Index &index : indices) { + DElement *delement = m_diagramController->findElement(index.elementKey(), diagram); + QMT_ASSERT(delement, return 0); + DObject *dobject = dynamic_cast<DObject *>(delement); + if (dobject && dobject->modelUid().isValid()) { + MObject *mobject = m_modelController->findElement<MObject>(delement->modelUid()); + if (mobject) { + const QList<MRelation *> relations = m_modelController->findRelationsOfObject(mobject); + QList<MRelation *> filteredRelations; + const QList<MRelation *> *relationsList = nullptr; + if (filter) { + for (MRelation *relation : relations) { + if (filter(dobject, mobject, relation)) + filteredRelations.append(relation); + } + relationsList = &filteredRelations; + } else { + relationsList = &relations; + } + for (MRelation *relation : *relationsList) { + if (relation->endAUid() != mobject->uid()) + ++counter; + else if (relation->endBUid() != mobject->uid()) + ++counter; + } + } + } + } + return counter; +} + +void DiagramSceneController::addRelatedElements( + const DSelection &selection, MDiagram *diagram, + std::function<bool (qmt::DObject *dobject, qmt::MObject *mobject, qmt::MRelation *relation)> filter) { m_diagramController->undoController()->beginMergeSequence(Tr::tr("Add Related Element")); const QList<DSelection::Index> indices = selection.indices(); @@ -435,8 +470,19 @@ void DiagramSceneController::addRelatedElements(const DSelection &selection, MDi qreal dAngle = 360.0 / 11.5; qreal dRadius = 100.0; const QList<MRelation *> relations = m_modelController->findRelationsOfObject(mobject); + QList<MRelation *> filteredRelations; + const QList<MRelation *> *relationsList = nullptr; + if (filter) { + for (MRelation *relation : relations) { + if (filter(dobject, mobject, relation)) + filteredRelations.append(relation); + } + relationsList = &filteredRelations; + } else { + relationsList = &relations; + } int count = 0; - for (MRelation *relation : relations) { + for (MRelation *relation : *relationsList) { if (relation->endAUid() != mobject->uid() || relation->endBUid() != mobject->uid()) ++count; } @@ -446,7 +492,7 @@ void DiagramSceneController::addRelatedElements(const DSelection &selection, MDi } qreal radius = 200.0; qreal angle = 0.0; - for (MRelation *relation : relations) { + for (MRelation *relation : *relationsList) { QPointF pos(dobject->pos()); pos += QPointF(radius * sin(angle / 180 * M_PI), -radius * cos(angle / 180 * M_PI)); bool added = false; diff --git a/src/libs/modelinglib/qmt/tasks/diagramscenecontroller.h b/src/libs/modelinglib/qmt/tasks/diagramscenecontroller.h index a062fa10c4..14e704a192 100644 --- a/src/libs/modelinglib/qmt/tasks/diagramscenecontroller.h +++ b/src/libs/modelinglib/qmt/tasks/diagramscenecontroller.h @@ -86,7 +86,12 @@ public: DElement *topMostElementAtPos, const QPointF &pos, MDiagram *diagram, const QPoint &viewPos, const QSize &viewSize); void dropNewModelElement(MObject *modelObject, MPackage *parentPackage, const QPointF &pos, MDiagram *diagram); - void addRelatedElements(const DSelection &selection, MDiagram *diagram); + int countRelatedElements( + const DSelection &selection, MDiagram *diagram, + std::function<bool (qmt::DObject *dobject, qmt::MObject *mobject, qmt::MRelation *relation)> filter); + void addRelatedElements( + const DSelection &selection, MDiagram *diagram, + std::function<bool (qmt::DObject *dobject, qmt::MObject *mobject, qmt::MRelation *relation)> filter); MPackage *findSuitableParentPackage(DElement *topmostDiagramElement, MDiagram *diagram); MDiagram *findDiagramBySearchId(MPackage *package, const QString &diagramName); diff --git a/src/libs/modelinglib/qmt/tasks/ielementtasks.h b/src/libs/modelinglib/qmt/tasks/ielementtasks.h index a40277d874..8c22632762 100644 --- a/src/libs/modelinglib/qmt/tasks/ielementtasks.h +++ b/src/libs/modelinglib/qmt/tasks/ielementtasks.h @@ -59,8 +59,13 @@ public: virtual void createAndOpenDiagram(const MElement *) = 0; virtual void createAndOpenDiagram(const DElement *, const MDiagram *) = 0; + virtual bool hasLinkedFile(const MElement *) const = 0; + virtual bool hasLinkedFile(const DElement *, const MDiagram *) const = 0; + virtual void openLinkedFile(const MElement *) = 0; + virtual void openLinkedFile(const DElement *, const MDiagram *) = 0; + virtual bool extendContextMenu(const DElement *, const MDiagram *, QMenu *) = 0; - virtual bool handleContextMenuAction(const DElement *, const MDiagram *, const QString &) = 0; + virtual bool handleContextMenuAction(DElement *, MDiagram *, const QString &) = 0; }; } // namespace qmt diff --git a/src/libs/modelinglib/qmt/tasks/voidelementtasks.cpp b/src/libs/modelinglib/qmt/tasks/voidelementtasks.cpp index 2bd7fbe049..fdc2e24bd4 100644 --- a/src/libs/modelinglib/qmt/tasks/voidelementtasks.cpp +++ b/src/libs/modelinglib/qmt/tasks/voidelementtasks.cpp @@ -147,12 +147,30 @@ void VoidElementTasks::createAndOpenDiagram(const DElement *, const MDiagram *) { } +bool VoidElementTasks::hasLinkedFile(const MElement *) const +{ + return false; +} + +bool VoidElementTasks::hasLinkedFile(const DElement *, const MDiagram *) const +{ + return false; +} + +void VoidElementTasks::openLinkedFile(const MElement *) +{ +} + +void VoidElementTasks::openLinkedFile(const DElement *, const MDiagram *) +{ +} + bool VoidElementTasks::extendContextMenu(const DElement *, const MDiagram *, QMenu *) { return false; } -bool VoidElementTasks::handleContextMenuAction(const DElement *, const MDiagram *, const QString &) +bool VoidElementTasks::handleContextMenuAction(DElement *, MDiagram *, const QString &) { return false; } diff --git a/src/libs/modelinglib/qmt/tasks/voidelementtasks.h b/src/libs/modelinglib/qmt/tasks/voidelementtasks.h index 3e3d483d9a..9e8753b71e 100644 --- a/src/libs/modelinglib/qmt/tasks/voidelementtasks.h +++ b/src/libs/modelinglib/qmt/tasks/voidelementtasks.h @@ -51,8 +51,13 @@ public: void createAndOpenDiagram(const MElement *) override; void createAndOpenDiagram(const DElement *, const MDiagram *) override; + bool hasLinkedFile(const qmt::MElement *) const override; + bool hasLinkedFile(const qmt::DElement *, const qmt::MDiagram *) const override; + void openLinkedFile(const qmt::MElement *) override; + void openLinkedFile(const qmt::DElement *, const qmt::MDiagram *) override; + bool extendContextMenu(const DElement *, const MDiagram *, QMenu *) override; - bool handleContextMenuAction(const DElement *, const MDiagram *, const QString &) override; + bool handleContextMenuAction(DElement *, MDiagram *, const QString &) override; }; } // namespace qmt |