ModelEditor: Support custom relations in configuration files

Change-Id: I87338f290bd1ea729682236df8b017516a18e7bb
Reviewed-by: Tobias Hunger <tobias.hunger@qt.io>
This commit is contained in:
Jochen Becher
2016-06-12 20:07:15 +02:00
parent 42844215ca
commit 8100812531
21 changed files with 1201 additions and 95 deletions

View File

@@ -2,6 +2,9 @@
//
// Language syntax and commands:
//
//
// An Icon defines an icon of an element selected by stereotype.
//
// Icon {
// id: <id>
// title: <a UI title. Defaults to the id of the icon.>
@@ -29,6 +32,8 @@
// RoundedRect { x: <x>; y: <y>; width: <width>; height: <height>; radius: <radius> }
// Circle { x: <center_x>; y: <center_y>; radius: <radius> }
// Ellipse { x: <center_x>; y: <center_y>; radiusX: <radius_x>; radiusY: <radius_y> }
// Diamond { x: <center_x>; y: <center_y>; width: <width>; height: <height; filled: <yes or no or true or false> }
// Triangle { x: <center_x>; y: <center_y>; width: <width>; height: <height; filled: <yes or no or true or false> }
// Arc { x: <center_x>; y: <center_y>; radiusX: <radius_x>; radiusY: <radius_y>; start: <start_angle>; span: <span_angle> }
// MoveTo { x: <x>; y: <y> }
// LineTo { x: <x>; y: <y> }
@@ -38,6 +43,79 @@
// }
// }
//
//
// A Relation defines a new relation between to items:
//
// Relation {
// id: <id>
// title: <a UI title. Defaults to the id of the icon.>
// elements: <A list of elements that may be the start or end element of the relation. Can be one of
// class, component, package, diagram, item or any id of an Icon definition. Must be given.>
// stereotype: <Stereotype as a string. Defaults to nothing.>
// name: <Name of the relation. Defaults to nothing.>
// direction: <One of AtoB, BtoA or Bi. Defaults to nothing.>
// pattern: <The pattern used for the relation shaft.
// One of solid, dash, dot, dashdot, dashdotdot. Defaults to solid>
// color: <The color of the relation. One of A or B (uses the color of the respective end element) or
// any valid color (#rrggbb or a color name). Defaults to A.>
// End {
// end: <One of A or B. Defines the settings of the relations end.>
// elements: <Overrides the elements property of parent.>
// role: <The role of the end. A string that defaults to nothing.>
// cardinality: <An integer or a string defining the cardinality of the end. Defaults to nothing.>
// navigable: <If the end is navigable. One of Yes, No, True, False. Defaults to nothing.>
// Shape {
// }
// }
// }
//
//
// A Dependency defines a number of settings of a dependency specialization:
//
// Dependency {
// id: <id>
// title: <a UI title. Defaults to the id of the icon.>
// elements: <A list of elements that provides this specialization in its menu. Can be one of
// class, component, package, diagram, item or any id of an Icon definition. Must be given.>
// stereotype: <Stereotype as a string. Defaults to nothing.>
// name: <Name of the relation. Defaults to nothing.>
// direction: <One of AtoB, BtoA or Bi. Defaults to nothing.>
// }
//
//
// An Inheritance defines settings of an inheritance specialization:
//
// Inheritance {
// id: <id>
// title: <a UI title. Defaults to the id of the icon.>
// elements: <A list of elements that provides this specialization in its menu. Can be one of
// class or any id of an Icon definition for classes. Defaults to class.>
// stereotype: <Stereotype as a string. Defaults to nothing.>
// name: <Name of the relation. Defaults to nothing.>
// }
//
//
// An Association defines settings of an association specialization:
//
// Association {
// id: <id>
// title: <a UI title. Defaults to the id of the icon.>
// elements: <A list of elements that provides this specialization in its menu. Can be one of
// class or any id of an Icon definition for classes. Defaults to class.>
// stereotype: <Stereotype as a string. Defaults to nothing.>
// name: <Name of the relation. Defaults to nothing.>
// End {
// end: <One of A or B. Defines the settings of the relations end.>
// role: <The role of the end. A string that defaults to nothing.>
// cardinality: <An integer or a string defining the cardinality of the end. Defaults to nothing.>
// navigable: <If the end is navigable. One of Yes, No, True, False. Defaults to nothing.>
// relationship: <One of Association, Aggregation, Composition. Defaults to Association.>
// }
// }
//
//
// A toolbar of icons. If no toolbar is defined the standard toolbar is shown.
//
// Toolbar {
// id: <id>
// title: <a Ui title. Defaults to the id of the toolbar>
@@ -104,6 +182,54 @@ Icon {
}
}
Association {
id: AggregationOne
title: "Aggregation (?:1)"
End {
end: A
cardinality: 1
navigable: yes
relationship: aggregation
}
}
Association {
id: AggregationMany
title: "Aggregation (?:N)"
End {
end: A
cardinality: "*"
navigable: yes
relationship: aggregation
}
}
// ********************
// ** Class Toolbars **
// ********************
Toolbar {
id: ClassToolbar
element: class
Tools {
Tool { element: dependency }
Tool { element: inheritance }
Tool { element: association }
Tool { element: AggregationOne }
Tool { element: AggregationMany }
}
}
Toolbar {
id: InterfaceToolbar
element: Interface
Tools {
Tool { element: dependency }
Tool { element: inheritance }
}
}
// ****************
// ** Components **
// ****************
@@ -276,6 +402,34 @@ Icon {
}
}
Relation {
id: Controlflow
pattern: solid
color: A
End {
end: A
elements: Start, Activity, Condition, HorizontalBar, VerticalBar
role: ""
}
End {
end: B
elements: Activity, Condition, HorizontalBar, VerticalBar, Termination
head: filledtriangle
//Shape {
// Triangle { x: 6; y: 5.2; width: 12; height: 10.4; filled: yes }
//}
}
}
Toolbar {
id: ActivityToolbar
element: Start, Activity, Condition, HorizontalBar, VerticalBar
Tools {
Tool { element: Controlflow }
Tool { element: dependency }
}
}
// **************
// ** Toolbars **
// **************

View File

