aboutsummaryrefslogtreecommitdiffstats
path: root/SQLiteStudio3/coreSQLiteStudio/plugins
diff options
context:
space:
mode:
Diffstat (limited to 'SQLiteStudio3/coreSQLiteStudio/plugins')
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/plugins/builtinplugin.cpp58
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/plugins/builtinplugin.h147
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/plugins/codeformatterplugin.h16
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/plugins/confignotifiableplugin.h14
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/plugins/dbplugin.h73
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/plugins/dbpluginsqlite3.cpp47
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/plugins/dbpluginsqlite3.h24
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/plugins/exportplugin.h372
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/plugins/generalpurposeplugin.h20
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/plugins/genericexportplugin.cpp160
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/plugins/genericexportplugin.h56
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/plugins/genericplugin.cpp67
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/plugins/genericplugin.h126
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/plugins/importplugin.h132
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/plugins/plugin.h182
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/plugins/pluginsymbolresolver.cpp45
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/plugins/pluginsymbolresolver.h24
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/plugins/plugintype.cpp52
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/plugins/plugintype.h63
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/plugins/populateconstant.cpp48
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/plugins/populateconstant.h44
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/plugins/populateconstant.ui37
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/plugins/populatedictionary.cpp94
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/plugins/populatedictionary.h52
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/plugins/populatedictionary.ui123
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/plugins/populateplugin.h70
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/plugins/populaterandom.cpp55
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/plugins/populaterandom.h47
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/plugins/populaterandom.ui106
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/plugins/populaterandomtext.cpp91
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/plugins/populaterandomtext.h52
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/plugins/populaterandomtext.ui181
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/plugins/populatescript.cpp125
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/plugins/populatescript.h63
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/plugins/populatescript.ui112
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/plugins/populatesequence.cpp53
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/plugins/populatesequence.h47
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/plugins/populatesequence.ui64
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/plugins/scriptingplugin.h50
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/plugins/scriptingqt.cpp276
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/plugins/scriptingqt.h72
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/plugins/scriptingqt.pngbin0 -> 1750 bytes
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/plugins/scriptingqtdbproxy.cpp145
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/plugins/scriptingqtdbproxy.h44
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/plugins/scriptingsql.cpp146
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/plugins/scriptingsql.h46
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/plugins/scriptingsql.pngbin0 -> 376 bytes
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/plugins/sqlformatterplugin.cpp29
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/plugins/sqlformatterplugin.h16
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/plugins/uiconfiguredplugin.h56
50 files changed, 4022 insertions, 0 deletions
diff --git a/SQLiteStudio3/coreSQLiteStudio/plugins/builtinplugin.cpp b/SQLiteStudio3/coreSQLiteStudio/plugins/builtinplugin.cpp
new file mode 100644
index 0000000..29397e0
--- /dev/null
+++ b/SQLiteStudio3/coreSQLiteStudio/plugins/builtinplugin.cpp
@@ -0,0 +1,58 @@
+#include "builtinplugin.h"
+#include "services/pluginmanager.h"
+#include <QMetaClassInfo>
+
+QString BuiltInPlugin::getName() const
+{
+ return metaObject()->className();
+}
+
+QString BuiltInPlugin::getTitle() const
+{
+ const char *title = getMetaInfo("title");
+ if (!title)
+ return getName();
+
+ return title;
+}
+
+QString BuiltInPlugin::getDescription() const
+{
+ return getMetaInfo("description");
+}
+
+int BuiltInPlugin::getVersion() const
+{
+ return QString(getMetaInfo("version")).toInt();
+}
+
+QString BuiltInPlugin::getPrintableVersion() const
+{
+ return PLUGINS->toPrintableVersion(getVersion());
+}
+
+QString BuiltInPlugin::getAuthor() const
+{
+ return getMetaInfo("author");
+}
+
+bool BuiltInPlugin::init()
+{
+ return true;
+}
+
+void BuiltInPlugin::deinit()
+{
+}
+
+const char* BuiltInPlugin::getMetaInfo(const QString& key) const
+{
+ for (int i = 0; i < metaObject()->classInfoCount(); i++)
+ {
+ if (key != metaObject()->classInfo(i).name())
+ continue;
+
+ return metaObject()->classInfo(i).value();
+ }
+ return nullptr;
+}
diff --git a/SQLiteStudio3/coreSQLiteStudio/plugins/builtinplugin.h b/SQLiteStudio3/coreSQLiteStudio/plugins/builtinplugin.h
new file mode 100644
index 0000000..ccb5c3d
--- /dev/null
+++ b/SQLiteStudio3/coreSQLiteStudio/plugins/builtinplugin.h
@@ -0,0 +1,147 @@
+#ifndef BUILTINPLUGIN_H
+#define BUILTINPLUGIN_H
+
+#include "coreSQLiteStudio_global.h"
+#include "plugins/plugin.h"
+
+/**
+ * @brief Helper class for implementing built-in plugins
+ *
+ * This class can be inherited, so most of the abstract methods from Plugin interface get implemented.
+ * All details (description, name, title, author, ...) are defined using Q_CLASSINFO.
+ *
+ * There are macros defined to help you with defining those details. You don't need to define
+ * Q_CLASSINFO and know all required keys. You can use following macros:
+ * <ul>
+ * <li>::SQLITESTUDIO_PLUGIN_TITLE</li>
+ * <li>::SQLITESTUDIO_PLUGIN_DESC</li>
+ * <li>::SQLITESTUDIO_PLUGIN_UI</li>
+ * <li>::SQLITESTUDIO_PLUGIN_VERSION</li>
+ * <li>::SQLITESTUDIO_PLUGIN_AUTHOR</li>
+ * </ul>
+ *
+ * Most of plugin implementations will use this class as a base, because it simplifies process
+ * of plugin development. Using this class you don't have to implement any of virtual methods
+ * from Plugin interface. It's enough to define meta information, like this:
+ * @code
+ * class MyPlugin : GenericPlugin
+ * {
+ * Q_OBJECT
+ *
+ * SQLITESTUDIO_PLUGIN
+ * SQLITESTUDIO_PLUGIN_TITLE("My plugin")
+ * SQLITESTUDIO_PLUGIN_DESC("Does nothing. It's an example plugin.")
+ * SQLITESTUDIO_PLUGIN_UI("formObjectName")
+ * SQLITESTUDIO_PLUGIN_VERSION(10000)
+ * SQLITESTUDIO_PLUGIN_AUTHOR("sqlitestudio.pl")
+ * };
+ * @endcode
+ */class API_EXPORT BuiltInPlugin : public QObject, public virtual Plugin
+{
+ Q_OBJECT
+ Q_INTERFACES(Plugin)
+
+ public:
+ /**
+ * @brief Provides plugin internal name.
+ * @return Plugin class name.
+ */
+ QString getName() const;
+
+ /**
+ * @brief Provides plugin title.
+ * @return Title defined in plugin's metadata file with key "title" or (if not defined) the same value as getName().
+ */
+ QString getTitle() const;
+
+ /**
+ * @brief Provides plugin description.
+ * @return Description as defined in plugin's metadata file with key "description", or null QString if not defined.
+ */
+ QString getDescription() const;
+
+ /**
+ * @brief Provides plugin numeric version.
+ * @return Version number as defined in plugin's metadata file with key "version", or 0 if not defined.
+ */
+ int getVersion() const;
+
+ /**
+ * @brief Converts plugin version to human readable form.
+ * @return Version in format X.Y.Z.
+ */
+ QString getPrintableVersion() const;
+
+ /**
+ * @brief Provides an author name.
+ * @return Author name as defined with in plugin's metadata file with key "author", or null QString if not defined.
+ */
+ QString getAuthor() const;
+
+ /**
+ * @brief Does nothing.
+ * @return Always true.
+ *
+ * This is a default (empty) implementation of init() for plugins.
+ */
+ bool init();
+
+ /**
+ * @brief Does nothing.
+ *
+ * This is a default (empty) implementation of init() for plugins.
+ */
+ void deinit();
+
+ private:
+ /**
+ * @brief Extracts class meta information with given key.
+ * @param key Key to extract.
+ * @return Value of the meta information, or null if there's no information with given key.
+ *
+ * This is a helper method which queries Qt's meta object subsystem for class meta information defined with Q_CLASSINFO.
+ */
+ const char* getMetaInfo(const QString& key) const;
+};
+
+/**
+ * @def SQLITESTUDIO_PLUGIN_TITLE
+ * @brief Defines plugin title.
+ *
+ * This is a built-in plugin replacement for "title" key in external plugin's json metadata file.
+ */
+#define SQLITESTUDIO_PLUGIN_TITLE(Title) Q_CLASSINFO("title", Title)
+
+/**
+ * @def SQLITESTUDIO_PLUGIN_DESC
+ * @brief Defines plugin description.
+ *
+ * This is a built-in plugin replacement for "description" key in external plugin's json metadata file.
+ */
+#define SQLITESTUDIO_PLUGIN_DESC(Desc) Q_CLASSINFO("description", Desc)
+
+/**
+ * @def SQLITESTUDIO_PLUGIN_UI
+ * @brief Defines Qt Designer Form object name to be used in configuration dialog.
+ *
+ * This is a built-in plugin replacement for "ui" key in external plugin's json metadata file.
+ */
+#define SQLITESTUDIO_PLUGIN_UI(FormName) Q_CLASSINFO("ui", FormName)
+
+/**
+ * @def SQLITESTUDIO_PLUGIN_VERSION
+ * @brief Defines plugin version.
+ *
+ * This is a built-in plugin replacement for "version" key in external plugin's json metadata file.
+ */
+#define SQLITESTUDIO_PLUGIN_VERSION(Ver) Q_CLASSINFO("version", #Ver)
+
+/**
+ * @def SQLITESTUDIO_PLUGIN_AUTHOR
+ * @brief Defines an author of the plugin.
+ *
+ * This is a built-in plugin replacement for "author" key in external plugin's json metadata file.
+ */
+#define SQLITESTUDIO_PLUGIN_AUTHOR(Author) Q_CLASSINFO("author", Author)
+
+#endif // BUILTINPLUGIN_H
diff --git a/SQLiteStudio3/coreSQLiteStudio/plugins/codeformatterplugin.h b/SQLiteStudio3/coreSQLiteStudio/plugins/codeformatterplugin.h
new file mode 100644
index 0000000..093c20e
--- /dev/null
+++ b/SQLiteStudio3/coreSQLiteStudio/plugins/codeformatterplugin.h
@@ -0,0 +1,16 @@
+#ifndef CODEFORMATTERPLUGIN_H
+#define CODEFORMATTERPLUGIN_H
+
+#include "coreSQLiteStudio_global.h"
+#include "plugin.h"
+
+class Db;
+
+class API_EXPORT CodeFormatterPlugin : virtual public Plugin
+{
+ public:
+ virtual QString getLanguage() const = 0;
+ virtual QString format(const QString& code, Db* contextDb) = 0;
+};
+
+#endif // CODEFORMATTERPLUGIN_H
diff --git a/SQLiteStudio3/coreSQLiteStudio/plugins/confignotifiableplugin.h b/SQLiteStudio3/coreSQLiteStudio/plugins/confignotifiableplugin.h
new file mode 100644
index 0000000..d8a8f9b
--- /dev/null
+++ b/SQLiteStudio3/coreSQLiteStudio/plugins/confignotifiableplugin.h
@@ -0,0 +1,14 @@
+#ifndef CONFIGNOTIFIABLEPLUGIN_H
+#define CONFIGNOTIFIABLEPLUGIN_H
+
+#include <QVariant>
+
+class CfgEntry;
+
+class ConfigNotifiablePlugin
+{
+ public:
+ virtual void configModified(CfgEntry* key, const QVariant& value) = 0;
+};
+
+#endif // CONFIGNOTIFIABLEPLUGIN_H
diff --git a/SQLiteStudio3/coreSQLiteStudio/plugins/dbplugin.h b/SQLiteStudio3/coreSQLiteStudio/plugins/dbplugin.h
new file mode 100644
index 0000000..2f2e62e
--- /dev/null
+++ b/SQLiteStudio3/coreSQLiteStudio/plugins/dbplugin.h
@@ -0,0 +1,73 @@
+#ifndef DBPLUGIN_H
+#define DBPLUGIN_H
+
+#include "db/db.h"
+#include "db/dbpluginoption.h"
+#include "plugins/plugin.h"
+
+/**
+ * @brief Interface for database plugins
+ *
+ * This is a specialization of Plugin interface, which all database plugins have to implement.
+ */
+class API_EXPORT DbPlugin : virtual public Plugin
+{
+ public:
+ /**
+ * @brief Creates database instance defined by the plugin.
+ * @param name Name for the database.
+ * @param path Path to the database file.
+ * @param options Options for the database passed while registering the database in the application.
+ * @param errorMessage If the result is null (on failure) and this pointer is not null, the error message will be stored in it.
+ * @return Database instance on success, or null pointer on failure.
+ *
+ * Options can contain for example password for an encrypted database, or other connection options.
+ */
+ virtual Db* getInstance(const QString& name, const QString& path, const QHash<QString,QVariant> &options, QString* errorMessage = 0) = 0;
+
+ /**
+ * @brief Provides label of what type is the database.
+ * @return Type label.
+ *
+ * The label is used for presenting to the user what kind of database this is. It's used on GUI
+ * to display database type in databases dialog. It's usually either "SQLite3" or "SQLite2",
+ * but it may be something else, like for example encrypted database might provide "Encrypted SQLite3",
+ * or something similar.
+ */
+ virtual QString getLabel() const = 0;
+
+ /**
+ * @brief Provides list of options configurable for this database plugin.
+ * @return List of options.
+ *
+ * DbDialog uses this to provide GUI interface, so user can configure connection options.
+ * For each element in the list DbDialog adds QLabel and the input widget for entering option value.
+ * Option values entered by user are later passed to getInstance() as second argument.
+ */
+ virtual QList<DbPluginOption> getOptionsList() const = 0;
+
+ /**
+ * @brief Generates suggestion for database name.
+ * @param baseValue Value enterd as file path in DbDialog.
+ * @return Generated name suggestion.
+ *
+ * This can be simply string representation of \p baseValue,
+ * but the plugin may add something characteristic for the plugin.
+ */
+ virtual QString generateDbName(const QVariant& baseValue) = 0;
+
+ /**
+ * @brief Tests if the given database support is provided by this plugin.
+ * @param db Database to test.
+ * @return true if the database is supported by this plugin, or false otherwise.
+ *
+ * Implementation of this method should check if given database object
+ * is of the same type, that those returned from getInstance().
+ *
+ * This method is used by DbManager to find out which databases are supported by which plugins,
+ * so when some plugin is about to be unloaded, all its databases are closed properly first.
+ */
+ virtual bool checkIfDbServedByPlugin(Db* db) const = 0;
+};
+
+#endif // DBPLUGIN_H
diff --git a/SQLiteStudio3/coreSQLiteStudio/plugins/dbpluginsqlite3.cpp b/SQLiteStudio3/coreSQLiteStudio/plugins/dbpluginsqlite3.cpp
new file mode 100644
index 0000000..0f16098
--- /dev/null
+++ b/SQLiteStudio3/coreSQLiteStudio/plugins/dbpluginsqlite3.cpp
@@ -0,0 +1,47 @@
+#include "dbpluginsqlite3.h"
+#include "db/dbsqlite3.h"
+#include "common/unused.h"
+#include <QFileInfo>
+
+Db* DbPluginSqlite3::getInstance(const QString& name, const QString& path, const QHash<QString, QVariant>& options, QString* errorMessage)
+{
+ UNUSED(errorMessage);
+ Db* db = new DbSqlite3(name, path, options);
+
+ if (!db->openForProbing())
+ {
+ delete db;
+ return nullptr;
+ }
+
+ SqlQueryPtr results = db->exec("SELECT * FROM sqlite_master");
+ if (results->isError())
+ {
+ delete db;
+ return nullptr;
+ }
+
+ db->closeQuiet();
+ return db;
+}
+
+QString DbPluginSqlite3::getLabel() const
+{
+ return "SQLite 3";
+}
+
+QList<DbPluginOption> DbPluginSqlite3::getOptionsList() const
+{
+ return QList<DbPluginOption>();
+}
+
+QString DbPluginSqlite3::generateDbName(const QVariant& baseValue)
+{
+ QFileInfo file(baseValue.toString());
+ return file.baseName();
+}
+
+bool DbPluginSqlite3::checkIfDbServedByPlugin(Db* db) const
+{
+ return (db && dynamic_cast<DbSqlite3*>(db));
+}
diff --git a/SQLiteStudio3/coreSQLiteStudio/plugins/dbpluginsqlite3.h b/SQLiteStudio3/coreSQLiteStudio/plugins/dbpluginsqlite3.h
new file mode 100644
index 0000000..ee522d7
--- /dev/null
+++ b/SQLiteStudio3/coreSQLiteStudio/plugins/dbpluginsqlite3.h
@@ -0,0 +1,24 @@
+#ifndef DBPLUGINSQLITE3_H
+#define DBPLUGINSQLITE3_H
+
+#include "dbplugin.h"
+#include "builtinplugin.h"
+
+class DbPluginSqlite3 : public BuiltInPlugin, public DbPlugin
+{
+ Q_OBJECT
+
+ SQLITESTUDIO_PLUGIN_TITLE("SQLite 3")
+ SQLITESTUDIO_PLUGIN_DESC("SQLite 3 databases support.")
+ SQLITESTUDIO_PLUGIN_VERSION(10000)
+ SQLITESTUDIO_PLUGIN_AUTHOR("sqlitestudio.pl")
+
+ public:
+ Db* getInstance(const QString& name, const QString& path, const QHash<QString, QVariant>& options, QString* errorMessage);
+ QString getLabel() const;
+ QList<DbPluginOption> getOptionsList() const;
+ QString generateDbName(const QVariant& baseValue);
+ bool checkIfDbServedByPlugin(Db* db) const;
+};
+
+#endif // DBPLUGINSQLITE3_H
diff --git a/SQLiteStudio3/coreSQLiteStudio/plugins/exportplugin.h b/SQLiteStudio3/coreSQLiteStudio/plugins/exportplugin.h
new file mode 100644
index 0000000..06aded7
--- /dev/null
+++ b/SQLiteStudio3/coreSQLiteStudio/plugins/exportplugin.h
@@ -0,0 +1,372 @@
+#ifndef EXPORTPLUGIN_H
+#define EXPORTPLUGIN_H
+
+#include "plugin.h"
+#include "db/sqlquery.h"
+#include "db/queryexecutor.h"
+#include "services/exportmanager.h"
+#include "parser/ast/sqlitecreatetable.h"
+#include "parser/ast/sqlitecreateindex.h"
+#include "parser/ast/sqlitecreatetrigger.h"
+#include "parser/ast/sqlitecreateview.h"
+#include "parser/ast/sqlitecreatevirtualtable.h"
+
+class CfgMain;
+
+/**
+ * @brief Provides support for particular export format.
+ *
+ * All export methods in this class should report any warnings, error messages, etc through the NotifyManager,
+ * that is by using notifyError() and its family methods.
+ */
+class ExportPlugin : virtual public Plugin
+{
+ public:
+ /**
+ * @brief Provides name of the format that the plugin exports to.
+ * @return Format name that will be displayed on UI.
+ *
+ * Format must be a single word name.
+ */
+ virtual QString getFormatName() const = 0;
+
+ /**
+ * @brief Tells what standard exporting options should be displayed to the user on UI.
+ * @return OR-ed set of option enum values.
+ */
+ virtual ExportManager::StandardConfigFlags standardOptionsToEnable() const = 0;
+
+ /**
+ * @brief Tells which character encoding use by default in export dialog.
+ * @return Name of the encoding.
+ *
+ * If the plugin doesn't return ExportManager::CODEC in results from standardOptionsToEnable(), then result
+ * of this function is ignored and it can return null string.
+ */
+ virtual QString getDefaultEncoding() const = 0;
+
+ /**
+ * @brief Provides set of modes supported by this export plugin.
+ * @return OR-ed set of supported modes.
+ *
+ * Some export plugins might not support some of exporting modes. For example CSV export plugin
+ * will not support DATABASE exporting, because CSV cannot represent schema of the database.
+ *
+ * If a plugin doesn't return some mode in this method, then that plugin will be excluded
+ * from list of available formats to export, when user requests to export in this mode.
+ */
+ virtual ExportManager::ExportModes getSupportedModes() const = 0;
+
+ /**
+ * @brief Provides set of flags for additional information that needs to be provided for this plugin.
+ * @return OR-ed set of flags.
+ *
+ * Some plugins might need to know what is a total number of rows that are expected to be
+ * exported for each table or query results. Other plugins might want to know
+ * what is the maximum number of characters/bytes in each exported table column,
+ * so they can calculate column widths when drawing them in the exported files, documents, etc.
+ *
+ * Those additional information are not provided by default, because they are gathered with extra queries
+ * to the database and for huge tables it might cause the table to be exported much longer, even if
+ * those information wasn't required by some plugin.
+ *
+ * See ExportManager::ExportProviderFlags for list of possible flags and what they mean.
+ */
+ virtual ExportManager::ExportProviderFlags getProviderFlags() const = 0;
+
+ /**
+ * @brief Provides config object that holds configuration for exporting.
+ * @return Config object, or null if the exporting with this plugin is not configurable.
+ */
+ virtual CfgMain* getConfig() = 0;
+
+ /**
+ * @brief Provides name of the form to use for configuration of exporting in given mode.
+ * @param mode Mode for which the form is requested for.
+ * @return Name of the form (toplevel QWidget in the ui file).
+ *
+ * If exporting with this plugin is not configurable (i.e. getConfig() returns null),
+ * then this method is not even called, so it can return anything, just to satisfy method
+ * return type. In that case good idea is to always return QString::null.
+ *
+ * @see FormManager
+ */
+ virtual QString getExportConfigFormName() const = 0;
+
+ /**
+ * @brief Tells plugin what is going to be the next export mode.
+ * @param mode Mode that the next export is going to be performed for.
+ *
+ * Plugin should remember this and use later when some logic is depended on what the mode is.
+ * For example getConfigFormName() (as well as getConfig()) might return different config forms
+ * for different modes.
+ */
+ virtual void setExportMode(ExportManager::ExportMode mode) = 0;
+
+ /**
+ * @brief Called when the UI expects any configuration options to be re-validated.
+ *
+ * When user interacts with the UI in a way that it doesn't change the config values,
+ * but it still requires some options to be re-validated, this method is called.
+ *
+ * It should validate any configuration values defined with CFG_CATEGORY and CFG_ENTRY
+ * and post the validation results by calling EXPORT_MANAGER->handleValidationFromPlugin()
+ * for every validated CfgEntry.
+ *
+ * This is also a good idea to connect to the CfgEntry::changed() signal for entries that should be validated
+ * and call this method from the slot, so any changes to the configuration values will be
+ * immediately validated and reflected on the UI.
+ */
+ virtual void validateOptions() = 0;
+
+ /**
+ * @brief Provides usual file name extension used with this format.
+ * @return File name extension (like ".csv").
+ *
+ * This extension will be automatically appended to file name when user picked file name
+ * with file dialog, but the file extension part in the selected file was ommited.
+ */
+ virtual QString defaultFileExtension() const = 0;
+
+ /**
+ * @brief Mime type for when exporting binary format to clipboard.
+ * @return Mime type, like "image/png".
+ *
+ * Value returned from this method is used to set mime type when the exporting is done into the system clipboard.
+ * The clipboard needs mime type to identify what kind of data is in it.
+ *
+ * See details http://qt-project.org/doc/qt-5/qmimedata.html#setData
+ *
+ * If the plugin exports just a string, then this method can return QString::null and SqliteStudio will assume
+ * that the data is of "text/plain" type.
+ */
+ virtual QString getMimeType() const = 0;
+
+ /**
+ * @brief Tells if the data being exported is a binary or text.
+ * @return true for binary data, false for textual data.
+ *
+ * This is used by the SQLiteStudio to configure output device properly. For example CSV format is textual,
+ * but PNG is considered binary.
+ */
+ virtual bool isBinaryData() const = 0;
+
+ /**
+ * @brief Provides common state values before the export process begins.
+ * @param db Database that the export will be performed on.
+ * @param output Output device to write exporting data to.
+ * @param config Common exporting configuration, like file name, codec, etc.
+ * @return true for success, or false in case of a fatal error.
+ *
+ * This is called exactly once before every export process (that is once per each export called by user).
+ * Use it to remember database, output device, config for further method calls, or write a file header.
+ * This method will be followed by any of *export*() methods from this interface.
+ *
+ * There's a convenient class GenericExportPlugin, which you can extend instead of ExportPlugin. If you use
+ * GenericExportPlugin for a base class of exprt plugin, then this method is already implemented there
+ * and it stores all these parameters in protected class members so you can use them in other methods.
+ */
+ virtual bool initBeforeExport(Db* db, QIODevice* output, const ExportManager::StandardExportConfig& config) = 0;
+
+ /**
+ * @brief Does initial entry for exported query results.
+ * @param query Query that was executed to get the results.
+ * @param columns Columns returned from the query.
+ * @param providedData All data entries requested by the plugin in the return value of getProviderFlags().
+ * @return true for success, or false in case of a fatal error.
+ *
+ * It's called just before actual data entries are exported (with exportQueryResultsRow()).
+ * It's called exactly once for single query results export.
+ */
+ virtual bool beforeExportQueryResults(const QString& query, QList<QueryExecutor::ResultColumnPtr>& columns,
+ const QHash<ExportManager::ExportProviderFlag,QVariant> providedData) = 0;
+
+ /**
+ * @brief Does export entry for a single row of data.
+ * @param row Single data row.
+ * @return true for success, or false in case of a fatal error.
+ *
+ * It's called for each data row returned from the query.
+ */
+ virtual bool exportQueryResultsRow(SqlResultsRowPtr row) = 0;
+
+ /**
+ * @brief Does final entry for exported query results.
+ * @return true for success, or false in case of a fatal error.
+ *
+ * It's called once after all data from the query was exported.
+ */
+ virtual bool afterExportQueryResults() = 0;
+
+ /**
+ * @brief Prepares for exporting tables from database.
+ * @return true for success, or false in case of a fatal error.
+ *
+ * This is called only for database export. For single table export only exportTable() is called.
+ */
+ virtual bool beforeExportTables() = 0;
+
+ /**
+ * @brief Does initial entry for exported table.
+ * @param database "Attach" name of the database that the table belongs to. Can be "main", "temp", or any attach name.
+ * @param table Name of the table to export.
+ * @param columnNames Name of columns in the table, in order they will appear in the rows passed to exportTableRow().
+ * @param ddl The DDL of the table.
+ * @param createTable Table DDL parsed into an object.
+ * @param providedData All data entries requested by the plugin in the return value of getProviderFlags().
+ * @return true for success, or false in case of a fatal error.
+ */
+ virtual bool exportTable(const QString& database, const QString& table, const QStringList& columnNames, const QString& ddl, SqliteCreateTablePtr createTable,
+ const QHash<ExportManager::ExportProviderFlag,QVariant> providedData) = 0;
+
+ /**
+ * @brief Does initial entry for exported virtual table.
+ * @param database "Attach" name of the database that the table belongs to. Can be "main", "temp", or any attach name.
+ * @param table Name of the table to export.
+ * @param columnNames Name of columns in the table, in order they will appear in the rows passed to exportTableRow().
+ * @param ddl The DDL of the table.
+ * @param createTable Table DDL parsed into an object.
+ * @param providedData All data entries requested by the plugin in the return value of getProviderFlags().
+ * @return true for success, or false in case of a fatal error.
+ */
+ virtual bool exportVirtualTable(const QString& database, const QString& table, const QStringList& columnNames, const QString& ddl,
+ SqliteCreateVirtualTablePtr createTable, const QHash<ExportManager::ExportProviderFlag,QVariant> providedData) = 0;
+
+ /**
+ * @brief Does export entry for a single row of data.
+ * @param data Single data row.
+ * @return true for success, or false in case of a fatal error.
+ *
+ * This method will be called only if StandardExportConfig::exportData in initBeforeExport() was true.
+ */
+ virtual bool exportTableRow(SqlResultsRowPtr data) = 0;
+
+ /**
+ * @brief Does final entry for exported table, after its data was exported.
+ * @return true for success, or false in case of a fatal error.
+ */
+ virtual bool afterExportTable() = 0;
+
+ /**
+ * @brief Does final entries after all tables have been exported.
+ * @return true for success, or false in case of a fatal error.
+ *
+ * This is called only for database export. For single table export only exportTable() is called.
+ * After table exporting also an afterExport() is called, so you can use that for any postprocessing.
+ */
+ virtual bool afterExportTables() = 0;
+
+ /**
+ * @brief Does initial entry for the entire database export.
+ * @param database Database name (as listed in database list).
+ * @return true for success, or false in case of a fatal error.
+ *
+ * It's called just once, before each database object gets exported.
+ * This method will be followed by calls to (in this order): beforeExportTables(), exportTable(), exportVirtualTable(), exportTableRow(), afterExportTable(),
+ * afterExportTables(), beforeExportIndexes(), exportIndex(), afterExportIndexes(), beforeExportTriggers(), exportTrigger(), afterExportTriggers(),
+ * beforeExportViews(), exportView(), afterExportViews() and afterExportDatabase().
+ * Note, that exportTableRow() will be called only if StandardExportConfig::exportData in initBeforeExport() was true.
+ */
+ virtual bool beforeExportDatabase(const QString& database) = 0;
+
+ /**
+ * @brief Prepares for exporting indexes from database.
+ * @return true for success, or false in case of a fatal error.
+ */
+ virtual bool beforeExportIndexes() = 0;
+
+ /**
+ * @brief Does entire export entry for an index.
+ * @param database "Attach" name of the database that the index belongs to. Can be "main", "temp", or any attach name.
+ * @param table Name of the index to export.
+ * @param ddl The DDL of the index.
+ * @param createIndex Index DDL parsed into an object.
+ * @return true for success, or false in case of a fatal error.
+ *
+ * This is the only method called for index export.
+ */
+ virtual bool exportIndex(const QString& database, const QString& name, const QString& ddl, SqliteCreateIndexPtr createIndex) = 0;
+
+ /**
+ * @brief Does final entries after all indexes have been exported.
+ * @return true for success, or false in case of a fatal error.
+ */
+ virtual bool afterExportIndexes() = 0;
+
+ /**
+ * @brief Prepares for exporting triggers from database.
+ * @return true for success, or false in case of a fatal error.
+ */
+ virtual bool beforeExportTriggers() = 0;
+
+ /**
+ * @brief Does entire export entry for an trigger.
+ * @param database "Attach" name of the database that the trigger belongs to. Can be "main", "temp", or any attach name.
+ * @param table Name of the trigger to export.
+ * @param ddl The DDL of the trigger.
+ * @param createTrigger Trigger DDL parsed into an object.
+ * @return true for success, or false in case of a fatal error.
+ *
+ * This is the only method called for trigger export.
+ */
+ virtual bool exportTrigger(const QString& database, const QString& name, const QString& ddl, SqliteCreateTriggerPtr createTrigger) = 0;
+
+ /**
+ * @brief Does final entries after all triggers have been exported.
+ * @return true for success, or false in case of a fatal error.
+ */
+ virtual bool afterExportTriggers() = 0;
+
+ /**
+ * @brief Prepares for exporting views from database.
+ * @return true for success, or false in case of a fatal error.
+ */
+ virtual bool beforeExportViews() = 0;
+
+ /**
+ * @brief Does entire export entry for an view.
+ * @param database "Attach" name of the database that the view belongs to. Can be "main", "temp", or any attach name.
+ * @param table Name of the trigger to view.
+ * @param ddl The DDL of the view.
+ * @param createView View DDL parsed into an object.
+ * @return true for success, or false in case of a fatal error.
+ *
+ * This is the only method called for view export.
+ */
+ virtual bool exportView(const QString& database, const QString& name, const QString& ddl, SqliteCreateViewPtr view) = 0;
+
+ /**
+ * @brief Does final entries after all views have been exported.
+ * @return true for success, or false in case of a fatal error.
+ */
+ virtual bool afterExportViews() = 0;
+
+ /**
+ * @brief Does final entry for the entire database export.
+ * @return true for success, or false in case of a fatal error.
+ *
+ * It's called just once, after all database object get exported.
+ */
+ virtual bool afterExportDatabase() = 0;
+
+ /**
+ * @brief Does final entry for any export process.
+ * @return true for success, or false in case of a fatal error.
+ *
+ * This is similar to afterExportDatabase() when the export mode is database,
+ * but this is called at the end for any export mode, not only for database export.
+ *
+ * Use it to write a footer, or anything like that.
+ */
+ virtual bool afterExport() = 0;
+
+ /**
+ * @brief Called after every export, even failed one.
+ *
+ * Implementation of this method should cleanup any resources used during each single export process.
+ * This method is guaranteed to be executed, no matter if export was successful or not.
+ */
+ virtual void cleanupAfterExport() = 0;
+};
+
+#endif // EXPORTPLUGIN_H
diff --git a/SQLiteStudio3/coreSQLiteStudio/plugins/generalpurposeplugin.h b/SQLiteStudio3/coreSQLiteStudio/plugins/generalpurposeplugin.h
new file mode 100644
index 0000000..b50834a
--- /dev/null
+++ b/SQLiteStudio3/coreSQLiteStudio/plugins/generalpurposeplugin.h
@@ -0,0 +1,20 @@
+#ifndef GENERALPURPOSEPLUGIN_H
+#define GENERALPURPOSEPLUGIN_H
+
+#include "plugin.h"
+
+/**
+ * @brief The general purpose plugin interface.
+ *
+ * General purpose plugins are not designated for some specific function.
+ * They rely on init() and deinit() implementations to add some menubar entries,
+ * or toolbar entries (or anything else), so user can interact with the plugin.
+ *
+ * @see Plugin
+ * @see GenericPlugin
+ */
+class API_EXPORT GeneralPurposePlugin : virtual public Plugin
+{
+};
+
+#endif // GENERALPURPOSEPLUGIN_H
diff --git a/SQLiteStudio3/coreSQLiteStudio/plugins/genericexportplugin.cpp b/SQLiteStudio3/coreSQLiteStudio/plugins/genericexportplugin.cpp
new file mode 100644
index 0000000..d191ae4
--- /dev/null
+++ b/SQLiteStudio3/coreSQLiteStudio/plugins/genericexportplugin.cpp
@@ -0,0 +1,160 @@
+#include "genericexportplugin.h"
+#include "common/utils.h"
+#include "services/notifymanager.h"
+#include "common/unused.h"
+#include "config_builder.h"
+#include <QTextCodec>
+
+bool GenericExportPlugin::initBeforeExport(Db* db, QIODevice* output, const ExportManager::StandardExportConfig& config)
+{
+ this->db = db;
+ this->output = output;
+ this->config = &config;
+
+ if (standardOptionsToEnable().testFlag(ExportManager::CODEC))
+ {
+ codec = codecForName(this->config->codec);
+ if (!codec)
+ {
+ codec = defaultCodec();
+ notifyWarn(tr("Could not initialize text codec for exporting. Using default codec: %1").arg(QString::fromLatin1(codec->name())));
+ }
+ }
+
+ return beforeExport();
+}
+
+ExportManager::ExportModes GenericExportPlugin::getSupportedModes() const
+{
+ return ExportManager::FILE|ExportManager::CLIPBOARD|ExportManager::DATABASE|ExportManager::TABLE|ExportManager::QUERY_RESULTS;
+}
+
+ExportManager::ExportProviderFlags GenericExportPlugin::getProviderFlags() const
+{
+ return ExportManager::NONE;
+}
+
+QString GenericExportPlugin::getExportConfigFormName() const
+{
+ return QString();
+}
+
+CfgMain* GenericExportPlugin::getConfig()
+{
+ return nullptr;
+}
+
+QString GenericExportPlugin::getConfigFormName(ExportManager::ExportMode mode) const
+{
+ UNUSED(mode);
+ return QString::null;
+}
+
+QString GenericExportPlugin::getMimeType() const
+{
+ return QString::null;
+}
+
+QString GenericExportPlugin::getDefaultEncoding() const
+{
+ return QString();
+}
+
+bool GenericExportPlugin::isBinaryData() const
+{
+ return false;
+}
+
+void GenericExportPlugin::setExportMode(ExportManager::ExportMode mode)
+{
+ this->exportMode = mode;
+}
+
+bool GenericExportPlugin::afterExportQueryResults()
+{
+ return true;
+}
+
+bool GenericExportPlugin::afterExportTable()
+{
+ return true;
+}
+
+bool GenericExportPlugin::initBeforeExport()
+{
+ return true;
+}
+
+void GenericExportPlugin::write(const QString& str)
+{
+ output->write(codec->fromUnicode(str));
+}
+
+void GenericExportPlugin::writeln(const QString& str)
+{
+ write(str + "\n");
+}
+
+bool GenericExportPlugin::isTableExport() const
+{
+ return exportMode == ExportManager::TABLE;
+}
+
+bool GenericExportPlugin::beforeExportTables()
+{
+ return true;
+}
+
+bool GenericExportPlugin::afterExportTables()
+{
+ return true;
+}
+
+bool GenericExportPlugin::beforeExportIndexes()
+{
+ return true;
+}
+
+bool GenericExportPlugin::afterExportIndexes()
+{
+ return true;
+}
+
+bool GenericExportPlugin::beforeExportTriggers()
+{
+ return true;
+}
+
+bool GenericExportPlugin::afterExportTriggers()
+{
+ return true;
+}
+
+bool GenericExportPlugin::beforeExportViews()
+{
+ return true;
+}
+
+bool GenericExportPlugin::afterExportViews()
+{
+ return true;
+}
+
+bool GenericExportPlugin::afterExportDatabase()
+{
+ return true;
+}
+
+bool GenericExportPlugin::afterExport()
+{
+ return true;
+}
+
+void GenericExportPlugin::cleanupAfterExport()
+{
+}
+
+bool GenericExportPlugin::beforeExport()
+{
+ return true;
+}
diff --git a/SQLiteStudio3/coreSQLiteStudio/plugins/genericexportplugin.h b/SQLiteStudio3/coreSQLiteStudio/plugins/genericexportplugin.h
new file mode 100644
index 0000000..1719baa
--- /dev/null
+++ b/SQLiteStudio3/coreSQLiteStudio/plugins/genericexportplugin.h
@@ -0,0 +1,56 @@
+#ifndef GENERICEXPORTPLUGIN_H
+#define GENERICEXPORTPLUGIN_H
+
+#include "exportplugin.h"
+#include "genericplugin.h"
+
+class API_EXPORT GenericExportPlugin : virtual public GenericPlugin, public ExportPlugin
+{
+ public:
+ bool initBeforeExport(Db* db, QIODevice* output, const ExportManager::StandardExportConfig& config);
+ ExportManager::ExportModes getSupportedModes() const;
+ ExportManager::ExportProviderFlags getProviderFlags() const;
+ QString getExportConfigFormName() const;
+ CfgMain* getConfig();
+ QString getConfigFormName(ExportManager::ExportMode exportMode) const;
+ QString getMimeType() const;
+ QString getDefaultEncoding() const;
+ bool isBinaryData() const;
+ void setExportMode(ExportManager::ExportMode exportMode);
+ bool afterExportQueryResults();
+ bool afterExportTable();
+ bool beforeExportTables();
+ bool afterExportTables();
+ bool beforeExportIndexes();
+ bool afterExportIndexes();
+ bool beforeExportTriggers();
+ bool afterExportTriggers();
+ bool beforeExportViews();
+ bool afterExportViews();
+ bool afterExportDatabase();
+ bool afterExport();
+ void cleanupAfterExport();
+
+ /**
+ * @brief Does the initial entry in the export.
+ * @return true for success, or false in case of a fatal error.
+ *
+ * Use it to write the header, or something like that. Default implementation does nothing.
+ * This is called from initBeforeExport(), after exportMode and other settings are already prepared.
+ */
+ virtual bool beforeExport();
+
+ protected:
+ virtual bool initBeforeExport();
+ void write(const QString& str);
+ void writeln(const QString& str);
+ bool isTableExport() const;
+
+ Db* db = nullptr;
+ QIODevice* output = nullptr;
+ const ExportManager::StandardExportConfig* config = nullptr;
+ QTextCodec* codec = nullptr;
+ ExportManager::ExportMode exportMode = ExportManager::UNDEFINED;
+};
+
+#endif // GENERICEXPORTPLUGIN_H
diff --git a/SQLiteStudio3/coreSQLiteStudio/plugins/genericplugin.cpp b/SQLiteStudio3/coreSQLiteStudio/plugins/genericplugin.cpp
new file mode 100644
index 0000000..899691c
--- /dev/null
+++ b/SQLiteStudio3/coreSQLiteStudio/plugins/genericplugin.cpp
@@ -0,0 +1,67 @@
+#include "genericplugin.h"
+#include "services/pluginmanager.h"
+#include <QMetaClassInfo>
+
+QString GenericPlugin::getName() const
+{
+ return metaData["name"].toString();
+}
+
+QString GenericPlugin::getTitle() const
+{
+ if (!metaData["title"].isValid())
+ return getName();
+
+ return metaData["title"].toString();
+}
+
+CfgMain* GenericPlugin::getMainUiConfig()
+{
+ return nullptr;
+}
+
+QString GenericPlugin::getDescription() const
+{
+ return metaData["description"].toString();
+}
+
+int GenericPlugin::getVersion() const
+{
+ return metaData["version"].toInt();
+}
+
+QString GenericPlugin::getPrintableVersion() const
+{
+ return PLUGINS->toPrintableVersion(getVersion());
+}
+
+bool GenericPlugin::init()
+{
+ return true;
+}
+
+void GenericPlugin::deinit()
+{
+}
+
+void GenericPlugin::loadMetaData(const QJsonObject& metaData)
+{
+ this->metaData = PLUGINS->readMetaData(metaData);
+}
+
+const char* GenericPlugin::getMetaInfo(const QString& key) const
+{
+ for (int i = 0; i < metaObject()->classInfoCount(); i++)
+ {
+ if (key != metaObject()->classInfo(i).name())
+ continue;
+
+ return metaObject()->classInfo(i).value();
+ }
+ return nullptr;
+}
+
+QString GenericPlugin::getAuthor() const
+{
+ return metaData["author"].toString();
+}
diff --git a/SQLiteStudio3/coreSQLiteStudio/plugins/genericplugin.h b/SQLiteStudio3/coreSQLiteStudio/plugins/genericplugin.h
new file mode 100644
index 0000000..ce008e0
--- /dev/null
+++ b/SQLiteStudio3/coreSQLiteStudio/plugins/genericplugin.h
@@ -0,0 +1,126 @@
+#ifndef GENERICPLUGIN_H
+#define GENERICPLUGIN_H
+
+#include "plugin.h"
+#include <QObject>
+#include <QtPlugin>
+#include <QHash>
+#include <QVariant>
+
+/** @file */
+
+/**
+ * @brief Helper class for implementing plugins
+ *
+ * This class can be inherited, so most of the abstract methods from Plugin interface get implemented.
+ * All details (description, name, title, author, ...) are defined in separate json file.
+ *
+ * Most of plugin implementations will use this class as a base, because it simplifies process
+ * of plugin development. Using this class you don't have to implement any of virtual methods
+ * from Plugin interface. It's enough to define meta information in the json file, like this:
+ * @code
+ * {
+ * "type": "ScriptingPlugin",
+ * "title": "My plugin",
+ * "description": "Does nothing. It's an example plugin.",
+ * "version": 10000
+ * "author": "sqlitestudio.pl"
+ * };
+ * @endcode
+ *
+ * and then just declare the class as SQLiteStudio plugin, pointing the json file you just created:
+ * @code
+ * class MyPlugin : public GenericPlugin, public ScriptingPlugin
+ * {
+ * Q_OBJECT
+ * SQLITESTUDIO_PLUGIN("myplugin.json")
+ *
+ * // rest of the class
+ * };
+ * @endcode
+ */
+class API_EXPORT GenericPlugin : public QObject, public virtual Plugin
+{
+ Q_OBJECT
+ Q_INTERFACES(Plugin)
+
+ public:
+ /**
+ * @brief Provides plugin internal name.
+ * @return Plugin class name.
+ */
+ QString getName() const;
+
+ /**
+ * @brief Provides plugin title.
+ * @return Title defined in plugin's metadata file with key "title" or (if not defined) the same value as getName().
+ */
+ QString getTitle() const;
+
+ /**
+ * @brief Provides configuration object to use in ConfigDialog.
+ * @return This implementation always returns null.
+ */
+ CfgMain* getMainUiConfig();
+
+ /**
+ * @brief Provides plugin description.
+ * @return Description as defined in plugin's metadata file with key "description", or null QString if not defined.
+ */
+ QString getDescription() const;
+
+ /**
+ * @brief Provides plugin numeric version.
+ * @return Version number as defined in plugin's metadata file with key "version", or 0 if not defined.
+ */
+ int getVersion() const;
+
+ /**
+ * @brief Converts plugin version to human readable format.
+ * @return Version in format X.Y.Z.
+ */
+ QString getPrintableVersion() const;
+
+ /**
+ * @brief Provides an author name.
+ * @return Author name as defined with in plugin's metadata file with key "author", or null QString if not defined.
+ */
+ QString getAuthor() const;
+
+ /**
+ * @brief Does nothing.
+ * @return Always true.
+ *
+ * This is a default (empty) implementation of init() for plugins.
+ */
+ bool init();
+
+ /**
+ * @brief Does nothing.
+ *
+ * This is a default (empty) implementation of init() for plugins.
+ */
+ void deinit();
+
+ /**
+ * @brief Loads metadata from given Json object.
+ * @param The metadata from json file.
+ *
+ * This is called by PluginManager.
+ */
+ void loadMetaData(const QJsonObject& metaData);
+
+ private:
+ /**
+ * @brief Extracts class meta information with given key.
+ * @param key Key to extract.
+ * @return Value of the meta information, or null if there's no information with given key.
+ *
+ * This is a helper method which queries Qt's meta object subsystem for class meta information defined with Q_CLASSINFO.
+ */
+ const char* getMetaInfo(const QString& key) const;
+
+ QHash<QString,QVariant> metaData;
+};
+
+#endif // GENERICPLUGIN_H
diff --git a/SQLiteStudio3/coreSQLiteStudio/plugins/importplugin.h b/SQLiteStudio3/coreSQLiteStudio/plugins/importplugin.h
new file mode 100644
index 0000000..ff520cd
--- /dev/null
+++ b/SQLiteStudio3/coreSQLiteStudio/plugins/importplugin.h
@@ -0,0 +1,132 @@
+#ifndef IMPORTPLUGIN_H
+#define IMPORTPLUGIN_H
+
+#include "plugin.h"
+#include "services/importmanager.h"
+
+class QIODevice;
+class CfgMain;
+
+/**
+ * @brief Provides support for particular import format.
+ *
+ * All import methods in this class should report any warnings, error messages, etc through the NotifyManager,
+ * that is by using notifyError() and its family methods.
+ */
+class ImportPlugin : virtual public Plugin
+{
+ public:
+ /**
+ * @brief Pair of column name and its data type.
+ */
+ typedef QPair<QString,QString> ColumnDefinition;
+
+ /**
+ * @brief Used to show this plugin in the combo of data source types in the import dialog.
+ * @return String representing this plugin in the import dialog.
+ */
+ virtual QString getDataSourceTypeName() const = 0;
+
+ /**
+ * @brief Tells which standard import options should be available on to the user.
+ * @return OR-ed set of standard option enums.
+ */
+ virtual ImportManager::StandardConfigFlags standardOptionsToEnable() const = 0;
+
+ /**
+ * @brief Provides file name filter for file dialog.
+ * @return Filter compliant with QFileDialog documentation.
+ *
+ * If your plugin does not return ImportManager::FILE_NAME, this method can simply return QString::null.
+ * If your plugin does use input file name, then this method can (but don't have to) return file name filter
+ * to match expected files when user browses for the input file.
+ *
+ * The filter value (if not null) is passed directly to the QFileDialog.
+ */
+ virtual QString getFileFilter() const = 0;
+
+ /**
+ * @brief Called before each import that user makes.
+ * @param config Standard options configured by user in the import dialog.
+ * @return true if everything looks fine from plugin's perspective, or false otherwise.
+ *
+ * In case there were some problems at this step, plugin should return false, but before that it should tell what was wrong using NotifyManager's global shortcut
+ * method: notifyError().
+ */
+ virtual bool beforeImport(const ImportManager::StandardImportConfig& config) = 0;
+
+ /**
+ * @brief Called after import process has been finished (successfully or not).
+ *
+ * Implement this method to clean up any resources that the plugin has initialized before.
+ */
+ virtual void afterImport() = 0;
+
+ /**
+ * @brief Provides list of columns (with their datatypes) for the data to be imported.
+ * @return List of columns, each column consists of column name and its data type definition.
+ *
+ * The ColumnDefinition is actually a QPair of two QString types. First in the pair is column name, second is column's data type,
+ * as a string representation.
+ *
+ * Let's say your plugin wants to import data that fits into 2 columns, first of <tt>INTEGER</tt> type and the second of <tt>VARCHAR(0, 5)</tt> type.
+ * You would write it like this:
+ * @code
+ * QList<ColumnDefinition> list;
+ * list << ColumnDefinition("column1", "INTEGER");
+ * list << ColumnDefinition("column2", "VARCHAR (0, 5)");
+ * return list;
+ * @endcode
+ */
+ virtual QList<ColumnDefinition> getColumns() const = 0;
+
+ /**
+ * @brief Provides next set of data from the data source.
+ * @return List of values, where number of elements must be equal to number of columns returned from getColumns().
+ *
+ * This is essential import plugin method. It provides the data.
+ * This method simply provides next row of the data for a table.
+ * It will be called again and again, until it returns empty list, which will be interpreted as the end of data to import.
+ */
+ virtual QList<QVariant> next() = 0;
+
+ /**
+ * @brief Provides config object that holds configuration for importing.
+ * @return Config object, or null if the importing with this plugin is not configurable.
+ */
+ virtual CfgMain* getConfig() = 0;
+
+ /**
+ * @brief Provides name of the form to use for configuration of import dialog.
+ * @return Name of the form (toplevel QWidget in the ui file).
+ *
+ * If importing with this plugin is not configurable (i.e. getConfig() returns null),
+ * then this method is not even called, so it can return anything, just to satisfy method
+ * return type. In that case good idea is to always return QString::null.
+ *
+ * @see FormManager
+ */
+ virtual QString getImportConfigFormName() const = 0;
+
+ /**
+ * @brief Called when the UI expects any configuration options to be re-validated.
+ * @return true when validation was successful, or false if any error occured.
+ *
+ * When user interacts with the UI in a way that it doesn't change the config values,
+ * but it still requires some options to be re-validated, this method is called.
+ *
+ * It should validate any configuration values defined with CFG_CATEGORY and CFG_ENTRY
+ * and post the validation results by calling IMPORT_MANAGER->handleValidationFromPlugin()
+ * for every validated CfgEntry.
+ *
+ * This is also a good idea to connect to the CfgEntry::changed() signal for entries that should be validated
+ * and call this method from the slot, so any changes to the configuration values will be
+ * immediately validated and reflected on the UI.
+ *
+ * In this method you can also call IMPORT_MANAGER->configStateUpdateFromPlugin() to adjust options UI
+ * to the current config values.
+ */
+ virtual bool validateOptions() = 0;
+};
+
+#endif // IMPORTPLUGIN_H
diff --git a/SQLiteStudio3/coreSQLiteStudio/plugins/plugin.h b/SQLiteStudio3/coreSQLiteStudio/plugins/plugin.h
new file mode 100644
index 0000000..183adf7
--- /dev/null
+++ b/SQLiteStudio3/coreSQLiteStudio/plugins/plugin.h
@@ -0,0 +1,182 @@
+#ifndef PLUGIN_H
+#define PLUGIN_H
+
+#include "coreSQLiteStudio_global.h"
+#include <QString>
+#include <QHash>
+#include <QtPlugin>
+
+class PluginType;
+class CfgMain;
+
+/** @file */
+
+/**
+ * @brief General plugin interface.
+ *
+ * This is the top-most generic interface for SQLiteStudio plugins.
+ * It's based in Qt's plugins framework. Every SQLiteStudio plugin must
+ * implement this (or its descendant) interface.
+ *
+ * SQLiteStudio plugin is basicly class implementing this interface,
+ * compiled as shared library (*.dll, *.so, *.dylib).
+ *
+ * Apart from implementing Plugin interface, the plugin class must also declare ::SQLITESTUDIO_PLUGIN macro, like this:
+ * @code
+ * class MyPlugin : Plugin
+ * {
+ * Q_OBJECT
+ *
+ * SQLITESTUDIO_PLUGIN
+ *
+ * public:
+ * // ...
+ * };
+ * @endcode
+ *
+ * Full tutorial for writting plugins is at: http://wiki.sqlitestudio.pl/index.php/Writting_plugins
+ *
+ * SQLiteStudio looks for plugins in following directories:
+ * <ul>
+ * <li><tt>{current_executable_dir}/plugins</tt> - a "plugins" subdirectory of the directory where application binary is placed,</li>
+ * <li><tt>{configuration_dir}/plugins</tt> - a "plugins" subdirectory of configuration directory detected and defined in Config,</li>
+ * <li><tt>{env_var:SQLITESTUDIO_PLUGINS}</tt> - environment variable with name "SQLITESTUDIO_PLUGINS",</li>
+ * <li><tt>{compile_time:PLUGINS_DIR}</tt> - compile time defined parameter's value of parameter with the name "PLUGINS_DIR".</li>
+ * </ul>
+ */
+class API_EXPORT Plugin
+{
+ public:
+ /**
+ * @brief Virtual destructor to make sure all plugins are destroyed correctly.
+ */
+ virtual ~Plugin() {}
+
+ /**
+ * @brief Gets name of the plugin.
+ * @return Name of the plugin.
+ *
+ * The name of the plugin is a kind of primary key for plugins. It has to be unique across all loaded plugins.
+ * An attempt to load two plugins with the same name will result in failed load of the second plugin.
+ *
+ * The name is a kind of internal plugin's name. It's designated for presenting to the user
+ * - for that purpose there is a getTitle().
+ *
+ * It's a good practice to keep it as single word. Providing plugin's class name can be a good idea.
+ *
+ * BUG: Currently this implementation of this method has to always return the name of the plugin's main implementation class
+ * (like DbSqlite2), otherwise SQLiteStudio will either unable to load it, or dependencies to this plugin will fail.
+ * This has to do with PluginManagerImpl relying on "className" entry returned from QPluginLoader's metadata.
+ */
+ virtual QString getName() const = 0;
+
+ /**
+ * @brief Gets title for the plugin.
+ * @return Plugin title.
+ *
+ * This is plugin's name to be presented to the user. It can be multiple words name. It should be localized (translatable) text.
+ * It's used solely for presenting plugin to the user, nothing more.
+ */
+ virtual QString getTitle() const = 0;
+
+ /**
+ * @brief Provides name of the plugin's author.
+ * @return Author name.
+ *
+ * This is displayed in ConfigDialog when user clicks on Details button of the plugin.
+ */
+ virtual QString getAuthor() const = 0;
+
+ /**
+ * @brief Provides some details on what does the plugin.
+ * @return Plugin description.
+ *
+ * This is displayed in ConfigDialog when user clicks on Details button of the plugin.
+ */
+ virtual QString getDescription() const = 0;
+
+ /**
+ * @brief Provides plugin version number.
+ * @return Version number.
+ *
+ * Version number format can be picked by plugin developer, but it is recommended
+ * to use XXYYZZ, where XX is major version, YY is minor version and ZZ is patch version.
+ * Of course the XX can be single X if major version is less then 10.
+ *
+ * This would result in versions like: 10000 (for version 1.0.0), or 10102 (for version 1.1.2),
+ * or 123200 (for version 12.32.0).
+ *
+ * This is of course just a suggestion, you don't have to stick to it. Just keep in mind,
+ * that this number is used by SQLiteStudio to compare plugin versions. If there's a plugin with higher version,
+ * SQLiteStudio will propose to update it.
+ *
+ * The suggested format is also easier to convert to printable (string) version later in getPrintableVersion().
+ */
+ virtual int getVersion() const = 0;
+
+ /**
+ * @brief Provides formatted version string.
+ * @return Version string.
+ *
+ * It provides string that represents version returned from getVersion() in a human-readable form.
+ * It's a good practice to return versions like "1.3.2", or "1.5", as they are easy to read.
+ *
+ * This version string is presented to the user.
+ */
+ virtual QString getPrintableVersion() const = 0;
+
+ /**
+ * @brief Initializes plugin just after it was loaded.
+ * @return true on success, or false otherwise.
+ *
+ * This is called as a first, just after plugin was loaded. If it returns false,
+ * then plugin loading is considered to be failed and gets unloaded.
+ *
+ * If this method returns false, then deinit() is not called.
+ */
+ virtual bool init() = 0;
+
+ /**
+ * @brief Deinitializes plugin that is about to be unloaded.
+ *
+ * This is called just before plugin is unloaded. It's called only when plugin was loaded
+ * successfully. It's NOT called when init() returned false.
+ */
+ virtual void deinit() = 0;
+};
+
+/**
+ * @def SqliteStudioPluginInterface
+ * @brief SQLiteStudio plugin interface ID.
+ *
+ * This is an ID string for Qt's plugins framework. It's used by ::SQLITESTUDIO_PLUGIN macro.
+ * No need to use it directly.
+ */
+#define SqliteStudioPluginInterface "pl.sqlitestudio.Plugin/1.0"
+
+/**
+ * @def SQLITESTUDIO_PLUGIN
+ * @brief Defines class as a SQLiteStudio plugin
+ *
+ * Every class implementing SQLiteStudio plugin must have this declaration,
+ * otherwise SQLiteStudio won't be able to load the plugin.
+ *
+ * It has to be placed in class declaration:
+ * @code
+ * class MyPlugin : public QObject, public Plugin
+ * {
+ * Q_OBJECT
+ * SQLITESTUDIO_PLUGIN
+ *
+ * public:
+ * // ...
+ * }
+ * @endcode
+ */
+#define SQLITESTUDIO_PLUGIN(file)\
+ Q_PLUGIN_METADATA(IID SqliteStudioPluginInterface FILE file) \
+ Q_INTERFACES(Plugin)
+
+Q_DECLARE_INTERFACE(Plugin, SqliteStudioPluginInterface)
+
+#endif // PLUGIN_H
diff --git a/SQLiteStudio3/coreSQLiteStudio/plugins/pluginsymbolresolver.cpp b/SQLiteStudio3/coreSQLiteStudio/plugins/pluginsymbolresolver.cpp
new file mode 100644
index 0000000..39988bd
--- /dev/null
+++ b/SQLiteStudio3/coreSQLiteStudio/plugins/pluginsymbolresolver.cpp
@@ -0,0 +1,45 @@
+#include "pluginsymbolresolver.h"
+#include <QCoreApplication>
+#include <QDir>
+
+PluginSymbolResolver::PluginSymbolResolver()
+{
+}
+
+void PluginSymbolResolver::addFileNameMask(const QString &mask)
+{
+ nameFilters << mask;
+}
+
+void PluginSymbolResolver::addLookupSubFolder(const QString &name)
+{
+ subFolders << name;
+}
+
+bool PluginSymbolResolver::load()
+{
+ QStringList paths = qApp->libraryPaths();
+ foreach (QString path, paths)
+ foreach (QString subFolder, subFolders)
+ paths << path + "/" + subFolder;
+
+ foreach (QString path, paths)
+ {
+ QDir dir(path);
+ foreach (QString file, dir.entryList(nameFilters))
+ {
+ lib.setFileName(path+"/"+file);
+ if (lib.load())
+ break;
+ }
+ if (lib.isLoaded())
+ break;
+ }
+
+ return lib.isLoaded();
+}
+
+QFunctionPointer PluginSymbolResolver::resolve(const char *symbol)
+{
+ return lib.resolve(symbol);
+}
diff --git a/SQLiteStudio3/coreSQLiteStudio/plugins/pluginsymbolresolver.h b/SQLiteStudio3/coreSQLiteStudio/plugins/pluginsymbolresolver.h
new file mode 100644
index 0000000..da6c62e
--- /dev/null
+++ b/SQLiteStudio3/coreSQLiteStudio/plugins/pluginsymbolresolver.h
@@ -0,0 +1,24 @@
+#ifndef PLUGINSYMBOLRESOLVER_H
+#define PLUGINSYMBOLRESOLVER_H
+
+#include "coreSQLiteStudio_global.h"
+#include <QStringList>
+#include <QLibrary>
+
+class API_EXPORT PluginSymbolResolver
+{
+ public:
+ PluginSymbolResolver();
+
+ void addFileNameMask(const QString& mask);
+ void addLookupSubFolder(const QString& name);
+ bool load();
+ QFunctionPointer resolve(const char* symbol);
+
+ private:
+ QStringList nameFilters;
+ QStringList subFolders;
+ QLibrary lib;
+};
+
+#endif // PLUGINSYMBOLRESOLVER_H
diff --git a/SQLiteStudio3/coreSQLiteStudio/plugins/plugintype.cpp b/SQLiteStudio3/coreSQLiteStudio/plugins/plugintype.cpp
new file mode 100644
index 0000000..57c69c0
--- /dev/null
+++ b/SQLiteStudio3/coreSQLiteStudio/plugins/plugintype.cpp
@@ -0,0 +1,52 @@
+#include "plugin.h"
+#include "plugintype.h"
+#include "services/pluginmanager.h"
+#include <QDebug>
+
+PluginType::PluginType(const QString& title, const QString& form) :
+ title(title), configUiForm(form)
+{
+}
+
+
+PluginType::~PluginType()
+{
+}
+
+QString PluginType::getName() const
+{
+ return name;
+}
+
+void PluginType::setNativeName(const QString& nativeName)
+{
+ name = nativeName;
+ while (name.at(0).isDigit())
+ name = name.mid(1);
+}
+QString PluginType::getTitle() const
+{
+ return title;
+}
+
+QString PluginType::getConfigUiForm() const
+{
+ return configUiForm;
+}
+
+QList<Plugin*> PluginType::getLoadedPlugins() const
+{
+ PluginType* type = const_cast<PluginType*>(this);
+ return PLUGINS->getLoadedPlugins(type);
+}
+
+QStringList PluginType::getAllPluginNames() const
+{
+ PluginType* type = const_cast<PluginType*>(this);
+ return PLUGINS->getAllPluginNames(type);
+}
+
+bool PluginType::nameLessThan(PluginType* type1, PluginType* type2)
+{
+ return type1->title.compare(type2->title) < 0;
+}
diff --git a/SQLiteStudio3/coreSQLiteStudio/plugins/plugintype.h b/SQLiteStudio3/coreSQLiteStudio/plugins/plugintype.h
new file mode 100644
index 0000000..1002ac8
--- /dev/null
+++ b/SQLiteStudio3/coreSQLiteStudio/plugins/plugintype.h
@@ -0,0 +1,63 @@
+#ifndef PLUGINTYPE_H
+#define PLUGINTYPE_H
+
+#include "coreSQLiteStudio_global.h"
+#include <QList>
+#include <QString>
+#include <QObject>
+
+class Plugin;
+
+template <class T>
+class DefinedPluginType;
+
+class API_EXPORT PluginType
+{
+ public:
+ virtual ~PluginType();
+
+ QString getName() const;
+ QString getTitle() const;
+ QString getConfigUiForm() const;
+ QList<Plugin*> getLoadedPlugins() const;
+ QStringList getAllPluginNames() const;
+
+ virtual bool test(Plugin* plugin) = 0;
+
+ template <class T>
+ bool isForPluginType()
+ {
+ return dynamic_cast<const DefinedPluginType<T>*>(this) != nullptr;
+ }
+
+ static bool nameLessThan(PluginType* type1, PluginType* type2);
+
+ protected:
+ PluginType(const QString& title, const QString& form);
+ void setNativeName(const QString& nativeName);
+
+ QString title;
+ QString configUiForm;
+ QString name;
+};
+
+
+template <class T>
+class DefinedPluginType : public PluginType
+{
+ friend class PluginManager;
+
+ public:
+ bool test(Plugin* plugin)
+ {
+ return (dynamic_cast<T*>(plugin) != nullptr);
+ }
+
+ protected:
+ DefinedPluginType(const QString& title, const QString& form) : PluginType(title, form)
+ {
+ setNativeName(typeid(T).name());
+ }
+};
+
+#endif // PLUGINTYPE_H
diff --git a/SQLiteStudio3/coreSQLiteStudio/plugins/populateconstant.cpp b/SQLiteStudio3/coreSQLiteStudio/plugins/populateconstant.cpp
new file mode 100644
index 0000000..75c213f
--- /dev/null
+++ b/SQLiteStudio3/coreSQLiteStudio/plugins/populateconstant.cpp
@@ -0,0 +1,48 @@
+#include "populateconstant.h"
+#include "common/unused.h"
+
+PopulateConstant::PopulateConstant()
+{
+}
+
+QString PopulateConstant::getTitle() const
+{
+ return tr("Constant", "populate constant plugin name");
+}
+
+PopulateEngine*PopulateConstant::createEngine()
+{
+ return new PopulateConstantEngine();
+}
+
+bool PopulateConstantEngine::beforePopulating(Db* db, const QString& table)
+{
+ UNUSED(db);
+ UNUSED(table);
+ return true;
+}
+
+QVariant PopulateConstantEngine::nextValue(bool& nextValueError)
+{
+ UNUSED(nextValueError);
+ return cfg.PopulateConstant.Value.get();
+}
+
+void PopulateConstantEngine::afterPopulating()
+{
+}
+
+CfgMain*PopulateConstantEngine::getConfig()
+{
+ return &cfg;
+}
+
+QString PopulateConstantEngine::getPopulateConfigFormName() const
+{
+ return QStringLiteral("PopulateConstantConfig");
+}
+
+bool PopulateConstantEngine::validateOptions()
+{
+ return true;
+}
diff --git a/SQLiteStudio3/coreSQLiteStudio/plugins/populateconstant.h b/SQLiteStudio3/coreSQLiteStudio/plugins/populateconstant.h
new file mode 100644
index 0000000..1061519
--- /dev/null
+++ b/SQLiteStudio3/coreSQLiteStudio/plugins/populateconstant.h
@@ -0,0 +1,44 @@
+#ifndef POPULATECONSTANT_H
+#define POPULATECONSTANT_H
+
+#include "builtinplugin.h"
+#include "populateplugin.h"
+#include "config_builder.h"
+
+CFG_CATEGORIES(PopulateConstantConfig,
+ CFG_CATEGORY(PopulateConstant,
+ CFG_ENTRY(QString, Value, QString())
+ )
+)
+
+class PopulateConstant : public BuiltInPlugin, public PopulatePlugin
+{
+ Q_OBJECT
+
+ SQLITESTUDIO_PLUGIN_TITLE("Constant")
+ SQLITESTUDIO_PLUGIN_DESC("Support for populating tables with a constant value.")
+ SQLITESTUDIO_PLUGIN_VERSION(10001)
+ SQLITESTUDIO_PLUGIN_AUTHOR("sqlitestudio.pl")
+
+ public:
+ PopulateConstant();
+
+ QString getTitle() const;
+ PopulateEngine* createEngine();
+};
+
+class PopulateConstantEngine : public PopulateEngine
+{
+ public:
+ bool beforePopulating(Db* db, const QString& table);
+ QVariant nextValue(bool& nextValueError);
+ void afterPopulating();
+ CfgMain* getConfig();
+ QString getPopulateConfigFormName() const;
+ bool validateOptions();
+
+ private:
+ CFG_LOCAL(PopulateConstantConfig, cfg)
+};
+
+#endif // POPULATECONSTANT_H
diff --git a/SQLiteStudio3/coreSQLiteStudio/plugins/populateconstant.ui b/SQLiteStudio3/coreSQLiteStudio/plugins/populateconstant.ui
new file mode 100644
index 0000000..39e80e1
--- /dev/null
+++ b/SQLiteStudio3/coreSQLiteStudio/plugins/populateconstant.ui
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>PopulateConstantConfig</class>
+ <widget class="QWidget" name="PopulateConstantConfig">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>400</width>
+ <height>77</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Form</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <item>
+ <widget class="QGroupBox" name="valueGroup">
+ <property name="title">
+ <string>Constant value:</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_2">
+ <item>
+ <widget class="QLineEdit" name="valueEdit">
+ <property name="cfg" stdset="0">
+ <string>PopulateConstant.Value</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/SQLiteStudio3/coreSQLiteStudio/plugins/populatedictionary.cpp b/SQLiteStudio3/coreSQLiteStudio/plugins/populatedictionary.cpp
new file mode 100644
index 0000000..3d78de5
--- /dev/null
+++ b/SQLiteStudio3/coreSQLiteStudio/plugins/populatedictionary.cpp
@@ -0,0 +1,94 @@
+#include "populatedictionary.h"
+#include "services/populatemanager.h"
+#include "services/notifymanager.h"
+#include "common/unused.h"
+#include <QFileInfo>
+#include <QFile>
+#include <QTextStream>
+
+PopulateDictionary::PopulateDictionary()
+{
+}
+
+QString PopulateDictionary::getTitle() const
+{
+ return tr("Dictionary", "dictionary populating plugin name");
+}
+
+PopulateEngine*PopulateDictionary::createEngine()
+{
+ return new PopulateDictionaryEngine();
+}
+
+bool PopulateDictionaryEngine::beforePopulating(Db* db, const QString& table)
+{
+ UNUSED(db);
+ UNUSED(table);
+ QFile file(cfg.PopulateDictionary.File.get());
+ if (!file.open(QIODevice::ReadOnly))
+ {
+ notifyError(QObject::tr("Could not open dictionary file %1 for reading.").arg(cfg.PopulateDictionary.File.get()));
+ return false;
+ }
+ QTextStream stream(&file);
+ QString dataStr = stream.readAll();
+ file.close();
+
+ if (cfg.PopulateDictionary.Lines.get())
+ dictionary = dataStr.split("\n");
+ else
+ dictionary = dataStr.split(QRegExp("\\s+"));
+
+ if (dictionary.size() == 0)
+ dictionary << QString();
+
+ dictionaryPos = 0;
+ dictionarySize = dictionary.size();
+ if (cfg.PopulateDictionary.Random.get())
+ qsrand(QDateTime::currentDateTime().toTime_t());
+
+ return true;
+}
+
+QVariant PopulateDictionaryEngine::nextValue(bool& nextValueError)
+{
+ UNUSED(nextValueError);
+ if (cfg.PopulateDictionary.Random.get())
+ {
+ int r = qrand() % dictionarySize;
+ return dictionary[r];
+ }
+ else
+ {
+ if (dictionaryPos >= dictionarySize)
+ dictionaryPos = 0;
+
+ return dictionary[dictionaryPos++];
+ }
+}
+
+void PopulateDictionaryEngine::afterPopulating()
+{
+ dictionary.clear();
+ dictionarySize = 0;
+ dictionaryPos = 0;
+}
+
+CfgMain* PopulateDictionaryEngine::getConfig()
+{
+ return &cfg;
+}
+
+QString PopulateDictionaryEngine::getPopulateConfigFormName() const
+{
+ return QStringLiteral("PopulateDictionaryConfig");
+}
+
+bool PopulateDictionaryEngine::validateOptions()
+{
+ QFileInfo fi(cfg.PopulateDictionary.File.get());
+ bool fileValid = fi.exists() && fi.isReadable() && !fi.isDir();
+ POPULATE_MANAGER->handleValidationFromPlugin(fileValid, cfg.PopulateDictionary.File, QObject::tr("Dictionary file must exist and be readable."));
+
+ return fileValid;
+}
diff --git a/SQLiteStudio3/coreSQLiteStudio/plugins/populatedictionary.h b/SQLiteStudio3/coreSQLiteStudio/plugins/populatedictionary.h
new file mode 100644
index 0000000..f5e754f
--- /dev/null
+++ b/SQLiteStudio3/coreSQLiteStudio/plugins/populatedictionary.h
@@ -0,0 +1,52 @@
+#ifndef POPULATEDICTIONARY_H
+#define POPULATEDICTIONARY_H
+
+#include "builtinplugin.h"
+#include "populateplugin.h"
+#include "config_builder.h"
+
+class QFile;
+class QTextStream;
+
+CFG_CATEGORIES(PopulateDictionaryConfig,
+ CFG_CATEGORY(PopulateDictionary,
+ CFG_ENTRY(QString, File, QString())
+ CFG_ENTRY(bool, Lines, false)
+ CFG_ENTRY(bool, Random, false)
+ )
+)
+
+class PopulateDictionary : public BuiltInPlugin, public PopulatePlugin
+{
+ Q_OBJECT
+
+ SQLITESTUDIO_PLUGIN_TITLE("Dictionary")
+ SQLITESTUDIO_PLUGIN_DESC("Support for populating tables with values from a dictionary file.")
+ SQLITESTUDIO_PLUGIN_VERSION(10001)
+ SQLITESTUDIO_PLUGIN_AUTHOR("sqlitestudio.pl")
+
+ public:
+ PopulateDictionary();
+
+ QString getTitle() const;
+ PopulateEngine* createEngine();
+};
+
+class PopulateDictionaryEngine : public PopulateEngine
+{
+ public:
+ bool beforePopulating(Db* db, const QString& table);
+ QVariant nextValue(bool& nextValueError);
+ void afterPopulating();
+ CfgMain* getConfig();
+ QString getPopulateConfigFormName() const;
+ bool validateOptions();
+
+ private:
+ CFG_LOCAL(PopulateDictionaryConfig, cfg)
+ QStringList dictionary;
+ int dictionarySize = 0;
+ int dictionaryPos = 0;
+};
+
+#endif // POPULATEDICTIONARY_H
diff --git a/SQLiteStudio3/coreSQLiteStudio/plugins/populatedictionary.ui b/SQLiteStudio3/coreSQLiteStudio/plugins/populatedictionary.ui
new file mode 100644
index 0000000..f99491f
--- /dev/null
+++ b/SQLiteStudio3/coreSQLiteStudio/plugins/populatedictionary.ui
@@ -0,0 +1,123 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>PopulateDictionaryConfig</class>
+ <widget class="QWidget" name="PopulateDictionaryConfig">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>307</width>
+ <height>255</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Form</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <item>
+ <widget class="QGroupBox" name="fileGroup">
+ <property name="title">
+ <string>Dictionary file</string>
+ </property>
+ <layout class="QHBoxLayout" name="horizontalLayout">
+ <item>
+ <widget class="FileEdit" name="fileEdit" native="true">
+ <property name="cfg" stdset="0">
+ <string>PopulateDictionary.File</string>
+ </property>
+ <property name="dialogTitle" stdset="0">
+ <string>Pick dictionary file</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="separatorGroup">
+ <property name="title">
+ <string>Word separator</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_2">
+ <item>
+ <widget class="ConfigRadioButton" name="whitespaceRadio">
+ <property name="cfg" stdset="0">
+ <string>PopulateDictionary.Lines</string>
+ </property>
+ <property name="text">
+ <string>Whitespace</string>
+ </property>
+ <property name="assignedValue" stdset="0">
+ <bool>false</bool>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="ConfigRadioButton" name="libeBreakRadio">
+ <property name="cfg" stdset="0">
+ <string>PopulateDictionary.Lines</string>
+ </property>
+ <property name="text">
+ <string>Line break</string>
+ </property>
+ <property name="assignedValue" stdset="0">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="methodGroup">
+ <property name="title">
+ <string>Method of using words</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_3">
+ <item>
+ <widget class="ConfigRadioButton" name="orderedRadio">
+ <property name="cfg" stdset="0">
+ <string>PopulateDictionary.Random</string>
+ </property>
+ <property name="text">
+ <string>Ordered</string>
+ </property>
+ <property name="assignedValue" stdset="0">
+ <bool>false</bool>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="ConfigRadioButton" name="randomlyRadio">
+ <property name="cfg" stdset="0">
+ <string>PopulateDictionary.Random</string>
+ </property>
+ <property name="text">
+ <string>Randomly</string>
+ </property>
+ <property name="assignedValue" stdset="0">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <customwidgets>
+ <customwidget>
+ <class>ConfigRadioButton</class>
+ <extends>QRadioButton</extends>
+ <header>common/configradiobutton.h</header>
+ </customwidget>
+ <customwidget>
+ <class>FileEdit</class>
+ <extends>QWidget</extends>
+ <header>common/fileedit.h</header>
+ <container>1</container>
+ </customwidget>
+ </customwidgets>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/SQLiteStudio3/coreSQLiteStudio/plugins/populateplugin.h b/SQLiteStudio3/coreSQLiteStudio/plugins/populateplugin.h
new file mode 100644
index 0000000..1a1db43
--- /dev/null
+++ b/SQLiteStudio3/coreSQLiteStudio/plugins/populateplugin.h
@@ -0,0 +1,70 @@
+#ifndef POPULATEPLUGIN_H
+#define POPULATEPLUGIN_H
+
+#include "coreSQLiteStudio_global.h"
+#include "plugins/plugin.h"
+
+class CfgMain;
+class PopulateEngine;
+class Db;
+
+class API_EXPORT PopulatePlugin : virtual public Plugin
+{
+ public:
+ virtual PopulateEngine* createEngine() = 0;
+};
+
+class API_EXPORT PopulateEngine
+{
+ public:
+ virtual ~PopulateEngine() {}
+
+ virtual bool beforePopulating(Db* db, const QString& table) = 0;
+ virtual QVariant nextValue(bool& nextValueError) = 0;
+ virtual void afterPopulating() = 0;
+
+ /**
+ * @brief Provides config object that holds configuration for populating.
+ * @return Config object, or null if the importing with this plugin is not configurable.
+ */
+ virtual CfgMain* getConfig() = 0;
+
+ /**
+ * @brief Provides name of the form to use for configuration of this plugin in the populate dialog.
+ * @return Name of the form (toplevel QWidget in the ui file).
+ *
+ * If populating with this plugin is not configurable (i.e. getConfig() returns null),
+ * then this method is not even called, so it can return anything, just to satisfy method
+ * return type. In that case good idea is to always return QString::null.
+ *
+ * @see FormManager
+ */
+ virtual QString getPopulateConfigFormName() const = 0;
+
+ /**
+ * @brief Called when the UI expects any configuration options to be re-validated.
+ * @return true if the validation was successful, or false otherwise.
+ *
+ * When user interacts with the UI in a way that it doesn't change the config values,
+ * but it still requires some options to be re-validated, this method is called.
+ *
+ * It should validate any configuration values defined with CFG_CATEGORY and CFG_ENTRY
+ * and post the validation results by calling POPULATE_MANAGER->handleValidationFromPlugin()
+ * for every validated CfgEntry.
+ *
+ * This is also a good idea to connect to the CfgEntry::changed() signal for entries that should be validated
+ * and call this method from the slot, so any changes to the configuration values will be
+ * immediately validated and reflected on the UI.
+ *
+ * In this method you can also call POPULATE_MANAGER->configStateUpdateFromPlugin() to adjust options UI
+ * to the current config values.
+ *
+ * Apart from calling POPULATE_MANAGER with validation results, it should also return true or false,
+ * according to validation results. The return value is used by the PopulateDialog to tell if the plugin
+ * is currently configured correctly, without going into details, without handling signals from POPULATE_MANAGER.
+ */
+ virtual bool validateOptions() = 0;
+};
+
+
+#endif // POPULATEPLUGIN_H
diff --git a/SQLiteStudio3/coreSQLiteStudio/plugins/populaterandom.cpp b/SQLiteStudio3/coreSQLiteStudio/plugins/populaterandom.cpp
new file mode 100644
index 0000000..3258bbc
--- /dev/null
+++ b/SQLiteStudio3/coreSQLiteStudio/plugins/populaterandom.cpp
@@ -0,0 +1,55 @@
+#include "populaterandom.h"
+#include "services/populatemanager.h"
+#include "common/unused.h"
+#include <QDateTime>
+
+PopulateRandom::PopulateRandom()
+{
+}
+
+QString PopulateRandom::getTitle() const
+{
+ return tr("Random number");
+}
+
+PopulateEngine* PopulateRandom::createEngine()
+{
+ return new PopulateRandomEngine();
+}
+
+bool PopulateRandomEngine::beforePopulating(Db* db, const QString& table)
+{
+ UNUSED(db);
+ UNUSED(table);
+ qsrand(QDateTime::currentDateTime().toTime_t());
+ range = cfg.PopulateRandom.MaxValue.get() - cfg.PopulateRandom.MinValue.get() + 1;
+ return (range > 0);
+}
+
+QVariant PopulateRandomEngine::nextValue(bool& nextValueError)
+{
+ UNUSED(nextValueError);
+ QString randValue = QString::number((qrand() % range) + cfg.PopulateRandom.MinValue.get());
+ return (cfg.PopulateRandom.Prefix.get() + randValue + cfg.PopulateRandom.Suffix.get());
+}
+
+void PopulateRandomEngine::afterPopulating()
+{
+}
+
+CfgMain* PopulateRandomEngine::getConfig()
+{
+ return &cfg;
+}
+
+QString PopulateRandomEngine::getPopulateConfigFormName() const
+{
+ return QStringLiteral("PopulateRandomConfig");
+}
+
+bool PopulateRandomEngine::validateOptions()
+{
+ bool valid = (cfg.PopulateRandom.MinValue.get() <= cfg.PopulateRandom.MaxValue.get());
+ POPULATE_MANAGER->handleValidationFromPlugin(valid, cfg.PopulateRandom.MaxValue, QObject::tr("Maximum value cannot be less than minimum value."));
+ return valid;
+}
diff --git a/SQLiteStudio3/coreSQLiteStudio/plugins/populaterandom.h b/SQLiteStudio3/coreSQLiteStudio/plugins/populaterandom.h
new file mode 100644
index 0000000..f4e9feb
--- /dev/null
+++ b/SQLiteStudio3/coreSQLiteStudio/plugins/populaterandom.h
@@ -0,0 +1,47 @@
+#ifndef POPULATERANDOM_H
+#define POPULATERANDOM_H
+
+#include "builtinplugin.h"
+#include "populateplugin.h"
+#include "config_builder.h"
+
+CFG_CATEGORIES(PopulateRandomConfig,
+ CFG_CATEGORY(PopulateRandom,
+ CFG_ENTRY(int, MinValue, 0)
+ CFG_ENTRY(int, MaxValue, 99999999)
+ CFG_ENTRY(QString, Prefix, QString())
+ CFG_ENTRY(QString, Suffix, QString())
+ )
+)
+
+class PopulateRandom : public BuiltInPlugin, public PopulatePlugin
+{
+ Q_OBJECT
+
+ SQLITESTUDIO_PLUGIN_TITLE("Random")
+ SQLITESTUDIO_PLUGIN_DESC("Support for populating tables with random numbers.")
+ SQLITESTUDIO_PLUGIN_VERSION(10001)
+ SQLITESTUDIO_PLUGIN_AUTHOR("sqlitestudio.pl")
+
+ public:
+ PopulateRandom();
+
+ QString getTitle() const;
+ PopulateEngine* createEngine();
+};
+
+class PopulateRandomEngine : public PopulateEngine
+{
+ public:
+ bool beforePopulating(Db* db, const QString& table);
+ QVariant nextValue(bool& nextValueError);
+ void afterPopulating();
+ CfgMain* getConfig();
+ QString getPopulateConfigFormName() const;
+ bool validateOptions();
+
+ private:
+ CFG_LOCAL(PopulateRandomConfig, cfg)
+ int range;
+};
+#endif // POPULATERANDOM_H
diff --git a/SQLiteStudio3/coreSQLiteStudio/plugins/populaterandom.ui b/SQLiteStudio3/coreSQLiteStudio/plugins/populaterandom.ui
new file mode 100644
index 0000000..fb304ea
--- /dev/null
+++ b/SQLiteStudio3/coreSQLiteStudio/plugins/populaterandom.ui
@@ -0,0 +1,106 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>PopulateRandomConfig</class>
+ <widget class="QWidget" name="PopulateRandomConfig">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>400</width>
+ <height>144</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Form</string>
+ </property>
+ <layout class="QGridLayout" name="gridLayout">
+ <item row="2" column="0">
+ <widget class="QGroupBox" name="prefixGroup">
+ <property name="title">
+ <string>Constant prefix</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_4">
+ <item>
+ <widget class="QLineEdit" name="prefixEdit">
+ <property name="cfg" stdset="0">
+ <string>PopulateRandom.Prefix</string>
+ </property>
+ <property name="placeholderText">
+ <string>No prefix</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item row="0" column="0">
+ <widget class="QGroupBox" name="minGroup">
+ <property name="title">
+ <string>Minimum value</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_2">
+ <item>
+ <widget class="QSpinBox" name="minSpin">
+ <property name="cfg" stdset="0">
+ <string>PopulateRandom.MinValue</string>
+ </property>
+ <property name="minimum">
+ <number>-999999999</number>
+ </property>
+ <property name="maximum">
+ <number>999999999</number>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QGroupBox" name="maxGroup">
+ <property name="title">
+ <string>Maximum value</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_3">
+ <item>
+ <widget class="QSpinBox" name="maxSpin">
+ <property name="cfg" stdset="0">
+ <string>PopulateRandom.MaxValue</string>
+ </property>
+ <property name="minimum">
+ <number>-999999999</number>
+ </property>
+ <property name="maximum">
+ <number>999999999</number>
+ </property>
+ <property name="value">
+ <number>999999999</number>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item row="2" column="1">
+ <widget class="QGroupBox" name="suffixGroup">
+ <property name="title">
+ <string>Constant suffix</string>
+ </property>
+ <layout class="QHBoxLayout" name="horizontalLayout">
+ <item>
+ <widget class="QLineEdit" name="suffixEdit">
+ <property name="cfg" stdset="0">
+ <string>PopulateRandom.Suffix</string>
+ </property>
+ <property name="placeholderText">
+ <string>No suffix</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/SQLiteStudio3/coreSQLiteStudio/plugins/populaterandomtext.cpp b/SQLiteStudio3/coreSQLiteStudio/plugins/populaterandomtext.cpp
new file mode 100644
index 0000000..d9f148a
--- /dev/null
+++ b/SQLiteStudio3/coreSQLiteStudio/plugins/populaterandomtext.cpp
@@ -0,0 +1,91 @@
+#include "populaterandomtext.h"
+#include "common/utils.h"
+#include "common/unused.h"
+#include "services/populatemanager.h"
+
+PopulateRandomText::PopulateRandomText()
+{
+}
+
+QString PopulateRandomText::getTitle() const
+{
+ return tr("Random text");
+}
+
+PopulateEngine* PopulateRandomText::createEngine()
+{
+ return new PopulateRandomTextEngine();
+}
+
+bool PopulateRandomTextEngine::beforePopulating(Db* db, const QString& table)
+{
+ UNUSED(db);
+ UNUSED(table);
+ qsrand(QDateTime::currentDateTime().toTime_t());
+ range = cfg.PopulateRandomText.MaxLength.get() - cfg.PopulateRandomText.MinLength.get() + 1;
+
+ chars = "";
+
+ if (cfg.PopulateRandomText.UseCustomSets.get())
+ {
+ chars = cfg.PopulateRandomText.CustomCharacters.get();
+ }
+ else if (cfg.PopulateRandomText.IncludeBinary.get())
+ {
+ for (int i = 0; i < 256; i++)
+ chars.append(QChar((char)i));
+ }
+ else
+ {
+ if (cfg.PopulateRandomText.IncludeAlpha.get())
+ chars += QStringLiteral("abcdefghijklmnopqrstuvwxyz");
+
+ if (cfg.PopulateRandomText.IncludeNumeric.get())
+ chars += QStringLiteral("0123456789");
+
+ if (cfg.PopulateRandomText.IncludeWhitespace.get())
+ chars += QStringLiteral(" \t\n");
+ }
+
+ return !chars.isEmpty();
+}
+
+QVariant PopulateRandomTextEngine::nextValue(bool& nextValueError)
+{
+ UNUSED(nextValueError);
+ int lgt = (qrand() % range) + cfg.PopulateRandomText.MinLength.get();
+ return randStr(lgt, chars);
+}
+
+void PopulateRandomTextEngine::afterPopulating()
+{
+}
+
+CfgMain* PopulateRandomTextEngine::getConfig()
+{
+ return &cfg;
+}
+
+QString PopulateRandomTextEngine::getPopulateConfigFormName() const
+{
+ return QStringLiteral("PopulateRandomTextConfig");
+}
+
+bool PopulateRandomTextEngine::validateOptions()
+{
+ bool rangeValid = (cfg.PopulateRandomText.MinLength.get() <= cfg.PopulateRandomText.MaxLength.get());
+ POPULATE_MANAGER->handleValidationFromPlugin(rangeValid, cfg.PopulateRandomText.MaxLength, QObject::tr("Maximum length cannot be less than minimum length."));
+
+ bool useCustom = cfg.PopulateRandomText.UseCustomSets.get();
+ bool useBinary = cfg.PopulateRandomText.IncludeBinary.get();
+ POPULATE_MANAGER->updateVisibilityAndEnabled(cfg.PopulateRandomText.IncludeAlpha, true, !useCustom && !useBinary);
+ POPULATE_MANAGER->updateVisibilityAndEnabled(cfg.PopulateRandomText.IncludeNumeric, true, !useCustom && !useBinary);
+ POPULATE_MANAGER->updateVisibilityAndEnabled(cfg.PopulateRandomText.IncludeWhitespace, true, !useCustom && !useBinary);
+ POPULATE_MANAGER->updateVisibilityAndEnabled(cfg.PopulateRandomText.IncludeBinary, true, !useCustom);
+ POPULATE_MANAGER->updateVisibilityAndEnabled(cfg.PopulateRandomText.CustomCharacters, true, useCustom);
+
+ bool customValid = !useCustom || !cfg.PopulateRandomText.CustomCharacters.get().isEmpty();
+ POPULATE_MANAGER->handleValidationFromPlugin(customValid, cfg.PopulateRandomText.CustomCharacters, QObject::tr("Custom character set cannot be empty."));
+
+ return rangeValid && customValid;
+}
diff --git a/SQLiteStudio3/coreSQLiteStudio/plugins/populaterandomtext.h b/SQLiteStudio3/coreSQLiteStudio/plugins/populaterandomtext.h
new file mode 100644
index 0000000..892b302
--- /dev/null
+++ b/SQLiteStudio3/coreSQLiteStudio/plugins/populaterandomtext.h
@@ -0,0 +1,52 @@
+#ifndef POPULATERANDOMTEXT_H
+#define POPULATERANDOMTEXT_H
+
+#include "builtinplugin.h"
+#include "populateplugin.h"
+#include "config_builder.h"
+
+CFG_CATEGORIES(PopulateRandomTextConfig,
+ CFG_CATEGORY(PopulateRandomText,
+ CFG_ENTRY(int, MinLength, 4)
+ CFG_ENTRY(int, MaxLength, 20)
+ CFG_ENTRY(bool, IncludeAlpha, true)
+ CFG_ENTRY(bool, IncludeNumeric, true)
+ CFG_ENTRY(bool, IncludeWhitespace, true)
+ CFG_ENTRY(bool, IncludeBinary, false)
+ CFG_ENTRY(bool, UseCustomSets, false)
+ CFG_ENTRY(QString, CustomCharacters, QString())
+ )
+)
+class PopulateRandomText : public BuiltInPlugin, public PopulatePlugin
+{
+ Q_OBJECT
+
+ SQLITESTUDIO_PLUGIN_TITLE("Random text")
+ SQLITESTUDIO_PLUGIN_DESC("Support for populating tables with random characters.")
+ SQLITESTUDIO_PLUGIN_VERSION(10001)
+ SQLITESTUDIO_PLUGIN_AUTHOR("sqlitestudio.pl")
+
+ public:
+ PopulateRandomText();
+
+ QString getTitle() const;
+ PopulateEngine* createEngine();
+};
+
+class PopulateRandomTextEngine : public PopulateEngine
+{
+ public:
+ bool beforePopulating(Db* db, const QString& table);
+ QVariant nextValue(bool& nextValueError);
+ void afterPopulating();
+ CfgMain* getConfig();
+ QString getPopulateConfigFormName() const;
+ bool validateOptions();
+
+ private:
+ CFG_LOCAL(PopulateRandomTextConfig, cfg)
+ int range;
+ QString chars;
+};
+
+#endif // POPULATERANDOMTEXT_H
diff --git a/SQLiteStudio3/coreSQLiteStudio/plugins/populaterandomtext.ui b/SQLiteStudio3/coreSQLiteStudio/plugins/populaterandomtext.ui
new file mode 100644
index 0000000..28febde
--- /dev/null
+++ b/SQLiteStudio3/coreSQLiteStudio/plugins/populaterandomtext.ui
@@ -0,0 +1,181 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>PopulateRandomTextConfig</class>
+ <widget class="QWidget" name="PopulateRandomTextConfig">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>367</width>
+ <height>291</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Form</string>
+ </property>
+ <layout class="QGridLayout" name="gridLayout">
+ <item row="1" column="0" colspan="2">
+ <widget class="ConfigRadioButton" name="commonSetRadio">
+ <property name="cfg" stdset="0">
+ <string>PopulateRandomText.UseCustomSets</string>
+ </property>
+ <property name="text">
+ <string>Use characters from common sets:</string>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ <property name="assignedValue" stdset="0">
+ <bool>false</bool>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="0">
+ <widget class="QGroupBox" name="minLengthGroup">
+ <property name="title">
+ <string>Minimum length</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_3">
+ <item>
+ <widget class="QSpinBox" name="minLengthSpin">
+ <property name="cfg" stdset="0">
+ <string>PopulateRandomText.MinLength</string>
+ </property>
+ <property name="maximum">
+ <number>999999999</number>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item row="2" column="0" colspan="2">
+ <widget class="QFrame" name="commonSetFrame">
+ <property name="frameShape">
+ <enum>QFrame::StyledPanel</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>QFrame::Raised</enum>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <item>
+ <widget class="QCheckBox" name="alphaCheck">
+ <property name="toolTip">
+ <string>Letters from a to z.</string>
+ </property>
+ <property name="cfg" stdset="0">
+ <string>PopulateRandomText.IncludeAlpha</string>
+ </property>
+ <property name="text">
+ <string>Alpha</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QCheckBox" name="numericCheck">
+ <property name="toolTip">
+ <string>Numbers from 0 to 9.</string>
+ </property>
+ <property name="cfg" stdset="0">
+ <string>PopulateRandomText.IncludeNumeric</string>
+ </property>
+ <property name="text">
+ <string>Numeric</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QCheckBox" name="whitespaceCheck">
+ <property name="toolTip">
+ <string>A whitespace, a tab and a new line character.</string>
+ </property>
+ <property name="cfg" stdset="0">
+ <string>PopulateRandomText.IncludeWhitespace</string>
+ </property>
+ <property name="text">
+ <string>Whitespace</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QCheckBox" name="binaryCheck">
+ <property name="toolTip">
+ <string>Includes all above and all others.</string>
+ </property>
+ <property name="cfg" stdset="0">
+ <string>PopulateRandomText.IncludeBinary</string>
+ </property>
+ <property name="text">
+ <string>Binary</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item row="3" column="0" colspan="2">
+ <widget class="ConfigRadioButton" name="customSetRadio">
+ <property name="cfg" stdset="0">
+ <string>PopulateRandomText.UseCustomSets</string>
+ </property>
+ <property name="text">
+ <string>Use characters from my custom set:</string>
+ </property>
+ <property name="assignedValue" stdset="0">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QGroupBox" name="maxLengthGroup">
+ <property name="title">
+ <string>Maximum length</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_4">
+ <item>
+ <widget class="QSpinBox" name="maxLengthSpin">
+ <property name="cfg" stdset="0">
+ <string>PopulateRandomText.MaxLength</string>
+ </property>
+ <property name="maximum">
+ <number>999999999</number>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item row="4" column="0" colspan="2">
+ <widget class="QFrame" name="customSetFrame">
+ <property name="frameShape">
+ <enum>QFrame::StyledPanel</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>QFrame::Raised</enum>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_2">
+ <item>
+ <widget class="QLineEdit" name="customSetEdit">
+ <property name="toolTip">
+ <string>If you type some character multiple times, it's more likely to be used.</string>
+ </property>
+ <property name="cfg" stdset="0">
+ <string>PopulateRandomText.CustomCharacters</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <customwidgets>
+ <customwidget>
+ <class>ConfigRadioButton</class>
+ <extends>QRadioButton</extends>
+ <header>common/configradiobutton.h</header>
+ </customwidget>
+ </customwidgets>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/SQLiteStudio3/coreSQLiteStudio/plugins/populatescript.cpp b/SQLiteStudio3/coreSQLiteStudio/plugins/populatescript.cpp
new file mode 100644
index 0000000..79a8ac1
--- /dev/null
+++ b/SQLiteStudio3/coreSQLiteStudio/plugins/populatescript.cpp
@@ -0,0 +1,125 @@
+#include "populatescript.h"
+#include "common/unused.h"
+#include "services/populatemanager.h"
+#include "services/pluginmanager.h"
+#include "services/notifymanager.h"
+
+PopulateScript::PopulateScript()
+{
+}
+
+QString PopulateScript::getTitle() const
+{
+ return tr("Script");
+}
+
+PopulateEngine* PopulateScript::createEngine()
+{
+ return new PopulateScriptEngine();
+}
+
+bool PopulateScriptEngine::beforePopulating(Db* db, const QString& table)
+{
+ this->db = db;
+ this->table = table;
+
+ evalArgs = {db->getName(), table};
+
+ scriptingPlugin = nullptr;
+ for (ScriptingPlugin* plugin : PLUGINS->getLoadedPlugins<ScriptingPlugin>())
+ {
+ if (plugin->getLanguage() == cfg.PopulateScript.Language.get())
+ {
+ scriptingPlugin = plugin;
+ break;
+ }
+ }
+
+ if (!scriptingPlugin)
+ {
+ notifyError(QObject::tr("Could not find plugin to support scripting language: %1").arg(cfg.PopulateScript.Language.get()));
+ return false;
+ }
+
+ dbAwarePlugin = dynamic_cast<DbAwareScriptingPlugin*>(scriptingPlugin);
+
+ context = scriptingPlugin->createContext();
+
+ QString initCode = cfg.PopulateScript.InitCode.get();
+ if (!initCode.trimmed().isEmpty())
+ {
+ if (dbAwarePlugin)
+ dbAwarePlugin->evaluate(context, initCode, evalArgs, db);
+ else
+ scriptingPlugin->evaluate(context, initCode, evalArgs);
+
+ if (scriptingPlugin->hasError(context))
+ {
+ notifyError(QObject::tr("Error while executing populating initial code: %1").arg(scriptingPlugin->getErrorMessage(context)));
+ releaseContext();
+ return false;
+ }
+ }
+
+ rowCnt = 1;
+ evalArgs << rowCnt;
+
+ return true;
+}
+
+QVariant PopulateScriptEngine::nextValue(bool& nextValueError)
+{
+ QVariant result;
+ if (dbAwarePlugin)
+ result = dbAwarePlugin->evaluate(context, cfg.PopulateScript.Code.get(), evalArgs, db);
+ else
+ result = scriptingPlugin->evaluate(context, cfg.PopulateScript.Code.get(), evalArgs);
+
+ if (scriptingPlugin->hasError(context))
+ {
+ notifyError(QObject::tr("Error while executing populating code: %1").arg(scriptingPlugin->getErrorMessage(context)));
+ releaseContext();
+ nextValueError = true;
+ return QVariant();
+ }
+
+ evalArgs[2] = ++rowCnt;
+
+ return result;
+}
+
+void PopulateScriptEngine::afterPopulating()
+{
+ releaseContext();
+}
+
+CfgMain* PopulateScriptEngine::getConfig()
+{
+ return &cfg;
+}
+
+QString PopulateScriptEngine::getPopulateConfigFormName() const
+{
+ return QStringLiteral("PopulateScriptConfig");
+}
+
+bool PopulateScriptEngine::validateOptions()
+{
+ bool langValid = !cfg.PopulateScript.Language.get().isEmpty();
+ bool codeValid = !cfg.PopulateScript.Code.get().trimmed().isEmpty();
+ QString lang = cfg.PopulateScript.Language.get();
+
+ POPULATE_MANAGER->handleValidationFromPlugin(langValid, cfg.PopulateScript.Language, QObject::tr("Select implementation language."));
+ POPULATE_MANAGER->handleValidationFromPlugin(codeValid, cfg.PopulateScript.Code, QObject::tr("Implementation code cannot be empty."));
+
+ POPULATE_MANAGER->propertySetFromPlugin(cfg.PopulateScript.InitCode, PluginServiceBase::LANG_PROPERTY_NAME, lang);
+ POPULATE_MANAGER->propertySetFromPlugin(cfg.PopulateScript.Code, PluginServiceBase::LANG_PROPERTY_NAME, lang);
+
+ return langValid && codeValid;
+}
+
+void PopulateScriptEngine::releaseContext()
+{
+ scriptingPlugin->releaseContext(context);
+ context = nullptr;
+}
diff --git a/SQLiteStudio3/coreSQLiteStudio/plugins/populatescript.h b/SQLiteStudio3/coreSQLiteStudio/plugins/populatescript.h
new file mode 100644
index 0000000..8870b67
--- /dev/null
+++ b/SQLiteStudio3/coreSQLiteStudio/plugins/populatescript.h
@@ -0,0 +1,63 @@
+#ifndef POPULATESCRIPT_H
+#define POPULATESCRIPT_H
+
+#include "builtinplugin.h"
+#include "populateplugin.h"
+#include "config_builder.h"
+#include "scriptingplugin.h"
+
+CFG_CATEGORIES(PopulateScriptConfig,
+ CFG_CATEGORY(PopulateScript,
+ CFG_ENTRY(QString, Language, QString())
+ CFG_ENTRY(QString, InitCode, QString())
+ CFG_ENTRY(QString, Code, QString())
+ )
+)
+
+/**
+ * @brief Populate from evaluated script code
+ *
+ * Initial code evaluation gets 2 arguments - the db name and the table name.
+ * Each evaluation of per-step code gets 3 arguments, 2 of them just like above
+ * and the 3rd is number of the row being currently populated (starting from 1).
+ */
+class PopulateScript : public BuiltInPlugin, public PopulatePlugin
+{
+ Q_OBJECT
+
+ SQLITESTUDIO_PLUGIN_TITLE("Constant")
+ SQLITESTUDIO_PLUGIN_DESC("Support for populating tables with a constant value.")
+ SQLITESTUDIO_PLUGIN_VERSION(10001)
+ SQLITESTUDIO_PLUGIN_AUTHOR("sqlitestudio.pl")
+
+ public:
+ PopulateScript();
+
+ QString getTitle() const;
+ PopulateEngine* createEngine();
+};
+
+class PopulateScriptEngine : public PopulateEngine
+{
+ public:
+ bool beforePopulating(Db* db, const QString& table);
+ QVariant nextValue(bool& nextValueError);
+ void afterPopulating();
+ CfgMain* getConfig();
+ QString getPopulateConfigFormName() const;
+ bool validateOptions();
+
+ private:
+ CFG_LOCAL(PopulateScriptConfig, cfg)
+ void releaseContext();
+
+ ScriptingPlugin* scriptingPlugin = nullptr;
+ DbAwareScriptingPlugin* dbAwarePlugin = nullptr;
+ ScriptingPlugin::Context* context = nullptr;
+ Db* db = nullptr;
+ QString table;
+ int rowCnt = 0;
+ QList<QVariant> evalArgs;
+};
+
+#endif // POPULATESCRIPT_H
diff --git a/SQLiteStudio3/coreSQLiteStudio/plugins/populatescript.ui b/SQLiteStudio3/coreSQLiteStudio/plugins/populatescript.ui
new file mode 100644
index 0000000..8d37994
--- /dev/null
+++ b/SQLiteStudio3/coreSQLiteStudio/plugins/populatescript.ui
@@ -0,0 +1,112 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>PopulateScriptConfig</class>
+ <widget class="QWidget" name="PopulateScriptConfig">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>443</width>
+ <height>362</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Form</string>
+ </property>
+ <property name="initialSize" stdset="0">
+ <size>
+ <width>440</width>
+ <height>360</height>
+ </size>
+ </property>
+ <layout class="QGridLayout" name="gridLayout">
+ <item row="1" column="0" colspan="2">
+ <widget class="QGroupBox" name="initCodeGroup">
+ <property name="title">
+ <string>Initialization code (optional)</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_4">
+ <item>
+ <widget class="QPlainTextEdit" name="initCodeEdit">
+ <property name="cfg" stdset="0">
+ <string>PopulateScript.InitCode</string>
+ </property>
+ <property name="scriptingEdit" stdset="0">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item row="2" column="0" colspan="2">
+ <widget class="QGroupBox" name="codeGroup">
+ <property name="title">
+ <string>Per step code</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_2">
+ <item>
+ <widget class="QPlainTextEdit" name="codeEdit">
+ <property name="cfg" stdset="0">
+ <string>PopulateScript.Code</string>
+ </property>
+ <property name="scriptingEdit" stdset="0">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item row="0" column="0">
+ <widget class="QGroupBox" name="langGroup">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Expanding" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="title">
+ <string>Language</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_3">
+ <item>
+ <widget class="QComboBox" name="langCombo">
+ <property name="cfg" stdset="0">
+ <string>PopulateScript.Language</string>
+ </property>
+ <property name="ScriptingLangCombo" stdset="0">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QGroupBox" name="groupBox">
+ <property name="title">
+ <string>Help</string>
+ </property>
+ <layout class="QHBoxLayout" name="horizontalLayout">
+ <item>
+ <widget class="QToolButton" name="toolButton">
+ <property name="text">
+ <string>...</string>
+ </property>
+ <property name="openUrl" stdset="0">
+ <string>http://sqlitestudio.pl/wiki/index.php/Official_plugins#Script_.28built-in.29</string>
+ </property>
+ <property name="customIcon" stdset="0">
+ <string>help</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/SQLiteStudio3/coreSQLiteStudio/plugins/populatesequence.cpp b/SQLiteStudio3/coreSQLiteStudio/plugins/populatesequence.cpp
new file mode 100644
index 0000000..a0ad94e
--- /dev/null
+++ b/SQLiteStudio3/coreSQLiteStudio/plugins/populatesequence.cpp
@@ -0,0 +1,53 @@
+#include "populatesequence.h"
+#include "common/global.h"
+#include "services/populatemanager.h"
+#include "common/unused.h"
+#include <QVariant>
+
+PopulateSequence::PopulateSequence()
+{
+}
+
+QString PopulateSequence::getTitle() const
+{
+ return tr("Sequence");
+}
+
+PopulateEngine* PopulateSequence::createEngine()
+{
+ return new PopulateSequenceEngine();
+}
+
+bool PopulateSequenceEngine::beforePopulating(Db* db, const QString& table)
+{
+ UNUSED(db);
+ UNUSED(table);
+ seq = cfg.PopulateSequence.StartValue.get();
+ step = cfg.PopulateSequence.Step.get();
+ return true;
+}
+
+QVariant PopulateSequenceEngine::nextValue(bool& nextValueError)
+{
+ UNUSED(nextValueError);
+ return seq += step;
+}
+
+void PopulateSequenceEngine::afterPopulating()
+{
+}
+
+CfgMain* PopulateSequenceEngine::getConfig()
+{
+ return &cfg;
+}
+
+QString PopulateSequenceEngine::getPopulateConfigFormName() const
+{
+ return QStringLiteral("PopulateSequenceConfig");
+}
+
+bool PopulateSequenceEngine::validateOptions()
+{
+ return true;
+}
diff --git a/SQLiteStudio3/coreSQLiteStudio/plugins/populatesequence.h b/SQLiteStudio3/coreSQLiteStudio/plugins/populatesequence.h
new file mode 100644
index 0000000..5435477
--- /dev/null
+++ b/SQLiteStudio3/coreSQLiteStudio/plugins/populatesequence.h
@@ -0,0 +1,47 @@
+#ifndef POPULATESEQUENCE_H
+#define POPULATESEQUENCE_H
+
+#include "builtinplugin.h"
+#include "populateplugin.h"
+#include "config_builder.h"
+
+CFG_CATEGORIES(PopulateSequenceConfig,
+ CFG_CATEGORY(PopulateSequence,
+ CFG_ENTRY(int, StartValue, 0)
+ CFG_ENTRY(int, Step, 1)
+ )
+)
+
+class PopulateSequence : public BuiltInPlugin, public PopulatePlugin
+{
+ Q_OBJECT
+
+ SQLITESTUDIO_PLUGIN_TITLE("Sequence")
+ SQLITESTUDIO_PLUGIN_DESC("Support for populating tables with sequenced values.")
+ SQLITESTUDIO_PLUGIN_VERSION(10001)
+ SQLITESTUDIO_PLUGIN_AUTHOR("sqlitestudio.pl")
+
+ public:
+ PopulateSequence();
+
+ QString getTitle() const;
+ PopulateEngine* createEngine();
+};
+
+class PopulateSequenceEngine : public PopulateEngine
+{
+ public:
+ bool beforePopulating(Db* db, const QString& table);
+ QVariant nextValue(bool& nextValueError);
+ void afterPopulating();
+ CfgMain* getConfig();
+ QString getPopulateConfigFormName() const;
+ bool validateOptions();
+
+ private:
+ CFG_LOCAL(PopulateSequenceConfig, cfg)
+ qint64 seq = 0;
+ qint64 step = 1;
+};
+
+#endif // POPULATESEQUENCE_H
diff --git a/SQLiteStudio3/coreSQLiteStudio/plugins/populatesequence.ui b/SQLiteStudio3/coreSQLiteStudio/plugins/populatesequence.ui
new file mode 100644
index 0000000..231af85
--- /dev/null
+++ b/SQLiteStudio3/coreSQLiteStudio/plugins/populatesequence.ui
@@ -0,0 +1,64 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>PopulateSequenceConfig</class>
+ <widget class="QWidget" name="PopulateSequenceConfig">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>279</width>
+ <height>67</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Form</string>
+ </property>
+ <layout class="QGridLayout" name="gridLayout">
+ <item row="0" column="1">
+ <widget class="QSpinBox" name="startValueSpin">
+ <property name="cfg" stdset="0">
+ <string>PopulateSequence.StartValue</string>
+ </property>
+ <property name="minimum">
+ <number>-99999999</number>
+ </property>
+ <property name="maximum">
+ <number>99999999</number>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="0">
+ <widget class="QLabel" name="startValueLabel">
+ <property name="text">
+ <string>Start value:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QSpinBox" name="stepSpin">
+ <property name="cfg" stdset="0">
+ <string>PopulateSequence.Step</string>
+ </property>
+ <property name="minimum">
+ <number>-999999</number>
+ </property>
+ <property name="maximum">
+ <number>999999</number>
+ </property>
+ <property name="value">
+ <number>1</number>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QLabel" name="stepLabel">
+ <property name="text">
+ <string>Step:</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/SQLiteStudio3/coreSQLiteStudio/plugins/scriptingplugin.h b/SQLiteStudio3/coreSQLiteStudio/plugins/scriptingplugin.h
new file mode 100644
index 0000000..d081073
--- /dev/null
+++ b/SQLiteStudio3/coreSQLiteStudio/plugins/scriptingplugin.h
@@ -0,0 +1,50 @@
+#ifndef SCRIPTINGPLUGIN_H
+#define SCRIPTINGPLUGIN_H
+
+#include "plugin.h"
+#include <QVariant>
+
+class Db;
+
+class ScriptingPlugin : virtual public Plugin
+{
+ public:
+ class Context
+ {
+ public:
+ virtual ~Context() {}
+ };
+
+ virtual QString getLanguage() const = 0;
+ virtual Context* createContext() = 0;
+ virtual void releaseContext(Context* context) = 0;
+ virtual void resetContext(Context* context) = 0;
+ virtual void setVariable(Context* context, const QString& name, const QVariant& value) = 0;
+ virtual QVariant getVariable(Context* context, const QString& name) = 0;
+ virtual QVariant evaluate(Context* context, const QString& code, const QList<QVariant>& args = QList<QVariant>()) = 0;
+ virtual bool hasError(Context* context) const = 0;
+ virtual QString getErrorMessage(Context* context) const = 0;
+ virtual QVariant evaluate(const QString& code, const QList<QVariant>& args = QList<QVariant>(), QString* errorMessage = nullptr) = 0;
+ virtual QString getIconPath() const = 0;
+};
+
+class DbAwareScriptingPlugin : public ScriptingPlugin
+{
+ public:
+ virtual QVariant evaluate(Context* context, const QString& code, const QList<QVariant>& args, Db* db, bool locking = false) = 0;
+ virtual QVariant evaluate(const QString& code, const QList<QVariant>& args, Db* db, bool locking = false, QString* errorMessage = nullptr) = 0;
+
+ QVariant evaluate(Context* context, const QString& code, const QList<QVariant>& args)
+ {
+ return evaluate(context, code, args, nullptr, true);
+ }
+
+ QVariant evaluate(const QString& code, const QList<QVariant>& args, QString* errorMessage = nullptr)
+ {
+ return evaluate(code, args, nullptr, true, errorMessage);
+ }
+};
+
+Q_DECLARE_METATYPE(ScriptingPlugin::Context*)
+
+#endif // SCRIPTINGPLUGIN_H
diff --git a/SQLiteStudio3/coreSQLiteStudio/plugins/scriptingqt.cpp b/SQLiteStudio3/coreSQLiteStudio/plugins/scriptingqt.cpp
new file mode 100644
index 0000000..334c0cc
--- /dev/null
+++ b/SQLiteStudio3/coreSQLiteStudio/plugins/scriptingqt.cpp
@@ -0,0 +1,276 @@
+#include "scriptingqt.h"
+#include "common/unused.h"
+#include "common/global.h"
+#include "scriptingqtdbproxy.h"
+#include <QScriptEngine>
+#include <QMutex>
+#include <QMutexLocker>
+#include <QDebug>
+
+static QScriptValue scriptingQtDebug(QScriptContext *context, QScriptEngine *engine)
+{
+ UNUSED(engine);
+ QStringList args;
+ for (int i = 0; i < context->argumentCount(); i++)
+ args << context->argument(i).toString();
+
+ qDebug() << "[ScriptingQt]" << args;
+ return QScriptValue();
+}
+
+ScriptingQt::ScriptingQt()
+{
+ mainEngineMutex = new QMutex();
+}
+
+ScriptingQt::~ScriptingQt()
+{
+ safe_delete(mainEngineMutex);
+}
+
+QString ScriptingQt::getLanguage() const
+{
+ return QStringLiteral("QtScript");
+}
+
+ScriptingPlugin::Context* ScriptingQt::createContext()
+{
+ ContextQt* ctx = new ContextQt;
+ ctx->engine->pushContext();
+ contexts << ctx;
+ return ctx;
+}
+
+void ScriptingQt::releaseContext(ScriptingPlugin::Context* context)
+{
+ ContextQt* ctx = getContext(context);
+ if (!ctx)
+ return;
+
+ contexts.removeOne(ctx);
+ delete ctx;
+}
+
+void ScriptingQt::resetContext(ScriptingPlugin::Context* context)
+{
+ ContextQt* ctx = getContext(context);
+ if (!ctx)
+ return;
+
+ ctx->engine->popContext();
+ ctx->engine->pushContext();
+}
+
+QVariant ScriptingQt::evaluate(const QString& code, const QList<QVariant>& args, Db* db, bool locking, QString* errorMessage)
+{
+ QMutexLocker locker(mainEngineMutex);
+
+ // Enter a new context
+ QScriptContext* engineContext = mainContext->engine->pushContext();
+
+ // Call the function
+ QVariant result = evaluate(mainContext, engineContext, code, args, db, locking);
+
+ // Handle errors
+ if (!mainContext->error.isEmpty())
+ *errorMessage = mainContext->error;
+
+ // Leave the context to reset "this".
+ mainContext->engine->popContext();
+
+ return result;
+}
+
+QVariant ScriptingQt::evaluate(ScriptingPlugin::Context* context, const QString& code, const QList<QVariant>& args, Db* db, bool locking)
+{
+ ContextQt* ctx = getContext(context);
+ if (!ctx)
+ return QVariant();
+
+ return evaluate(ctx, ctx->engine->currentContext(), code, args, db, locking);
+}
+
+QVariant ScriptingQt::evaluate(ContextQt* ctx, QScriptContext* engineContext, const QString& code, const QList<QVariant>& args, Db* db, bool locking)
+{
+ // Define function to call
+ QScriptValue functionValue = getFunctionValue(ctx, code);
+
+ // Db for this evaluation
+ ctx->dbProxy->setDb(db);
+ ctx->dbProxy->setUseDbLocking(locking);
+
+ // Call the function
+ QScriptValue result;
+ if (args.size() > 0)
+ result = functionValue.call(engineContext->activationObject(), ctx->engine->toScriptValue(args));
+ else
+ result = functionValue.call(engineContext->activationObject());
+
+ // Handle errors
+ ctx->error.clear();
+ if (ctx->engine->hasUncaughtException())
+ ctx->error = ctx->engine->uncaughtException().toString();
+
+ ctx->dbProxy->setDb(nullptr);
+ ctx->dbProxy->setUseDbLocking(false);
+
+ return convertVariant(result.toVariant());
+}
+
+QVariant ScriptingQt::convertVariant(const QVariant& value, bool wrapStrings)
+{
+ switch (value.type())
+ {
+ case QVariant::Hash:
+ {
+ QHash<QString, QVariant> hash = value.toHash();
+ QHashIterator<QString, QVariant> it(hash);
+ QStringList list;
+ while (it.hasNext())
+ {
+ it.next();
+ list << it.key() + ": " + convertVariant(it.value(), true).toString();
+ }
+ return "{" + list.join(", ") + "}";
+ }
+ case QVariant::Map:
+ {
+ QMap<QString, QVariant> map = value.toMap();
+ QMapIterator<QString, QVariant> it(map);
+ QStringList list;
+ while (it.hasNext())
+ {
+ it.next();
+ list << it.key() + ": " + convertVariant(it.value(), true).toString();
+ }
+ return "{" + list.join(", ") + "}";
+ }
+ case QVariant::List:
+ {
+ QStringList list;
+ for (const QVariant& var : value.toList())
+ list << convertVariant(var, true).toString();
+
+ return "[" + list.join(", ") + "]";
+ }
+ case QVariant::StringList:
+ {
+ return "[\"" + value.toStringList().join("\", \"") + "\"]";
+ }
+ case QVariant::String:
+ {
+ if (wrapStrings)
+ return "\"" + value.toString() + "\"";
+
+ break;
+ }
+ default:
+ break;
+ }
+ return value;
+}
+
+void ScriptingQt::setVariable(ScriptingPlugin::Context* context, const QString& name, const QVariant& value)
+{
+ ContextQt* ctx = getContext(context);
+ if (!ctx)
+ return;
+
+ ctx->engine->globalObject().setProperty(name, ctx->engine->newVariant(value));
+}
+
+QVariant ScriptingQt::getVariable(ScriptingPlugin::Context* context, const QString& name)
+{
+ ContextQt* ctx = getContext(context);
+ if (!ctx)
+ return QVariant();
+
+ QScriptValue value = ctx->engine->globalObject().property(name);
+ return convertVariant(value.toVariant());
+}
+
+bool ScriptingQt::hasError(ScriptingPlugin::Context* context) const
+{
+ ContextQt* ctx = getContext(context);
+ if (!ctx)
+ return false;
+
+ return !ctx->error.isEmpty();
+}
+
+QString ScriptingQt::getErrorMessage(ScriptingPlugin::Context* context) const
+{
+ ContextQt* ctx = getContext(context);
+ if (!ctx)
+ return QString::null;
+
+ return ctx->error;
+}
+
+QString ScriptingQt::getIconPath() const
+{
+ return ":/images/plugins/scriptingqt.png";
+}
+
+bool ScriptingQt::init()
+{
+ QMutexLocker locker(mainEngineMutex);
+ mainContext = new ContextQt;
+ return true;
+}
+
+void ScriptingQt::deinit()
+{
+ foreach (Context* ctx, contexts)
+ delete ctx;
+
+ contexts.clear();
+
+ QMutexLocker locker(mainEngineMutex);
+ safe_delete(mainContext);
+}
+
+ScriptingQt::ContextQt* ScriptingQt::getContext(ScriptingPlugin::Context* context) const
+{
+ ContextQt* ctx = dynamic_cast<ContextQt*>(context);
+ if (!ctx)
+ qDebug() << "Invalid context passed to ScriptingQt:" << context;
+
+ return ctx;
+}
+
+QScriptValue ScriptingQt::getFunctionValue(ContextQt* ctx, const QString& code)
+{
+ static const QString fnDef = QStringLiteral("(function () {%1\n})");
+
+ QScriptProgram* prog = nullptr;
+ if (!ctx->scriptCache.contains(code))
+ {
+ prog = new QScriptProgram(fnDef.arg(code));
+ ctx->scriptCache.insert(code, prog);
+ }
+ else
+ {
+ prog = ctx->scriptCache[code];
+ }
+ return ctx->engine->evaluate(*prog);
+}
+
+ScriptingQt::ContextQt::ContextQt()
+{
+ engine = new QScriptEngine();
+
+ dbProxy = new ScriptingQtDbProxy();
+ dbProxyScriptValue = engine->newQObject(dbProxy, QScriptEngine::QtOwnership, QScriptEngine::ExcludeDeleteLater);
+
+ engine->globalObject().setProperty("debug", engine->newFunction(scriptingQtDebug));
+ engine->globalObject().setProperty("db", dbProxyScriptValue);
+
+ scriptCache.setMaxCost(cacheSize);
+}
+
+ScriptingQt::ContextQt::~ContextQt()
+{
+ safe_delete(engine);
+ safe_delete(dbProxy);
+}
diff --git a/SQLiteStudio3/coreSQLiteStudio/plugins/scriptingqt.h b/SQLiteStudio3/coreSQLiteStudio/plugins/scriptingqt.h
new file mode 100644
index 0000000..125789a
--- /dev/null
+++ b/SQLiteStudio3/coreSQLiteStudio/plugins/scriptingqt.h
@@ -0,0 +1,72 @@
+#ifndef SCRIPTINGQT_H
+#define SCRIPTINGQT_H
+
+#include "builtinplugin.h"
+#include "scriptingplugin.h"
+#include <QHash>
+#include <QVariant>
+#include <QCache>
+#include <QScriptValue>
+#include <QScriptProgram>
+
+class QScriptEngine;
+class QMutex;
+class QScriptContext;
+class ScriptingQtDbProxy;
+
+class ScriptingQt : public BuiltInPlugin, public DbAwareScriptingPlugin
+{
+ Q_OBJECT
+
+ SQLITESTUDIO_PLUGIN_TITLE("Qt scripting")
+ SQLITESTUDIO_PLUGIN_DESC("Qt scripting support.")
+ SQLITESTUDIO_PLUGIN_VERSION(10000)
+ SQLITESTUDIO_PLUGIN_AUTHOR("sqlitestudio.pl")
+
+ public:
+ ScriptingQt();
+ ~ScriptingQt();
+
+ QString getLanguage() const;
+ Context* createContext();
+ void releaseContext(Context* context);
+ void resetContext(Context* context);
+ QVariant evaluate(const QString& code, const QList<QVariant>& args, Db* db, bool locking = false, QString* errorMessage = nullptr);
+ QVariant evaluate(Context* context, const QString& code, const QList<QVariant>& args, Db* db, bool locking = false);
+ void setVariable(Context* context, const QString& name, const QVariant& value);
+ QVariant getVariable(Context* context, const QString& name);
+ bool hasError(Context* context) const;
+ QString getErrorMessage(Context* context) const;
+ QString getIconPath() const;
+ bool init();
+ void deinit();
+
+ private:
+ using DbAwareScriptingPlugin::evaluate;
+
+ class ContextQt : public ScriptingPlugin::Context
+ {
+ public:
+ ContextQt();
+ ~ContextQt();
+
+ QScriptEngine* engine = nullptr;
+ QCache<QString,QScriptProgram> scriptCache;
+ QString error;
+ ScriptingQtDbProxy* dbProxy = nullptr;
+ QScriptValue dbProxyScriptValue;
+ };
+
+ ContextQt* getContext(ScriptingPlugin::Context* context) const;
+ QScriptValue getFunctionValue(ContextQt* ctx, const QString& code);
+ QVariant evaluate(ContextQt* ctx, QScriptContext* engineContext, const QString& code, const QList<QVariant>& args, Db* db, bool locking);
+ QVariant convertVariant(const QVariant& value, bool wrapStrings = false);
+
+ static const constexpr int cacheSize = 5;
+
+ ContextQt* mainContext = nullptr;
+ QList<Context*> contexts;
+ QMutex* mainEngineMutex = nullptr;
+};
+
+#endif // SCRIPTINGQT_H
diff --git a/SQLiteStudio3/coreSQLiteStudio/plugins/scriptingqt.png b/SQLiteStudio3/coreSQLiteStudio/plugins/scriptingqt.png
new file mode 100644
index 0000000..220ea27
--- /dev/null
+++ b/SQLiteStudio3/coreSQLiteStudio/plugins/scriptingqt.png
Binary files differ
diff --git a/SQLiteStudio3/coreSQLiteStudio/plugins/scriptingqtdbproxy.cpp b/SQLiteStudio3/coreSQLiteStudio/plugins/scriptingqtdbproxy.cpp
new file mode 100644
index 0000000..ff3c7ee
--- /dev/null
+++ b/SQLiteStudio3/coreSQLiteStudio/plugins/scriptingqtdbproxy.cpp
@@ -0,0 +1,145 @@
+#include "scriptingqtdbproxy.h"
+#include "db/db.h"
+#include "db/sqlquery.h"
+#include <QScriptContext>
+#include <QScriptEngine>
+
+ScriptingQtDbProxy::ScriptingQtDbProxy(QObject *parent) :
+ QObject(parent)
+{
+}
+Db* ScriptingQtDbProxy::getDb() const
+{
+ return db;
+}
+
+void ScriptingQtDbProxy::setDb(Db* value)
+{
+ db = value;
+}
+bool ScriptingQtDbProxy::getUseDbLocking() const
+{
+ return useDbLocking;
+}
+
+void ScriptingQtDbProxy::setUseDbLocking(bool value)
+{
+ useDbLocking = value;
+}
+
+QHash<QString, QVariant> ScriptingQtDbProxy::mapToHash(const QMap<QString, QVariant>& map)
+{
+ QHash<QString, QVariant> hash;
+ QMapIterator<QString, QVariant> it(map);
+ while (it.hasNext())
+ {
+ it.next();
+ hash[it.key()] = it.value();
+ }
+ return hash;
+}
+
+QVariant ScriptingQtDbProxy::evalInternal(const QString& sql, const QList<QVariant>& listArgs, const QMap<QString, QVariant>& mapArgs,
+ bool singleCell, const QScriptValue* funcPtr)
+{
+ if (!db)
+ {
+ QString funcName = singleCell ? QStringLiteral("db.onecolumn()") : QStringLiteral("db.eval()");
+ context()->throwError(tr("No database available in current context, while called QtScript's %1 command.").arg(funcName));
+ return evalInternalErrorResult(singleCell);
+ }
+
+ Db::Flags flags;
+ if (!useDbLocking)
+ flags |= Db::Flag::NO_LOCK;
+
+ SqlQueryPtr results;
+ if (listArgs.size() > 0)
+ results = db->exec(sql, listArgs, flags);
+ else
+ results = db->exec(sql, mapToHash(mapArgs), flags);
+
+ if (results->isError())
+ {
+ QString funcName = singleCell ? QStringLiteral("db.onecolumn()") : QStringLiteral("db.eval()");
+ context()->throwError(tr("Error from %1: %2").arg(funcName, results->getErrorText()));
+ return evalInternalErrorResult(singleCell);
+ }
+
+ if (singleCell)
+ {
+ return results->getSingleCell();
+ }
+ else if (funcPtr)
+ {
+ QScriptValue func(*funcPtr);
+ SqlResultsRowPtr row;
+ QScriptValue funcArgs;
+ QScriptValue funcResult;
+ while (results->hasNext())
+ {
+ row = results->next();
+ funcArgs = context()->engine()->toScriptValue(row->valueList());
+ funcResult = func.call(context()->thisObject(), funcArgs);
+ if (!funcResult.isUndefined())
+ break;
+ }
+ return funcResult.toVariant();
+ }
+ else
+ {
+ QList<QVariant> evalResults;
+ SqlResultsRowPtr row;
+ while (results->hasNext())
+ {
+ row = results->next();
+ evalResults << QVariant(row->valueList());
+ }
+ return evalResults;
+ }
+}
+
+QVariant ScriptingQtDbProxy::evalInternalErrorResult(bool singleCell)
+{
+ QList<QVariant> result;
+ if (singleCell)
+ result << QVariant();
+
+ return result;
+}
+
+QVariant ScriptingQtDbProxy::eval(const QString& sql)
+{
+ return evalInternal(sql, QList<QVariant>(), QMap<QString, QVariant>(), false);
+}
+
+QVariant ScriptingQtDbProxy::eval(const QString& sql, const QList<QVariant>& args)
+{
+ return evalInternal(sql, args, QMap<QString, QVariant>(), false);
+}
+
+QVariant ScriptingQtDbProxy::eval(const QString& sql, const QMap<QString, QVariant>& args)
+{
+ return evalInternal(sql, QList<QVariant>(), args, false);
+}
+
+QVariant ScriptingQtDbProxy::eval(const QString& sql, const QList<QVariant>& args, const QScriptValue& func)
+{
+ return evalInternal(sql, args, QMap<QString, QVariant>(), false, &func);
+}
+
+QVariant ScriptingQtDbProxy::eval(const QString& sql, const QMap<QString, QVariant>& args, const QScriptValue& func)
+{
+ return evalInternal(sql, QList<QVariant>(), args, false, &func);
+}
+
+QVariant ScriptingQtDbProxy::onecolumn(const QString& sql, const QList<QVariant>& args)
+{
+ return evalInternal(sql, args, QMap<QString, QVariant>(), true);
+}
+
+QVariant ScriptingQtDbProxy::onecolumn(const QString& sql, const QMap<QString, QVariant>& args)
+{
+ return evalInternal(sql, QList<QVariant>(), args, true);
+}
+
diff --git a/SQLiteStudio3/coreSQLiteStudio/plugins/scriptingqtdbproxy.h b/SQLiteStudio3/coreSQLiteStudio/plugins/scriptingqtdbproxy.h
new file mode 100644
index 0000000..add9540
--- /dev/null
+++ b/SQLiteStudio3/coreSQLiteStudio/plugins/scriptingqtdbproxy.h
@@ -0,0 +1,44 @@
+#ifndef SCRIPTINGQTDBPROXY_H
+#define SCRIPTINGQTDBPROXY_H
+
+#include <QObject>
+#include <QScriptable>
+#include <QHash>
+#include <QList>
+#include <QVariant>
+
+class Db;
+
+class ScriptingQtDbProxy : public QObject, protected QScriptable
+{
+ Q_OBJECT
+ public:
+ explicit ScriptingQtDbProxy(QObject *parent = 0);
+
+ Db* getDb() const;
+ void setDb(Db* value);
+
+ bool getUseDbLocking() const;
+ void setUseDbLocking(bool value);
+
+ private:
+ QVariant evalInternal(const QString& sql, const QList<QVariant>& listArgs, const QMap<QString, QVariant>& mapArgs, bool singleCell,
+ const QScriptValue* funcPtr = nullptr);
+ QVariant evalInternalErrorResult(bool singleCell);
+
+ static QHash<QString, QVariant> mapToHash(const QMap<QString, QVariant>& map);
+
+ Db* db = nullptr;
+ bool useDbLocking = false;
+
+ public slots:
+ QVariant eval(const QString& sql);
+ QVariant eval(const QString& sql, const QList<QVariant>& args);
+ QVariant eval(const QString& sql, const QMap<QString, QVariant>& args);
+ QVariant eval(const QString& sql, const QList<QVariant>& args, const QScriptValue& func);
+ QVariant eval(const QString& sql, const QMap<QString, QVariant>& args, const QScriptValue& func);
+ QVariant onecolumn(const QString& sql, const QList<QVariant>& args);
+ QVariant onecolumn(const QString& sql, const QMap<QString, QVariant>& args);
+};
+
+#endif // SCRIPTINGQTDBPROXY_H
diff --git a/SQLiteStudio3/coreSQLiteStudio/plugins/scriptingsql.cpp b/SQLiteStudio3/coreSQLiteStudio/plugins/scriptingsql.cpp
new file mode 100644
index 0000000..93a6d91
--- /dev/null
+++ b/SQLiteStudio3/coreSQLiteStudio/plugins/scriptingsql.cpp
@@ -0,0 +1,146 @@
+#include "scriptingsql.h"
+#include "common/unused.h"
+#include "db/db.h"
+#include "db/sqlquery.h"
+#include "services/dbmanager.h"
+
+ScriptingSql::ScriptingSql()
+{
+}
+
+ScriptingSql::~ScriptingSql()
+{
+}
+
+QString ScriptingSql::getLanguage() const
+{
+ return "SQL";
+}
+
+ScriptingPlugin::Context* ScriptingSql::createContext()
+{
+ SqlContext* ctx = new SqlContext();
+ contexts << ctx;
+ return ctx;
+}
+
+void ScriptingSql::releaseContext(ScriptingPlugin::Context* context)
+{
+ if (!contexts.contains(context))
+ return;
+
+ delete context;
+ contexts.removeOne(context);
+}
+
+void ScriptingSql::resetContext(ScriptingPlugin::Context* context)
+{
+ dynamic_cast<SqlContext*>(context)->errorText.clear();
+}
+
+QVariant ScriptingSql::evaluate(ScriptingPlugin::Context* context, const QString& code, const QList<QVariant>& args, Db* db, bool locking)
+{
+ SqlContext* ctx = dynamic_cast<SqlContext*>(context);
+ ctx->errorText.clear();
+
+ Db* theDb = nullptr;
+ if (db && db->isValid())
+ theDb = db;
+ else if (memDb)
+ theDb = memDb;
+ else
+ return QVariant();
+
+ Db::Flags execFlags;
+ if (!locking)
+ execFlags |= Db::Flag::NO_LOCK;
+
+ QString sql = code;
+ if (ctx->variables.size() > 0)
+ {
+ QString value;
+ for (const QString& key : ctx->variables.keys())
+ {
+ value = "'" + ctx->variables[key].toString() + "'";
+ sql.replace(":" + key, value).replace("@" + key, value).replace("$" + key, value);
+ }
+ }
+
+ SqlQueryPtr result = theDb->exec(sql, args, execFlags);
+ if (result->isError())
+ {
+ dynamic_cast<SqlContext*>(context)->errorText = result->getErrorText();
+ return QVariant();
+ }
+
+ return result->getSingleCell();
+}
+
+QVariant ScriptingSql::evaluate(const QString& code, const QList<QVariant>& args, Db* db, bool locking, QString* errorMessage)
+{
+ Db* theDb = nullptr;
+
+ if (db && db->isValid())
+ theDb = db;
+ else if (memDb)
+ theDb = memDb;
+ else
+ return QVariant();
+
+ Db::Flags execFlags;
+ if (!locking)
+ execFlags |= Db::Flag::NO_LOCK;
+
+ SqlQueryPtr result = theDb->exec(code, args, execFlags);
+ if (result->isError())
+ {
+ *errorMessage = result->getErrorText();
+ return QVariant();
+ }
+
+ return result->getSingleCell();
+}
+
+void ScriptingSql::setVariable(ScriptingPlugin::Context* context, const QString& name, const QVariant& value)
+{
+ dynamic_cast<SqlContext*>(context)->variables[name] = value;
+}
+
+QVariant ScriptingSql::getVariable(ScriptingPlugin::Context* context, const QString& name)
+{
+ if (dynamic_cast<SqlContext*>(context)->variables.contains(name))
+ return dynamic_cast<SqlContext*>(context)->variables[name];
+
+ return QVariant();
+}
+
+bool ScriptingSql::hasError(ScriptingPlugin::Context* context) const
+{
+ return !getErrorMessage(context).isNull();
+}
+
+QString ScriptingSql::getErrorMessage(ScriptingPlugin::Context* context) const
+{
+ return dynamic_cast<SqlContext*>(context)->errorText;
+}
+
+QString ScriptingSql::getIconPath() const
+{
+ return ":/images/plugins/scriptingsql.png";
+}
+
+bool ScriptingSql::init()
+{
+ memDb = DBLIST->createInMemDb();
+ return memDb != nullptr;
+}
+
+void ScriptingSql::deinit()
+{
+ for (Context* context : contexts)
+ delete context;
+
+ contexts.clear();
+
+ safe_delete(memDb);
+}
diff --git a/SQLiteStudio3/coreSQLiteStudio/plugins/scriptingsql.h b/SQLiteStudio3/coreSQLiteStudio/plugins/scriptingsql.h
new file mode 100644
index 0000000..7b8fd3b
--- /dev/null
+++ b/SQLiteStudio3/coreSQLiteStudio/plugins/scriptingsql.h
@@ -0,0 +1,46 @@
+#ifndef SCRIPTINGSQL_H
+#define SCRIPTINGSQL_H
+
+#include "builtinplugin.h"
+#include "scriptingplugin.h"
+
+class ScriptingSql : public BuiltInPlugin, public DbAwareScriptingPlugin
+{
+ Q_OBJECT
+
+ SQLITESTUDIO_PLUGIN_TITLE("SQL scripting")
+ SQLITESTUDIO_PLUGIN_DESC("SQL scripting support.")
+ SQLITESTUDIO_PLUGIN_VERSION(10000)
+ SQLITESTUDIO_PLUGIN_AUTHOR("sqlitestudio.pl")
+
+ public:
+ class SqlContext : public Context
+ {
+ public:
+ QString errorText;
+ QHash<QString,QVariant> variables;
+ };
+
+ ScriptingSql();
+ ~ScriptingSql();
+
+ QString getLanguage() const;
+ Context* createContext();
+ void releaseContext(Context* context);
+ void resetContext(Context* context);
+ QVariant evaluate(Context* context, const QString& code, const QList<QVariant>& args, Db* db, bool locking);
+ QVariant evaluate(const QString& code, const QList<QVariant>& args, Db* db, bool locking, QString* errorMessage);
+ void setVariable(Context* context, const QString& name, const QVariant& value);
+ QVariant getVariable(Context* context, const QString& name);
+ bool hasError(Context* context) const;
+ QString getErrorMessage(Context* context) const;
+ QString getIconPath() const;
+ bool init();
+ void deinit();
+
+ private:
+ QList<Context*> contexts;
+ Db* memDb = nullptr;
+};
+
+#endif // SCRIPTINGSQL_H
diff --git a/SQLiteStudio3/coreSQLiteStudio/plugins/scriptingsql.png b/SQLiteStudio3/coreSQLiteStudio/plugins/scriptingsql.png
new file mode 100644
index 0000000..ea232a7
--- /dev/null
+++ b/SQLiteStudio3/coreSQLiteStudio/plugins/scriptingsql.png
Binary files differ
diff --git a/SQLiteStudio3/coreSQLiteStudio/plugins/sqlformatterplugin.cpp b/SQLiteStudio3/coreSQLiteStudio/plugins/sqlformatterplugin.cpp
new file mode 100644
index 0000000..5e1d610
--- /dev/null
+++ b/SQLiteStudio3/coreSQLiteStudio/plugins/sqlformatterplugin.cpp
@@ -0,0 +1,29 @@
+#include "sqlformatterplugin.h"
+#include "parser/parser.h"
+#include "db/db.h"
+#include <QDebug>
+
+QString SqlFormatterPlugin::format(const QString& code, Db* contextDb)
+{
+ Dialect dialect = Dialect::Sqlite3;
+ if (contextDb && contextDb->isValid())
+ contextDb->getDialect();
+
+ Parser parser(dialect);
+ if (!parser.parse(code))
+ {
+ qWarning() << "Could not parse SQL in order to format it. The SQL was:" << code;
+ return code;
+ }
+
+ QStringList formattedQueries;
+ foreach (SqliteQueryPtr query, parser.getQueries())
+ formattedQueries << format(query);
+
+ return formattedQueries.join("\n");
+}
+
+QString SqlFormatterPlugin::getLanguage() const
+{
+ return "sql";
+}
diff --git a/SQLiteStudio3/coreSQLiteStudio/plugins/sqlformatterplugin.h b/SQLiteStudio3/coreSQLiteStudio/plugins/sqlformatterplugin.h
new file mode 100644
index 0000000..cb500f6
--- /dev/null
+++ b/SQLiteStudio3/coreSQLiteStudio/plugins/sqlformatterplugin.h
@@ -0,0 +1,16 @@
+#ifndef SQLFORMATTERPLUGIN_H
+#define SQLFORMATTERPLUGIN_H
+
+#include "coreSQLiteStudio_global.h"
+#include "codeformatterplugin.h"
+#include "parser/ast/sqlitequery.h"
+
+class API_EXPORT SqlFormatterPlugin : public CodeFormatterPlugin
+{
+ public:
+ QString format(const QString& code, Db* contextDb);
+ QString getLanguage() const;
+ virtual QString format(SqliteQueryPtr query) = 0;
+};
+
+#endif // SQLFORMATTERPLUGIN_H
diff --git a/SQLiteStudio3/coreSQLiteStudio/plugins/uiconfiguredplugin.h b/SQLiteStudio3/coreSQLiteStudio/plugins/uiconfiguredplugin.h
new file mode 100644
index 0000000..c9af8e0
--- /dev/null
+++ b/SQLiteStudio3/coreSQLiteStudio/plugins/uiconfiguredplugin.h
@@ -0,0 +1,56 @@
+#ifndef UICONFIGUREDPLUGIN_H
+#define UICONFIGUREDPLUGIN_H
+
+#include "coreSQLiteStudio_global.h"
+
+class API_EXPORT UiConfiguredPlugin
+{
+ public:
+ /**
+ * @brief Gets name of the configuration UI form.
+ * @return Name of the form object.
+ *
+ * Some plugins may link (during compilation) only to the coreSQLiteStudio part of the application, but they can still
+ * benefit from SQLiteStudio GUI application by providing UI form that will be used in ConfigDialog.
+ *
+ * This method should return the object name of the top-most widget found in the provided *.ui file.
+ *
+ * For more details see: http://wiki.sqlitestudio.pl/index.php/Plugin_UI_forms
+ */
+ virtual QString getConfigUiForm() const = 0;
+
+ /**
+ * @brief Provides config object for ConfigDialog.
+ * @return Config used by the plugin, or null when there's no config, or when config should not be configured with property binding.
+ *
+ * When this method returns null, but getConfigUiForm() returns existing form, then configuration is assumed to be kept
+ * in global CfgMain object (which is known to application, as all global CfgMain objects). In this case configuration is loaded/stored
+ * using initial and final calls to load/store values from the form. This is different from when this method returns not null. Keep reading.
+ *
+ * When this method returns pointer to an object, then ConfigDialog uses ConfigMapper to bind widgets from getConfigUiForm() with
+ * config values from CfgMain returned by this method. See ConfigMapper for details about binding.
+ * In this case ConfigDialog uses CfgMain::begin(), CfgMain::commit() and CfgMain::rollback() methods to make changes to the config
+ * transactional (when users clicks "cancel" or "apply").
+ */
+ virtual CfgMain* getMainUiConfig() = 0;
+
+ /**
+ * @brief Notifies about ConfigDialog being just open.
+ *
+ * This is called just after the config dialog was open and all its contents are already initialized.
+ * This is a good moment to connect to plugin's CfgMain configuration object to listen for changes,
+ * so all uncommited (yet) configuration changes can be reflected by this plugin.
+ */
+ virtual void configDialogOpen() = 0;
+
+ /**
+ * @brief Notifies about ConfigDialog being closed.
+ *
+ * This is called just before the config dialog gets closed.
+ * This is a good moment to disconnect from configuration object and not listen to changes in the configuration anymore
+ * (couse config can change for example when application is starting and loading entire configuration, etc).
+ */
+ virtual void configDialogClosed() = 0;
+};
+
+#endif // UICONFIGUREDPLUGIN_H