7#include "richtextcomposercontroler.h"
8#include "inserthtmldialog.h"
9#include "klinkdialog_p.h"
10#include "nestedlisthelper_p.h"
11#include "richtextcomposerimages.h"
12#include <QApplication>
13#include <QRegularExpression>
15#include "insertimagedialog.h"
17#include <KColorScheme>
18#include <KLocalizedString>
21#include <QColorDialog>
24#include <QRegularExpression>
26#include <QTextDocumentFragment>
31using namespace std::chrono_literals;
33using namespace KPIMTextEdit;
38 RichTextComposerControllerPrivate(RichTextComposer *composer, RichTextComposerControler *qq)
39 : richtextComposer(composer)
42 nestedListHelper =
new NestedListHelper(composer);
43 richTextImages =
new RichTextComposerImages(richtextComposer, q);
46 ~RichTextComposerControllerPrivate()
48 delete nestedListHelper;
51 void regenerateColorScheme()
59 if (mLinkColor.isValid()) {
62 regenerateColorScheme();
66 void selectLinkText(QTextCursor *cursor)
const;
67 void fixupTextEditString(QString &text)
const;
68 void mergeFormatOnWordOrSelection(
const QTextCharFormat &format);
69 [[nodiscard]] QString addQuotesToText(
const QString &inputText,
const QString &defaultQuoteSign);
70 void updateLink(
const QString &linkUrl,
const QString &linkText);
73 QTextCharFormat painterFormat;
74 NestedListHelper *nestedListHelper =
nullptr;
75 RichTextComposer *richtextComposer =
nullptr;
76 RichTextComposerImages *richTextImages =
nullptr;
77 RichTextComposerControler *
const q;
78 bool painterActive =
false;
81void RichTextComposerControler::RichTextComposerControllerPrivate::selectLinkText(
QTextCursor *cursor)
const
100 if (cursor->
atEnd()) {
103 const int oldPosition = cursor->
position();
107 if (oldPosition == cursor->
position()) {
123void RichTextComposerControler::RichTextComposerControllerPrivate::mergeFormatOnWordOrSelection(
const QTextCharFormat &format)
125 QTextCursor cursor = richtextComposer->textCursor();
126 QTextCursor wordStart(cursor);
127 QTextCursor wordEnd(cursor);
137 richtextComposer->mergeCurrentCharFormat(format);
141RichTextComposerControler::RichTextComposerControler(RichTextComposer *richtextComposer,
QObject *
parent)
143 , d(new RichTextComposerControllerPrivate(richtextComposer, this))
147RichTextComposerControler::~RichTextComposerControler() =
default;
149void RichTextComposerControler::regenerateColorScheme()
151 d->regenerateColorScheme();
154bool RichTextComposerControler::painterActive()
const
156 return d->painterActive;
159void RichTextComposerControler::addCheckbox(
bool add)
161 QTextBlockFormat fmt;
162 fmt.
setMarker(add ? QTextBlockFormat::MarkerType::Unchecked : QTextBlockFormat::MarkerType::NoMarker);
163 QTextCursor cursor = richTextComposer()->textCursor();
179void RichTextComposerControler::setFontForWholeText(
const QFont &font)
183 QTextCursor cursor(richTextComposer()->document());
186 richTextComposer()->document()->setDefaultFont(font);
189void RichTextComposerControler::disablePainter()
193 if (richTextComposer()->textCursor().hasSelection()) {
194 QTextCursor cursor = richTextComposer()->textCursor();
196 richTextComposer()->setTextCursor(cursor);
198 d->painterActive =
false;
201RichTextComposerImages *RichTextComposerControler::composerImages()
const
203 return d->richTextImages;
206NestedListHelper *RichTextComposerControler::nestedListHelper()
const
208 return d->nestedListHelper;
211void RichTextComposerControler::ensureCursorVisibleDelayed()
213 d->richtextComposer->ensureCursorVisible();
218 return d->richtextComposer;
221void RichTextComposerControler::insertHorizontalRule()
223 QTextCursor cursor = richTextComposer()->textCursor();
231 richTextComposer()->setTextCursor(cursor);
232 richTextComposer()->activateRichText();
235void RichTextComposerControler::alignLeft()
238 richTextComposer()->setFocus();
239 richTextComposer()->activateRichText();
242void RichTextComposerControler::alignCenter()
245 richTextComposer()->setFocus();
246 richTextComposer()->activateRichText();
249void RichTextComposerControler::alignRight()
252 richTextComposer()->setFocus();
253 richTextComposer()->activateRichText();
256void RichTextComposerControler::alignJustify()
259 richTextComposer()->setFocus();
260 richTextComposer()->activateRichText();
263void RichTextComposerControler::makeRightToLeft()
265 QTextBlockFormat format;
267 QTextCursor cursor = richTextComposer()->textCursor();
269 richTextComposer()->setTextCursor(cursor);
270 richTextComposer()->setFocus();
271 richTextComposer()->activateRichText();
274void RichTextComposerControler::makeLeftToRight()
276 QTextBlockFormat format;
278 QTextCursor cursor = richTextComposer()->textCursor();
280 richTextComposer()->setTextCursor(cursor);
281 richTextComposer()->setFocus();
282 richTextComposer()->activateRichText();
285void RichTextComposerControler::setTextBold(
bool bold)
289 d->mergeFormatOnWordOrSelection(fmt);
290 richTextComposer()->setFocus();
291 richTextComposer()->activateRichText();
294void RichTextComposerControler::setTextItalic(
bool italic)
298 d->mergeFormatOnWordOrSelection(fmt);
299 richTextComposer()->setFocus();
300 richTextComposer()->activateRichText();
303void RichTextComposerControler::setTextUnderline(
bool underline)
307 d->mergeFormatOnWordOrSelection(fmt);
308 richTextComposer()->setFocus();
309 richTextComposer()->activateRichText();
312void RichTextComposerControler::setTextStrikeOut(
bool strikeOut)
316 d->mergeFormatOnWordOrSelection(fmt);
317 richTextComposer()->setFocus();
318 richTextComposer()->activateRichText();
321void RichTextComposerControler::setTextForegroundColor(
const QColor &color)
325 d->mergeFormatOnWordOrSelection(fmt);
326 richTextComposer()->setFocus();
327 richTextComposer()->activateRichText();
330void RichTextComposerControler::setTextBackgroundColor(
const QColor &color)
334 d->mergeFormatOnWordOrSelection(fmt);
335 richTextComposer()->setFocus();
336 richTextComposer()->activateRichText();
339void RichTextComposerControler::setFontFamily(
const QString &fontFamily)
343 d->mergeFormatOnWordOrSelection(fmt);
344 richTextComposer()->setFocus();
345 richTextComposer()->activateRichText();
348void RichTextComposerControler::setFontSize(
int size)
352 d->mergeFormatOnWordOrSelection(fmt);
353 richTextComposer()->setFocus();
354 richTextComposer()->activateRichText();
357void RichTextComposerControler::setFont(
const QFont &font)
361 d->mergeFormatOnWordOrSelection(fmt);
362 richTextComposer()->setFocus();
363 richTextComposer()->activateRichText();
366void RichTextComposerControler::setTextSuperScript(
bool superscript)
370 d->mergeFormatOnWordOrSelection(fmt);
371 richTextComposer()->setFocus();
372 richTextComposer()->activateRichText();
375void RichTextComposerControler::setTextSubScript(
bool subscript)
379 d->mergeFormatOnWordOrSelection(fmt);
380 richTextComposer()->setFocus();
381 richTextComposer()->activateRichText();
384void RichTextComposerControler::setHeadingLevel(
int level)
386 const int boundedLevel = qBound(0, 6, level);
389 const int sizeAdjustment = boundedLevel > 0 ? 5 - boundedLevel : 0;
391 QTextCursor cursor = richTextComposer()->textCursor();
394 QTextBlockFormat blkfmt;
398 QTextCharFormat chrfmt;
402 QTextCursor selectCursor = cursor;
404 QTextCursor top = selectCursor;
408 QTextCursor bottom = selectCursor;
421 richTextComposer()->setTextCursor(cursor);
422 richTextComposer()->setFocus();
423 richTextComposer()->activateRichText();
426void RichTextComposerControler::setChangeTextForegroundColor()
428 const QColor currentColor = richTextComposer()->textColor();
434 setTextForegroundColor(defaultColor);
435 }
else if (selectedColor.
isValid()) {
436 setTextForegroundColor(selectedColor);
440void RichTextComposerControler::setChangeTextBackgroundColor()
442 QTextCharFormat fmt = richTextComposer()->textCursor().charFormat();
449 setTextBackgroundColor(defaultColor);
450 }
else if (selectedColor.
isValid()) {
451 setTextBackgroundColor(selectedColor);
455QString RichTextComposerControler::currentLinkUrl()
const
457 return richTextComposer()->textCursor().charFormat().anchorHref();
460QString RichTextComposerControler::currentLinkText()
const
462 QTextCursor cursor = richTextComposer()->textCursor();
463 d->selectLinkText(&cursor);
467void RichTextComposerControler::selectLinkText()
const
469 QTextCursor cursor = richTextComposer()->textCursor();
470 d->selectLinkText(&cursor);
471 richTextComposer()->setTextCursor(cursor);
474void RichTextComposerControler::manageLink()
477 QPointer<KLinkDialog> linkDialog =
new KLinkDialog(richTextComposer());
478 linkDialog->setLinkText(currentLinkText());
479 linkDialog->setLinkUrl(currentLinkUrl());
481 if (linkDialog->exec()) {
482 d->updateLink(linkDialog->linkUrl(), linkDialog->linkText());
488void RichTextComposerControler::updateLink(
const QString &linkUrl,
const QString &linkText)
490 d->updateLink(linkUrl, linkText);
493void RichTextComposerControler::RichTextComposerControllerPrivate::updateLink(
const QString &linkUrl,
const QString &linkText)
497 QTextCursor cursor = richtextComposer->textCursor();
517 richtextComposer->activateRichText();
525 QTextDocument defaultTextDocument;
526 QTextCharFormat defaultCharFormat = defaultTextDocument.
begin().
charFormat();
536 _linkText = linkText;
545QString RichTextComposerControler::toCleanHtml()
const
547 QString result = richTextComposer()->toHtml();
549 static const QString EMPTYLINEHTML = QStringLiteral(
550 "<p style=\"-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; "
551 "margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; \"> </p>");
555 static const QRegularExpression EMPTYLINEREGEX(QStringLiteral(
"<p style=\"-qt-paragraph-type:empty;(.*?)</p>"));
557 static const QString OLLISTPATTERNQT = QStringLiteral(
"<ol style=\"margin-top: 0px; margin-bottom: 0px; margin-left: 0px;");
559 static const QString ULLISTPATTERNQT = QStringLiteral(
"<ul style=\"margin-top: 0px; margin-bottom: 0px; margin-left: 0px;");
561 static const QString ORDEREDLISTHTML = QStringLiteral(
"<ol style=\"margin-top: 0px; margin-bottom: 0px;");
563 static const QString UNORDEREDLISTHTML = QStringLiteral(
"<ul style=\"margin-top: 0px; margin-bottom: 0px;");
571 result.
replace(EMPTYLINEREGEX, EMPTYLINEHTML);
575 result.
replace(OLLISTPATTERNQT, ORDEREDLISTHTML);
579 result.
replace(ULLISTPATTERNQT, UNORDEREDLISTHTML);
584bool RichTextComposerControler::canIndentList()
const
586 return d->nestedListHelper->canIndent();
589bool RichTextComposerControler::canDedentList()
const
591 return d->nestedListHelper->canDedent();
594void RichTextComposerControler::indentListMore()
596 d->nestedListHelper->handleOnIndentMore();
597 richTextComposer()->activateRichText();
600void RichTextComposerControler::indentListLess()
602 d->nestedListHelper->handleOnIndentLess();
605void RichTextComposerControler::setListStyle(
int styleIndex)
607 d->nestedListHelper->handleOnBulletType(-styleIndex);
608 richTextComposer()->setFocus();
609 richTextComposer()->activateRichText();
612void RichTextComposerControler::insertLink(
const QString &url)
618 QTextCursor cursor = richTextComposer()->textCursor();
624 const QTextCharFormat originalFormat = format;
642 richTextComposer()->textCursor().insertText(url + QLatin1Char(
'\n'));
646void RichTextComposerControler::setCursorPositionFromStart(
unsigned int pos)
649 QTextCursor cursor = richTextComposer()->textCursor();
652 richTextComposer()->setTextCursor(cursor);
653 ensureCursorVisible();
657void RichTextComposerControler::ensureCursorVisible()
666 QTimer::singleShot(500ms, richTextComposer()->composerControler(), &RichTextComposerControler::ensureCursorVisibleDelayed);
669void RichTextComposerControler::RichTextComposerControllerPrivate::fixupTextEditString(QString &text)
const
676 text.
remove(QChar(0xFFFC));
682bool RichTextComposerControler::isFormattingUsed()
const
691void RichTextComposerControler::slotAddEmoticon(
const QString &text)
693 QTextCursor cursor = richTextComposer()->textCursor();
697void RichTextComposerControler::slotInsertHtml()
700 QPointer<KPIMTextEdit::InsertHtmlDialog> dialog =
new KPIMTextEdit::InsertHtmlDialog(richTextComposer());
701 const QTextDocumentFragment fragmentSelected = richTextComposer()->textCursor().selection();
702 if (!fragmentSelected.
isEmpty()) {
703 dialog->setSelectedText(fragmentSelected.
toHtml());
705 if (dialog->exec()) {
706 const QString str = dialog->html();
708 QTextCursor cursor = richTextComposer()->textCursor();
716void RichTextComposerControler::slotAddImage()
718 QPointer<KPIMTextEdit::InsertImageDialog> dlg =
new KPIMTextEdit::InsertImageDialog(richTextComposer());
720 const QUrl url = dlg->imageUrl();
722 int imageHeight = -1;
723 if (!dlg->keepOriginalSize()) {
724 imageWidth = dlg->imageWidth();
725 imageHeight = dlg->imageHeight();
728 d->richTextImages->addImage(url, imageWidth, imageHeight);
736void RichTextComposerControler::slotFormatReset()
738 setTextBackgroundColor(richTextComposer()->palette().highlightedText().color());
739 setTextForegroundColor(richTextComposer()->palette().text().color());
740 richTextComposer()->setFont(d->saveFont);
743void RichTextComposerControler::slotPasteAsQuotation()
745#ifndef QT_NO_CLIPBOARD
746 if (richTextComposer()->hasFocus()) {
749 richTextComposer()->insertPlainText(d->addQuotesToText(s, d->richtextComposer->defaultQuoteSign()));
755void RichTextComposerControler::slotPasteWithoutFormatting()
757#ifndef QT_NO_CLIPBOARD
758 if (richTextComposer()->hasFocus()) {
761 richTextComposer()->insertPlainText(s);
767void RichTextComposerControler::slotRemoveQuotes()
769 QTextCursor cursor = richTextComposer()->textCursor();
775 QTextBlock block = richTextComposer()->document()->findBlock(cursor.
selectionStart());
779 const int length = richTextComposer()->quoteLength(block.
text(),
true);
783 selectionEnd -= length;
785 block = block.
next();
791void RichTextComposerControler::slotAddQuotes()
793 addQuotes(d->richtextComposer->defaultQuoteSign());
796void RichTextComposerControler::addQuotes(
const QString &defaultQuote)
798 QTextCursor cursor = richTextComposer()->textCursor();
800 QString selectedText;
801 bool lastCharacterIsAParagraphChar =
false;
809 lastCharacterIsAParagraphChar =
true;
812 QString text = d->addQuotesToText(selectedText, defaultQuote);
813 if (lastCharacterIsAParagraphChar) {
816 richTextComposer()->insertPlainText(text);
821QString RichTextComposerControler::RichTextComposerControllerPrivate::addQuotesToText(
const QString &inputText,
const QString &defaultQuoteSign)
823 QString answer = inputText;
824 answer.
replace(QLatin1Char(
'\n'), QLatin1Char(
'\n') + defaultQuoteSign);
827 answer.
prepend(defaultQuoteSign);
828 answer += QLatin1Char(
'\n');
829 return richtextComposer->smartQuote(answer);
832void RichTextComposerControler::slotFormatPainter(
bool active)
835 d->painterFormat = richTextComposer()->currentCharFormat();
836 d->painterActive =
true;
837 richTextComposer()->viewport()->setCursor(QCursor(
QIcon::fromTheme(QStringLiteral(
"draw-brush")).pixmap(32, 32), 0, 32));
839 d->painterFormat = QTextCharFormat();
840 d->painterActive =
false;
848 d->saveFont = richTextComposer()->currentFont();
852QString RichTextComposerControler::toCleanPlainText(
const QString &plainText)
const
854 QString temp = plainText.
isEmpty() ? richTextComposer()->toPlainText() : plainText;
855 d->fixupTextEditString(temp);
859QString RichTextComposerControler::toWrappedPlainText()
const
861 QTextDocument *doc = richTextComposer()->document();
862 return toWrappedPlainText(doc);
865QString RichTextComposerControler::toWrappedPlainText(QTextDocument *doc)
const
868 static const QRegularExpression rx(QStringLiteral(
"(http|ftp|ldap)s?\\S+-$"));
869 QTextBlock block = doc->
begin();
871 QTextLayout *layout = block.
layout();
872 const int numberOfLine(layout->
lineCount());
873 bool urlStart =
false;
874 for (
int i = 0; i < numberOfLine; ++i) {
875 const QTextLine line = layout->
lineAt(i);
878 if (lineText.
contains(rx) || (urlStart && !lineText.
contains(QLatin1Char(
' ')) && lineText.
endsWith(QLatin1Char(
'-')))) {
883 temp += lineText + QLatin1Char(
'\n');
886 block = block.
next();
890 if (temp.
endsWith(QLatin1Char(
'\n'))) {
893 d->fixupTextEditString(temp);
897bool RichTextComposerControler::event(QEvent *ev)
900 regenerateColorScheme();
906#include "moc_richtextcomposercontroler.cpp"
The RichTextComposerControler class.
The RichTextComposer class.
QString i18n(const char *text, const TYPE &arg...)
void error(QWidget *parent, const QString &text, const QString &title, const KGuiItem &buttonOk, Options options=Notify)
KPIMTEXTEDIT_EXPORT bool containsFormatting(const QTextDocument *document)
Returns whether the QTextDocument document contains rich text formatting.
const QColor & color() const const
QString text(Mode mode) const const
bool isValid() const const
QColor getColor(const QColor &initial, QWidget *parent, const QString &title, ColorDialogOptions options)
QIcon fromTheme(const QString &name)
virtual bool event(QEvent *e)
QObject * parent() const const
bool contains(QChar ch, Qt::CaseSensitivity cs) const const
bool endsWith(QChar c, Qt::CaseSensitivity cs) const const
bool isEmpty() const const
qsizetype length() const const
QString mid(qsizetype position, qsizetype n) const const
QString & prepend(QChar ch)
QString & remove(QChar ch, Qt::CaseSensitivity cs)
QString & replace(QChar before, QChar after, Qt::CaseSensitivity cs)
QTextCharFormat charFormat() const const
bool isValid() const const
QTextLayout * layout() const const
QTextBlock next() const const
int position() const const
QString text() const const
void setHeadingLevel(int level)
void setMarker(MarkerType marker)
QString anchorHref() const const
bool isAnchor() const const
void setAnchor(bool anchor)
void setAnchorHref(const QString &value)
void setFont(const QFont &font, FontPropertiesInheritanceBehavior behavior)
void setFontFamilies(const QStringList &families)
void setFontItalic(bool italic)
void setFontPointSize(qreal size)
void setFontStrikeOut(bool strikeOut)
void setFontUnderline(bool underline)
void setFontWeight(int weight)
void setUnderlineColor(const QColor &color)
void setUnderlineStyle(UnderlineStyle style)
void setVerticalAlignment(VerticalAlignment alignment)
QColor underlineColor() const const
UnderlineStyle underlineStyle() const const
bool atStart() const const
QTextBlockFormat blockFormat() const const
QTextCharFormat charFormat() const const
QTextList * currentList() const const
QTextDocument * document() const const
bool hasSelection() const const
void insertHtml(const QString &html)
void insertText(const QString &text)
void mergeBlockCharFormat(const QTextCharFormat &modifier)
void mergeBlockFormat(const QTextBlockFormat &modifier)
void mergeCharFormat(const QTextCharFormat &modifier)
bool movePosition(MoveOperation operation, MoveMode mode, int n)
int position() const const
void removeSelectedText()
void select(SelectionType selection)
QString selectedText() const const
int selectionEnd() const const
int selectionStart() const const
void setCharFormat(const QTextCharFormat &format)
void setPosition(int pos, MoveMode m)
QTextBlock begin() const const
int characterCount() const const
bool isEmpty() const const
QString toHtml() const const
QBrush background() const const
QBrush foreground() const const
void setBackground(const QBrush &brush)
void setForeground(const QBrush &brush)
void setLayoutDirection(Qt::LayoutDirection direction)
void setProperty(int propertyId, const QList< QTextLength > &value)
QTextLine lineAt(int i) const const
int lineCount() const const
int textLength() const const
int textStart() const const
bool isLocalFile() const const