1// SPDX-FileCopyrightText: 2004 Zack Rusin <zack@kde.org>
2// SPDX-FileCopyrightText: 2013 Martin Sandsmark <martin.sandsmark@kde.org>
3// SPDX-FileCopyrightText: 2013 Aurélien Gâteau <agateau@kde.org>
4// SPDX-FileCopyrightText: 2020 Christian Mollekopf <mollekopf@kolabsystems.com>
5// SPDX-FileCopyrightText: 2021 Carl Schwan <carlschwan@kde.org>
6// SPDX-License-Identifier: LGPL-2.1-or-later
7
8#pragma once
9
10// TODO KF6 create AbstractSpellcheckHighlighter and make the QtQuick and QtWidget inherit
11// from it.
12
13#include <QQuickTextDocument>
14#include <QSyntaxHighlighter>
15
16class HighlighterPrivate;
17
18/// \brief The Sonnet Highlighter class, used for drawing red lines in text fields
19/// when detecting spelling mistakes.
20///
21/// SpellcheckHighlighter is adapted for QML applications. In usual Kirigami/QQC2-desktop-style
22/// applications, this can be used directly by adding `Kirigami.SpellCheck.enabled: true` on
23/// a TextArea.
24///
25/// On other QML applications, you can add the SpellcheckHighlighter as a child of a TextArea.
26///
27/// Note: TextField is not supported, as it lacks QTextDocument API that Sonnet relies on.
28///
29/// \code{.qml}
30/// TextArea {
31/// id: textArea
32/// Sonnet.SpellcheckHighlighter {
33/// id: spellcheckhighlighter
34/// document: textArea.textDocument
35/// cursorPosition: textArea.cursorPosition
36/// selectionStart: textArea.selectionStart
37/// selectionEnd: textArea.selectionEnd
38/// misspelledColor: Kirigami.Theme.negativeTextColor
39/// active: true
40///
41/// onChangeCursorPosition: {
42/// textArea.cursorPosition = start;
43/// textArea.moveCursorSelection(end, TextEdit.SelectCharacters);
44/// }
45/// }
46/// }
47/// \endcode
48///
49/// Additionally SpellcheckHighlighter provides some convenient methods to create
50/// a context menu with suggestions. \see suggestions
51///
52/// \since 5.88
53class SpellcheckHighlighter : public QSyntaxHighlighter
54{
55 Q_OBJECT
56 QML_ELEMENT
57 /// This property holds the underneath document from a QML TextEdit.
58 /// \since 5.88
59 Q_PROPERTY(QQuickTextDocument *document READ quickDocument WRITE setQuickDocument NOTIFY documentChanged)
60
61 /// This property holds the current cursor position.
62 /// \since 5.88
63 Q_PROPERTY(int cursorPosition READ cursorPosition WRITE setCursorPosition NOTIFY cursorPositionChanged)
64
65 /// This property holds the start of the selection.
66 /// \since 5.88
67 Q_PROPERTY(int selectionStart READ selectionStart WRITE setSelectionStart NOTIFY selectionStartChanged)
68
69 /// This property holds the end of the selection.
70 /// \since 5.88
71 Q_PROPERTY(int selectionEnd READ selectionEnd WRITE setSelectionEnd NOTIFY selectionEndChanged)
72
73 /// This property holds whether the current word under the mouse is misspelled.
74 /// \since 5.88
75 Q_PROPERTY(bool wordIsMisspelled READ wordIsMisspelled NOTIFY wordIsMisspelledChanged)
76
77 /// This property holds the current word under the mouse.
78 /// \since 5.88
79 Q_PROPERTY(QString wordUnderMouse READ wordUnderMouse NOTIFY wordUnderMouseChanged)
80
81 /// This property holds the spell color. By default, it's red.
82 /// \since 5.88
83 Q_PROPERTY(QColor misspelledColor READ misspelledColor WRITE setMisspelledColor NOTIFY misspelledColorChanged)
84
85 /// This property holds the current language used for spell checking.
86 /// \since 5.88
87 Q_PROPERTY(QString currentLanguage READ currentLanguage NOTIFY currentLanguageChanged)
88
89 /// This property holds whether a spell checking backend with support for the
90 /// \ref currentLanguage was found.
91 /// \since 5.88
92 Q_PROPERTY(bool spellCheckerFound READ spellCheckerFound CONSTANT)
93
94 /// \brief This property holds whether spell checking is enabled.
95 ///
96 /// If \p active is true then spell checking is enabled; otherwise it
97 /// is disabled. Note that you have to disable automatic (de)activation
98 /// with \ref automatic before you change the state of spell
99 /// checking if you want to persistently enable/disable spell
100 /// checking.
101 ///
102 /// \see automatic
103 /// \since 5.88
104 Q_PROPERTY(bool active READ active WRITE setActive NOTIFY activeChanged)
105
106 /// This property holds whether spell checking is automatically disabled
107 /// if there's too many errors.
108 /// \since 5.88
109 Q_PROPERTY(bool automatic READ automatic WRITE setAutomatic NOTIFY automaticChanged)
110
111 /// This property holds whether the automatic language detection is disabled
112 /// overriding the Sonnet global settings.
113 /// \since 5.88
114 Q_PROPERTY(bool autoDetectLanguageDisabled READ autoDetectLanguageDisabled WRITE setAutoDetectLanguageDisabled NOTIFY autoDetectLanguageDisabledChanged)
115
116public:
117 explicit SpellcheckHighlighter(QObject *parent = nullptr);
118 ~SpellcheckHighlighter() override;
119
120 /// Returns a list of suggested replacements for the given misspelled word.
121 /// If the word is not misspelled, the list will be empty.
122 ///
123 /// \param word the misspelled word
124 /// \param max at most this many suggestions will be returned. If this is
125 /// -1, as many suggestions as the spell backend supports will
126 /// be returned.
127 /// \return a list of suggested replacements for the word
128 /// \since 5.88
129 Q_INVOKABLE QStringList suggestions(int position, int max = 5);
130
131 /// Ignores the given word. This word will not be marked misspelled for
132 /// this session. It will again be marked as misspelled when creating
133 /// new highlighters.
134 ///
135 /// \param word the word which will be ignored
136 /// \since 5.88
137 Q_INVOKABLE void ignoreWord(const QString &word);
138
139 /// Adds the given word permanently to the dictionary. It will never
140 /// be marked as misspelled again, even after restarting the application.
141 ///
142 /// \param word the word which will be added to the dictionary
143 /// \since 5.88
144 Q_INVOKABLE void addWordToDictionary(const QString &word);
145
146 /// Replace word at the current cursor position, or @param at if
147 /// @param at is not -1.
148 /// \since 5.88
149 Q_INVOKABLE void replaceWord(const QString &word, int at = -1);
150
151 /// Checks if a given word is marked as misspelled by the highlighter.
152 ///
153 /// \param word the word to be checked
154 /// \return true if the given word is misspelled.
155 /// \since 5.88
156 Q_INVOKABLE bool isWordMisspelled(const QString &word);
157
158 Q_REQUIRED_RESULT QQuickTextDocument *quickDocument() const;
159 void setQuickDocument(QQuickTextDocument *document);
160 Q_REQUIRED_RESULT int cursorPosition() const;
161 void setCursorPosition(int position);
162 Q_REQUIRED_RESULT int selectionStart() const;
163 void setSelectionStart(int position);
164 Q_REQUIRED_RESULT int selectionEnd() const;
165 void setSelectionEnd(int position);
166 Q_REQUIRED_RESULT bool wordIsMisspelled() const;
167 Q_REQUIRED_RESULT QString wordUnderMouse() const;
168 Q_REQUIRED_RESULT bool spellCheckerFound() const;
169 Q_REQUIRED_RESULT QString currentLanguage() const;
170 void setActive(bool active);
171 Q_REQUIRED_RESULT bool active() const;
172 void setAutomatic(bool automatic);
173 Q_REQUIRED_RESULT bool automatic() const;
174 void setAutoDetectLanguageDisabled(bool autoDetectDisabled);
175 Q_REQUIRED_RESULT bool autoDetectLanguageDisabled() const;
176 void setMisspelledColor(const QColor &color);
177 Q_REQUIRED_RESULT QColor misspelledColor() const;
178 void setQuoteColor(const QColor &color);
179 Q_REQUIRED_RESULT QColor quoteColor() const;
180
181 /// Return true if checker is enabled by default
182 /// \since 5.88
183 bool checkerEnabledByDefault() const;
184
185 /// Set a new @ref QTextDocument for this highlighter to operate on.
186 /// \param document the new document to operate on.
187 /// \since 5.88
188 void setDocument(QTextDocument *document);
189
190Q_SIGNALS:
191 void documentChanged();
192 void cursorPositionChanged();
193 void selectionStartChanged();
194 void selectionEndChanged();
195 void wordIsMisspelledChanged();
196 void wordUnderMouseChanged();
197 void changeCursorPosition(int start, int end);
198 void activeChanged();
199 void misspelledColorChanged();
200 void autoDetectLanguageDisabledChanged();
201 void automaticChanged();
202 void currentLanguageChanged();
203
204 /// Emitted when as-you-type spell checking is enabled or disabled.
205 ///
206 /// \param description is a i18n description of the new state,
207 /// with an optional reason
208 /// \since 5.88
209 void activeChanged(const QString &description);
210
211protected:
212 void highlightBlock(const QString &text) override;
213 virtual void setMisspelled(int start, int count);
214 virtual void setMisspelledSelected(int start, int count);
215 virtual void unsetMisspelled(int start, int count);
216 bool eventFilter(QObject *o, QEvent *e) override;
217
218 bool intraWordEditing() const;
219 void setIntraWordEditing(bool editing);
220
221public Q_SLOTS:
222 /// Set language to use for spell checking.
223 ///
224 /// \param language the language code for the new language to use.
225 /// \since 5.88
226 void setCurrentLanguage(const QString &language);
227
228 /// Run auto detection, disabling spell checking if too many errors are found.
229 /// \since 5.88
230 void slotAutoDetection();
231
232 /// Force a new highlighting.
233 /// \since 5.88
234 void slotRehighlight();
235
236private:
237 Q_REQUIRED_RESULT QTextCursor textCursor() const;
238 Q_REQUIRED_RESULT QTextDocument *textDocument() const;
239 void contentsChange(int pos, int add, int rem);
240
241 void autodetectLanguage(const QString &sentence);
242
243 HighlighterPrivate *const d;
244 Q_DISABLE_COPY(SpellcheckHighlighter)
245};
246

source code of sonnet/src/quick/spellcheckhighlighter.h