File indexing completed on 2024-05-12 04:35:05

0001 /* This file is part of the TikZKit project.
0002  *
0003  * Copyright (C) 2013-2018 Dominik Haumann <dhaumann@kde.org>
0004  *
0005  * This library is free software; you can redistribute it and/or modify
0006  * it under the terms of the GNU Library General Public License as published
0007  * by the Free Software Foundation, either version 2 of the License, or
0008  * (at your option) any later version.
0009  *
0010  * This library is distributed in the hope that it will be useful,
0011  * but WITHOUT ANY WARRANTY; without even the implied warranty of
0012  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
0013  * GNU Library General Public License for more details.
0014  *
0015  * You should have received a copy of the GNU Library General Public License
0016  * along with this library; see the file COPYING.LIB.  If not, see
0017  * <http://www.gnu.org/licenses/>.
0018  */
0019 
0020 #include "Style.h"
0021 
0022 #include "Document.h"
0023 #include "Visitor.h"
0024 
0025 #include <QSet>
0026 
0027 namespace tikz {
0028 namespace core {
0029 
0030 // NOTE: these strings have to match the Q_PROPERTY strings, otherise
0031 //       serialization will not work correctly.
0032 static constexpr char s_penStyle[] = "penStyle";
0033 static constexpr char s_lineWidth[] = "lineWidth";
0034 static constexpr char s_doubleLine[] = "doubleLine";
0035 static constexpr char s_innerLineWidth[] = "innerLineWidth";
0036 static constexpr char s_penOpacity[] = "penOpacity";
0037 static constexpr char s_fillOpacity[] = "fillOpacity";
0038 static constexpr char s_penColor[] = "penColor";
0039 static constexpr char s_innerLineColor[] = "innerLineColor";
0040 static constexpr char s_fillColor[] = "fillColor";
0041 static constexpr char s_rotation[] = "rotation";
0042 
0043 // Path properties
0044 static constexpr char s_radiusX[] = "radiusX";
0045 static constexpr char s_radiusY[] = "radiusY";
0046 static constexpr char s_bendAngle[] = "bendAngle";
0047 static constexpr char s_looseness[] = "looseness";
0048 static constexpr char s_outAngle[] = "outAngle";
0049 static constexpr char s_inAngle[] = "inAngle";
0050 static constexpr char s_arrowTail[] = "arrowTail";
0051 static constexpr char s_arrowHead[] = "arrowHead";
0052 static constexpr char s_shortenStart[] = "shortenStart";
0053 static constexpr char s_shortenEnd[] = "shortenEnd";
0054 
0055 // Node properties
0056 static constexpr char s_align[] = "textAlign";
0057 static constexpr char s_shape[] = "shape";
0058 static constexpr char s_innerSep[] = "innerSep";
0059 static constexpr char s_outerSep[] = "outerSep";
0060 static constexpr char s_minimumHeight[] = "minimumHeight";
0061 static constexpr char s_minimumWidth[] = "minimumWidth";
0062 
0063 /**
0064  * Private data and helper functions of class Style.
0065  */
0066 class StylePrivate
0067 {
0068 public:
0069     // parent / child hierarchy
0070     Uid parentStyle;
0071     QVector<Uid> children;
0072 
0073     // config reference counter
0074     int refCounter = 0;
0075 
0076     // list of set properties
0077     QSet<QString> properties;
0078 
0079     // line style
0080     PenStyle penStyle = tikz::PenStyle::SolidLine;
0081 
0082     // line width
0083     tikz::Value lineWidth = tikz::Value::semiThick();
0084 
0085     // double lines
0086     bool doubleLine = false;
0087     tikz::Value innerLineWidth = tikz::Value::semiThick();
0088 
0089     // colors
0090     QColor penColor = Qt::black;
0091     QColor innerLineColor = Qt::white;
0092     QColor fillColor = Qt::transparent;
0093 
0094     qreal penOpacity = 1.0;
0095     qreal fillOpacity = 1.0;
0096 
0097     qreal rotation = 0.0;
0098 
0099 //
0100 // Path members
0101 //
0102 public:
0103     tikz::Value radiusX = 0.0_cm;
0104     tikz::Value radiusY = 0.0_cm;
0105 
0106     qreal bendAngle = 0.0; // in degree
0107     qreal looseness = 1.0;
0108     qreal outAngle = 45.0; // in degree
0109     qreal inAngle = 135.0; // in degree
0110 
0111     Arrow arrowTail = tikz::Arrow::NoArrow;
0112     Arrow arrowHead = tikz::Arrow::NoArrow;
0113 
0114     tikz::Value shortenStart;
0115     tikz::Value shortenEnd;
0116 
0117 //
0118 // Node members
0119 //
0120 public:
0121     // Node styles
0122     TextAlignment textAlign = tikz::TextAlignment::NoAlign;
0123     Shape shape = tikz::Shape::ShapeRectangle;
0124     tikz::Value innerSep = 3.0_pt; //FIXME: 0.3333em
0125     tikz::Value outerSep = 0.5_pt; // 0.5 \pgflinewidth
0126     tikz::Value minimumHeight = 0.0_cm;
0127     tikz::Value minimumWidth = 0.0_cm;
0128 };
0129 
0130 Style::Style()
0131     : Entity()
0132     , d(new StylePrivate())
0133 {
0134 }
0135 
0136 Style::Style(const Uid & uid)
0137     : Entity(uid)
0138     , d(new StylePrivate())
0139 {
0140 }
0141 
0142 Style::~Style()
0143 {
0144     // unregister all child styles
0145     for (const Uid & styleUid : qAsConst(d->children)) {
0146         styleUid.entity<Style>()->setParentStyle(d->parentStyle);
0147     }
0148     Q_ASSERT(d->children.size() == 0);
0149 
0150     // avoid unnecessary propagation of the changed() signal
0151     disconnect(this, SIGNAL(changed()), nullptr, nullptr);
0152 
0153     // now: remove from parent's child list, if needed
0154     setParentStyle(Uid());
0155 }
0156 
0157 tikz::EntityType Style::entityType() const
0158 {
0159     return EntityType::Style;
0160 }
0161 
0162 bool Style::accept(Visitor & visitor)
0163 {
0164     visitor.visit(this);
0165     return true;
0166 }
0167 
0168 void Style::setStyle(const Style * other)
0169 {
0170     if (this == other) {
0171         return;
0172     }
0173 
0174     // start configuration
0175     ConfigTransaction transaction(this);
0176 
0177     // backup properties not to copy
0178     Uid parent = d->parentStyle;
0179 
0180     // perform copy of properties
0181     *d = *other->d;
0182 
0183     // restore persistent properties
0184     d->parentStyle = parent;
0185 }
0186 
0187 void Style::loadData(const QJsonObject & json)
0188 {
0189     ConfigTransaction transaction(this);
0190 
0191     if (json.contains("parentStyle")) {
0192         d->parentStyle = Uid(json["parentStyle"].toString(), document());
0193     }
0194 
0195     if (json.contains("penColor")) {
0196         setPenColor(json["penColor"].toString());
0197     }
0198 
0199     if (json.contains("fillColor")) {
0200         setFillColor(json["fillColor"].toString());
0201     }
0202 
0203     if (json.contains("penOpacity")) {
0204         setPenOpacity(json["penOpacity"].toDouble());
0205     }
0206 
0207     if (json.contains("fillOpacity")) {
0208         setFillOpacity(json["fillOpacity"].toDouble());
0209     }
0210 
0211     if (json.contains("penStyle")) {
0212         setPenStyle(toEnum<PenStyle>(json["penStyle"].toString()));
0213     }
0214 
0215     if (json.contains("lineWidth")) {
0216         setLineWidth(Value::fromString(json["lineWidth"].toString()));
0217     }
0218     // FIXME line type, inner line type?
0219 
0220     if (json.contains("doubleLine")) {
0221         setDoubleLine(json["doubleLine"].toBool());
0222     }
0223 
0224     if (json.contains("innerLineWidth")) {
0225         setInnerLineWidth(Value::fromString(json["innerLineWidth"].toString()));
0226     }
0227 
0228     if (json.contains("innerLineColor")) {
0229         setInnerLineColor(json["innerLineColor"].toString());
0230     }
0231 
0232     if (json.contains("rotation")) {
0233         setRotation(json["rotation"].toDouble());
0234     }
0235 
0236     if (json.contains("radiusX")) {
0237         setRadiusX(tikz::Value::fromString(json["radiusX"].toString()));
0238     }
0239 
0240     if (json.contains("radiusY")) {
0241         setRadiusY(tikz::Value::fromString(json["radiusY"].toString()));
0242     }
0243 
0244     if (json.contains("bendAngle")) {
0245         setBendAngle(json["bendAngle"].toDouble());
0246     }
0247 
0248     if (json.contains("looseness")) {
0249         setLooseness(json["looseness"].toDouble());
0250     }
0251 
0252     if (json.contains("outAngle")) {
0253         setOutAngle(json["outAngle"].toDouble());
0254     }
0255 
0256     if (json.contains("inAngle")) {
0257         setInAngle(json["inAngle"].toDouble());
0258     }
0259 
0260     if (json.contains("arrowTail")) {
0261         setArrowTail(toEnum<Arrow>(json["arrowTail"].toString()));
0262     }
0263 
0264     if (json.contains("arrowHead")) {
0265         setArrowHead(toEnum<Arrow>(json["arrowHead"].toString()));
0266     }
0267 
0268     if (json.contains("shortenStart")) {
0269         setShortenStart(tikz::Value::fromString(json["shortenStart"].toString()));
0270     }
0271 
0272     if (json.contains("shortenEnd")) {
0273         setShortenEnd(tikz::Value::fromString(json["shortenEnd"].toString()));
0274     }
0275 
0276     if (json.contains("textAlign")) {
0277         setTextAlign(toEnum<TextAlignment>(json["textAlign"].toString()));
0278     }
0279 
0280     if (json.contains("shape")) {
0281         setShape(toEnum<Shape>(json["shape"].toString()));
0282     }
0283 
0284     if (json.contains("minimumWidth")) {
0285         setMinimumWidth(tikz::Value::fromString(json["minimumWidth"].toString()));
0286     }
0287 
0288     if (json.contains("minimumHeight")) {
0289         setMinimumHeight(tikz::Value::fromString(json["minimumHeight"].toString()));
0290     }
0291 
0292     if (json.contains("innerSep")) {
0293         setInnerSep(tikz::Value::fromString(json["innerSep"].toString()));
0294     }
0295 
0296     if (json.contains("outerSep")) {
0297         setOuterSep(tikz::Value::fromString(json["outerSep"].toString()));
0298     }
0299 }
0300 
0301 QJsonObject Style::saveData() const
0302 {
0303     QJsonObject json = Entity::saveData();
0304 
0305     json["parentStyle"] = parentStyle().isValid() ? parentStyle().entity<Style>()->uid().toString() : Uid().toString();
0306 
0307     if (penColorSet()) {
0308         json["penColor"] = penColor().name();
0309     }
0310 
0311     if (fillColorSet()) {
0312         json["fillColor"] = fillColor().name();
0313     }
0314 
0315     if (penOpacitySet()) {
0316         json["penOpacity"] = penOpacity();
0317     }
0318 
0319     if (fillOpacitySet()) {
0320         json["fillOpacity"] = fillOpacity();
0321     }
0322 
0323     if (penStyleSet()) {
0324         json["penStyle"] = toString(penStyle());
0325     }
0326 
0327     if (lineWidthSet()) {
0328         json["lineWidth"] = lineWidth().toString();
0329     }
0330 
0331     // FIXME line type
0332 
0333     if (doubleLineSet()) {
0334         json["doubleLine"] = doubleLine();
0335     }
0336 
0337     if (innerLineWidthSet()) {
0338         json["innerLineWidth"] = innerLineWidth().toString();
0339     }
0340 
0341     if (innerLineColorSet()) {
0342         json["innerLineColor"] = innerLineColor().name();
0343     }
0344 
0345     if (rotationSet()) {
0346         json["rotation"] = rotation();
0347     }
0348 
0349     if (radiusXSet()) {
0350         json["radiusX"] = radiusX().toString();
0351     }
0352 
0353     if (radiusYSet()) {
0354         json["radiusY"] = radiusY().toString();
0355     }
0356 
0357     if (bendAngleSet()) {
0358         json["bendAngle"] = bendAngle();
0359     }
0360 
0361     if (loosenessSet()) {
0362         json["looseness"] = looseness();
0363     }
0364 
0365     if (outAngleSet()) {
0366         json["outAngle"] = outAngle();
0367     }
0368 
0369     if (inAngleSet()) {
0370         json["inAngle"] = inAngle();
0371     }
0372 
0373     if (arrowTailSet()) {
0374         json["arrowTail"] = toString(arrowTail());
0375     }
0376 
0377     if (arrowHeadSet()) {
0378         json["arrowHead"] = toString(arrowHead());
0379     }
0380 
0381     if (shortenStartSet()) {
0382         json["shortenStart"] = shortenStart().toString();
0383     }
0384 
0385     if (shortenEndSet()) {
0386         json["shortenEnd"] = shortenEnd().toString();
0387     }
0388 
0389     if (textAlignSet()) {
0390         json["textAlign"] = toString(textAlign());
0391     }
0392 
0393     if (shapeSet()) {
0394         json["shape"] = toString(shape());
0395     }
0396 
0397     if (minimumWidthSet()) {
0398         json["minimumWidth"] = minimumWidth().toString();
0399     }
0400 
0401     if (minimumHeightSet()) {
0402         json["minimumHeight"] = minimumHeight().toString();
0403     }
0404 
0405     if (innerSepSet()) {
0406         json["innerSep"] = innerSep().toString();
0407     }
0408 
0409     if (outerSepSet()) {
0410         json["outerSep"] = outerSep().toString();
0411     }
0412 
0413     return json;
0414 }
0415 
0416 Uid Style::parentStyle() const
0417 {
0418     return d->parentStyle;
0419 }
0420 
0421 void Style::setParentStyle(const Uid & parentUid)
0422 {
0423     if (!uid().isValid()) {
0424         return;
0425     }
0426 
0427     if (d->parentStyle != parentUid) {
0428         ConfigTransaction transaction(this);
0429         if (d->parentStyle.isValid()) {
0430             // disconnect all signals (e.g. changed())
0431             disconnect(d->parentStyle.entity(), nullptr, this, nullptr);
0432 
0433             // remove this in old parent's children list
0434             Q_ASSERT(d->parentStyle.entity<Style>()->d->children.contains(uid()));
0435             d->parentStyle.entity<Style>()->d->children.remove(d->parentStyle.entity<Style>()->d->children.indexOf(uid()));
0436         }
0437         d->parentStyle = parentUid;
0438         if (d->parentStyle.isValid()) {
0439             // forward changed() signal
0440             connect(d->parentStyle.entity(), SIGNAL(changed()), this, SIGNAL(changed()));
0441 
0442             // insert us into the new parent's children list
0443             Q_ASSERT(! d->parentStyle.entity<Style>()->d->children.contains(uid()));
0444             d->parentStyle.entity<Style>()->d->children.append(uid());
0445         }
0446     }
0447 }
0448 
0449 bool Style::hasChildStyles() const
0450 {
0451     return d->children.size() > 0;
0452 }
0453 
0454 void Style::addProperty(const QString & property)
0455 {
0456     d->properties.insert(property);
0457 }
0458 
0459 void Style::removeProperty(const QString & property)
0460 {
0461     d->properties.remove(property);
0462 }
0463 
0464 bool Style::propertySet(const QString & property) const
0465 {
0466     return d->properties.contains(property);
0467 }
0468 
0469 PenStyle Style::penStyle() const
0470 {
0471     if (propertySet(s_penStyle)) {
0472         return d->penStyle;
0473     }
0474 
0475     if (parentStyle().isValid()) {
0476         return parentStyle().entity<Style>()->penStyle();
0477     }
0478 
0479     return tikz::PenStyle::SolidLine;
0480 }
0481 
0482 bool Style::penStyleSet() const
0483 {
0484     return propertySet(s_penStyle);
0485 }
0486 
0487 void Style::setPenStyle(tikz::PenStyle style)
0488 {
0489     if (!propertySet(s_penStyle) || d->penStyle != style) {
0490         ConfigTransaction transaction(this);
0491         addProperty(s_penStyle);
0492         d->penStyle = style;
0493     }
0494 }
0495 
0496 void Style::unsetPenStyle()
0497 {
0498     if (propertySet(s_penStyle)) {
0499         ConfigTransaction transaction(this);
0500         removeProperty(s_penStyle);
0501         d->penStyle = tikz::PenStyle::SolidLine;
0502     }
0503 }
0504 
0505 tikz::Value Style::penWidth() const
0506 {
0507     if (!doubleLine()) {
0508         return lineWidth();
0509     } else {
0510         const Value width = lineWidth();
0511         const Value innerWidth = innerLineWidth();
0512         return 2.0 * width + innerWidth;
0513     }
0514 }
0515 
0516 bool Style::lineWidthSet() const
0517 {
0518     return propertySet(s_lineWidth);
0519 }
0520 
0521 void Style::setLineWidth(const tikz::Value & width)
0522 {
0523     if (!propertySet(s_lineWidth)
0524         || d->lineWidth != width
0525     ) {
0526         ConfigTransaction transaction(this);
0527         addProperty(s_lineWidth);
0528         d->lineWidth = width;
0529     }
0530 }
0531 
0532 tikz::Value Style::lineWidth() const
0533 {
0534     if (propertySet(s_lineWidth)) {
0535         return d->lineWidth;
0536     }
0537 
0538     if (parentStyle().isValid()) {
0539         return parentStyle().entity<Style>()->lineWidth();
0540     }
0541 
0542     return tikz::Value::semiThick();
0543 }
0544 
0545 void Style::unsetLineWidth()
0546 {
0547     if (propertySet(s_lineWidth)) {
0548         ConfigTransaction transaction(this);
0549         removeProperty(s_lineWidth);
0550         d->lineWidth = tikz::Value::semiThick();
0551     }
0552 }
0553 
0554 bool Style::doubleLine() const
0555 {
0556     if (propertySet(s_doubleLine)) {
0557         return d->doubleLine;
0558     }
0559 
0560     if (parentStyle().isValid()) {
0561         return parentStyle().entity<Style>()->doubleLine();
0562     }
0563 
0564     return false;
0565 }
0566 
0567 bool Style::doubleLineSet() const
0568 {
0569     return propertySet(s_doubleLine);
0570 }
0571 
0572 void Style::setDoubleLine(bool enabled)
0573 {
0574     if (!propertySet(s_doubleLine) || d->doubleLine != enabled) {
0575         ConfigTransaction transaction(this);
0576         addProperty(s_doubleLine);
0577         d->doubleLine = enabled;
0578     }
0579 }
0580 
0581 void Style::unsetDoubleLine()
0582 {
0583     if (propertySet(s_doubleLine)) {
0584         ConfigTransaction transaction(this);
0585         removeProperty(s_doubleLine);
0586         d->doubleLine = false;
0587     }
0588 }
0589 
0590 tikz::Value Style::innerLineWidth() const
0591 {
0592     if (propertySet(s_innerLineWidth)) {
0593         return d->innerLineWidth;
0594     }
0595 
0596     if (parentStyle().isValid() && parentStyle().entity<Style>()->doubleLine()) {
0597         return parentStyle().entity<Style>()->innerLineWidth();
0598     }
0599 
0600     return tikz::Value::semiThick();
0601 }
0602 
0603 bool Style::innerLineWidthSet() const
0604 {
0605     return propertySet(s_innerLineWidth);
0606 }
0607 
0608 void Style::setInnerLineWidth(const tikz::Value & width)
0609 {
0610     if (!propertySet(s_innerLineWidth) || d->innerLineWidth != width) {
0611         ConfigTransaction transaction(this);
0612         addProperty(s_innerLineWidth);
0613         d->innerLineWidth = width;
0614     }
0615 }
0616 
0617 void Style::unsetInnerLineWidth()
0618 {
0619     if (propertySet(s_innerLineWidth)) {
0620         ConfigTransaction transaction(this);
0621         removeProperty(s_innerLineWidth);
0622         d->innerLineWidth = tikz::Value::semiThick();
0623     }
0624 }
0625 
0626 qreal Style::penOpacity() const
0627 {
0628     if (propertySet(s_penOpacity)) {
0629         return d->penOpacity;
0630     }
0631 
0632     if (parentStyle().isValid()) {
0633         return parentStyle().entity<Style>()->penOpacity();
0634     }
0635 
0636     return 1.0;
0637 }
0638 
0639 void Style::setPenOpacity(qreal opacity)
0640 {
0641     if (!propertySet(s_penOpacity) || d->penOpacity != opacity) {
0642         ConfigTransaction transaction(this);
0643         addProperty(s_penOpacity);
0644         d->penOpacity = opacity;
0645     }
0646 }
0647 
0648 bool Style::penOpacitySet() const
0649 {
0650     return propertySet(s_penOpacity);
0651 }
0652 
0653 void Style::unsetPenOpacity()
0654 {
0655     if (propertySet(s_penOpacity)) {
0656         ConfigTransaction transaction(this);
0657         removeProperty(s_penOpacity);
0658         d->penOpacity = 1.0;
0659     }
0660 }
0661 
0662 qreal Style::fillOpacity() const
0663 {
0664     if (propertySet(s_fillOpacity)) {
0665         return d->fillOpacity;
0666     }
0667 
0668     if (parentStyle().isValid()) {
0669         return parentStyle().entity<Style>()->fillOpacity();
0670     }
0671 
0672     return 1.0;
0673 }
0674 
0675 bool Style::fillOpacitySet() const
0676 {
0677     return propertySet(s_fillOpacity);
0678 }
0679 
0680 void Style::setFillOpacity(qreal opacity)
0681 {
0682     if (!propertySet(s_fillOpacity) || d->fillOpacity != opacity) {
0683         ConfigTransaction transaction(this);
0684         addProperty(s_fillOpacity);
0685         d->fillOpacity = opacity;
0686     }
0687 }
0688 
0689 void Style::unsetFillOpacity()
0690 {
0691     if (propertySet(s_fillOpacity)) {
0692         ConfigTransaction transaction(this);
0693         removeProperty(s_fillOpacity);
0694         d->fillOpacity = 1.0;
0695     }
0696 }
0697 
0698 QColor Style::penColor() const
0699 {
0700     if (propertySet(s_penColor)) {
0701         return d->penColor;
0702     }
0703 
0704     if (parentStyle().isValid()) {
0705         return parentStyle().entity<Style>()->penColor();
0706     }
0707 
0708     return Qt::black;
0709 }
0710 
0711 bool Style::penColorSet() const
0712 {
0713     return propertySet(s_penColor);
0714 }
0715 
0716 QColor Style::innerLineColor() const
0717 {
0718     if (propertySet(s_innerLineColor)) {
0719         return d->innerLineColor;
0720     }
0721 
0722     if (parentStyle().isValid()) {
0723         return parentStyle().entity<Style>()->innerLineColor();
0724     }
0725 
0726     return Qt::white;
0727 }
0728 
0729 bool Style::innerLineColorSet() const
0730 {
0731     return propertySet(s_innerLineColor);
0732 }
0733 
0734 QColor Style::fillColor() const
0735 {
0736     if (propertySet(s_fillColor)) {
0737         return d->fillColor;
0738     }
0739 
0740     if (parentStyle().isValid()) {
0741         return parentStyle().entity<Style>()->fillColor();
0742     }
0743 
0744     return Qt::transparent;
0745 }
0746 
0747 bool Style::fillColorSet() const
0748 {
0749     return propertySet(s_fillColor);
0750 }
0751 
0752 void Style::setPenColor(const QColor & color)
0753 {
0754     if (!propertySet(s_penColor) || d->penColor != color) {
0755         ConfigTransaction transaction(this);
0756         addProperty(s_penColor);
0757         d->penColor = color;
0758     }
0759 }
0760 
0761 void Style::unsetPenColor()
0762 {
0763     if (propertySet(s_penColor)) {
0764         ConfigTransaction transaction(this);
0765         removeProperty(s_penColor);
0766         d->penColor = Qt::black;
0767     }
0768 }
0769 
0770 void Style::setInnerLineColor(const QColor & color)
0771 {
0772     if (!propertySet(s_innerLineColor) || d->innerLineColor != color) {
0773         ConfigTransaction transaction(this);
0774         addProperty(s_innerLineColor);
0775         d->innerLineColor = color;
0776     }
0777 }
0778 
0779 void Style::unsetInnerLineColor()
0780 {
0781     if (propertySet(s_innerLineColor)) {
0782         ConfigTransaction transaction(this);
0783         removeProperty(s_innerLineColor);
0784         d->innerLineColor = Qt::white;
0785     }
0786 }
0787 
0788 void Style::setFillColor(const QColor & color)
0789 {
0790     if (!propertySet(s_fillColor) || d->fillColor != color) {
0791         ConfigTransaction transaction(this);
0792         addProperty(s_fillColor);
0793         d->fillColor = color;
0794     }
0795 }
0796 
0797 void Style::unsetFillColor()
0798 {
0799     if (propertySet(s_fillColor)) {
0800         ConfigTransaction transaction(this);
0801         removeProperty(s_fillColor);
0802         d->fillColor = Qt::transparent;
0803     }
0804 }
0805 
0806 qreal Style::rotation() const
0807 {
0808     if (propertySet(s_rotation)) {
0809         return d->rotation;
0810     }
0811 
0812     if (parentStyle().isValid()) {
0813         return parentStyle().entity<Style>()->rotation();
0814     }
0815 
0816     return 0.0;
0817 }
0818 
0819 bool Style::rotationSet() const
0820 {
0821     return propertySet(s_rotation);
0822 }
0823 
0824 void Style::setRotation(qreal angle)
0825 {
0826     if (!propertySet(s_rotation) || d->rotation != angle) {
0827         ConfigTransaction transaction(this);
0828         addProperty(s_rotation);
0829         d->rotation = angle;
0830     }
0831 }
0832 
0833 void Style::unsetRotation()
0834 {
0835     if (propertySet(s_rotation)) {
0836         ConfigTransaction transaction(this);
0837         removeProperty(s_rotation);
0838         d->rotation = 0.0;
0839     }
0840 }
0841 
0842 tikz::Value Style::radiusX() const
0843 {
0844     if (propertySet(s_radiusX)) {
0845         return d->radiusX;
0846     }
0847 
0848     auto style = parentStyle().entity<Style>();
0849     if (style) {
0850         return style->radiusX();
0851     }
0852 
0853     return 0.0_cm;
0854 }
0855 
0856 tikz::Value Style::radiusY() const
0857 {
0858     if (propertySet(s_radiusY)) {
0859         return d->radiusY;
0860     }
0861 
0862     auto style = parentStyle().entity<Style>();
0863     if (style) {
0864         return style->radiusY();
0865     }
0866 
0867     return 0.0_cm;
0868 }
0869 
0870 bool Style::radiusXSet() const
0871 {
0872     return propertySet(s_radiusX);
0873 }
0874 
0875 bool Style::radiusYSet() const
0876 {
0877     return propertySet(s_radiusY);
0878 }
0879 
0880 void Style::setRadiusX(const tikz::Value & xradius)
0881 {
0882     if (!propertySet(s_radiusX) || d->radiusX != xradius) {
0883         ConfigTransaction transaction(this);
0884         addProperty(s_radiusX);
0885         d->radiusX = xradius;
0886     }
0887 }
0888 
0889 void Style::setRadiusY(const tikz::Value & yradius)
0890 {
0891     if (!propertySet(s_radiusY) || d->radiusY != yradius) {
0892         ConfigTransaction transaction(this);
0893         addProperty(s_radiusY);
0894         d->radiusY = yradius;
0895     }
0896 }
0897 
0898 void Style::unsetRadiusX()
0899 {
0900     if (propertySet(s_radiusX)) {
0901         ConfigTransaction transaction(this);
0902         removeProperty(s_radiusX);
0903         d->radiusX = 0.0_cm;
0904     }
0905 }
0906 
0907 void Style::unsetRadiusY()
0908 {
0909     if (propertySet(s_radiusY)) {
0910         ConfigTransaction transaction(this);
0911         removeProperty(s_radiusY);
0912         d->radiusY = 0.0_cm;
0913     }
0914 }
0915 
0916 qreal Style::bendAngle() const
0917 {
0918     if (propertySet(s_bendAngle)) {
0919         return d->bendAngle;
0920     }
0921 
0922     auto style = parentStyle().entity<Style>();
0923     if (style) {
0924         return style->bendAngle();
0925     }
0926 
0927     return 0.0;
0928 }
0929 
0930 bool Style::bendAngleSet() const
0931 {
0932     return propertySet(s_bendAngle);
0933 }
0934 
0935 void Style::setBendAngle(qreal angle)
0936 {
0937     // normalize to [-180, 180]
0938     while (angle > 180) angle -= 360.0;
0939     while (angle < -180) angle += 360.0;
0940 
0941     if (!propertySet(s_bendAngle) || d->bendAngle != angle) {
0942         ConfigTransaction transaction(this);
0943         addProperty(s_bendAngle);
0944         d->bendAngle = angle;
0945     }
0946 }
0947 
0948 void Style::unsetBendAngle()
0949 {
0950     if (propertySet(s_bendAngle)) {
0951         ConfigTransaction transaction(this);
0952         removeProperty(s_bendAngle);
0953         d->bendAngle = 0.0;
0954     }
0955 }
0956 
0957 qreal Style::looseness() const
0958 {
0959     if (propertySet(s_looseness)) {
0960         return d->looseness;
0961     }
0962 
0963     auto style = parentStyle().entity<Style>();
0964     if (style) {
0965         return style->looseness();
0966     }
0967 
0968     return 1.0;
0969 }
0970 
0971 bool Style::loosenessSet() const
0972 {
0973     return propertySet(s_looseness);
0974 }
0975 
0976 void Style::setLooseness(qreal looseness)
0977 {
0978     if (!propertySet(s_looseness) || d->looseness != looseness) {
0979         ConfigTransaction transaction(this);
0980         addProperty(s_looseness);
0981         d->looseness = looseness;
0982     }
0983 }
0984 
0985 void Style::unsetLooseness()
0986 {
0987     if (propertySet(s_looseness)) {
0988         ConfigTransaction transaction(this);
0989         removeProperty(s_looseness);
0990         d->looseness = 1.0;
0991     }
0992 }
0993 
0994 void Style::setStartControlPoint(const QPointF & cp1)
0995 {
0996     Q_UNUSED(cp1)
0997     // TODO
0998 }
0999 
1000 void Style::setEndControlPoint(const QPointF & cp2)
1001 {
1002     Q_UNUSED(cp2)
1003     // TODO
1004 }
1005 
1006 QPointF Style::startControlPoint() const
1007 {
1008     return QPointF(); // TODO
1009 }
1010 
1011 QPointF Style::endControlPoint() const
1012 {
1013     return QPointF(); // TODO
1014 }
1015 
1016 qreal Style::outAngle() const
1017 {
1018     if (propertySet(s_outAngle)) {
1019         return d->outAngle;
1020     }
1021 
1022     auto style = parentStyle().entity<Style>();
1023     if (style) {
1024         return style->outAngle();
1025     }
1026 
1027     return 45;
1028 }
1029 
1030 bool Style::outAngleSet() const
1031 {
1032     return propertySet(s_outAngle);
1033 }
1034 
1035 void Style::setOutAngle(qreal angle)
1036 {
1037     if (!propertySet(s_outAngle) || d->outAngle != angle) {
1038         ConfigTransaction transaction(this);
1039         addProperty(s_outAngle);
1040         d->outAngle = angle;
1041     }
1042 }
1043 
1044 void Style::unsetOutAngle()
1045 {
1046     if (propertySet(s_outAngle)) {
1047         ConfigTransaction transaction(this);
1048         removeProperty(s_outAngle);
1049         d->outAngle = 45;
1050     }
1051 }
1052 
1053 qreal Style::inAngle() const
1054 {
1055     if (propertySet(s_inAngle)) {
1056         return d->inAngle;
1057     }
1058 
1059     auto style = parentStyle().entity<Style>();
1060     if (style) {
1061         return style->inAngle();
1062     }
1063 
1064     return 135;
1065 }
1066 
1067 bool Style::inAngleSet() const
1068 {
1069     return propertySet(s_inAngle);
1070 }
1071 
1072 void Style::setInAngle(qreal angle)
1073 {
1074     if (!propertySet(s_inAngle) || d->inAngle != angle) {
1075         ConfigTransaction transaction(this);
1076         addProperty(s_inAngle);
1077         d->inAngle = angle;
1078     }
1079 }
1080 
1081 void Style::unsetInAngle()
1082 {
1083     if (propertySet(s_inAngle)) {
1084         ConfigTransaction transaction(this);
1085         removeProperty(s_inAngle);
1086         d->inAngle = 135;
1087     }
1088 }
1089 
1090 Arrow Style::arrowTail() const
1091 {
1092     if (propertySet(s_arrowTail)) {
1093         return d->arrowTail;
1094     }
1095 
1096     auto style = parentStyle().entity<Style>();
1097     if (style) {
1098         return style->arrowTail();
1099     }
1100 
1101     return tikz::Arrow::NoArrow;
1102 }
1103 
1104 bool Style::arrowTailSet() const
1105 {
1106     return propertySet(s_arrowTail);
1107 }
1108 
1109 Arrow Style::arrowHead() const
1110 {
1111     if (propertySet(s_arrowHead)) {
1112         return d->arrowHead;
1113     }
1114 
1115     auto style = parentStyle().entity<Style>();
1116     if (style) {
1117         return style->arrowHead();
1118     }
1119 
1120     return tikz::Arrow::NoArrow;
1121 }
1122 
1123 bool Style::arrowHeadSet() const
1124 {
1125     return propertySet(s_arrowHead);
1126 }
1127 
1128 void Style::setArrowTail(tikz::Arrow tail)
1129 {
1130     if (!propertySet(s_arrowTail) || d->arrowTail != tail) {
1131         ConfigTransaction transaction(this);
1132         addProperty(s_arrowTail);
1133         d->arrowTail = tail;
1134     }
1135 }
1136 
1137 void Style::setArrowHead(tikz::Arrow head)
1138 {
1139     if (!propertySet(s_arrowHead) || d->arrowHead != head) {
1140         ConfigTransaction transaction(this);
1141         addProperty(s_arrowHead);
1142         d->arrowHead = head;
1143     }
1144 }
1145 
1146 void Style::unsetArrowTail()
1147 {
1148     if (propertySet(s_arrowTail)) {
1149         ConfigTransaction transaction(this);
1150         removeProperty(s_arrowTail);
1151         d->arrowTail = tikz::Arrow::NoArrow;
1152     }
1153 }
1154 
1155 void Style::unsetArrowHead()
1156 {
1157     if (propertySet(s_arrowHead)) {
1158         ConfigTransaction transaction(this);
1159         removeProperty(s_arrowHead);
1160         d->arrowHead = tikz::Arrow::NoArrow;
1161     }
1162 }
1163 
1164 tikz::Value Style::shortenStart() const
1165 {
1166     if (propertySet(s_shortenStart)) {
1167         return d->shortenStart;
1168     }
1169 
1170     auto style = parentStyle().entity<Style>();
1171     if (style) {
1172         return style->shortenStart();
1173     }
1174 
1175     return 0.0_cm;
1176 }
1177 
1178 bool Style::shortenStartSet() const
1179 {
1180     return propertySet(s_shortenStart);
1181 }
1182 
1183 tikz::Value Style::shortenEnd() const
1184 {
1185     if (propertySet(s_shortenEnd)) {
1186         return d->shortenEnd;
1187     }
1188 
1189     auto style = parentStyle().entity<Style>();
1190     if (style) {
1191         return style->shortenEnd();
1192     }
1193 
1194     return 0.0_cm;
1195 }
1196 
1197 bool Style::shortenEndSet() const
1198 {
1199     return propertySet(s_shortenEnd);
1200 }
1201 
1202 void Style::setShortenStart(const tikz::Value & shorten)
1203 {
1204     if (!propertySet(s_shortenStart) || d->shortenStart != shorten) {
1205         ConfigTransaction transaction(this);
1206         addProperty(s_shortenStart);
1207         d->shortenStart = shorten;
1208     }
1209 }
1210 
1211 void Style::setShortenEnd(const tikz::Value & shorten)
1212 {
1213     if (!propertySet(s_shortenEnd) || d->shortenEnd != shorten) {
1214         ConfigTransaction transaction(this);
1215         addProperty(s_shortenEnd);
1216         d->shortenEnd = shorten;
1217     }
1218 }
1219 
1220 void Style::unsetShortenStart()
1221 {
1222     if (propertySet(s_shortenStart)) {
1223         ConfigTransaction transaction(this);
1224         removeProperty(s_shortenStart);
1225         d->shortenStart = 0.0_cm;
1226     }
1227 }
1228 
1229 void Style::unsetShortenEnd()
1230 {
1231     if (propertySet(s_shortenEnd)) {
1232         ConfigTransaction transaction(this);
1233         removeProperty(s_shortenEnd);
1234         d->shortenEnd = 0.0_cm;
1235     }
1236 }
1237 
1238 TextAlignment Style::textAlign() const
1239 {
1240     if (propertySet(s_align)) {
1241         return d->textAlign;
1242     }
1243 
1244     auto style = parentStyle().entity<Style>();
1245     if (style) {
1246         return style->textAlign();
1247     }
1248 
1249     return TextAlignment::NoAlign;
1250 }
1251 
1252 bool Style::textAlignSet() const
1253 {
1254     return propertySet(s_align);
1255 }
1256 
1257 void Style::setTextAlign(tikz::TextAlignment align)
1258 {
1259     if (!propertySet(s_align) || d->textAlign != align) {
1260         ConfigTransaction transaction(this);
1261         addProperty(s_align);
1262         d->textAlign = align;
1263     }
1264 }
1265 
1266 void Style::unsetTextAlign()
1267 {
1268     if (propertySet(s_align)) {
1269         ConfigTransaction transaction(this);
1270         removeProperty(s_align);
1271         d->textAlign = TextAlignment::NoAlign;
1272     }
1273 }
1274 
1275 Shape Style::shape() const
1276 {
1277     if (propertySet(s_shape)) {
1278         return d->shape;
1279     }
1280 
1281     auto style = parentStyle().entity<Style>();
1282     if (style) {
1283         return style->shape();
1284     }
1285 
1286     return Shape::ShapeRectangle;
1287 }
1288 
1289 bool Style::shapeSet() const
1290 {
1291     return propertySet(s_shape);
1292 }
1293 
1294 void Style::setShape(tikz::Shape shape)
1295 {
1296     if (!propertySet(s_shape) || d->shape != shape) {
1297         ConfigTransaction transaction(this);
1298         addProperty(s_shape);
1299         d->shape = shape;
1300     }
1301 }
1302 
1303 void Style::unsetShape()
1304 {
1305     if (propertySet(s_shape)) {
1306         ConfigTransaction transaction(this);
1307         removeProperty(s_shape);
1308         d->shape = Shape::ShapeRectangle;
1309     }
1310 }
1311 
1312 void Style::setInnerSep(const tikz::Value & sep)
1313 {
1314     if (!propertySet(s_innerSep) || d->innerSep != sep) {
1315         ConfigTransaction transaction(this);
1316         addProperty(s_innerSep);
1317         d->innerSep = sep;
1318     }
1319 }
1320 
1321 bool Style::innerSepSet() const
1322 {
1323     return propertySet(s_innerSep);
1324 }
1325 
1326 tikz::Value Style::innerSep() const
1327 {
1328     auto style = parentStyle().entity<Style>();
1329     if (!propertySet(s_innerSep) && style) {
1330         return style->innerSep();
1331     }
1332     return d->innerSep;
1333 }
1334 
1335 void Style::setOuterSep(const tikz::Value & sep)
1336 {
1337     if (!propertySet(s_outerSep) || d->outerSep != sep) {
1338         ConfigTransaction transaction(this);
1339         addProperty(s_outerSep);
1340         d->outerSep = sep;
1341     }
1342 }
1343 
1344 bool Style::outerSepSet() const
1345 {
1346     return propertySet(s_outerSep);
1347 }
1348 
1349 tikz::Value Style::outerSep() const
1350 {
1351     if (!propertySet(s_outerSep)) {
1352         return 0.5 * penWidth();
1353     }
1354     return d->outerSep;
1355 }
1356 
1357 void Style::unsetInnerSep()
1358 {
1359     if (propertySet(s_innerSep)) {
1360         ConfigTransaction transaction(this);
1361         removeProperty(s_innerSep);
1362         d->innerSep = 3.0_pt; //FIXME: 0.3333em
1363     }
1364 }
1365 
1366 void Style::unsetOuterSep()
1367 {
1368     if (propertySet(s_outerSep)) {
1369         ConfigTransaction transaction(this);
1370         removeProperty(s_outerSep);
1371         d->outerSep = 3.0_pt; //FIXME: 0.3333em
1372     }
1373 }
1374 
1375 tikz::Value Style::minimumHeight() const
1376 {
1377     if (propertySet(s_minimumHeight)) {
1378         return d->minimumHeight;
1379     }
1380 
1381     auto style = parentStyle().entity<Style>();
1382     if (style) {
1383         return style->minimumHeight();
1384     }
1385 
1386     return 0.0_cm;
1387 }
1388 
1389 bool Style::minimumHeightSet() const
1390 {
1391     return propertySet(s_minimumHeight);
1392 }
1393 
1394 tikz::Value Style::minimumWidth() const
1395 {
1396     if (propertySet(s_minimumWidth)) {
1397         return d->minimumWidth;
1398     }
1399 
1400     auto style = parentStyle().entity<Style>();
1401     if (style) {
1402         return style->minimumWidth();
1403     }
1404 
1405     return 0.0_cm;
1406 }
1407 
1408 bool Style::minimumWidthSet() const
1409 {
1410     return propertySet(s_minimumWidth);
1411 }
1412 
1413 void Style::setMinimumHeight(const tikz::Value & height)
1414 {
1415     if (!propertySet(s_minimumHeight) || d->minimumHeight != height) {
1416         ConfigTransaction transaction(this);
1417         addProperty(s_minimumHeight);
1418         d->minimumHeight = height;
1419     }
1420 }
1421 
1422 void Style::setMinimumWidth(const tikz::Value & width)
1423 {
1424     if (!propertySet(s_minimumWidth) || d->minimumWidth != width) {
1425         ConfigTransaction transaction(this);
1426         addProperty(s_minimumWidth);
1427         d->minimumWidth = width;
1428     }
1429 }
1430 
1431 void Style::unsetMinimumHeight()
1432 {
1433     if (propertySet(s_minimumHeight)) {
1434         ConfigTransaction transaction(this);
1435         removeProperty(s_minimumHeight);
1436         d->minimumHeight = 0.0_cm;
1437     }
1438 }
1439 
1440 void Style::unsetMinimumWidth()
1441 {
1442     if (propertySet(s_minimumWidth)) {
1443         ConfigTransaction transaction(this);
1444         removeProperty(s_minimumWidth);
1445         d->minimumWidth = 0.0_cm;
1446     }
1447 }
1448 
1449 
1450 }
1451 }
1452 
1453 // kate: indent-width 4; replace-tabs on;