Sonnet

spellcheckhighlighter.cpp
1// SPDX-FileCopyrightText: 2013 Aurélien Gâteau <agateau@kde.org>
2// SPDX-FileCopyrightText: 2020 Christian Mollekopf <mollekopf@kolabsystems.com>
3// SPDX-FileCopyrightText: 2021 Carl Schwan <carlschwan@kde.org>
4// SPDX-License-Identifier: LGPL-2.1-or-later
5
6#include "spellcheckhighlighter.h"
7#include "guesslanguage.h"
8#include "languagefilter_p.h"
9#include "loader_p.h"
10#include "settingsimpl_p.h"
11#include "speller.h"
12#include "tokenizer_p.h"
13
14#include "quick_debug.h"
15
16#include <QColor>
17#include <QHash>
18#include <QKeyEvent>
19#include <QMetaMethod>
20#include <QTextBoundaryFinder>
21#include <QTextCharFormat>
22#include <QTextCursor>
23#include <QTimer>
24#include <memory>
25
26using namespace Sonnet;
27
28// Cache of previously-determined languages (when using AutoDetectLanguage)
29// There is one such cache per block (paragraph)
30class LanguageCache : public QTextBlockUserData
31{
32public:
33 // Key: QPair<start, length>
34 // Value: language name
35 QMap<QPair<int, int>, QString> languages;
36
37 // Remove all cached language information after @p pos
38 void invalidate(int pos)
39 {
40 QMutableMapIterator<QPair<int, int>, QString> it(languages);
41 it.toBack();
42 while (it.hasPrevious()) {
43 it.previous();
44 if (it.key().first + it.key().second >= pos) {
45 it.remove();
46 } else {
47 break;
48 }
49 }
50 }
51
52 QString languageAtPos(int pos) const
53 {
54 // The data structure isn't really great for such lookups...
55 QMapIterator<QPair<int, int>, QString> it(languages);
56 while (it.hasNext()) {
57 it.next();
58 if (it.key().first <= pos && it.key().first + it.key().second >= pos) {
59 return it.value();
60 }
61 }
62 return QString();
63 }
64};
65
66class HighlighterPrivate
67{
68public:
69 HighlighterPrivate(SpellcheckHighlighter *qq)
70 : q(qq)
71 {
72 tokenizer = std::make_unique<WordTokenizer>();
73 active = true;
74 automatic = false;
75 autoDetectLanguageDisabled = false;
76 connected = false;
77 wordCount = 0;
78 errorCount = 0;
79 intraWordEditing = false;
80 completeRehighlightRequired = false;
81 spellColor = spellColor.isValid() ? spellColor : Qt::red;
82 languageFilter = std::make_unique<LanguageFilter>(new SentenceTokenizer());
83
84 loader = Loader::openLoader();
85 loader->settings()->restore();
86
87 spellchecker = std::make_unique<Speller>();
88 spellCheckerFound = spellchecker->isValid();
89 rehighlightRequest = new QTimer(q);
90 q->connect(rehighlightRequest, &QTimer::timeout, q, &SpellcheckHighlighter::slotRehighlight);
91
92 if (!spellCheckerFound) {
93 return;
94 }
95
96 disablePercentage = loader->settings()->disablePercentageWordError();
97 disableWordCount = loader->settings()->disableWordErrorCount();
98
99 completeRehighlightRequired = true;
100 rehighlightRequest->setInterval(0);
101 rehighlightRequest->setSingleShot(true);
102 rehighlightRequest->start();
103
104 // Danger red from our color scheme
105 errorFormat.setForeground(spellColor);
106 errorFormat.setUnderlineColor(spellColor);
107 errorFormat.setUnderlineStyle(QTextCharFormat::SingleUnderline);
108
109 selectedErrorFormat.setForeground(spellColor);
110 auto bg = spellColor;
111 bg.setAlphaF(0.1);
112 selectedErrorFormat.setBackground(bg);
113 selectedErrorFormat.setUnderlineColor(spellColor);
114 selectedErrorFormat.setUnderlineStyle(QTextCharFormat::SingleUnderline);
115
116 quoteFormat.setForeground(QColor{"#7f8c8d"});
117 }
118
119 ~HighlighterPrivate();
120 std::unique_ptr<WordTokenizer> tokenizer;
121 std::unique_ptr<LanguageFilter> languageFilter;
122 Loader *loader = nullptr;
123 std::unique_ptr<Speller> spellchecker;
124
125 QTextCharFormat errorFormat;
126 QTextCharFormat selectedErrorFormat;
127 QTextCharFormat quoteFormat;
128 std::unique_ptr<Sonnet::GuessLanguage> languageGuesser;
129 QString selectedWord;
130 QQuickTextDocument *document = nullptr;
131 int cursorPosition = 0;
132 int selectionStart = 0;
133 int selectionEnd = 0;
134
135 int autoCompleteBeginPosition = -1;
136 int autoCompleteEndPosition = -1;
137 int wordIsMisspelled = false;
138 bool active = false;
139 bool automatic = false;
140 bool autoDetectLanguageDisabled = false;
141 bool completeRehighlightRequired = false;
142 bool intraWordEditing = false;
143 bool spellCheckerFound = false; // cached d->dict->isValid() value
144 bool connected = false;
145 int disablePercentage = 0;
146 int disableWordCount = 0;
147 int wordCount = 0;
148 int errorCount = 0;
149 QTimer *rehighlightRequest = nullptr;
150 QColor spellColor;
151 SpellcheckHighlighter *const q;
152};
153
154HighlighterPrivate::~HighlighterPrivate()
155{
156}
157
158SpellcheckHighlighter::SpellcheckHighlighter(QObject *parent)
159 : QSyntaxHighlighter(parent)
160 , d(new HighlighterPrivate(this))
161{
162}
163
164SpellcheckHighlighter::~SpellcheckHighlighter()
165{
166 if (document()) {
167 disconnect(document(), nullptr, this, nullptr);
168 }
169}
170
172{
173 return d->spellCheckerFound;
174}
175
177{
178 if (d->completeRehighlightRequired) {
179 d->wordCount = 0;
180 d->errorCount = 0;
181 rehighlight();
182 } else {
183 // rehighlight the current para only (undo/redo safe)
184 QTextCursor cursor = textCursor();
185 if (cursor.hasSelection()) {
186 cursor.clearSelection();
187 }
188 cursor.insertText(QString());
189 }
190 // if (d->checksDone == d->checksRequested)
191 // d->completeRehighlightRequired = false;
193}
194
196{
197 return d->automatic;
198}
199
201{
202 return d->autoDetectLanguageDisabled;
203}
204
205bool SpellcheckHighlighter::intraWordEditing() const
206{
207 return d->intraWordEditing;
208}
209
210void SpellcheckHighlighter::setIntraWordEditing(bool editing)
211{
212 d->intraWordEditing = editing;
213}
214
215void SpellcheckHighlighter::setAutomatic(bool automatic)
216{
217 if (automatic == d->automatic) {
218 return;
219 }
220
221 d->automatic = automatic;
222 if (d->automatic) {
224 }
225}
226
227void SpellcheckHighlighter::setAutoDetectLanguageDisabled(bool autoDetectDisabled)
228{
229 d->autoDetectLanguageDisabled = autoDetectDisabled;
230}
231
233{
234 bool savedActive = d->active;
235
236 // don't disable just because 1 of 4 is misspelled.
237 if (d->automatic && d->wordCount >= 10) {
238 // tme = Too many errors
239 /* clang-format off */
240 bool tme = (d->errorCount >= d->disableWordCount)
241 && (d->errorCount * 100 >= d->disablePercentage * d->wordCount);
242 /* clang-format on */
243
244 if (d->active && tme) {
245 d->active = false;
246 } else if (!d->active && !tme) {
247 d->active = true;
248 }
249 }
250
251 if (d->active != savedActive) {
252 if (d->active) {
253 Q_EMIT activeChanged(tr("As-you-type spell checking enabled."));
254 } else {
255 qCDebug(SONNET_LOG_QUICK) << "Sonnet: Disabling spell checking, too many errors";
256 Q_EMIT activeChanged(
257 tr("Too many misspelled words. "
258 "As-you-type spell checking disabled."));
259 }
260
261 d->completeRehighlightRequired = true;
262 d->rehighlightRequest->setInterval(100);
263 d->rehighlightRequest->setSingleShot(true);
264 }
265}
266
267void SpellcheckHighlighter::setActive(bool active)
268{
269 if (active == d->active) {
270 return;
271 }
272 d->active = active;
273 Q_EMIT activeChanged();
274 rehighlight();
275
276 if (d->active) {
277 Q_EMIT activeChanged(tr("As-you-type spell checking enabled."));
278 } else {
279 Q_EMIT activeChanged(tr("As-you-type spell checking disabled."));
280 }
281}
282
284{
285 return d->active;
286}
287
288static bool hasNotEmptyText(const QString &text)
289{
290 for (int i = 0; i < text.length(); ++i) {
291 if (!text.at(i).isSpace()) {
292 return true;
293 }
294 }
295 return false;
296}
297
298void SpellcheckHighlighter::contentsChange(int pos, int add, int rem)
299{
300 // Invalidate the cache where the text has changed
301 const QTextBlock &lastBlock = document()->findBlock(pos + add - rem);
302 QTextBlock block = document()->findBlock(pos);
303 do {
304 LanguageCache *cache = dynamic_cast<LanguageCache *>(block.userData());
305 if (cache) {
306 cache->invalidate(pos - block.position());
307 }
308 block = block.next();
309 } while (block.isValid() && block < lastBlock);
310}
311
312void SpellcheckHighlighter::highlightBlock(const QString &text)
313{
314 if (!hasNotEmptyText(text) || !d->active || !d->spellCheckerFound) {
315 return;
316 }
317
318 // Avoid spellchecking quotes
319 if (text.isEmpty() || text.at(0) == QLatin1Char('>')) {
320 setFormat(0, text.length(), d->quoteFormat);
321 return;
322 }
323
324 if (!d->connected) {
325 connect(textDocument(), &QTextDocument::contentsChange, this, &SpellcheckHighlighter::contentsChange);
326 d->connected = true;
327 }
328 QTextCursor cursor = textCursor();
329 const int index = cursor.position() + 1;
330
331 const int lengthPosition = text.length() - 1;
332
333 if (index != lengthPosition //
334 || (lengthPosition > 0 && !text[lengthPosition - 1].isLetter())) {
335 d->languageFilter->setBuffer(text);
336
337 LanguageCache *cache = dynamic_cast<LanguageCache *>(currentBlockUserData());
338 if (!cache) {
339 cache = new LanguageCache;
341 }
342
343 const bool autodetectLanguage = d->spellchecker->testAttribute(Speller::AutoDetectLanguage);
344 while (d->languageFilter->hasNext()) {
345 Sonnet::Token sentence = d->languageFilter->next();
346 if (autodetectLanguage && !d->autoDetectLanguageDisabled) {
347 QString lang;
348 QPair<int, int> spos = QPair<int, int>(sentence.position(), sentence.length());
349 // try cache first
350 if (cache->languages.contains(spos)) {
351 lang = cache->languages.value(spos);
352 } else {
353 lang = d->languageFilter->language();
354 if (!d->languageFilter->isSpellcheckable()) {
355 lang.clear();
356 }
357 cache->languages[spos] = lang;
358 }
359 if (lang.isEmpty()) {
360 continue;
361 }
362 d->spellchecker->setLanguage(lang);
363 }
364
365 d->tokenizer->setBuffer(sentence.toString());
366 int offset = sentence.position();
367 while (d->tokenizer->hasNext()) {
368 Sonnet::Token word = d->tokenizer->next();
369 if (!d->tokenizer->isSpellcheckable()) {
370 continue;
371 }
372 ++d->wordCount;
373 if (d->spellchecker->isMisspelled(word.toString())) {
374 ++d->errorCount;
375 if (word.position() + offset <= cursor.position() && cursor.position() <= word.position() + offset + word.length()) {
376 setMisspelledSelected(word.position() + offset, word.length());
377 } else {
378 setMisspelled(word.position() + offset, word.length());
379 }
380 } else {
381 unsetMisspelled(word.position() + offset, word.length());
382 }
383 }
384 }
385 }
386 // QTimer::singleShot( 0, this, SLOT(checkWords()) );
388}
389
391{
392 if (!textDocument()) {
393 return {};
394 }
395
396 Q_EMIT changeCursorPosition(mousePosition, mousePosition);
397
398 QTextCursor cursor = textCursor();
399
400 QTextCursor cursorAtMouse(textDocument());
401 cursorAtMouse.setPosition(mousePosition);
402
403 // Check if the user clicked a selected word
404 const bool selectedWordClicked = cursor.hasSelection() && mousePosition >= cursor.selectionStart() && mousePosition <= cursor.selectionEnd();
405
406 // Get the word under the (mouse-)cursor and see if it is misspelled.
407 // Don't include apostrophes at the start/end of the word in the selection.
408 QTextCursor wordSelectCursor(cursorAtMouse);
409 wordSelectCursor.clearSelection();
410 wordSelectCursor.select(QTextCursor::WordUnderCursor);
411 d->selectedWord = wordSelectCursor.selectedText();
412
413 // Clear the selection again, we re-select it below (without the apostrophes).
414 wordSelectCursor.setPosition(wordSelectCursor.position() - d->selectedWord.size());
415 if (d->selectedWord.startsWith(QLatin1Char('\'')) || d->selectedWord.startsWith(QLatin1Char('\"'))) {
416 d->selectedWord = d->selectedWord.right(d->selectedWord.size() - 1);
418 }
419 if (d->selectedWord.endsWith(QLatin1Char('\'')) || d->selectedWord.endsWith(QLatin1Char('\"'))) {
420 d->selectedWord.chop(1);
421 }
422
423 wordSelectCursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor, d->selectedWord.size());
424
425 Q_EMIT wordUnderMouseChanged();
426
427 bool isMouseCursorInsideWord = true;
428 if ((mousePosition < wordSelectCursor.selectionStart() || mousePosition >= wordSelectCursor.selectionEnd()) //
429 && (d->selectedWord.length() > 1)) {
430 isMouseCursorInsideWord = false;
431 }
432
433 wordSelectCursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor, d->selectedWord.size());
434
435 d->wordIsMisspelled = isMouseCursorInsideWord && !d->selectedWord.isEmpty() && d->spellchecker->isMisspelled(d->selectedWord);
436 Q_EMIT wordIsMisspelledChanged();
437
438 if (!d->wordIsMisspelled || selectedWordClicked) {
439 return QStringList{};
440 }
441
442 LanguageCache *cache = dynamic_cast<LanguageCache *>(cursor.block().userData());
443 if (cache) {
444 const QString cachedLanguage = cache->languageAtPos(cursor.positionInBlock());
445 if (!cachedLanguage.isEmpty()) {
446 d->spellchecker->setLanguage(cachedLanguage);
447 }
448 }
449 QStringList suggestions = d->spellchecker->suggest(d->selectedWord);
450 if (max >= 0 && suggestions.count() > max) {
451 suggestions = suggestions.mid(0, max);
452 }
453
454 return suggestions;
455}
456
458{
459 return d->spellchecker->language();
460}
461
463{
464 QString prevLang = d->spellchecker->language();
465 d->spellchecker->setLanguage(lang);
466 d->spellCheckerFound = d->spellchecker->isValid();
467 if (!d->spellCheckerFound) {
468 qCDebug(SONNET_LOG_QUICK) << "No dictionary for \"" << lang << "\" staying with the current language.";
469 d->spellchecker->setLanguage(prevLang);
470 return;
471 }
472 d->wordCount = 0;
473 d->errorCount = 0;
474 if (d->automatic || d->active) {
475 d->rehighlightRequest->start(0);
476 }
477}
478
479void SpellcheckHighlighter::setMisspelled(int start, int count)
480{
481 setFormat(start, count, d->errorFormat);
482}
483
484void SpellcheckHighlighter::setMisspelledSelected(int start, int count)
485{
486 setFormat(start, count, d->selectedErrorFormat);
487}
488
489void SpellcheckHighlighter::unsetMisspelled(int start, int count)
490{
491 setFormat(start, count, QTextCharFormat());
492}
493
495{
496 d->spellchecker->addToPersonal(word);
497 rehighlight();
498}
499
501{
502 d->spellchecker->addToSession(word);
503 rehighlight();
504}
505
506void SpellcheckHighlighter::replaceWord(const QString &replacement, int at)
507{
508 QTextCursor textCursorUnderUserCursor(textDocument());
509 textCursorUnderUserCursor.setPosition(at == -1 ? d->cursorPosition : at);
510
511 // Get the word under the cursor
512 QTextCursor wordSelectCursor(textCursorUnderUserCursor);
513 wordSelectCursor.clearSelection();
514 wordSelectCursor.select(QTextCursor::WordUnderCursor);
515
516 auto selectedWord = wordSelectCursor.selectedText();
517
518 // Trim leading and trailing apostrophes
519 wordSelectCursor.setPosition(wordSelectCursor.position() - selectedWord.size());
520 if (selectedWord.startsWith(QLatin1Char('\'')) || selectedWord.startsWith(QLatin1Char('\"'))) {
521 selectedWord = selectedWord.right(selectedWord.size() - 1);
523 }
524 if (selectedWord.endsWith(QLatin1Char('\'')) || d->selectedWord.endsWith(QLatin1Char('\"'))) {
525 selectedWord.chop(1);
526 }
527
528 wordSelectCursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor, d->selectedWord.size());
529
530 wordSelectCursor.insertText(replacement);
531}
532
533QQuickTextDocument *SpellcheckHighlighter::quickDocument() const
534{
535 return d->document;
536}
537
538void SpellcheckHighlighter::setQuickDocument(QQuickTextDocument *document)
539{
540 if (document == d->document) {
541 return;
542 }
543
544 if (d->document) {
545 d->document->parent()->removeEventFilter(this);
546 d->document->textDocument()->disconnect(this);
547 }
548 d->document = document;
549 document->parent()->installEventFilter(this);
550 setDocument(document->textDocument());
551 Q_EMIT documentChanged();
552}
553
559
561{
562 return d->cursorPosition;
563}
564
565void SpellcheckHighlighter::setCursorPosition(int position)
566{
567 if (position == d->cursorPosition) {
568 return;
569 }
570
571 d->cursorPosition = position;
572 d->rehighlightRequest->start(0);
573 Q_EMIT cursorPositionChanged();
574}
575
577{
578 return d->selectionStart;
579}
580
581void SpellcheckHighlighter::setSelectionStart(int position)
582{
583 if (position == d->selectionStart) {
584 return;
585 }
586
587 d->selectionStart = position;
588 Q_EMIT selectionStartChanged();
589}
590
592{
593 return d->selectionEnd;
594}
595
596void SpellcheckHighlighter::setSelectionEnd(int position)
597{
598 if (position == d->selectionEnd) {
599 return;
600 }
601
602 d->selectionEnd = position;
603 Q_EMIT selectionEndChanged();
604}
605
606QTextCursor SpellcheckHighlighter::textCursor() const
607{
608 QTextDocument *doc = textDocument();
609 if (!doc) {
610 return QTextCursor();
611 }
612
613 QTextCursor cursor(doc);
614 if (d->selectionStart != d->selectionEnd) {
615 cursor.setPosition(d->selectionStart);
616 cursor.setPosition(d->selectionEnd, QTextCursor::KeepAnchor);
617 } else {
618 cursor.setPosition(d->cursorPosition);
619 }
620 return cursor;
621}
622
623QTextDocument *SpellcheckHighlighter::textDocument() const
624{
625 if (!d->document) {
626 return nullptr;
627 }
628
629 return d->document->textDocument();
630}
631
633{
634 return d->wordIsMisspelled;
635}
636
638{
639 return d->selectedWord;
640}
641
643{
644 return d->spellColor;
645}
646
647void SpellcheckHighlighter::setMisspelledColor(const QColor &color)
648{
649 if (color == d->spellColor) {
650 return;
651 }
652 d->spellColor = color;
653 Q_EMIT misspelledColorChanged();
654}
655
657{
658 return d->spellchecker->isMisspelled(word);
659}
660
661bool SpellcheckHighlighter::eventFilter(QObject *o, QEvent *e)
662{
663 if (!d->spellCheckerFound) {
664 return false;
665 }
666 if (o == d->document->parent() && (e->type() == QEvent::KeyPress)) {
667 QKeyEvent *k = static_cast<QKeyEvent *>(e);
668
669 if (k->key() == Qt::Key_Enter || k->key() == Qt::Key_Return || k->key() == Qt::Key_Up || k->key() == Qt::Key_Down || k->key() == Qt::Key_Left
670 || k->key() == Qt::Key_Right || k->key() == Qt::Key_PageUp || k->key() == Qt::Key_PageDown || k->key() == Qt::Key_Home || k->key() == Qt::Key_End
672 && (k->key() == Qt::Key_A || k->key() == Qt::Key_B || k->key() == Qt::Key_E || k->key() == Qt::Key_N
673 || k->key() == Qt::Key_P))) { /* clang-format on */
674 if (intraWordEditing()) {
675 setIntraWordEditing(false);
676 d->completeRehighlightRequired = true;
677 d->rehighlightRequest->setInterval(500);
678 d->rehighlightRequest->setSingleShot(true);
679 d->rehighlightRequest->start();
680 }
681 } else {
682 setIntraWordEditing(true);
683 }
684 if (k->key() == Qt::Key_Space //
685 || k->key() == Qt::Key_Enter //
686 || k->key() == Qt::Key_Return) {
687 QTimer::singleShot(0, this, SLOT(slotAutoDetection()));
688 }
689 } else if (d->document && e->type() == QEvent::MouseButtonPress) {
690 if (intraWordEditing()) {
691 setIntraWordEditing(false);
692 d->completeRehighlightRequired = true;
693 d->rehighlightRequest->setInterval(0);
694 d->rehighlightRequest->setSingleShot(true);
695 d->rehighlightRequest->start();
696 }
697 }
698 return false;
699}
700
701#include "moc_spellcheckhighlighter.cpp"
QString language() const
Definition speller.cpp:144
void slotRehighlight()
Force a new highlighting.
bool wordIsMisspelled
This property holds whether the current word under the mouse is misspelled.
Q_INVOKABLE void ignoreWord(const QString &word)
Ignores the given word.
int selectionStart
This property holds the start of the selection.
void setCurrentLanguage(const QString &language)
Set language to use for spell checking.
QString currentLanguage
This property holds the current language used for spell checking.
QString wordUnderMouse
This property holds the current word under the mouse.
QML_ELEMENTQQuickTextDocument * document
This property holds the underneath document from a QML TextEdit.
void setDocument(QTextDocument *document)
Set a new QTextDocument for this highlighter to operate on.
Q_INVOKABLE void replaceWord(const QString &word, int at=-1)
Replace word at the current cursor position, or.
int selectionEnd
This property holds the end of the selection.
int cursorPosition
This property holds the current cursor position.
bool autoDetectLanguageDisabled
This property holds whether the automatic language detection is disabled overriding the Sonnet global...
bool active
This property holds whether spell checking is enabled.
Q_INVOKABLE QStringList suggestions(int position, int max=5)
Returns a list of suggested replacements for the given misspelled word.
void slotAutoDetection()
Run auto detection, disabling spell checking if too many errors are found.
QColor misspelledColor
This property holds the spell color.
Q_INVOKABLE bool isWordMisspelled(const QString &word)
Checks if a given word is marked as misspelled by the highlighter.
bool automatic
This property holds whether spell checking is automatically disabled if there's too many errors.
bool spellCheckerFound
This property holds whether a spell checking backend with support for the currentLanguage was found.
Q_INVOKABLE void addWordToDictionary(const QString &word)
Adds the given word permanently to the dictionary.
Q_SCRIPTABLE QString start(QString train="")
Q_SCRIPTABLE Q_NOREPLY void start()
The sonnet namespace.
bool isSpace(char32_t ucs4)
Type type() const const
int key() const const
Qt::KeyboardModifiers modifiers() const const
bool contains(const Key &key) const const
T value(const Key &key, const T &defaultValue) const const
Q_EMITQ_EMIT
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
bool disconnect(const QMetaObject::Connection &connection)
QString tr(const char *sourceText, const char *disambiguation, int n)
const QChar at(qsizetype position) const const
void clear()
bool isEmpty() const const
qsizetype length() const const
QTextBlockUserData * currentBlockUserData() const const
void setCurrentBlockState(int newState)
void setCurrentBlockUserData(QTextBlockUserData *data)
void setDocument(QTextDocument *doc)
void setFormat(int start, int count, const QColor &color)
Key_Enter
ControlModifier
bool isValid() const const
QTextBlock next() const const
int position() const const
QTextBlockUserData * userData() const const
QTextBlock block() const const
void clearSelection()
bool hasSelection() const const
void insertText(const QString &text)
bool movePosition(MoveOperation operation, MoveMode mode, int n)
int position() const const
int positionInBlock() const const
void select(SelectionType selection)
QString selectedText() const const
int selectionEnd() const const
int selectionStart() const const
void setPosition(int pos, MoveMode m)
void contentsChange(int position, int charsRemoved, int charsAdded)
void timeout()
This file is part of the KDE documentation.
Documentation copyright © 1996-2025 The KDE developers.
Generated on Fri Jan 24 2025 11:56:17 by doxygen 1.13.2 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.