KCompletion

kcompletion.cpp
1 /*
2  This file is part of the KDE libraries
3  SPDX-FileCopyrightText: 1999, 2000, 2001 Carsten Pfeiffer <[email protected]>
4 
5  SPDX-License-Identifier: LGPL-2.0-or-later
6 */
7 
8 #include "kcompletion.h"
9 #include "kcompletion_p.h"
10 #include <kcompletion_debug.h>
11 
12 #include <QCollator>
13 
14 void KCompletionPrivate::addWeightedItem(const QString &item)
15 {
16  Q_Q(KCompletion);
17  if (order != KCompletion::Weighted) {
18  q->addItem(item, 0);
19  return;
20  }
21 
22  int len = item.length();
23  uint weight = 0;
24 
25  // find out the weighting of this item (appended to the string as ":num")
26  int index = item.lastIndexOf(QLatin1Char(':'));
27  if (index > 0) {
28  bool ok;
29 #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
30  weight = QStringView(item).mid(index + 1).toUInt(&ok);
31 #else
32  weight = item.midRef(index + 1).toUInt(&ok);
33 #endif
34  if (!ok) {
35  weight = 0;
36  }
37 
38  len = index; // only insert until the ':'
39  }
40 
41  q->addItem(item.left(len), weight);
42  return;
43 }
44 
45 // tries to complete "string" from the tree-root
46 QString KCompletionPrivate::findCompletion(const QString &string)
47 {
49  const KCompTreeNode *node = m_treeRoot.get();
50 
51  // start at the tree-root and try to find the search-string
52  for (const auto ch : string) {
53  node = node->find(ch);
54 
55  if (node) {
56  completion += ch;
57  } else {
58  return QString(); // no completion
59  }
60  }
61 
62  // Now we have the last node of the to be completed string.
63  // Follow it as long as it has exactly one child (= longest possible
64  // completion)
65 
66  while (node->childrenCount() == 1) {
67  node = node->firstChild();
68  if (!node->isNull()) {
69  completion += *node;
70  }
71  }
72  // if multiple matches and auto-completion mode
73  // -> find the first complete match
74  if (node && node->childrenCount() > 1) {
75  hasMultipleMatches = true;
76 
78  rotationIndex = 1;
79  if (order != KCompletion::Weighted) {
80  while ((node = node->firstChild())) {
81  if (!node->isNull()) {
82  completion += *node;
83  } else {
84  break;
85  }
86  }
87  } else {
88  // don't just find the "first" match, but the one with the
89  // highest priority
90 
91  const KCompTreeNode *temp_node = nullptr;
92  while (1) {
93  int count = node->childrenCount();
94  temp_node = node->firstChild();
95  uint weight = temp_node->weight();
96  const KCompTreeNode *hit = temp_node;
97  for (int i = 1; i < count; i++) {
98  temp_node = node->childAt(i);
99  if (temp_node->weight() > weight) {
100  hit = temp_node;
101  weight = hit->weight();
102  }
103  }
104  // 0x0 has the highest priority -> we have the best match
105  if (hit->isNull()) {
106  break;
107  }
108 
109  node = hit;
110  completion += *node;
111  }
112  }
113  }
114  }
115 
116  return completion;
117 }
118 
119 void KCompletionPrivate::defaultSort(QStringList &stringList)
120 {
121  QCollator c;
123  std::stable_sort(stringList.begin(), stringList.end(), c);
124 }
125 
127  : d_ptr(new KCompletionPrivate(this))
128 {
130 }
131 
133 {
134 }
135 
137 {
138  Q_D(KCompletion);
139  d->order = order;
140  d->matches.setSorting(order);
141 }
142 
143 KCompletion::CompOrder KCompletion::order() const
144 {
145  Q_D(const KCompletion);
146  return d->order;
147 }
148 
149 void KCompletion::setIgnoreCase(bool ignoreCase)
150 {
151  Q_D(KCompletion);
152  d->ignoreCase = ignoreCase;
153 }
154 
155 bool KCompletion::ignoreCase() const
156 {
157  Q_D(const KCompletion);
158  return d->ignoreCase;
159 }
160 
161 void KCompletion::setItems(const QStringList &itemList)
162 {
163  clear();
164  insertItems(itemList);
165 }
166 
168 {
169  Q_D(KCompletion);
170  for (const auto &str : items) {
171  if (d->order == Weighted) {
172  d->addWeightedItem(str);
173  } else {
174  addItem(str, 0);
175  }
176  }
177 }
178 
179 QStringList KCompletion::items() const
180 {
181  Q_D(const KCompletion);
182  KCompletionMatchesWrapper list(d->sorterFunction); // unsorted
183  list.extractStringsFromNode(d->m_treeRoot.get(), QString(), d->order == Weighted);
184  return list.list();
185 }
186 
188 {
189  Q_D(const KCompletion);
190  return (d->m_treeRoot->childrenCount() == 0);
191 }
192 
194 {
195 }
196 
198 {
199 }
200 
202 {
203 }
204 
205 void KCompletion::addItem(const QString &item)
206 {
207  Q_D(KCompletion);
208  d->matches.clear();
209  d->rotationIndex = 0;
210  d->lastString.clear();
211 
212  addItem(item, 0);
213 }
214 
215 void KCompletion::addItem(const QString &item, uint weight)
216 {
217  Q_D(KCompletion);
218  if (item.isEmpty()) {
219  return;
220  }
221 
222  KCompTreeNode *node = d->m_treeRoot.get();
223  int len = item.length();
224 
225  bool sorted = (d->order == Sorted);
226  bool weighted = ((d->order == Weighted) && weight > 1);
227 
228  // knowing the weight of an item, we simply add this weight to all of its
229  // nodes.
230 
231  for (int i = 0; i < len; i++) {
232  node = node->insert(item.at(i), sorted);
233  if (weighted) {
234  node->confirm(weight - 1); // node->insert() sets weighting to 1
235  }
236  }
237 
238  // add 0x0-item as delimiter with evtl. weight
239  node = node->insert(QChar(0x0), true);
240  if (weighted) {
241  node->confirm(weight - 1);
242  }
243  // qDebug("*** added: %s (%i)", item.toLatin1().constData(), node->weight());
244 }
245 
247 {
248  Q_D(KCompletion);
249  d->matches.clear();
250  d->rotationIndex = 0;
251  d->lastString.clear();
252 
253  d->m_treeRoot->remove(item);
254 }
255 
257 {
258  Q_D(KCompletion);
259  d->matches.clear();
260  d->rotationIndex = 0;
261  d->lastString.clear();
262 
263  d->m_treeRoot.reset(new KCompTreeNode);
264 }
265 
267 {
268  Q_D(KCompletion);
269  if (d->completionMode == CompletionNone) {
270  return QString();
271  }
272 
273  // qDebug() << "KCompletion: completing: " << string;
274 
275  d->matches.clear();
276  d->rotationIndex = 0;
277  d->hasMultipleMatches = false;
278  d->lastMatch = d->currentMatch;
279 
280  // in Shell-completion-mode, emit all matches when we get the same
281  // complete-string twice
282  if (d->completionMode == CompletionShell && string == d->lastString) {
283  // Don't use d->matches since calling postProcessMatches()
284  // on d->matches here would interfere with call to
285  // postProcessMatch() during rotation
286 
287  d->matches.findAllCompletions(d->m_treeRoot.get(), string, d->ignoreCase, d->hasMultipleMatches);
288  QStringList l = d->matches.list();
289  postProcessMatches(&l);
290  Q_EMIT matches(l);
291 
292  return QString();
293  }
294 
296  // in case-insensitive popup mode, we search all completions at once
297  if (d->completionMode == CompletionPopup || d->completionMode == CompletionPopupAuto) {
298  d->matches.findAllCompletions(d->m_treeRoot.get(), string, d->ignoreCase, d->hasMultipleMatches);
299  if (!d->matches.isEmpty()) {
300  completion = d->matches.first();
301  }
302  } else {
303  completion = d->findCompletion(string);
304  }
305 
306  if (d->hasMultipleMatches) {
308  }
309 
310  d->lastString = string;
311  d->currentMatch = completion;
312 
314 
315  if (!string.isEmpty()) { // only emit match when string is not empty
316  // qDebug() << "KCompletion: Match: " << completion;
318  }
319 
320  return completion;
321 }
322 
324 {
325  Q_D(const KCompletion);
326  // get all items in the tree, eventually in sorted order
327  KCompletionMatchesWrapper allItems(d->sorterFunction, d->order);
328  allItems.extractStringsFromNode(d->m_treeRoot.get(), QString(), false);
329 
330  QStringList list = allItems.list();
331 
332  // subStringMatches is invoked manually, via a shortcut
333  if (list.isEmpty()) {
334  return list;
335  }
336 
337  if (!string.isEmpty()) { // If it's empty, nothing to compare
338  auto it = std::remove_if(list.begin(), list.end(), [&string](const QString &item) {
339  return !item.contains(string, Qt::CaseInsensitive); // always case insensitive
340  });
341  list.erase(it, list.end());
342  }
343 
344  postProcessMatches(&list);
345  return list;
346 }
347 
349 {
350  Q_D(KCompletion);
351  d->completionMode = mode;
352 }
353 
355 {
356  Q_D(const KCompletion);
357  return d->completionMode;
358 }
359 
360 void KCompletion::setShouldAutoSuggest(const bool shouldAutoSuggest)
361 {
362  Q_D(KCompletion);
363  d->shouldAutoSuggest = shouldAutoSuggest;
364 }
365 
367 {
368  Q_D(const KCompletion);
369  return d->shouldAutoSuggest;
370 }
371 
373 {
374  Q_D(KCompletion);
375  d->sorterFunction = sortFunc ? sortFunc : KCompletionPrivate::defaultSort;
376 }
377 
379 {
380  Q_D(KCompletion);
381  // Don't use d->matches since calling postProcessMatches()
382  // on d->matches here would interfere with call to
383  // postProcessMatch() during rotation
384  KCompletionMatchesWrapper matches(d->sorterFunction, d->order);
385  bool dummy;
386  matches.findAllCompletions(d->m_treeRoot.get(), d->lastString, d->ignoreCase, dummy);
387  QStringList l = matches.list();
388  postProcessMatches(&l);
389  return l;
390 }
391 
393 {
394  Q_D(KCompletion);
395  // Don't use d->matches since calling postProcessMatches()
396  // on d->matches here would interfere with call to
397  // postProcessMatch() during rotation
398  KCompletionMatchesWrapper matches(d->sorterFunction, d->order);
399  bool dummy;
400  matches.findAllCompletions(d->m_treeRoot.get(), d->lastString, d->ignoreCase, dummy);
402  postProcessMatches(&ret);
403  return ret;
404 }
405 
407 {
408  Q_D(KCompletion);
409  KCompletionMatchesWrapper matches(d->sorterFunction, d->order);
410  bool dummy;
411  matches.findAllCompletions(d->m_treeRoot.get(), string, d->ignoreCase, dummy);
412  QStringList l = matches.list();
413  postProcessMatches(&l);
414  return l;
415 }
416 
418 {
419  Q_D(KCompletion);
420  KCompletionMatchesWrapper matches(d->sorterFunction, d->order);
421  bool dummy;
422  matches.findAllCompletions(d->m_treeRoot.get(), string, d->ignoreCase, dummy);
424  postProcessMatches(&ret);
425  return ret;
426 }
427 
429 {
430  Q_D(KCompletion);
431  d->beep = enable;
432 }
433 
435 {
436  Q_D(const KCompletion);
437  return d->beep;
438 }
439 
441 {
442  Q_D(const KCompletion);
443  return d->hasMultipleMatches;
444 }
445 
446 /////////////////////////////////////////////////////
447 ///////////////// tree operations ///////////////////
448 
450 {
451  Q_D(KCompletion);
453  d->lastMatch = d->currentMatch;
454 
455  if (d->matches.isEmpty()) {
456  d->matches.findAllCompletions(d->m_treeRoot.get(), d->lastString, d->ignoreCase, d->hasMultipleMatches);
457  if (!d->matches.isEmpty()) {
458  completion = d->matches.first();
459  }
460  d->currentMatch = completion;
461  d->rotationIndex = 0;
464  return completion;
465  }
466 
467  QStringList matches = d->matches.list();
468  d->lastMatch = matches[d->rotationIndex++];
469 
470  if (d->rotationIndex == matches.count()) {
471  d->rotationIndex = 0;
472  }
473 
474  completion = matches[d->rotationIndex];
475  d->currentMatch = completion;
478  return completion;
479 }
480 
482 {
483  Q_D(const KCompletion);
484  return d->lastMatch;
485 }
486 
488 {
489  Q_D(KCompletion);
491  d->lastMatch = d->currentMatch;
492 
493  if (d->matches.isEmpty()) {
494  d->matches.findAllCompletions(d->m_treeRoot.get(), d->lastString, d->ignoreCase, d->hasMultipleMatches);
495  if (!d->matches.isEmpty()) {
496  completion = d->matches.last();
497  }
498  d->currentMatch = completion;
499  d->rotationIndex = 0;
502  return completion;
503  }
504 
505  QStringList matches = d->matches.list();
506  d->lastMatch = matches[d->rotationIndex];
507 
508  if (d->rotationIndex == 0) {
509  d->rotationIndex = matches.count();
510  }
511 
512  d->rotationIndex--;
513 
514  completion = matches[d->rotationIndex];
515  d->currentMatch = completion;
518  return completion;
519 }
520 
521 QSharedPointer<KZoneAllocator> KCompTreeNode::m_alloc(new KZoneAllocator(8 * 1024));
522 
523 #include "moc_kcompletion.cpp"
QString previousMatch()
Returns the next item from the list of matching items.
CompletionMode completionMode() const
Returns the current completion mode.
@ CompletionAuto
Text is automatically filled in whenever possible.
Definition: kcompletion.h:139
@ CompletionPopup
Lists all possible matches in a popup list box to choose from.
Definition: kcompletion.h:151
virtual void postProcessMatch(QString *match) const
This method is called after a completion is found and before the matching string is emitted.
virtual void setItems(const QStringList &itemList)
Sets the list of items available for completion.
CaseSensitive
Q_EMITQ_EMIT
QStringRef midRef(int position, int n) const const
virtual void setSoundsEnabled(bool enable)
Enables/disables emitting a sound when.
QStringView mid(qsizetype start) const const
A generic class for completing QStrings.
Definition: kcompletion.h:117
void setSorterFunction(SorterFunction sortFunc)
Sets a custom function to be used to sort the matches.
void setShouldAutoSuggest(bool shouldAutosuggest)
Deriving classes may set this property and control whether the auto-suggestion should be displayed fo...
@ Weighted
Use weighted order.
Definition: kcompletion.h:166
int lastIndexOf(QChar ch, int from, Qt::CaseSensitivity cs) const const
CompletionMode
This enum describes the completion mode used for by the KCompletion class.
Definition: kcompletion.h:131
@ CompletionNone
No completion is used.
Definition: kcompletion.h:135
virtual const QString & lastMatch() const
Returns the last match.
@ Insertion
Use order of insertion.
Definition: kcompletion.h:165
CompOrder
Constants that represent the order in which KCompletion performs completion lookups.
Definition: kcompletion.h:163
void insertItems(const QStringList &items)
Inserts items into the list of possible completions.
~KCompletion() override
Destructor, nothing special here, either.
void match(const QString &item)
This signal is emitted when a match is found.
List for keeping matches returned from KCompletion.
QStringList substringCompletion(const QString &string) const
Returns a list of all completion items that contain the given string.
void matches(const QStringList &matchlist)
This signal is emitted by makeCompletion() in shell-completion mode when the same string is passed to...
KCompletion::CompletionMode completionMode() const
Returns the current completion mode.
QString nextMatch()
Returns the next item from the list of matching items.
bool isEmpty() const const
int length() const const
bool isEmpty() const
Returns true if the completion object contains no entries.
bool isEmpty() const const
@ CompletionShell
Completes text much in the same way as a typical *nix shell would.
Definition: kcompletion.h:147
virtual void setIgnoreCase(bool ignoreCase)
Setting this to true makes KCompletion behave case insensitively.
virtual void setCompletionMode(CompletionMode mode)
Sets the completion mode.
QStringList allMatches()
Returns a list of all items matching the last completed string.
@ CompletionPopupAuto
Lists all possible matches in a popup list box to choose from, and automatically fills the result whe...
Definition: kcompletion.h:156
virtual QString makeCompletion(const QString &string)
Attempts to find an item in the list of available completions that begins with string.
uint toUInt(bool *ok, int base) const const
bool hasMultipleMatches() const
Returns true when more than one match is found.
void multipleMatches()
This signal is emitted when calling makeCompletion() and more than one matching item is found.
QList::iterator erase(QList::iterator pos)
QString left(int n) const const
virtual void postProcessMatches(QStringList *matchList) const
This method is called before a list of all available completions is emitted via matches().
void removeItem(const QString &item)
Removes an item from the list of available completions.
bool shouldAutoSuggest() const
Informs the caller if they should display the auto-suggestion for the last completion operation perfo...
void addItem(const QString &item)
Adds an item to the list of available completions.
const QChar at(int position) const const
QList::iterator begin()
std::function< void(QStringList &)> SorterFunction
The sorter function signature.
Definition: kcompletion.h:176
KCompletionMatches allWeightedMatches()
Returns a list of all items matching the last completed string.
virtual void clear()
Removes all inserted items.
bool soundsEnabled() const
Tells you whether KCompletion will emit sounds on certain occasions.
QList::iterator end()
void setCaseSensitivity(Qt::CaseSensitivity sensitivity)
KCompletion()
Constructor, nothing special here :)
Q_D(Todo)
@ Sorted
Use alphabetically sorted order or custom sorter logic.
Definition: kcompletion.h:164
void completion(const QString &)
Emitted when the completion key is pressed.
virtual void setOrder(CompOrder order)
KCompletion offers three different ways in which it offers its items:
This file is part of the KDE documentation.
Documentation copyright © 1996-2023 The KDE developers.
Generated on Thu Dec 7 2023 03:58:04 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.