/*************************************************************************** * 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 "lrcallbackdatasourceintf.h" #include "lrcollection.h" #include "lrdatasourceintf.h" #include "lrdatasourcemanagerintf.h" #include "lrglobal.h" #include "lrhorizontallayout.h" #include "lrscriptenginemanagerintf.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> 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 } // namespace LimeReport #ifdef USE_QTSCRIPTENGINE Q_DECLARE_METATYPE(LimeReport::ComboBoxPrototype*) Q_DECLARE_METATYPE(QComboBox*) #endif #endif // LRSCRIPTENGINEMANAGER_H