From 0e4e012c6451ae46461a7da3e7f3a7d8a3d6e623 Mon Sep 17 00:00:00 2001 From: Jakub Melka Date: Wed, 11 Mar 2020 20:09:14 +0100 Subject: [PATCH] Code generator - beginnings --- CodeGenerator/CodeGenerator.pro | 40 +++++ CodeGenerator/codegenerator.cpp | 234 ++++++++++++++++++++++++++ CodeGenerator/codegenerator.h | 150 +++++++++++++++++ CodeGenerator/generatormainwindow.cpp | 134 +++++++++++++++ CodeGenerator/generatormainwindow.h | 59 +++++++ CodeGenerator/generatormainwindow.ui | 205 ++++++++++++++++++++++ CodeGenerator/main.cpp | 28 +++ PdfForQt.pro | 1 + 8 files changed, 851 insertions(+) create mode 100644 CodeGenerator/CodeGenerator.pro create mode 100644 CodeGenerator/codegenerator.cpp create mode 100644 CodeGenerator/codegenerator.h create mode 100644 CodeGenerator/generatormainwindow.cpp create mode 100644 CodeGenerator/generatormainwindow.h create mode 100644 CodeGenerator/generatormainwindow.ui create mode 100644 CodeGenerator/main.cpp diff --git a/CodeGenerator/CodeGenerator.pro b/CodeGenerator/CodeGenerator.pro new file mode 100644 index 0000000..dc2193d --- /dev/null +++ b/CodeGenerator/CodeGenerator.pro @@ -0,0 +1,40 @@ +QT += core gui xml + +greaterThan(QT_MAJOR_VERSION, 4): QT += widgets + +CONFIG += c++11 + +# The following define makes your compiler emit warnings if you use +# any Qt feature that has been marked deprecated (the exact warnings +# depend on your compiler). Please consult the documentation of the +# deprecated API in order to know how to port your code away from it. +DEFINES += QT_DEPRECATED_WARNINGS + +# You can also make your code fail to compile if it uses deprecated APIs. +# In order to do so, uncomment the following line. +# You can also select to disable deprecated APIs only up to a certain version of Qt. +#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 + +QMAKE_CXXFLAGS += /std:c++latest /utf-8 + +DESTDIR = $$OUT_PWD/.. + +LIBS += -L$$OUT_PWD/.. + + +SOURCES += \ + codegenerator.cpp \ + main.cpp \ + generatormainwindow.cpp + +HEADERS += \ + codegenerator.h \ + generatormainwindow.h + +FORMS += \ + generatormainwindow.ui + +# Default rules for deployment. +qnx: target.path = /tmp/$${TARGET}/bin +else: unix:!android: target.path = /opt/$${TARGET}/bin +!isEmpty(target.path): INSTALLS += target diff --git a/CodeGenerator/codegenerator.cpp b/CodeGenerator/codegenerator.cpp new file mode 100644 index 0000000..f6e8976 --- /dev/null +++ b/CodeGenerator/codegenerator.cpp @@ -0,0 +1,234 @@ +// Copyright (C) 2020 Jakub Melka +// +// This file is part of PdfForQt. +// +// PdfForQt 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. +// +// PdfForQt 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with PDFForQt. If not, see . + +#include "codegenerator.h" + +namespace codegen +{ + +GeneratedCodeStorage::GeneratedCodeStorage(QObject* parent) : + BaseClass(parent) +{ + +} + +QObjectList GeneratedCodeStorage::getFunctions() const +{ + return m_functions; +} + +void GeneratedCodeStorage::setFunctions(const QObjectList& functions) +{ + m_functions = functions; +} + +GeneratedFunction* GeneratedCodeStorage::addFunction(const QString& name) +{ + GeneratedFunction* function = new GeneratedFunction(this); + function->setFunctionName(name); + m_functions.append(function); + return function; +} + +QObject* Serializer::load(const QDomElement& element, QObject* parent) +{ + QString className = element.attribute("class"); + const int metaTypeId = QMetaType::type(className.toLatin1()); + const QMetaObject* metaObject = QMetaType::metaObjectForType(metaTypeId); + + if (metaObject) + { + QObject* deserializedObject = metaObject->newInstance(Q_ARG(QObject*, parent)); + + const int propertyCount = metaObject->propertyCount(); + for (int i = 0; i < propertyCount; ++i) + { + QMetaProperty property = metaObject->property(i); + if (property.isWritable()) + { + // Find, if property was serialized + QDomElement propertyElement; + QDomNodeList children = element.childNodes(); + for (int i = 0; i < children.count(); ++i) + { + QDomNode child = children.item(i); + if (child.isElement()) + { + QDomElement childElement = child.toElement(); + QString attributeName = childElement.attribute("name"); + if (attributeName == property.name()) + { + propertyElement = child.toElement(); + break; + } + } + } + + if (!propertyElement.isNull()) + { + // Deserialize the element + if (property.userType() == qMetaTypeId()) + { + QObjectList objectList; + QDomNodeList children = propertyElement.childNodes(); + for (int i = 0; i < children.count(); ++i) + { + QDomNode node = children.item(i); + if (node.isElement()) + { + QDomElement element = node.toElement(); + if (QObject* object = Serializer::load(element, deserializedObject)) + { + objectList.append(object); + } + } + } + + property.write(deserializedObject, QVariant::fromValue(qMove(objectList))); + } + else + { + QVariant value = propertyElement.text(); + property.write(deserializedObject, value); + } + } + } + } + + return deserializedObject; + } + + return nullptr; +} + +void Serializer::store(QObject* object, QDomElement& element) +{ + Q_ASSERT(object); + + const QMetaObject* metaObject = object->metaObject(); + element.setAttribute("class", QString(metaObject->className())); + + const int propertyCount = metaObject->propertyCount(); + if (propertyCount > 0) + { + for (int i = 0; i < propertyCount; ++i) + { + QMetaProperty property = metaObject->property(i); + if (property.isReadable()) + { + QDomElement propertyElement = element.ownerDocument().createElement("property"); + element.appendChild(propertyElement); + propertyElement.setAttribute("name", property.name()); + + QVariant value = property.read(object); + if (value.canConvert()) + { + QObjectList objectList = value.value(); + for (QObject* currentObject : objectList) + { + QDomElement objectElement = element.ownerDocument().createElement("QObject"); + propertyElement.appendChild(objectElement); + Serializer::store(currentObject, objectElement); + } + } + else + { + propertyElement.appendChild(propertyElement.ownerDocument().createTextNode(value.toString())); + } + } + } + } +} + +CodeGenerator::CodeGenerator(QObject* parent) : + BaseClass(parent) +{ + qRegisterMetaType("codegen::GeneratedCodeStorage"); + qRegisterMetaType("codegen::GeneratedFunction"); + qRegisterMetaType("QObjectList"); +} + +void CodeGenerator::initialize() +{ + m_storage = new GeneratedCodeStorage(this); +} + +void CodeGenerator::load(const QDomDocument& document) +{ + delete m_storage; + m_storage = nullptr; + + m_storage = qobject_cast(Serializer::load(document.firstChildElement("root"), this)); +} + +void CodeGenerator::store(QDomDocument& document) +{ + if (m_storage) + { + QDomElement rootElement = document.createElement("root"); + document.appendChild(rootElement); + Serializer::store(m_storage, rootElement); + } +} + +GeneratedFunction::GeneratedFunction(QObject* parent) : + BaseClass(parent) +{ + +} + +QString GeneratedFunction::getFunctionTypeString() const +{ + return Serializer::convertEnumToString(m_functionType); +} + +void GeneratedFunction::setFunctionTypeString(const QString& string) +{ + Serializer::convertStringToEnum(string, m_functionType); +} + +GeneratedFunction::FunctionType GeneratedFunction::getFunctionType() const +{ + return m_functionType; +} + +void GeneratedFunction::setFunctionType(const FunctionType& functionType) +{ + m_functionType = functionType; +} + +QString GeneratedFunction::getFunctionName() const +{ + return m_functionName; +} + +void GeneratedFunction::setFunctionName(const QString& functionName) +{ + m_functionName = functionName; +} + +QString GeneratedFunction::getFunctionDescription() const +{ + return m_functionDescription; +} + +void GeneratedFunction::setFunctionDescription(const QString& functionDescription) +{ + m_functionDescription = functionDescription; +} + +} diff --git a/CodeGenerator/codegenerator.h b/CodeGenerator/codegenerator.h new file mode 100644 index 0000000..26dd907 --- /dev/null +++ b/CodeGenerator/codegenerator.h @@ -0,0 +1,150 @@ +// Copyright (C) 2020 Jakub Melka +// +// This file is part of PdfForQt. +// +// PdfForQt 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. +// +// PdfForQt 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with PDFForQt. If not, see . + +#ifndef CODEGENERATOR_H +#define CODEGENERATOR_H + +#include +#include +#include +#include +#include +#include + +namespace codegen +{ + +class Serializer +{ +public: + static QObject* load(const QDomElement& element, QObject* parent); + static void store(QObject* object, QDomElement& element); + + template + static inline QString convertEnumToString(T enumValue) + { + QMetaEnum metaEnum = QMetaEnum::fromType(); + Q_ASSERT(metaEnum.isValid()); + return metaEnum.valueToKey(enumValue); + } + + template + static inline void convertStringToEnum(const QString enumString, T& value) + { + QMetaEnum metaEnum = QMetaEnum::fromType(); + Q_ASSERT(metaEnum.isValid()); + bool ok = false; + value = static_cast(metaEnum.keyToValue(enumString.toLatin1().data(), &ok)); + if (!ok) + { + // Set default value, if something fails. + value = T(); + } + } +}; + +class GeneratedFunction; + +class GeneratedCodeStorage : public QObject +{ + Q_OBJECT + +private: + using BaseClass = QObject; + +public: + Q_INVOKABLE GeneratedCodeStorage(QObject* parent); + + Q_PROPERTY(QObjectList functions READ getFunctions WRITE setFunctions) + + QObjectList getFunctions() const; + void setFunctions(const QObjectList& functions); + + GeneratedFunction* addFunction(const QString& name); + +private: + QObjectList m_functions; +}; + +class GeneratedFunction : public QObject +{ + Q_OBJECT + +private: + using BaseClass = QObject; + +public: + + enum FunctionType + { + Annotations, + ColorSpace + }; + Q_ENUM(FunctionType) + + + Q_INVOKABLE GeneratedFunction(QObject* parent); + + Q_PROPERTY(QString functionType READ getFunctionTypeString WRITE setFunctionTypeString) + Q_PROPERTY(QString functionName READ getFunctionName WRITE setFunctionName) + Q_PROPERTY(QString functionDescription READ getFunctionDescription WRITE setFunctionDescription) + + QString getFunctionTypeString() const; + void setFunctionTypeString(const QString& string); + + FunctionType getFunctionType() const; + void setFunctionType(const FunctionType& functionType); + + QString getFunctionName() const; + void setFunctionName(const QString& functionName); + + QString getFunctionDescription() const; + void setFunctionDescription(const QString& functionDescription); + +private: + FunctionType m_functionType = FunctionType::Annotations; + QString m_functionName; + QString m_functionDescription; +}; + +class CodeGenerator : public QObject +{ + Q_OBJECT + +private: + using BaseClass = QObject; + +public: + CodeGenerator(QObject* parent = nullptr); + + void initialize(); + + GeneratedFunction* addFunction(const QString& name) { return m_storage->addFunction(name); } + + void load(const QDomDocument& document); + void store(QDomDocument& document); + +private: + GeneratedCodeStorage* m_storage = nullptr; +}; + +} // namespace codegen + +Q_DECLARE_METATYPE(codegen::GeneratedCodeStorage*) +Q_DECLARE_METATYPE(codegen::GeneratedFunction*) + +#endif // CODEGENERATOR_H diff --git a/CodeGenerator/generatormainwindow.cpp b/CodeGenerator/generatormainwindow.cpp new file mode 100644 index 0000000..f3c6672 --- /dev/null +++ b/CodeGenerator/generatormainwindow.cpp @@ -0,0 +1,134 @@ +// Copyright (C) 2020 Jakub Melka +// +// This file is part of PdfForQt. +// +// PdfForQt 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. +// +// PdfForQt 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with PDFForQt. If not, see . + +#include "generatormainwindow.h" +#include "ui_generatormainwindow.h" + +#include "codegenerator.h" + +#include +#include +#include +#include +#include + +GeneratorMainWindow::GeneratorMainWindow(QWidget *parent) : + QMainWindow(parent), + ui(new Ui::GeneratorMainWindow), + m_generator(new codegen::CodeGenerator(this)) +{ + ui->setupUi(this); + + m_generator->initialize(); + + loadSettings(); + + if (!m_defaultFileName.isEmpty()) + { + load(m_defaultFileName); + } + + // Temporary - to delete: + m_generator->addFunction("createAnnotationWatermark"); + m_generator->addFunction("createAnnotationFreeText"); +} + +GeneratorMainWindow::~GeneratorMainWindow() +{ + delete ui; +} + +void GeneratorMainWindow::load(const QString& fileName) +{ + QFile file(fileName); + if (file.open(QFile::ReadOnly | QFile::Truncate)) + { + QTextStream stream(&file); + stream.setCodec("UTF-8"); + + QDomDocument document; + document.setContent(stream.readAll()); + m_generator->load(document); + file.close(); + } +} + +void GeneratorMainWindow::saveSettings() +{ + QSettings settings("MelkaJ"); + settings.setValue("fileName", m_defaultFileName); +} + +void GeneratorMainWindow::loadSettings() +{ + QSettings settings("MelkaJ"); + m_defaultFileName = settings.value("fileName").toString(); +} + +void GeneratorMainWindow::save(const QString& fileName) +{ + QFile file(fileName); + if (file.open(QFile::WriteOnly | QFile::Truncate)) + { + QTextStream stream(&file); + stream.setCodec("UTF-8"); + + QDomDocument document; + m_generator->store(document); + document.save(stream, 2, QDomDocument::EncodingFromTextStream); + file.close(); + + // Update default file name + m_defaultFileName = fileName; + saveSettings(); + } +} + +void GeneratorMainWindow::on_actionLoad_triggered() +{ + QFileInfo info(m_defaultFileName); + QString fileName = QFileDialog::getOpenFileName(this, tr("Select XML definition file"), info.dir().absolutePath(), "XML files (*.xml)"); + if (!fileName.isEmpty()) + { + m_defaultFileName = fileName; + saveSettings(); + + load(m_defaultFileName); + } +} + +void GeneratorMainWindow::on_actionSaveAs_triggered() +{ + QFileInfo info(m_defaultFileName); + QString fileName = QFileDialog::getSaveFileName(this, tr("Select XML definition file"), info.dir().absolutePath(), "XML files (*.xml)"); + if (!fileName.isEmpty()) + { + save(fileName); + } +} + +void GeneratorMainWindow::on_actionSave_triggered() +{ + if (!m_defaultFileName.isEmpty()) + { + save(m_defaultFileName); + } + else + { + on_actionSaveAs_triggered(); + } +} diff --git a/CodeGenerator/generatormainwindow.h b/CodeGenerator/generatormainwindow.h new file mode 100644 index 0000000..6ac705d --- /dev/null +++ b/CodeGenerator/generatormainwindow.h @@ -0,0 +1,59 @@ +// Copyright (C) 2020 Jakub Melka +// +// This file is part of PdfForQt. +// +// PdfForQt 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. +// +// PdfForQt 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with PDFForQt. If not, see . + +#ifndef GENERATORMAINWINDOW_H +#define GENERATORMAINWINDOW_H + +#include + +namespace codegen +{ +class CodeGenerator; +} + +namespace Ui +{ +class GeneratorMainWindow; +} + +class GeneratorMainWindow : public QMainWindow +{ + Q_OBJECT + +public: + GeneratorMainWindow(QWidget* parent = nullptr); + virtual ~GeneratorMainWindow() override; + + void load(const QString& fileName); + void save(const QString& fileName); + +private slots: + void on_actionLoad_triggered(); + void on_actionSaveAs_triggered(); + + void on_actionSave_triggered(); + +private: + void loadSettings(); + void saveSettings(); + + Ui::GeneratorMainWindow* ui; + codegen::CodeGenerator* m_generator; + QString m_defaultFileName; +}; + +#endif // GENERATORMAINWINDOW_H diff --git a/CodeGenerator/generatormainwindow.ui b/CodeGenerator/generatormainwindow.ui new file mode 100644 index 0000000..d902060 --- /dev/null +++ b/CodeGenerator/generatormainwindow.ui @@ -0,0 +1,205 @@ + + + GeneratorMainWindow + + + + 0 + 0 + 1121 + 662 + + + + Code Generator + + + + + + + + + + 1 + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Up + + + + + + + Down + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Remove + + + + + + + Clone + + + + + + + New + + + + + + + + + + + + + Parameters + + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + PushButton + + + + + + + PushButton + + + + + + + PushButton + + + + + + + PushButton + + + + + + + + + + + + + 0 + 0 + 1121 + 21 + + + + + File + + + + + + + + + + + Load + + + Ctrl+O + + + + + Save + + + Ctrl+S + + + + + Save As... + + + + + + diff --git a/CodeGenerator/main.cpp b/CodeGenerator/main.cpp new file mode 100644 index 0000000..cae4c84 --- /dev/null +++ b/CodeGenerator/main.cpp @@ -0,0 +1,28 @@ +// Copyright (C) 2020 Jakub Melka +// +// This file is part of PdfForQt. +// +// PdfForQt 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. +// +// PdfForQt 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with PDFForQt. If not, see . + +#include "generatormainwindow.h" + +#include + +int main(int argc, char *argv[]) +{ + QApplication a(argc, argv); + GeneratorMainWindow w; + w.show(); + return a.exec(); +} diff --git a/PdfForQt.pro b/PdfForQt.pro index 8c88194..05207e4 100644 --- a/PdfForQt.pro +++ b/PdfForQt.pro @@ -18,6 +18,7 @@ TEMPLATE = subdirs SUBDIRS += \ + CodeGenerator \ JBIG2_Viewer \ PdfForQtLib \ UnitTests \