KPimTextEdit

texteditorcompleter.cpp
1 /*
2  * SPDX-FileCopyrightText: 2015-2022 Laurent Montel <[email protected]>
3  *
4  * SPDX-License-Identifier: LGPL-2.1-or-later
5  */
6 
7 #include "texteditorcompleter.h"
8 #include <QAbstractItemView>
9 #include <QCompleter>
10 #include <QPlainTextEdit>
11 #include <QScrollBar>
12 #include <QStringListModel>
13 #include <QTextEdit>
14 
15 using namespace KPIMTextEdit;
16 
17 class TextEditorCompleter::TextEditorCompleterPrivate
18 {
19 public:
20  TextEditorCompleterPrivate(QTextEdit *editor, TextEditorCompleter *qq)
21  : textEdit(editor)
22  , q(qq)
23  {
24  createCompleter();
25  }
26 
27  TextEditorCompleterPrivate(QPlainTextEdit *editor, TextEditorCompleter *qq)
28  : plainTextEdit(editor)
29  , q(qq)
30  {
31  createCompleter();
32  }
33 
34  void setCompletion(const QString &completion);
35  Q_REQUIRED_RESULT QString wordUnderCursor() const;
36  void createCompleter();
37  void completeText();
38  QString excludeOfCharacters;
39  QCompleter *completer = nullptr;
40  QPlainTextEdit *plainTextEdit = nullptr;
41  QTextEdit *textEdit = nullptr;
42  TextEditorCompleter *const q;
43 };
44 
45 void TextEditorCompleter::TextEditorCompleterPrivate::createCompleter()
46 {
47  if (!completer) {
48  completer = new QCompleter(q);
49  }
50  if (plainTextEdit) {
51  completer->setWidget(plainTextEdit);
52  } else {
53  completer->setWidget(textEdit);
54  }
55  completer->setModelSorting(QCompleter::CaseSensitivelySortedModel);
56  completer->setCaseSensitivity(Qt::CaseInsensitive);
57  completer->setCompletionMode(QCompleter::PopupCompletion);
58  connect(completer, qOverload<const QString &>(&QCompleter::activated), q, &TextEditorCompleter::slotCompletion);
59 }
60 
61 QString TextEditorCompleter::TextEditorCompleterPrivate::wordUnderCursor() const
62 {
63  static QString eow = QStringLiteral("[email protected]#$%^&*()+{}|\"<>,./;'[]\\-= "); // everything without ':', '?' and '_'
64  QTextCursor tc;
65  QTextDocument *document = nullptr;
66  if (plainTextEdit) {
67  tc = plainTextEdit->textCursor();
68  document = plainTextEdit->document();
69  } else {
70  tc = textEdit->textCursor();
71  document = textEdit->document();
72  }
73 
74  tc.anchor();
75  const QString eowStr = excludeOfCharacters.isEmpty() ? eow : excludeOfCharacters;
76  while (true) {
77  // vHanda: I don't understand why the cursor seems to give a pos 1 past the last char instead
78  // of just the last char.
79  int pos = tc.position() - 1;
80  if (pos < 0 || eowStr.contains(document->characterAt(pos)) || document->characterAt(pos) == QChar(QChar::LineSeparator)
81  || document->characterAt(pos) == QChar(QChar::ParagraphSeparator)) {
82  break;
83  }
85  }
86  return tc.selectedText();
87 }
88 
89 void TextEditorCompleter::TextEditorCompleterPrivate::setCompletion(const QString &completion)
90 {
91  QTextCursor tc;
92  if (plainTextEdit) {
93  tc = plainTextEdit->textCursor();
94  } else {
95  tc = textEdit->textCursor();
96  }
97  const int extra = completion.length() - completer->completionPrefix().length();
100  tc.insertText(completion.right(extra));
101  if (plainTextEdit) {
102  plainTextEdit->setTextCursor(tc);
103  } else {
104  textEdit->setTextCursor(tc);
105  }
106 }
107 
108 void TextEditorCompleter::TextEditorCompleterPrivate::completeText()
109 {
110  if (!completer) {
111  return;
112  }
113  const QString text = wordUnderCursor();
114  if (text.length() < 2) { // min 2 char for completion
115  return;
116  }
117 
118  completer->setCompletionPrefix(text);
119 
120  QRect cr;
121  if (plainTextEdit) {
122  cr = plainTextEdit->cursorRect();
123  } else {
124  cr = textEdit->cursorRect();
125  }
126  cr.setWidth(completer->popup()->sizeHintForColumn(0) + completer->popup()->verticalScrollBar()->sizeHint().width());
127  completer->complete(cr);
128 }
129 
130 TextEditorCompleter::TextEditorCompleter(QTextEdit *editor, QObject *parent)
131  : QObject(parent)
132  , d(new TextEditorCompleter::TextEditorCompleterPrivate(editor, this))
133 {
134 }
135 
136 TextEditorCompleter::TextEditorCompleter(QPlainTextEdit *editor, QObject *parent)
137  : QObject(parent)
138  , d(new TextEditorCompleter::TextEditorCompleterPrivate(editor, this))
139 {
140 }
141 
142 TextEditorCompleter::~TextEditorCompleter() = default;
143 
144 QCompleter *TextEditorCompleter::completer() const
145 {
146  return d->completer;
147 }
148 
149 void TextEditorCompleter::setCompleterStringList(const QStringList &listWord)
150 {
151  d->completer->setModel(new QStringListModel(QStringList() << listWord << QStringLiteral("TESTING"), d->completer));
152 }
153 
154 void TextEditorCompleter::slotCompletion(const QString &completion)
155 {
156  d->setCompletion(completion);
157 }
158 
159 void TextEditorCompleter::completeText()
160 {
161  d->completeText();
162 }
163 
164 void TextEditorCompleter::setExcludeOfCharacters(const QString &excludes)
165 {
166  d->excludeOfCharacters = excludes;
167 }
void setWidth(int width)
The TextEditorCompleter class.
CaseInsensitive
QString selectedText() const const
int anchor() const const
bool isEmpty() const const
int length() const const
QChar characterAt(int pos) const const
CaseSensitivelySortedModel
void insertText(const QString &text)
bool contains(QChar ch, Qt::CaseSensitivity cs) const const
bool movePosition(QTextCursor::MoveOperation operation, QTextCursor::MoveMode mode, int n)
const QList< QKeySequence > & completion()
int position() const const
void activated(const QString &text)
This file is part of the KDE documentation.
Documentation copyright © 1996-2022 The KDE developers.
Generated on Fri May 27 2022 04:12:49 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.