diff --git a/examples/KReportExampleData.h b/examples/KReportExampleData.h --- a/examples/KReportExampleData.h +++ b/examples/KReportExampleData.h @@ -44,6 +44,9 @@ virtual bool close(); virtual bool open(); + virtual QStringList scriptList() const; + virtual QString scriptCode(const QString &script) const; + private: struct Data { diff --git a/examples/KReportExampleData.cpp b/examples/KReportExampleData.cpp --- a/examples/KReportExampleData.cpp +++ b/examples/KReportExampleData.cpp @@ -153,3 +153,45 @@ { return true; } + +QStringList KReportExampleData::scriptList() const +{ + QStringList scripts; + + scripts << "example"; + + return scripts; +} + +QString KReportExampleData::scriptCode(const QString &script) const +{ + if (script != "example") + return QString(); + + QString scriptcode; + + scriptcode = "" + "function detail(){\n" + " var count = 0;" + " this.OnRender = function() {\n" + " count++;\n" + " debug.print(\"printing this from the javascript engine\");\n" + " if (count % 2 == 0) {\n" + " example_report.section_detail.setBackgroundColor(\"#ffffff\");\n" + " } else {\n" + " example_report.section_detail.setBackgroundColor(\"#dddddd\");\n" + " }\n" + " example_report.section_detail.objectByName(\"label1\").setCaption(\"Record: \" + count);\n" + " }\n" + "}\n" + "\n" + "function report(){\n" + " this.OnOpen = function() {\n" + " debug.print(\"report on-open event\");\n" + " }\n" + "}\n" + "example_report.section_detail.initialize(new detail());\n" + "example_report.initialize(new report());\n"; + + return scriptcode; +} diff --git a/examples/designerwindow.cpp b/examples/designerwindow.cpp --- a/examples/designerwindow.cpp +++ b/examples/designerwindow.cpp @@ -68,8 +68,6 @@ connect(m_reportDesigner, SIGNAL(dirty()), this, SLOT(designDirty())); m_reportDesigner->setReportData(new KReportExampleData()); - - } DesignerWindow::~DesignerWindow() diff --git a/examples/window.cpp b/examples/window.cpp --- a/examples/window.cpp +++ b/examples/window.cpp @@ -96,6 +96,7 @@ } preRenderer.setSourceData(m_testData); + preRenderer.setName("example_report"); m_reportView->setDocument(preRenderer.generate()); m_reportView->moveToFirstPage(); diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -3,8 +3,8 @@ configure_file(config-kreport.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-kreport.h) if(KREPORT_SCRIPTING) - find_package(KF5 5.7.0 REQUIRED Kross) - set(SCRIPTING_LIBS KF5::KrossCore KF5::KrossUi) + find_package(Qt5Qml REQUIRED) + set(SCRIPTING_LIBS Qt5::Qml) endif() add_subdirectory(plugins) @@ -266,6 +266,7 @@ HEADER_NAMES krscripthandler krscriptdraw + krscriptconstants ) ecm_generate_headers(kreport_FORWARDING_HEADERS diff --git a/src/common/KoReportData.h b/src/common/KoReportData.h --- a/src/common/KoReportData.h +++ b/src/common/KoReportData.h @@ -96,12 +96,12 @@ //! @todo These are probably eligable to be moved into a new class #ifdef KREPORT_SCRIPTING - //! Allow the reportdata implementation to return a list of possible scripts for a given language - virtual QStringList scriptList(const QString& language) const; + //! Allow the reportdata implementation to return a list of possible scripts + virtual QStringList scriptList() const; //! Allow the reportdata implementation to return some script code based on a specific script name - //! and a language, as set in the report - virtual QString scriptCode(const QString& script, const QString& language) const; + //! as set in the report + virtual QString scriptCode(const QString& script) const; #endif //! Return a list of data sources possible for advanced controls diff --git a/src/common/KoReportData.cpp b/src/common/KoReportData.cpp --- a/src/common/KoReportData.cpp +++ b/src/common/KoReportData.cpp @@ -16,7 +16,7 @@ */ #include "KoReportData.h" - +#include #include KoReportData::~KoReportData() @@ -51,16 +51,14 @@ } #ifdef KREPORT_SCRIPTING -QStringList KoReportData::scriptList(const QString &language) const +QStringList KoReportData::scriptList() const { - Q_UNUSED(language); return QStringList(); } -QString KoReportData::scriptCode(const QString &script, const QString &language) const +QString KoReportData::scriptCode(const QString &script) const { Q_UNUSED(script); - Q_UNUSED(language); return QString(); } #endif diff --git a/src/common/renderobjects.cpp b/src/common/renderobjects.cpp --- a/src/common/renderobjects.cpp +++ b/src/common/renderobjects.cpp @@ -43,7 +43,10 @@ OROPage* ORODocument::page(int pnum) { - return m_pages.at(pnum); + if (pnum >= 0 && pnum < m_pages.count()) { + return m_pages.at(pnum); + } + return Q_NULLPTR; } void ORODocument::addPage(OROPage* p) diff --git a/src/items/check/KoReportScriptCheck.cpp b/src/items/check/KoReportScriptCheck.cpp --- a/src/items/check/KoReportScriptCheck.cpp +++ b/src/items/check/KoReportScriptCheck.cpp @@ -17,6 +17,7 @@ */ #include "KoReportScriptCheck.h" +#include namespace Scripting { diff --git a/src/items/image/krscriptimage.cpp b/src/items/image/krscriptimage.cpp --- a/src/items/image/krscriptimage.cpp +++ b/src/items/image/krscriptimage.cpp @@ -18,6 +18,8 @@ #include "krscriptimage.h" #include "KoReportItemImage.h" +#include + namespace Scripting { diff --git a/src/renderer/KoReportPreRenderer.cpp b/src/renderer/KoReportPreRenderer.cpp --- a/src/renderer/KoReportPreRenderer.cpp +++ b/src/renderer/KoReportPreRenderer.cpp @@ -49,16 +49,18 @@ m_leftMargin = m_rightMargin = 0.0; m_pageCounter = 0; m_maxHeight = m_maxWidth = 0.0; - m_kodata = new KReportOneRecordData(); + m_oneRecord = new KReportOneRecordData(); + m_kodata = m_oneRecord; + asyncManager = new KoReportASyncItemManager(this); connect(asyncManager, SIGNAL(finished()), this, SLOT(asyncItemsFinished())); } KoReportPreRendererPrivate::~KoReportPreRendererPrivate() { delete m_reportData; - delete m_kodata; + delete m_oneRecord; m_postProcText.clear(); } @@ -503,10 +505,12 @@ if (i.key() == QLatin1String("field")) QObject::connect(d->m_scriptHandler, SIGNAL(groupChanged(QString)), i.value(), SLOT(setWhere(QString))); } + //execute the script, if it fails, abort and return the empty document + if (!d->m_scriptHandler->trigger()) { + d->m_scriptHandler->displayErrors(); + return d->m_document; + } } - - //execute the script - d->m_scriptHandler->trigger(); #endif d->createNewPage(); @@ -618,9 +622,6 @@ void KoReportPreRenderer::setSourceData(KoReportData *data) { if (data) { - if (d->m_kodata) { - delete d->m_kodata; - } d->m_kodata = data; } } diff --git a/src/renderer/KoReportPreRenderer_p.h b/src/renderer/KoReportPreRenderer_p.h --- a/src/renderer/KoReportPreRenderer_p.h +++ b/src/renderer/KoReportPreRenderer_p.h @@ -26,6 +26,8 @@ #include +class KReportOneRecordData; + /*! This class is the private class that houses all the internal variables so we can provide a cleaner interface to the user without presenting to them things that they don't need to see @@ -54,6 +56,8 @@ int m_recordCount; KoReportData* m_kodata; + KReportOneRecordData *m_oneRecord; + QList m_postProcText; #ifdef KREPORT_SCRIPTING diff --git a/src/renderer/KoReportScreenRenderer.cpp b/src/renderer/KoReportScreenRenderer.cpp --- a/src/renderer/KoReportScreenRenderer.cpp +++ b/src/renderer/KoReportScreenRenderer.cpp @@ -46,6 +46,10 @@ OROPage *p = document->page(page); + if (!p) { + return false; + } + // Render Page Objects for (int i = 0; i < p->primitives(); i++) { OROPrimitive *prim = p->primitive(i); diff --git a/src/renderer/scripting/krscripthandler.h b/src/renderer/scripting/krscripthandler.h --- a/src/renderer/scripting/krscripthandler.h +++ b/src/renderer/scripting/krscripthandler.h @@ -26,12 +26,12 @@ #ifdef KREPORT_SCRIPTING #include "krscriptconstants.h" #include "KoReportData.h" - -//! @todo Scripting +#include class KRScriptDebug; class KRScriptDraw; class KRSectionData; +class QJSEngine; namespace Scripting { @@ -47,8 +47,8 @@ QVariant evaluate(const QString&); void displayErrors(); - void registerScriptObject(QObject*, const QString&); - void trigger(); + QJSValue registerScriptObject(QObject*, const QString&); + bool trigger(); public Q_SLOTS: @@ -78,7 +78,8 @@ QString m_source; KoReportReportData *m_reportData; - //! @todo IKross::Action* m_action; + QJSEngine* m_engine; + QJSValue m_scriptValue; QMap m_groups; QMap m_sectionMap; diff --git a/src/renderer/scripting/krscripthandler.cpp b/src/renderer/scripting/krscripthandler.cpp --- a/src/renderer/scripting/krscripthandler.cpp +++ b/src/renderer/scripting/krscripthandler.cpp @@ -23,98 +23,86 @@ #include "krscriptreport.h" #include "krscriptdraw.h" #include "krscriptconstants.h" - #include "krsectiondata.h" #include "KoReportItemBase.h" #include "krreportdata.h" #include "krdetailsectiondata.h" #include "renderobjects.h" - -#include - -//! @todo #include - #include "kreport_debug.h" +#include +#include +#include + KRScriptHandler::KRScriptHandler(const KoReportData* kodata, KoReportReportData* d) { m_reportData = d; m_koreportData = kodata; - m_action = 0; + m_engine = 0; m_constants = 0; m_debug = 0; m_draw = 0; - // Create the Kross::Action instance . - m_action = new Kross::Action(this, QLatin1String("ReportScript")); - - /*! @todo The kjsembed interpreter is buggy, and crashes on expressions - involving QVariant, which happens often. So, as a workaground - if the qtscript interpreter is available, load that instead. - we do this instead of hiding the javascript interpreter incase a - user has a database using that interpreter. */ - - QStringList interpreters = Kross::Manager::self().interpreters(); - QString interpreter = d->interpreter(); - - if (interpreter.toLower() == QLatin1String("javascript") && interpreters.contains(QLatin1String("qtscript"))) { - interpreter = QLatin1String("qtscript"); - } - - m_action->setInterpreter(interpreter); + // Create the script engine instance . + m_engine = new QJSEngine(this); //Add constants object m_constants = new KRScriptConstants(); - m_action->addObject(m_constants, QLatin1String("constants")); + registerScriptObject(m_constants, QLatin1String("constants")); //A simple debug function to allow printing from functions m_debug = new KRScriptDebug(); - m_action->addObject(m_debug, QLatin1String("debug")); + registerScriptObject(m_debug, QLatin1String("debug")); //A simple drawing object m_draw = new KRScriptDraw(); - m_action->addObject(m_draw, "draw"); + registerScriptObject(m_draw, QLatin1String("draw")); //Add a general report object m_report = new Scripting::Report(m_reportData); + QJSValue r = registerScriptObject(m_report, m_reportData->name()); //Add the sections QList secs = m_reportData->sections(); foreach(KRSectionData *sec, secs) { m_sectionMap[sec] = new Scripting::Section(sec); m_sectionMap[sec]->setParent(m_report); m_sectionMap[sec]->setObjectName(sec->name().replace(QLatin1Char('-'), QLatin1Char('_')) .remove(QLatin1String("report:"))); - //kreportDebug() << "Added" << m_sectionMap[sec]->objectName() << "to report" << m_reportData->name(); + QJSValue s = m_engine->newQObject(m_sectionMap[sec]); + r.setProperty(m_sectionMap[sec]->objectName(), s); + kreportDebug() << "Added" << m_sectionMap[sec]->objectName() << "to report" << m_reportData->name(); } - m_action->addObject(m_report, m_reportData->name()); - //kreportDebug() << "Report name is" << m_reportData->name(); - QString code = m_koreportData->scriptCode(m_reportData->script(), m_reportData->interpreter()); - m_action->setCode(code.toUtf8()); + kreportDebug() << "Report name is" << m_reportData->name(); } -void KRScriptHandler::trigger() +bool KRScriptHandler::trigger() { - //kreportDebug() << m_action->code(); - m_action->trigger(); - if (m_action->hadError()) { - KMessageBox::error(0, m_action->errorMessage()); - } else { - kreportDebug() << "Function Names:" << m_action->functionNames(); + QString code = m_koreportData->scriptCode(m_reportData->script()); + kreportDebug() << code; + + if (code.isEmpty()) { + return true; } + + m_scriptValue = m_engine->evaluate(code, m_reportData->script()); + + if (m_scriptValue.isError()) { + return false; + }/*TODO else { + kreportDebug() << "Function Names:" << m_engine->functionNames(); + }*/ m_report->eventOnOpen(); + return true; } KRScriptHandler::~KRScriptHandler() { delete m_report; - delete m_constants; - delete m_debug; - delete m_draw; - delete m_action; + delete m_engine; } void KRScriptHandler::newPage() @@ -152,38 +140,42 @@ QVariant KRScriptHandler::evaluate(const QString &code) { - if (!m_action->hadError()) { - QVariant result = m_action->evaluate(code.toUtf8()); - return QString::fromUtf8(result.toByteArray()); - } else { - return QVariant(); + if (!m_scriptValue.isError()) { + QJSValue result = m_engine->evaluate(code); + if (!result.isError()) { + return result.toVariant(); + } else { + QMessageBox::warning(0, tr("Script Error"), m_scriptValue.toString()); + } } + return QVariant(); } void KRScriptHandler::displayErrors() { - if (m_action->hadError()) { - KMessageBox::error(0, m_action->errorMessage()); + if (m_scriptValue.isError()) { + QMessageBox::warning(0, tr("Script Error"), m_scriptValue.toString()); } } QString KRScriptHandler::where() { QString w; QMap::const_iterator i = m_groups.constBegin(); while (i != m_groups.constEnd()) { - w += QLatin1Char('(') + i.key() + QLatin1String(" = '") + i.value().toString() + QLatin1Char("') AND "); + w += QLatin1Char('(') + i.key() + QLatin1String(" = '") + i.value().toString() + QLatin1String("') AND "); ++i; } w.chop(4); //kreportDebug() << w; return w; } -void KRScriptHandler::registerScriptObject(QObject* obj, const QString& name) +QJSValue KRScriptHandler::registerScriptObject(QObject* obj, const QString& name) { - //kreportDebug(); - if (m_action) - m_action->addObject(obj, name); + QJSValue val; + val = m_engine->newQObject(obj); + m_engine->globalObject().setProperty(name, val); + return val; } diff --git a/src/renderer/scripting/krscriptline.cpp b/src/renderer/scripting/krscriptline.cpp --- a/src/renderer/scripting/krscriptline.cpp +++ b/src/renderer/scripting/krscriptline.cpp @@ -18,6 +18,8 @@ #include "krscriptline.h" #include "KoReportItemLine.h" +#include + namespace Scripting { diff --git a/src/renderer/scripting/krscriptreport.h b/src/renderer/scripting/krscriptreport.h --- a/src/renderer/scripting/krscriptreport.h +++ b/src/renderer/scripting/krscriptreport.h @@ -18,8 +18,7 @@ #define SCRIPTINGKRSCRIPTREPORT_H #include - -#include +#include class KoReportReportData; @@ -44,14 +43,14 @@ QObject* sectionByName(const QString &); - void initialize(Kross::Object::Ptr); + void initialize(const QJSValue &val); void eventOnOpen(); void eventOnComplete(); void eventOnNewPage(); private: KoReportReportData *m_reportData; - Kross::Object::Ptr m_scriptObject; + QJSValue m_scriptObject; }; } diff --git a/src/renderer/scripting/krscriptreport.cpp b/src/renderer/scripting/krscriptreport.cpp --- a/src/renderer/scripting/krscriptreport.cpp +++ b/src/renderer/scripting/krscriptreport.cpp @@ -23,6 +23,7 @@ #include "krscriptline.h" #include "krscriptsection.h" #include "KoReportItemLine.h" +#include "kreport_debug.h" namespace Scripting { @@ -91,27 +92,27 @@ } } -void Report::initialize(Kross::Object::Ptr ptr) +void Report::initialize(const QJSValue &val) { - m_scriptObject = ptr; + m_scriptObject = val; } void Report::eventOnOpen() { - if (m_scriptObject) - m_scriptObject->callMethod(QLatin1String("OnOpen")); + if (m_scriptObject.isObject() && m_scriptObject.hasProperty(QLatin1String("OnOpen"))) + m_scriptObject.property(QLatin1String("OnOpen")).call(); } void Report::eventOnComplete() { - if (m_scriptObject) - m_scriptObject->callMethod(QLatin1String("OnComplete")); + if (m_scriptObject.isObject() && m_scriptObject.hasProperty(QLatin1String("OnComlete"))) + m_scriptObject.property(QLatin1String("OnComplete")).call(); } void Report::eventOnNewPage() { - if (m_scriptObject) - m_scriptObject->callMethod(QLatin1String("OnNewPage")); + if (m_scriptObject.isObject() && m_scriptObject.hasProperty(QLatin1String("OnNewPage"))) + m_scriptObject.property(QLatin1String("OnNewPage")).call(); } } diff --git a/src/renderer/scripting/krscriptsection.h b/src/renderer/scripting/krscriptsection.h --- a/src/renderer/scripting/krscriptsection.h +++ b/src/renderer/scripting/krscriptsection.h @@ -18,8 +18,7 @@ #define KRSCRIPTSECTION_H #include - -#include +#include class KRSectionData; @@ -56,12 +55,12 @@ /**Returns an object in the section, by name*/ QObject* objectByName(const QString&); - void initialize(Kross::Object::Ptr); + void initialize(const QJSValue &s); void eventOnRender(); private: KRSectionData *m_section; - Kross::Object::Ptr m_scriptObject; + QJSValue m_scriptObject; }; } #endif diff --git a/src/renderer/scripting/krscriptsection.cpp b/src/renderer/scripting/krscriptsection.cpp --- a/src/renderer/scripting/krscriptsection.cpp +++ b/src/renderer/scripting/krscriptsection.cpp @@ -21,8 +21,8 @@ #include "KoReportPluginManager.h" #include "KoReportPluginInterface.h" #include "KoReportItemLine.h" - #include "kreport_debug.h" +#include "krsectiondata.h" namespace Scripting { @@ -95,14 +95,14 @@ return 0; } -void Section::initialize(Kross::Object::Ptr p) +void Section::initialize(const QJSValue &s) { - m_scriptObject = p; + m_scriptObject = s; } void Section::eventOnRender() { - if (m_scriptObject) - m_scriptObject->callMethod(QLatin1String("OnRender")); + if (m_scriptObject.isObject() && m_scriptObject.hasProperty(QLatin1String("OnRender"))) + m_scriptObject.property(QLatin1String("OnRender")).call(); } } diff --git a/src/wrtembed/KoReportDesigner.cpp b/src/wrtembed/KoReportDesigner.cpp --- a/src/wrtembed/KoReportDesigner.cpp +++ b/src/wrtembed/KoReportDesigner.cpp @@ -33,10 +33,7 @@ #include "common/krutils.h" #include "common/KoReportPluginInterface.h" #include "common/KoReportPluginManager.h" - -#ifdef KREPORT_SCRIPTING -#include -#endif +#include "kreport_debug.h" #include #include @@ -50,7 +47,7 @@ #include #include #include -#include "kreport_debug.h" +#include //! Also add public method for runtime? const char ns[] = "http://kexi-project.org/report/2.0"; @@ -180,7 +177,6 @@ KProperty *gridSnap; KProperty *labelType; #ifdef KREPORT_SCRIPTING - KProperty *interpreter; KProperty *script; #endif @@ -201,6 +197,9 @@ bool modified; // true if this document has been modified, false otherwise + QString originalInterpreter; //Value of the script interpreter at load time + QString originalScript; //Value of the script at load time + KoReportData *kordata; }; @@ -287,8 +286,16 @@ setReportTitle(it.firstChild().nodeValue()); #ifdef KREPORT_SCRIPTING } else if (n == QLatin1String("report:script")) { - d->interpreter->setValue(it.toElement().attribute(QLatin1String("report:script-interpreter"))); - d->script->setValue(it.firstChild().nodeValue()); + d->originalInterpreter = it.toElement().attribute(QLatin1String("report:script-interpreter")); + d->originalScript = it.firstChild().nodeValue(); + d->script->setValue(d->originalScript); + + if (d->originalInterpreter != QLatin1String("javascript") || d->originalInterpreter != QLatin1String("qtscript")) { + QString msg = tr("This report is using a script type other than \"javascript\". " + "To prevent loosing the script, the type of script will not be changed unless the script is changed. " + "Please be aware that the script will not work unless it is changed to a \"javascript\" script."); + QMessageBox::warning(this, tr("Unsupported script type"), msg); + } #endif } else if (n == QLatin1String("report:grid")) { d->showGrid->setValue(it.toElement().attribute(QLatin1String("report:grid-visible"), QString::number(1)).toInt() != 0); @@ -357,6 +364,7 @@ QDomElement KoReportDesigner::document() const { QDomDocument doc; + QString saveInterpreter; QDomElement content = doc.createElement(QLatin1String("report:content")); content.setAttribute(QLatin1String("xmlns:report"), QLatin1String(ns)); @@ -369,9 +377,18 @@ content.appendChild(propertyToElement(&doc, d->title)); #ifdef KREPORT_SCRIPTING - QDomElement scr = propertyToElement(&doc, d->script); - KRUtils::addPropertyAsAttribute(&scr, d->interpreter); - content.appendChild(scr); + if (!d->script->value().toString().isEmpty()) { + if (d->script->value().toString() != d->originalScript || d->originalInterpreter == QLatin1String("qtscript")) { + //The script has changed so force interpreter to 'javascript'. Also set if was using qtscript + saveInterpreter = QLatin1String("javascript"); + } else { + saveInterpreter = d->originalInterpreter; + } + + QDomElement scr = propertyToElement(&doc, d->script); + scr.setAttribute(QLatin1String("report:interpreter"), saveInterpreter); + content.appendChild(scr); + } #endif QDomElement grd = doc.createElement(QLatin1String("report:grid")); @@ -755,11 +772,7 @@ d->set->addProperty(d->bottomMargin); #ifdef KREPORT_SCRIPTING - keys = Kross::Manager::self().interpreters(); - d->interpreter = new KProperty("script-interpreter", keys, keys, keys.value(0), tr("Script Interpreter")); - d->set->addProperty(d->interpreter); - d->script = new KProperty("script", keys, keys, QString(), tr("Object Script")); - d->set->addProperty(d->interpreter); + d->script = new KProperty("script", QStringList(), QStringList(), QString(), tr("Object Script")); d->set->addProperty(d->script); #endif @@ -791,7 +804,8 @@ { #ifdef KREPORT_SCRIPTING if (d->kordata) { - QStringList sl = d->kordata->scriptList(d->interpreter->value().toString()); + QStringList sl = d->kordata->scriptList(); + sl.prepend(QLatin1String("")); d->script->setListData(sl, sl); } changeSet(d->set);