/*************************************************************************** * This file is part of the Lime Report project * * Copyright (C) 2021 by Alexander Arin * * arin_a@bk.ru * * * ** GNU General Public License Usage ** * * * This library 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 3 of the License, or * * (at your option) any later version. * * You should have received a copy of the GNU General Public License * * along with this program. If not, see . * * * ** GNU Lesser General Public License ** * * * This library is free software: you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License as * * published by the Free Software Foundation, either version 3 of the * * License, or (at your option) any later version. * * You should have received a copy of the GNU Lesser General Public * * License along with this library. * * If not, see . * * * * 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 General Public License for more details. * ****************************************************************************/ #ifndef LRSCRIPTENGINEMANAGER_H #define LRSCRIPTENGINEMANAGER_H #ifdef USE_QTSCRIPTENGINE #include #include #endif #include #include #include #include #include #include #include //#include #ifdef HAVE_UI_LOADER #include #endif #include "base/lrsingleton.h" #include "lrglobal.h" #include "lrscriptenginemanagerintf.h" #include "lrcallbackdatasourceintf.h" #include "lrcollection.h" #include "lrdatasourceintf.h" #include "lrdatasourcemanagerintf.h" #include "lrhorizontallayout.h" #include "lrverticallayout.h" namespace LimeReport{ class DataSourceManager; class BaseDesignIntf; class PageItemDesignIntf; class BandDesignIntf; struct ContentItem { QString content; int indent; int pageNumber; QString uniqKey; }; class TableOfContents : public QObject{ Q_OBJECT public: TableOfContents(QObject* parent = 0):QObject(parent){} ~TableOfContents(); void setItem(const QString& uniqKey, const QString& content, int pageNumber, int indent = 0); void clear(); bool isEmpty(){ return m_tableOfContents.isEmpty();} private slots: void slotOneSlotDS(LimeReport::CallbackInfo info, QVariant &data); private: QVector m_tableOfContents; QHash m_hash; }; struct ScriptFunctionDesc{ enum FuncType {Native,Script}; ScriptValueType scriptValue; QString name; QString description; QString category; FuncType type; }; class ScriptEngineNode { public: enum NodeType{Root, Category, Function, Dialog, DialogElement}; ScriptEngineNode(const QString& name = "", const QString& description = "", NodeType type = Root, ScriptEngineNode* parent = 0, const QIcon& icon = QIcon()); virtual ~ScriptEngineNode(); int childCount(){return m_childs.count();} ScriptEngineNode* child(int index){return m_childs[index];} ScriptEngineNode* parent(){return m_parent;} ScriptEngineNode* addChild(const QString& name = "", const QString &description = "", NodeType type = Root, const QIcon& icon = QIcon()); int row(); QString name(){return m_name;} QString description(){return m_description;} QIcon icon(){return m_icon;} void clear(); NodeType type(){return m_type;} private: QString m_name; QString m_description; QIcon m_icon; NodeType m_type; ScriptEngineNode* m_parent; QVector m_childs; }; class ScriptEngineManager; class ScriptEngineModel : public QAbstractItemModel{ Q_OBJECT public: friend class ScriptEngineManager; explicit ScriptEngineModel():m_scriptManager(0), m_rootNode(new ScriptEngineNode()){} explicit ScriptEngineModel(ScriptEngineManager* scriptManager); ~ScriptEngineModel(); QModelIndex parent(const QModelIndex &child) const; QModelIndex index(int row, int column, const QModelIndex &parent) const; int rowCount(const QModelIndex &parent) const; int columnCount(const QModelIndex &parent) const; QVariant data(const QModelIndex &index, int role) const; void setScriptEngineManager(ScriptEngineManager* scriptManager); inline ScriptEngineManager* scriptEngineManager(){return m_scriptManager;} private slots: void slotScriptEngineChanged(); private: ScriptEngineNode* nodeFromIndex(const QModelIndex &index) const; void updateModel(); private: ScriptEngineManager* m_scriptManager; ScriptEngineNode* m_rootNode; }; class DialogDescriber : public QObject { Q_OBJECT Q_PROPERTY(QString name READ name WRITE setName) Q_PROPERTY(QByteArray description READ description WRITE setDescription) public: typedef QSharedPointer Ptr; static Ptr create(const QString& name, const QByteArray &desc); static Ptr create(){return Ptr(new DialogDescriber);} QString name() const; void setName(const QString& name); QByteArray description() const; void setDescription(const QByteArray& description); private : QString m_name; QByteArray m_description; }; typedef QList< QSharedPointer > ReportPages; class ScriptEngineContext : public QObject, public ICollectionContainer { Q_OBJECT Q_PROPERTY(ACollectionProperty dialogs READ fakeCollectionReader) Q_PROPERTY(QString initScript READ initScript WRITE setInitScript) public: #ifdef HAVE_UI_LOADER typedef QSharedPointer DialogPtr; #endif explicit ScriptEngineContext(QObject* parent=0): QObject(parent), m_currentBand(0), m_currentPage(0), m_tableOfContents(new TableOfContents(this)), m_hasChanges(false) {} #ifdef HAVE_UI_LOADER void addDialog(const QString& name, const QByteArray& description); bool changeDialog(const QString& name, const QByteArray &description); bool changeDialogName(const QString& oldName, const QString& newName); bool previewDialog(const QString& dialogName); bool containsDialog(const QString& dialogName); const QVector& dialogDescribers(){return m_dialogs;} void deleteDialog(const QString& dialogName); QDialog *getDialog(const QString &dialogName); QString getNewDialogName(); void initDialogs(); #endif void baseDesignIntfToScript(const QString& pageName, BaseDesignIntf *item); void qobjectToScript(const QString &name, QObject* item); void clear(); QString initScript() const; void setInitScript(const QString& initScript); bool runInitScript(); BandDesignIntf* currentBand() const; void setCurrentBand(BandDesignIntf* currentBand); PageItemDesignIntf* currentPage() const; void setCurrentPage(PageItemDesignIntf* currentPage); TableOfContents* tableOfContents() const; void setTableOfContents(TableOfContents* tableOfContents); void dropChanges(){ m_hasChanges = false;} bool hasChanges(){ return m_hasChanges;} ReportPages* reportPages() const; void setReportPages(ReportPages* value); #ifdef HAVE_UI_LOADER signals: void dialogNameChanged(QString dialogName); void dialogDeleted(QString dialogName); void dialogAdded(QString dialogName); #endif protected: QObject* createElement(const QString& collectionName,const QString& elementType); int elementsCount(const QString& collectionName); QObject* elementAt(const QString& collectionName,int index); void collectionLoadFinished(const QString &collectionName); #ifdef HAVE_UI_LOADER QDialog *createDialog(DialogDescriber *cont); QDialog *findDialog(const QString &dialogName); DialogDescriber* findDialogContainer(const QString& dialogName); #endif private: #ifdef HAVE_UI_LOADER QVector m_dialogs; QList m_createdDialogs; #endif QString m_lastError; QString m_initScript; BandDesignIntf* m_currentBand; PageItemDesignIntf* m_currentPage; TableOfContents* m_tableOfContents; bool m_hasChanges; ReportPages* m_reportPages; }; class JSFunctionDesc{ public: JSFunctionDesc(){} JSFunctionDesc(const QString& functionName, const QString& functionCategory, const QString& functionDescription, const QString& functionManagerName, QObject* functionManager, const QString& functionScriptWrapper ): m_name(functionName), m_category(functionCategory), m_description(functionDescription), m_managerName(functionManagerName), m_manager(functionManager), m_scriptWrapper(functionScriptWrapper) {} QString name() const; void setName(const QString &name); QString category() const; void setCategory(const QString &category); QString description() const; void setDescription(const QString &description); QString managerName() const; void setManagerName(const QString &managerName); QObject *manager() const; void setManager(QObject *manager); QString scriptWrapper() const; void setScriptWrapper(const QString &scriptWrapper); private: QString m_name; QString m_category; QString m_description; QString m_managerName; QObject* m_manager; QString m_scriptWrapper; }; #ifdef USE_QTSCRIPTENGINE class ComboBoxPrototype : public QObject, public QScriptable{ Q_OBJECT public: ComboBoxPrototype(QObject* parent = 0):QObject(parent){} public slots: void addItem( const QString& text); void addItems(const QStringList& texts); }; #endif class IWrapperCreator{ public: virtual QObject* createWrapper(QObject* item) = 0; virtual ~IWrapperCreator(){} }; class ComboBoxWrapper : public QObject{ Q_OBJECT public: ComboBoxWrapper(QComboBox* comboBox, QObject* parent = 0) : QObject(parent), m_comboBox(comboBox){} Q_INVOKABLE void addItems(const QStringList& texts){ m_comboBox->addItems(texts);} Q_INVOKABLE void addItem(const QString& text){ m_comboBox->addItem(text);} private: QComboBox* m_comboBox; }; class ComboBoxWrapperCreator: public IWrapperCreator{ private: QObject* createWrapper(QObject* item); }; class TableBuilder: public QObject{ Q_OBJECT public: TableBuilder(LimeReport::HorizontalLayout* layout, DataSourceManager* dataManager); ~TableBuilder(){delete m_patternLayout;} Q_INVOKABLE QObject* addRow(); Q_INVOKABLE QObject* currentRow(); Q_INVOKABLE void fillInRowData(QObject* row); Q_INVOKABLE void buildTable(const QString& datasourceName); private: void checkBaseLayout(); private: LimeReport::HorizontalLayout* m_horizontalLayout; LimeReport::HorizontalLayout* m_patternLayout; LimeReport::VerticalLayout* m_baseLayout; DataSourceManager* m_dataManager; }; class DatasourceFunctions : public QObject{ Q_OBJECT public: explicit DatasourceFunctions(IDataSourceManager* dataManager) : m_dataManager(dynamic_cast(dataManager)){} Q_INVOKABLE bool first(const QString& datasourceName); Q_INVOKABLE bool next(const QString& datasourceName); Q_INVOKABLE bool prior(const QString& datasourceName); Q_INVOKABLE bool isEOF(const QString& datasourceName); Q_INVOKABLE int rowCount(const QString& datasourceName); Q_INVOKABLE bool invalidate(const QString& datasourceName); Q_INVOKABLE QObject *createTableBuilder(QObject *horizontalLayout); private: DataSourceManager* m_dataManager; }; class ScriptFunctionsManager : public QObject{ Q_OBJECT public: explicit ScriptFunctionsManager(QObject* parent = 0):QObject(parent){ m_wrappersFactory.insert("QComboBox",new ComboBoxWrapperCreator()); } ~ScriptFunctionsManager(){ foreach(IWrapperCreator* wrapper, m_wrappersFactory.values()){ delete wrapper;} m_wrappersFactory.clear(); } Q_INVOKABLE QVariant calcGroupFunction(const QString& name, const QString& expressionID, const QString& bandName, QObject* currentPage); Q_INVOKABLE QVariant calcGroupFunction(const QString& name, const QString& expressionID, const QString& bandName); Q_INVOKABLE QVariant line(const QString& bandName); Q_INVOKABLE QVariant numberFormat(QVariant value, const char &format, int precision, const QString& locale); Q_INVOKABLE QVariant dateFormat(QVariant value, const QString& format, const QString& locale); Q_INVOKABLE QVariant timeFormat(QVariant value, const QString& format); Q_INVOKABLE QVariant dateTimeFormat(QVariant value, const QString& format, const QString& locale); Q_INVOKABLE QVariant sectotimeFormat(QVariant value, const QString& format); Q_INVOKABLE QVariant date(); Q_INVOKABLE QVariant now(); Q_INVOKABLE QVariant currencyFormat(QVariant value, const QString& locale); Q_INVOKABLE QVariant currencyUSBasedFormat(QVariant value, const QString& currencySymbol); Q_INVOKABLE void setVariable(const QString& name, QVariant value); Q_INVOKABLE QVariant getVariable(const QString& name); Q_INVOKABLE QVariant getField(const QString& field); Q_INVOKABLE QVariant getFieldByKeyField(const QString& datasourceName, const QString& valueFieldName, const QString& keyFieldName, QVariant keyValue); Q_INVOKABLE QVariant getFieldByRowIndex(const QString& fieldName, int rowIndex); Q_INVOKABLE void reopenDatasource(const QString& datasourceName); Q_INVOKABLE QVariant color(const QString& color){ return QColor(color);} Q_INVOKABLE void addBookmark(const QString& uniqKey, const QString& content); Q_INVOKABLE int findPageIndexByBookmark(const QString &uniqKey); Q_INVOKABLE void addTableOfContentsItem(const QString& uniqKey, const QString& content, int indent = 0); Q_INVOKABLE void clearTableOfContents(); Q_INVOKABLE QFont font(const QString& family, int pointSize = -1, bool bold = false, bool italic = false, bool underLine = false); /*! * \brief getFieldByRowIndexEx Выдает для поля значение заданной роли * \param fieldName имя источника данных + имя поля * \param rowIndex индекс строки * \param role код роли * \return */ Q_INVOKABLE QVariant getFieldByRowIndexEx(const QString &fieldName, int rowIndex, const int role); /*! * \brief getFieldByRowIndexEx2 Выдает для поля значение заданной роли * \param fieldName имя источника данных + имя поля * \param rowIndex индекс строки * \param roleName имя роли из roleNames() * \return */ Q_INVOKABLE QVariant getFieldByRowIndexEx2(const QString &fieldName, int rowIndex, const QString &roleName); /*! * \brief getHeaderData Выдает для поля заголовка значение заданной роли * \param fieldName имя источника данных + имя поля * \param role имя роли из roleNames() * \return */ Q_INVOKABLE QVariant getHeaderData(const QString &fieldName, const QString &roleName); /*! * \brief getHeaderColumnNameByIndex Выдает имя колонки по ее индексу (имя используемое LR) * \param datasourceName имя источника данных * \param columnIndex индекс колонки * \return */ Q_INVOKABLE QVariant getHeaderColumnNameByIndex(const QString &datasourceName, const int columnIndex); /*! * \brief getColumnCount Выдает число столбцов в источнике данных * \param datasourceName имя источника данных * \return возможно -1 при ошибке */ Q_INVOKABLE int getColumnCount(const QString &datasourceName); #ifdef USE_QJSENGINE Q_INVOKABLE void addItemsToComboBox(QJSValue object, const QStringList& values); Q_INVOKABLE void addItemToComboBox(QJSValue object, const QString& value); Q_INVOKABLE QJSValue createComboBoxWrapper(QJSValue comboBox); Q_INVOKABLE QJSValue createWrapper(QJSValue item); #else Q_INVOKABLE void addItemsToComboBox(QScriptValue object, const QStringList& values); Q_INVOKABLE void addItemToComboBox(QScriptValue object, const QString& value); Q_INVOKABLE QScriptValue createComboBoxWrapper(QScriptValue comboBox); Q_INVOKABLE QScriptValue createWrapper(QScriptValue item); #endif Q_INVOKABLE QFont font(QVariantMap params); Q_INVOKABLE int getPageFreeSpace(QObject *page); ScriptEngineManager *scriptEngineManager() const; void setScriptEngineManager(ScriptEngineManager *scriptEngineManager); static QColor createQColor(const QString& color){ return QColor(color);} private: ScriptEngineManager* m_scriptEngineManager; QMap m_wrappersFactory; }; class ScriptNode{ public: typedef QSharedPointer Ptr; QString body(){ if (m_body.isEmpty() && m_children.count() > 0) return m_children.at(0)->body(); return m_body; } void setBody(const QString& body){ m_body = body;} void setStartLex(const QString startLex){ m_startLex = startLex;} QString script(){return m_startLex + m_body + '}';} Ptr createChildNode(){ Ptr result = Ptr(new ScriptNode()); m_children.append(result); return result; } QVector children() const {return m_children;} private: QVector m_children; QString m_body; QString m_startLex; }; class ScriptExtractor { public: enum State{None,BuksFound,SFound,StartScriptFound,OpenBracketFound,CloseBracketFound,DFound,VFound, SignFound}; explicit ScriptExtractor(const QString& value): m_context(value), m_scriptTree(ScriptNode::Ptr(new ScriptNode())){} bool parse(); ScriptNode::Ptr scriptTree(){return m_scriptTree;} private: bool isStartLexem(int &curPos, QChar value); bool parse(int& curPos, const State &state, ScriptNode::Ptr scriptNode); void skipField(int &curPos); void extractScript(int& curPos, const QString &startStr, ScriptNode::Ptr scriptNode); bool extractBracket(int& curPos, ScriptNode::Ptr scriptNode); bool isStartScriptLexem(int &curPos); bool isStartFieldLexem(int &curPos); bool isStartVariableLexem(int &curPos); QString substring(const QString& value, int start, int end); private: QString m_context; ScriptNode::Ptr m_scriptTree; }; class ScriptEngineManager : public QObject, public Singleton, public IScriptEngineManager { Q_OBJECT public: friend class Singleton; ScriptEngineType* scriptEngine(){return m_scriptEngine;} ~ScriptEngineManager(); bool isFunctionExists(const QString& functionName) const; void deleteFunction(const QString& functionsName); bool addFunction(const JSFunctionDesc& functionsDescriber); #ifdef USE_QTSCRIPTENGINE bool addFunction(const QString &name, QScriptEngine::FunctionSignature function, const QString &category, const QString &description); #endif bool addFunction(const QString &name, const QString& script, const QString &category="", const QString &description=""); const QString& lastError() const {return m_lastError;} QStringList functionsNames(); const QHash& functionsDescribers(){return m_functions;} ScriptEngineModel* model(){return m_model;} void setContext(ScriptEngineContext* context){m_context=context;} DataSourceManager* dataManager() const {return m_dataManager;} void setDataManager(DataSourceManager* dataManager); QString expandUserVariables(QString context, RenderPass pass, ExpandType expandType, QVariant &varValue); QString expandDataFields(QString context, ExpandType expandType, QVariant &varValue, QObject* reportItem); QString expandScripts(QString context, QVariant &varValue, QObject* reportItem); QString replaceScripts(QString context, QVariant& varValue, QObject *reportItem, ScriptEngineType *se, ScriptNode::Ptr scriptTree); QVariant evaluateScript(const QString &script); void addBookMark(const QString &uniqKey, const QString &content); int findPageIndexByBookmark(const QString& uniqKey); void addTableOfContentsItem(const QString& uniqKey, const QString& content, int indent); void clearTableOfContents(); int getPageFreeSpace(PageItemDesignIntf *page); ScriptValueType moveQObjectToScript(QObject* object, const QString objectName); protected: void updateModel(); private: Q_DISABLE_COPY(ScriptEngineManager) bool createLineFunction(); bool createNumberFomatFunction(); bool createDateFormatFunction(); bool createTimeFormatFunction(); bool createDateTimeFormatFunction(); bool createSectotimeFormatFunction(); bool createDateFunction(); bool createNowFunction(); bool createCurrencyFormatFunction(); bool createCurrencyUSBasedFormatFunction(); bool createSetVariableFunction(); bool createGetVariableFunction(); bool createGetFieldFunction(); bool createGetFieldByKeyFunction(); bool createGetFieldByRowIndex(); bool createAddBookmarkFunction(); bool createFindPageIndexByBookmark(); bool createAddTableOfContentsItemFunction(); bool createClearTableOfContentsFunction(); bool createReopenDatasourceFunction(); bool createGetFieldByRowIndexEx(); bool createGetFieldByRowIndexEx2(); bool createHeaderData(); bool createHeaderColumnNameByIndex(); bool createColumnCount(); private: ScriptEngineManager(); ScriptEngineType* m_scriptEngine; QString m_lastError; QHash m_functions; ScriptEngineModel* m_model; ScriptEngineContext* m_context; DataSourceManager* m_dataManager; ScriptFunctionsManager* m_functionManager; }; #ifdef USE_QTSCRIPTENGINE class QFontPrototype : public QObject, public QScriptable { Q_OBJECT Q_PROPERTY(QString family READ family) Q_PROPERTY(int size READ size) Q_PROPERTY(bool bold READ bold) Q_PROPERTY(bool italic READ italic) Q_PROPERTY(bool underline READ underline) public: QFontPrototype(QObject * parent = NULL) : QObject(parent) , QScriptable() { this->setObjectName("QFontPrototype"); } QString family() const { QFont font(qScriptValueToValue(this->thisObject())); return font.family(); } int size(){ QFont font = qScriptValueToValue(thisObject()); return font.pointSize(); } bool bold(){ QFont font = qScriptValueToValue(thisObject()); return font.bold(); } bool italic(){ QFont font = qScriptValueToValue(thisObject()); return font.italic(); } bool underline(){ QFont font = qScriptValueToValue(thisObject()); return font.underline(); } static QScriptValue constructorQFont(QScriptContext * context, QScriptEngine * engine) { QFont font; switch (context->argumentCount()) { case 5: font.setUnderline(qScriptValueToValue(context->argument(4))); case 4: font.setBold(qScriptValueToValue(context->argument(3))); case 3: font.setItalic(qScriptValueToValue(context->argument(2))); case 2: font.setPointSize(qScriptValueToValue(context->argument(1))); case 1: font.setFamily(qScriptValueToValue(context->argument(0))); case 0: break; default: break; } return qScriptValueFromValue(engine, font); } }; #endif } #ifdef USE_QTSCRIPTENGINE Q_DECLARE_METATYPE(LimeReport::ComboBoxPrototype*) Q_DECLARE_METATYPE(QComboBox*) #endif #endif // LRSCRIPTENGINEMANAGER_H