7#include "richtexteditor.h"
10#include "textcustomeditor_debug.h"
12#include "widgets/textmessageindicator.h"
14#include <KConfigGroup>
16#include <KLocalizedString>
18#include <KSharedConfig>
19#include <KStandardAction>
20#include <KStandardGuiItem>
21#include <QActionGroup>
24#include "config-textcustomeditor.h"
25#if HAVE_KTEXTADDONS_KIO_SUPPORT
26#include <KIO/KUriFilterSearchProviderActions>
28#include <Sonnet/Dialog>
29#include <Sonnet/Highlighter>
30#include <sonnet/backgroundchecker.h>
31#include <sonnet/spellcheckdecorator.h>
32#include <sonnet/speller.h>
33#if HAVE_KTEXTADDONS_TEXT_TO_SPEECH_SUPPORT
34#include <TextEditTextToSpeech/TextToSpeech>
36#include <TextEmoticonsWidgets/EmoticonTextEditAction>
38#include <KColorScheme>
39#include <QApplication>
41#include <QContextMenuEvent>
42#include <QDialogButtonBox>
47#include <QTextDocumentFragment>
49using namespace TextCustomEditor;
56#if HAVE_KTEXTADDONS_KIO_SUPPORT
57 , webshortcutMenuManager(new
KIO::KUriFilterSearchProviderActions(q))
60 KConfig sonnetKConfig(QStringLiteral(
"sonnetrc"));
62 checkSpellingEnabled = group.readEntry(
"checkerEnabledByDefault",
false);
63 supportFeatures |= RichTextEditor::Search;
64 supportFeatures |= RichTextEditor::SpellChecking;
65 supportFeatures |= RichTextEditor::TextToSpeech;
66 supportFeatures |= RichTextEditor::AllowTab;
67#if HAVE_KTEXTADDONS_KIO_SUPPORT
68 supportFeatures |= RichTextEditor::AllowWebShortcut;
96 ~RichTextEditorPrivate()
98 delete richTextDecorator;
105 QString spellCheckingConfigFileName;
110#if HAVE_KTEXTADDONS_KIO_SUPPORT
114 QColor mReadOnlyBackgroundColor;
115 int mInitialFontSize;
116 bool customPalette =
false;
117 bool checkSpellingEnabled =
false;
118 bool activateLanguageMenu =
true;
119 bool showAutoCorrectionButton =
false;
122RichTextEditor::RichTextEditor(
QWidget *parent)
124 , d(new RichTextEditorPrivate(this))
126 setAcceptRichText(
true);
128 setSpellCheckingConfigFileName(
QString());
129 d->mInitialFontSize = font().pointSize();
130 regenerateColorScheme();
133RichTextEditor::~RichTextEditor() =
default;
135void RichTextEditor::regenerateColorScheme()
138 updateReadOnlyColor();
141void RichTextEditor::setDefaultFontSize(
int val)
143 d->mInitialFontSize = val;
147void RichTextEditor::slotDisplayMessageIndicator(
const QString &message)
149 d->textIndicator->display(message);
154 if (d->richTextDecorator) {
155 return d->richTextDecorator->highlighter();
161bool RichTextEditor::activateLanguageMenu()
const
163 return d->activateLanguageMenu;
166void RichTextEditor::setActivateLanguageMenu(
bool activate)
168 d->activateLanguageMenu = activate;
184 const bool emptyDocument =
document()->isEmpty();
187 enum { UndoAct, RedoAct, CutAct, CopyAct, PasteAct, ClearAct, SelectAllAct, NCountActs };
188 QAction *separatorAction =
nullptr;
189 const int idx = actionList.
indexOf(actionList[SelectAllAct]) + 1;
190 if (idx < actionList.
count()) {
191 separatorAction = actionList.
at(idx);
193 if (separatorAction) {
201 if (searchSupport()) {
221 if (!
isReadOnly() && spellCheckingSupport()) {
225 if (!d->speller->availableBackends().isEmpty()) {
227 i18n(
"Check Spelling…"),
229 &RichTextEditor::slotCheckSpelling);
234 QAction *autoSpellCheckAction = popup->
addAction(
i18n(
"Auto Spell Check"),
this, &RichTextEditor::slotToggleAutoSpellCheck);
236 autoSpellCheckAction->
setChecked(checkSpellingEnabled());
239 if (checkSpellingEnabled() && d->activateLanguageMenu) {
240 auto languagesMenu =
new QMenu(
i18n(
"Spell Checking Language"), popup);
242 languagesGroup->setExclusive(
true);
244 QString defaultSpellcheckingLanguage = spellCheckingLanguage();
245 if (defaultSpellcheckingLanguage.
isEmpty()) {
246 defaultSpellcheckingLanguage = d->speller->defaultLanguage();
250 while (i.hasNext()) {
252 QAction *languageAction = languagesMenu->addAction(i.key());
254 languageAction->
setChecked(defaultSpellcheckingLanguage == i.value());
255 languageAction->
setData(i.value());
271#if HAVE_KTEXTADDONS_TEXT_TO_SPEECH_SUPPORT
272 if (!emptyDocument) {
278#if HAVE_KTEXTADDONS_KIO_SUPPORT
279 if (webShortcutSupport() &&
textCursor().hasSelection()) {
282 d->webshortcutMenuManager->setSelectedText(selectedText);
283 d->webshortcutMenuManager->addWebShortcutsToMenu(popup);
286 if (emojiSupport()) {
292 addExtraMenuEntry(popup,
pos);
298void RichTextEditor::slotInsertEmoticon(
const QString &str)
303void RichTextEditor::slotSpeakText()
314void RichTextEditor::setWebShortcutSupport(
bool b)
316#if HAVE_KTEXTADDONS_KIO_SUPPORT
318 d->supportFeatures |= AllowWebShortcut;
320 d->supportFeatures = (d->supportFeatures & ~AllowWebShortcut);
327bool RichTextEditor::webShortcutSupport()
const
329#if HAVE_KTEXTADDONS_KIO_SUPPORT
330 return d->supportFeatures & AllowWebShortcut;
336void RichTextEditor::setEmojiSupport(
bool b)
339 d->supportFeatures |= Emoji;
341 d->supportFeatures = (d->supportFeatures & ~Emoji);
345bool RichTextEditor::emojiSupport()
const
347 return d->supportFeatures & Emoji;
350void RichTextEditor::addIgnoreWords(
const QStringList &lst)
352 d->ignoreSpellCheckingWords = lst;
353 addIgnoreWordsToHighLighter();
356void RichTextEditor::forceAutoCorrection(
bool selectedText)
358 Q_UNUSED(selectedText)
362void RichTextEditor::setSearchSupport(
bool b)
365 d->supportFeatures |= Search;
367 d->supportFeatures = (d->supportFeatures & ~Search);
371bool RichTextEditor::searchSupport()
const
373 return d->supportFeatures & Search;
376void RichTextEditor::setAllowTabSupport(
bool b)
379 d->supportFeatures |= AllowTab;
381 d->supportFeatures = (d->supportFeatures & ~AllowTab);
385bool RichTextEditor::allowTabSupport()
const
387 return d->supportFeatures & AllowTab;
390void RichTextEditor::setShowAutoCorrectButton(
bool b)
392 d->showAutoCorrectionButton = b;
395bool RichTextEditor::showAutoCorrectButton()
const
397 return d->showAutoCorrectionButton;
400bool RichTextEditor::spellCheckingSupport()
const
402 return d->supportFeatures & SpellChecking;
405void RichTextEditor::setSpellCheckingSupport(
bool check)
408 d->supportFeatures |= SpellChecking;
410 d->supportFeatures = (d->supportFeatures & ~SpellChecking);
414void RichTextEditor::setTextToSpeechSupport(
bool b)
417 d->supportFeatures |= TextToSpeech;
419 d->supportFeatures = (d->supportFeatures & ~TextToSpeech);
423bool RichTextEditor::textToSpeechSupport()
const
425 return d->supportFeatures & TextToSpeech;
428void RichTextEditor::slotAllowTab()
433void RichTextEditor::addExtraMenuEntry(
QMenu *menu,
QPoint pos)
439void RichTextEditor::slotUndoableClear()
445 cursor.removeSelectedText();
449void RichTextEditor::updateReadOnlyColor()
459void RichTextEditor::setReadOnly(
bool readOnly)
461 if (!
readOnly &&
hasFocus() && checkSpellingEnabled() && !d->richTextDecorator) {
472 updateReadOnlyColor();
488void RichTextEditor::checkSpelling(
bool force)
491 slotDisplayMessageIndicator(
i18n(
"Nothing to spell check."));
493 Q_EMIT spellCheckingFinished();
498 if (backgroundSpellCheck->speller().availableBackends().isEmpty()) {
501 i18n(
"No backend available for spell checking. Do you want to send the email anyways?"),
503 KGuiItem(
i18nc(
"@action:button",
"Send"), QStringLiteral(
"mail-send")),
505 if (answer == KMessageBox::ButtonCode::PrimaryAction) {
506 Q_EMIT spellCheckingFinished();
509 slotDisplayMessageIndicator(
i18n(
"No backend available for spell checking."));
511 delete backgroundSpellCheck;
514 if (!d->spellCheckingLanguage.isEmpty()) {
515 backgroundSpellCheck->changeLanguage(d->spellCheckingLanguage);
517 if (!d->ignoreSpellCheckingWords.isEmpty()) {
518 for (
const QString &word : std::as_const(d->ignoreSpellCheckingWords)) {
519 backgroundSpellCheck->speller().addToSession(word);
522 auto spellDialog =
new Sonnet::Dialog(backgroundSpellCheck, force ?
this :
nullptr);
532 qCWarning(TEXTCUSTOMEDITOR_LOG) <<
" Impossible to find qdialogbuttonbox";
534 backgroundSpellCheck->
setParent(spellDialog);
536 spellDialog->activeAutoCorrect(d->showAutoCorrectionButton);
537 connect(spellDialog, &Sonnet::Dialog::replace,
this, &RichTextEditor::slotSpellCheckerCorrected);
538 connect(spellDialog, &Sonnet::Dialog::misspelling,
this, &RichTextEditor::slotSpellCheckerMisspelling);
539 connect(spellDialog, &Sonnet::Dialog::autoCorrect,
this, &RichTextEditor::slotSpellCheckerAutoCorrect);
541 connect(spellDialog, &Sonnet::Dialog::cancel,
this, &RichTextEditor::slotSpellCheckerCanceled);
546 connect(spellDialog, &Sonnet::Dialog::cancel,
this, &RichTextEditor::spellCheckingCanceled);
553void RichTextEditor::slotCheckSpelling()
555 checkSpelling(
false);
558void RichTextEditor::forceSpellChecking()
563void RichTextEditor::slotSpellCheckerCanceled()
568 cursor.insertFragment(d->originalDoc);
569 slotSpellCheckerFinished();
572void RichTextEditor::slotSpellCheckerAutoCorrect(
const QString ¤tWord,
const QString &autoCorrectWord)
574 Q_EMIT spellCheckerAutoCorrect(currentWord, autoCorrectWord);
577void RichTextEditor::slotSpellCheckerMisspelling(
const QString &text,
int pos)
582void RichTextEditor::slotSpellCheckerCorrected(
const QString &oldWord,
int pos,
const QString &newWord)
584 if (oldWord != newWord) {
588 cursor.insertText(newWord);
592void RichTextEditor::slotSpellCheckerFinished()
602void RichTextEditor::highlightWord(
int length,
int pos)
611void RichTextEditor::createHighlighter()
615 setHighlighter(highlighter);
623void RichTextEditor::addIgnoreWordsToHighLighter()
625 if (d->ignoreSpellCheckingWords.isEmpty()) {
628 if (d->richTextDecorator) {
630 for (
const QString &word : std::as_const(d->ignoreSpellCheckingWords)) {
642 d->richTextDecorator = decorator;
643 addIgnoreWordsToHighLighter();
646void RichTextEditor::focusInEvent(
QFocusEvent *event)
648 if (d->checkSpellingEnabled && !
isReadOnly() && !d->richTextDecorator && spellCheckingSupport()) {
655void RichTextEditor::setSpellCheckingConfigFileName(
const QString &_fileName)
657 d->spellCheckingConfigFileName = _fileName;
659 if (config->hasGroup(
"Spelling"_L1)) {
661 d->checkSpellingEnabled = group.readEntry(
"checkerEnabledByDefault",
false);
662 d->spellCheckingLanguage = group.readEntry(
"Language",
QString());
664 setCheckSpellingEnabled(checkSpellingEnabled());
666 if (!d->spellCheckingLanguage.isEmpty() && highlighter()) {
672QString RichTextEditor::spellCheckingConfigFileName()
const
674 return d->spellCheckingConfigFileName;
677bool RichTextEditor::checkSpellingEnabled()
const
679 return d->checkSpellingEnabled;
682void RichTextEditor::setCheckSpellingEnabled(
bool check)
684 if (check == d->checkSpellingEnabled) {
687 d->checkSpellingEnabled = check;
688 Q_EMIT checkSpellingChanged(check);
695 if (!d->richTextDecorator) {
698 if (!d->spellCheckingLanguage.isEmpty()) {
699 setSpellCheckingLanguage(spellCheckingLanguage());
708void RichTextEditor::updateHighLighter()
712void RichTextEditor::clearDecorator()
714 delete d->richTextDecorator;
715 d->richTextDecorator =
nullptr;
718const QString &RichTextEditor::spellCheckingLanguage()
const
720 return d->spellCheckingLanguage;
723void RichTextEditor::setSpellCheckingLanguage(
const QString &_language)
729 if (_language != d->spellCheckingLanguage) {
730 d->spellCheckingLanguage = _language;
733 group.writeEntry(
"Language", d->spellCheckingLanguage);
735 Q_EMIT languageChanged(_language);
739void RichTextEditor::slotToggleAutoSpellCheck()
741 setCheckSpellingEnabled(!checkSpellingEnabled());
744 group.writeEntry(
"checkerEnabledByDefault", d->checkSpellingEnabled);
747void RichTextEditor::slotLanguageSelected()
750 setSpellCheckingLanguage(languageAction->
data().
toString());
760void RichTextEditor::deleteWordBack()
765void RichTextEditor::deleteWordForward()
770bool RichTextEditor::event(
QEvent *ev)
774 if (overrideShortcut(e)) {
779 regenerateColorScheme();
787 const int angleDeltaY{
event->angleDelta().y()};
788 if (angleDeltaY > 0) {
790 }
else if (angleDeltaY < 0) {
799bool RichTextEditor::handleShortcut(
QKeyEvent *event)
801 const int key =
event->key() |
event->modifiers();
920 cursor.removeSelectedText();
928bool RichTextEditor::overrideShortcut(
QKeyEvent *event)
930 const int key =
event->key() |
event->modifiers();
978void RichTextEditor::keyPressEvent(
QKeyEvent *event)
982 if (handleShortcut(event)) {
984 }
else if (
event->key() ==
Qt::Key_Up && isControlClicked && isShiftClicked) {
985 moveLineUpDown(
true);
988 moveLineUpDown(
false);
991 moveCursorBeginUpDown(
true);
994 moveCursorBeginUpDown(
false);
1001int RichTextEditor::zoomFactor()
const
1003 int pourcentage = 100;
1005 if (d->mInitialFontSize != f.
pointSize()) {
1006 pourcentage = (f.
pointSize() * 100) / d->mInitialFontSize;
1011void RichTextEditor::slotZoomReset()
1014 if (d->mInitialFontSize != f.
pointSize()) {
1020void RichTextEditor::moveCursorBeginUpDown(
bool moveUp)
1028 move.endEditBlock();
1032void RichTextEditor::moveLineUpDown(
bool moveUp)
1038 const bool hasSelection =
cursor.hasSelection();
1052 move.removeSelectedText();
1060 if (
move.atBlockStart()) {
1070 move.clearSelection();
1071 move.insertText(text);
1075 move.setPosition(end);
1080 move.endEditBlock();
1085#include "moc_richtexteditor.cpp"
QBrush background(BackgroundRole=NormalBackground) const
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)
void setCurrentLanguage(const QString &language)
Highlighter * highlighter() const
void setHighlighter(Highlighter *highlighter)
The RichTextEditor class.
A widget that displays messages in the top-left corner.
The TextEmoticonsWidgets::EmoticonTextEditAction class.
void insertEmoticon(const QString &)
This signal is emitted each time the user selects an emoji.
Q_SCRIPTABLE Q_NOREPLY void start()
QString i18nc(const char *context, const char *text, const TYPE &arg...)
QString i18n(const char *text, const TYPE &arg...)
AKONADI_CALENDAR_EXPORT KCalendarCore::Event::Ptr event(const Akonadi::Item &item)
ButtonCode questionTwoActions(QWidget *parent, const QString &text, const QString &title, const KGuiItem &primaryAction, const KGuiItem &secondaryAction, const QString &dontAskAgainName=QString(), Options options=Notify)
QAction * replace(const QObject *recvr, const char *slot, QObject *parent)
QAction * find(const QObject *recvr, const char *slot, QObject *parent)
QAction * clear(const QObject *recvr, const char *slot, QObject *parent)
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)
const QColor & color() const const
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)
virtual bool event(QEvent *e)
QObject * sender() const const
const QColor & color(ColorGroup group, ColorRole role) const const
void setColor(ColorGroup group, ColorRole role, const QColor &color)
bool isEmpty() const const
qsizetype length() const const
int position() const const
bool isAnchor() const const
bool atBlockEnd() const const
bool atBlockStart() const const
QTextCharFormat charFormat() const const
bool hasSelection() const const
bool movePosition(MoveOperation operation, MoveMode mode, int n)
void removeSelectedText()
QString selectedText() const const
void setCharFormat(const QTextCharFormat &format)
QMenu * createStandardContextMenu()
QRect cursorRect() 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
QString toString() const const