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 <KLocalizedString>
20#include <QColorDialog>
23#include <QRegularExpression>
25#include <QTextDocumentFragment>
30using namespace std::chrono_literals;
32using namespace KPIMTextEdit;
37 RichTextComposerControllerPrivate(RichTextComposer *composer, RichTextComposerControler *qq)
38 : richtextComposer(composer)
41 nestedListHelper =
new NestedListHelper(composer);
42 richTextImages =
new RichTextComposerImages(richtextComposer, q);
45 ~RichTextComposerControllerPrivate()
47 delete nestedListHelper;
52 return richtextComposer->palette().link().color();
55 void selectLinkText(QTextCursor *cursor)
const;
56 void fixupTextEditString(QString &text)
const;
57 void mergeFormatOnWordOrSelection(
const QTextCharFormat &format);
58 [[nodiscard]] QString addQuotesToText(
const QString &inputText,
const QString &defaultQuoteSign);
59 void updateLink(
const QString &linkUrl,
const QString &linkText);
62 QTextCharFormat painterFormat;
63 NestedListHelper *nestedListHelper =
nullptr;
64 RichTextComposer *richtextComposer =
nullptr;
65 RichTextComposerImages *richTextImages =
nullptr;
66 RichTextComposerControler *
const q;
67 bool painterActive =
false;
70void RichTextComposerControler::RichTextComposerControllerPrivate::selectLinkText(
QTextCursor *cursor)
const
89 if (cursor->
atEnd()) {
92 const int oldPosition = cursor->
position();
96 if (oldPosition == cursor->
position()) {
112void RichTextComposerControler::RichTextComposerControllerPrivate::mergeFormatOnWordOrSelection(
const QTextCharFormat &format)
114 QTextCursor cursor = richtextComposer->textCursor();
115 QTextCursor wordStart(cursor);
116 QTextCursor wordEnd(cursor);
126 richtextComposer->mergeCurrentCharFormat(format);
130RichTextComposerControler::RichTextComposerControler(RichTextComposer *richtextComposer,
QObject *
parent)
132 , d(new RichTextComposerControllerPrivate(richtextComposer, this))
136RichTextComposerControler::~RichTextComposerControler() =
default;
138bool RichTextComposerControler::painterActive()
const
140 return d->painterActive;
143void RichTextComposerControler::addCheckbox(
bool add)
145 QTextBlockFormat fmt;
146 fmt.
setMarker(add ? QTextBlockFormat::MarkerType::Unchecked : QTextBlockFormat::MarkerType::NoMarker);
147 QTextCursor cursor = richTextComposer()->textCursor();
163void RichTextComposerControler::setFontForWholeText(
const QFont &font)
167 QTextCursor cursor(richTextComposer()->document());
170 richTextComposer()->document()->setDefaultFont(font);
173void RichTextComposerControler::disablePainter()
177 if (richTextComposer()->textCursor().hasSelection()) {
178 QTextCursor cursor = richTextComposer()->textCursor();
180 richTextComposer()->setTextCursor(cursor);
182 d->painterActive =
false;
185RichTextComposerImages *RichTextComposerControler::composerImages()
const
187 return d->richTextImages;
190NestedListHelper *RichTextComposerControler::nestedListHelper()
const
192 return d->nestedListHelper;
195void RichTextComposerControler::ensureCursorVisibleDelayed()
197 d->richtextComposer->ensureCursorVisible();
202 return d->richtextComposer;
205void RichTextComposerControler::insertHorizontalRule()
207 QTextCursor cursor = richTextComposer()->textCursor();
215 richTextComposer()->setTextCursor(cursor);
216 richTextComposer()->activateRichText();
219void RichTextComposerControler::alignLeft()
222 richTextComposer()->setFocus();
223 richTextComposer()->activateRichText();
226void RichTextComposerControler::alignCenter()
229 richTextComposer()->setFocus();
230 richTextComposer()->activateRichText();
233void RichTextComposerControler::alignRight()
236 richTextComposer()->setFocus();
237 richTextComposer()->activateRichText();
240void RichTextComposerControler::alignJustify()
243 richTextComposer()->setFocus();
244 richTextComposer()->activateRichText();
247void RichTextComposerControler::makeRightToLeft()
249 QTextBlockFormat format;
251 QTextCursor cursor = richTextComposer()->textCursor();
253 richTextComposer()->setTextCursor(cursor);
254 richTextComposer()->setFocus();
255 richTextComposer()->activateRichText();
258void RichTextComposerControler::makeLeftToRight()
260 QTextBlockFormat format;
262 QTextCursor cursor = richTextComposer()->textCursor();
264 richTextComposer()->setTextCursor(cursor);
265 richTextComposer()->setFocus();
266 richTextComposer()->activateRichText();
269void RichTextComposerControler::setTextBold(
bool bold)
273 d->mergeFormatOnWordOrSelection(fmt);
274 richTextComposer()->setFocus();
275 richTextComposer()->activateRichText();
278void RichTextComposerControler::setTextItalic(
bool italic)
282 d->mergeFormatOnWordOrSelection(fmt);
283 richTextComposer()->setFocus();
284 richTextComposer()->activateRichText();
287void RichTextComposerControler::setTextUnderline(
bool underline)
291 d->mergeFormatOnWordOrSelection(fmt);
292 richTextComposer()->setFocus();
293 richTextComposer()->activateRichText();
296void RichTextComposerControler::setTextStrikeOut(
bool strikeOut)
300 d->mergeFormatOnWordOrSelection(fmt);
301 richTextComposer()->setFocus();
302 richTextComposer()->activateRichText();
305void RichTextComposerControler::setTextForegroundColor(
const QColor &color)
309 d->mergeFormatOnWordOrSelection(fmt);
310 richTextComposer()->setFocus();
311 richTextComposer()->activateRichText();
314void RichTextComposerControler::setTextBackgroundColor(
const QColor &color)
318 d->mergeFormatOnWordOrSelection(fmt);
319 richTextComposer()->setFocus();
320 richTextComposer()->activateRichText();
323void RichTextComposerControler::setFontFamily(
const QString &fontFamily)
327 d->mergeFormatOnWordOrSelection(fmt);
328 richTextComposer()->setFocus();
329 richTextComposer()->activateRichText();
332void RichTextComposerControler::setFontSize(
int size)
336 d->mergeFormatOnWordOrSelection(fmt);
337 richTextComposer()->setFocus();
338 richTextComposer()->activateRichText();
341void RichTextComposerControler::setFont(
const QFont &font)
345 d->mergeFormatOnWordOrSelection(fmt);
346 richTextComposer()->setFocus();
347 richTextComposer()->activateRichText();
350void RichTextComposerControler::setTextSuperScript(
bool superscript)
354 d->mergeFormatOnWordOrSelection(fmt);
355 richTextComposer()->setFocus();
356 richTextComposer()->activateRichText();
359void RichTextComposerControler::setTextSubScript(
bool subscript)
363 d->mergeFormatOnWordOrSelection(fmt);
364 richTextComposer()->setFocus();
365 richTextComposer()->activateRichText();
368void RichTextComposerControler::setHeadingLevel(
int level)
370 const int boundedLevel = qBound(0, 6, level);
373 const int sizeAdjustment = boundedLevel > 0 ? 5 - boundedLevel : 0;
375 QTextCursor cursor = richTextComposer()->textCursor();
378 QTextBlockFormat blkfmt;
382 QTextCharFormat chrfmt;
386 QTextCursor selectCursor = cursor;
388 QTextCursor top = selectCursor;
392 QTextCursor bottom = selectCursor;
405 richTextComposer()->setTextCursor(cursor);
406 richTextComposer()->setFocus();
407 richTextComposer()->activateRichText();
410void RichTextComposerControler::setChangeTextForegroundColor()
412 const QColor currentColor = richTextComposer()->textColor();
413 const QColor defaultColor = richTextComposer()->palette().text().color();
418 setTextForegroundColor(defaultColor);
419 }
else if (selectedColor.
isValid()) {
420 setTextForegroundColor(selectedColor);
424void RichTextComposerControler::setChangeTextBackgroundColor()
426 QTextCharFormat fmt = richTextComposer()->textCursor().charFormat();
428 const QColor defaultColor = richTextComposer()->palette().base().color();
433 setTextBackgroundColor(defaultColor);
434 }
else if (selectedColor.
isValid()) {
435 setTextBackgroundColor(selectedColor);
439QString RichTextComposerControler::currentLinkUrl()
const
441 return richTextComposer()->textCursor().charFormat().anchorHref();
444QString RichTextComposerControler::currentLinkText()
const
446 QTextCursor cursor = richTextComposer()->textCursor();
447 d->selectLinkText(&cursor);
451void RichTextComposerControler::selectLinkText()
const
453 QTextCursor cursor = richTextComposer()->textCursor();
454 d->selectLinkText(&cursor);
455 richTextComposer()->setTextCursor(cursor);
458void RichTextComposerControler::manageLink()
461 QPointer<KLinkDialog> linkDialog =
new KLinkDialog(richTextComposer());
462 linkDialog->setLinkText(currentLinkText());
463 linkDialog->setLinkUrl(currentLinkUrl());
465 if (linkDialog->exec()) {
466 d->updateLink(linkDialog->linkUrl(), linkDialog->linkText());
472void RichTextComposerControler::updateLink(
const QString &linkUrl,
const QString &linkText)
474 d->updateLink(linkUrl, linkText);
477void RichTextComposerControler::RichTextComposerControllerPrivate::updateLink(
const QString &linkUrl,
const QString &linkText)
481 QTextCursor cursor = richtextComposer->textCursor();
501 richtextComposer->activateRichText();
509 QTextDocument defaultTextDocument;
510 QTextCharFormat defaultCharFormat = defaultTextDocument.
begin().
charFormat();
520 _linkText = linkText;
529QString RichTextComposerControler::toCleanHtml()
const
531 QString result = richTextComposer()->toHtml();
533 static const QString EMPTYLINEHTML = QStringLiteral(
534 "<p style=\"-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; "
535 "margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; \"> </p>");
539 static const QRegularExpression EMPTYLINEREGEX(QStringLiteral(
"<p style=\"-qt-paragraph-type:empty;(.*?)</p>"));
541 static const QString OLLISTPATTERNQT = QStringLiteral(
"<ol style=\"margin-top: 0px; margin-bottom: 0px; margin-left: 0px;");
543 static const QString ULLISTPATTERNQT = QStringLiteral(
"<ul style=\"margin-top: 0px; margin-bottom: 0px; margin-left: 0px;");
545 static const QString ORDEREDLISTHTML = QStringLiteral(
"<ol style=\"margin-top: 0px; margin-bottom: 0px;");
547 static const QString UNORDEREDLISTHTML = QStringLiteral(
"<ul style=\"margin-top: 0px; margin-bottom: 0px;");
555 result.
replace(EMPTYLINEREGEX, EMPTYLINEHTML);
559 result.
replace(OLLISTPATTERNQT, ORDEREDLISTHTML);
563 result.
replace(ULLISTPATTERNQT, UNORDEREDLISTHTML);
568bool RichTextComposerControler::canIndentList()
const
570 return d->nestedListHelper->canIndent();
573bool RichTextComposerControler::canDedentList()
const
575 return d->nestedListHelper->canDedent();
578void RichTextComposerControler::indentListMore()
580 d->nestedListHelper->handleOnIndentMore();
581 richTextComposer()->activateRichText();
584void RichTextComposerControler::indentListLess()
586 d->nestedListHelper->handleOnIndentLess();
589void RichTextComposerControler::setListStyle(
int styleIndex)
591 d->nestedListHelper->handleOnBulletType(-styleIndex);
592 richTextComposer()->setFocus();
593 richTextComposer()->activateRichText();
596void RichTextComposerControler::insertLink(
const QString &url)
602 QTextCursor cursor = richTextComposer()->textCursor();
608 const QTextCharFormat originalFormat = format;
626 richTextComposer()->textCursor().insertText(url + QLatin1Char(
'\n'));
630void RichTextComposerControler::setCursorPositionFromStart(
unsigned int pos)
633 QTextCursor cursor = richTextComposer()->textCursor();
636 richTextComposer()->setTextCursor(cursor);
637 ensureCursorVisible();
641void RichTextComposerControler::ensureCursorVisible()
650 QTimer::singleShot(500ms, richTextComposer()->composerControler(), &RichTextComposerControler::ensureCursorVisibleDelayed);
653void RichTextComposerControler::RichTextComposerControllerPrivate::fixupTextEditString(QString &text)
const
660 text.
remove(QChar(0xFFFC));
666bool RichTextComposerControler::isFormattingUsed()
const
675void RichTextComposerControler::slotAddEmoticon(
const QString &text)
677 QTextCursor cursor = richTextComposer()->textCursor();
681void RichTextComposerControler::slotInsertHtml()
684 QPointer<KPIMTextEdit::InsertHtmlDialog> dialog =
new KPIMTextEdit::InsertHtmlDialog(richTextComposer());
685 const QTextDocumentFragment fragmentSelected = richTextComposer()->textCursor().selection();
686 if (!fragmentSelected.
isEmpty()) {
687 dialog->setSelectedText(fragmentSelected.
toHtml());
689 if (dialog->exec()) {
690 const QString str = dialog->html();
692 QTextCursor cursor = richTextComposer()->textCursor();
700void RichTextComposerControler::slotAddImage()
702 QPointer<KPIMTextEdit::InsertImageDialog> dlg =
new KPIMTextEdit::InsertImageDialog(richTextComposer());
704 const QUrl url = dlg->imageUrl();
706 int imageHeight = -1;
707 if (!dlg->keepOriginalSize()) {
708 imageWidth = dlg->imageWidth();
709 imageHeight = dlg->imageHeight();
712 d->richTextImages->addImage(url, imageWidth, imageHeight);
720void RichTextComposerControler::slotFormatReset()
722 setTextBackgroundColor(richTextComposer()->palette().highlightedText().color());
723 setTextForegroundColor(richTextComposer()->palette().text().color());
724 richTextComposer()->setFont(d->saveFont);
727void RichTextComposerControler::slotPasteAsQuotation()
729#ifndef QT_NO_CLIPBOARD
730 if (richTextComposer()->hasFocus()) {
733 richTextComposer()->insertPlainText(d->addQuotesToText(s, d->richtextComposer->defaultQuoteSign()));
739void RichTextComposerControler::slotPasteWithoutFormatting()
741#ifndef QT_NO_CLIPBOARD
742 if (richTextComposer()->hasFocus()) {
745 richTextComposer()->insertPlainText(s);
751void RichTextComposerControler::slotRemoveQuotes()
753 QTextCursor cursor = richTextComposer()->textCursor();
759 QTextBlock block = richTextComposer()->document()->findBlock(cursor.
selectionStart());
763 const int length = richTextComposer()->quoteLength(block.
text(),
true);
767 selectionEnd -= length;
769 block = block.
next();
775void RichTextComposerControler::slotAddQuotes()
777 addQuotes(d->richtextComposer->defaultQuoteSign());
780void RichTextComposerControler::addQuotes(
const QString &defaultQuote)
782 QTextCursor cursor = richTextComposer()->textCursor();
784 QString selectedText;
785 bool lastCharacterIsAParagraphChar =
false;
793 lastCharacterIsAParagraphChar =
true;
796 QString text = d->addQuotesToText(selectedText, defaultQuote);
797 if (lastCharacterIsAParagraphChar) {
800 richTextComposer()->insertPlainText(text);
805QString RichTextComposerControler::RichTextComposerControllerPrivate::addQuotesToText(
const QString &inputText,
const QString &defaultQuoteSign)
807 QString answer = inputText;
808 answer.
replace(QLatin1Char(
'\n'), QLatin1Char(
'\n') + defaultQuoteSign);
811 answer.
prepend(defaultQuoteSign);
812 answer += QLatin1Char(
'\n');
813 return richtextComposer->smartQuote(answer);
816void RichTextComposerControler::slotFormatPainter(
bool active)
819 d->painterFormat = richTextComposer()->currentCharFormat();
820 d->painterActive =
true;
821 richTextComposer()->viewport()->setCursor(QCursor(
QIcon::fromTheme(QStringLiteral(
"draw-brush")).pixmap(32, 32), 0, 32));
823 d->painterFormat = QTextCharFormat();
824 d->painterActive =
false;
832 d->saveFont = richTextComposer()->currentFont();
836QString RichTextComposerControler::toCleanPlainText(
const QString &plainText)
const
838 QString temp = plainText.
isEmpty() ? richTextComposer()->toPlainText() : plainText;
839 d->fixupTextEditString(temp);
843QString RichTextComposerControler::toWrappedPlainText()
const
845 QTextDocument *doc = richTextComposer()->document();
846 return toWrappedPlainText(doc);
849QString RichTextComposerControler::toWrappedPlainText(QTextDocument *doc)
const
852 static const QRegularExpression rx(QStringLiteral(
"(http|ftp|ldap)s?\\S+-$"));
853 QTextBlock block = doc->
begin();
855 QTextLayout *layout = block.
layout();
856 const int numberOfLine(layout->
lineCount());
857 bool urlStart =
false;
858 for (
int i = 0; i < numberOfLine; ++i) {
859 const QTextLine line = layout->
lineAt(i);
862 if (lineText.
contains(rx) || (urlStart && !lineText.
contains(QLatin1Char(
' ')) && lineText.
endsWith(QLatin1Char(
'-')))) {
867 temp += lineText + QLatin1Char(
'\n');
870 block = block.
next();
874 if (temp.
endsWith(QLatin1Char(
'\n'))) {
877 d->fixupTextEditString(temp);
881#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)
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