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.
This commit is contained in:
eelke 2018-12-31 15:20:55 +01:00
parent f996703937
commit b0cd47ef46
17 changed files with 209 additions and 55 deletions

View file

@ -4,11 +4,16 @@
#include "ResultTableModelUtil.h" #include "ResultTableModelUtil.h"
#include "PgLabItemDelegate.h" #include "PgLabItemDelegate.h"
#include "IntegerRange.h" #include "IntegerRange.h"
#include "OpenDatabase.h"
#include "catalog/PgClassContainer.h"
#include "catalog/PgDatabaseCatalog.h"
#include <QDebug> #include <QDebug>
#include <QMenu> #include <QMenu>
#include <QMessageBox> #include <QMessageBox>
#include <iterator> #include <iterator>
#include <set> #include <set>
#include "plugin_support/PluginRegister.h"
#include "plugin_support/IPluginContentWidgetContext.h"
CrudTab::CrudTab(IPluginContentWidgetContext *context, QWidget *parent) CrudTab::CrudTab(IPluginContentWidgetContext *context, QWidget *parent)
@ -41,11 +46,11 @@ CrudTab::~CrudTab()
delete ui; delete ui;
} }
void CrudTab::setConfig(std::shared_ptr<OpenDatabase> db, const PgClass &table) void CrudTab::setConfig(Oid oid) //std::shared_ptr<OpenDatabase> db, const PgClass &table)
{ {
m_db = db; m_db = context()->getDatabase();;
m_table = table; m_table = *m_db->catalog()->classes()->getByKey(oid);
m_crudModel->setConfig(db, table); m_crudModel->setConfig(m_db, *m_table);
} }
void CrudTab::refresh() void CrudTab::refresh()
@ -113,3 +118,34 @@ void CrudTab::headerCustomContextMenu(const QPoint &pos)
auto horizontal_header = ui->tableView->horizontalHeader(); auto horizontal_header = ui->tableView->horizontalHeader();
menu->popup(horizontal_header->mapToGlobal(pos)); menu->popup(horizontal_header->mapToGlobal(pos));
} }
void CrudPageModule::init()
{
registerModuleAction("open",
[this] (IPluginContentWidgetContext* context,
const ModuleActionParameters &params)
{
moduleAction_open(context, params);
});
}
void CrudPageModule::moduleAction_open(
IPluginContentWidgetContext* context,
const ModuleActionParameters &params
)
{
// 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<PluginModule> register_variable = createPluginModule<CrudPageModule>
("CRUD tool", "pglab.crudpage");
}

View file

@ -4,6 +4,7 @@
#include "catalog/PgClass.h" #include "catalog/PgClass.h"
#include <QWidget> #include <QWidget>
#include "plugin_support/PluginContentWidget.h" #include "plugin_support/PluginContentWidget.h"
#include "plugin_support/PluginModule.h"
#include <memory> #include <memory>
#include <optional> #include <optional>
@ -22,7 +23,7 @@ public:
explicit CrudTab(IPluginContentWidgetContext *context, QWidget *parent = nullptr); explicit CrudTab(IPluginContentWidgetContext *context, QWidget *parent = nullptr);
~CrudTab() override; ~CrudTab() override;
void setConfig(std::shared_ptr<OpenDatabase> db, const PgClass &table); void setConfig(Oid oid);
void refresh(); void refresh();
@ -41,4 +42,17 @@ private slots:
void headerCustomContextMenu(const QPoint &pos); 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 &params);
};
#endif // CRUDTAB_H #endif // CRUDTAB_H

View file

