File indexing completed on 2024-04-28 03:53:09
0001 /* 0002 This file is part of the KDE libraries 0003 SPDX-FileCopyrightText: 1999 Carsten Pfeiffer <pfeiffer@kde.org> 0004 0005 SPDX-License-Identifier: LGPL-2.0-or-later 0006 */ 0007 0008 #ifndef KCOMPLETIONMATCHESWRAPPER_P_H 0009 #define KCOMPLETIONMATCHESWRAPPER_P_H 0010 0011 #include "kcompletion.h" 0012 #include "kcomptreenode_p.h" 0013 0014 #include <kcompletionmatches.h> 0015 0016 class KCOMPLETION_EXPORT KCompletionMatchesWrapper 0017 { 0018 public: 0019 explicit KCompletionMatchesWrapper(KCompletion::SorterFunction const &sorterFunction, KCompletion::CompOrder compOrder = KCompletion::Insertion) 0020 : m_sortedListPtr(compOrder == KCompletion::Weighted ? new KCompletionMatchesList : nullptr) 0021 , m_dirty(false) 0022 , m_compOrder(compOrder) 0023 , m_sorterFunction(sorterFunction) 0024 { 0025 } 0026 0027 KCompletionMatchesWrapper(const KCompletionMatchesWrapper &) = delete; 0028 KCompletionMatchesWrapper &operator=(const KCompletionMatchesWrapper &) = delete; 0029 0030 void setSorting(KCompletion::CompOrder compOrder) 0031 { 0032 if (compOrder == KCompletion::Weighted && !m_sortedListPtr) { 0033 m_sortedListPtr = std::make_unique<KCompletionMatchesList>(); 0034 } else if (compOrder != KCompletion::Weighted) { 0035 m_sortedListPtr.reset(); 0036 } 0037 m_compOrder = compOrder; 0038 m_stringList.clear(); 0039 m_dirty = false; 0040 } 0041 0042 KCompletion::CompOrder sorting() const 0043 { 0044 return m_compOrder; 0045 } 0046 0047 void append(int i, const QString &string) 0048 { 0049 if (m_sortedListPtr) { 0050 m_sortedListPtr->insert(i, string); 0051 } else { 0052 m_stringList.append(string); 0053 } 0054 m_dirty = true; 0055 } 0056 0057 void clear() 0058 { 0059 if (m_sortedListPtr) { 0060 m_sortedListPtr->clear(); 0061 } 0062 m_stringList.clear(); 0063 m_dirty = false; 0064 } 0065 0066 uint size() const 0067 { 0068 if (m_sortedListPtr) { 0069 return m_sortedListPtr->size(); 0070 } 0071 return m_stringList.size(); 0072 } 0073 0074 bool isEmpty() const 0075 { 0076 return size() == 0; 0077 } 0078 0079 QString first() const 0080 { 0081 return list().constFirst(); 0082 } 0083 0084 QString last() const 0085 { 0086 return list().constLast(); 0087 } 0088 0089 inline QStringList list() const; 0090 0091 inline void findAllCompletions(const KCompTreeNode *, const QString &, bool ignoreCase, bool &hasMultipleMatches); 0092 0093 inline void extractStringsFromNode(const KCompTreeNode *, const QString &beginning, bool addWeight = false); 0094 0095 inline void extractStringsFromNodeCI(const KCompTreeNode *, const QString &beginning, const QString &restString); 0096 0097 mutable QStringList m_stringList; 0098 std::unique_ptr<KCompletionMatchesList> m_sortedListPtr; 0099 mutable bool m_dirty; 0100 KCompletion::CompOrder m_compOrder; 0101 KCompletion::SorterFunction const &m_sorterFunction; 0102 }; 0103 0104 void KCompletionMatchesWrapper::findAllCompletions(const KCompTreeNode *treeRoot, const QString &string, bool ignoreCase, bool &hasMultipleMatches) 0105 { 0106 // qDebug() << "*** finding all completions for " << string; 0107 0108 if (string.isEmpty()) { 0109 return; 0110 } 0111 0112 if (ignoreCase) { // case insensitive completion 0113 extractStringsFromNodeCI(treeRoot, QString(), string); 0114 hasMultipleMatches = (size() > 1); 0115 return; 0116 } 0117 0118 QString completion; 0119 const KCompTreeNode *node = treeRoot; 0120 0121 // start at the tree-root and try to find the search-string 0122 for (const QChar ch : string) { 0123 node = node->find(ch); 0124 0125 if (node) { 0126 completion += ch; 0127 } else { 0128 return; // no completion -> return empty list 0129 } 0130 } 0131 0132 // Now we have the last node of the to be completed string. 0133 // Follow it as long as it has exactly one child (= longest possible 0134 // completion) 0135 0136 while (node->childrenCount() == 1) { 0137 node = node->firstChild(); 0138 if (!node->isNull()) { 0139 completion += *node; 0140 } 0141 // qDebug() << completion << node->latin1(); 0142 } 0143 0144 // there is just one single match) 0145 if (node->childrenCount() == 0) { 0146 append(node->weight(), completion); 0147 } 0148 0149 else { 0150 // node has more than one child 0151 // -> recursively find all remaining completions 0152 hasMultipleMatches = true; 0153 extractStringsFromNode(node, completion); 0154 } 0155 } 0156 0157 QStringList KCompletionMatchesWrapper::list() const 0158 { 0159 if (m_sortedListPtr && m_dirty) { 0160 m_sortedListPtr->sort(); 0161 m_dirty = false; 0162 0163 m_stringList.clear(); 0164 m_stringList.reserve(m_sortedListPtr->size()); 0165 // high weight == sorted last -> reverse the sorting here 0166 std::transform(m_sortedListPtr->crbegin(), m_sortedListPtr->crend(), std::back_inserter(m_stringList), [](const KSortableItem<QString> &item) { 0167 return item.value(); 0168 }); 0169 } else if (m_compOrder == KCompletion::Sorted) { 0170 m_sorterFunction(m_stringList); 0171 } 0172 0173 return m_stringList; 0174 } 0175 0176 void KCompletionMatchesWrapper::extractStringsFromNode(const KCompTreeNode *node, const QString &beginning, bool addWeight) 0177 { 0178 if (!node) { 0179 return; 0180 } 0181 0182 // qDebug() << "Beginning: " << beginning; 0183 const KCompTreeChildren *list = node->children(); 0184 QString string; 0185 QString w; 0186 0187 // loop thru all children 0188 for (KCompTreeNode *cur = list->begin(); cur; cur = cur->m_next) { 0189 string = beginning; 0190 node = cur; 0191 if (!node->isNull()) { 0192 string += *node; 0193 } 0194 0195 while (node && node->childrenCount() == 1) { 0196 node = node->firstChild(); 0197 if (node->isNull()) { 0198 break; 0199 } 0200 string += *node; 0201 } 0202 0203 if (node && node->isNull()) { // we found a leaf 0204 if (addWeight) { 0205 // add ":num" to the string to store the weighting 0206 string += QLatin1Char(':'); 0207 w.setNum(node->weight()); 0208 string.append(w); 0209 } 0210 append(node->weight(), string); 0211 } 0212 0213 // recursively find all other strings. 0214 if (node && node->childrenCount() > 1) { 0215 extractStringsFromNode(node, string, addWeight); 0216 } 0217 } 0218 } 0219 0220 void KCompletionMatchesWrapper::extractStringsFromNodeCI(const KCompTreeNode *node, const QString &beginning, const QString &restString) 0221 { 0222 if (restString.isEmpty()) { 0223 extractStringsFromNode(node, beginning, false /*noweight*/); 0224 return; 0225 } 0226 0227 QChar ch1 = restString.at(0); 0228 QString newRest = restString.mid(1); 0229 KCompTreeNode *child1; 0230 KCompTreeNode *child2; 0231 0232 child1 = node->find(ch1); // the correct match 0233 if (child1) { 0234 extractStringsFromNodeCI(child1, beginning + QChar(*child1), newRest); 0235 } 0236 0237 // append the case insensitive matches, if available 0238 if (ch1.isLetter()) { 0239 // find out if we have to lower or upper it. Is there a better way? 0240 QChar ch2 = ch1.toLower(); 0241 if (ch1 == ch2) { 0242 ch2 = ch1.toUpper(); 0243 } 0244 if (ch1 != ch2) { 0245 child2 = node->find(ch2); 0246 if (child2) { 0247 extractStringsFromNodeCI(child2, beginning + QChar(*child2), newRest); 0248 } 0249 } 0250 } 0251 } 0252 0253 #endif // KCOMPLETIONMATCHESWRAPPER_P_H