diff --git a/CMakeLists.txt b/CMakeLists.txt --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -304,6 +304,7 @@ 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 diff --git a/core/document.h b/core/document.h --- a/core/document.h +++ b/core/document.h @@ -1040,6 +1040,8 @@ */ QByteArray requestSignedRevisionData( const Okular::SignatureInfo &info ); + void executeScript(); + 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 @@ -4075,11 +4075,9 @@ { if ( !action ) return; - // Don't execute next actions if the action itself caused the closing of the document bool executeNextActions = true; auto connectionId = connect( this, &Document::aboutToClose, [&executeNextActions] { executeNextActions = false; } ); - switch( action->actionType() ) { case Action::Goto: { @@ -5112,6 +5110,12 @@ return data; } +void Document::executeScript() +{ + QString function = sender()->property( "function" ).toString(); + d->m_scripter->execute( JavaScript, function ); +} + void DocumentPrivate::requestDone( PixmapRequest * req ) { if ( !req ) diff --git a/core/page.cpp b/core/page.cpp --- a/core/page.cpp +++ b/core/page.cpp @@ -520,7 +520,6 @@ return d->m_closingAction; break; } - return nullptr; } diff --git a/core/script/builtin.js b/core/script/builtin.js --- a/core/script/builtin.js +++ b/core/script/builtin.js @@ -59,4 +59,4 @@ } event.value = ret; -} +} \ No newline at end of file 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,6 +24,7 @@ #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" @@ -64,6 +65,7 @@ JSFullscreen::initType( ctx ); JSConsole::initType( ctx ); JSData::initType( ctx ); + JSDisplay::initType( ctx ); JSDocument::initType( ctx ); JSEvent::initType( ctx ); JSField::initType( ctx ); @@ -73,6 +75,7 @@ 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("spell"), JSSpell::object( ctx ) ); m_docObject.setProperty( ctx, QStringLiteral("util"), JSUtil::object( ctx ) ); } @@ -84,6 +87,8 @@ ExecutorKJS::~ExecutorKJS() { + JSField::clearCachedFields(); + JSApp::clearCachedFields(); delete d; } @@ -120,5 +125,4 @@ << event->type() << "value:" << event->value(); } } - JSField::clearCachedFields(); } 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,41 @@ return KJSUndefined(); } +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(); + + timer->setProperty( "function", function ); + + QObject::connect( timer, &QTimer::timeout, doc->m_parent, &Document::executeScript ); + + timer->start( interval ); + + return JSApp::wrapTimer( ctx, timer ); +} + +static KJSObject appClearInterval( KJSContext *ctx, void *object, + const KJSArguments &arguments ) +{ + KJSObject timerObject = arguments.at( 0 ); + int timerId = timerObject.property( ctx, "timerID" ).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 +261,29 @@ 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 ); } 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"), timer->timerId() ); + + g_timerCache->insert( timer->timerId(), timer ); + + return timerObject; +} + +void JSApp::clearCachedFields() +{ + if ( g_timerCache.exists() ) + { + g_timerCache->clear(); + } +} 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,6 +25,8 @@ public: static void initType( KJSContext *ctx ); static KJSObject object( KJSContext *ctx, DocumentPrivate *doc ); + static KJSObject wrapTimer( KJSContext *ctx, QTimer *timer ); + static void clearCachedFields(); }; } 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 ); +} 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,28 +1,25 @@ /*************************************************************************** - * 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 ); }; } 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 @@ -129,6 +129,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 +263,32 @@ 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(); +} + void JSDocument::initType( KJSContext *ctx ) { assert( g_docProto ); @@ -266,6 +307,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,6 +323,7 @@ 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 ); } KJSGlobalObject JSDocument::wrapDocument( DocumentPrivate *doc ) 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 ) diff --git a/core/scripter.cpp b/core/scripter.cpp --- a/core/scripter.cpp +++ b/core/scripter.cpp @@ -78,7 +78,7 @@ { d->m_kjs.reset(new ExecutorKJS( d->m_doc )); } - d->m_kjs->execute( builtInScript + script, d->m_event ); + d->m_kjs->execute( builtInScript + script , d->m_event ); break; } #endif 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 ) ); + } + } } }