File indexing completed on 2024-05-12 15:58:29

0001 /*
0002  *  SPDX-FileCopyrightText: 2009 Cyrille Berger <cberger@cberger.net>
0003  *
0004  *  SPDX-License-Identifier: GPL-2.0-or-later
0005  */
0006 
0007 #include "kis_node_query_path.h"
0008 
0009 #include <QStringList>
0010 #include <kis_node.h>
0011 #include <kis_image.h>
0012 #include <kis_paint_device.h>
0013 
0014 struct PathElement {
0015     enum Type {
0016         Wildcard,
0017         Parent,
0018         Index
0019     };
0020     PathElement(Type _type) : type(_type) {
0021         Q_ASSERT(type == Wildcard || type == Parent);
0022     }
0023     PathElement(int _i) : type(Index), index(_i) {}
0024     Type type;
0025     unsigned int index {0};
0026 };
0027 
0028 struct Q_DECL_HIDDEN KisNodeQueryPath::Private {
0029     QList<PathElement> elements;
0030     bool relative;
0031     /// This function will remove unneeded call to parent, for instance, "1/../3/../5" => "5"
0032     void simplifyPath() {
0033         // No elements then return
0034         if (elements.isEmpty()) return;
0035         QList<PathElement> newelements;
0036         int i = 0;
0037         for (; i < elements.count() && elements[i].type == PathElement::Parent; ++i) {
0038             newelements.push_back(PathElement::Parent);
0039         }
0040         // Loop ofver the element of the list
0041         for (; i < elements.count(); ++i) {
0042             PathElement pe = elements[i];
0043             // If it's the last element, or the next element isn't a parent
0044             if (pe.type != PathElement::Parent) {
0045                 newelements.push_back(pe);
0046             } else {
0047                 if (newelements.isEmpty() || newelements.last().type == PathElement::Parent) {
0048                     newelements.push_back(PathElement::Parent);
0049                 } else {
0050                     newelements.removeLast();
0051                 }
0052             }
0053         }
0054         // Set the new list
0055         elements = newelements;
0056     }
0057     void queryLevel(int _level, KisNodeSP _node, QList<KisNodeSP>& _result) {
0058         if (_level >= elements.size()) {
0059             _result.push_back(_node);
0060         } else {
0061             PathElement pe = elements[_level];
0062 
0063             switch (pe.type) {
0064             case PathElement::Wildcard: {
0065                 for (KisNodeSP child = _node->firstChild();
0066                         child != 0; child = child->nextSibling()) {
0067                     queryLevel(_level + 1, child, _result);
0068                 }
0069             }
0070             break;
0071             case PathElement::Parent: {
0072                 if (_node->parent()) {
0073                     queryLevel(_level + 1, _node->parent(), _result);
0074                 } else {
0075                     dbgKrita << "No parent";
0076                 }
0077                 break;
0078             }
0079             case PathElement::Index: {
0080                 if (pe.index < _node->childCount()) {
0081                     queryLevel(_level + 1, _node->at(pe.index), _result);
0082                 } else {
0083                     dbgKrita << "No parent";
0084                 }
0085                 break;
0086             }
0087             }
0088         }
0089     }
0090 };
0091 
0092 KisNodeQueryPath::KisNodeQueryPath() : d(new Private)
0093 {
0094 }
0095 
0096 KisNodeQueryPath::~KisNodeQueryPath()
0097 {
0098     delete d;
0099 }
0100 
0101 KisNodeQueryPath::KisNodeQueryPath(const KisNodeQueryPath& nqp) : d(new Private(*nqp.d))
0102 {
0103 }
0104 
0105 KisNodeQueryPath& KisNodeQueryPath::operator=(const KisNodeQueryPath & nqp)
0106 {
0107     *d = *nqp.d;
0108     return *this;
0109 }
0110 
0111 bool KisNodeQueryPath::isRelative() const
0112 {
0113     return d->relative;
0114 }
0115 
0116 
0117 QList<KisNodeSP> KisNodeQueryPath::queryNodes(KisImageWSP image, KisNodeSP currentNode) const
0118 {
0119     KisNodeSP _node;
0120     if (d->relative) {
0121         _node = currentNode;
0122     } else {
0123         _node = image->root();
0124     }
0125 
0126     QList<KisNodeSP> result;
0127 
0128     d->queryLevel(0, _node, result);
0129 
0130     return result;
0131 }
0132 
0133 KisNodeSP KisNodeQueryPath::queryUniqueNode(KisImageWSP image, KisNodeSP currentNode) const
0134 {
0135     QList<KisNodeSP> result = queryNodes(image, currentNode);
0136     KIS_ASSERT_RECOVER_NOOP(result.size() <= 1);
0137 
0138     return !result.isEmpty() ? result.first() : 0;
0139 }
0140 
0141 QString KisNodeQueryPath::toString() const
0142 {
0143     QString str;
0144     if (!d->relative) {
0145         str = '/';
0146     } else if (d->elements.count() == 0) {
0147         return QString('.');
0148     }
0149     for (int i = 0; i < d->elements.count(); ++i) {
0150         PathElement pe = d->elements[i];
0151         switch (pe.type) {
0152         case PathElement::Wildcard:
0153             str += '*';
0154             break;
0155         case PathElement::Parent:
0156             str += "..";
0157             break;
0158         case PathElement::Index:
0159             str += QString::number(pe.index);
0160             break;
0161         }
0162         if (i != d->elements.count() - 1) {
0163             str += '/';
0164         }
0165     }
0166     return str;
0167 }
0168 
0169 KisNodeQueryPath KisNodeQueryPath::fromString(const QString& _path)
0170 {
0171     KisNodeQueryPath path;
0172     if (_path.size() == 0 || _path == ".") {
0173         path.d->relative = true;
0174         return path;
0175     }
0176     if (_path == "/") {
0177         path.d->relative = false;
0178         return path;
0179     }
0180     path.d->relative = !(_path.at(0) == '/');
0181     QStringList indexes = _path.split('/');
0182     if (!path.d->relative) {
0183         indexes.pop_front(); // In case of an absolute path "/1/2", the list is "", "1", "2" which is not good
0184     }
0185     Q_FOREACH (const QString& index, indexes) {
0186         if (index == "*") {
0187             path.d->elements.push_back(PathElement::Wildcard);
0188         } else if (index == "..") {
0189             path.d->elements.push_back(PathElement::Parent);
0190         } else {
0191             path.d->elements.push_back(index.toInt());
0192         }
0193     }
0194     path.d->simplifyPath();
0195     return path;
0196 }
0197 
0198 KisNodeQueryPath KisNodeQueryPath::absolutePath(KisNodeSP node)
0199 {
0200     KisNodeQueryPath path;
0201     path.d->relative = false;
0202     KisNodeSP parent = 0;
0203     while ((parent = node->parent())) {
0204         int index = parent->index(node);
0205         if (index >= 0) {
0206             path.d->elements.push_front(index);
0207         }
0208         node = parent;
0209     }
0210     return path;
0211 }
0212 
0213