@ -18,18 +18,17 @@
#include "plugin_support/PluginContentWidget.h" #include "plugin_support/PluginContentWidget.h"
#include "CodeGenerator.h" #include "CodeGenerator.h"
#include "MasterController.h" #include "MasterController.h"
#include "CrudTab.h"
#include "WorkManager.h" #include "WorkManager.h"
#include "ScopeGuard.h" #include "ScopeGuard.h"
#include "EditTableWidget.h" #include "EditTableWidget.h"
#include "IPluginContentWidgetContext.h" #include "plugin_support/PluginContentWidgetContextBase.h"
#include "TaskExecutor.h" #include "TaskExecutor.h"
namespace pg = Pgsql; namespace pg = Pgsql;
namespace DatabaseWindow_details { namespace DatabaseWindow_details {
class DatabaseWindowContentContext: public IPluginContentWidgetContext { class DatabaseWindowContentContext: public PluginContentWidgetContextBase {
public: public:
explicit DatabaseWindowContentContext(DatabaseWindow *window) explicit DatabaseWindowContentContext(DatabaseWindow *window)
: m_window(window) : m_window(window)
@ -54,6 +53,11 @@ namespace DatabaseWindow_details {
{ {
m_window->statusBar()->showMessage(msg); m_window->statusBar()->showMessage(msg);
} }
void addContentWidget(PluginContentWidget *widget) override
{
m_window->addPage(widget, "");
}
private: private:
DatabaseWindow *m_window; DatabaseWindow *m_window;
}; };
@ -96,13 +100,6 @@ void DatabaseWindow::newCreateTablePage()
ui->tabWidget->addTab(w, "Create table"); 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<const Pgsql::Result> dbres) void DatabaseWindow::newCodeGenPage(QString query, std::shared_ptr<const Pgsql::Result> dbres)
{ {
auto cgtab = new CodeGenerator(m_context, this); auto cgtab = new CodeGenerator(m_context, this);
@ -110,7 +107,6 @@ void DatabaseWindow::newCodeGenPage(QString query, std::shared_ptr<const Pgsql::
addPage(cgtab, "Codegen"); addPage(cgtab, "Codegen");
} }
QueryTab *DatabaseWindow::GetActiveQueryTab() QueryTab *DatabaseWindow::GetActiveQueryTab()
{ {
QWidget *widget = ui->tabWidget->currentWidget(); QWidget *widget = ui->tabWidget->currentWidget();
@ -143,16 +139,16 @@ void DatabaseWindow::catalogLoaded()
//SCOPE_EXIT { loadFuture = {}; }; //SCOPE_EXIT { loadFuture = {}; };
m_database = loadWatcher.future().result(); m_database = loadWatcher.future().result();
auto tt = new TablesPage(this); auto tt = new TablesPage(m_context, this);
tt->setCatalog(m_database->catalog()); tt->setCatalog(m_database->catalog());
ui->tabWidget->addTab(tt, "Tables"); 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->setNamespaceFilter(TablesTableModel::PgCatalog);
pg_cat_tables->setCatalog(m_database->catalog()); pg_cat_tables->setCatalog(m_database->catalog());
ui->tabWidget->addTab(pg_cat_tables, "pg_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->setNamespaceFilter(TablesTableModel::InformationSchema);
info_schema_tables->setCatalog(m_database->catalog()); info_schema_tables->setCatalog(m_database->catalog());
ui->tabWidget->addTab(info_schema_tables, "information_schema"); ui->tabWidget->addTab(info_schema_tables, "information_schema");

View file

@ -55,11 +55,13 @@ public:
std::shared_ptr<OpenDatabase> getDatabase() { return m_database; } std::shared_ptr<OpenDatabase> getDatabase() { return m_database; }
void newCrudPage(const PgClass &table);
void newCodeGenPage(QString query, std::shared_ptr<const Pgsql::Result> dbres); void newCodeGenPage(QString query, std::shared_ptr<const Pgsql::Result> dbres);
void setTabCaptionForWidget(QWidget *widget, const QString &caption, const QString &hint); void setTabCaptionForWidget(QWidget *widget, const QString &caption, const QString &hint);
void setTabIcon(QWidget *widget, const QString &iconname); 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: private:
Ui::MainWindow *ui; Ui::MainWindow *ui;
@ -124,8 +126,6 @@ private:
void newCreateTablePage(); 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 /// Called when a page is completely removed from the QTabWidget
void removePage(PluginContentWidget *page); void removePage(PluginContentWidget *page);

View file

@ -21,7 +21,7 @@
#include "util.h" #include "util.h"
#include "GlobalIoService.h" #include "GlobalIoService.h"
#include "UserConfiguration.h" #include "UserConfiguration.h"
#include "IPluginContentWidgetContext.h" #include "plugin_support/IPluginContentWidgetContext.h"
#include "plugin_support/PluginRegister.h" #include "plugin_support/PluginRegister.h"
QueryTab::QueryTab(IPluginContentWidgetContext *context_, QWidget *parent) QueryTab::QueryTab(IPluginContentWidgetContext *context_, QWidget *parent)
@ -687,16 +687,7 @@ void QueryToolModule::new_triggered()
namespace { namespace {
std::weak_ptr<PluginModule> register_variable = createPluginModule<QueryToolModule>
std::shared_ptr<PluginModule> createModule() ("Query tool", "pglab.querytool");
{
auto module = std::make_shared<QueryToolModule>("Query tool", "pglab.querytool");
module->init();
PluginRegister::getInstance()->registerModule(module);
return std::move(module);
}
std::weak_ptr<PluginModule> register_variable = createModule();
} }

View file

@ -9,8 +9,8 @@
#include <QWidget> #include <QWidget>
#include "plugin_support/PluginContentWidget.h" #include "plugin_support/PluginContentWidget.h"
#include <memory>
#include "plugin_support/PluginModule.h" #include "plugin_support/PluginModule.h"
#include <memory>
namespace Ui { namespace Ui {
class QueryTab; class QueryTab;

View file

@ -21,11 +21,11 @@
#include "SqlCodePreview.h" #include "SqlCodePreview.h"
#include <QStringBuilder> #include <QStringBuilder>
#include <unordered_set> #include <unordered_set>
#include "plugin_support/IPluginContentWidgetContext.h"
TablesPage::TablesPage(DatabaseWindow *parent) TablesPage::TablesPage(IPluginContentWidgetContext *context_, QWidget *parent)
: QWidget(parent) : PluginContentWidget(context_, parent)
, ui(new Ui::TablesPage) , ui(new Ui::TablesPage)
, m_window(parent)
{ {
ui->setupUi(this); ui->setupUi(this);
@ -239,9 +239,10 @@ void TablesPage::on_tableListTable_doubleClicked(const QModelIndex &index)
{ {
PgClass table = m_tablesModel->getTable(index.row()); PgClass table = m_tablesModel->getTable(index.row());
if (table.oid() != InvalidOid) { if (table.oid() != InvalidOid) {
m_window->newCrudPage(table); context()->moduleAction("pglab.crudpage", "open", {
{ "oid", table.oid() }
});
} }
} }
void TablesPage::updateSqlTab(const std::optional<PgClass> &table) void TablesPage::updateSqlTab(const std::optional<PgClass> &table)

View file

@ -6,6 +6,8 @@
#include <optional> #include <optional>
#include <QItemSelection> #include <QItemSelection>
#include "TablesTableModel.h" #include "TablesTableModel.h"
#include "plugin_support/PluginContentWidget.h"
#include "plugin_support/PluginModule.h"
namespace Ui { namespace Ui {
class TablesPage; class TablesPage;
@ -18,25 +20,23 @@ class ConstraintModel;
class PgDatabaseCatalog; class PgDatabaseCatalog;
class NamespaceFilterWidget; class NamespaceFilterWidget;
class IndexModel; class IndexModel;
class DatabaseWindow;
class PropertiesPage; class PropertiesPage;
class TriggerPage; class TriggerPage;
class PgClass; class PgClass;
class SqlCodePreview; class SqlCodePreview;
class TablesPage : public QWidget class TablesPage : public PluginContentWidget
{ {
Q_OBJECT Q_OBJECT
public: public:
explicit TablesPage(DatabaseWindow *parent = nullptr); explicit TablesPage(IPluginContentWidgetContext *context, QWidget *parent = nullptr);
~TablesPage(); ~TablesPage();
void setCatalog(std::shared_ptr<PgDatabaseCatalog> cat); void setCatalog(std::shared_ptr<PgDatabaseCatalog> cat);
void setNamespaceFilter(TablesTableModel::NamespaceFilter filter); void setNamespaceFilter(TablesTableModel::NamespaceFilter filter);
private: private:
Ui::TablesPage *ui; Ui::TablesPage *ui;
DatabaseWindow *m_window;
// QWidget *m_columnsTab; // QWidget *m_columnsTab;
ColumnPage *m_columnsPage; ColumnPage *m_columnsPage;
// QWidget *m_propertiesTab; // QWidget *m_propertiesTab;

View file

@ -87,7 +87,8 @@ PropertyProxyModel.cpp \
plugin_support/MenuLocation.cpp \ plugin_support/MenuLocation.cpp \
plugin_support/ToolbarLocation.cpp \ plugin_support/ToolbarLocation.cpp \
plugin_support/PluginRegister.cpp \ plugin_support/PluginRegister.cpp \
plugin_support/PluginContentWidget.cpp plugin_support/PluginContentWidget.cpp \
plugin_support/PluginContentWidgetContextBase.cpp
HEADERS += \ HEADERS += \
QueryResultModel.h \ QueryResultModel.h \
@ -147,13 +148,15 @@ CustomDataRole.h \
SequencesPage.h \ SequencesPage.h \
DatabaseWindow.h \ DatabaseWindow.h \
PgLabTableView.h \ PgLabTableView.h \
IPluginContentWidgetContext.h \
plugin_support/PluginModule.h \ plugin_support/PluginModule.h \
plugin_support/MenuPath.h \ plugin_support/MenuPath.h \
plugin_support/MenuLocation.h \ plugin_support/MenuLocation.h \
plugin_support/ToolbarLocation.h \ plugin_support/ToolbarLocation.h \
plugin_support/PluginRegister.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 \ FORMS += mainwindow.ui \
ConnectionManagerWindow.ui \ ConnectionManagerWindow.ui \

View file

@ -3,7 +3,7 @@
#include <QString> #include <QString>
#include <memory> #include <memory>
#include "tsqueue.h" #include "plugin_support/ModuleActionParameters.h"
class OpenDatabase; class OpenDatabase;
class PluginContentWidget; class PluginContentWidget;
@ -11,7 +11,8 @@ class PluginContentWidget;
/** This class serves to isolate the plugin from the actual construct in which it is /** This class serves to isolate the plugin from the actual construct in which it is
* used. * 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 { class IPluginContentWidgetContext {
public: public:
@ -36,9 +37,15 @@ public:
*/ */
virtual std::shared_ptr<OpenDatabase> getDatabase() = 0; virtual std::shared_ptr<OpenDatabase> getDatabase() = 0;
virtual void QueueTask(TSQueue::t_Callable c) = 0;
virtual void showStatusMessage(const QString &msg) = 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 #endif // IPLUGINCONTENTWIDGETCONTEXT_H

View file

@ -0,0 +1,10 @@
#ifndef MODULEACTIONPARAMETERS_H
#define MODULEACTIONPARAMETERS_H
#include <map>
#include <QString>
#include <QVariant>
using ModuleActionParameters = std::map<QString, QVariant>;
#endif // MODULEACTIONPARAMETERS_H

View file

@ -0,0 +1,27 @@
#include "PluginContentWidgetContextBase.h"
#include "PluginRegister.h"
#include "PluginModule.h"
#include <QDebug>
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);
}

View file

@ -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

View file

@ -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;
}

View file

@ -1,16 +1,23 @@
#ifndef PLUGIN_SUPPORTPLUGINMODULE_H #ifndef PLUGIN_SUPPORTPLUGINMODULE_H
#define PLUGIN_SUPPORTPLUGINMODULE_H #define PLUGIN_SUPPORTPLUGINMODULE_H
#include "plugin_support/MenuLocation.h" #include "MenuLocation.h"
#include "plugin_support/ToolbarLocation.h" #include "ToolbarLocation.h"
#include "ModuleActionParameters.h"
#include "PluginRegister.h"
#include <QObject> #include <QObject>
#include <functional>
#include <map>
class QAction; class QAction;
class IPluginContentWidgetContext;
class PluginModule: public QObject { class PluginModule: public QObject {
Q_OBJECT Q_OBJECT
public: public:
using ModuleAction = std::function<void(IPluginContentWidgetContext*, const ModuleActionParameters &)>;
using ModuleActionMap = std::map<QString, ModuleAction>;
PluginModule(QString name, QString ident); PluginModule(QString name, QString ident);
const QString& name() const { return m_name; } const QString& name() const { return m_name; }
@ -19,13 +26,32 @@ public:
void setDisplayCategory(QString category); void setDisplayCategory(QString category);
void registerAction(QAction *action, MenuLocation menu_location, ToolbarLocation toolbar_location); 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: private:
/// Name shown to end users /// Name shown to end users
QString m_name; QString m_name;
/// Unique identifier /// Unique identifier
QString m_ident; QString m_ident;
QString m_displayCategory; QString m_displayCategory;
ModuleActionMap m_moduleActions;
}; };
template <typename T>
std::shared_ptr<PluginModule> createPluginModule(QString name, QString ident)
{
auto module = std::make_shared<T>(std::move(name), std::move(ident));
module->init();
PluginRegister::getInstance()->registerModule(module);
return std::move(module);
}
#endif // PLUGIN_SUPPORTPLUGINMODULE_H #endif // PLUGIN_SUPPORTPLUGINMODULE_H

View file

@ -25,5 +25,13 @@ PluginRegister::PluginRegister() = default;
void PluginRegister::registerModule(PluginModuleSPtr module) 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();
} }

View file

@ -19,6 +19,8 @@ public:
PluginRegister(); PluginRegister();
void registerModule(PluginModuleSPtr module); void registerModule(PluginModuleSPtr module);
const ModuleMap& modules() const { return m_moduleMap; } const ModuleMap& modules() const { return m_moduleMap; }
const PluginModule* findModule(const QString &module_ident) const;
private: private:
ModuleMap m_moduleMap; ModuleMap m_moduleMap;