6#include "plaintexteditor.h"
7#include "config-textcustomeditor.h"
8#include "widgets/textmessageindicator.h"
10#if HAVE_KTEXTADDONS_TEXT_TO_SPEECH_SUPPORT
11#include <TextEditTextToSpeech/TextToSpeech>
13#include <TextEmoticonsWidgets/EmoticonTextEditAction>
16#include <KConfigGroup>
18#if HAVE_KTEXTADDONS_KIO_SUPPORT
19#include <KIO/KUriFilterSearchProviderActions>
21#include <KColorScheme>
22#include <KLocalizedString>
23#include <KSharedConfig>
24#include <KStandardActions>
25#include <Sonnet/BackgroundChecker>
26#include <Sonnet/Dialog>
27#include <Sonnet/Highlighter>
28#include <Sonnet/SpellCheckDecorator>
29#include <Sonnet/Speller>
31#include <QActionGroup>
32#include <QApplication>
38#include <QTextDocumentFragment>
40using namespace TextCustomEditor;
46 PlainTextEditorPrivate(PlainTextEditor *qq)
48 , mTextIndicator(new TextCustomEditor::TextMessageIndicator(q))
49#if HAVE_KTEXTADDONS_KIO_SUPPORT
50 , webshortcutMenuManager(new KIO::KUriFilterSearchProviderActions(q))
53 KConfig sonnetKConfig(QStringLiteral(
"sonnetrc"));
54 KConfigGroup group(&sonnetKConfig,
"Spelling"_L1);
55 checkSpellingEnabled = group.readEntry(
"checkerEnabledByDefault",
false);
56 supportFeatures |= PlainTextEditor::Search;
57 supportFeatures |= PlainTextEditor::SpellChecking;
58 supportFeatures |= PlainTextEditor::TextToSpeech;
59#if HAVE_KTEXTADDONS_KIO_SUPPORT
60 supportFeatures |= PlainTextEditor::AllowWebShortcut;
64 ~PlainTextEditorPrivate()
66 delete richTextDecorator;
70 QStringList ignoreSpellCheckingWords;
71 PlainTextEditor *
const q;
72 TextCustomEditor::TextMessageIndicator *
const mTextIndicator;
73#if HAVE_KTEXTADDONS_KIO_SUPPORT
74 KIO::KUriFilterSearchProviderActions *
const webshortcutMenuManager;
76 Sonnet::SpellCheckDecorator *richTextDecorator =
nullptr;
77 Sonnet::Speller *speller =
nullptr;
79 QString spellCheckingConfigFileName;
80 QString spellCheckingLanguage;
81 QTextDocumentFragment originalDoc;
82 PlainTextEditor::SupportFeatures supportFeatures;
83 QColor mReadOnlyBackgroundColor;
84 int mInitialFontSize = 0;
85 bool customPalette =
false;
86 bool activateLanguageMenu =
true;
87 bool checkSpellingEnabled =
false;
90PlainTextEditor::PlainTextEditor(
QWidget *parent)
95 setSpellCheckingConfigFileName(
QString());
96 d->mInitialFontSize = font().pointSize();
97 regenerateColorScheme();
100PlainTextEditor::~PlainTextEditor() =
default;
102void PlainTextEditor::regenerateColorScheme()
105 updateReadOnlyColor();
108void PlainTextEditor::addIgnoreWords(
const QStringList &lst)
110 d->ignoreSpellCheckingWords = lst;
111 addIgnoreWordsToHighLighter();
114void PlainTextEditor::slotDisplayMessageIndicator(
const QString &message)
116 d->mTextIndicator->display(message);
119void PlainTextEditor::contextMenuEvent(QContextMenuEvent *event)
125 const QList<QAction *> actionList = popup->
actions();
136 QAction *separatorAction =
nullptr;
137 const int idx = actionList.
indexOf(actionList[SelectAllAct]) + 1;
138 if (idx < actionList.
count()) {
139 separatorAction = actionList.
at(idx);
141 if (separatorAction) {
142 if (!emptyDocument) {
143 QAction *clearAllAction = KStandardActions::clear(
this, &PlainTextEditor::slotUndoableClear, popup);
148 if (d->supportFeatures & Search) {
150 if (!emptyDocument) {
151 QAction *findAction = KStandardActions::find(
this, &PlainTextEditor::findText, popup);
156 if (!emptyDocument) {
157 QAction *replaceAction = KStandardActions::replace(
this, &PlainTextEditor::replaceText, popup);
166 if (!
isReadOnly() && spellCheckingSupport()) {
168 d->speller =
new Sonnet::Speller();
170 if (!d->speller->availableBackends().isEmpty()) {
171 if (!emptyDocument) {
173 i18n(
"Check Spelling…"),
175 &PlainTextEditor::slotCheckSpelling);
178 QAction *autoSpellCheckAction = popup->
addAction(
i18n(
"Auto Spell Check"),
this, &PlainTextEditor::slotToggleAutoSpellCheck);
180 autoSpellCheckAction->
setChecked(checkSpellingEnabled());
183 if (checkSpellingEnabled() && d->activateLanguageMenu) {
184 auto languagesMenu =
new QMenu(
i18n(
"Spell Checking Language"), popup);
185 auto languagesGroup =
new QActionGroup(languagesMenu);
186 languagesGroup->setExclusive(
true);
188 QString defaultSpellcheckingLanguage = spellCheckingLanguage();
189 if (defaultSpellcheckingLanguage.
isEmpty()) {
191 defaultSpellcheckingLanguage = d->speller->defaultLanguage();
194 QMapIterator<QString, QString> i(d->speller->availableDictionaries());
195 while (i.hasNext()) {
197 QAction *languageAction = languagesMenu->addAction(i.key());
199 languageAction->
setChecked(defaultSpellcheckingLanguage == i.value());
200 languageAction->
setData(i.value());
209 if (d->supportFeatures & TextToSpeech) {
210#if HAVE_KTEXTADDONS_TEXT_TO_SPEECH_SUPPORT
211 if (!emptyDocument) {
218#if HAVE_KTEXTADDONS_KIO_SUPPORT
219 if (webShortcutSupport() &&
textCursor().hasSelection()) {
222 d->webshortcutMenuManager->setSelectedText(selectedText);
223 d->webshortcutMenuManager->addWebShortcutsToMenu(popup);
226 if (emojiSupport()) {
228 auto action =
new TextEmoticonsWidgets::EmoticonTextEditAction(
this);
232 addExtraMenuEntry(popup, event->pos());
233 popup->
exec(event->globalPos());
239void PlainTextEditor::slotInsertEmoticon(
const QString &str)
243void PlainTextEditor::setEmojiSupport(
bool b)
246 d->supportFeatures |= Emoji;
248 d->supportFeatures = (d->supportFeatures & ~Emoji);
252bool PlainTextEditor::emojiSupport()
const
254 return d->supportFeatures & Emoji;
257void PlainTextEditor::addExtraMenuEntry(QMenu *menu, QPoint pos)
263void PlainTextEditor::slotSpeakText()
275void PlainTextEditor::slotUndoableClear()
281 cursor.removeSelectedText();
285void PlainTextEditor::setSearchSupport(
bool b)
288 d->supportFeatures |= Search;
290 d->supportFeatures = (d->supportFeatures & ~Search);
294bool PlainTextEditor::searchSupport()
const
296 return d->supportFeatures & Search;
299void PlainTextEditor::setTextToSpeechSupport(
bool b)
302 d->supportFeatures |= TextToSpeech;
304 d->supportFeatures = (d->supportFeatures & ~TextToSpeech);
308bool PlainTextEditor::textToSpeechSupport()
const
310 return d->supportFeatures & TextToSpeech;
313bool PlainTextEditor::spellCheckingSupport()
const
315 return d->supportFeatures & SpellChecking;
318void PlainTextEditor::setSpellCheckingSupport(
bool check)
321 d->supportFeatures |= SpellChecking;
323 d->supportFeatures = (d->supportFeatures & ~SpellChecking);
327void PlainTextEditor::setWebShortcutSupport(
bool b)
329#if HAVE_KTEXTADDONS_KIO_SUPPORT
331 d->supportFeatures |= AllowWebShortcut;
333 d->supportFeatures = (d->supportFeatures & ~AllowWebShortcut);
340bool PlainTextEditor::webShortcutSupport()
const
342#if HAVE_KTEXTADDONS_KIO_SUPPORT
343 return d->supportFeatures & AllowWebShortcut;
349void PlainTextEditor::updateReadOnlyColor()
359void PlainTextEditor::setReadOnly(
bool readOnly)
361 if (!
readOnly &&
hasFocus() && d->checkSpellingEnabled && !d->richTextDecorator) {
372 updateReadOnlyColor();
388void PlainTextEditor::slotCheckSpelling()
391 slotDisplayMessageIndicator(
i18n(
"Nothing to spell check."));
394 auto backgroundSpellCheck =
new Sonnet::BackgroundChecker;
395 if (backgroundSpellCheck->speller().availableBackends().isEmpty()) {
396 slotDisplayMessageIndicator(
i18n(
"No backend available for spell checking."));
397 delete backgroundSpellCheck;
400 if (!d->spellCheckingLanguage.isEmpty()) {
401 backgroundSpellCheck->changeLanguage(d->spellCheckingLanguage);
403 if (!d->ignoreSpellCheckingWords.isEmpty()) {
404 for (
const QString &word : std::as_const(d->ignoreSpellCheckingWords)) {
405 backgroundSpellCheck->speller().addToSession(word);
408 auto spellDialog =
new Sonnet::Dialog(backgroundSpellCheck,
nullptr);
409 backgroundSpellCheck->setParent(spellDialog);
411 connect(spellDialog, &Sonnet::Dialog::replace,
this, &PlainTextEditor::slotSpellCheckerCorrected);
412 connect(spellDialog, &Sonnet::Dialog::misspelling,
this, &PlainTextEditor::slotSpellCheckerMisspelling);
413 connect(spellDialog, &Sonnet::Dialog::autoCorrect,
this, &PlainTextEditor::slotSpellCheckerAutoCorrect);
415 connect(spellDialog, &Sonnet::Dialog::cancel,
this, &PlainTextEditor::slotSpellCheckerCanceled);
418 d->originalDoc = QTextDocumentFragment(
document());
423void PlainTextEditor::slotSpellCheckerCanceled()
428 cursor.insertFragment(d->originalDoc);
429 slotSpellCheckerFinished();
432void PlainTextEditor::slotSpellCheckerAutoCorrect(
const QString ¤tWord,
const QString &autoCorrectWord)
434 Q_EMIT spellCheckerAutoCorrect(currentWord, autoCorrectWord);
437void PlainTextEditor::slotSpellCheckerMisspelling(
const QString &text,
int pos)
442void PlainTextEditor::slotSpellCheckerCorrected(
const QString &oldWord,
int pos,
const QString &newWord)
444 if (oldWord != newWord) {
448 cursor.insertText(newWord);
452void PlainTextEditor::slotSpellCheckerFinished()
459void PlainTextEditor::highlightWord(
int length,
int pos)
475void PlainTextEditor::deleteWordBack()
480void PlainTextEditor::deleteWordForward()
485bool PlainTextEditor::event(QEvent *ev)
488 auto e =
static_cast<QKeyEvent *
>(ev);
489 if (overrideShortcut(e)) {
494 regenerateColorScheme();
500bool PlainTextEditor::overrideShortcut(QKeyEvent *event)
502 const int key =
event->key() |
event->modifiers();
549bool PlainTextEditor::handleShortcut(QKeyEvent *event)
551 const int key =
event->key() |
event->modifiers();
669void PlainTextEditor::deleteEndOfLine()
672 QTextBlock block =
cursor.block();
678 cursor.removeSelectedText();
682void PlainTextEditor::moveCursorBeginUpDown(
bool moveUp)
686 move.beginEditBlock();
694void PlainTextEditor::moveLineUpDown(
bool moveUp)
698 move.beginEditBlock();
700 const bool hasSelection =
cursor.hasSelection();
711 const QString text =
move.selectedText();
714 move.removeSelectedText();
722 if (
move.atBlockStart()) {
732 move.clearSelection();
733 move.insertText(text);
737 move.setPosition(end);
747void PlainTextEditor::wheelEvent(QWheelEvent *event)
750 if (event->angleDelta().y() > 0) {
752 }
else if (event->angleDelta().y() < 0) {
761void PlainTextEditor::keyPressEvent(QKeyEvent *event)
765 if (handleShortcut(event)) {
767 }
else if (event->key() ==
Qt::Key_Up && isControlClicked && isShiftClicked) {
768 moveLineUpDown(
true);
770 }
else if (event->key() ==
Qt::Key_Down && isControlClicked && isShiftClicked) {
771 moveLineUpDown(
false);
773 }
else if (event->key() ==
Qt::Key_Up && isControlClicked) {
774 moveCursorBeginUpDown(
true);
776 }
else if (event->key() ==
Qt::Key_Down && isControlClicked) {
777 moveCursorBeginUpDown(
false);
784bool PlainTextEditor::activateLanguageMenu()
const
786 return d->activateLanguageMenu;
789void PlainTextEditor::setActivateLanguageMenu(
bool activate)
791 d->activateLanguageMenu = activate;
794Sonnet::Highlighter *PlainTextEditor::highlighter()
const
796 if (d->richTextDecorator) {
797 return d->richTextDecorator->highlighter();
803Sonnet::SpellCheckDecorator *PlainTextEditor::createSpellCheckDecorator()
805 return new Sonnet::SpellCheckDecorator(
this);
808void PlainTextEditor::addIgnoreWordsToHighLighter()
810 if (d->ignoreSpellCheckingWords.isEmpty()) {
813 if (d->richTextDecorator) {
814 Sonnet::Highlighter *_highlighter = d->richTextDecorator->highlighter();
815 for (
const QString &word : std::as_const(d->ignoreSpellCheckingWords)) {
821void PlainTextEditor::setHighlighter(Sonnet::Highlighter *_highLighter)
823 Sonnet::SpellCheckDecorator *decorator = createSpellCheckDecorator();
827 d->richTextDecorator = decorator;
828 addIgnoreWordsToHighLighter();
831void PlainTextEditor::focusInEvent(QFocusEvent *event)
833 if (checkSpellingEnabled() && !
isReadOnly() && !d->richTextDecorator && spellCheckingSupport()) {
840bool PlainTextEditor::checkSpellingEnabled()
const
842 return d->checkSpellingEnabled;
845void PlainTextEditor::setCheckSpellingEnabled(
bool check)
847 if (check == d->checkSpellingEnabled) {
850 d->checkSpellingEnabled = check;
851 Q_EMIT checkSpellingChanged(check);
857 if (!d->richTextDecorator) {
860 if (!d->spellCheckingLanguage.isEmpty()) {
861 setSpellCheckingLanguage(spellCheckingLanguage());
870void PlainTextEditor::updateHighLighter()
874void PlainTextEditor::clearDecorator()
876 delete d->richTextDecorator;
877 d->richTextDecorator =
nullptr;
880void PlainTextEditor::createHighlighter()
882 auto highlighter =
new Sonnet::Highlighter(
this);
883 highlighter->setCurrentLanguage(spellCheckingLanguage());
884 setHighlighter(highlighter);
887void PlainTextEditor::setSpellCheckingConfigFileName(
const QString &_fileName)
889 d->spellCheckingConfigFileName = _fileName;
891 if (config->hasGroup(
"Spelling"_L1)) {
892 KConfigGroup group(config,
"Spelling"_L1);
893 d->checkSpellingEnabled = group.readEntry(
"checkerEnabledByDefault",
false);
894 d->spellCheckingLanguage = group.readEntry(
"Language", QString());
896 setCheckSpellingEnabled(checkSpellingEnabled());
898 if (!d->spellCheckingLanguage.isEmpty() && highlighter()) {
899 highlighter()->setCurrentLanguage(d->spellCheckingLanguage);
900 highlighter()->rehighlight();
904QString PlainTextEditor::spellCheckingConfigFileName()
const
906 return d->spellCheckingConfigFileName;
909void PlainTextEditor::slotLanguageSelected()
912 setSpellCheckingLanguage(languageAction->
data().
toString());
915const QString &PlainTextEditor::spellCheckingLanguage()
const
917 return d->spellCheckingLanguage;
920void PlainTextEditor::setSpellCheckingLanguage(
const QString &_language)
923 highlighter()->setCurrentLanguage(_language);
924 highlighter()->rehighlight();
927 if (_language != d->spellCheckingLanguage) {
928 d->spellCheckingLanguage = _language;
930 KConfigGroup group(config,
"Spelling"_L1);
931 group.writeEntry(
"Language", d->spellCheckingLanguage);
932 setCheckSpellingEnabled(checkSpellingEnabled());
934 Q_EMIT languageChanged(_language);
938void PlainTextEditor::slotToggleAutoSpellCheck()
940 setCheckSpellingEnabled(!checkSpellingEnabled());
942 KConfigGroup group(config,
"Spelling"_L1);
943 group.writeEntry(
"checkerEnabledByDefault", d->checkSpellingEnabled);
946void PlainTextEditor::slotZoomReset()
949 if (d->mInitialFontSize != f.
pointSize()) {
955#include "moc_plaintexteditor.cpp"
static void setAutoHideCursor(QWidget *w, bool enable, bool customEventFilter=false)
static KSharedConfig::Ptr openConfig(const QString &fileName=QString(), OpenFlags mode=FullConfig, QStandardPaths::StandardLocation type=QStandardPaths::GenericConfigLocation)
void spellCheckDone(const QString &newBuffer)
void spellCheckStatus(const QString &)
void languageChanged(const QString &language)
void ignoreWord(const QString &word)
Highlighter * highlighter() const
void setHighlighter(Highlighter *highlighter)
The PlainTextEditor class.
void insertEmoticon(const QString &)
This signal is emitted each time the user selects an emoji.
Q_SCRIPTABLE Q_NOREPLY void start()
QString i18n(const char *text, const TYPE &arg...)
const QList< QKeySequence > & beginningOfLine()
const QList< QKeySequence > & begin()
const QList< QKeySequence > & cut()
const QList< QKeySequence > & undo()
const QList< QKeySequence > & next()
const QList< QKeySequence > & deleteWordBack()
const QList< QKeySequence > & find()
const QList< QKeySequence > & paste()
const QList< QKeySequence > & end()
const QList< QKeySequence > & copy()
const QList< QKeySequence > & backwardWord()
const QList< QKeySequence > & endOfLine()
const QList< QKeySequence > & forwardWord()
const QList< QKeySequence > & deleteWordForward()
const QList< QKeySequence > & findNext()
const QList< QKeySequence > & prior()
const QList< QKeySequence > & replace()
const QList< QKeySequence > & pasteSelection()
const QList< QKeySequence > & redo()
KOSM_EXPORT double distance(const std::vector< const OSM::Node * > &path, Coordinate coord)
void triggerAction(SliderAction action)
QVariant data() const const
void setIcon(const QIcon &icon)
void setActionGroup(QActionGroup *group)
void setData(const QVariant &data)
void triggered(bool checked)
QString text(Mode mode) const const
int pointSize() const const
void setPointSize(int pointSize)
Qt::KeyboardModifiers keyboardModifiers()
QIcon fromTheme(const QString &name)
const_reference at(qsizetype i) const const
qsizetype count() const const
qsizetype indexOf(const AT &value, qsizetype from) const const
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
QObject * sender() const const
const QColor & color(ColorGroup group, ColorRole role) const const
void setColor(ColorGroup group, ColorRole role, const QColor &color)
QMenu * createStandardContextMenu()
QRect cursorRect() const const
QTextDocument * document() const const
void ensureCursorVisible()
virtual void focusInEvent(QFocusEvent *e) override
void insertPlainText(const QString &text)
virtual void keyPressEvent(QKeyEvent *e) override
bool isReadOnly() const const
void setTextCursor(const QTextCursor &cursor)
QTextCursor textCursor() const const
QString toPlainText() const const
virtual void wheelEvent(QWheelEvent *e) override
bool isEmpty() const const
qsizetype length() const const
int position() const const
bool movePosition(MoveOperation operation, MoveMode mode, int n)
void removeSelectedText()
QString selectedText() const const
bool isEmpty() const const
QString toString() const const