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