7#include "richtextcomposerng.h"
8#include "richtextcomposersignatures.h"
9#include "settings/messagecomposersettings.h"
10#include <KPIMTextEdit/MarkupDirector>
11#include <KPIMTextEdit/PlainTextMarkupBuilder>
12#include <KPIMTextEdit/RichTextComposerControler>
13#include <KPIMTextEdit/RichTextComposerImages>
14#include <KPIMTextEdit/TextHTMLBuilder>
16#include <TextAutoCorrectionCore/AutoCorrection>
18#include "part/textpart.h"
22#include <QRegularExpression>
26class MessageComposer::RichTextComposerNgPrivate
29 explicit RichTextComposerNgPrivate(RichTextComposerNg *q)
31 , richTextComposerSignatures(new MessageComposer::RichTextComposerSignatures(richtextComposer, richtextComposer))
35 void fixHtmlFontSize(QString &cleanHtml)
const;
36 [[nodiscard]] QString toCleanHtml()
const;
37 TextAutoCorrectionCore::AutoCorrection *autoCorrection =
nullptr;
38 RichTextComposerNg *
const richtextComposer;
39 MessageComposer::RichTextComposerSignatures *
const richTextComposerSignatures;
42RichTextComposerNg::RichTextComposerNg(
QWidget *parent)
48RichTextComposerNg::~RichTextComposerNg() =
default;
52 return d->richTextComposerSignatures;
55TextAutoCorrectionCore::AutoCorrection *RichTextComposerNg::autocorrection()
const
57 return d->autoCorrection;
60void RichTextComposerNg::setAutocorrection(TextAutoCorrectionCore::AutoCorrection *autocorrect)
62 d->autoCorrection = autocorrect;
65void RichTextComposerNg::setAutocorrectionLanguage(
const QString &lang)
67 if (d->autoCorrection) {
68 TextAutoCorrectionCore::AutoCorrectionSettings *settings = d->autoCorrection->autoCorrectionSettings();
69 settings->setLanguage(lang);
70 d->autoCorrection->setAutoCorrectionSettings(settings);
74static bool isSpecial(
const QTextCharFormat &charFormat)
80bool RichTextComposerNg::processModifyText(QKeyEvent *e)
82 if (d->autoCorrection && d->autoCorrection->autoCorrectionSettings()->isEnabledAutoCorrection()) {
84 if (!isLineQuoted(textCursor().block().text()) && !textCursor().hasSelection()) {
85 const QTextCharFormat initialTextFormat = textCursor().charFormat();
86 const bool richText = (
textMode() == RichTextComposer::Rich);
87 int position = textCursor().position();
88 const bool addSpace = d->autoCorrection->autocorrect(richText, *document(), position);
89 QTextCursor cur = textCursor();
92 if (overwriteMode() && spacePressed) {
94 const QChar insertChar = QLatin1Char(
' ');
98 if (richText && !isSpecial(initialTextFormat)) {
106 const QChar insertChar = spacePressed ? QLatin1Char(
' ') : QLatin1Char(
'\n');
107 if (richText && !isSpecial(initialTextFormat)) {
108 if ((spacePressed && addSpace) || !spacePressed) {
109 cur.
insertText(insertChar, initialTextFormat);
112 if ((spacePressed && addSpace) || !spacePressed) {
125void RichTextComposerNgPrivate::fixHtmlFontSize(QString &cleanHtml)
const
128 static const QRegularExpression styleRegex(QStringLiteral(
"<span style=\".*?font-size:(.*?)pt;.*?</span>"));
130 QRegularExpressionMatch rmatch;
132 while (cleanHtml.
indexOf(styleRegex, offset, &rmatch) != -1) {
137 const double emValue = ptValue / 12;
151MessageComposer::PluginEditorConvertTextInterface::ConvertTextStatus RichTextComposerNg::convertPlainText(MessageComposer::TextPart *textPart)
154 return MessageComposer::PluginEditorConvertTextInterface::ConvertTextStatus::NotConverted;
157void RichTextComposerNg::fillComposerTextPart(MessageComposer::TextPart *textPart)
159 const bool wasConverted = convertPlainText(textPart) == MessageComposer::PluginEditorConvertTextInterface::ConvertTextStatus::Converted;
160 if (composerControler()->isFormattingUsed() && !wasConverted) {
161 if (MessageComposer::MessageComposerSettings::self()->improvePlainTextOfHtmlMessage()) {
162 KPIMTextEdit::PlainTextMarkupBuilder pb;
164 KPIMTextEdit::MarkupDirector pmd(&pb);
165 pmd.processDocument(document());
166 const QString plainText = pb.getResult();
167 textPart->setCleanPlainText(composerControler()->toCleanPlainText(plainText));
168 QTextDocument doc(plainText);
171 textPart->setWrappedPlainText(composerControler()->toWrappedPlainText(&doc));
173 textPart->setCleanPlainText(composerControler()->toCleanPlainText());
174 textPart->setWrappedPlainText(composerControler()->toWrappedPlainText());
176 }
else if (!wasConverted) {
177 textPart->setCleanPlainText(composerControler()->toCleanPlainText());
178 textPart->setWrappedPlainText(composerControler()->toWrappedPlainText());
182 if (composerControler()->isFormattingUsed() && !wasConverted) {
183 KPIMTextEdit::TextHTMLBuilder pb;
185 KPIMTextEdit::MarkupDirector pmd(&pb);
186 pmd.processDocument(document());
188 QStringLiteral(
"<html>\n<head>\n<meta http-equiv=\"content-type\" content=\"text/html; charset=UTF-8\">\n</head>\n<body>%1</body>\n</html>")
190 d->fixHtmlFontSize(cleanHtml);
191 textPart->setCleanHtml(cleanHtml);
194 textPart->setEmbeddedImages(composerControler()->composerImages()->embeddedImages());
198QString RichTextComposerNgPrivate::toCleanHtml()
const
200 QString result = richtextComposer->toHtml();
202 static const QString EMPTYLINEHTML = QStringLiteral(
203 "<p style=\"-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; "
204 "margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; \"> </p>");
209 static const QString EMPTYLINEREGEX = QStringLiteral(
"<p style=\"-qt-paragraph-type:empty;(?:.*?)</p>");
211 static const QString OLLISTPATTERNQT = QStringLiteral(
"<ol style=\"margin-top: 0px; margin-bottom: 0px; margin-left: 0px;");
213 static const QString ULLISTPATTERNQT = QStringLiteral(
"<ul style=\"margin-top: 0px; margin-bottom: 0px; margin-left: 0px;");
215 static const QString ORDEREDLISTHTML = QStringLiteral(
"<ol style=\"margin-top: 0px; margin-bottom: 0px;");
217 static const QString UNORDEREDLISTHTML = QStringLiteral(
"<ul style=\"margin-top: 0px; margin-bottom: 0px;");
225 result.
replace(QRegularExpression(EMPTYLINEREGEX), EMPTYLINEHTML);
229 result.
replace(OLLISTPATTERNQT, ORDEREDLISTHTML);
233 result.
replace(ULLISTPATTERNQT, UNORDEREDLISTHTML);
238static bool isCursorAtEndOfLine(
const QTextCursor &cursor)
240 QTextCursor testCursor = cursor;
245static void insertSignatureHelper(
const QString &signature,
254 bool isModified = textEdit->document()->isModified();
257 QTextCursor cursor = textEdit->textCursor();
258 QTextCursor oldCursor = cursor;
268 textEdit->setTextCursor(cursor);
273 lineSep = QStringLiteral(
"<br>");
275 lineSep = QLatin1Char(
'\n');
280 int newCursorPos = -1;
290 if (oldCursor.
position() == textEdit->toPlainText().length()) {
291 newCursorPos = oldCursor.
position();
299 headSep = lineSep + lineSep;
300 if (!isCursorAtEndOfLine(cursor)) {
304 if (!isCursorAtEndOfLine(cursor)) {
309 const QString full_signature = headSep + signature + tailSep;
311 textEdit->insertHtml(full_signature);
313 textEdit->insertPlainText(full_signature);
317 if (newCursorPos != -1) {
321 textEdit->setTextCursor(oldCursor);
322 textEdit->ensureCursorVisible();
324 textEdit->document()->setModified(isModified);
327 textEdit->activateRichText();
332void RichTextComposerNg::insertSignature(
const KIdentityManagementCore::Signature &signature,
336 if (signature.isEnabledSignature()) {
337 QString signatureStr;
343 signatureStr = signature.
rawText(&ok, &errorMessage);
350 insertSignatureHelper(signatureStr,
353 (signature.
isInlinedHtml() && signature.
type() == KIdentityManagementCore::Signature::Inlined),
358 const QList<KIdentityManagementCore::Signature::EmbeddedImagePtr> embeddedImages = signature.embeddedImages();
359 for (
const KIdentityManagementCore::Signature::EmbeddedImagePtr &image : embeddedImages) {
360 composerControler()->composerImages()->loadImage(image->image, image->name, image->name);
366QString RichTextComposerNg::toCleanHtml()
const
368 return d->toCleanHtml();
371void RichTextComposerNg::fixHtmlFontSize(QString &cleanHtml)
const
373 d->fixHtmlFontSize(cleanHtml);
376void RichTextComposerNg::forceAutoCorrection(
bool selectedText)
378 if (document()->isEmpty()) {
381 if (d->autoCorrection && d->autoCorrection->autoCorrectionSettings()->isEnabledAutoCorrection()) {
382 const bool richText = (
textMode() == RichTextComposer::Rich);
383 const int initialPosition = textCursor().position();
384 QTextCursor cur = textCursor();
390 int cursorPosition = positionStart;
391 while (cursorPosition < positionEnd) {
398 (void)d->autoCorrection->autocorrect(richText, *document(), cursorPosition);
402 while (!cur.
atEnd()) {
408 int cursorPosition = cur.
position();
409 (void)d->autoCorrection->autocorrect(richText, *document(), cursorPosition);
413 if (cur.
position() != initialPosition) {
420#include "moc_richtextcomposerng.cpp"
bool isInlinedHtml() const
QFlags< AddedTextFlag > AddedText
QString rawText(bool *ok=nullptr, QString *errorMessage=nullptr) const
QString withSeparator(bool *ok=nullptr, QString *errorMessage=nullptr) const
QString getResult() override
The RichTextComposerNg class.
The RichTextComposerSignatures class.
KCALUTILS_EXPORT QString errorMessage(const KCalendarCore::Exception &exception)
void error(QWidget *parent, const QString &text, const QString &title, const KGuiItem &buttonOk, Options options=Notify)
Simple interface that both EncryptJob and SignEncryptJob implement so the composer can extract some e...
QString captured(QStringView name) const const
qsizetype capturedEnd(QStringView name) const const
qsizetype capturedLength(QStringView name) const const
qsizetype capturedStart(QStringView name) const const
QString arg(Args &&... args) const const
qsizetype indexOf(QChar ch, qsizetype from, Qt::CaseSensitivity cs) const const
bool isEmpty() const const
QString number(double n, char format, int precision)
QString & replace(QChar before, QChar after, Qt::CaseSensitivity cs)
qsizetype size() const const
double toDouble(bool *ok) const const
QString text() const const
bool atBlockEnd() const const
QTextBlock block() const const
bool hasSelection() const const
void insertText(const QString &text)
bool movePosition(MoveOperation operation, MoveMode mode, int n)
int position() const const
int selectionEnd() const const
int selectionStart() const const
void setPosition(int pos, MoveMode m)
bool isFrameFormat() const const
bool isImageFormat() const const
bool isListFormat() const const
bool isTableCellFormat() const const
bool isTableFormat() const const