File indexing completed on 2023-09-24 04:15:31
0001 // SPDX-FileCopyrightText: 2004 Zack Rusin <zack@kde.org> 0002 // SPDX-FileCopyrightText: 2013 Martin Sandsmark <martin.sandsmark@kde.org> 0003 // SPDX-FileCopyrightText: 2013 Aurélien Gâteau <agateau@kde.org> 0004 // SPDX-FileCopyrightText: 2020 Christian Mollekopf <mollekopf@kolabsystems.com> 0005 // SPDX-FileCopyrightText: 2021 Carl Schwan <carlschwan@kde.org> 0006 // SPDX-License-Identifier: LGPL-2.1-or-later 0007 0008 #pragma once 0009 0010 // TODO KF6 create AbstractSpellcheckHighlighter and make the QtQuick and QtWidget inherit 0011 // from it. 0012 0013 #include <QQuickTextDocument> 0014 #include <QSyntaxHighlighter> 0015 0016 class HighlighterPrivate; 0017 0018 /// \brief The Sonnet Highlighter class, used for drawing red lines in text fields 0019 /// when detecting spelling mistakes. 0020 /// 0021 /// SpellcheckHighlighter is adapted for QML applications. In usual Kirigami/QQC2-desktop-style 0022 /// applications, this can be used directly by adding `Kirigami.SpellChecking.enabled: true` on 0023 /// a TextArea/TextField. 0024 /// 0025 /// On other QML applications, you can add the SpellcheckHighlighter as a child of a TextArea/TextField. 0026 /// 0027 /// \code{.qml} 0028 /// TextArea { 0029 /// id: textArea 0030 /// Sonnet.SpellcheckHighlighter { 0031 /// id: spellcheckhighlighter 0032 /// document: textArea.textDocument 0033 /// cursorPosition: textArea.cursorPosition 0034 /// selectionStart: textArea.selectionStart 0035 /// selectionEnd: textArea.selectionEnd 0036 /// misspelledColor: Kirigami.Theme.negativeTextColor 0037 /// active: true 0038 /// 0039 /// onChangeCursorPosition: { 0040 /// textArea.cursorPosition = start; 0041 /// textArea.moveCursorSelection(end, TextEdit.SelectCharacters); 0042 /// } 0043 /// } 0044 /// } 0045 /// \endcode 0046 /// 0047 /// Additionally SpellcheckHighlighter provides some convenient methods to create 0048 /// a context menu with suggestions. \see suggestions 0049 /// 0050 /// \since 5.88 0051 class SpellcheckHighlighter : public QSyntaxHighlighter 0052 { 0053 Q_OBJECT 0054 /// This property holds the underneath document from a QML TextEdit. 0055 /// \since 5.88 0056 Q_PROPERTY(QQuickTextDocument *document READ quickDocument WRITE setQuickDocument NOTIFY documentChanged) 0057 0058 /// This property holds the current cursor position. 0059 /// \since 5.88 0060 Q_PROPERTY(int cursorPosition READ cursorPosition WRITE setCursorPosition NOTIFY cursorPositionChanged) 0061 0062 /// This property holds the start of the selection. 0063 /// \since 5.88 0064 Q_PROPERTY(int selectionStart READ selectionStart WRITE setSelectionStart NOTIFY selectionStartChanged) 0065 0066 /// This property holds the end of the selection. 0067 /// \since 5.88 0068 Q_PROPERTY(int selectionEnd READ selectionEnd WRITE setSelectionEnd NOTIFY selectionEndChanged) 0069 0070 /// This property holds whether the current word under the mouse is misspelled. 0071 /// \since 5.88 0072 Q_PROPERTY(bool wordIsMisspelled READ wordIsMisspelled NOTIFY wordIsMisspelledChanged) 0073 0074 /// This property holds the current word under the mouse. 0075 /// \since 5.88 0076 Q_PROPERTY(QString wordUnderMouse READ wordUnderMouse NOTIFY wordUnderMouseChanged) 0077 0078 /// This property holds the spell color. By default, it's red. 0079 /// \since 5.88 0080 Q_PROPERTY(QColor misspelledColor READ misspelledColor WRITE setMisspelledColor NOTIFY misspelledColorChanged) 0081 0082 /// This property holds the current language used for spell checking. 0083 /// \since 5.88 0084 Q_PROPERTY(QString currentLanguage READ currentLanguage NOTIFY currentLanguageChanged) 0085 0086 /// This property holds whether a spell checking backend with support for the 0087 /// \ref currentLanguage was found. 0088 /// \since 5.88 0089 Q_PROPERTY(bool spellCheckerFound READ spellCheckerFound CONSTANT) 0090 0091 /// \brief This property holds whether spell checking is enabled. 0092 /// 0093 /// If \p active is true then spell checking is enabled; otherwise it 0094 /// is disabled. Note that you have to disable automatic (de)activation 0095 /// with \ref automatic before you change the state of spell 0096 /// checking if you want to persistently enable/disable spell 0097 /// checking. 0098 /// 0099 /// \see automatic 0100 /// \since 5.88 0101 Q_PROPERTY(bool active READ active WRITE setActive NOTIFY activeChanged) 0102 0103 /// This property holds whether spell checking is automatically disabled 0104 /// if there's too many errors. 0105 /// \since 5.88 0106 Q_PROPERTY(bool automatic READ automatic WRITE setAutomatic NOTIFY automaticChanged) 0107 0108 /// This property holds whether the automatic language detection is disabled 0109 /// overriding the Sonnet global settings. 0110 /// \since 5.88 0111 Q_PROPERTY(bool autoDetectLanguageDisabled READ autoDetectLanguageDisabled WRITE setAutoDetectLanguageDisabled NOTIFY autoDetectLanguageDisabledChanged) 0112 0113 public: 0114 explicit SpellcheckHighlighter(QObject *parent = nullptr); 0115 0116 /// Returns a list of suggested replacements for the given misspelled word. 0117 /// If the word is not misspelled, the list will be empty. 0118 /// 0119 /// \param word the misspelled word 0120 /// \param max at most this many suggestions will be returned. If this is 0121 /// -1, as many suggestions as the spell backend supports will 0122 /// be returned. 0123 /// \return a list of suggested replacements for the word 0124 /// \since 5.88 0125 Q_INVOKABLE QStringList suggestions(int position, int max = 5); 0126 0127 /// Ignores the given word. This word will not be marked misspelled for 0128 /// this session. It will again be marked as misspelled when creating 0129 /// new highlighters. 0130 /// 0131 /// \param word the word which will be ignored 0132 /// \since 5.88 0133 Q_INVOKABLE void ignoreWord(const QString &word); 0134 0135 /// Adds the given word permanently to the dictionary. It will never 0136 /// be marked as misspelled again, even after restarting the application. 0137 /// 0138 /// \param word the word which will be added to the dictionary 0139 /// \since 5.88 0140 Q_INVOKABLE void addWordToDictionary(const QString &word); 0141 0142 /// Replace word at the current cursor position. 0143 /// \since 5.88 0144 Q_INVOKABLE void replaceWord(const QString &word); 0145 0146 /// Checks if a given word is marked as misspelled by the highlighter. 0147 /// 0148 /// \param word the word to be checked 0149 /// \return true if the given word is misspelled. 0150 /// \since 5.88 0151 Q_INVOKABLE bool isWordMisspelled(const QString &word); 0152 0153 Q_REQUIRED_RESULT QQuickTextDocument *quickDocument() const; 0154 void setQuickDocument(QQuickTextDocument *document); 0155 Q_REQUIRED_RESULT int cursorPosition() const; 0156 void setCursorPosition(int position); 0157 Q_REQUIRED_RESULT int selectionStart() const; 0158 void setSelectionStart(int position); 0159 Q_REQUIRED_RESULT int selectionEnd() const; 0160 void setSelectionEnd(int position); 0161 Q_REQUIRED_RESULT bool wordIsMisspelled() const; 0162 Q_REQUIRED_RESULT QString wordUnderMouse() const; 0163 Q_REQUIRED_RESULT bool spellCheckerFound() const; 0164 Q_REQUIRED_RESULT QString currentLanguage() const; 0165 void setActive(bool active); 0166 Q_REQUIRED_RESULT bool active() const; 0167 void setAutomatic(bool automatic); 0168 Q_REQUIRED_RESULT bool automatic() const; 0169 void setAutoDetectLanguageDisabled(bool autoDetectDisabled); 0170 Q_REQUIRED_RESULT bool autoDetectLanguageDisabled() const; 0171 void setMisspelledColor(const QColor &color); 0172 Q_REQUIRED_RESULT QColor misspelledColor() const; 0173 void setQuoteColor(const QColor &color); 0174 Q_REQUIRED_RESULT QColor quoteColor() const; 0175 0176 /// Return true if checker is enabled by default 0177 /// \since 5.88 0178 bool checkerEnabledByDefault() const; 0179 0180 /// Set a new @ref QTextDocument for this highlighter to operate on. 0181 /// \param document the new document to operate on. 0182 /// \since 5.88 0183 void setDocument(QTextDocument *document); 0184 0185 Q_SIGNALS: 0186 void documentChanged(); 0187 void cursorPositionChanged(); 0188 void selectionStartChanged(); 0189 void selectionEndChanged(); 0190 void wordIsMisspelledChanged(); 0191 void wordUnderMouseChanged(); 0192 void changeCursorPosition(int start, int end); 0193 void activeChanged(); 0194 void misspelledColorChanged(); 0195 void autoDetectLanguageDisabledChanged(); 0196 void automaticChanged(); 0197 void currentLanguageChanged(); 0198 0199 /// Emitted when as-you-type spell checking is enabled or disabled. 0200 /// 0201 /// \param description is a i18n description of the new state, 0202 /// with an optional reason 0203 /// \since 5.88 0204 void activeChanged(const QString &description); 0205 0206 protected: 0207 void highlightBlock(const QString &text) override; 0208 virtual void setMisspelled(int start, int count); 0209 virtual void unsetMisspelled(int start, int count); 0210 bool eventFilter(QObject *o, QEvent *e) override; 0211 0212 bool intraWordEditing() const; 0213 void setIntraWordEditing(bool editing); 0214 0215 public Q_SLOTS: 0216 /// Set language to use for spell checking. 0217 /// 0218 /// \param language the language code for the new language to use. 0219 /// \since 5.88 0220 void setCurrentLanguage(const QString &language); 0221 0222 /// Run auto detection, disabling spell checking if too many errors are found. 0223 /// \since 5.88 0224 void slotAutoDetection(); 0225 0226 /// Force a new highlighting. 0227 /// \since 5.88 0228 void slotRehighlight(); 0229 0230 private: 0231 Q_REQUIRED_RESULT QTextCursor textCursor() const; 0232 Q_REQUIRED_RESULT QTextDocument *textDocument() const; 0233 void contentsChange(int pos, int add, int rem); 0234 0235 void autodetectLanguage(const QString &sentence); 0236 0237 HighlighterPrivate *const d; 0238 Q_DISABLE_COPY(SpellcheckHighlighter) 0239 };