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

0001 /* This file is part of the TikZKit project.
0002  *
0003  * Copyright (C) 2013-2014 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 "MetaPos.h"
0021 #include "MetaPos_p.h"
0022 
0023 #include "Document.h"
0024 #include "Node.h"
0025 
0026 #include <QDebug>
0027 
0028 namespace tikz {
0029 namespace core {
0030 
0031 MetaPos::MetaPos(Document * doc)
0032     : d(new MetaPosPrivate(this))
0033 {
0034     Q_ASSERT(doc != nullptr);
0035     d->doc = doc;
0036 
0037     Q_ASSERT(! d->nodeId.isValid());
0038     Q_ASSERT(d->anchor.isEmpty());
0039 }
0040 
0041 MetaPos::MetaPos(const MetaPos & pos)
0042     : d(new MetaPosPrivate(this))
0043 {
0044     d->doc = pos.d->doc;
0045 
0046     // now copy object
0047     *this = pos;
0048 }
0049 
0050 MetaPos::MetaPos(const QString & str, Document * document)
0051     : MetaPos(document)
0052 {
0053     fromString(str);
0054 }
0055 
0056 MetaPos::~MetaPos()
0057 {
0058     delete d;
0059 }
0060 
0061 Document * MetaPos::document() const
0062 {
0063     return d->doc;
0064 }
0065 
0066 QString MetaPos::toString() const
0067 {
0068     if (node()) {
0069         return QString("(%1%2)")
0070             .arg(d->nodeId.toString())
0071             .arg(d->anchor.isEmpty() ? QString() : (QLatin1Char('.') + d->anchor));
0072     } else {
0073         return d->pos.toString();
0074     }
0075 }
0076 
0077 void MetaPos::fromString(const QString & str)
0078 {
0079     if (str.contains(QLatin1Char(','))) {
0080         setPos(tikz::Pos::fromString(str));
0081     } else {
0082         const int openIndex = str.indexOf(QLatin1Char('('));
0083         const int dotIndex = str.indexOf(QLatin1Char('.'));
0084         const int closeIndex = str.indexOf(QLatin1Char(')'));
0085 
0086         Q_ASSERT(openIndex >= 0);
0087         Q_ASSERT(closeIndex >= openIndex);
0088 
0089         d->beginChange();
0090         const int endIndex = (dotIndex > 0) ? dotIndex : closeIndex;
0091         bool ok;
0092         d->nodeId = Uid(str.mid(openIndex + 1, endIndex - (openIndex + 1)).toLongLong(&ok), d->doc);
0093         Q_ASSERT(ok);
0094 
0095         // read the anchor
0096         if (dotIndex > 0) {
0097             d->anchor = str.mid(dotIndex + 1, closeIndex - (dotIndex + 1));
0098         } else {
0099             d->anchor.clear();
0100         }
0101         d->endChange();
0102     }
0103 }
0104 
0105 MetaPos & MetaPos::operator=(const MetaPos & other)
0106 {
0107     if (*this == other) {
0108         return *this;
0109     }
0110 
0111     // for now, only same Document is supported
0112     Q_ASSERT(d->doc == other.d->doc);
0113 
0114     // start copying private data
0115     d->beginChange();
0116 
0117     // call setNode() to properly set up signals & slots
0118     setNode(other.node());
0119 
0120     // now copy rest
0121     d->doc = other.d->doc;
0122     d->pos = other.d->pos;
0123     d->anchor = other.d->anchor;
0124 
0125     // calls changed
0126     d->endChange();
0127 
0128     return *this;
0129 }
0130 
0131 bool MetaPos::operator==(const MetaPos & other) const
0132 {
0133     if (&other == this) {
0134         return true;
0135     }
0136 
0137     if (d->doc != other.d->doc) {
0138         Q_ASSERT(d->doc == other.d->doc);
0139         return false;
0140     }
0141 
0142     if (d->nodeId.isValid() || other.d->nodeId.isValid()) {
0143         return d->nodeId == other.d->nodeId
0144             && d->anchor == other.d->anchor;
0145     }
0146 
0147     return d->pos == other.d->pos;
0148 }
0149 
0150 bool MetaPos::operator!=(const MetaPos & other) const
0151 {
0152     return ! (*this == other);
0153 }
0154 
0155 tikz::Pos MetaPos::pos() const
0156 {
0157     if (! d->nodeId.isValid()) {
0158         Q_ASSERT(node() == nullptr);
0159         return d->pos;
0160     }
0161 
0162     Q_ASSERT(node() != nullptr);
0163     return document()->scenePos(*this);
0164 }
0165 
0166 void MetaPos::setPos(const tikz::Pos & pos)
0167 {
0168     bool change = false;
0169 
0170     // detach from node, if required
0171     Node * oldNode = node();
0172     if (oldNode) {
0173         // disconnect changed() signal
0174         QObject::disconnect(oldNode, nullptr, d, nullptr);
0175         d->nodeId = Uid();
0176 
0177         change = true;
0178     }
0179 
0180     if (d->pos != pos) {
0181         // update pos
0182         d->pos = pos;
0183 
0184         change = true;
0185     }
0186 
0187     // notify about change
0188     if (change) {
0189         d->changeRequest();
0190     }
0191 }
0192 
0193 bool MetaPos::setNode(Node* newNode)
0194 {
0195     Node * curNode = node();
0196 
0197     // if equal, stop
0198     if (curNode == newNode) {
0199         return false;
0200     }
0201 
0202     // start changing this MetaPos
0203     d->beginChange();
0204 
0205     // detach from old node
0206     if (curNode) {
0207         // disconnect changed() signal
0208         QObject::disconnect(curNode, nullptr, d, nullptr);
0209 
0210         // update pos in case the newNode is 0
0211         d->pos = pos();
0212     }
0213 
0214     // set new node and forward change() signal if applicable
0215     d->nodeId = newNode ? newNode->uid() : Uid();
0216     curNode = node();
0217 
0218     // attach to newNode
0219     if (curNode) {
0220         // connect changed() signal to helper object
0221         QObject::connect(curNode, SIGNAL(changed()), d, SLOT(changeRequest()));
0222     }
0223 
0224     // reset anchor
0225     d->anchor.clear();
0226 
0227     // notify about change
0228     d->endChange();
0229 
0230     // node was changed
0231     return true;
0232 }
0233 
0234 Node* MetaPos::node() const
0235 {
0236     return d->nodeId.entity<Node>();
0237 }
0238 
0239 void MetaPos::setAnchor(const QString & anchor)
0240 {
0241     // setting an anchor only makes sense with a node
0242     Q_ASSERT(d->nodeId.isValid());
0243 
0244     if (d->anchor != anchor) {
0245         d->beginChange();
0246         d->anchor = anchor;
0247         d->endChange();
0248     }
0249 }
0250 
0251 QString MetaPos::anchor() const
0252 {
0253     return (d->nodeId.isValid()) ? d->anchor : QString();
0254 }
0255 
0256 QObject * MetaPos::notificationObject()
0257 {
0258     return d;
0259 }
0260 
0261 }
0262 }
0263 
0264 namespace QTest {
0265     // Value: template specialization for QTest::toString()
0266     template<>
0267     char *toString(const tikz::core::MetaPos & metaPos)
0268     {
0269         // FIXME: Maybe not QCOMPARE fails due to different units, this may need a fix
0270         const QString str = "MetaPos[" + metaPos.toString() + "]";
0271         const QByteArray ba = str.toLatin1();
0272         return qstrdup(ba.data());
0273     }
0274 }
0275 
0276 // kate: indent-width 4; replace-tabs on;