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 {
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...
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);
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);
108
109 selectedErrorFormat.setForeground(spellColor);
110 auto bg = spellColor;
111 bg.setAlphaF(0.1);
112 selectedErrorFormat.setBackground(bg);
113 selectedErrorFormat.setUnderlineColor(spellColor);
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 int endSelection = wordSelectCursor.selectionEnd();
426 Q_EMIT wordUnderMouseChanged();
427
428 bool isMouseCursorInsideWord = true;
429 if ((mousePosition < wordSelectCursor.selectionStart() || mousePosition >= wordSelectCursor.selectionEnd()) //
430 && (d->selectedWord.length() > 1)) {
431 isMouseCursorInsideWord = false;
432 }
433
434 wordSelectCursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor, d->selectedWord.size());
435
436 d->wordIsMisspelled = isMouseCursorInsideWord && !d->selectedWord.isEmpty() && d->spellchecker->isMisspelled(d->selectedWord);
437 Q_EMIT wordIsMisspelledChanged();
438
439 if (!d->wordIsMisspelled || selectedWordClicked) {
440 return QStringList{};
441 }
442
443 LanguageCache *cache = dynamic_cast<LanguageCache *>(cursor.block().userData());
444 if (cache) {
445 const QString cachedLanguage = cache->languageAtPos(cursor.positionInBlock());
446 if (!cachedLanguage.isEmpty()) {
447 d->spellchecker->setLanguage(cachedLanguage);
448 }
449 }
450 QStringList suggestions = d->spellchecker->suggest(d->selectedWord);
451 if (max >= 0 && suggestions.count() > max) {
452 suggestions = suggestions.mid(0, max);
453 }
454
455 return suggestions;
456}
457
459{
460 return d->spellchecker->language();
461}
462
464{
465 QString prevLang = d->spellchecker->language();
466 d->spellchecker->setLanguage(lang);
467 d->spellCheckerFound = d->spellchecker->isValid();
468 if (!d->spellCheckerFound) {
469 qCDebug(SONNET_LOG_QUICK) << "No dictionary for \"" << lang << "\" staying with the current language.";
470 d->spellchecker->setLanguage(prevLang);
471 return;
472 }
473 d->wordCount = 0;
474 d->errorCount = 0;
475 if (d->automatic || d->active) {
476 d->rehighlightRequest->start(0);
477 }
478}
479
480void SpellcheckHighlighter::setMisspelled(int start, int count)
481{
482 setFormat(start, count, d->errorFormat);
483}
484
485void SpellcheckHighlighter::setMisspelledSelected(int start, int count)
486{
487 setFormat(start, count, d->selectedErrorFormat);
488}
489
490void SpellcheckHighlighter::unsetMisspelled(int start, int count)
491{
493}
494
496{
497 d->spellchecker->addToPersonal(word);
498 rehighlight();
499}
500
502{
503 d->spellchecker->addToSession(word);
504 rehighlight();
505}
506
507void SpellcheckHighlighter::replaceWord(const QString &replacement, int at)
508{
509 QTextCursor textCursorUnderUserCursor(textDocument());
510 textCursorUnderUserCursor.setPosition(at == -1 ? d->cursorPosition : at);
511
512 // Get the word under the cursor
513 QTextCursor wordSelectCursor(textCursorUnderUserCursor);
514 wordSelectCursor.clearSelection();
515 wordSelectCursor.select(QTextCursor::WordUnderCursor);
516
517 auto selectedWord = wordSelectCursor.selectedText();
518
519 // Trim leading and trailing apostrophes
520 wordSelectCursor.setPosition(wordSelectCursor.position() - selectedWord.size());
521 if (selectedWord.startsWith(QLatin1Char('\'')) || selectedWord.startsWith(QLatin1Char('\"'))) {
522 selectedWord = selectedWord.right(selectedWord.size() - 1);
524 }
525 if (selectedWord.endsWith(QLatin1Char('\'')) || d->selectedWord.endsWith(QLatin1Char('\"'))) {
526 selectedWord.chop(1);
527 }
528
529 wordSelectCursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor, d->selectedWord.size());
530
531 wordSelectCursor.insertText(replacement);
532}
533
534QQuickTextDocument *SpellcheckHighlighter::quickDocument() const
535{
536 return d->document;
537}
538
539void SpellcheckHighlighter::setQuickDocument(QQuickTextDocument *document)
540{
541 if (document == d->document) {
542 return;
543 }
544
545 if (d->document) {
546 d->document->parent()->removeEventFilter(this);
547 d->document->textDocument()->disconnect(this);
548 }
549 d->document = document;
550 document->parent()->installEventFilter(this);
551 setDocument(document->textDocument());
552 Q_EMIT documentChanged();
553}
554
556{
557 d->connected = false;
559}
560
562{
563 return d->cursorPosition;
564}
565
566void SpellcheckHighlighter::setCursorPosition(int position)
567{
568 if (position == d->cursorPosition) {
569 return;
570 }
571
572 d->cursorPosition = position;
573 d->rehighlightRequest->start(0);
574 Q_EMIT cursorPositionChanged();
575}
576
578{
579 return d->selectionStart;
580}
581
582void SpellcheckHighlighter::setSelectionStart(int position)
583{
584 if (position == d->selectionStart) {
585 return;
586 }
587
588 d->selectionStart = position;
589 Q_EMIT selectionStartChanged();
590}
591
593{
594 return d->selectionEnd;
595}
596
597void SpellcheckHighlighter::setSelectionEnd(int position)
598{
599 if (position == d->selectionEnd) {
600 return;
601 }
602
603 d->selectionEnd = position;
604 Q_EMIT selectionEndChanged();
605}
606
607QTextCursor SpellcheckHighlighter::textCursor() const
608{
609 QTextDocument *doc = textDocument();
610 if (!doc) {
611 return QTextCursor();
612 }
613
614 QTextCursor cursor(doc);
615 if (d->selectionStart != d->selectionEnd) {
616 cursor.setPosition(d->selectionStart);
617 cursor.setPosition(d->selectionEnd, QTextCursor::KeepAnchor);
618 } else {
619 cursor.setPosition(d->cursorPosition);
620 }
621 return cursor;
622}
623
624QTextDocument *SpellcheckHighlighter::textDocument() const
625{
626 if (!d->document) {
627 return nullptr;
628 }
629
630 return d->document->textDocument();
631}
632
634{
635 return d->wordIsMisspelled;
636}
637
639{
640 return d->selectedWord;
641}
642
644{
645 return d->spellColor;
646}
647
648void SpellcheckHighlighter::setMisspelledColor(const QColor &color)
649{
650 if (color == d->spellColor) {
651 return;
652 }
653 d->spellColor = color;
654 Q_EMIT misspelledColorChanged();
655}
656
658{
659 return d->spellchecker->isMisspelled(word);
660}
661
662bool SpellcheckHighlighter::eventFilter(QObject *o, QEvent *e)
663{
664 if (!d->spellCheckerFound) {
665 return false;
666 }
667 if (o == d->document->parent() && (e->type() == QEvent::KeyPress)) {
668 QKeyEvent *k = static_cast<QKeyEvent *>(e);
669
670 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
671 || 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
673 && (k->key() == Qt::Key_A || k->key() == Qt::Key_B || k->key() == Qt::Key_E || k->key() == Qt::Key_N
674 || k->key() == Qt::Key_P))) { /* clang-format on */
675 if (intraWordEditing()) {
676 setIntraWordEditing(false);
677 d->completeRehighlightRequired = true;
678 d->rehighlightRequest->setInterval(500);
679 d->rehighlightRequest->setSingleShot(true);
680 d->rehighlightRequest->start();
681 }
682 } else {
683 setIntraWordEditing(true);
684 }
685 if (k->key() == Qt::Key_Space //
686 || k->key() == Qt::Key_Enter //
687 || k->key() == Qt::Key_Return) {
688 QTimer::singleShot(0, this, SLOT(slotAutoDetection()));
689 }
690 } else if (d->document && e->type() == QEvent::MouseButtonPress) {
691 if (intraWordEditing()) {
692 setIntraWordEditing(false);
693 d->completeRehighlightRequired = true;
694 d->rehighlightRequest->setInterval(0);
695 d->rehighlightRequest->setSingleShot(true);
696 d->rehighlightRequest->start();
697 }
698 }
699 return false;
700}
701
702#include "moc_spellcheckhighlighter.cpp"
bool addToSession(const QString &word)
Adds word to the words recognizable in the current session.
Definition speller.cpp:136
bool isMisspelled(const QString &word) const
Checks the given word.
Definition speller.cpp:96
QStringList suggest(const QString &word) const
Fetches suggestions for the word.
Definition speller.cpp:104
QString language() const
Definition speller.cpp:144
bool addToPersonal(const QString &word)
Adds word to the list of of personal words.
Definition speller.cpp:128
bool isValid() const
Definition speller.cpp:238
void setLanguage(const QString &lang)
Sets the language supported by this speller.
Definition speller.cpp:243
The Sonnet Highlighter class, used for drawing red lines in text fields when detecting spelling mista...
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 Q_NOREPLY void start()
The sonnet namespace.
bool isSpace(char32_t ucs4)
bool isValid() const const
void setAlphaF(float alpha)
Type type() const const
int key() const const
Qt::KeyboardModifiers modifiers() const const
qsizetype count() const const
QList< T > mid(qsizetype pos, qsizetype length) 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
void setUnderlineColor(const QColor &color)
void setUnderlineStyle(UnderlineStyle style)
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 setBackground(const QBrush &brush)
void setForeground(const QBrush &brush)
void setInterval(int msec)
void setSingleShot(bool singleShot)
void start()
void timeout()
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Mon Nov 18 2024 12:10:25 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.