diff --git a/CMakeLists.txt b/CMakeLists.txt --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -304,13 +304,15 @@ core/script/kjs_app.cpp core/script/kjs_console.cpp core/script/kjs_data.cpp + core/script/kjs_display.cpp core/script/kjs_document.cpp core/script/kjs_field.cpp core/script/kjs_fullscreen.cpp core/script/kjs_field.cpp core/script/kjs_spell.cpp core/script/kjs_util.cpp core/script/kjs_event.cpp + core/script/kjs_ocg.cpp ) target_link_libraries(okularcore PRIVATE KF5::JS KF5::JSApi) endif() diff --git a/autotests/CMakeLists.txt b/autotests/CMakeLists.txt --- a/autotests/CMakeLists.txt +++ b/autotests/CMakeLists.txt @@ -17,6 +17,11 @@ TEST_NAME "visibilitytest" LINK_LIBRARIES Qt5::Widgets Qt5::Test okularcore ) + + ecm_add_test(kjsfunctionstest.cpp + TEST_NAME "kjsfunctionstest" + LINK_LIBRARIES Qt5::Widgets Qt5::Test okularcore + ) endif() ecm_add_test(documenttest.cpp diff --git a/autotests/data/kjsfunctionstest.pdf b/autotests/data/kjsfunctionstest.pdf new file mode 100644 index 0000000000000000000000000000000000000000..c580283a6eba847a7c5dd9331a4e2ef2f5fb821d GIT binary patch literal 83477 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 literal 0 Hc$@ * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + ***************************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include "../settings_core.h" +#include "core/document.h" +#include "core/scripter.h" +#include +#include +#include + +#include "../generators/poppler/config-okular-poppler.h" + +class KJSFunctionsTest: public QObject +{ + Q_OBJECT + +private slots: + void initTestCase(); + void testNumFields(); + void testNthFieldName(); + void testDisplay(); + void testSetClearInterval(); + void testSetClearTimeOut(); + void testGetOCGs(); + void testOCGSetState(); + void cleanupTestCase(); + +private: + Okular::Document *m_document; + QMap m_fields; +}; + +void KJSFunctionsTest::initTestCase() +{ + Okular::SettingsCore::instance( QStringLiteral("kjsfunctionstest") ); + m_document = new Okular::Document( nullptr ); + + const QString testFile = QStringLiteral( KDESRCDIR "data/kjsfunctionstest.pdf" ); + QMimeDatabase db; + const QMimeType mime = db.mimeTypeForFile( testFile ); + QCOMPARE( m_document->openDocument( testFile, QUrl(), mime), Okular::Document::OpenSuccess ); + + const Okular::Page* page = m_document->page( 0 ); + for ( Okular::FormField *ff: page->formFields() ) + { + m_fields.insert( ff->name(), ff ); + } +} + +void KJSFunctionsTest::testNumFields() +{ + QString result = m_document->executeScript( "Doc.numFields" ); + QCOMPARE( "31", result ); +} + +void KJSFunctionsTest::testNthFieldName() +{ + for(int i = 0;i < 21;++i) + { + QString result = m_document->executeScript( QString( "Doc.getNthFieldName(%1)" ).arg( i ) ); + QCOMPARE( result, QString( "0.%1" ).arg(i) ); + } +} + +void KJSFunctionsTest::testDisplay() +{ + m_document->executeScript( "field = Doc.getField(\"0.0\");field.display=display.hidden;\ + field = Doc.getField(\"0.10\");field.display=display.visible;" ); + QCOMPARE( false, m_fields["0.0"]->isVisible() ); + QCOMPARE( true, m_fields["0.10"]->isVisible() ); + + m_document->executeScript( "field = Doc.getField(\"0.10\");field.display=display.hidden;\ + field = Doc.getField(\"0.15\");field.display=display.visible;" ); + QCOMPARE( false, m_fields["0.10"]->isVisible() ); + QCOMPARE( true, m_fields["0.15"]->isVisible() ); + + m_document->executeScript( "field = Doc.getField(\"0.15\");field.display=display.hidden;\ + field = Doc.getField(\"0.20\");field.display=display.visible;" ); + QCOMPARE( false, m_fields["0.15"]->isVisible() ); + QCOMPARE( true, m_fields["0.20"]->isVisible() ); + + m_document->executeScript( "field = Doc.getField(\"0.20\");field.display=display.hidden;\ + field = Doc.getField(\"0.0\");field.display=display.visible;" ); + QCOMPARE( false, m_fields["0.20"]->isVisible() ); + QCOMPARE( true, m_fields["0.0"]->isVisible() ); +} + +void delay() +{ + QTime dieTime= QTime::currentTime().addSecs( 2 ); + while (QTime::currentTime() < dieTime) + QCoreApplication::processEvents(QEventLoop::AllEvents, 100); +} + +void KJSFunctionsTest::testSetClearInterval() +{ + QString result = m_document->executeScript( "obj = new Object();obj.idx=0;\ + obj.inc=function(){obj.idx = obj.idx + 1;};\ + intv = app.setInterval('obj.inc()', 450);obj.idx;" ); + QCOMPARE( "0", result ); + delay(); + + result = m_document->executeScript( "app.clearInterval(intv);obj.idx;"); + QCOMPARE( "4", result ); +} + +void KJSFunctionsTest::testSetClearTimeOut() +{ + QString result = m_document->executeScript( "intv = app.setTimeOut('obj.inc()', 1);obj.idx;" ); + QCOMPARE( "4", result ); + delay(); + + result = m_document->executeScript( "obj.idx;" ); + QCOMPARE( "5", result ); + + result = m_document->executeScript( "intv = app.setTimeOut('obj.inc()', 2000);obj.idx;" ); + QCOMPARE( "5", result ); + + result = m_document->executeScript( "app.clearTimeOut(intv);obj.idx;" ); + QCOMPARE( "5", result ); + delay(); + QCOMPARE( "5", result ); +} + +void KJSFunctionsTest::testGetOCGs() +{ + QAbstractItemModel *model = m_document->layersModel(); + + QString result = m_document->executeScript( "var ocg = this.getOCGs(this.pageNum);\ + ocgName = ocg[0].name;" ); + QCOMPARE( model->data( model->index( 0, 0 ), Qt::DisplayRole ).toString() , result ); + + result = m_document->executeScript( "ocgName = ocg[1].name;" ); + QCOMPARE( model->data( model->index( 1, 0 ), Qt::DisplayRole ).toString() , result ); + + result = m_document->executeScript( "ocgName = ocg[2].name;" ); + QCOMPARE( model->data( model->index( 2, 0 ), Qt::DisplayRole ).toString() , result ); + + result = m_document->executeScript( "ocgName = ocg[3].name;" ); + QCOMPARE( model->data( model->index( 3, 0 ), Qt::DisplayRole ).toString() , result ); + + result = m_document->executeScript( "ocgName = ocg[0].initState;" ); + QCOMPARE( model->data( model->index( 0, 0 ), Qt::CheckStateRole ).toBool() ? "true" : "false" + , result ); + + result = m_document->executeScript( "ocgName = ocg[1].initState;" ); + QCOMPARE( model->data( model->index( 1, 0 ), Qt::CheckStateRole ).toBool() ? "true" : "false" + , result ); + + result = m_document->executeScript( "ocgName = ocg[2].initState;" ); + QCOMPARE( model->data( model->index( 2, 0 ), Qt::CheckStateRole ).toBool() ? "true" : "false" + , result ); + + result = m_document->executeScript( "ocgName = ocg[3].initState;" ); + QCOMPARE( model->data( model->index( 3, 0 ), Qt::CheckStateRole ).toBool() ? "true" : "false" + , result ); +} + +void KJSFunctionsTest::testOCGSetState() +{ + QAbstractItemModel *model = m_document->layersModel(); + + QString result = m_document->executeScript( "ocgName = ocg[0].state;" ); + QCOMPARE( model->data( model->index( 0, 0 ), Qt::CheckStateRole ).toBool() ? "true" : "false", result ); + + result = m_document->executeScript( "ocg[0].state = false;ocgName = ocg[0].state;"); + QCOMPARE( model->data( model->index( 0, 0 ), Qt::CheckStateRole ).toBool() ? "true" : "false", result ); + + result = m_document->executeScript( "ocgName = ocg[1].state;" ); + QCOMPARE( model->data( model->index( 1, 0 ), Qt::CheckStateRole ).toBool() ? "true" : "false", result ); + + result = m_document->executeScript( "ocg[1].state = false;ocgName = ocg[1].state;"); + QCOMPARE( model->data( model->index( 1, 0 ), Qt::CheckStateRole ).toBool() ? "true" : "false", result ); + + result = m_document->executeScript( "ocgName = ocg[2].state;" ); + QCOMPARE( model->data( model->index( 2, 0 ), Qt::CheckStateRole ).toBool() ? "true" : "false", result ); + + result = m_document->executeScript( "ocg[2].state = true;ocgName = ocg[2].state;"); + QCOMPARE( model->data( model->index( 2, 0 ), Qt::CheckStateRole ).toBool() ? "true" : "false", result ); + + result = m_document->executeScript( "ocgName = ocg[3].state;" ); + QCOMPARE( model->data( model->index( 3, 0 ), Qt::CheckStateRole ).toBool() ? "true" : "false", result ); + + result = m_document->executeScript( "ocg[3].state = true;ocgName = ocg[3].state;"); + QCOMPARE( model->data( model->index( 3, 0 ), Qt::CheckStateRole ).toBool() ? "true" : "false", result ); +} + +void KJSFunctionsTest::cleanupTestCase() +{ + m_document->closeDocument(); + delete m_document; +} + + +QTEST_MAIN( KJSFunctionsTest ) +#include "kjsfunctionstest.moc" diff --git a/core/document.h b/core/document.h --- a/core/document.h +++ b/core/document.h @@ -1038,6 +1038,13 @@ */ QByteArray requestSignedRevisionData( const Okular::SignatureInfo &info ); + /** + * Executes a JavaScript script from the setInterval function. + * + * @since 1.7 + */ + QString executeScript( QString function ); + Q_SIGNALS: /** * This signal is emitted whenever the document is about to close. diff --git a/core/document.cpp b/core/document.cpp --- a/core/document.cpp +++ b/core/document.cpp @@ -5112,6 +5112,13 @@ return data; } +QString Document::executeScript( QString function ) +{ + if( !d->m_scripter ) + d->m_scripter = new Scripter( d ); + return d->m_scripter->execute( JavaScript, function ); +} + void DocumentPrivate::requestDone( PixmapRequest * req ) { if ( !req ) diff --git a/core/script/executor_kjs.cpp b/core/script/executor_kjs.cpp --- a/core/script/executor_kjs.cpp +++ b/core/script/executor_kjs.cpp @@ -24,10 +24,12 @@ #include "kjs_app_p.h" #include "kjs_console_p.h" #include "kjs_data_p.h" +#include "kjs_display_p.h" #include "kjs_document_p.h" #include "kjs_event_p.h" #include "kjs_field_p.h" #include "kjs_fullscreen_p.h" +#include "kjs_ocg_p.h" #include "kjs_spell_p.h" #include "kjs_util_p.h" @@ -64,15 +66,19 @@ JSFullscreen::initType( ctx ); JSConsole::initType( ctx ); JSData::initType( ctx ); + JSDisplay::initType( ctx ); JSDocument::initType( ctx ); JSEvent::initType( ctx ); JSField::initType( ctx ); + JSOCG::initType( ctx ); JSSpell::initType( ctx ); JSUtil::initType( ctx ); m_docObject.setProperty( ctx, QStringLiteral("app"), JSApp::object( ctx, m_doc ) ); m_docObject.setProperty( ctx, QStringLiteral("console"), JSConsole::object( ctx ) ); m_docObject.setProperty( ctx, QStringLiteral("Doc"), m_docObject ); + m_docObject.setProperty( ctx, QStringLiteral("display"), JSDisplay::object( ctx ) ); + m_docObject.setProperty( ctx, QStringLiteral("OCG"), JSOCG::object( ctx ) ); m_docObject.setProperty( ctx, QStringLiteral("spell"), JSSpell::object( ctx ) ); m_docObject.setProperty( ctx, QStringLiteral("util"), JSUtil::object( ctx ) ); } @@ -84,6 +90,9 @@ ExecutorKJS::~ExecutorKJS() { + JSField::clearCachedFields(); + JSApp::clearCachedFields(); + JSOCG::clearCachedFields(); delete d; } @@ -106,6 +115,9 @@ KJSResult result = d->m_interpreter->evaluate( QStringLiteral("okular.js"), 1, script, &d->m_docObject ); + + resultValue = new QString( result.value().toString( ctx ) ); + if ( result.isException() || ctx->hasException() ) { qCDebug(OkularCoreDebug) << "JS exception" << result.errorMessage(); @@ -120,5 +132,9 @@ << event->type() << "value:" << event->value(); } } - JSField::clearCachedFields(); } + +QString ExecutorKJS::getResult() +{ + return *resultValue; +} \ No newline at end of file diff --git a/core/script/executor_kjs_p.h b/core/script/executor_kjs_p.h --- a/core/script/executor_kjs_p.h +++ b/core/script/executor_kjs_p.h @@ -28,10 +28,12 @@ ExecutorKJS &operator=(const ExecutorKJS &) = delete; void execute( const QString &script, Event *event ); + QString getResult(); private: friend class ExecutorKJSPrivate; ExecutorKJSPrivate* d; + QString* resultValue; }; } diff --git a/core/script/kjs_app.cpp b/core/script/kjs_app.cpp --- a/core/script/kjs_app.cpp +++ b/core/script/kjs_app.cpp @@ -21,11 +21,14 @@ #include #include "../document_p.h" +#include "../scripter.h" #include "kjs_fullscreen_p.h" using namespace Okular; static KJSPrototype *g_appProto; +typedef QHash< int, QTimer * > TimerCache; +Q_GLOBAL_STATIC( TimerCache, g_timerCache ) // the acrobat version we fake static const double fake_acroversion = 8.00; @@ -199,6 +202,77 @@ return KJSUndefined(); } +// app.setInterval() +static KJSObject appSetInterval( KJSContext *ctx, void *object, + const KJSArguments &arguments ) +{ + DocumentPrivate *doc = reinterpret_cast< DocumentPrivate * >( object ); + QString function = arguments.at( 0 ).toString( ctx ) + ';'; + int interval = arguments.at( 1 ).toInt32( ctx ); + + QTimer *timer = new QTimer(); + + QObject::connect( timer, &QTimer::timeout, [=](){ doc->m_parent->executeScript( function ); } ); + + timer->start( interval ); + + return JSApp::wrapTimer( ctx, timer ); +} + +// app.clearInterval() +static KJSObject appClearInterval( KJSContext *ctx, void *object, + const KJSArguments &arguments ) +{ + KJSObject timerObject = arguments.at( 0 ); + int timerId = timerObject.property( ctx, QStringLiteral( "timerID" ).toLatin1().toBase64() ).toInt32( ctx ); + QTimer *timer = g_timerCache->value( timerId ); + if( timer != nullptr ) + { + timer->stop(); + g_timerCache->remove( timerId ); + delete timer; + } + + return KJSUndefined(); +} + +// app.setTimeOut() +static KJSObject appSetTimeOut( KJSContext *ctx, void *object, + const KJSArguments &arguments ) +{ + DocumentPrivate *doc = reinterpret_cast< DocumentPrivate * >( object ); + QString function = arguments.at( 0 ).toString( ctx ) + ';'; + int interval = arguments.at( 1 ).toInt32( ctx ); + + QTimer *timer = new QTimer(); + timer->setSingleShot( true ); + + QObject::connect( timer, &QTimer::timeout, [=](){ doc->m_parent->executeScript( function ); } ); + + timer->start( interval ); + + return JSApp::wrapTimer( ctx, timer ); +} + +// app.clearTimeOut() +static KJSObject appClearTimeOut( KJSContext *ctx, void *object, + const KJSArguments &arguments ) +{ + KJSObject timerObject = arguments.at( 0 ); + int timerId = timerObject.property( ctx, QStringLiteral( "timerID" ).toLatin1().toBase64() ).toInt32( ctx ); + QTimer *timer = g_timerCache->value( timerId ); + + if( timer != nullptr ) + { + timer->stop(); + g_timerCache->remove( timerId ); + delete timer; + } + + return KJSUndefined(); +} + + void JSApp::initType( KJSContext *ctx ) { static bool initialized = false; @@ -223,9 +297,40 @@ g_appProto->defineFunction( ctx, QStringLiteral("getNthPlugInName"), appGetNthPlugInName ); g_appProto->defineFunction( ctx, QStringLiteral("goBack"), appGoBack ); g_appProto->defineFunction( ctx, QStringLiteral("goForward"), appGoForward ); + g_appProto->defineFunction( ctx, QStringLiteral("setInterval"), appSetInterval ); + g_appProto->defineFunction( ctx, QStringLiteral("clearInterval"), appClearInterval ); + g_appProto->defineFunction( ctx, QStringLiteral("setTimeOut"), appSetTimeOut ); + g_appProto->defineFunction( ctx, QStringLiteral("clearTimeOut"), appClearTimeOut ); } KJSObject JSApp::object( KJSContext *ctx, DocumentPrivate *doc ) { return g_appProto->constructObject( ctx, doc ); } + +KJSObject JSApp::wrapTimer( KJSContext *ctx, QTimer *timer) +{ + KJSObject timerObject = g_appProto->constructObject( ctx, timer ); + timerObject.setProperty( ctx, QStringLiteral("timerID").toLatin1().toBase64(), timer->timerId() ); + + g_timerCache->insert( timer->timerId(), timer ); + + return timerObject; +} + +void JSApp::clearCachedFields() +{ + if ( g_timerCache ) + { + for( auto it = g_timerCache->begin(); it != g_timerCache->end(); ++it ) + { + QTimer *timer = it.value(); + + if(timer == nullptr) + continue; + + delete timer; + } + g_timerCache->clear(); + } +} \ No newline at end of file diff --git a/core/script/kjs_app_p.h b/core/script/kjs_app_p.h --- a/core/script/kjs_app_p.h +++ b/core/script/kjs_app_p.h @@ -14,6 +14,8 @@ class KJSContext; class KJSObject; +#include + namespace Okular { class DocumentPrivate; @@ -23,8 +25,10 @@ public: static void initType( KJSContext *ctx ); static KJSObject object( KJSContext *ctx, DocumentPrivate *doc ); + static KJSObject wrapTimer( KJSContext *ctx, QTimer *timer ); + static void clearCachedFields(); }; } -#endif +#endif \ No newline at end of file diff --git a/core/script/kjs_display.cpp b/core/script/kjs_display.cpp new file mode 100644 --- /dev/null +++ b/core/script/kjs_display.cpp @@ -0,0 +1,49 @@ +/*************************************************************************** + * Copyright (C) 2019 by João Netto * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + ***************************************************************************/ + +#include "kjs_display_p.h" + +#include +#include + +#include + +#include + +using namespace Okular; + +std::unique_ptr < KJSPrototype > g_displayProto; + +// display.hidden +static KJSObject displayGetHidden( KJSContext *, void * ) +{ + return KJSBoolean( false ); +} + +// display.visible +static KJSObject displayGetVisible( KJSContext *, void * ) +{ + return KJSBoolean( true ); +} + +void JSDisplay::initType( KJSContext *ctx ) +{ + if ( g_displayProto ) + return; + + g_displayProto.reset(new KJSPrototype); + + g_displayProto->defineProperty( ctx, QStringLiteral("hidden"), displayGetHidden ); + g_displayProto->defineProperty( ctx, QStringLiteral("visible"), displayGetVisible ); +} + +KJSObject JSDisplay::object( KJSContext *ctx ) +{ + return g_displayProto->constructObject( ctx, nullptr ); +} \ No newline at end of file diff --git a/core/script/kjs_app_p.h b/core/script/kjs_display_p.h copy from core/script/kjs_app_p.h copy to core/script/kjs_display_p.h --- a/core/script/kjs_app_p.h +++ b/core/script/kjs_display_p.h @@ -1,30 +1,27 @@ /*************************************************************************** - * Copyright (C) 2008 by Pino Toscano * - * Copyright (C) 2008 by Harri Porten * + * Copyright (C) 2019 by João Netto * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * ***************************************************************************/ -#ifndef OKULAR_SCRIPT_KJS_APP_P_H -#define OKULAR_SCRIPT_KJS_APP_P_H +#ifndef OKULAR_SCRIPT_KJS_DISPLAY_P_H +#define OKULAR_SCRIPT_KJS_DISPLAY_P_H class KJSContext; class KJSObject; namespace Okular { -class DocumentPrivate; - -class JSApp +class JSDisplay { public: static void initType( KJSContext *ctx ); - static KJSObject object( KJSContext *ctx, DocumentPrivate *doc ); + static KJSObject object( KJSContext *ctx ); }; } -#endif +#endif \ No newline at end of file diff --git a/core/script/kjs_document.cpp b/core/script/kjs_document.cpp --- a/core/script/kjs_document.cpp +++ b/core/script/kjs_document.cpp @@ -24,6 +24,7 @@ #include "../form.h" #include "kjs_data_p.h" #include "kjs_field_p.h" +#include "kjs_ocg_p.h" using namespace Okular; @@ -129,6 +130,21 @@ return KJSBoolean( !isShell ); } +// Document.numFields +static KJSObject docGetNumFields( KJSContext *, void *object ) +{ + DocumentPrivate *doc = reinterpret_cast< DocumentPrivate* >( object ); + + unsigned int numFields = 0; + + for ( const Page * pIt : qAsConst(doc->m_pagesVector) ) + { + numFields += pIt->formFields().size(); + } + + return KJSNumber( numFields ); +} + static KJSObject docGetInfo( KJSContext *ctx, void *object ) { @@ -248,6 +264,56 @@ return KJSUndefined(); } +// Document.getNthFieldName +static KJSObject docGetNthFieldName( KJSContext *ctx, void *object, + const KJSArguments &arguments ) +{ + DocumentPrivate *doc = reinterpret_cast< DocumentPrivate* >( object ); + + int numField = arguments.at( 0 ).toInt32( ctx ); + + for ( const Page * pIt : qAsConst(doc->m_pagesVector) ) + { + const QLinkedList< Okular::FormField * > pageFields = pIt->formFields(); + + if(numField < pageFields.size()) + { + auto ffIt = pageFields.begin(); + ffIt += numField; + + return KJSString( (*ffIt)->name() ); + } + + numField -= pageFields.size(); + } + + return KJSUndefined(); +} + +static KJSObject docGetOCGs( KJSContext *ctx, void *object, + const KJSArguments &arguments ) +{ + DocumentPrivate *doc = reinterpret_cast< DocumentPrivate* >( object ); + + QAbstractItemModel * model = doc->m_parent->layersModel(); + + KJSArray array( ctx, model->rowCount() ); + + for(int i = 0;i < model->rowCount();++i){ + for(int j = 0;j < model->columnCount();++j){ + QModelIndex index = model->index( i, j ); + + KJSObject item = JSOCG::wrapOCGObject( ctx, model, i, j ); + item.setProperty( ctx, QStringLiteral("name"), model->data( index , Qt::DisplayRole ).toString() ); + item.setProperty( ctx, QStringLiteral("initState"), model->data( index , Qt::CheckStateRole ).toBool() ); + + array.setProperty( ctx, QString::number( i ), item ); + } + } + + return array; +} + void JSDocument::initType( KJSContext *ctx ) { assert( g_docProto ); @@ -266,6 +332,7 @@ g_docProto->defineProperty( ctx, QStringLiteral("permStatusReady"), docGetPermStatusReady ); g_docProto->defineProperty( ctx, QStringLiteral("dataObjects"), docGetDataObjects ); g_docProto->defineProperty( ctx, QStringLiteral("external"), docGetExternal ); + g_docProto->defineProperty( ctx, QStringLiteral("numFields"), docGetNumFields ); // info properties g_docProto->defineProperty( ctx, QStringLiteral("info"), docGetInfo ); @@ -281,11 +348,13 @@ g_docProto->defineFunction( ctx, QStringLiteral("getPageRotation"), docGetPageRotation ); g_docProto->defineFunction( ctx, QStringLiteral("gotoNamedDest"), docGotoNamedDest ); g_docProto->defineFunction( ctx, QStringLiteral("syncAnnotScan"), docSyncAnnotScan ); + g_docProto->defineFunction( ctx, QStringLiteral("getNthFieldName"), docGetNthFieldName ); + g_docProto->defineFunction( ctx, QStringLiteral("getOCGs"), docGetOCGs ); } KJSGlobalObject JSDocument::wrapDocument( DocumentPrivate *doc ) { if ( !g_docProto ) g_docProto = new KJSPrototype(); return g_docProto->constructGlobalObject( doc ); -} +} \ No newline at end of file diff --git a/core/script/kjs_field.cpp b/core/script/kjs_field.cpp --- a/core/script/kjs_field.cpp +++ b/core/script/kjs_field.cpp @@ -224,6 +224,24 @@ updateField( field ); } +// Field.display (getter) +static KJSObject fieldGetDisplay( KJSContext *, void *object ) +{ + const FormField *field = reinterpret_cast< FormField * >( object ); + return KJSBoolean( field->isVisible() ); +} + +// Field.display (setter) +static void fieldSetDisplay( KJSContext *context, void *object, KJSObject value ) +{ + FormField *field = reinterpret_cast< FormField * >( object ); + bool b = value.toBoolean( context ); + field->setVisible( b ); + + updateField( field ); +} + + void JSField::initType( KJSContext *ctx ) { static bool initialized = false; @@ -241,6 +259,7 @@ g_fieldProto->defineProperty( ctx, QStringLiteral("type"), fieldGetType ); g_fieldProto->defineProperty( ctx, QStringLiteral("value"), fieldGetValue, fieldSetValue ); g_fieldProto->defineProperty( ctx, QStringLiteral("hidden"), fieldGetHidden, fieldSetHidden ); + g_fieldProto->defineProperty( ctx, QStringLiteral("display"), fieldGetDisplay, fieldSetDisplay ); } KJSObject JSField::wrapField( KJSContext *ctx, FormField *field, Page *page ) @@ -258,4 +277,4 @@ { g_fieldCache->clear(); } -} +} \ No newline at end of file diff --git a/core/script/kjs_ocg.cpp b/core/script/kjs_ocg.cpp new file mode 100644 --- /dev/null +++ b/core/script/kjs_ocg.cpp @@ -0,0 +1,86 @@ +/*************************************************************************** + * Copyright (C) 2019 by João Netto * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + ***************************************************************************/ + +#include "kjs_ocg_p.h" + +#include +#include +#include + +#include +#include +#include +#include + +#include + +using namespace Okular; + +std::unique_ptr < KJSPrototype > g_OCGProto; + +typedef QHash< QPair< int, int > *, QAbstractItemModel* > OCGCache; +Q_GLOBAL_STATIC( OCGCache, g_OCGCache ) + +// OCG.state (getter) +static KJSObject OCGGetState( KJSContext *, void *object ) +{ + QPair< int, int > *pair = reinterpret_cast< QPair< int, int >* > ( object ); + QAbstractItemModel *model = g_OCGCache->value( pair ); + + QModelIndex index = model->index( pair->first, pair->second ); + + bool state = model->data( index, Qt::CheckStateRole ).toBool(); + + return KJSBoolean( state ); +} + +// OCG.state (setter) +static void OCGSetState( KJSContext* ctx, void* object, + KJSObject value ) +{ + QPair< int, int > *pair = reinterpret_cast< QPair< int, int >* > ( object ); + QAbstractItemModel *model = g_OCGCache->value( pair ); + + QModelIndex index = model->index( pair->first, pair->second ); + + bool state = value.toBoolean( ctx ); + + model->setData( index, QVariant( state ? Qt::Checked : Qt::Unchecked ), Qt::CheckStateRole ); +} + + +void JSOCG::initType( KJSContext *ctx ) +{ + if ( g_OCGProto ) + return; + + g_OCGProto.reset(new KJSPrototype); + + g_OCGProto->defineProperty( ctx, QStringLiteral("state"), OCGGetState, OCGSetState ); +} + +KJSObject JSOCG::object( KJSContext *ctx ) +{ + return g_OCGProto->constructObject( ctx, nullptr ); +} + +KJSObject JSOCG::wrapOCGObject( KJSContext *ctx, QAbstractItemModel *model, int i, int j ) +{ + QPair< int, int > *pair = new QPair< int ,int >( i, j ); + g_OCGCache->insert( pair, model ); + return g_OCGProto->constructObject( ctx, pair ); +} + +void JSOCG::clearCachedFields() +{ + if ( g_OCGCache.exists() ) + { + g_OCGCache->clear(); + } +} \ No newline at end of file diff --git a/core/script/kjs_app_p.h b/core/script/kjs_ocg_p.h copy from core/script/kjs_app_p.h copy to core/script/kjs_ocg_p.h --- a/core/script/kjs_app_p.h +++ b/core/script/kjs_ocg_p.h @@ -1,30 +1,30 @@ /*************************************************************************** - * Copyright (C) 2008 by Pino Toscano * - * Copyright (C) 2008 by Harri Porten * + * Copyright (C) 2019 by João Netto * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * ***************************************************************************/ -#ifndef OKULAR_SCRIPT_KJS_APP_P_H -#define OKULAR_SCRIPT_KJS_APP_P_H +#ifndef OKULAR_SCRIPT_KJS_OCG_P_H +#define OKULAR_SCRIPT_KJS_OCG_P_H class KJSContext; class KJSObject; +class QAbstractItemModel; namespace Okular { -class DocumentPrivate; - -class JSApp +class JSOCG { public: static void initType( KJSContext *ctx ); - static KJSObject object( KJSContext *ctx, DocumentPrivate *doc ); + static KJSObject object( KJSContext *ctx ); + static KJSObject wrapOCGObject( KJSContext *ctx, QAbstractItemModel *model, int i, int j ); + static void clearCachedFields(); }; } -#endif +#endif \ No newline at end of file diff --git a/core/scripter.cpp b/core/scripter.cpp --- a/core/scripter.cpp +++ b/core/scripter.cpp @@ -79,7 +79,8 @@ d->m_kjs.reset(new ExecutorKJS( d->m_doc )); } d->m_kjs->execute( builtInScript + script, d->m_event ); - break; + + return d->m_kjs->getResult(); } #endif return QString(); diff --git a/ui/formwidgets.cpp b/ui/formwidgets.cpp --- a/ui/formwidgets.cpp +++ b/ui/formwidgets.cpp @@ -375,7 +375,7 @@ { return; } - setVisibility( form->isVisible() /*&& !form->isReadOnly()*/ ); + setVisibility( form->isVisible() && m_controller->shouldFormWidgetBeShown( form ) ); m_widget->setEnabled( !form->isReadOnly() ); } @@ -385,6 +385,12 @@ : QPushButton( parent ), FormWidgetIface( this, button ) { setText( button->caption() ); + + if( button->caption().isEmpty() ) + { + setFlat( true ); + } + setVisible( button->isVisible() ); setCursor( Qt::ArrowCursor ); } diff --git a/ui/pageview.cpp b/ui/pageview.cpp --- a/ui/pageview.cpp +++ b/ui/pageview.cpp @@ -1473,6 +1473,18 @@ Q_FOREACH ( VideoWidget *videoWidget, item->videoWidgets() ) videoWidget->pageLeft(); } + + // On close, run the widget scripts, needed for running animated PDF + const Okular::Page *page = d->document->page( previous ); + foreach( Okular::Annotation *annotation, page->annotations() ) + { + if ( annotation->subType() == Okular::Annotation::AWidget ) + { + Okular::WidgetAnnotation *widgetAnnotation = static_cast( annotation ); + d->document->processAction( widgetAnnotation->additionalAction( Okular::Annotation::PageClosing ) ); + } + } + } if ( current != -1 ) @@ -1487,6 +1499,17 @@ // update zoom text and factor if in a ZoomFit/* zoom mode if ( d->zoomMode != ZoomFixed ) updateZoomText(); + + // Opening any widget scripts, needed for running animated PDF + const Okular::Page *page = d->document->page( current ); + foreach( Okular::Annotation *annotation, page->annotations() ) + { + if ( annotation->subType() == Okular::Annotation::AWidget ) + { + Okular::WidgetAnnotation *widgetAnnotation = static_cast( annotation ); + d->document->processAction( widgetAnnotation->additionalAction( Okular::Annotation::PageOpening ) ); + } + } } } @@ -2571,7 +2594,7 @@ { // handle click over a image } -/* Enrico and me have decided this is not worth the trouble it generates +/* Enrico and me have decided this is not worth the trouble it generates else { // if not on a rect, the click selects the page @@ -3754,20 +3777,17 @@ void PageView::scrollPosIntoView( const QPoint & pos ) { - // this number slows the speed of the page by its value, chosen not to be too fast or too slow, the actual speed is determined from the mouse position, not critical - const int damping=6; - - if (pos.x() < horizontalScrollBar()->value()) d->dragScrollVector.setX((pos.x() - horizontalScrollBar()->value())/damping); - else if (horizontalScrollBar()->value() + viewport()->width() < pos.x()) d->dragScrollVector.setX((pos.x() - horizontalScrollBar()->value() - viewport()->width())/damping); + if (pos.x() < horizontalScrollBar()->value()) d->dragScrollVector.setX(pos.x() - horizontalScrollBar()->value()); + else if (horizontalScrollBar()->value() + viewport()->width() < pos.x()) d->dragScrollVector.setX(pos.x() - horizontalScrollBar()->value() - viewport()->width()); else d->dragScrollVector.setX(0); - if (pos.y() < verticalScrollBar()->value()) d->dragScrollVector.setY((pos.y() - verticalScrollBar()->value())/damping); - else if (verticalScrollBar()->value() + viewport()->height() < pos.y()) d->dragScrollVector.setY((pos.y() - verticalScrollBar()->value() - viewport()->height())/damping); + if (pos.y() < verticalScrollBar()->value()) d->dragScrollVector.setY(pos.y() - verticalScrollBar()->value()); + else if (verticalScrollBar()->value() + viewport()->height() < pos.y()) d->dragScrollVector.setY(pos.y() - verticalScrollBar()->value() - viewport()->height()); else d->dragScrollVector.setY(0); if (d->dragScrollVector != QPoint(0, 0)) { - if (!d->dragScrollTimer.isActive()) d->dragScrollTimer.start(1000/60); //60 fps + if (!d->dragScrollTimer.isActive()) d->dragScrollTimer.start(100); } else d->dragScrollTimer.stop(); } @@ -5580,4 +5600,4 @@ #include "moc_pageview.cpp" -/* kate: replace-tabs on; indent-width 4; */ +/* kate: replace-tabs on; indent-width 4; */ \ No newline at end of file