File indexing completed on 2024-04-28 15:34:20

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 };