@@ -252,6 +252,8 @@ QtcLibrary {
"serializer/modelserializer.h",
"serializer/projectserializer.cpp",
"serializer/projectserializer.h",
"stereotype/customrelation.cpp",
"stereotype/customrelation.h",
"stereotype/iconshape.cpp",
"stereotype/iconshape.h",
"stereotype/shape.h",

View File

@@ -71,6 +71,8 @@ void ConfigController::readStereotypeDefinitions(const QString &path)
StereotypeDefinitionParser parser;
connect(&parser, &StereotypeDefinitionParser::iconParsed,
this, &ConfigController::onStereotypeIconParsed);
connect(&parser, &StereotypeDefinitionParser::relationParsed,
this, &ConfigController::onRelationParsed);
connect(&parser, &StereotypeDefinitionParser::toolbarParsed,
this, &ConfigController::onToolbarParsed);
@@ -116,6 +118,11 @@ void ConfigController::onStereotypeIconParsed(const StereotypeIcon &stereotypeIc
d->m_stereotypeController->addStereotypeIcon(stereotypeIcon);
}
void ConfigController::onRelationParsed(const CustomRelation &customRelation)
{
d->m_stereotypeController->addCustomRelation(customRelation);
}
void ConfigController::onToolbarParsed(const Toolbar &toolbar)
{
d->m_stereotypeController->addToolbar(toolbar);

View File

@@ -30,6 +30,7 @@
namespace qmt {
class CustomRelation;
class StereotypeController;
class StereotypeIcon;
class Toolbar;
@@ -49,6 +50,7 @@ public:
private:
void onStereotypeIconParsed(const StereotypeIcon &stereotypeIcon);
void onRelationParsed(const CustomRelation &customRelation);
void onToolbarParsed(const Toolbar &toolbar);
ConfigControllerPrivate *d;

View File

@@ -30,6 +30,7 @@
#include "qmt/infrastructure/qmtassert.h"
#include "qmt/stereotype/stereotypeicon.h"
#include "qmt/stereotype/shapevalue.h"
#include "qmt/stereotype/customrelation.h"
#include "qmt/stereotype/toolbar.h"
#include <QHash>
@@ -89,6 +90,40 @@ static const int KEYWORD_TOOL = 73;
static const int KEYWORD_ELEMENT = 74;
static const int KEYWORD_SEPARATOR = 75;
// Relation Definition
static const int KEYWORD_RELATION = 100;
static const int KEYWORD_DEPENDENCY = 101;
static const int KEYWORD_INHERITANCE = 102;
static const int KEYWORD_ASSOCIATION = 103;
static const int KEYWORD_NAME = 104;
static const int KEYWORD_DIRECTION = 105;
static const int KEYWORD_ATOB = 106;
static const int KEYWORD_BTOA = 107;
static const int KEYWORD_BI = 108;
static const int KEYWORD_END = 109;
static const int KEYWORD_A = 110;
static const int KEYWORD_B = 111;
static const int KEYWORD_ROLE = 112;
static const int KEYWORD_CARDINALITY = 113;
static const int KEYWORD_NAVIGABLE = 114;
static const int KEYWORD_RELATIONSHIP = 115;
static const int KEYWORD_AGGREGATION = 116;
static const int KEYWORD_COMPOSITION = 117;
static const int KEYWORD_SHAFT = 118;
static const int KEYWORD_HEAD = 119;
// Relation Shapes
static const int KEYWORD_DIAMOND = 130;
static const int KEYWORD_TRIANGLE = 131;
static const int KEYWORD_FILLED = 132;
static const int KEYWORD_PATTERN = 133;
static const int KEYWORD_SOLID = 134;
static const int KEYWORD_DOT = 135;
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 OPERATOR_SEMICOLON = 1;
static const int OPERATOR_BRACE_OPEN = 2;
@@ -124,18 +159,61 @@ public:
class StereotypeDefinitionParser::IconCommandParameter
{
public:
enum Type {
ShapeValue,
Boolean
};
IconCommandParameter() = default;
IconCommandParameter(int keyword, ShapeValueF::Unit unit, ShapeValueF::Origin origin = ShapeValueF::OriginSmart)
: m_keyword(keyword),
m_type(ShapeValue),
m_unit(unit),
m_origin(origin)
{
}
IconCommandParameter(int keyword, Type type)
: m_keyword(keyword),
m_type(type)
{
}
operator ShapeValueF() const { return m_shapeValue; }
Type type() const { return m_type; }
ShapeValueF::Unit unit() const { return m_unit; }
ShapeValueF::Origin origin() const { return m_origin; }
ShapeValueF shapeValue() const { return m_shapeValue; }
void setShapeValue(const ShapeValueF &shapeValue) { m_shapeValue = shapeValue; }
bool boolean() const { return m_boolean; }
void setBoolean(bool boolean) { m_boolean = boolean; }
private:
int m_keyword = -1;
Type m_type = ShapeValue;
ShapeValueF::Unit m_unit = ShapeValueF::UnitAbsolute;
ShapeValueF::Origin m_origin = ShapeValueF::OriginCenter;
ShapeValueF::Origin m_origin = ShapeValueF::OriginSmart;
ShapeValueF m_shapeValue;
bool m_boolean = false;
};
class StereotypeDefinitionParser::Value
{
public:
Value(StereotypeDefinitionParser::Type type, QVariant value)
: m_type(type),
m_value(value)
{
}
StereotypeDefinitionParser::Type type() const { return m_type; }
QVariant value() const { return m_value; }
private:
StereotypeDefinitionParser::Type m_type = StereotypeDefinitionParser::Void;
QVariant m_value;
};
StereotypeDefinitionParser::StereotypeDefinitionParser(QObject *parent)
@@ -196,6 +274,36 @@ void StereotypeDefinitionParser::parse(ITextSource *source)
<< qMakePair(QString(QStringLiteral("tool")), KEYWORD_TOOL)
<< qMakePair(QString(QStringLiteral("element")), KEYWORD_ELEMENT)
<< qMakePair(QString(QStringLiteral("separator")), KEYWORD_SEPARATOR)
<< qMakePair(QString(QStringLiteral("relation")), KEYWORD_RELATION)
<< qMakePair(QString(QStringLiteral("dependency")), KEYWORD_DEPENDENCY)
<< qMakePair(QString(QStringLiteral("inheritance")), KEYWORD_INHERITANCE)
<< qMakePair(QString(QStringLiteral("association")), KEYWORD_ASSOCIATION)
<< qMakePair(QString(QStringLiteral("name")), KEYWORD_NAME)
<< qMakePair(QString(QStringLiteral("direction")), KEYWORD_DIRECTION)
<< qMakePair(QString(QStringLiteral("atob")), KEYWORD_ATOB)
<< qMakePair(QString(QStringLiteral("btoa")), KEYWORD_BTOA)
<< qMakePair(QString(QStringLiteral("bi")), KEYWORD_BI)
<< qMakePair(QString(QStringLiteral("end")), KEYWORD_END)
<< qMakePair(QString(QStringLiteral("a")), KEYWORD_A)
<< qMakePair(QString(QStringLiteral("b")), KEYWORD_B)
<< qMakePair(QString(QStringLiteral("role")), KEYWORD_ROLE)
<< qMakePair(QString(QStringLiteral("cardinality")), KEYWORD_CARDINALITY)
<< qMakePair(QString(QStringLiteral("navigable")), KEYWORD_NAVIGABLE)
<< qMakePair(QString(QStringLiteral("relationship")), KEYWORD_RELATIONSHIP)
<< qMakePair(QString(QStringLiteral("aggregation")), KEYWORD_AGGREGATION)
<< qMakePair(QString(QStringLiteral("composition")), KEYWORD_COMPOSITION)
<< qMakePair(QString(QStringLiteral("shaft")), KEYWORD_SHAFT)
<< qMakePair(QString(QStringLiteral("head")), KEYWORD_HEAD)
<< qMakePair(QString(QStringLiteral("diamond")), KEYWORD_DIAMOND)
<< qMakePair(QString(QStringLiteral("triangle")), KEYWORD_TRIANGLE)
<< qMakePair(QString(QStringLiteral("filled")), KEYWORD_FILLED)
<< qMakePair(QString(QStringLiteral("pattern")), KEYWORD_PATTERN)
<< qMakePair(QString(QStringLiteral("solid")), KEYWORD_SOLID)
<< qMakePair(QString(QStringLiteral("dot")), KEYWORD_DOT)
<< qMakePair(QString(QStringLiteral("dash")), KEYWORD_DASH)
<< qMakePair(QString(QStringLiteral("dashdot")), KEYWORD_DASHDOT)
<< qMakePair(QString(QStringLiteral("dashdotdot")), KEYWORD_DASHDOTDOT)
<< qMakePair(QString(QStringLiteral("color")), KEYWORD_COLOR)
);
textScanner.setOperators(
QList<QPair<QString, int> >()
@@ -229,8 +337,16 @@ void StereotypeDefinitionParser::parseFile()
parseIcon();
else if (token.type() == Token::TokenKeyword && token.subtype() == KEYWORD_TOOLBAR)
parseToolbar();
else if (token.type() == Token::TokenKeyword && token.subtype() == KEYWORD_RELATION)
parseRelation(CustomRelation::Element::Relation);
else if (token.type() == Token::TokenKeyword && token.subtype() == KEYWORD_DEPENDENCY)
parseRelation(CustomRelation::Element::Dependency);
else if (token.type() == Token::TokenKeyword && token.subtype() == KEYWORD_INHERITANCE)
parseRelation(CustomRelation::Element::Inheritance);
else if (token.type() == Token::TokenKeyword && token.subtype() == KEYWORD_ASSOCIATION)
parseRelation(CustomRelation::Element::Association);
else
throw StereotypeDefinitionParserError(QStringLiteral("Expected 'Icon' or 'Toolbar'."), token.sourcePos());
throw StereotypeDefinitionParserError(QStringLiteral("Expected 'Icon', 'Toolbar', 'Relation', 'Dependency', 'Inheritance' or 'Association'."), token.sourcePos());
}
}
@@ -251,19 +367,15 @@ void StereotypeDefinitionParser::parseIcon()
break;
case KEYWORD_ELEMENTS:
{
QList<QString> identifiers = parseIdentifierListProperty();
foreach (const QString &identifier, identifiers) {
static QHash<QString, StereotypeIcon::Element> elementNames = QHash<QString, StereotypeIcon::Element>()
<< qMakePair(QString(QStringLiteral("package")), StereotypeIcon::ElementPackage)
<< qMakePair(QString(QStringLiteral("component")), StereotypeIcon::ElementComponent)
<< qMakePair(QString(QStringLiteral("class")), StereotypeIcon::ElementClass)
<< qMakePair(QString(QStringLiteral("diagram")), StereotypeIcon::ElementDiagram)
<< qMakePair(QString(QStringLiteral("item")), StereotypeIcon::ElementItem);
QString elementName = identifier.toLower();
if (!elementNames.contains(elementName))
throw StereotypeDefinitionParserError(QString(QStringLiteral("Unexpected value \"%1\" for element.")).arg(identifier), token.sourcePos());
elements.insert(elementNames.value(elementName));
}
const static QHash<QString, StereotypeIcon::Element> elementNames = QHash<QString, StereotypeIcon::Element>()
<< qMakePair(QString(QStringLiteral("package")), StereotypeIcon::ElementPackage)
<< qMakePair(QString(QStringLiteral("component")), StereotypeIcon::ElementComponent)
<< qMakePair(QString(QStringLiteral("class")), StereotypeIcon::ElementClass)
<< qMakePair(QString(QStringLiteral("diagram")), StereotypeIcon::ElementDiagram)
<< qMakePair(QString(QStringLiteral("item")), StereotypeIcon::ElementItem);
parseEnums<StereotypeIcon::Element>(
parseIdentifierListProperty(), elementNames, token.sourcePos(),
[&](StereotypeIcon::Element element) { elements.insert(element); });
break;
}
case KEYWORD_STEREOTYPE:
@@ -283,61 +395,46 @@ void StereotypeDefinitionParser::parseIcon()
break;
case KEYWORD_LOCK_SIZE:
{
QString lockValue = parseIdentifierProperty();
QString lockName = lockValue.toLower();
static QHash<QString, StereotypeIcon::SizeLock> lockNames = QHash<QString, StereotypeIcon::SizeLock>()
const static QHash<QString, StereotypeIcon::SizeLock> lockNames = QHash<QString, StereotypeIcon::SizeLock>()
<< qMakePair(QString(QStringLiteral("none")), StereotypeIcon::LockNone)
<< qMakePair(QString(QStringLiteral("width")), StereotypeIcon::LockWidth)
<< qMakePair(QString(QStringLiteral("height")), StereotypeIcon::LockHeight)
<< qMakePair(QString(QStringLiteral("size")), StereotypeIcon::LockSize)
<< qMakePair(QString(QStringLiteral("ratio")), StereotypeIcon::LockRatio);
if (lockNames.contains(lockName)) {
StereotypeIcon::SizeLock sizeLock = lockNames.value(lockName);
stereotypeIcon.setSizeLock(sizeLock);
} else {
throw StereotypeDefinitionParserError(QString(QStringLiteral("Unexpected value \"%1\" for size lock.")).arg(lockValue), token.sourcePos());
}
parseEnum<StereotypeIcon::SizeLock>(
parseIdentifierProperty(), lockNames, token.sourcePos(),
[&](StereotypeIcon::SizeLock lock) { stereotypeIcon.setSizeLock(lock); });
break;
}
case KEYWORD_DISPLAY:
{
QString displayValue = parseIdentifierProperty();
QString displayName = displayValue.toLower();
static QHash<QString, StereotypeIcon::Display> displayNames = QHash<QString, StereotypeIcon::Display>()
const static QHash<QString, StereotypeIcon::Display> displayNames = QHash<QString, StereotypeIcon::Display>()
<< qMakePair(QString(QStringLiteral("none")), StereotypeIcon::DisplayNone)
<< qMakePair(QString(QStringLiteral("label")), StereotypeIcon::DisplayLabel)
<< qMakePair(QString(QStringLiteral("decoration")), StereotypeIcon::DisplayDecoration)
<< qMakePair(QString(QStringLiteral("icon")), StereotypeIcon::DisplayIcon)
<< qMakePair(QString(QStringLiteral("smart")), StereotypeIcon::DisplaySmart);
if (displayNames.contains(displayName)) {
StereotypeIcon::Display display = displayNames.value(displayName);
stereotypeIcon.setDisplay(display);
} else {
throw StereotypeDefinitionParserError(QString(QStringLiteral("Unexpected value \"%1\" for stereotype display.")).arg(displayValue), token.sourcePos());
}
parseEnum<StereotypeIcon::Display>(
parseIdentifierProperty(), displayNames, token.sourcePos(),
[&](StereotypeIcon::Display display) { stereotypeIcon.setDisplay(display); });
break;
}
case KEYWORD_TEXTALIGN:
{
QString alignValue = parseIdentifierProperty();
QString alignName = alignValue.toLower();
static QHash<QString, StereotypeIcon::TextAlignment> alignNames = QHash<QString, StereotypeIcon::TextAlignment>()
const static QHash<QString, StereotypeIcon::TextAlignment> alignNames = QHash<QString, StereotypeIcon::TextAlignment>()
<< qMakePair(QString(QStringLiteral("below")), StereotypeIcon::TextalignBelow)
<< qMakePair(QString(QStringLiteral("center")), StereotypeIcon::TextalignCenter)
<< qMakePair(QString(QStringLiteral("none")), StereotypeIcon::TextalignNone);
if (alignNames.contains(alignName)) {
StereotypeIcon::TextAlignment textAlignment = alignNames.value(alignName);
stereotypeIcon.setTextAlignment(textAlignment);
} else {
throw StereotypeDefinitionParserError(QString(QStringLiteral("Unexpected value \"%1\" for text alignment.")).arg(alignValue), token.sourcePos());
}
parseEnum<StereotypeIcon::TextAlignment>(
parseIdentifierProperty(), alignNames, token.sourcePos(),
[&](StereotypeIcon::TextAlignment align) { stereotypeIcon.setTextAlignment(align); });
break;
}
case KEYWORD_BASECOLOR:
stereotypeIcon.setBaseColor(parseColorProperty());
break;
case KEYWORD_SHAPE:
parseIconShape(&stereotypeIcon);
stereotypeIcon.setIconShape(parseIconShape());
break;
default:
throwUnknownPropertyError(token);
@@ -367,10 +464,15 @@ QPair<int, StereotypeDefinitionParser::IconCommandParameter> StereotypeDefinitio
return qMakePair(keyword, IconCommandParameter(keyword, ShapeValueF::UnitAbsolute));
}
void StereotypeDefinitionParser::parseIconShape(StereotypeIcon *stereotypeIcon)
QPair<int, StereotypeDefinitionParser::IconCommandParameter> StereotypeDefinitionParser::BOOLEAN(int keyword)
{
return qMakePair(keyword, IconCommandParameter(keyword, IconCommandParameter::Boolean));
}
IconShape StereotypeDefinitionParser::parseIconShape()
{
IconShape iconShape;
QHash<int, ShapeValueF> values;
QHash<int, IconCommandParameter> values;
typedef QHash<int, IconCommandParameter> Parameters;
expectBlockBegin();
Token token;
@@ -458,26 +560,50 @@ void StereotypeDefinitionParser::parseIconShape(StereotypeIcon *stereotypeIcon)
iconShape.closePath();
skipOptionalEmptyBlock();
break;
case KEYWORD_DIAMOND:
values = parseIconShapeProperties(
Parameters() << SCALED(KEYWORD_X) << SCALED(KEYWORD_Y)
<< SCALED(KEYWORD_WIDTH) << SCALED(KEYWORD_HEIGHT)
<< BOOLEAN(KEYWORD_FILLED));
iconShape.addDiamond(ShapePointF(values.value(KEYWORD_X), values.value(KEYWORD_Y)),
ShapeSizeF(values.value(KEYWORD_WIDTH), values.value(KEYWORD_HEIGHT)),
values.value(KEYWORD_FILLED).boolean());
break;
case KEYWORD_TRIANGLE:
values = parseIconShapeProperties(
Parameters() << SCALED(KEYWORD_X) << SCALED(KEYWORD_Y)
<< SCALED(KEYWORD_WIDTH) << SCALED(KEYWORD_HEIGHT)
<< BOOLEAN(KEYWORD_FILLED));
iconShape.addTriangle(ShapePointF(values.value(KEYWORD_X), values.value(KEYWORD_Y)),
ShapeSizeF(values.value(KEYWORD_WIDTH), values.value(KEYWORD_HEIGHT)),
values.value(KEYWORD_FILLED).boolean());
break;
default:
throwUnknownPropertyError(token);
}
if (!expectPropertySeparatorOrBlockEnd())
break;
}
stereotypeIcon->setIconShape(iconShape);
return iconShape;
}
QHash<int, ShapeValueF> StereotypeDefinitionParser::parseIconShapeProperties(const QHash<int, IconCommandParameter> &parameters)
QHash<int, StereotypeDefinitionParser::IconCommandParameter> StereotypeDefinitionParser::parseIconShapeProperties(const QHash<int, IconCommandParameter> &parameters)
{
expectBlockBegin();
QHash<int, ShapeValueF> values;
QHash<int, IconCommandParameter> values;
Token token;
while (readProperty(&token)) {
if (parameters.contains(token.subtype())) {
IconCommandParameter parameter = parameters.value(token.subtype());
if (values.contains(token.subtype()))
throw StereotypeDefinitionParserError(QStringLiteral("Property givent twice."), token.sourcePos());
values.insert(token.subtype(), ShapeValueF(parseFloatProperty(), parameter.m_unit, parameter.m_origin));
throw StereotypeDefinitionParserError(QStringLiteral("Property given twice."), token.sourcePos());
IconCommandParameter parameter = parameters.value(token.subtype());
if (parameter.type() == IconCommandParameter::ShapeValue)
parameter.setShapeValue(ShapeValueF(parseFloatProperty(), parameter.unit(), parameter.origin()));
else if (parameter.type() == IconCommandParameter::Boolean)
parameter.setBoolean(parseBoolProperty());
else
throw StereotypeDefinitionParserError("Unexpected type of property.", token.sourcePos());
values.insert(token.subtype(), parameter);
} else {
throwUnknownPropertyError(token);
}
@@ -491,6 +617,192 @@ QHash<int, ShapeValueF> StereotypeDefinitionParser::parseIconShapeProperties(con
return values;
}
void StereotypeDefinitionParser::parseRelation(CustomRelation::Element element)
{
CustomRelation relation;
relation.setElement(element);
QSet<QString> stereotypes;
expectBlockBegin();
Token token;
while (readProperty(&token)) {
switch (token.subtype()) {
case KEYWORD_ID:
relation.setId(parseIdentifierProperty());
break;
case KEYWORD_TITLE:
relation.setTitle(parseStringProperty());
break;
case KEYWORD_ELEMENTS:
relation.setEndItems(parseIdentifierListProperty());
break;
case KEYWORD_STEREOTYPE:
stereotypes.insert(parseStringProperty());
break;
case KEYWORD_NAME:
relation.setName(parseStringProperty());
break;
case KEYWORD_DIRECTION:
{
const static QHash<QString, CustomRelation::Direction> directionNames = QHash<QString, CustomRelation::Direction>()
<< qMakePair(QString(QStringLiteral("atob")), CustomRelation::Direction::AtoB)
<< qMakePair(QString(QStringLiteral("btoa")), CustomRelation::Direction::BToA)
<< qMakePair(QString(QStringLiteral("bi")), CustomRelation::Direction::Bi);
if (element != CustomRelation::Element::Dependency)
throwUnknownPropertyError(token);
parseEnum<CustomRelation::Direction>(
parseIdentifierProperty(), directionNames, token.sourcePos(),
[&](CustomRelation::Direction direction) { relation.setDirection(direction); });
break;
}
case KEYWORD_PATTERN:
{
const static QHash<QString, CustomRelation::ShaftPattern> patternNames = QHash<QString, CustomRelation::ShaftPattern>()
<< qMakePair(QString(QStringLiteral("solid")), CustomRelation::ShaftPattern::Solid)
<< qMakePair(QString(QStringLiteral("dash")), CustomRelation::ShaftPattern::Dash)
<< qMakePair(QString(QStringLiteral("dot")), CustomRelation::ShaftPattern::Dot)
<< qMakePair(QString(QStringLiteral("dashdot")), CustomRelation::ShaftPattern::DashDot)
<< qMakePair(QString(QStringLiteral("dashdotdot")), CustomRelation::ShaftPattern::DashDotDot);
if (element != CustomRelation::Element::Relation)
throwUnknownPropertyError(token);
parseEnum<CustomRelation::ShaftPattern>(
parseIdentifierProperty(), patternNames, token.sourcePos(),
[&](CustomRelation::ShaftPattern pattern) { relation.setShaftPattern(pattern); });
break;
}
case KEYWORD_COLOR:
{
if (element != CustomRelation::Element::Relation)
throwUnknownPropertyError(token);
Value expression = parseProperty();
if (expression.type() == Color) {
relation.setColorType(CustomRelation::ColorType::Custom);
relation.setColor(expression.value().value<QColor>());
} else if (expression.type() == Identifier) {
QString colorValue = expression.value().toString();
QString colorName = colorValue.toLower();
if (colorName == "a") {
relation.setColorType(CustomRelation::ColorType::EndA);
} else if (colorName == "b") {
relation.setColorType(CustomRelation::ColorType::EndB);
} else if (QColor::isValidColor(colorName)) {
relation.setColorType(CustomRelation::ColorType::Custom);
relation.setColor(QColor(colorName));
} else {
throw StereotypeDefinitionParserError(QString(QStringLiteral("Unexpected value \"%1\" for color.")).arg(colorValue), token.sourcePos());
}
} else {
throw StereotypeDefinitionParserError(QStringLiteral("Unexpected value for color."), token.sourcePos());
}
break;
}
case KEYWORD_END:
parseRelationEnd(&relation);
break;
default:
throwUnknownPropertyError(token);
}
if (!expectPropertySeparatorOrBlockEnd())
break;
}
relation.setStereotypes(stereotypes);
if (relation.id().isEmpty())
throw StereotypeDefinitionParserError(QStringLiteral("Missing id in Relation definition."), d->m_scanner->sourcePos());
emit relationParsed(relation);
}
void StereotypeDefinitionParser::parseRelationEnd(CustomRelation *relation)
{
CustomRelation::End relationEnd;
bool isEndB = false;
expectBlockBegin();
Token token;
while (readProperty(&token)) {
switch (token.subtype()) {
case KEYWORD_END:
{
QString endValue = parseIdentifierProperty();
QString endName = endValue.toLower();
if (endName == "a")
isEndB = false;
else if (endName == "b")
isEndB = true;
else
throw StereotypeDefinitionParserError(QString(QStringLiteral("Unexpected value \"%1\" for end.")).arg(endValue), token.sourcePos());
break;
}
case KEYWORD_ELEMENTS:
if (relation->element() != CustomRelation::Element::Relation)
throwUnknownPropertyError(token);
relationEnd.setEndItems(parseIdentifierListProperty());
break;
case KEYWORD_ROLE:
if (relation->element() != CustomRelation::Element::Relation && relation->element() != CustomRelation::Element::Association)
throwUnknownPropertyError(token);
relationEnd.setRole(parseStringProperty());
break;
case KEYWORD_CARDINALITY:
{
if (relation->element() != CustomRelation::Element::Relation && relation->element() != CustomRelation::Element::Association)
throwUnknownPropertyError(token);
Value expression = parseProperty();
if (expression.type() == Int || expression.type() == String)
relationEnd.setCardinality(expression.value().toString());
else
throw StereotypeDefinitionParserError("Wrong type for cardinality.", token.sourcePos());
break;
}
case KEYWORD_NAVIGABLE:
if (relation->element() != CustomRelation::Element::Relation && relation->element() != CustomRelation::Element::Association)
throwUnknownPropertyError(token);
relationEnd.setNavigable(parseBoolProperty());
break;
case KEYWORD_RELATIONSHIP:
{
if (relation->element() != CustomRelation::Element::Association)
throwUnknownPropertyError(token);
const static QHash<QString, CustomRelation::Relationship> relationshipNames = QHash<QString, CustomRelation::Relationship>()
<< qMakePair(QString(QStringLiteral("association")), CustomRelation::Relationship::Association)
<< qMakePair(QString(QStringLiteral("aggregation")), CustomRelation::Relationship::Aggregation)
<< qMakePair(QString(QStringLiteral("composition")), CustomRelation::Relationship::Composition);
parseEnum<CustomRelation::Relationship>(
parseIdentifierProperty(), relationshipNames, token.sourcePos(),
[&](CustomRelation::Relationship relationship) { relationEnd.setRelationship(relationship); });
break;
}
case KEYWORD_HEAD:
{
if (relation->element() != CustomRelation::Element::Relation)
throwUnknownPropertyError(token);
const static QHash<QString, CustomRelation::Head> headNames = QHash<QString, CustomRelation::Head>()
<< qMakePair(QString(QStringLiteral("none")), CustomRelation::Head::None)
<< qMakePair(QString(QStringLiteral("arrow")), CustomRelation::Head::Arrow)
<< qMakePair(QString(QStringLiteral("triangle")), CustomRelation::Head::Triangle)
<< qMakePair(QString(QStringLiteral("filledtriangle")), CustomRelation::Head::FilledTriangle)
<< qMakePair(QString(QStringLiteral("diamond")), CustomRelation::Head::Diamond)
<< qMakePair(QString(QStringLiteral("filleddiamond")), CustomRelation::Head::FilledDiamond);
parseEnum<CustomRelation::Head>(
parseIdentifierProperty(), headNames, token.sourcePos(),
[&](CustomRelation::Head head) { relationEnd.setHead(head); });
break;
}
case KEYWORD_SHAPE:
if (relation->element() != CustomRelation::Element::Relation)
throwUnknownPropertyError(token);
relationEnd.setHead(CustomRelation::Head::Shape);
relationEnd.setShape(parseIconShape());
break;
default:
throwUnknownPropertyError(token);
}
if (!expectPropertySeparatorOrBlockEnd())
break;
}
if (isEndB)
relation->setEndB(relationEnd);
else
relation->setEndA(relationEnd);
}
void StereotypeDefinitionParser::parseToolbar()
{
Toolbar toolbar;
@@ -507,6 +819,10 @@ void StereotypeDefinitionParser::parseToolbar()
case KEYWORD_PRIORITY:
toolbar.setPriority(parseIntProperty());
break;
case KEYWORD_ELEMENT:
toolbar.setElementTypes(parseIdentifierListProperty());
toolbar.setToolbarType(toolbar.elementTypes().isEmpty() ? Toolbar::ObjectToolbar : Toolbar::RelationToolbar);
break;
case KEYWORD_TOOLS:
parseToolbarTools(&toolbar);
break;
@@ -532,7 +848,7 @@ void StereotypeDefinitionParser::parseToolbarTools(Toolbar *toolbar)
{
Toolbar::Tool tool;
tool.m_toolType = Toolbar::TooltypeTool;
parseToolbarTool(&tool);
parseToolbarTool(toolbar, &tool);
tools.append(tool);
break;
}
@@ -549,7 +865,7 @@ void StereotypeDefinitionParser::parseToolbarTools(Toolbar *toolbar)
toolbar->setTools(tools);
}
void StereotypeDefinitionParser::parseToolbarTool(Toolbar::Tool *tool)
void StereotypeDefinitionParser::parseToolbarTool(const Toolbar *toolbar, Toolbar::Tool *tool)
{
expectBlockBegin();
Token token;
@@ -561,17 +877,29 @@ void StereotypeDefinitionParser::parseToolbarTool(Toolbar::Tool *tool)
case KEYWORD_ELEMENT:
{
QString element = parseIdentifierProperty();
static QSet<QString> elementNames = QSet<QString>()
<< QStringLiteral("package")
<< QStringLiteral("component")
<< QStringLiteral("class")
<< QStringLiteral("item")
<< QStringLiteral("annotation")
<< QStringLiteral("boundary");
QString elementName = element.toLower();
if (!elementNames.contains(elementName))
throw StereotypeDefinitionParserError(QString(QStringLiteral("Unexpected value \"%1\" for element.")).arg(element), token.sourcePos());
tool->m_elementType = elementName;
if (toolbar->toolbarType() == Toolbar::ObjectToolbar) {
static QSet<QString> elementNames = QSet<QString>()
<< QStringLiteral("package")
<< QStringLiteral("component")
<< QStringLiteral("class")
<< QStringLiteral("item")
<< QStringLiteral("annotation")
<< QStringLiteral("boundary");
QString elementName = element.toLower();
if (!elementNames.contains(elementName))
throw StereotypeDefinitionParserError(QString(QStringLiteral("Unexpected value \"%1\" for element.")).arg(element), token.sourcePos());
tool->m_elementType = elementName;
} else {
static QSet<QString> relationNames = QSet<QString>()
<< "dependency"
<< "inheritance"
<< "association";
QString relationName = element.toLower();
if (relationNames.contains(relationName))
tool->m_elementType = relationName;
else
tool->m_elementType = element;
}
break;
}
case KEYWORD_STEREOTYPE:
@@ -585,6 +913,28 @@ void StereotypeDefinitionParser::parseToolbarTool(Toolbar::Tool *tool)
}
}
template<typename T>
void StereotypeDefinitionParser::parseEnums(const QList<QString> &identifiers,
const QHash<QString, T> &identifierNames,
const SourcePos &sourcePos,
std::function<void (T)> setter)
{
for (const QString &identifier : identifiers)
parseEnum(identifier, identifierNames, sourcePos, setter);
}
template<typename T>
void StereotypeDefinitionParser::parseEnum(const QString &identifier,
const QHash<QString, T> &identifierNames,
const SourcePos &sourcePos,
std::function<void (T)> setter)
{
const QString name = identifier.toLower();
if (!identifierNames.contains(name))
throw StereotypeDefinitionParserError(QString(QStringLiteral("Unexpected value \"%1\".")).arg(identifier), sourcePos);
setter(identifierNames.value(name));
}
QString StereotypeDefinitionParser::parseStringProperty()
{
expectColon();
@@ -640,6 +990,12 @@ QColor StereotypeDefinitionParser::parseColorProperty()
return parseColorExpression();
}
StereotypeDefinitionParser::Value StereotypeDefinitionParser::parseProperty()
{
expectColon();
return parseExpression();
}
QString StereotypeDefinitionParser::parseStringExpression()
{
Token token = d->m_scanner->read();
@@ -723,6 +1079,50 @@ QColor StereotypeDefinitionParser::parseColorExpression()
throw StereotypeDefinitionParserError(QStringLiteral("Expected color name."), token.sourcePos());
}
StereotypeDefinitionParser::Value StereotypeDefinitionParser::parseExpression()
{
Token token = d->m_scanner->read();
if (token.type() == Token::TokenString) {
return Value(String, QVariant(token.text()));
} else if (token.type() == Token::TokenOperator && token.subtype() == OPERATOR_MINUS) {
Value v = parseExpression();
if (v.type() == Int)
return Value(Int, QVariant(-v.value().toInt()));
else if (v.type() == Float)
return Value(Float, QVariant(-v.value().toDouble()));
else
throw StereotypeDefinitionParserError(QStringLiteral("Illegal number expression."), token.sourcePos());
} else if (token.type() == Token::TokenInteger) {
bool ok = false;
int value = token.text().toInt(&ok);
QMT_CHECK(ok);
return Value(Int, QVariant(value));
} else if (token.type() == Token::TokenFloat) {
bool ok = false;
qreal value = token.text().toDouble(&ok);
QMT_CHECK(ok);
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);
return Value(Color, QVariant(color));
} else {
throw StereotypeDefinitionParserError(QStringLiteral("Invalid color."), token.sourcePos());
}
} else if (token.type() == Token::TokenIdentifier || token.type() == Token::TokenKeyword) {
QString value = token.text().toLower();
if (value == QStringLiteral("yes") || value == QStringLiteral("true"))
return Value(Boolean, QVariant(true));
else if (value == QStringLiteral("no") || value == QStringLiteral("false"))
return Value(Boolean, QVariant(false));
else
return Value(Identifier, QVariant(token.text()));
}
throw StereotypeDefinitionParserError(QStringLiteral("Syntax error in expression."), token.sourcePos());
}
void StereotypeDefinitionParser::expectBlockBegin()
{
skipEOLTokens();

View File

@@ -27,12 +27,14 @@
#include <QObject>
#include "qmt/infrastructure/exceptions.h"
#include "qmt/stereotype/customrelation.h"
#include "qmt/stereotype/toolbar.h"
#include "sourcepos.h"
#include <QPair>
#include <QHash>
#include <functional>
namespace qmt {
@@ -59,6 +61,17 @@ class QMT_EXPORT StereotypeDefinitionParser : public QObject
Q_OBJECT
class StereotypeDefinitionParserPrivate;
class IconCommandParameter;
class Value;
enum Type {
Void,
Identifier,
String,
Int,
Float,
Boolean,
Color
};
public:
explicit StereotypeDefinitionParser(QObject *parent = 0);
@@ -66,6 +79,7 @@ public:
signals:
void iconParsed(const StereotypeIcon &stereotypeIcon);
void relationParsed(const CustomRelation &relation);
void toolbarParsed(const Toolbar &toolbar);
public:
@@ -78,12 +92,24 @@ private:
static QPair<int, IconCommandParameter> SCALED(int keyword);
static QPair<int, IconCommandParameter> FIX(int keyword);
static QPair<int, IconCommandParameter> ABSOLUTE(int keyword);
void parseIconShape(StereotypeIcon *stereotypeIcon);
QHash<int, ShapeValueF> parseIconShapeProperties(const QHash<int, IconCommandParameter> &parameters);
static QPair<int, IconCommandParameter> BOOLEAN(int keyword);
IconShape parseIconShape();
QHash<int, IconCommandParameter> parseIconShapeProperties(const QHash<int, IconCommandParameter> &parameters);
void parseRelation(CustomRelation::Element element);
void parseRelationEnd(CustomRelation *relation);
void parseToolbar();
void parseToolbarTools(Toolbar *toolbar);
void parseToolbarTool(Toolbar::Tool *tool);
void parseToolbarTool(const Toolbar *toolbar, Toolbar::Tool *tool);
template<typename T>
void parseEnums(const QList<QString> &identifiers, const QHash<QString, T> &identifierNames,
const SourcePos &sourcePos, std::function<void(T)> setter);
template<typename T>
void parseEnum(const QString &identifier, const QHash<QString, T> &identifierNames,
const SourcePos &sourcePos, std::function<void(T)> setter);
QString parseStringProperty();
int parseIntProperty();
@@ -92,6 +118,7 @@ private:
QList<QString> parseIdentifierListProperty();
bool parseBoolProperty();
QColor parseColorProperty();
Value parseProperty();
QString parseStringExpression();
qreal parseFloatExpression();
@@ -99,6 +126,7 @@ private:
QString parseIdentifierExpression();
bool parseBoolExpression();
QColor parseColorExpression();
Value parseExpression();
void expectBlockBegin();
bool readProperty(Token *token);

View File

@@ -130,6 +130,7 @@ HEADERS += \
$$PWD/serializer/infrastructureserializer.h \
$$PWD/serializer/modelserializer.h \
$$PWD/serializer/projectserializer.h \
$$PWD/stereotype/customrelation.h \
$$PWD/stereotype/iconshape.h \
$$PWD/stereotype/shape.h \
$$PWD/stereotype/shapepaintvisitor.h \
@@ -155,7 +156,7 @@ HEADERS += \
$$PWD/tasks/ielementtasks.h \
$$PWD/tasks/isceneinspector.h \
$$PWD/tasks/voidelementtasks.h \
$$PWD/infrastructure/qmtassert.h
$$PWD/infrastructure/qmtassert.h \
SOURCES += \
$$PWD/config/configcontroller.cpp \
@@ -258,6 +259,7 @@ SOURCES += \
$$PWD/serializer/infrastructureserializer.cpp \
$$PWD/serializer/modelserializer.cpp \
$$PWD/serializer/projectserializer.cpp \
$$PWD/stereotype/customrelation.cpp \
$$PWD/stereotype/iconshape.cpp \
$$PWD/stereotype/shapepaintvisitor.cpp \
$$PWD/stereotype/shapes.cpp \
@@ -277,7 +279,7 @@ SOURCES += \
$$PWD/tasks/diagramscenecontroller.cpp \
$$PWD/tasks/finddiagramvisitor.cpp \
$$PWD/tasks/findrootdiagramvisitor.cpp \
$$PWD/tasks/voidelementtasks.cpp
$$PWD/tasks/voidelementtasks.cpp \
RESOURCES += \
$$PWD/resources/resources.qrc

View File

@@ -0,0 +1,139 @@
/****************************************************************************
**
** Copyright (C) 2016 Jochen Becher
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#include "customrelation.h"
namespace qmt {
void CustomRelation::End::setEndItems(const QList<QString> &endItems)
{
m_endItems = endItems;
}
void CustomRelation::End::setRole(const QString &role)
{
m_role = role;
}
void CustomRelation::End::setCardinality(const QString &cardinality)
{
m_cardinality = cardinality;
}
void CustomRelation::End::setNavigable(bool navigable)
{
m_navigable = navigable;
}
void CustomRelation::End::setRelationship(CustomRelation::Relationship relationship)
{
m_relationship = relationship;
}
void CustomRelation::End::setHead(CustomRelation::Head head)
{
m_head = head;
}
void CustomRelation::End::setShape(const IconShape &shape)
{
m_shape = shape;
}
CustomRelation::CustomRelation()
{
}
CustomRelation::~CustomRelation()
{
}
bool CustomRelation::isNull() const
{
return m_id.isEmpty();
}
void CustomRelation::setElement(CustomRelation::Element element)
{
m_element = element;
}
void CustomRelation::setId(const QString &id)
{
m_id = id;
}
void CustomRelation::setTitle(const QString &title)
{
m_title = title;
}
void CustomRelation::setEndItems(const QList<QString> &endItems)
{
m_endItems = endItems;
}
void CustomRelation::setStereotypes(const QSet<QString> &stereotypes)
{
m_stereotypes = stereotypes;
}
void CustomRelation::setName(const QString &name)
{
m_name = name;
}
void CustomRelation::setDirection(CustomRelation::Direction direction)
{
m_direction = direction;
}
void CustomRelation::setEndA(const CustomRelation::End &end)
{
m_endA = end;
}
void CustomRelation::setEndB(const CustomRelation::End &end)
{
m_endB = end;
}
void CustomRelation::setShaftPattern(CustomRelation::ShaftPattern shaftPattern)
{
m_shaftPattern = shaftPattern;
}
void CustomRelation::setColorType(CustomRelation::ColorType colorType)
{
m_colorType = colorType;
}
void CustomRelation::setColor(const QColor &color)
{
m_color = color;
}
} // namespace qmt

View File

@@ -0,0 +1,166 @@
/****************************************************************************
**
** Copyright (C) 2016 Jochen Becher
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#pragma once
#include "iconshape.h"
#include <QString>
#include <QList>
#include <QSet>
#include <QColor>
namespace qmt {
class QMT_EXPORT CustomRelation
{
public:
enum class Element {
Relation,
Dependency,
Inheritance,
Association
};
enum class Direction {
AtoB,
BToA,
Bi
};
enum class Relationship {
Association,
Aggregation,
Composition
};
enum class ShaftPattern {
Solid,
Dash,
Dot,
DashDot,
DashDotDot
};
enum class Head {
None,
Shape,
Arrow,
Triangle,
FilledTriangle,
Diamond,
FilledDiamond
};
enum class ColorType {
EndA,
EndB,
Custom
};
class End {
public:
QList<QString> endItems() const { return m_endItems; }
void setEndItems(const QList<QString> &endItems);
QString role() const { return m_role; }
void setRole(const QString &role);
QString cardinality() const { return m_cardinality; }
void setCardinality(const QString &cardinality);
bool navigable() const { return m_navigable; }
void setNavigable(bool navigable);
Relationship relationship() const { return m_relationship; }
void setRelationship(Relationship relationship);
Head head() const { return m_head; }
void setHead(Head head);
IconShape shape() const { return m_shape; }
void setShape(const IconShape &shape);
private:
QList<QString> m_endItems;
QString m_role;
QString m_cardinality;
bool m_navigable = false;
Relationship m_relationship = Relationship::Association;
Head m_head = Head::None;
IconShape m_shape;
};
CustomRelation();
~CustomRelation();
bool isNull() const;
Element element() const { return m_element; }
void setElement(Element element);
QString id() const { return m_id; }
void setId(const QString &id);
QString title() const { return m_title; }
void setTitle(const QString &title);
QList<QString> endItems() const { return m_endItems; }
void setEndItems(const QList<QString> &endItems);
QSet<QString> stereotypes() const { return m_stereotypes; }
void setStereotypes(const QSet<QString> &stereotypes);
QString name() const { return m_name; }
void setName(const QString &name);
Direction direction() const { return m_direction; }
void setDirection(Direction direction);
End endA() const { return m_endA; }
void setEndA(const End &end);
End endB() const { return m_endB; }
void setEndB(const End &end);
ShaftPattern shaftPattern() const { return m_shaftPattern; }
void setShaftPattern(ShaftPattern shaftPattern);
ColorType colorType() const { return m_colorType; }
void setColorType(ColorType colorType);
QColor color() const { return m_color; }
void setColor(const QColor &color);
private:
Element m_element = Element::Relation;
QString m_id;
QString m_title;
QList<QString> m_endItems;
QSet<QString> m_stereotypes;
QString m_name;
Direction m_direction = Direction::AtoB;
End m_endA;
End m_endB;
ShaftPattern m_shaftPattern = ShaftPattern::Solid;
ColorType m_colorType = ColorType::EndA;
QColor m_color;
};
inline uint qHash(CustomRelation::Relationship relationship) {
return ::qHash(static_cast<int>(relationship));
}
inline uint qHash(CustomRelation::ShaftPattern pattern) {
return ::qHash(static_cast<int>(pattern));
}
inline uint qHash(CustomRelation::Head head) {
return ::qHash(static_cast<int>(head));
}
} // namespace qmt

View File

@@ -34,11 +34,11 @@
#include <QPainter>
template<class T>
QList<T *> CloneAll(const QList<T *> &rhs)
QList<T *> cloneAll(const QList<T *> &rhs)
{
QList<T *> result;
foreach (T *t, rhs)
result.append(t != 0 ? t->Clone() : 0);
result.append(t != 0 ? t->clone() : 0);
return result;
}
@@ -50,7 +50,7 @@ public:
IconShapePrivate() = default;
IconShapePrivate(const IconShapePrivate &rhs)
: m_shapes(CloneAll(rhs.m_shapes))
: m_shapes(cloneAll(rhs.m_shapes))
{
}
@@ -63,7 +63,7 @@ public:
{
if (this != &rhs) {
qDeleteAll(m_shapes);
m_shapes = CloneAll(rhs.m_shapes);
m_shapes = cloneAll(rhs.m_shapes);
}
return *this;
}
@@ -132,6 +132,16 @@ void IconShape::addEllipse(const ShapePointF &center, const ShapeSizeF &radius)
d->m_shapes.append(new EllipseShape(center, radius));
}
void IconShape::addDiamond(const ShapePointF &center, const ShapeSizeF &size, bool filled)
{
d->m_shapes.append(new DiamondShape(center, size, filled));
}
void IconShape::addTriangle(const ShapePointF &center, const ShapeSizeF &size, bool filled)
{
d->m_shapes.append(new TriangleShape(center, size, filled));
}
void IconShape::addArc(const ShapePointF &center, const ShapeSizeF &radius, qreal startAngle, qreal spanAngle)
{
d->m_shapes.append(new ArcShape(center, radius, startAngle, spanAngle));

View File

@@ -42,11 +42,9 @@ class QMT_EXPORT IconShape
public:
IconShape();
IconShape(const IconShape &rhs);
IconShape(const IconShape &&) = delete;
~IconShape();
IconShape &operator=(const IconShape &rhs);
IconShape &operator=(const IconShape &&) = delete;
QSizeF size() const;
void setSize(const QSizeF &size);
@@ -56,6 +54,8 @@ public:
void addRoundedRect(const ShapePointF &pos, const ShapeSizeF &size, const ShapeValueF &radius);
void addCircle(const ShapePointF &center, const ShapeValueF &radius);
void addEllipse(const ShapePointF &center, const ShapeSizeF &radius);
void addDiamond(const ShapePointF &center, const ShapeSizeF &size, bool filled);
void addTriangle(const ShapePointF &center, const ShapeSizeF &size, bool filled);
void addArc(const ShapePointF &center, const ShapeSizeF &radius,
qreal startAngle, qreal spanAngle);

View File

@@ -35,7 +35,7 @@ class IShape
public:
virtual ~IShape() { }
virtual IShape *Clone() const = 0;
virtual IShape *clone() const = 0;
virtual void accept(ShapeVisitor *visitor) = 0;
virtual void accept(ShapeConstVisitor *visitor) const = 0;
};

View File

@@ -75,6 +75,37 @@ void ShapePaintVisitor::visitEllipse(const EllipseShape *shapeEllipse)
radius.width(), radius.height());
}
void ShapePaintVisitor::visitDiamond(const DiamondShape *shapeDiamond)
{
m_painter->save();
m_painter->setRenderHint(QPainter::Antialiasing, true);
QPainterPath path;
QPointF center = shapeDiamond->center().mapScaledTo(m_scaledOrigin, m_originalSize, m_baseSize, m_size);
QSizeF size = shapeDiamond->size().mapScaledTo(m_scaledOrigin, m_originalSize, m_baseSize, m_size);
path.moveTo(center + QPointF(0.0, size.height() / 2.0));
path.lineTo(center + QPointF(-size.width() / 2.0, 0.0));
path.lineTo(center + QPointF(0.0, -size.height() / 2.0));
path.lineTo(center + QPointF(size.width() / 2.0, 0.0));
path.closeSubpath();
m_painter->drawPath(path);
m_painter->restore();
}
void ShapePaintVisitor::visitTriangle(const TriangleShape *shapeTriangle)
{
m_painter->save();
m_painter->setRenderHint(QPainter::Antialiasing, true);
QPainterPath path;
QPointF center = shapeTriangle->center().mapScaledTo(m_scaledOrigin, m_originalSize, m_baseSize, m_size);
QSizeF size = shapeTriangle->size().mapScaledTo(m_scaledOrigin, m_originalSize, m_baseSize, m_size);
path.moveTo(center + QPointF(size.width() / 2.0, size.height() / 2.0));
path.lineTo(center + QPointF(-size.width() / 2.0, size.height() / 2.0));
path.lineTo(center + QPointF(0.0, -size.height() / 2.0));
path.closeSubpath();
m_painter->drawPath(path);
m_painter->restore();
}
void ShapePaintVisitor::visitArc(const ArcShape *shapeArc)
{
QSizeF radius = shapeArc->radius().mapScaledTo(m_scaledOrigin, m_originalSize, m_baseSize, m_size);
@@ -160,6 +191,31 @@ void ShapeSizeVisitor::visitEllipse(const EllipseShape *shapeEllipse)
m_boundingRect |= QRectF(shapeEllipse->center().mapScaledTo(m_scaledOrigin, m_originalSize, m_baseSize, m_size) - QPointF(radius.width(), radius.height()), radius * 2.0);
}
void ShapeSizeVisitor::visitDiamond(const DiamondShape *shapeDiamond)
{
QPainterPath path;
QPointF center = shapeDiamond->center().mapScaledTo(m_scaledOrigin, m_originalSize, m_baseSize, m_size);
QSizeF size = shapeDiamond->size().mapScaledTo(m_scaledOrigin, m_originalSize, m_baseSize, m_size);
path.moveTo(center + QPointF(0.0, size.height() / 2.0));
path.lineTo(center + QPointF(-size.width() / 2.0, 0.0));
path.lineTo(center + QPointF(0.0, -size.height() / 2.0));
path.lineTo(center + QPointF(size.width() / 2.0, 0.0));
path.closeSubpath();
m_boundingRect |= path.boundingRect();
}
void ShapeSizeVisitor::visitTriangle(const TriangleShape *shapeTriangle)
{
QPainterPath path;
QPointF center = shapeTriangle->center().mapScaledTo(m_scaledOrigin, m_originalSize, m_baseSize, m_size);
QSizeF size = shapeTriangle->size().mapScaledTo(m_scaledOrigin, m_originalSize, m_baseSize, m_size);
path.moveTo(center + QPointF(size.width() / 2.0, size.height() / 2.0));
path.lineTo(center + QPointF(-size.width() / 2.0, size.height() / 2.0));
path.lineTo(center + QPointF(0.0, -size.height() / 2.0));
path.closeSubpath();
m_boundingRect |= path.boundingRect();
}
void ShapeSizeVisitor::visitArc(const ArcShape *shapeArc)
{
// TODO this is the max bound rect; not the minimal one

View File

@@ -45,6 +45,8 @@ public:
void visitRoundedRect(const RoundedRectShape *shapeRoundedRect) override;
void visitCircle(const CircleShape *shapeCircle) override;
void visitEllipse(const EllipseShape *shapeEllipse) override;
void visitDiamond(const DiamondShape *shapeDiamond) override;
void visitTriangle(const TriangleShape *shapeTriangle) override;
void visitArc(const ArcShape *shapeArc) override;
void visitPath(const PathShape *shapePath) override;
@@ -69,6 +71,8 @@ public:
void visitRoundedRect(const RoundedRectShape *shapeRoundedRect) override;
void visitCircle(const CircleShape *shapeCircle) override;
void visitEllipse(const EllipseShape *shapeEllipse) override;
void visitDiamond(const DiamondShape *shapeDiamond) override;
void visitTriangle(const TriangleShape *shapeTriangle) override;
void visitArc(const ArcShape *shapeArc) override;
void visitPath(const PathShape *shapePath) override;

View File

@@ -27,7 +27,7 @@
namespace qmt {
IShape *LineShape::Clone() const
IShape *LineShape::clone() const
{
return new LineShape(*this);
}
@@ -42,7 +42,7 @@ void LineShape::accept(ShapeConstVisitor *visitor) const
visitor->visitLine(this);
}
IShape *RectShape::Clone() const
IShape *RectShape::clone() const
{
return new RectShape(*this);
}
@@ -57,7 +57,7 @@ void RectShape::accept(ShapeConstVisitor *visitor) const
visitor->visitRect(this);
}
IShape *RoundedRectShape::Clone() const
IShape *RoundedRectShape::clone() const
{
return new RoundedRectShape(*this);
}
@@ -72,7 +72,7 @@ void RoundedRectShape::accept(ShapeConstVisitor *visitor) const
visitor->visitRoundedRect(this);
}
IShape *CircleShape::Clone() const
IShape *CircleShape::clone() const
{
return new CircleShape(*this);
}
@@ -87,7 +87,7 @@ void CircleShape::accept(ShapeConstVisitor *visitor) const
visitor->visitCircle(this);
}
IShape *EllipseShape::Clone() const
IShape *EllipseShape::clone() const
{
return new EllipseShape(*this);
}
@@ -102,7 +102,37 @@ void EllipseShape::accept(ShapeConstVisitor *visitor) const
visitor->visitEllipse(this);
}
IShape *ArcShape::Clone() const
IShape *DiamondShape::clone() const
{
return new DiamondShape(*this);
}
void DiamondShape::accept(ShapeVisitor *visitor)
{
visitor->visitDiamond(this);
}
void DiamondShape::accept(ShapeConstVisitor *visitor) const
{
visitor->visitDiamond(this);
}
IShape *TriangleShape::clone() const
{
return new TriangleShape(*this);
}
void TriangleShape::accept(ShapeVisitor *visitor)
{
visitor->visitTriangle(this);
}
void TriangleShape::accept(ShapeConstVisitor *visitor) const
{
visitor->visitTriangle(this);
}
IShape *ArcShape::clone() const
{
return new ArcShape(*this);
}
@@ -125,7 +155,7 @@ PathShape::~PathShape()
{
}
IShape *PathShape::Clone() const
IShape *PathShape::clone() const
{
return new PathShape(*this);
}

View File

@@ -49,7 +49,7 @@ public:
ShapePointF pos1() const { return m_pos1; }
ShapePointF pos2() const { return m_pos2; }
IShape *Clone() const override;
IShape *clone() const override;
void accept(ShapeVisitor *visitor) override;
void accept(ShapeConstVisitor *visitor) const override;
@@ -72,7 +72,7 @@ public:
ShapePointF pos() const { return m_pos; }
ShapeSizeF size() const { return m_size; }
IShape *Clone() const override;
IShape *clone() const override;
void accept(ShapeVisitor *visitor) override;
void accept(ShapeConstVisitor *visitor) const override;
@@ -97,7 +97,7 @@ public:
ShapeSizeF size() const { return m_size; }
ShapeValueF radius() const { return m_radius; }
IShape *Clone() const override;
IShape *clone() const override;
void accept(ShapeVisitor *visitor) override;
void accept(ShapeConstVisitor *visitor) const override;
@@ -121,7 +121,7 @@ public:
ShapePointF center() const { return m_center; }
ShapeValueF radius() const { return m_radius; }
IShape *Clone() const override;
IShape *clone() const override;
void accept(ShapeVisitor *visitor) override;
void accept(ShapeConstVisitor *visitor) const override;
@@ -144,7 +144,7 @@ public:
ShapePointF center() const { return m_center; }
ShapeSizeF radius() const { return m_radius; }
IShape *Clone() const override;
IShape *clone() const override;
void accept(ShapeVisitor *visitor) override;
void accept(ShapeConstVisitor *visitor) const override;
@@ -153,6 +153,58 @@ private:
ShapeSizeF m_radius;
};
class QMT_EXPORT DiamondShape : public IShape
{
public:
DiamondShape() = default;
DiamondShape(const ShapePointF &center, const ShapeSizeF &size, bool filled)
: m_center(center),
m_size(size),
m_filled(filled)
{
}
ShapePointF center() const { return m_center; }
ShapeSizeF size() const { return m_size; }
bool filled() const { return m_filled; }
IShape *clone() const override;
void accept(ShapeVisitor *visitor) override;
void accept(ShapeConstVisitor *visitor) const override;
private:
ShapePointF m_center;
ShapeSizeF m_size;
bool m_filled = false;
};
class QMT_EXPORT TriangleShape : public IShape
{
public:
TriangleShape() = default;
TriangleShape(const ShapePointF &center, const ShapeSizeF &size, bool filled)
: m_center(center),
m_size(size),
m_filled(filled)
{
}
ShapePointF center() const { return m_center; }
ShapeSizeF size() const { return m_size; }
bool filled() const { return m_filled; }
IShape *clone() const override;
void accept(ShapeVisitor *visitor) override;
void accept(ShapeConstVisitor *visitor) const override;
private:
ShapePointF m_center;
ShapeSizeF m_size;
bool m_filled = false;
};
class QMT_EXPORT ArcShape : public IShape
{
public:
@@ -171,7 +223,7 @@ public:
qreal startAngle() const { return m_startAngle; }
qreal spanAngle() const { return m_spanAngle; }
IShape *Clone() const override;
IShape *clone() const override;
void accept(ShapeVisitor *visitor) override;
void accept(ShapeConstVisitor *visitor) const override;
@@ -214,7 +266,7 @@ public:
QList<Element> elements() const { return m_elements; }
IShape *Clone() const override;
IShape *clone() const override;
void accept(ShapeVisitor *visitor) override;
void accept(ShapeConstVisitor *visitor) const override;

View File

@@ -32,6 +32,8 @@ class RectShape;
class RoundedRectShape;
class CircleShape;
class EllipseShape;
class DiamondShape;
class TriangleShape;
class ArcShape;
class PathShape;
@@ -45,6 +47,8 @@ public:
virtual void visitRoundedRect(RoundedRectShape *shapeRoundedRect) = 0;
virtual void visitCircle(CircleShape *shapeCircle) = 0;
virtual void visitEllipse(EllipseShape *shapeEllipse) = 0;
virtual void visitDiamond(DiamondShape *shapeDiamond) = 0;
virtual void visitTriangle(TriangleShape *shapeDiamond) = 0;
virtual void visitArc(ArcShape *shapeArc) = 0;
virtual void visitPath(PathShape *shapePath) = 0;
};
@@ -59,6 +63,8 @@ public:
virtual void visitRoundedRect(const RoundedRectShape *shapeRoundedRect) = 0;
virtual void visitCircle(const CircleShape *shapeCircle) = 0;
virtual void visitEllipse(const EllipseShape *shapeEllipse) = 0;
virtual void visitDiamond(const DiamondShape *shapeDiamond) = 0;
virtual void visitTriangle(const TriangleShape *shapeDiamond) = 0;
virtual void visitArc(const ArcShape *shapeArc) = 0;
virtual void visitPath(const PathShape *shapePath) = 0;
};

View File

@@ -25,12 +25,14 @@
#include "stereotypecontroller.h"
#include "customrelation.h"
#include "stereotypeicon.h"
#include "shapepaintvisitor.h"
#include "toolbar.h"
#include "qmt/infrastructure/qmtassert.h"
#include "qmt/style/style.h"
#include "utils/algorithm.h"
#include <QHash>
#include <QPainter>
@@ -46,7 +48,9 @@ class StereotypeController::StereotypeControllerPrivate
public:
QHash<QPair<StereotypeIcon::Element, QString>, QString> m_stereotypeToIconIdMap;
QHash<QString, StereotypeIcon> m_iconIdToStereotypeIconsMap;
QHash<QString, CustomRelation> m_relationIdToCustomRelationMap;
QList<Toolbar> m_toolbars;
QList<Toolbar> m_elementToolbars;
};
StereotypeController::StereotypeController(QObject *parent) :
@@ -70,6 +74,13 @@ QList<Toolbar> StereotypeController::toolbars() const
return d->m_toolbars;
}
QList<Toolbar> StereotypeController::findToolbars(const QString &elementType) const
{
return Utils::filtered(d->m_elementToolbars, [&elementType](const Toolbar &tb) {
return tb.elementTypes().contains(elementType);
});
}
QList<QString> StereotypeController::knownStereotypes(StereotypeIcon::Element stereotypeElement) const
{
QSet<QString> stereotypes;
@@ -105,12 +116,17 @@ QList<QString> StereotypeController::filterStereotypesByIconId(const QString &st
return filteredStereotypes;
}
StereotypeIcon StereotypeController::findStereotypeIcon(const QString &stereotypeIconId)
StereotypeIcon StereotypeController::findStereotypeIcon(const QString &stereotypeIconId) const
{
QMT_CHECK(d->m_iconIdToStereotypeIconsMap.contains(stereotypeIconId));
return d->m_iconIdToStereotypeIconsMap.value(stereotypeIconId);
}
CustomRelation StereotypeController::findCustomRelation(const QString &customRelationId) const
{
return d->m_relationIdToCustomRelationMap.value(customRelationId);
}
QIcon StereotypeController::createIcon(StereotypeIcon::Element element, const QList<QString> &stereotypes,
const QString &defaultIconPath, const Style *style, const QSize &size,
const QMarginsF &margins)
@@ -180,9 +196,17 @@ void StereotypeController::addStereotypeIcon(const StereotypeIcon &stereotypeIco
d->m_iconIdToStereotypeIconsMap.insert(stereotypeIcon.id(), stereotypeIcon);
}
void StereotypeController::addCustomRelation(const CustomRelation &customRelation)
{
d->m_relationIdToCustomRelationMap.insert(customRelation.id(), customRelation);
}
void StereotypeController::addToolbar(const Toolbar &toolbar)
{
d->m_toolbars.append(toolbar);
if (toolbar.elementTypes().isEmpty())
d->m_toolbars.append(toolbar);
else
d->m_elementToolbars.append(toolbar);
}
} // namespace qmt

View File

@@ -33,6 +33,7 @@
namespace qmt {
class CustomRelation;
class Toolbar;
class Style;
@@ -47,18 +48,21 @@ public:
QList<StereotypeIcon> stereotypeIcons() const;
QList<Toolbar> toolbars() const;
QList<Toolbar> findToolbars(const QString &elementType) const;
QList<QString> knownStereotypes(StereotypeIcon::Element stereotypeElement) const;
QString findStereotypeIconId(StereotypeIcon::Element element,
const QList<QString> &stereotypes) const;
QList<QString> filterStereotypesByIconId(const QString &stereotypeIconId,
const QList<QString> &stereotypes) const;
StereotypeIcon findStereotypeIcon(const QString &stereotypeIconId);
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);
void addStereotypeIcon(const StereotypeIcon &stereotypeIcon);
void addCustomRelation(const CustomRelation &customRelation);
void addToolbar(const Toolbar &toolbar);
private:

View File

@@ -28,7 +28,6 @@
namespace qmt {
Toolbar::Toolbar()
: m_priority(-1)
{
}
@@ -36,6 +35,11 @@ Toolbar::~Toolbar()
{
}
void Toolbar::setToolbarType(Toolbar::ToolbarType toolbarType)
{
m_toolbarType = toolbarType;
}
void Toolbar::setId(const QString &id)
{
m_id = id;
@@ -46,6 +50,11 @@ void Toolbar::setPriority(int priority)
m_priority = priority;
}
void Toolbar::setElementTypes(const QStringList &elementTypes)
{
m_elementTypes = elementTypes;
}
void Toolbar::setTools(const QList<Toolbar::Tool> &tools)
{
m_tools = tools;

View File

@@ -35,6 +35,11 @@ namespace qmt {
class QMT_EXPORT Toolbar
{
public:
enum ToolbarType {
ObjectToolbar,
RelationToolbar
};
enum ToolType {
TooltypeTool,
TooltypeSeparator
@@ -65,16 +70,22 @@ public:
Toolbar();
~Toolbar();
ToolbarType toolbarType() const { return m_toolbarType; }
void setToolbarType(ToolbarType toolbarType);
QString id() const { return m_id; }
void setId(const QString &id);
int priority() const { return m_priority; }
void setPriority(int priority);
QStringList elementTypes() const { return m_elementTypes; }
void setElementTypes(const QStringList &elementTypes);
QList<Tool> tools() const { return m_tools; }
void setTools(const QList<Tool> &tools);
private:
ToolbarType m_toolbarType = ObjectToolbar;
QString m_id;
int m_priority;
int m_priority = -1;
QStringList m_elementTypes;
QList<Tool> m_tools;
};