diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 0f26cd6a5..fa5d76a27 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -19,7 +19,7 @@ build_ubuntu_20_04: - apt-get update - apt-get install --yes eatmydata - eatmydata apt-get build-dep --yes --no-install-recommends okular - - eatmydata apt-get install --yes --no-install-recommends ninja-build + - eatmydata apt-get install --yes --no-install-recommends ninja-build qtbase5-private-dev script: - mkdir -p build && cd build - cmake -DOKULAR_UI=desktop -G Ninja .. @@ -37,7 +37,7 @@ build_clazy_clang_tidy: - apt-get update - apt-get install --yes eatmydata - eatmydata apt-get build-dep --yes --no-install-recommends okular - - eatmydata apt-get install --yes --no-install-recommends ninja-build clazy clang clang-tidy libkf5crash-dev libkf5purpose-dev kirigami2-dev libegl-dev jq + - eatmydata apt-get install --yes --no-install-recommends ninja-build clazy clang clang-tidy qtbase5-private-dev libkf5crash-dev libkf5purpose-dev kirigami2-dev libegl-dev jq script: - srcdir=`pwd` && mkdir -p /tmp/okular_build && cd /tmp/okular_build && CC=clang CXX=clazy CXXFLAGS="-Werror -Wno-deprecated-declarations" cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=ON -G Ninja $srcdir && cat compile_commands.json | jq '[.[] | select(.file | contains("'"$srcdir"'"))]' > compile_commands.aux.json && cat compile_commands.aux.json | jq '[.[] | select(.file | contains("/synctex/")| not)]' > compile_commands.json && cp "$srcdir/.clang-tidy" . diff --git a/CMakeLists.txt b/CMakeLists.txt index 05743e8e5..98cfd2b17 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -451,6 +451,7 @@ PUBLIC # these are included from the installed headers KF5::ConfigGui Qt5::PrintSupport Qt5::Widgets + Qt5::CorePrivate ) diff --git a/autotests/documenttest.cpp b/autotests/documenttest.cpp index 0b9c3eddc..e6115e10f 100644 --- a/autotests/documenttest.cpp +++ b/autotests/documenttest.cpp @@ -29,6 +29,8 @@ class DocumentTest : public QObject private Q_SLOTS: void testCloseDuringRotationJob(); void testDocdataMigration(); + void testDiff_data(); + void testDiff(); }; // Test that we don't crash if the document is closed while a RotationJob @@ -126,5 +128,52 @@ void DocumentTest::testDocdataMigration() delete m_document; } +void DocumentTest::testDiff_data() +{ + QTest::addColumn("oldVal"); + QTest::addColumn("newVal"); + QTest::addColumn("expectedDiff"); + + QTest::addRow("empty") << "" + << "" + << ""; + QTest::addRow("a") << "" + << "a" + << "a"; + QTest::addRow("ab") << "a" + << "b" + << "b"; + QTest::addRow("ab2") << "a" + << "ab" + << "b"; + QTest::addRow("kaesekuchen") << "Käse" + << "Käsekuchen" + << "kuchen"; + QTest::addRow("replace") << "kuchen" + << "wurst" + << "wurst"; + QTest::addRow("okular") << "Oku" + << "Okular" + << "lar"; + QTest::addRow("removal1") << "a" + << "" + << ""; + QTest::addRow("removal2") << "ab" + << "a" + << ""; + QTest::addRow("unicode") << "☮🤌" + << "☮🤌❤️" + << "❤️"; +} + +void DocumentTest::testDiff() +{ + QFETCH(QString, oldVal); + QFETCH(QString, newVal); + QFETCH(QString, expectedDiff); + + QCOMPARE(Okular::DocumentPrivate::diff(oldVal, newVal), expectedDiff); +} + QTEST_MAIN(DocumentTest) #include "documenttest.moc" diff --git a/core/document.cpp b/core/document.cpp index 2d91e3a79..33ff1182b 100644 --- a/core/document.cpp +++ b/core/document.cpp @@ -47,6 +47,7 @@ #include #include #include +#include #include #include @@ -4342,6 +4343,29 @@ void Document::processFormatAction(const Action *action, Okular::FormFieldText * } } +QString DocumentPrivate::diff(const QString &oldVal, const QString &newVal) +{ + QString diff; + + QStringIterator oldIt(oldVal); + QStringIterator newIt(newVal); + + while (oldIt.hasNext() && newIt.hasNext()) { + QChar oldToken = oldIt.next(); + QChar newToken = newIt.next(); + + if (oldToken != newToken) { + diff += newToken; + break; + } + } + + while (newIt.hasNext()) { + diff += newIt.next(); + } + return diff; +} + void Document::processKeystrokeAction(const Action *action, Okular::FormFieldText *fft, const QVariant &newValue) { if (action->actionType() != Action::Script) { @@ -4357,6 +4381,7 @@ void Document::processKeystrokeAction(const Action *action, Okular::FormFieldTex } std::shared_ptr event = Event::createKeystrokeEvent(fft, d->m_pagesVector[foundPage]); + event->setChange(DocumentPrivate::diff(fft->text(), newValue.toString())); const ScriptAction *linkscript = static_cast(action); diff --git a/core/document_p.h b/core/document_p.h index f5fbe5728..070b3e334 100644 --- a/core/document_p.h +++ b/core/document_p.h @@ -230,6 +230,8 @@ public: void clearAndWaitForRequests(); + OKULARCORE_EXPORT static QString diff(const QString &oldVal, const QString &newVal); + /* * Executes a ScriptAction with the event passed as parameter. */ diff --git a/core/script/event.cpp b/core/script/event.cpp index ec0f93efb..17bba46d7 100644 --- a/core/script/event.cpp +++ b/core/script/event.cpp @@ -36,6 +36,7 @@ public: bool m_returnCode; bool m_shiftModifier; bool m_willCommit; + QString m_change; }; Event::Event() @@ -181,6 +182,16 @@ void Event::setWillCommit(bool willCommit) d->m_willCommit = willCommit; } +QString Event::change() const +{ + return d->m_change; +} + +void Event::setChange(const QString &change) +{ + d->m_change = change; +} + // static std::shared_ptr Event::createFormCalculateEvent(FormField *target, Page *targetPage, FormField *source, Page *sourcePage, const QString &targetName) { diff --git a/core/script/event_p.h b/core/script/event_p.h index f3772722d..26c107619 100644 --- a/core/script/event_p.h +++ b/core/script/event_p.h @@ -108,6 +108,9 @@ public: bool willCommit() const; void setWillCommit(bool willCommit); + QString change() const; + void setChange(const QString &change); + static std::shared_ptr createFormCalculateEvent(FormField *target, Page *targetPage, FormField *source = nullptr, Page *sourcePage = nullptr, const QString &targetName = QString()); static std::shared_ptr createFormatEvent(FormField *target, Page *targetPage, const QString &targetName = QString()); static std::shared_ptr createKeystrokeEvent(FormField *target, Page *targetPage); diff --git a/core/script/kjs_event.cpp b/core/script/kjs_event.cpp index 11b84c047..9bb44f31e 100644 --- a/core/script/kjs_event.cpp +++ b/core/script/kjs_event.cpp @@ -123,6 +123,13 @@ static KJSObject eventGetWillCommit(KJSContext *, void *object) return KJSBoolean(event->willCommit()); } +// Event.change (getter) +static KJSObject eventGetChange(KJSContext *, void *object) +{ + const Event *event = reinterpret_cast(object); + return KJSString(event->change()); +} + void JSEvent::initType(KJSContext *ctx) { static bool initialized = false; @@ -144,6 +151,7 @@ void JSEvent::initType(KJSContext *ctx) g_eventProto->defineProperty(ctx, QStringLiteral("willCommit"), eventGetWillCommit); g_eventProto->defineProperty(ctx, QStringLiteral("value"), eventGetValue, eventSetValue); g_eventProto->defineProperty(ctx, QStringLiteral("rc"), eventGetReturnCode, eventSetReturnCode); + g_eventProto->defineProperty(ctx, QStringLiteral("change"), eventGetChange); } KJSObject JSEvent::wrapEvent(KJSContext *ctx, Event *event)