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