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 <KStandardActions>
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();
197 QAction *separatorAction =
nullptr;
198 const int idx = actionList.
indexOf(actionList[SelectAllAct]) + 1;
199 if (idx < actionList.
count()) {
200 separatorAction = actionList.
at(idx);
202 if (separatorAction) {
203 QAction *clearAllAction = KStandardActions::clear(
this, &RichTextEditor::slotUndoableClear, popup);
210 if (searchSupport()) {
212 QAction *findAction = KStandardActions::find(
this, &RichTextEditor::findText, popup);
219 QAction *act = KStandardActions::replace(
this, &RichTextEditor::replaceText, popup);
230 if (!
isReadOnly() && spellCheckingSupport()) {
234 if (!d->speller->availableBackends().isEmpty()) {
236 i18n(
"Check Spelling…"),
238 &RichTextEditor::slotCheckSpelling);
243 QAction *autoSpellCheckAction = popup->
addAction(
i18n(
"Auto Spell Check"),
this, &RichTextEditor::slotToggleAutoSpellCheck);
245 autoSpellCheckAction->
setChecked(checkSpellingEnabled());
248 if (checkSpellingEnabled() && d->activateLanguageMenu) {
249 auto languagesMenu =
new QMenu(
i18n(
"Spell Checking Language"), popup);
251 languagesGroup->setExclusive(
true);
253 QString defaultSpellcheckingLanguage = spellCheckingLanguage();
254 if (defaultSpellcheckingLanguage.
isEmpty()) {
255 defaultSpellcheckingLanguage = d->speller->defaultLanguage();
259 while (i.hasNext()) {
261 QAction *languageAction = languagesMenu->addAction(i.key());
263 languageAction->
setChecked(defaultSpellcheckingLanguage == i.value());
264 languageAction->
setData(i.value());
280#if HAVE_KTEXTADDONS_TEXT_TO_SPEECH_SUPPORT
281 if (!emptyDocument) {
287#if HAVE_KTEXTADDONS_KIO_SUPPORT
288 if (webShortcutSupport() &&
textCursor().hasSelection()) {
291 d->webshortcutMenuManager->setSelectedText(selectedText);
292 d->webshortcutMenuManager->addWebShortcutsToMenu(popup);
295 if (emojiSupport()) {
301 addExtraMenuEntry(popup,
pos);
307void RichTextEditor::slotInsertEmoticon(
const QString &str)
312void RichTextEditor::slotSpeakText()
323void RichTextEditor::setWebShortcutSupport(
bool b)
325#if HAVE_KTEXTADDONS_KIO_SUPPORT
327 d->supportFeatures |= AllowWebShortcut;
329 d->supportFeatures = (d->supportFeatures & ~AllowWebShortcut);
336bool RichTextEditor::webShortcutSupport()
const
338#if HAVE_KTEXTADDONS_KIO_SUPPORT
339 return d->supportFeatures & AllowWebShortcut;
345void RichTextEditor::setEmojiSupport(
bool b)
348 d->supportFeatures |= Emoji;
350 d->supportFeatures = (d->supportFeatures & ~Emoji);
354bool RichTextEditor::emojiSupport()
const
356 return d->supportFeatures & Emoji;
359void RichTextEditor::addIgnoreWords(
const QStringList &lst)
361 d->ignoreSpellCheckingWords = lst;
362 addIgnoreWordsToHighLighter();
365void RichTextEditor::forceAutoCorrection(
bool selectedText)
367 Q_UNUSED(selectedText)
371void RichTextEditor::setSearchSupport(
bool b)
374 d->supportFeatures |= Search;
376 d->supportFeatures = (d->supportFeatures & ~Search);
380bool RichTextEditor::searchSupport()
const
382 return d->supportFeatures & Search;
385void RichTextEditor::setAllowTabSupport(
bool b)
388 d->supportFeatures |= AllowTab;
390 d->supportFeatures = (d->supportFeatures & ~AllowTab);
394bool RichTextEditor::allowTabSupport()
const
396 return d->supportFeatures & AllowTab;
399void RichTextEditor::setShowAutoCorrectButton(
bool b)
401 d->showAutoCorrectionButton = b;
404bool RichTextEditor::showAutoCorrectButton()
const
406 return d->showAutoCorrectionButton;
409bool RichTextEditor::spellCheckingSupport()
const
411 return d->supportFeatures & SpellChecking;
414void RichTextEditor::setSpellCheckingSupport(
bool check)
417 d->supportFeatures |= SpellChecking;
419 d->supportFeatures = (d->supportFeatures & ~SpellChecking);
423void RichTextEditor::setTextToSpeechSupport(
bool b)
426 d->supportFeatures |= TextToSpeech;
428 d->supportFeatures = (d->supportFeatures & ~TextToSpeech);
432bool RichTextEditor::textToSpeechSupport()
const
434 return d->supportFeatures & TextToSpeech;
437void RichTextEditor::slotAllowTab()
442void RichTextEditor::addExtraMenuEntry(
QMenu *menu,
QPoint pos)
448void RichTextEditor::slotUndoableClear()
454 cursor.removeSelectedText();
458void RichTextEditor::updateReadOnlyColor()
468void RichTextEditor::setReadOnly(
bool readOnly)
470 if (!
readOnly &&
hasFocus() && checkSpellingEnabled() && !d->richTextDecorator) {
481 updateReadOnlyColor();
497void RichTextEditor::checkSpelling(
bool force)
500 slotDisplayMessageIndicator(
i18n(
"Nothing to spell check."));
502 Q_EMIT spellCheckingFinished();
507 if (backgroundSpellCheck->speller().availableBackends().isEmpty()) {
510 i18n(
"No backend available for spell checking. Do you want to send the email anyways?"),
512 KGuiItem(
i18nc(
"@action:button",
"Send"), QStringLiteral(
"mail-send")),
514 if (answer == KMessageBox::ButtonCode::PrimaryAction) {
515 Q_EMIT spellCheckingFinished();
518 slotDisplayMessageIndicator(
i18n(
"No backend available for spell checking."));
520 delete backgroundSpellCheck;
523 if (!d->spellCheckingLanguage.isEmpty()) {
524 backgroundSpellCheck->changeLanguage(d->spellCheckingLanguage);
526 if (!d->ignoreSpellCheckingWords.isEmpty()) {
527 for (
const QString &word : std::as_const(d->ignoreSpellCheckingWords)) {
528 backgroundSpellCheck->speller().addToSession(word);
531 auto spellDialog =
new Sonnet::Dialog(backgroundSpellCheck, force ?
this :
nullptr);
541 qCWarning(TEXTCUSTOMEDITOR_LOG) <<
" Impossible to find qdialogbuttonbox";
543 backgroundSpellCheck->
setParent(spellDialog);
545 spellDialog->activeAutoCorrect(d->showAutoCorrectionButton);
546 connect(spellDialog, &Sonnet::Dialog::replace,
this, &RichTextEditor::slotSpellCheckerCorrected);
547 connect(spellDialog, &Sonnet::Dialog::misspelling,
this, &RichTextEditor::slotSpellCheckerMisspelling);
548 connect(spellDialog, &Sonnet::Dialog::autoCorrect,
this, &RichTextEditor::slotSpellCheckerAutoCorrect);
550 connect(spellDialog, &Sonnet::Dialog::cancel,
this, &RichTextEditor::slotSpellCheckerCanceled);
555 connect(spellDialog, &Sonnet::Dialog::cancel,
this, &RichTextEditor::spellCheckingCanceled);
562void RichTextEditor::slotCheckSpelling()
564 checkSpelling(
false);
567void RichTextEditor::forceSpellChecking()
572void RichTextEditor::slotSpellCheckerCanceled()
577 cursor.insertFragment(d->originalDoc);
578 slotSpellCheckerFinished();
581void RichTextEditor::slotSpellCheckerAutoCorrect(
const QString ¤tWord,
const QString &autoCorrectWord)
583 Q_EMIT spellCheckerAutoCorrect(currentWord, autoCorrectWord);
586void RichTextEditor::slotSpellCheckerMisspelling(
const QString &text,
int pos)
591void RichTextEditor::slotSpellCheckerCorrected(
const QString &oldWord,
int pos,
const QString &newWord)
593 if (oldWord != newWord) {
597 cursor.insertText(newWord);
601void RichTextEditor::slotSpellCheckerFinished()
611void RichTextEditor::highlightWord(
int length,
int pos)
620void RichTextEditor::createHighlighter()
624 setHighlighter(highlighter);
632void RichTextEditor::addIgnoreWordsToHighLighter()
634 if (d->ignoreSpellCheckingWords.isEmpty()) {
637 if (d->richTextDecorator) {
639 for (
const QString &word : std::as_const(d->ignoreSpellCheckingWords)) {
651 d->richTextDecorator = decorator;
652 addIgnoreWordsToHighLighter();
655void RichTextEditor::focusInEvent(
QFocusEvent *event)
657 if (d->checkSpellingEnabled && !
isReadOnly() && !d->richTextDecorator && spellCheckingSupport()) {
664void RichTextEditor::setSpellCheckingConfigFileName(
const QString &_fileName)
666 d->spellCheckingConfigFileName = _fileName;
668 if (config->hasGroup(
"Spelling"_L1)) {
670 d->checkSpellingEnabled = group.readEntry(
"checkerEnabledByDefault",
false);
671 d->spellCheckingLanguage = group.readEntry(
"Language",
QString());
673 setCheckSpellingEnabled(checkSpellingEnabled());
675 if (!d->spellCheckingLanguage.isEmpty() && highlighter()) {
681QString RichTextEditor::spellCheckingConfigFileName()
const
683 return d->spellCheckingConfigFileName;
686bool RichTextEditor::checkSpellingEnabled()
const
688 return d->checkSpellingEnabled;
691void RichTextEditor::setCheckSpellingEnabled(
bool check)
693 if (check == d->checkSpellingEnabled) {
696 d->checkSpellingEnabled = check;
697 Q_EMIT checkSpellingChanged(check);
704 if (!d->richTextDecorator) {
707 if (!d->spellCheckingLanguage.isEmpty()) {
708 setSpellCheckingLanguage(spellCheckingLanguage());
717void RichTextEditor::updateHighLighter()
721void RichTextEditor::clearDecorator()
723 delete d->richTextDecorator;
724 d->richTextDecorator =
nullptr;
727const QString &RichTextEditor::spellCheckingLanguage()
const
729 return d->spellCheckingLanguage;
732void RichTextEditor::setSpellCheckingLanguage(
const QString &_language)
738 if (_language != d->spellCheckingLanguage) {
739 d->spellCheckingLanguage = _language;
742 group.writeEntry(
"Language", d->spellCheckingLanguage);
744 Q_EMIT languageChanged(_language);
748void RichTextEditor::slotToggleAutoSpellCheck()
750 setCheckSpellingEnabled(!checkSpellingEnabled());
753 group.writeEntry(
"checkerEnabledByDefault", d->checkSpellingEnabled);
756void RichTextEditor::slotLanguageSelected()
759 setSpellCheckingLanguage(languageAction->
data().
toString());
769void RichTextEditor::deleteWordBack()
774void RichTextEditor::deleteWordForward()
779bool RichTextEditor::event(
QEvent *ev)
783 if (overrideShortcut(e)) {
788 regenerateColorScheme();
796 const int angleDeltaY{
event->angleDelta().y()};
797 if (angleDeltaY > 0) {
799 }
else if (angleDeltaY < 0) {
808bool RichTextEditor::handleShortcut(
QKeyEvent *event)
810 const int key =
event->key() |
event->modifiers();
929 cursor.removeSelectedText();
937bool RichTextEditor::overrideShortcut(
QKeyEvent *event)
939 const int key =
event->key() |
event->modifiers();
987void RichTextEditor::keyPressEvent(
QKeyEvent *event)
991 if (handleShortcut(event)) {
993 }
else if (
event->key() ==
Qt::Key_Up && isControlClicked && isShiftClicked) {
994 moveLineUpDown(
true);
997 moveLineUpDown(
false);
1000 moveCursorBeginUpDown(
true);
1003 moveCursorBeginUpDown(
false);
1010int RichTextEditor::zoomFactor()
const
1012 int pourcentage = 100;
1014 if (d->mInitialFontSize != f.
pointSize()) {
1015 pourcentage = (f.
pointSize() * 100) / d->mInitialFontSize;
1020void RichTextEditor::slotZoomReset()
1023 if (d->mInitialFontSize != f.
pointSize()) {
1029void RichTextEditor::moveCursorBeginUpDown(
bool moveUp)
1037 move.endEditBlock();
1041void RichTextEditor::moveLineUpDown(
bool moveUp)
1047 const bool hasSelection =
cursor.hasSelection();
1061 move.removeSelectedText();
1069 if (
move.atBlockStart()) {
1079 move.clearSelection();
1080 move.insertText(text);
1084 move.setPosition(end);
1089 move.endEditBlock();
1094#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)
QAction * end(const QObject *recvr, const char *slot, QObject *parent)
ButtonCode questionTwoActions(QWidget *parent, const QString &text, const QString &title, const KGuiItem &primaryAction, const KGuiItem &secondaryAction, const QString &dontAskAgainName=QString(), Options options=Notify)
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)
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