diff --git a/autotests/emoticonlistwidgetselectortest.cpp b/autotests/emoticonlistwidgetselectortest.cpp index be09028..c8d9f29 100644 --- a/autotests/emoticonlistwidgetselectortest.cpp +++ b/autotests/emoticonlistwidgetselectortest.cpp @@ -1,37 +1,36 @@ /* Copyright (c) 2019 Montel Laurent This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "emoticonlistwidgetselectortest.h" #include "emoticon/emoticonlistwidgetselector.h" #include QTEST_MAIN(EmoticonListWidgetSelectorTest) EmoticonListWidgetSelectorTest::EmoticonListWidgetSelectorTest(QObject *parent) : QObject(parent) { - } void EmoticonListWidgetSelectorTest::shouldHaveDefaultValues() { KPIMTextEdit::EmoticonListWidgetSelector w; QCOMPARE(w.viewMode(), QListView::IconMode); QCOMPARE(w.selectionMode(), QAbstractItemView::SingleSelection); QVERIFY(!w.dragEnabled()); } diff --git a/autotests/emoticonunicodetabtest.cpp b/autotests/emoticonunicodetabtest.cpp index c31553c..2db79e2 100644 --- a/autotests/emoticonunicodetabtest.cpp +++ b/autotests/emoticonunicodetabtest.cpp @@ -1,35 +1,34 @@ /* Copyright (c) 2019 Montel Laurent This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "emoticonunicodetabtest.h" #include "emoticon/emoticonunicodetab.h" #include QTEST_MAIN(EmoticonUnicodeTabTest) EmoticonUnicodeTabTest::EmoticonUnicodeTabTest(QObject *parent) : QObject(parent) { - } void EmoticonUnicodeTabTest::shouldHaveDefaultValues() { KPIMTextEdit::EmoticonUnicodeTab w; QVERIFY(w.count() > 0); } diff --git a/src/composer-ng/autotests/richtextcomposertest.cpp b/src/composer-ng/autotests/richtextcomposertest.cpp index 4786dd5..8de00fa 100644 --- a/src/composer-ng/autotests/richtextcomposertest.cpp +++ b/src/composer-ng/autotests/richtextcomposertest.cpp @@ -1,540 +1,540 @@ /* Copyright (C) 2015-2019 Laurent Montel This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "richtextcomposertest.h" #include "../richtextcomposer.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace KPIMTextEdit; Q_DECLARE_METATYPE(KPIMTextEdit::RichTextComposer::Mode) RichTextComposerTest::RichTextComposerTest(QObject *parent) : QObject(parent) { qRegisterMetaType(); QIcon::setThemeName(QStringLiteral("breeze")); QStandardPaths::setTestModeEnabled(true); } RichTextComposerTest::~RichTextComposerTest() { } void RichTextComposerTest::testFormattingUsed() { // This method tries to test everything that krichtextedit makes available, so // we can sure that in KMail, when the user uses some formatting, the mail is actually // sent as HTML mail KPIMTextEdit::RichTextComposer textEdit; textEdit.createActions(new KActionCollection(this)); QVERIFY(!textEdit.composerControler()->isFormattingUsed()); // Insert some text. QTextCursor cursor(textEdit.document()); cursor.insertText(QStringLiteral("Hello World!!")); QVERIFY(!textEdit.composerControler()->isFormattingUsed()); cursor.setPosition(1); textEdit.setTextCursor(cursor); // // Test link // QString someUrl = QStringLiteral("www.test.de"); QString altText = QStringLiteral("Hello"); textEdit.composerControler()->updateLink(someUrl, altText); QVERIFY(textEdit.composerControler()->isFormattingUsed()); QCOMPARE(textEdit.composerControler()->currentLinkUrl(), someUrl); QCOMPARE(textEdit.composerControler()->currentLinkText(), altText); cursor.setPosition(1); textEdit.setTextCursor(cursor); textEdit.composerControler()->updateLink(QString(), QStringLiteral("Hello")); QVERIFY(textEdit.composerControler()->currentLinkUrl().isEmpty()); QVERIFY(!textEdit.composerControler()->currentLinkText().isEmpty()); QVERIFY(!textEdit.composerControler()->isFormattingUsed()); // // Test alignment // cursor.setPosition(1); textEdit.setTextCursor(cursor); textEdit.composerControler()->alignRight(); QVERIFY(textEdit.composerControler()->isFormattingUsed()); QCOMPARE(textEdit.alignment(), Qt::AlignRight); textEdit.composerControler()->alignLeft(); QVERIFY(!textEdit.composerControler()->isFormattingUsed()); textEdit.composerControler()->alignCenter(); QCOMPARE(textEdit.alignment(), Qt::AlignHCenter); QVERIFY(textEdit.composerControler()->isFormattingUsed()); textEdit.composerControler()->alignJustify(); QCOMPARE(textEdit.alignment(), Qt::AlignJustify); QVERIFY(textEdit.composerControler()->isFormattingUsed()); textEdit.composerControler()->alignLeft(); QCOMPARE(textEdit.alignment(), Qt::AlignLeft); QVERIFY(!textEdit.composerControler()->isFormattingUsed()); // // Test layout direction // textEdit.selectAll(); QTextCharFormat direction; direction.setLayoutDirection(Qt::RightToLeft); textEdit.mergeCurrentCharFormat(direction); QVERIFY(textEdit.composerControler()->isFormattingUsed()); direction.setLayoutDirection(Qt::LeftToRight); textEdit.mergeCurrentCharFormat(direction); QVERIFY(textEdit.composerControler()->isFormattingUsed()); // // Test lists // textEdit.composerControler()->setListStyle(QTextListFormat::ListCircle); QVERIFY(textEdit.composerControler()->isFormattingUsed()); textEdit.composerControler()->setListStyle(0); QVERIFY(textEdit.composerControler()->isFormattingUsed()); // // Test font attributes // textEdit.setFontFamily(QStringLiteral("Times")); QVERIFY(textEdit.composerControler()->isFormattingUsed()); textEdit.setFontFamily(textEdit.document()->defaultFont().family()); QVERIFY(textEdit.composerControler()->isFormattingUsed()); textEdit.composerControler()->setFontSize(48); QVERIFY(textEdit.composerControler()->isFormattingUsed()); textEdit.composerControler()->setFontSize(textEdit.document()->defaultFont().pointSize()); QVERIFY(textEdit.composerControler()->isFormattingUsed()); QFont myFont = textEdit.document()->defaultFont(); myFont.setStyle(QFont::StyleOblique); textEdit.composerControler()->setFont(myFont); QVERIFY(textEdit.composerControler()->isFormattingUsed()); textEdit.composerControler()->setFont(textEdit.document()->defaultFont()); QVERIFY(textEdit.composerControler()->isFormattingUsed()); // // Test bold, italic, underline and strikeout // textEdit.composerControler()->setTextBold(true); QVERIFY(textEdit.composerControler()->isFormattingUsed()); textEdit.composerControler()->setTextBold(false); QVERIFY(textEdit.composerControler()->isFormattingUsed()); textEdit.composerControler()->setTextUnderline(true); QVERIFY(textEdit.composerControler()->isFormattingUsed()); textEdit.composerControler()->setTextUnderline(false); QVERIFY(textEdit.composerControler()->isFormattingUsed()); textEdit.composerControler()->setTextItalic(true); QVERIFY(textEdit.composerControler()->isFormattingUsed()); textEdit.composerControler()->setTextItalic(false); QVERIFY(textEdit.composerControler()->isFormattingUsed()); textEdit.composerControler()->setTextStrikeOut(true); QVERIFY(textEdit.composerControler()->isFormattingUsed()); textEdit.composerControler()->setTextStrikeOut(false); QVERIFY(textEdit.composerControler()->isFormattingUsed()); // // Color // QColor oldForeground = textEdit.document()->firstBlock().charFormat().foreground().color(); textEdit.composerControler()->setTextForegroundColor(Qt::red); QVERIFY(textEdit.composerControler()->isFormattingUsed()); textEdit.composerControler()->setTextForegroundColor(oldForeground); QVERIFY(textEdit.composerControler()->isFormattingUsed()); QColor oldBackground = textEdit.document()->firstBlock().charFormat().background().color(); textEdit.composerControler()->setTextBackgroundColor(Qt::red); QVERIFY(textEdit.composerControler()->isFormattingUsed()); textEdit.composerControler()->setTextBackgroundColor(oldBackground); QVERIFY(textEdit.composerControler()->isFormattingUsed()); // // Horizontal rule // textEdit.composerControler()->insertHorizontalRule(); QVERIFY(textEdit.composerControler()->isFormattingUsed()); // No way to easily remove the horizontal line, so clear the text edit and start over textEdit.clear(); cursor.insertText(QStringLiteral("Hello World!!")); QVERIFY(!textEdit.composerControler()->isFormattingUsed()); cursor.setPosition(1); textEdit.setTextCursor(cursor); // // Sub and superscript // textEdit.composerControler()->setTextSuperScript(true); QVERIFY(textEdit.composerControler()->isFormattingUsed()); textEdit.composerControler()->setTextSuperScript(false); QVERIFY(!textEdit.composerControler()->isFormattingUsed()); textEdit.composerControler()->setTextSubScript(true); QVERIFY(textEdit.composerControler()->isFormattingUsed()); textEdit.composerControler()->setTextSubScript(false); QVERIFY(!textEdit.composerControler()->isFormattingUsed()); // // Image // const QString imagePath = KIconLoader::global()->iconPath(QStringLiteral("folder-new"), KIconLoader::Small, false); textEdit.composerControler()->composerImages()->addImage(QUrl::fromLocalFile(imagePath)); QVERIFY(textEdit.composerControler()->isFormattingUsed()); cursor = textEdit.textCursor(); cursor.movePosition(QTextCursor::Left, QTextCursor::KeepAnchor, 1); cursor.removeSelectedText(); QVERIFY(!textEdit.composerControler()->isFormattingUsed()); } void RichTextComposerTest::testQuoting() { KPIMTextEdit::RichTextComposer edit; edit.createActions(new KActionCollection(this)); QVERIFY(edit.isLineQuoted(QStringLiteral("> Hello"))); QVERIFY(edit.isLineQuoted(QStringLiteral(">Hello"))); QVERIFY(!edit.isLineQuoted(QStringLiteral("Hello"))); QCOMPARE(edit.quoteLength(QStringLiteral("Hello")), 0); QCOMPARE(edit.quoteLength(QStringLiteral(">Hello")), 1); QCOMPARE(edit.quoteLength(QStringLiteral("> Hello")), 2); QCOMPARE(edit.quoteLength(QStringLiteral(">>>Hello")), 3); QCOMPARE(edit.quoteLength(QStringLiteral("> > > Hello")), 6); QCOMPARE(edit.quoteLength(QStringLiteral("|Hello")), 1); QCOMPARE(edit.quoteLength(QStringLiteral("| |Hello")), 3); } void RichTextComposerTest::testCleanText() { KPIMTextEdit::RichTextComposer edit; edit.createActions(new KActionCollection(this)); QString html(QStringLiteral("Heelllo World
Bye!")); QString plain(QStringLiteral("Heelllo World\nBye!")); edit.setTextOrHtml(html); edit.composerControler()->composerImages()->addImage(QUrl::fromLocalFile(KIconLoader::global()->iconPath(QStringLiteral("folder-new"), KIconLoader::Small, false))); QVERIFY(edit.textMode() == KPIMTextEdit::RichTextComposer::Rich); QCOMPARE(edit.composerControler()->toCleanPlainText(), plain); edit.show(); // < otherwise toWrappedPlainText can't work, it needs a layout QCOMPARE(edit.composerControler()->toWrappedPlainText(), plain); } void RichTextComposerTest::testEnter_data() { QTest::addColumn("initalText"); QTest::addColumn("expectedText"); QTest::addColumn("cursorPos"); QTest::newRow("") << QStringLiteral("> Hello World") << QStringLiteral("> Hello \n> World") << 8; QTest::newRow("") << QStringLiteral("Hello World") << QStringLiteral("Hello \nWorld") << 6; QTest::newRow("") << QStringLiteral("> Hello World") << QStringLiteral("> Hello World\n") << 13; QTest::newRow("") << QStringLiteral(">Hello World") << QStringLiteral(">Hello \n>World") << 7; QTest::newRow("") << QStringLiteral("> > Hello World") << QStringLiteral("> > Hello \n> > World") << 10; QTest::newRow("") << QStringLiteral("| | Hello World") << QStringLiteral("| | Hello \n| | World") << 10; } void RichTextComposerTest::testEnter() { QFETCH(QString, initalText); QFETCH(QString, expectedText); QFETCH(int, cursorPos); KPIMTextEdit::RichTextComposer edit; edit.createActions(new KActionCollection(this)); edit.setPlainText(initalText); QTextCursor textCursor(edit.document()); textCursor.setPosition(cursorPos); edit.setTextCursor(textCursor); QTest::keyClick(&edit, Qt::Key_Return); QCOMPARE(edit.toPlainText(), expectedText); } void RichTextComposerTest::testImages() { KPIMTextEdit::RichTextComposer edit; edit.createActions(new KActionCollection(this)); QString image1Path = KIconLoader::global()->iconPath(QStringLiteral("folder-new"), KIconLoader::Small, false); QString image2Path = KIconLoader::global()->iconPath(QStringLiteral("arrow-up"), KIconLoader::Small, false); // Add one image, check that embeddedImages() returns the right stuff edit.composerControler()->composerImages()->addImage(QUrl::fromLocalFile(image1Path)); KPIMTextEdit::ImageList images = edit.composerControler()->composerImages()->embeddedImages(); KPIMTextEdit::ImageWithNameList imagesWithNames = edit.composerControler()->composerImages()->imagesWithName(); QCOMPARE(images.size(), 1); QCOMPARE(imagesWithNames.size(), 1); EmbeddedImage *image = images.first().data(); ImageWithName *imageWithName = imagesWithNames.first().data(); QCOMPARE(image->imageName, QString::fromLatin1("folder-new.png")); QCOMPARE(imageWithName->name, QString::fromLatin1("folder-new.png")); // Also check that it loads the correct image QImage diskImage(image1Path); QBuffer buffer; buffer.open(QIODevice::WriteOnly); diskImage.save(&buffer, "PNG"); QBuffer imageWithNameBuffer; imageWithNameBuffer.open(QIODevice::WriteOnly); imageWithName->image.save(&imageWithNameBuffer, "PNG"); QByteArray encodedImage = KCodecs::Codec::codecForName("base64")->encode(buffer.buffer()); QCOMPARE(image->image, encodedImage); QCOMPARE(buffer.buffer(), imageWithNameBuffer.buffer()); // No image should be there after clearing edit.clear(); QVERIFY(edit.composerControler()->composerImages()->embeddedImages().isEmpty()); QVERIFY(edit.composerControler()->composerImages()->imagesWithName().isEmpty()); // Check that manually removing the image also empties the image list edit.composerControler()->composerImages()->addImage(QUrl::fromLocalFile(image1Path)); QCOMPARE(edit.composerControler()->composerImages()->embeddedImages().size(), 1); QCOMPARE(edit.composerControler()->composerImages()->imagesWithName().size(), 1); QTextCursor cursor = edit.textCursor(); cursor.setPosition(0, QTextCursor::MoveAnchor); cursor.movePosition(QTextCursor::Right, QTextCursor::KeepAnchor, 1); cursor.removeSelectedText(); QVERIFY(edit.composerControler()->composerImages()->embeddedImages().isEmpty()); QVERIFY(edit.composerControler()->composerImages()->imagesWithName().isEmpty()); // Check that adding the identical image two times only adds the image once edit.composerControler()->composerImages()->addImage(QUrl::fromLocalFile(image1Path)); edit.composerControler()->composerImages()->addImage(QUrl::fromLocalFile(image1Path)); QCOMPARE(edit.composerControler()->composerImages()->embeddedImages().size(), 1); QCOMPARE(edit.composerControler()->composerImages()->imagesWithName().size(), 1); // Another different image added, and we should have two images edit.clear(); edit.composerControler()->composerImages()->addImage(QUrl::fromLocalFile(image1Path)); edit.composerControler()->composerImages()->addImage(QUrl::fromLocalFile(image2Path)); images = edit.composerControler()->composerImages()->embeddedImages(); imagesWithNames = edit.composerControler()->composerImages()->imagesWithName(); QCOMPARE(images.size(), 2); QCOMPARE(imagesWithNames.size(), 2); KPIMTextEdit::EmbeddedImage *image1 = images.first().data(); KPIMTextEdit::EmbeddedImage *image2 = images.last().data(); KPIMTextEdit::ImageWithName *imageWithName1 = imagesWithNames.first().data(); KPIMTextEdit::ImageWithName *imageWithName2 = imagesWithNames.last().data(); QCOMPARE(image1->imageName, QString::fromLatin1("folder-new2.png")); // ### FIXME: should be folder-new.png, but QTextEdit provides no way to remove cached resources! QCOMPARE(imageWithName1->name, QString::fromLatin1("folder-new2.png")); QCOMPARE(image2->imageName, QString::fromLatin1("arrow-up.png")); QCOMPARE(imageWithName2->name, QString::fromLatin1("arrow-up.png")); QVERIFY(image1->contentID != image2->contentID); } void RichTextComposerTest::testImageHtmlCode() { KPIMTextEdit::RichTextComposer edit; edit.createActions(new KActionCollection(this)); QString image1Path = KIconLoader::global()->iconPath(QStringLiteral("folder-new"), KIconLoader::Small, false); QString image2Path = KIconLoader::global()->iconPath(QStringLiteral("arrow-up"), KIconLoader::Small, false); edit.composerControler()->composerImages()->addImage(QUrl::fromLocalFile(image1Path)); edit.composerControler()->composerImages()->addImage(QUrl::fromLocalFile(image2Path)); KPIMTextEdit::ImageList images = edit.composerControler()->composerImages()->embeddedImages(); QCOMPARE(images.size(), 2); KPIMTextEdit::EmbeddedImage *image1 = images.first().data(); KPIMTextEdit::EmbeddedImage *image2 = images.last().data(); QString startHtml = QStringLiteral("BlaBlub"); QString endHtml = QStringLiteral("BlaBlub") .arg(image2->contentID).arg(image1->contentID); QCOMPARE(KPIMTextEdit::RichTextComposerImages::imageNamesToContentIds(startHtml.toLatin1(), images), endHtml.toLatin1()); } void RichTextComposerTest::testDeleteLine_data() { QTest::addColumn("initalText"); QTest::addColumn("expectedText"); QTest::addColumn("cursorPos"); QTest::newRow("delete1") << QStringLiteral("line1\nline2\nline3") - << QStringLiteral("line1\nline3") - << 6; + << QStringLiteral("line1\nline3") + << 6; QTest::newRow("delete2") << QStringLiteral("line1\nline2\nline3") - << QStringLiteral("line2\nline3") - << 5; + << QStringLiteral("line2\nline3") + << 5; QTest::newRow("delete3") << QStringLiteral("line1\nline2\nline3") - << QStringLiteral("line1\nline3") - << 11; + << QStringLiteral("line1\nline3") + << 11; QTest::newRow("delete4") << QStringLiteral("line1\nline2\nline3") - << QStringLiteral("line2\nline3") - << 0; + << QStringLiteral("line2\nline3") + << 0; QTest::newRow("delete5") << QStringLiteral("line1\nline2\nline3") - << QStringLiteral("line1\nline2") - << 17; + << QStringLiteral("line1\nline2") + << 17; QTest::newRow("delete6") << QStringLiteral("line1") - << QString() - << 0; + << QString() + << 0; QTest::newRow("delete7") << QStringLiteral("line1") - << QString() - << 5; + << QString() + << 5; // Now, test deletion with word wrapping. The line with the Ms is so long that it will get wrapped QTest::newRow("delete8") << QStringLiteral("line1\nMMMMMMM MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\nline3") - << QStringLiteral("line1\nMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\nline3") - << 6; + << QStringLiteral("line1\nMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\nline3") + << 6; QTest::newRow("delete9") << QStringLiteral("line1\nMMMMMMM MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\nline3") - << QStringLiteral("line1\nMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\nline3") - << 13; + << QStringLiteral("line1\nMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\nline3") + << 13; } void RichTextComposerTest::testDeleteLine() { #if 0 QFETCH(QString, initalText); QFETCH(QString, expectedText); QFETCH(int, cursorPos); KPIMTextEdit::RichTextComposer edit; edit.createActions(new KActionCollection(this)); edit.setPlainText(initalText); QTextCursor cursor = edit.textCursor(); cursor.setPosition(cursorPos); edit.setTextCursor(cursor); edit.show(); // we need a layout for this to work edit.composerControler()->deleteCurrentLine(); QCOMPARE(edit.toPlainText(), expectedText); #endif } void RichTextComposerTest::testLoadImage() { KPIMTextEdit::RichTextComposer edit; edit.createActions(new KActionCollection(this)); QString image1Path = KIconLoader::global()->iconPath(QStringLiteral("folder-new"), KIconLoader::Small, false); QString image2Path = KIconLoader::global()->iconPath(QStringLiteral("arrow-up"), KIconLoader::Small, false); QImage image1, image2; QVERIFY(image1.load(image1Path)); QVERIFY(image2.load(image2Path)); edit.setHtml(QStringLiteral("BlaBla")); // First try to load an image with a name that doesn't match, it should fail edit.composerControler()->composerImages()->loadImage(image1, QStringLiteral("doesntmatch"), QStringLiteral("folder-new")); QVERIFY(!edit.document()->resource(QTextDocument::ImageResource, QUrl(QStringLiteral("folder-new"))).isValid()); // Now, load the image for real edit.composerControler()->composerImages()->loadImage(image1, QStringLiteral("folder-new.png"), QStringLiteral("folder-new")); QVERIFY(edit.document()->resource(QTextDocument::ImageResource, QUrl(QStringLiteral("folder-new"))).isValid()); // New test with a new textedit (so that we don't use the cached resources // This example has two images in the same text block, make sure that doesn't crash (bug 204214) KPIMTextEdit::RichTextComposer edit2; edit2.createActions(new KActionCollection(this)); edit2.setHtml(QStringLiteral("")); edit2.composerControler()->composerImages()->loadImage(image1, QStringLiteral("folder-new.png"), QStringLiteral("folder-new")); QVERIFY(edit.document()->resource(QTextDocument::ImageResource, QUrl(QStringLiteral("folder-new"))).isValid()); QCOMPARE(edit.composerControler()->composerImages()->embeddedImages().size(), 1); } void RichTextComposerTest::testWrappedPlainText_data() { QTest::addColumn("input"); QTest::addColumn("output"); QString defaultStr = QStringLiteral( "http://example.org/test-test-test-test-test-test-test-test-test-test-test-test-test\n https://example.org/test-test-test-test-test-test-test-test-test-test-test-test-test\ntest ftp://example.org/test-test-test-test-test-test-test-test-test-test-test-test-test\nftps://example.org/test-test-test-test-test-test-test-test-test-test-test-test-test\n ldap://example.org/test-test-test-test-test-test-test-test-test-test-test-test-test"); QTest::newRow("default") << defaultStr << defaultStr; QTest::newRow("empty") << QString() << QString(); QTest::newRow("wrap") << QStringLiteral("foosdfsdf sdsf sdfsdfsfs fsf sdfs df sfsdf dsf sdfsdf sf sf sfsdf sdsdf") << QStringLiteral( "foosdfsdf sdsf sdfsdfsfs fsf sdfs df sfsdf \ndsf sdfsdf sf sf sfsdf sdsdf"); QTest::newRow("wrap-2") << QStringLiteral("test-test-test-test-test-test-test-test-test-test-test-test-test") << QStringLiteral("test-test-test-test-test-test-test-test-\ntest-test-test-test-test"); QTest::newRow("wrap-3") << QStringLiteral("test-test-test-test-test-test-test-test-test-test-test-test-test\n\n") << QStringLiteral( "test-test-test-test-test-test-test-test-\ntest-test-test-test-test\n\n"); } void RichTextComposerTest::testWrappedPlainText() { QFETCH(QString, input); QFETCH(QString, output); KPIMTextEdit::RichTextComposer edit; edit.createActions(new KActionCollection(this)); edit.setPlainText(input); edit.show(); // < otherwise toWrappedPlainText can't work, it needs a layout QCOMPARE(edit.composerControler()->toWrappedPlainText(), output); } void RichTextComposerTest::testEnableDisableActions() { KPIMTextEdit::RichTextComposer composer; KActionCollection *actionCollection = new KActionCollection(&composer); composer.createActions(actionCollection); bool enableAction = true; composer.setEnableActions(enableAction); for (QAction *act : composer.actions()) { QCOMPARE(act->isEnabled(), enableAction); } enableAction = false; composer.setEnableActions(enableAction); for (QAction *act : composer.actions()) { QCOMPARE(act->isEnabled(), enableAction); } } void RichTextComposerTest::shouldHaveDefaultValue() { KPIMTextEdit::RichTextComposer composer; KActionCollection *actionCollection = new KActionCollection(&composer); composer.createActions(actionCollection); QCOMPARE(composer.linePosition(), 0); QCOMPARE(composer.columnNumber(), 0); QCOMPARE(composer.textMode(), KPIMTextEdit::RichTextComposer::Plain); QVERIFY(!composer.acceptRichText()); QVERIFY(!composer.quotePrefixName().isEmpty()); } void RichTextComposerTest::shouldChangeMode() { KPIMTextEdit::RichTextComposer composer; KActionCollection *actionCollection = new KActionCollection(&composer); composer.createActions(actionCollection); QSignalSpy spy(&composer, SIGNAL(textModeChanged(KPIMTextEdit::RichTextComposer::Mode))); composer.activateRichText(); QCOMPARE(composer.textMode(), KPIMTextEdit::RichTextComposer::Rich); QVERIFY(composer.acceptRichText()); QCOMPARE(spy.count(), 1); } QTEST_MAIN(RichTextComposerTest) diff --git a/src/composer-ng/richtextcomposeremailquotehighlighter.cpp b/src/composer-ng/richtextcomposeremailquotehighlighter.cpp index 344902f..e9b3fcf 100644 --- a/src/composer-ng/richtextcomposeremailquotehighlighter.cpp +++ b/src/composer-ng/richtextcomposeremailquotehighlighter.cpp @@ -1,114 +1,113 @@ /* Copyright (C) 2015-2019 Laurent Montel This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "richtextcomposeremailquotehighlighter.h" #include "richtextcomposer.h" #include using namespace KPIMTextEdit; class Q_DECL_HIDDEN KPIMTextEdit::RichTextComposerEmailQuoteHighlighter::RichTextComposerEmailQuoteHighlighterPrivate { public: RichTextComposer *parent = nullptr; QColor col1; QColor col2; QColor col3; QColor misspelledColor; bool spellCheckingEnabled = false; }; -RichTextComposerEmailQuoteHighlighter::RichTextComposerEmailQuoteHighlighter(RichTextComposer *textEdit, const QColor &normalColor, const QColor "eDepth1, const QColor "eDepth2, - const QColor "eDepth3, const QColor &misspelledColor) +RichTextComposerEmailQuoteHighlighter::RichTextComposerEmailQuoteHighlighter(RichTextComposer *textEdit, const QColor &normalColor, const QColor "eDepth1, const QColor "eDepth2, const QColor "eDepth3, const QColor &misspelledColor) : Sonnet::Highlighter(textEdit) , d(new KPIMTextEdit::RichTextComposerEmailQuoteHighlighter::RichTextComposerEmailQuoteHighlighterPrivate()) { Q_UNUSED(normalColor); // Don't automatically disable the spell checker, for example because there // are too many misspelled words. That would also disable quote highlighting. // FIXME: disable this spell checking! setAutomatic(false); setActive(true); d->col1 = quoteDepth1; d->col2 = quoteDepth2; d->col3 = quoteDepth3; d->misspelledColor = misspelledColor; d->spellCheckingEnabled = false; d->parent = textEdit; } RichTextComposerEmailQuoteHighlighter::~RichTextComposerEmailQuoteHighlighter() { delete d; } void RichTextComposerEmailQuoteHighlighter::setQuoteColor(const QColor &normalColor, const QColor "eDepth1, const QColor "eDepth2, const QColor "eDepth3, const QColor &misspelledColor) { Q_UNUSED(normalColor); d->col1 = quoteDepth1; d->col2 = quoteDepth2; d->col3 = quoteDepth3; d->misspelledColor = misspelledColor; } void RichTextComposerEmailQuoteHighlighter::toggleSpellHighlighting(bool on) { if (on != d->spellCheckingEnabled) { d->spellCheckingEnabled = on; rehighlight(); } } void RichTextComposerEmailQuoteHighlighter::highlightBlock(const QString &text) { QString simplified = text; simplified = simplified.remove(QRegularExpression(QStringLiteral("\\s"))). replace(QLatin1Char('|'), QLatin1Char('>')); while (simplified.startsWith(QLatin1String(">>>>"))) { simplified = simplified.mid(3); } if (simplified.startsWith(QLatin1String(">>>"))) { setFormat(0, text.length(), d->col3); } else if (simplified.startsWith(QLatin1String(">>"))) { setFormat(0, text.length(), d->col2); } else if (simplified.startsWith(QLatin1String(">"))) { setFormat(0, text.length(), d->col1); } else if (d->parent->isLineQuoted(text)) { setFormat(0, text.length(), d->col1); // FIXME: custom quote prefix // can't handle multiple levels } else if (d->spellCheckingEnabled) { Highlighter::highlightBlock(text); return; //setCurrentBlockState already done in Highlighter::highlightBlock } setCurrentBlockState(0); } void RichTextComposerEmailQuoteHighlighter::unsetMisspelled(int start, int count) { Q_UNUSED(start); Q_UNUSED(count); } void RichTextComposerEmailQuoteHighlighter::setMisspelled(int start, int count) { setMisspelledColor(d->misspelledColor); Sonnet::Highlighter::setMisspelled(start, count); } diff --git a/src/composer-ng/richtextcomposeremailquotehighlighter.h b/src/composer-ng/richtextcomposeremailquotehighlighter.h index d4d9e43..3ea17f4 100644 --- a/src/composer-ng/richtextcomposeremailquotehighlighter.h +++ b/src/composer-ng/richtextcomposeremailquotehighlighter.h @@ -1,87 +1,86 @@ /* Copyright (C) 2015-2019 Laurent Montel This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KPIMTEXTEDITRICHTEXTCOMPOSEREMAILQUOTEHIGHLIGHTER_H #define KPIMTEXTEDITRICHTEXTCOMPOSEREMAILQUOTEHIGHLIGHTER_H #include #include "kpimtextedit_export.h" namespace KPIMTextEdit { class RichTextComposer; class KPIMTEXTEDIT_EXPORT RichTextComposerEmailQuoteHighlighter : public Sonnet::Highlighter { Q_OBJECT public: /** * Constructor. See setQuoteColor() for the parameters. * FIXME: Default colors don't obey color scheme */ - explicit RichTextComposerEmailQuoteHighlighter(RichTextComposer *textEdit, const QColor &normalColor = Qt::black, const QColor "eDepth1 = QColor(0x00, 0x80, 0x00), - const QColor "eDepth2 = QColor(0x00, 0x80, 0x00), const QColor "eDepth3 = QColor(0x00, 0x80, 0x00), const QColor &misspelledColor = Qt::red); + explicit RichTextComposerEmailQuoteHighlighter(RichTextComposer *textEdit, const QColor &normalColor = Qt::black, const QColor "eDepth1 = QColor(0x00, 0x80, 0x00), const QColor "eDepth2 = QColor(0x00, 0x80, 0x00), const QColor "eDepth3 = QColor(0x00, 0x80, 0x00), const QColor &misspelledColor = Qt::red); ~RichTextComposerEmailQuoteHighlighter() override; /** * Sets the colors used for highlighting quoted text and spelling mistakes. * * @param quoteDepth1 color for text quoted 1 level deep * @param quoteDepth2 color for text quoted 2 level deep * @param quoteDepth3 color for text quoted 3 level deep * @param misspelledColor color in which misspelled words will be underlined * @param normalColor will be ignored, only provided for KNode * compatibility. */ void setQuoteColor(const QColor &normalColor, const QColor "eDepth1, const QColor "eDepth2, const QColor "eDepth3, const QColor &misspelledColor = Qt::red); /** * Turns spellcheck highlighting on or off. * * @param on if true, spelling mistakes will be highlighted */ void toggleSpellHighlighting(bool on); /** * Reimplemented to highlight quote blocks. */ void highlightBlock(const QString &text) override; protected: /** * Reimplemented, the base version sets the text color to black, which * is not what we want. We do nothing, the format is already reset by * Qt. * @param start the beginning of text * @param count the amount of characters to set */ void unsetMisspelled(int start, int count) override; /** * Reimplemented to set the color of the misspelled word to a color * defined by setQuoteColor(). */ void setMisspelled(int start, int count) override; private: class RichTextComposerEmailQuoteHighlighterPrivate; RichTextComposerEmailQuoteHighlighterPrivate *const d; }; } #endif // RICHTEXTCOMPOSEREMAILQUOTEHIGHLIGHTER_H diff --git a/src/emoticon/emoticonlistwidgetselector.cpp b/src/emoticon/emoticonlistwidgetselector.cpp index 3fc6d46..f573073 100644 --- a/src/emoticon/emoticonlistwidgetselector.cpp +++ b/src/emoticon/emoticonlistwidgetselector.cpp @@ -1,104 +1,101 @@ /* Copyright (c) 2019 Montel Laurent This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "emoticonlistwidgetselector.h" #include "emoticontexteditselector.h" using namespace KPIMTextEdit; EmoticonTextEditItem::EmoticonTextEditItem(const QString &emoticonText, QListWidget *parent) : QListWidgetItem(parent) { mText = emoticonText; setText(mText); setToolTip(mText); } EmoticonTextEditItem::EmoticonTextEditItem(const QString &emoticonText, const QString &pixmapPath, QListWidget *parent) : QListWidgetItem(parent) { mText = emoticonText; mPixmapPath = pixmapPath; QPixmap p(mPixmapPath); // Some of the custom icons are rather large // so lets limit them to a maximum size for this display panel // //TODO need to fix hdpi support here. if (p.width() > 32 || p.height() > 32) { p = p.scaled(QSize(32, 32), Qt::KeepAspectRatio); } setIcon(p); setToolTip(mText); } QString EmoticonTextEditItem::text() const { return mText; } QString EmoticonTextEditItem::pixmapPath() const { return mPixmapPath; } - EmoticonListWidgetSelector::EmoticonListWidgetSelector(QWidget *parent) - : QListWidget (parent) + : QListWidget(parent) { setViewMode(QListView::IconMode); setSelectionMode(QAbstractItemView::SingleSelection); setMouseTracking(true); setDragEnabled(false); connect(this, &EmoticonListWidgetSelector::itemEntered, this, &EmoticonListWidgetSelector::slotMouseOverItem); connect(this, &EmoticonListWidgetSelector::itemClicked, this, &EmoticonListWidgetSelector::slotEmoticonClicked); } EmoticonListWidgetSelector::~EmoticonListWidgetSelector() { - } void EmoticonListWidgetSelector::slotMouseOverItem(QListWidgetItem *item) { item->setSelected(true); if (!hasFocus()) { setFocus(); } } void EmoticonListWidgetSelector::setEmoticons(const QList &lst) { for (uint emoji : lst) { const QString str = QString::fromUcs4(&emoji, 1); new KPIMTextEdit::EmoticonTextEditItem(str, this); } } void EmoticonListWidgetSelector::slotEmoticonClicked(QListWidgetItem *item) { if (!item) { return; } EmoticonTextEditItem *itemEmoticon = static_cast(item); Q_EMIT itemSelected(itemEmoticon->text()); } - diff --git a/src/emoticon/emoticonlistwidgetselector.h b/src/emoticon/emoticonlistwidgetselector.h index de2257d..7bdb202 100644 --- a/src/emoticon/emoticonlistwidgetselector.h +++ b/src/emoticon/emoticonlistwidgetselector.h @@ -1,59 +1,57 @@ /* Copyright (c) 2019 Montel Laurent This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef EMOTICONLISTWIDGETSELECTOR_H #define EMOTICONLISTWIDGETSELECTOR_H #include #include "kpimtextedit_private_export.h" namespace KPIMTextEdit { - class KPIMTEXTEDIT_TESTS_EXPORT EmoticonTextEditItem : public QListWidgetItem { public: explicit EmoticonTextEditItem(const QString &emoticonText, const QString &pixmapPath, QListWidget *parent); explicit EmoticonTextEditItem(const QString &emoticonText, QListWidget *parent); QString text() const; QString pixmapPath() const; private: QString mText; QString mPixmapPath; }; - class KPIMTEXTEDIT_TESTS_EXPORT EmoticonListWidgetSelector : public QListWidget { Q_OBJECT public: explicit EmoticonListWidgetSelector(QWidget *parent = nullptr); ~EmoticonListWidgetSelector(); void setEmoticons(const QList &lst); Q_SIGNALS: void itemSelected(const QString &); private: void slotMouseOverItem(QListWidgetItem *item); void slotEmoticonClicked(QListWidgetItem *item); }; } #endif // EMOTICONLISTWIDGETSELECTOR_H diff --git a/src/emoticon/emoticontexteditselector.cpp b/src/emoticon/emoticontexteditselector.cpp index 94d5216..aba676e 100644 --- a/src/emoticon/emoticontexteditselector.cpp +++ b/src/emoticon/emoticontexteditselector.cpp @@ -1,110 +1,108 @@ /* Copyright (c) 2012-2019 Montel Laurent based on code from kopete This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "emoticonlistwidgetselector.h" #include "emoticontexteditselector.h" #include "textutils.h" #include #include #include #include #include // Use a static for this as calls to the KEmoticons constructor are expensive. Q_GLOBAL_STATIC(KEmoticons, sEmoticons) using namespace KPIMTextEdit; - EmoticonTextEditSelector::EmoticonTextEditSelector(QWidget *parent) : QWidget(parent) { QHBoxLayout *lay = new QHBoxLayout(this); lay->setSpacing(0); lay->setContentsMargins(0, 0, 0, 0); mListEmoticon = new EmoticonListWidgetSelector(this); lay->addWidget(mListEmoticon); connect(mListEmoticon, &EmoticonListWidgetSelector::itemSelected, this, &EmoticonTextEditSelector::slotItemSelected); } EmoticonTextEditSelector::~EmoticonTextEditSelector() { } void EmoticonTextEditSelector::slotItemSelected(const QString &str) { Q_EMIT itemSelected(str); if (isVisible() && parentWidget() && parentWidget()->inherits("QMenu")) { parentWidget()->close(); } } void EmoticonTextEditSelector::slotCreateEmoticonList() { mListEmoticon->clear(); if (mEmojiPlainText) { const QList lstEmoji = TextUtils::unicodeFullEmoji(); for (uint emoji : lstEmoji) { const QString str = QString::fromUcs4(&emoji, 1); new EmoticonTextEditItem(str, mListEmoticon); } } else { static QString cachedEmoticonsThemeName; if (cachedEmoticonsThemeName.isEmpty()) { cachedEmoticonsThemeName = KEmoticons::currentThemeName(); } const QHash list = sEmoticons->theme(cachedEmoticonsThemeName).emoticonsMap(); //Keep in sync with linklocator.cpp QStringList exclude; exclude << QStringLiteral("(c)") << QStringLiteral("(C)") << QStringLiteral(">:-(") << QStringLiteral(">:(") << QStringLiteral("(B)") << QStringLiteral("(b)") << QStringLiteral("(P)") << QStringLiteral("(p)"); exclude << QStringLiteral("(O)") << QStringLiteral("(o)") << QStringLiteral("(D)") << QStringLiteral("(d)") << QStringLiteral("(E)") << QStringLiteral("(e)") << QStringLiteral("(K)") << QStringLiteral("(k)"); exclude << QStringLiteral("(I)") << QStringLiteral("(i)") << QStringLiteral("(L)") << QStringLiteral("(l)") << QStringLiteral("(8)") << QStringLiteral("(T)") << QStringLiteral("(t)") << QStringLiteral("(G)"); exclude << QStringLiteral("(g)") << QStringLiteral("(F)") << QStringLiteral("(f)") << QStringLiteral("(H)"); exclude << QStringLiteral("8)") << QStringLiteral("(N)") << QStringLiteral("(n)") << QStringLiteral("(Y)") << QStringLiteral("(y)") << QStringLiteral("(U)") << QStringLiteral("(u)") << QStringLiteral("(W)") << QStringLiteral("(w)"); const QHash::const_iterator end = list.constEnd(); for (QHash::const_iterator it = list.constBegin(); it != end; ++it) { const QString str = it.value().first(); if (!exclude.contains(str)) { new EmoticonTextEditItem(str, it.key(), mListEmoticon); } } } mListEmoticon->setIconSize(QSize(32, 32)); } bool EmoticonTextEditSelector::emojiPlainText() const { return mEmojiPlainText; } void EmoticonTextEditSelector::setEmojiPlainText(bool emojiPlainText) { mEmojiPlainText = emojiPlainText; } - diff --git a/src/emoticon/emoticontexteditselector.h b/src/emoticon/emoticontexteditselector.h index 4c093f7..363f6b7 100644 --- a/src/emoticon/emoticontexteditselector.h +++ b/src/emoticon/emoticontexteditselector.h @@ -1,54 +1,53 @@ /* Copyright (c) 2012-2019 Montel Laurent based on code from kopete This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KPIMTEXTEDIT_EMOTICONTEXTEDITSELECTOR_H #define KPIMTEXTEDIT_EMOTICONTEXTEDITSELECTOR_H #include #include namespace KPIMTextEdit { class EmoticonListWidgetSelector; - class EmoticonTextEditSelector : public QWidget { Q_OBJECT public: explicit EmoticonTextEditSelector(QWidget *parent = nullptr); ~EmoticonTextEditSelector(); Q_REQUIRED_RESULT bool emojiPlainText() const; void setEmojiPlainText(bool emojiPlainText); public Q_SLOTS: void slotCreateEmoticonList(); Q_SIGNALS: void itemSelected(const QString &); private: void slotItemSelected(const QString &str); EmoticonListWidgetSelector *mListEmoticon = nullptr; bool mEmojiPlainText = false; }; } #endif /* KPIMTEXTEDIT_EMOTICONTEXTEDITSELECTOR_H */ diff --git a/src/textutils.cpp b/src/textutils.cpp index 9504d2b..ce9aef9 100644 --- a/src/textutils.cpp +++ b/src/textutils.cpp @@ -1,230 +1,229 @@ /* This file is part of KDE. Copyright (c) 2009 Thomas McGuire Copyright (c) 2010 Stephen Kelly This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "textutils.h" #include "kpimtextedit_debug.h" #include #include #include using namespace KPIMTextEdit; static bool isCharFormatFormatted(const QTextCharFormat &format, const QFont &defaultFont, const QTextCharFormat &defaultBlockFormat) { if (!format.anchorHref().isEmpty() || format.font() != defaultFont || format.isAnchor() || format.verticalAlignment() != defaultBlockFormat.verticalAlignment() || format.layoutDirection() != defaultBlockFormat.layoutDirection() || format.underlineStyle() != defaultBlockFormat.underlineStyle() || format.foreground().color() != defaultBlockFormat.foreground().color() || format.background().color() != defaultBlockFormat.background().color()) { return true; } return false; } static bool isBlockFormatFormatted(const QTextBlockFormat &format, const QTextBlockFormat &defaultFormat) { if (format.alignment() != defaultFormat.alignment() || format.layoutDirection() != defaultFormat.layoutDirection() || format.indent() != defaultFormat.indent() || format.textIndent() != defaultFormat.textIndent()) { return true; } return false; } /// @return true if the format represents a list, table, image or something like that. static bool isSpecial(const QTextFormat &charFormat) { return charFormat.isFrameFormat() || charFormat.isImageFormat() || charFormat.isListFormat() || charFormat.isTableFormat() || charFormat.isTableCellFormat(); } bool TextUtils::containsFormatting(const QTextDocument *document) { if (!document) { return false; } QTextDocument defaultTextDocument; const QTextCharFormat defaultCharFormat = defaultTextDocument.begin().charFormat(); const QTextBlockFormat defaultBlockFormat = defaultTextDocument.begin().blockFormat(); const QFont defaultFont = defaultTextDocument.defaultFont(); QTextBlock block = document->firstBlock(); while (block.isValid()) { if (isBlockFormatFormatted(block.blockFormat(), defaultBlockFormat)) { return true; } if (isSpecial(block.charFormat()) || isSpecial(block.blockFormat()) || block.textList()) { return true; } QTextBlock::iterator it = block.begin(); while (!it.atEnd()) { const QTextFragment fragment = it.fragment(); const QTextCharFormat charFormat = fragment.charFormat(); if (isSpecial(charFormat)) { return true; } if (isCharFormatFormatted(fragment.charFormat(), defaultFont, defaultCharFormat)) { return true; } ++it; } block = block.next(); } if (document->toHtml().contains(QLatin1String("
"))) { return true; } return false; } QString TextUtils::flowText(QString &wrappedText, const QString &indent, int maxLength) { if (wrappedText.isEmpty()) { return indent; } if (maxLength <= indent.length()) { qCWarning(KPIMTEXTEDIT_LOG) << "indent was set to a string that is longer or the same length " << "as maxLength, setting maxLength to indent.length() + 1"; maxLength = indent.length() + 1; } maxLength -= indent.length(); // take into account indent QString result; while (!wrappedText.isEmpty()) { // first check for the next newline. if it's before maxLength, break there, and continue int newLine = wrappedText.indexOf(QLatin1Char('\n')); if (newLine > 0 && newLine <= maxLength) { result += indent + wrappedText.left(newLine + 1); wrappedText = wrappedText.mid(newLine + 1); continue; } // Find the next point in the wrappedText where we have to do a line break. // Start searching at maxLength position and then walk backwards looking // for a space. int breakPosition; if (wrappedText.length() > maxLength) { breakPosition = maxLength; while ((breakPosition >= 0) && (wrappedText[breakPosition] != QLatin1Char(' '))) { breakPosition--; } if (breakPosition <= 0) { // Couldn't break before maxLength. breakPosition = maxLength; } } else { breakPosition = wrappedText.length(); } QString line = wrappedText.left(breakPosition); if (breakPosition < wrappedText.length()) { wrappedText = wrappedText.mid(breakPosition); } else { wrappedText.clear(); } // Strip leading whitespace of new lines, since that looks strange if (!result.isEmpty() && line.startsWith(QLatin1Char(' '))) { line = line.mid(1); } result += indent + line + QLatin1Char('\n'); } return result.left(result.length() - 1); } QList TextUtils::unicodeFullEmoji() { - return TextUtils::unicodeFacesEmoji() + - TextUtils::unicodeAnimalsEmoji() + - TextUtils::unicodeEmotionEmoji() + - TextUtils::unicodeBodyEmoji() + - TextUtils::unicodeTransportEmoji() + - TextUtils::unicodeEventEmoji() + - TextUtils::unicodeFlagsEmoji(); + return TextUtils::unicodeFacesEmoji() + +TextUtils::unicodeAnimalsEmoji() + +TextUtils::unicodeEmotionEmoji() + +TextUtils::unicodeBodyEmoji() + +TextUtils::unicodeTransportEmoji() + +TextUtils::unicodeEventEmoji() + +TextUtils::unicodeFlagsEmoji(); } QList TextUtils::unicodeFacesEmoji() { const QList lstEmoji{0x1F600, 0x1F603, 0x1F604, 0x1F601, 0x1F606, 0x1F6005, 0x1F923, 0x1F602, 0x1F642, 0x1F643, 0x1F609, 0x1F60A, 0x1F607, 0x1F970, 0x1F60D, 0x1F929, 0x1F618, 0x1F617, 0x1F61A, 0x1F619, 0x1F60B, 0x1F61B, 0x1F61C, 0x1F92A, 0x1F61D, 0x1F911, 0x1F917, 0x1F92D, 0x1F92B, 0x1F914, 0x1F910, 0x1F928, 0x1F610, 0x1F611, 0x1F636, 0x1F60F, 0x1F612, 0x1F644, 0x1F62C, 0x1F925, 0x1F60C, 0x1F614, 0x1F62A, 0x1F924, 0x1F634, 0x1F637, 0x1F912, 0x1F915, 0x1F922, 0x1F92E, 0x1F927, 0x1F975, 0x1F976, 0x1F974, 0x1F635, 0x1F92F, 0x1F920, 0x1F973, 0x1F60E, 0x1F913, 0x1F908, 0x1F615, 0x1F61F, 0x1F641, 0x1F62E, 0x1F62F, 0x1F632, 0x1F633, 0x1F97A, 0x1F626, 0x1F627}; //Add more return lstEmoji; } QList TextUtils::unicodeAnimalsEmoji() { const QList lstEmoji{0x1F63A, 0x1F638, 0x1F639, 0x1F638, 0x1F63C, 0x1F63D, 0x1F640, 0x1F63F, 0x1F63E, 0x1F648, 0x1F649, 0x1F64A}; return lstEmoji; } QList TextUtils::unicodeEmotionEmoji() { //Add more const QList lstEmoji{0x1F48B, 0x1F48C, 0x1F498, 0x1F49D, 0x1F496, 0x1F497, 0x1F493, 0x1F49E, 0x1F495, 0x1F4AF, 0x1F4A2, 0x1F4A5, 0x1F4A4}; return lstEmoji; } QList TextUtils::unicodeBodyEmoji() { //Add more const QList lstEmoji{0x1F44B, 0x1F91A, 0x1F590, 0x270B, 0x1F596, 0x1F44C, 0x270C, 0x1F91F, 0x1F44D, 0x1F44F, 0x1F4AA}; return lstEmoji; - } QList TextUtils::unicodeTransportEmoji() { //Add more const QList lstEmoji{0x1F682, 0x1F683, 0x1F684, 0x1F685, 0x1F686, 0x1F687, 0x1F688, 0x1F689, 0x1F69D, 0x1F691, 0x1F692, 0x1F693, 0x1F694, 0x1F695, 0x1F696, 0x1F697}; return lstEmoji; } QList TextUtils::unicodeEventEmoji() { //Add more const QList lstEmoji{0x1F383, 0x1F384, 0x1F386, 0x1F387, 0x1F9E8, 0x2728, 0x1F388, 0x1F389, 0x1F38A, 0x1F38B, 0x1F38D, 0x1F38E, 0x1F38F, 0x1F390, 0x1F391}; return lstEmoji; } QList TextUtils::unicodeFlagsEmoji() { //Add more const QList lstEmoji{0x1F1E6, 0x1F1E9, 0x1F1EA, 0x1F1EB, 0x1F1EC, 0x1F1EE, 0x1F1FB, 0x1F1FE}; return lstEmoji; }