From b0cd47ef46131fd81f01f7d5909a1d35e9ee3c1d Mon Sep 17 00:00:00 2001 From: eelke Date: Mon, 31 Dec 2018 15:20:55 +0100 Subject: [PATCH] Extended the plugin system to allow for dynamic runtime bindings between modules. As a test implementation, this allows the TablesPage to open a CrudTab for a table/view without the need for TablesPage, CrudTab and DatabaseWindow to know anything about each other. --- pglab/CrudTab.cpp | 44 +++++++++++++++++-- pglab/CrudTab.h | 16 ++++++- pglab/DatabaseWindow.cpp | 24 +++++----- pglab/DatabaseWindow.h | 6 +-- pglab/QueryTab.cpp | 15 ++----- pglab/QueryTab.h | 2 +- pglab/TablesPage.cpp | 11 ++--- pglab/TablesPage.h | 8 ++-- pglab/pglab.pro | 9 ++-- .../IPluginContentWidgetContext.h | 15 +++++-- pglab/plugin_support/ModuleActionParameters.h | 10 +++++ .../PluginContentWidgetContextBase.cpp | 27 ++++++++++++ .../PluginContentWidgetContextBase.h | 19 ++++++++ pglab/plugin_support/PluginModule.cpp | 14 ++++++ pglab/plugin_support/PluginModule.h | 32 ++++++++++++-- pglab/plugin_support/PluginRegister.cpp | 10 ++++- pglab/plugin_support/PluginRegister.h | 2 + 17 files changed, 209 insertions(+), 55 deletions(-) rename pglab/{ => plugin_support}/IPluginContentWidgetContext.h (76%) create mode 100644 pglab/plugin_support/ModuleActionParameters.h create mode 100644 pglab/plugin_support/PluginContentWidgetContextBase.cpp create mode 100644 pglab/plugin_support/PluginContentWidgetContextBase.h diff --git a/pglab/CrudTab.cpp b/pglab/CrudTab.cpp index 57c1c45..7865faf 100644 --- a/pglab/CrudTab.cpp +++ b/pglab/CrudTab.cpp @@ -4,11 +4,16 @@ #include "ResultTableModelUtil.h" #include "PgLabItemDelegate.h" #include "IntegerRange.h" +#include "OpenDatabase.h" +#include "catalog/PgClassContainer.h" +#include "catalog/PgDatabaseCatalog.h" #include #include #include #include #include +#include "plugin_support/PluginRegister.h" +#include "plugin_support/IPluginContentWidgetContext.h" CrudTab::CrudTab(IPluginContentWidgetContext *context, QWidget *parent) @@ -41,11 +46,11 @@ CrudTab::~CrudTab() delete ui; } -void CrudTab::setConfig(std::shared_ptr db, const PgClass &table) +void CrudTab::setConfig(Oid oid) //std::shared_ptr db, const PgClass &table) { - m_db = db; - m_table = table; - m_crudModel->setConfig(db, table); + m_db = context()->getDatabase();; + m_table = *m_db->catalog()->classes()->getByKey(oid); + m_crudModel->setConfig(m_db, *m_table); } void CrudTab::refresh() @@ -113,3 +118,34 @@ void CrudTab::headerCustomContextMenu(const QPoint &pos) auto horizontal_header = ui->tableView->horizontalHeader(); menu->popup(horizontal_header->mapToGlobal(pos)); } + + +void CrudPageModule::init() +{ + registerModuleAction("open", + [this] (IPluginContentWidgetContext* context, + const ModuleActionParameters ¶ms) + { + moduleAction_open(context, params); + }); +} + +void CrudPageModule::moduleAction_open( + IPluginContentWidgetContext* context, + const ModuleActionParameters ¶ms + ) +{ + // create new widget for specified table + // hand widget to context for display + CrudTab *ct = new CrudTab(context, nullptr); + context->addContentWidget(ct); // maybe CrudTab should do this + ct->setConfig(params.at("oid").toUInt()); + +} + +namespace { + + std::weak_ptr register_variable = createPluginModule + ("CRUD tool", "pglab.crudpage"); + +} diff --git a/pglab/CrudTab.h b/pglab/CrudTab.h index 838492d..76628ee 100644 --- a/pglab/CrudTab.h +++ b/pglab/CrudTab.h @@ -4,6 +4,7 @@ #include "catalog/PgClass.h" #include #include "plugin_support/PluginContentWidget.h" +#include "plugin_support/PluginModule.h" #include #include @@ -22,7 +23,7 @@ public: explicit CrudTab(IPluginContentWidgetContext *context, QWidget *parent = nullptr); ~CrudTab() override; - void setConfig(std::shared_ptr db, const PgClass &table); + void setConfig(Oid oid); void refresh(); @@ -41,4 +42,17 @@ private slots: void headerCustomContextMenu(const QPoint &pos); }; +class CrudPageModule: public PluginModule { + Q_OBJECT +public: + using PluginModule::PluginModule; + + void init(); +private slots: + +private: + void moduleAction_open(IPluginContentWidgetContext* context, const ModuleActionParameters ¶ms); +}; + + #endif // CRUDTAB_H diff --git a/pglab/DatabaseWindow.cpp b/pglab/DatabaseWindow.cpp index dd813da..c5890c2 100644 --- a/pglab/DatabaseWindow.cpp +++ b/pglab/DatabaseWindow.cpp @@ -18,18 +18,17 @@ #include "plugin_support/PluginContentWidget.h" #include "CodeGenerator.h" #include "MasterController.h" -#include "CrudTab.h" #include "WorkManager.h" #include "ScopeGuard.h" #include "EditTableWidget.h" -#include "IPluginContentWidgetContext.h" +#include "plugin_support/PluginContentWidgetContextBase.h" #include "TaskExecutor.h" namespace pg = Pgsql; namespace DatabaseWindow_details { - class DatabaseWindowContentContext: public IPluginContentWidgetContext { + class DatabaseWindowContentContext: public PluginContentWidgetContextBase { public: explicit DatabaseWindowContentContext(DatabaseWindow *window) : m_window(window) @@ -54,6 +53,11 @@ namespace DatabaseWindow_details { { m_window->statusBar()->showMessage(msg); } + + void addContentWidget(PluginContentWidget *widget) override + { + m_window->addPage(widget, ""); + } private: DatabaseWindow *m_window; }; @@ -96,13 +100,6 @@ void DatabaseWindow::newCreateTablePage() ui->tabWidget->addTab(w, "Create table"); } -void DatabaseWindow::newCrudPage(const PgClass &table) -{ - CrudTab *ct = new CrudTab(m_context, this); - ct->setConfig(m_database, table); - addPage(ct, table.objectName()); -} - void DatabaseWindow::newCodeGenPage(QString query, std::shared_ptr dbres) { auto cgtab = new CodeGenerator(m_context, this); @@ -110,7 +107,6 @@ void DatabaseWindow::newCodeGenPage(QString query, std::shared_ptrtabWidget->currentWidget(); @@ -143,16 +139,16 @@ void DatabaseWindow::catalogLoaded() //SCOPE_EXIT { loadFuture = {}; }; m_database = loadWatcher.future().result(); - auto tt = new TablesPage(this); + auto tt = new TablesPage(m_context, this); tt->setCatalog(m_database->catalog()); ui->tabWidget->addTab(tt, "Tables"); - auto pg_cat_tables = new TablesPage(this); + auto pg_cat_tables = new TablesPage(m_context, this); pg_cat_tables->setNamespaceFilter(TablesTableModel::PgCatalog); pg_cat_tables->setCatalog(m_database->catalog()); ui->tabWidget->addTab(pg_cat_tables, "pg_catalog"); - auto info_schema_tables = new TablesPage(this); + auto info_schema_tables = new TablesPage(m_context, this); info_schema_tables->setNamespaceFilter(TablesTableModel::InformationSchema); info_schema_tables->setCatalog(m_database->catalog()); ui->tabWidget->addTab(info_schema_tables, "information_schema"); diff --git a/pglab/DatabaseWindow.h b/pglab/DatabaseWindow.h index 8999774..8183dff 100644 --- a/pglab/DatabaseWindow.h +++ b/pglab/DatabaseWindow.h @@ -55,11 +55,13 @@ public: std::shared_ptr getDatabase() { return m_database; } - void newCrudPage(const PgClass &table); void newCodeGenPage(QString query, std::shared_ptr dbres); void setTabCaptionForWidget(QWidget *widget, const QString &caption, const QString &hint); void setTabIcon(QWidget *widget, const QString &iconname); + + /// Called when a newly created page is added to the QTabWidget + void addPage(PluginContentWidget* page, QString caption); private: Ui::MainWindow *ui; @@ -124,8 +126,6 @@ private: void newCreateTablePage(); - /// Called when a newly created page is added to the QTabWidget - void addPage(PluginContentWidget* page, QString caption); /// Called when a page is completely removed from the QTabWidget void removePage(PluginContentWidget *page); diff --git a/pglab/QueryTab.cpp b/pglab/QueryTab.cpp index 703c799..fd33353 100644 --- a/pglab/QueryTab.cpp +++ b/pglab/QueryTab.cpp @@ -21,7 +21,7 @@ #include "util.h" #include "GlobalIoService.h" #include "UserConfiguration.h" -#include "IPluginContentWidgetContext.h" +#include "plugin_support/IPluginContentWidgetContext.h" #include "plugin_support/PluginRegister.h" QueryTab::QueryTab(IPluginContentWidgetContext *context_, QWidget *parent) @@ -687,16 +687,7 @@ void QueryToolModule::new_triggered() namespace { - - std::shared_ptr createModule() - { - auto module = std::make_shared("Query tool", "pglab.querytool"); - module->init(); - - PluginRegister::getInstance()->registerModule(module); - return std::move(module); - } - - std::weak_ptr register_variable = createModule(); + std::weak_ptr register_variable = createPluginModule + ("Query tool", "pglab.querytool"); } diff --git a/pglab/QueryTab.h b/pglab/QueryTab.h index 15fe669..70bcee4 100644 --- a/pglab/QueryTab.h +++ b/pglab/QueryTab.h @@ -9,8 +9,8 @@ #include #include "plugin_support/PluginContentWidget.h" -#include #include "plugin_support/PluginModule.h" +#include namespace Ui { class QueryTab; diff --git a/pglab/TablesPage.cpp b/pglab/TablesPage.cpp index 1151396..4afd76f 100644 --- a/pglab/TablesPage.cpp +++ b/pglab/TablesPage.cpp @@ -21,11 +21,11 @@ #include "SqlCodePreview.h" #include #include +#include "plugin_support/IPluginContentWidgetContext.h" -TablesPage::TablesPage(DatabaseWindow *parent) - : QWidget(parent) +TablesPage::TablesPage(IPluginContentWidgetContext *context_, QWidget *parent) + : PluginContentWidget(context_, parent) , ui(new Ui::TablesPage) - , m_window(parent) { ui->setupUi(this); @@ -239,9 +239,10 @@ void TablesPage::on_tableListTable_doubleClicked(const QModelIndex &index) { PgClass table = m_tablesModel->getTable(index.row()); if (table.oid() != InvalidOid) { - m_window->newCrudPage(table); + context()->moduleAction("pglab.crudpage", "open", { + { "oid", table.oid() } + }); } - } void TablesPage::updateSqlTab(const std::optional &table) diff --git a/pglab/TablesPage.h b/pglab/TablesPage.h index ecffd24..5079c45 100644 --- a/pglab/TablesPage.h +++ b/pglab/TablesPage.h @@ -6,6 +6,8 @@ #include #include #include "TablesTableModel.h" +#include "plugin_support/PluginContentWidget.h" +#include "plugin_support/PluginModule.h" namespace Ui { class TablesPage; @@ -18,25 +20,23 @@ class ConstraintModel; class PgDatabaseCatalog; class NamespaceFilterWidget; class IndexModel; -class DatabaseWindow; class PropertiesPage; class TriggerPage; class PgClass; class SqlCodePreview; -class TablesPage : public QWidget +class TablesPage : public PluginContentWidget { Q_OBJECT public: - explicit TablesPage(DatabaseWindow *parent = nullptr); + explicit TablesPage(IPluginContentWidgetContext *context, QWidget *parent = nullptr); ~TablesPage(); void setCatalog(std::shared_ptr cat); void setNamespaceFilter(TablesTableModel::NamespaceFilter filter); private: Ui::TablesPage *ui; - DatabaseWindow *m_window; // QWidget *m_columnsTab; ColumnPage *m_columnsPage; // QWidget *m_propertiesTab; diff --git a/pglab/pglab.pro b/pglab/pglab.pro index 4b399cc..18d35e4 100644 --- a/pglab/pglab.pro +++ b/pglab/pglab.pro @@ -87,7 +87,8 @@ PropertyProxyModel.cpp \ plugin_support/MenuLocation.cpp \ plugin_support/ToolbarLocation.cpp \ plugin_support/PluginRegister.cpp \ - plugin_support/PluginContentWidget.cpp + plugin_support/PluginContentWidget.cpp \ + plugin_support/PluginContentWidgetContextBase.cpp HEADERS += \ QueryResultModel.h \ @@ -147,13 +148,15 @@ CustomDataRole.h \ SequencesPage.h \ DatabaseWindow.h \ PgLabTableView.h \ - IPluginContentWidgetContext.h \ plugin_support/PluginModule.h \ plugin_support/MenuPath.h \ plugin_support/MenuLocation.h \ plugin_support/ToolbarLocation.h \ plugin_support/PluginRegister.h \ - plugin_support/PluginContentWidget.h + plugin_support/PluginContentWidget.h \ + plugin_support/ModuleActionParameters.h \ + plugin_support/IPluginContentWidgetContext.h \ + plugin_support/PluginContentWidgetContextBase.h FORMS += mainwindow.ui \ ConnectionManagerWindow.ui \ diff --git a/pglab/IPluginContentWidgetContext.h b/pglab/plugin_support/IPluginContentWidgetContext.h similarity index 76% rename from pglab/IPluginContentWidgetContext.h rename to pglab/plugin_support/IPluginContentWidgetContext.h index 8624ab8..9424ce4 100644 --- a/pglab/IPluginContentWidgetContext.h +++ b/pglab/plugin_support/IPluginContentWidgetContext.h @@ -3,7 +3,7 @@ #include #include -#include "tsqueue.h" +#include "plugin_support/ModuleActionParameters.h" class OpenDatabase; class PluginContentWidget; @@ -11,7 +11,8 @@ class PluginContentWidget; /** This class serves to isolate the plugin from the actual construct in which it is * used. * - * It provides functions for operating on the context without needing to many details. + * It provides interface for operating on the context without needing to many details. + * Actual default implementation is in PluginContentWidgetContextBase. */ class IPluginContentWidgetContext { public: @@ -36,9 +37,15 @@ public: */ virtual std::shared_ptr getDatabase() = 0; - virtual void QueueTask(TSQueue::t_Callable c) = 0; - virtual void showStatusMessage(const QString &msg) = 0; + + virtual void moduleAction( + const QString &module_identifier, + QString module_action, + const ModuleActionParameters &action_params + ) = 0; + + virtual void addContentWidget(PluginContentWidget *widget) = 0; }; #endif // IPLUGINCONTENTWIDGETCONTEXT_H diff --git a/pglab/plugin_support/ModuleActionParameters.h b/pglab/plugin_support/ModuleActionParameters.h new file mode 100644 index 0000000..e3fb45c --- /dev/null +++ b/pglab/plugin_support/ModuleActionParameters.h @@ -0,0 +1,10 @@ +#ifndef MODULEACTIONPARAMETERS_H +#define MODULEACTIONPARAMETERS_H + +#include +#include +#include + +using ModuleActionParameters = std::map; + +#endif // MODULEACTIONPARAMETERS_H diff --git a/pglab/plugin_support/PluginContentWidgetContextBase.cpp b/pglab/plugin_support/PluginContentWidgetContextBase.cpp new file mode 100644 index 0000000..10587ff --- /dev/null +++ b/pglab/plugin_support/PluginContentWidgetContextBase.cpp @@ -0,0 +1,27 @@ +#include "PluginContentWidgetContextBase.h" +#include "PluginRegister.h" +#include "PluginModule.h" +#include + +PluginContentWidgetContextBase::PluginContentWidgetContextBase() = default; + +void PluginContentWidgetContextBase::moduleAction( + const QString &module_identifier, + QString module_action, + const ModuleActionParameters &action_params + ) +{ + auto reg = PluginRegister::getInstance(); + auto mod = reg->findModule(module_identifier); + if (mod) { + auto action = mod->findModuleAction(module_action); + if (action) { + qDebug() << QString("module %1 action %2 called ").arg(module_identifier, module_action); + (*action)(this, action_params); + } + else + qWarning() << QString("module %1 has no action %2").arg(module_identifier, module_action); + } + else + qWarning() << QString("module not found %1").arg(module_identifier); +} diff --git a/pglab/plugin_support/PluginContentWidgetContextBase.h b/pglab/plugin_support/PluginContentWidgetContextBase.h new file mode 100644 index 0000000..d060ba3 --- /dev/null +++ b/pglab/plugin_support/PluginContentWidgetContextBase.h @@ -0,0 +1,19 @@ +#ifndef PLUGINCONTENTWIDGETCONTEXTBASE_H +#define PLUGINCONTENTWIDGETCONTEXTBASE_H + +#include "plugin_support/IPluginContentWidgetContext.h" + +/// Provides base implementation of IPluginContentWidgetContext +class PluginContentWidgetContextBase : public IPluginContentWidgetContext +{ +public: + PluginContentWidgetContextBase(); + + void moduleAction( + const QString &module_identifier, + QString module_action, + const ModuleActionParameters &action_params + ) override; +}; + +#endif // PLUGINCONTENTWIDGETCONTEXTBASE_H diff --git a/pglab/plugin_support/PluginModule.cpp b/pglab/plugin_support/PluginModule.cpp index 1991dd1..a9e9ec7 100644 --- a/pglab/plugin_support/PluginModule.cpp +++ b/pglab/plugin_support/PluginModule.cpp @@ -16,4 +16,18 @@ void PluginModule::registerAction(QAction *action, MenuLocation menu_location, T } +void PluginModule::registerModuleAction(QString module_action, ModuleAction action) +{ + m_moduleActions.emplace( + std::move(module_action), + std::move(action) + ); +} +const PluginModule::ModuleAction* PluginModule::findModuleAction(const QString &module_action) const +{ + auto res = m_moduleActions.find(module_action); + if (res == m_moduleActions.end()) + return nullptr; + return &res->second; +} diff --git a/pglab/plugin_support/PluginModule.h b/pglab/plugin_support/PluginModule.h index 6577550..68b147c 100644 --- a/pglab/plugin_support/PluginModule.h +++ b/pglab/plugin_support/PluginModule.h @@ -1,16 +1,23 @@ #ifndef PLUGIN_SUPPORTPLUGINMODULE_H #define PLUGIN_SUPPORTPLUGINMODULE_H -#include "plugin_support/MenuLocation.h" -#include "plugin_support/ToolbarLocation.h" +#include "MenuLocation.h" +#include "ToolbarLocation.h" +#include "ModuleActionParameters.h" +#include "PluginRegister.h" #include +#include +#include class QAction; - +class IPluginContentWidgetContext; class PluginModule: public QObject { Q_OBJECT public: + using ModuleAction = std::function; + using ModuleActionMap = std::map; + PluginModule(QString name, QString ident); const QString& name() const { return m_name; } @@ -19,13 +26,32 @@ public: void setDisplayCategory(QString category); void registerAction(QAction *action, MenuLocation menu_location, ToolbarLocation toolbar_location); + + void registerModuleAction(QString module_action, ModuleAction action); + + /// Searches for and returns a pointer to the requested module action. + /// When the action is not found nullptr is returned. + const ModuleAction* findModuleAction(const QString &module_action) const; private: /// Name shown to end users QString m_name; /// Unique identifier QString m_ident; QString m_displayCategory; + + ModuleActionMap m_moduleActions; }; +template +std::shared_ptr createPluginModule(QString name, QString ident) +{ + auto module = std::make_shared(std::move(name), std::move(ident)); + module->init(); + + PluginRegister::getInstance()->registerModule(module); + return std::move(module); +} + + #endif // PLUGIN_SUPPORTPLUGINMODULE_H diff --git a/pglab/plugin_support/PluginRegister.cpp b/pglab/plugin_support/PluginRegister.cpp index 57b9eca..342a691 100644 --- a/pglab/plugin_support/PluginRegister.cpp +++ b/pglab/plugin_support/PluginRegister.cpp @@ -25,5 +25,13 @@ PluginRegister::PluginRegister() = default; void PluginRegister::registerModule(PluginModuleSPtr module) { - qDebug() << "Register called for " << module->identifier(); + m_moduleMap.emplace(module->identifier(), module); +} + +const PluginModule* PluginRegister::findModule(const QString &module_ident) const +{ + auto res = m_moduleMap.find(module_ident); + if (res == m_moduleMap.end()) + return nullptr; + return res->second.get(); } diff --git a/pglab/plugin_support/PluginRegister.h b/pglab/plugin_support/PluginRegister.h index ff80136..3bd43a7 100644 --- a/pglab/plugin_support/PluginRegister.h +++ b/pglab/plugin_support/PluginRegister.h @@ -19,6 +19,8 @@ public: PluginRegister(); void registerModule(PluginModuleSPtr module); const ModuleMap& modules() const { return m_moduleMap; } + + const PluginModule* findModule(const QString &module_ident) const; private: ModuleMap m_moduleMap;