diff --git a/core/ControllableTask.h b/core/ControllableTask.h index 70a44fa..8fe5738 100644 --- a/core/ControllableTask.h +++ b/core/ControllableTask.h @@ -12,8 +12,9 @@ template class ControllableTask { public: + using Result = T; virtual ~ControllableTask() {} - virtual T run(TaskControl& control) = 0; + virtual Result run(TaskControl& control) = 0; }; #endif // CONTROLLABLETASK_H diff --git a/core/ExplainTreeModelItem.cpp b/core/ExplainTreeModelItem.cpp index 5f71489..035c671 100644 --- a/core/ExplainTreeModelItem.cpp +++ b/core/ExplainTreeModelItem.cpp @@ -4,6 +4,14 @@ namespace { + class registerMetaTypes { + public: + registerMetaTypes() + { + qRegisterMetaType(); + } + } registerMetaTypes_instance; + ExplainTreeModelItemPtr createPlanItemFromJson(Json::Value &plan) { diff --git a/core/ExplainTreeModelItem.h b/core/ExplainTreeModelItem.h index f800874..f78bb73 100644 --- a/core/ExplainTreeModelItem.h +++ b/core/ExplainTreeModelItem.h @@ -1,9 +1,9 @@ #pragma once #include -//#include #include #include +#include namespace Json { @@ -161,3 +161,4 @@ public: }; +Q_DECLARE_METATYPE(ExplainRoot::SPtr); diff --git a/core/RunControllableTask.h b/core/RunControllableTask.h index c93e19c..6cca6ae 100644 --- a/core/RunControllableTask.h +++ b/core/RunControllableTask.h @@ -7,14 +7,16 @@ #include #include +#include #include "ControllableTask.h" +#include "TaskControl.h" template class RunControllableTask : public QFutureInterface , public QRunnable { public: RunControllableTask(ControllableTask* tsk) : task(tsk) { } - virtial ~RunControllableTask() { delete task; } + virtual ~RunControllableTask() { delete task; } QFuture start() { diff --git a/core/TaskControl.h b/core/TaskControl.h index 7984900..3120e51 100644 --- a/core/TaskControl.h +++ b/core/TaskControl.h @@ -7,8 +7,7 @@ #include -class TaskControl -{ +class TaskControl { public: TaskControl(QFutureInterfaceBase *f) : fu(f) { } bool shouldRun() const { return !fu->isCanceled(); } diff --git a/core/TaskExecutor.h b/core/TaskExecutor.h index d44cf46..9a73b30 100644 --- a/core/TaskExecutor.h +++ b/core/TaskExecutor.h @@ -6,7 +6,7 @@ */ #include "ControllableTask.h" #include "RunControllableTask.h" - +#include /** * @brief The TaskExecutor class * diff --git a/core/WorkManager.cpp b/core/WorkManager.cpp deleted file mode 100644 index 05cf95d..0000000 --- a/core/WorkManager.cpp +++ /dev/null @@ -1,47 +0,0 @@ -#include "WorkManager.h" - -#include -#include -#include -#include -#include - - -class WorkManagerImpl: public WorkManager { -public: - void addRunnable(QRunnable *runnable) override; - void addWork(Work work) override; -}; - - - -std::shared_ptr WorkManager::getWorkManager() -{ - static std::shared_ptr wm = std::make_shared(); - return wm; -} - -void WorkManagerImpl::addRunnable(QRunnable *runnable) -{ - auto tp = QThreadPool::globalInstance(); - tp->start(runnable); -} - -class CallableTask : public QRunnable { -public: - CallableTask(WorkManager::Work &&w) - : work(std::move(w)) - {} -protected: - void run() final - { - work(); - } -private: - WorkManager::Work work; -}; - -void WorkManagerImpl::addWork(Work work) -{ - addRunnable(new CallableTask(std::move(work))); -} diff --git a/core/WorkManager.h b/core/WorkManager.h deleted file mode 100644 index c90239d..0000000 --- a/core/WorkManager.h +++ /dev/null @@ -1,22 +0,0 @@ -#ifndef WORKMANAGER_H -#define WORKMANAGER_H - -#include -#include - -class QRunnable; - -class WorkManager { -public: - - static std::shared_ptr getWorkManager(); - - using Work = std::function; - - virtual ~WorkManager() = default; - virtual void addRunnable(QRunnable *runnable) = 0; - virtual void addWork(Work work) = 0; - -}; - -#endif // WORKMANAGER_H diff --git a/core/core.pro b/core/core.pro index a4a7e22..2289bec 100644 --- a/core/core.pro +++ b/core/core.pro @@ -31,7 +31,6 @@ SOURCES += my_boost_assert_handler.cpp \ QueuedBackgroundTask.cpp \ ExplainTreeModelItem.cpp \ jsoncpp.cpp \ - WorkManager.cpp \ SqlParser.cpp \ SqlAstNode.cpp \ SqlAstSelectList.cpp \ @@ -48,7 +47,6 @@ HEADERS += PasswordManager.h \ Expected.h \ ExplainTreeModelItem.h \ json/json.h \ - WorkManager.h \ TaskControl.h \ ControllableTask.h \ RunControllableTask.h \ diff --git a/pglab/ASyncWindow.cpp b/pglab/ASyncWindow.cpp deleted file mode 100644 index ab15bf6..0000000 --- a/pglab/ASyncWindow.cpp +++ /dev/null @@ -1,25 +0,0 @@ -#include "ASyncWindow.h" -#include - -ASyncWindow::ASyncWindow(QWidget *parent) - : QMainWindow(parent) -{} - -void ASyncWindow::QueueTask(TSQueue::t_Callable c) -{ - m_taskQueue.add(std::move(c)); - // Theoretically this needs to be only called if the queue was empty because otherwise it already would - // be busy emptying the queue. For now however I think it is safer to call it just to make sure. - QMetaObject::invokeMethod(this, "processCallableQueue", Qt::QueuedConnection); // queues on main thread -} - -void ASyncWindow::processCallableQueue() -{ - if (!m_taskQueue.empty()) { - auto c = m_taskQueue.pop(); - c(); - if (!m_taskQueue.empty()) { - QTimer::singleShot(0, this, SLOT(processCallableQueue())); - } - } -} diff --git a/pglab/ASyncWindow.h b/pglab/ASyncWindow.h deleted file mode 100644 index 59f3f67..0000000 --- a/pglab/ASyncWindow.h +++ /dev/null @@ -1,24 +0,0 @@ -#ifndef ASYNCWINDOW_H -#define ASYNCWINDOW_H - -#include -#include "tsqueue.h" - -class ASyncWindow : public QMainWindow { - Q_OBJECT -public: - ASyncWindow(QWidget *parent); - /* Meant to be called from other threads to pass a code block - * that has to be executed in the context of the thread of the window. - */ - void QueueTask(TSQueue::t_Callable c); -private: - TSQueue m_taskQueue; -private slots: - - void processCallableQueue(); -}; - - - -#endif // ASYNCWINDOW_H diff --git a/pglab/ApplicationWindow.cpp b/pglab/ApplicationWindow.cpp deleted file mode 100644 index dd1c511..0000000 --- a/pglab/ApplicationWindow.cpp +++ /dev/null @@ -1,14 +0,0 @@ -#include "ApplicationWindow.h" -#include "ui_ApplicationWindow.h" - -ApplicationWindow::ApplicationWindow(QWidget *parent) : - QMainWindow(parent), - ui(new Ui::ApplicationWindow) -{ - ui->setupUi(this); -} - -ApplicationWindow::~ApplicationWindow() -{ - delete ui; -} diff --git a/pglab/ApplicationWindow.h b/pglab/ApplicationWindow.h deleted file mode 100644 index b65d4e4..0000000 --- a/pglab/ApplicationWindow.h +++ /dev/null @@ -1,22 +0,0 @@ -#ifndef APPLICATIONWINDOW_H -#define APPLICATIONWINDOW_H - -#include - -namespace Ui { -class ApplicationWindow; -} - -class ApplicationWindow : public QMainWindow -{ - Q_OBJECT - -public: - explicit ApplicationWindow(QWidget *parent = 0); - ~ApplicationWindow(); - -private: - Ui::ApplicationWindow *ui; -}; - -#endif // APPLICATIONWINDOW_H diff --git a/pglab/ApplicationWindow.ui b/pglab/ApplicationWindow.ui deleted file mode 100644 index b6faa92..0000000 --- a/pglab/ApplicationWindow.ui +++ /dev/null @@ -1,145 +0,0 @@ - - - ApplicationWindow - - - - 0 - 0 - 314 - 672 - - - - MainWindow - - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - QFrame::StyledPanel - - - QFrame::Raised - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - 0 - 0 - - - - QFrame::StyledPanel - - - QFrame::Raised - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - 1 - 1 - - - - false - - - - - - - ... - - - - - - - - - - - 0 - 0 - - - - QFrame::StyledPanel - - - QFrame::Raised - - - - - - - - - - - - 0 - 0 - 314 - 25 - - - - - - - - diff --git a/pglab/TablesPage.cpp b/pglab/CatalogInspector.cpp similarity index 69% rename from pglab/TablesPage.cpp rename to pglab/CatalogInspector.cpp index 1151396..c8063e4 100644 --- a/pglab/TablesPage.cpp +++ b/pglab/CatalogInspector.cpp @@ -1,4 +1,4 @@ -#include "TablesPage.h" +#include "CatalogInspector.h" #include "ui_TablesPage.h" #include "catalog/PgAttribute.h" @@ -10,7 +10,7 @@ #include "ConstraintModel.h" #include "IconColumnDelegate.h" #include "IndexModel.h" -#include "DatabaseWindow.h" +#include "OpenDatabase.h" #include "PgLabItemDelegate.h" #include "PropertiesPage.h" #include "ResultTableModelUtil.h" @@ -21,11 +21,11 @@ #include "SqlCodePreview.h" #include #include +#include "plugin_support/IPluginContentWidgetContext.h" -TablesPage::TablesPage(DatabaseWindow *parent) - : QWidget(parent) +CatalogInspector::CatalogInspector(IPluginContentWidgetContext *context_, QWidget *parent) + : PluginContentWidget(context_, parent) , ui(new Ui::TablesPage) - , m_window(parent) { ui->setupUi(this); @@ -62,24 +62,24 @@ TablesPage::TablesPage(DatabaseWindow *parent) // --------------- // Table selection connect(ui->tableListTable->selectionModel(), &QItemSelectionModel::currentRowChanged, this, - &TablesPage::tableListTable_currentRowChanged); + &CatalogInspector::tableListTable_currentRowChanged); connect(m_tablesModel, &QAbstractItemModel::layoutChanged, - this, &TablesPage::tableListTable_layoutChanged); + this, &CatalogInspector::tableListTable_layoutChanged); //layoutChanged(const QList &parents = ..., QAbstractItemModel::LayoutChangeHint hint = ...) connect(ui->constraintsTable->selectionModel(), &QItemSelectionModel::selectionChanged, this, - &TablesPage::constraintsTable_selectionChanged); + &CatalogInspector::constraintsTable_selectionChanged); connect(ui->constraintsTable->model(), &QAbstractItemModel::modelReset, this, - &TablesPage::constraintsTable_modelReset); + &CatalogInspector::constraintsTable_modelReset); // React to changes in de selected indexes, does not trigger when model is reset connect(ui->indexesTable->selectionModel(), &QItemSelectionModel::selectionChanged, this, - &TablesPage::indexesTable_selectionChanged); + &CatalogInspector::indexesTable_selectionChanged); // Capture model reset independently connect(ui->indexesTable->model(), &QAbstractItemModel::modelReset, this, - &TablesPage::indexesTable_modelReset); + &CatalogInspector::indexesTable_modelReset); // Non designer based code // - Columns page @@ -103,12 +103,15 @@ TablesPage::TablesPage(DatabaseWindow *parent) // Force focus on columns tab by default ui->twDetails->setCurrentIndex(0); + auto db = context_->getObject(); + + setCatalog(db->catalog()); retranslateUi(false); } -void TablesPage::retranslateUi(bool all) +void CatalogInspector::retranslateUi(bool all) { if (all) ui->retranslateUi(this); @@ -124,12 +127,12 @@ void TablesPage::retranslateUi(bool all) } -TablesPage::~TablesPage() +CatalogInspector::~CatalogInspector() { delete ui; } -void TablesPage::setCatalog(std::shared_ptr cat) +void CatalogInspector::setCatalog(std::shared_ptr cat) { m_catalog = cat; m_tablesModel->setCatalog(cat); @@ -143,13 +146,28 @@ void TablesPage::setCatalog(std::shared_ptr cat) highlighter->setTypes(*cat->types()); } -void TablesPage::setNamespaceFilter(TablesTableModel::NamespaceFilter filter) +void CatalogInspector::setNamespaceFilter(TablesTableModel::NamespaceFilter filter) { m_tablesModel->setNamespaceFilter(filter); + QString hint = "Catalog instpector"; + QString caption = "Inspector"; + switch (filter) { + case TablesTableModel::PgCatalog: + hint += " - pg_catalog"; + caption = "pg_catalog"; + break; + case TablesTableModel::InformationSchema: + hint += " - information_schema"; + caption = "information_schema"; + break; + default: + break; + } + context()->setCaption(this, caption, hint); } -void TablesPage::tableListTable_currentRowChanged(const QModelIndex ¤t, const QModelIndex &previous) +void CatalogInspector::tableListTable_currentRowChanged(const QModelIndex ¤t, const QModelIndex &previous) { if (current.row() != previous.row()) { if (current.isValid()) { @@ -162,7 +180,7 @@ void TablesPage::tableListTable_currentRowChanged(const QModelIndex ¤t, co } -void TablesPage::tableListTable_layoutChanged(const QList &, QAbstractItemModel::LayoutChangeHint ) +void CatalogInspector::tableListTable_layoutChanged(const QList &, QAbstractItemModel::LayoutChangeHint ) { auto&& index = ui->tableListTable->selectionModel()->currentIndex(); if (index.isValid()) { @@ -174,7 +192,7 @@ void TablesPage::tableListTable_layoutChanged(const QList } -void TablesPage::selectedTableChanged(const std::optional &table) +void CatalogInspector::selectedTableChanged(const std::optional &table) { m_columnsPage->setData(m_catalog, table); @@ -190,7 +208,7 @@ void TablesPage::selectedTableChanged(const std::optional &table) updateSqlTab(table); } -void TablesPage::constraintsTable_selectionChanged(const QItemSelection &/*selected*/, const QItemSelection &/*deselected*/) +void CatalogInspector::constraintsTable_selectionChanged(const QItemSelection &/*selected*/, const QItemSelection &/*deselected*/) { const auto indexes = ui->constraintsTable->selectionModel()->selectedIndexes(); std::unordered_set rijen; @@ -207,12 +225,12 @@ void TablesPage::constraintsTable_selectionChanged(const QItemSelection &/*selec ui->constraintSqlEdit->setPlainText(drops % "\n" % creates); } -void TablesPage::constraintsTable_modelReset() +void CatalogInspector::constraintsTable_modelReset() { ui->constraintSqlEdit->clear(); } -void TablesPage::indexesTable_selectionChanged(const QItemSelection &/*selected*/, const QItemSelection &/*deselected*/) +void CatalogInspector::indexesTable_selectionChanged(const QItemSelection &/*selected*/, const QItemSelection &/*deselected*/) { const auto indexes = ui->indexesTable->selectionModel()->selectedIndexes(); std::unordered_set rijen; @@ -230,21 +248,22 @@ void TablesPage::indexesTable_selectionChanged(const QItemSelection &/*selected* } -void TablesPage::indexesTable_modelReset() +void CatalogInspector::indexesTable_modelReset() { ui->indexSqlEdit->clear(); } -void TablesPage::on_tableListTable_doubleClicked(const QModelIndex &index) +void CatalogInspector::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) +void CatalogInspector::updateSqlTab(const std::optional &table) { if (!table.has_value()) { m_sqlCodePreview->clear(); @@ -286,3 +305,31 @@ void TablesPage::updateSqlTab(const std::optional &table) // m_sqlCodePreview->setPlainText(drop_sql % "\n\n" % create_sql); } + + +void CatalogInspectorModule::init() +{ + registerModuleAction("open", + [this] (IPluginContentWidgetContext* context, + const ModuleActionParameters ¶ms) + { + moduleAction_open(context, params); + }); +} + +void CatalogInspectorModule::moduleAction_open( + IPluginContentWidgetContext* context, + const ModuleActionParameters ¶ms + ) +{ + auto ct = new CatalogInspector(context, nullptr); + context->addContentWidget(this, ct); + auto nsf = params.at("namespace-filter").toString(); + TablesTableModel::NamespaceFilter filter = TablesTableModel::User; + if (nsf == "pg_catalog") filter = TablesTableModel::PgCatalog; + else if (nsf == "information_schema") filter = TablesTableModel::InformationSchema; + ct->setNamespaceFilter(filter); +} + +REGISTER_PLUGIN_MODULE_CAT(CatalogInspectorModule, "Catalog inspector tool", + "pglab.catalog-inspector", "database") diff --git a/pglab/TablesPage.h b/pglab/CatalogInspector.h similarity index 77% rename from pglab/TablesPage.h rename to pglab/CatalogInspector.h index ecffd24..8301398 100644 --- a/pglab/TablesPage.h +++ b/pglab/CatalogInspector.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,30 +20,25 @@ class ConstraintModel; class PgDatabaseCatalog; class NamespaceFilterWidget; class IndexModel; -class DatabaseWindow; class PropertiesPage; class TriggerPage; class PgClass; class SqlCodePreview; -class TablesPage : public QWidget +class CatalogInspector : public PluginContentWidget { Q_OBJECT public: - explicit TablesPage(DatabaseWindow *parent = nullptr); - ~TablesPage(); + explicit CatalogInspector(IPluginContentWidgetContext *context, QWidget *parent = nullptr); + ~CatalogInspector(); 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; PropertiesPage *m_propertiesPage; -// QWidget *m_triggerTab; TriggerPage *m_triggerPage; SqlCodePreview *m_sqlCodePreview; std::shared_ptr m_catalog; @@ -49,10 +46,8 @@ private: ColumnTableModel* m_columnsModel = nullptr; ConstraintModel* m_constraintModel = nullptr; IndexModel* m_indexModel = nullptr; - //NamespaceFilterWidget* m_namespaceFilterWidget; void retranslateUi(bool all = true); -// QWidget* addDetailTab(QWidget *contents, bool infront = false); void selectedTableChanged(const std::optional &table); void updateSqlTab(const std::optional &table); @@ -60,7 +55,6 @@ private slots: void tableListTable_currentRowChanged(const QModelIndex ¤t, const QModelIndex &previous); void tableListTable_layoutChanged(const QList &parents, QAbstractItemModel::LayoutChangeHint hint); -// void constraintsTable_currentRowChanged(const QModelIndex ¤t, const QModelIndex &previous); void constraintsTable_selectionChanged(const QItemSelection &selected, const QItemSelection &deselected); void constraintsTable_modelReset(); void indexesTable_selectionChanged(const QItemSelection &selected, const QItemSelection &deselected); @@ -68,4 +62,17 @@ private slots: void on_tableListTable_doubleClicked(const QModelIndex &index); }; +class CatalogInspectorModule: public PluginModule { + Q_OBJECT +public: + using PluginModule::PluginModule; + + void init(); +private slots: + +private: + void moduleAction_open(IPluginContentWidgetContext* context, const ModuleActionParameters ¶ms); +}; + + #endif // TABLESPAGE_H diff --git a/pglab/CodeGenerator.cpp b/pglab/CodeGenerator.cpp index dfc5b7e..3c2841e 100644 --- a/pglab/CodeGenerator.cpp +++ b/pglab/CodeGenerator.cpp @@ -5,8 +5,8 @@ #include "UserConfiguration.h" #include -CodeGenerator::CodeGenerator(QWidget *parent) : - PlgPage(parent), +CodeGenerator::CodeGenerator(IPluginContentWidgetContext *context, QWidget *parent) : + PluginContentWidget(context, parent), ui(new Ui::CodeGenerator) { ui->setupUi(this); @@ -27,11 +27,8 @@ void CodeGenerator::Init(std::shared_ptr catalog, QString que m_query = std::move(query); m_dbres = std::move(dbres); generateCode(); - - } - void CodeGenerator::on_updateCodeButton_clicked() { generateCode(); diff --git a/pglab/CodeGenerator.h b/pglab/CodeGenerator.h index fb09c88..63ac868 100644 --- a/pglab/CodeGenerator.h +++ b/pglab/CodeGenerator.h @@ -2,7 +2,7 @@ #define CODEGENERATOR_H #include -#include "PlgPage.h" +#include "plugin_support/PluginContentWidget.h" #include "Pgsql_declare.h" namespace Ui { @@ -11,12 +11,12 @@ class CodeGenerator; class PgDatabaseCatalog; -class CodeGenerator : public PlgPage +class CodeGenerator : public PluginContentWidget { Q_OBJECT public: - explicit CodeGenerator(QWidget *parent = nullptr); + CodeGenerator(IPluginContentWidgetContext *context, QWidget *parent = nullptr); ~CodeGenerator(); void Init(std::shared_ptr catalog, QString query, std::shared_ptr dbres); diff --git a/pglab/CrudModel.cpp b/pglab/CrudModel.cpp index 6089774..3acc331 100644 --- a/pglab/CrudModel.cpp +++ b/pglab/CrudModel.cpp @@ -1,5 +1,4 @@ #include "CrudModel.h" -#include "ASyncWindow.h" #include "OpenDatabase.h" #include "catalog/PgDatabaseCatalog.h" #include "catalog/PgAttribute.h" @@ -7,7 +6,6 @@ #include "catalog/PgConstraintContainer.h" #include "GlobalIoService.h" #include "SqlFormattingUtils.h" -#include "WorkManager.h" #include "Pgsql_oids.h" #include #include @@ -19,8 +17,8 @@ #include #include "ScopeGuard.h" -CrudModel::CrudModel(ASyncWindow *async_window) - : m_asyncWindow(async_window) +CrudModel::CrudModel(QObject *parent) + : QAbstractTableModel(parent) , m_dbConn(*getGlobalAsioIoService()) { qDebug("CrudModel created"); @@ -172,7 +170,8 @@ void CrudModel::loadData() if (res.valid()) { auto dbres = res.get(); if (dbres && *dbres) { - m_asyncWindow->QueueTask([this, dbres]() { loadIntoModel(dbres); }); + QMetaObject::invokeMethod(this, "loadIntoModel", Qt::QueuedConnection, + Q_ARG(std::shared_ptr, dbres)); } } else { @@ -196,7 +195,7 @@ void CrudModel::loadIntoModel(std::shared_ptr data) void CrudModel::initRowMapping() { - m_rowMapping.resize(m_rowCount); + m_rowMapping.resize(static_cast(m_rowCount)); for (int i = 0; i < m_rowCount; ++i) m_rowMapping[i] = RowMapping{ i }; } diff --git a/pglab/CrudModel.h b/pglab/CrudModel.h index db21177..19482bb 100644 --- a/pglab/CrudModel.h +++ b/pglab/CrudModel.h @@ -18,7 +18,6 @@ class PgConstraint; class OpenDatabase; -class ASyncWindow; /** * @brief The CrudModel class @@ -55,7 +54,7 @@ class ASyncWindow; class CrudModel: public QAbstractTableModel { Q_OBJECT public: - explicit CrudModel(ASyncWindow *async_win); + explicit CrudModel(QObject *parent = nullptr); ~CrudModel() override; void setConfig(std::shared_ptr db, const PgClass &table); @@ -231,8 +230,6 @@ private: }; using RowMappingVector = std::vector; - - ASyncWindow * m_asyncWindow; std::shared_ptr m_database; std::optional m_table; std::optional m_primaryKey; @@ -266,13 +263,6 @@ private: /// call on initial load to fill in the mappings void initRowMapping(); -// using RedirectVec = std::vector; -// /// In sync with the actual table, used to efficiently find the correct row in the result -// RedirectVec m_redirectVector; - - - void loadIntoModel(std::shared_ptr data); - Value getData(const QModelIndex &index) const; Oid getType(int column) const; @@ -320,11 +310,8 @@ private: int attNumToCol(int attnum) const { return attnum - 1; } private slots: + void loadIntoModel(std::shared_ptr data); void connectionStateChanged(ASyncDBConnection::State state); -// void queryResult(std::shared_ptr result); -// void queryError(); - -// void dataProcessingFutureFinished(); }; #endif // CRUDMODEL_H diff --git a/pglab/CrudTab.cpp b/pglab/CrudTab.cpp index 36ad56b..7b73a1e 100644 --- a/pglab/CrudTab.cpp +++ b/pglab/CrudTab.cpp @@ -1,21 +1,24 @@ #include "CrudTab.h" #include "ui_CrudTab.h" #include "CrudModel.h" -#include "DatabaseWindow.h" #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(DatabaseWindow *parent) - : PlgPage(parent) +CrudTab::CrudTab(IPluginContentWidgetContext *context, QWidget *parent) + : PluginContentWidget(context, parent) , ui(new Ui::CrudTab) - , m_window(parent) { ui->setupUi(this); @@ -43,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()->getObject(); // getDatabase();; + m_table = *m_db->catalog()->classes()->getByKey(oid); + m_crudModel->setConfig(m_db, *m_table); } void CrudTab::refresh() @@ -90,19 +93,6 @@ void CrudTab::on_actionRemove_rows_triggered() } } -std::vector CrudTab::getToolbarActions() -{ - if (actions.empty()) { - QAction *action = new QAction(QIcon(":/icons/script_go.png"), tr("Refresh"), this); - action->setShortcut(QKeySequence(Qt::Key_F5)); - connect(action, &QAction::triggered, this, &CrudTab::refresh); - actions.push_back(action); - - actions.push_back(ui->actionRemove_rows); - } - return actions; -} - void CrudTab::headerCustomContextMenu(const QPoint &pos) { auto menu = new QMenu(this); @@ -115,3 +105,37 @@ 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); + }); + + { + LWidgetAction wa("Refresh", SLOT(refresh())); + wa.setMenuLocation(MenuPath("Window/1")); + wa.setIcon(QIcon(":/icons/script_go.png")); + wa.setShortcut(QKeySequence(Qt::Key_F5)); + registerWidgetAction(wa); + } +} + +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(this, ct); // maybe CrudTab should do this + ct->setConfig(params.at("oid").toUInt()); + +} + +REGISTER_PLUGIN_MODULE_CAT(CrudPageModule, "CRUD tool", "pglab.crudpage", "database") diff --git a/pglab/CrudTab.h b/pglab/CrudTab.h index fd15e3f..d20e4af 100644 --- a/pglab/CrudTab.h +++ b/pglab/CrudTab.h @@ -3,7 +3,8 @@ #include "catalog/PgClass.h" #include -#include "PlgPage.h" +#include "plugin_support/PluginContentWidget.h" +#include "plugin_support/PluginModule.h" #include #include @@ -13,26 +14,21 @@ namespace Ui { class OpenDatabase; class CrudModel; -class DatabaseWindow; -class CrudTab : public PlgPage +class CrudTab : public PluginContentWidget { Q_OBJECT public: - explicit CrudTab(DatabaseWindow *parent = 0); + explicit CrudTab(IPluginContentWidgetContext *context, QWidget *parent = nullptr); ~CrudTab() override; - void setConfig(std::shared_ptr db, const PgClass &table); - + void setConfig(Oid oid); +public slots: void refresh(); - - virtual std::vector getToolbarActions() override; private: Ui::CrudTab *ui; - DatabaseWindow *m_window; - std::shared_ptr m_db; std::optional m_table; @@ -40,9 +36,21 @@ private: std::vector actions; private slots: -// void tableView_currentRowChanged(const QModelIndex ¤t, const QModelIndex &previous); void on_actionRemove_rows_triggered(); 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 e53eda8..4a06812 100644 --- a/pglab/DatabaseWindow.cpp +++ b/pglab/DatabaseWindow.cpp @@ -1,84 +1,49 @@ #include "DatabaseWindow.h" -#include "ui_MainWindow.h" -#include "TablesPage.h" +#include "plugin_support/IPluginContentWidgetContext.h" +#include "util.h" +#include "MasterController.h" +#include "TaskExecutor.h" +#include +#include +#include +#include + +// Pages that should become modules +#include "EditTableWidget.h" +#include "CodeGenerator.h" #include "FunctionsPage.h" #include "SequencesPage.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "QueryTab.h" -#include "util.h" -#include "PlgPage.h" -#include "CodeGenerator.h" -#include "MasterController.h" -#include "CrudTab.h" -#include "WorkManager.h" -#include "ScopeGuard.h" -#include "EditTableWidget.h" - namespace pg = Pgsql; DatabaseWindow::DatabaseWindow(MasterController *master, QWidget *parent) -: ASyncWindow(parent) - , ui(new Ui::MainWindow) + : LMainWindow(parent) + , m_masterController(master) { - ui->setupUi(this); - ui->tabWidget->setDocumentMode(true); + connect(&loadWatcher, &QFutureWatcher::finished, + this, &DatabaseWindow::catalogLoaded); + + initModuleMenus(); + QMetaObject::connectSlotsByName(this); } -DatabaseWindow::~DatabaseWindow() -{ - loader.reset(); - delete ui; -} - -QueryTab* DatabaseWindow::newSqlPage() -{ - QueryTab *qt = new QueryTab(this); - qt->setConfig(m_config, m_database->catalog()); - addPage(qt, "Tab"); - qt->newdoc(); - qt->focusEditor(); - return qt; -} +DatabaseWindow::~DatabaseWindow() = default; void DatabaseWindow::newCreateTablePage() { auto w = new EditTableWidget(m_database, this); - ui->tabWidget->addTab(w, "Create table"); -} - -void DatabaseWindow::newCrudPage(const PgClass &table) -{ - CrudTab *ct = new CrudTab(this); - ct->setConfig(m_database, table); - addPage(ct, table.objectName()); + m_tabWidget->addTab(w, "Create table"); } void DatabaseWindow::newCodeGenPage(QString query, std::shared_ptr dbres) { - auto cgtab = new CodeGenerator(this); + auto cgtab = new CodeGenerator(context(), this); cgtab->Init(m_database->catalog(), query, dbres); addPage(cgtab, "Codegen"); } - -QueryTab *DatabaseWindow::GetActiveQueryTab() -{ - QWidget *widget = ui->tabWidget->currentWidget(); - QueryTab *qt = dynamic_cast(widget); - return qt; -} - void DatabaseWindow::setConfig(const ConnectionConfig &config) { m_config = config; @@ -87,10 +52,9 @@ void DatabaseWindow::setConfig(const ConnectionConfig &config) title += m_config.name().c_str(); setWindowTitle(title); - loader = std::make_shared(m_config, [this](QRunnable *) { - QueueTask([this] () { catalogLoaded(); }); - } ); - WorkManager::getWorkManager()->addRunnable(loader.get()); + auto f = TaskExecutor::run(new LoadCatalog(m_config)); + loadWatcher.setFuture(f); + } catch (std::runtime_error &ex) { QMessageBox::critical(this, "Error reading database", QString::fromUtf8(ex.what())); @@ -102,32 +66,23 @@ void DatabaseWindow::setConfig(const ConnectionConfig &config) void DatabaseWindow::catalogLoaded() { try { - SCOPE_EXIT { loader.reset(); }; - m_database = loader->GetResult(); + m_database = loadWatcher.future().result(); + auto ctx = context(); + ctx->registerObject(m_database); - auto tt = new TablesPage(this); - tt->setCatalog(m_database->catalog()); - ui->tabWidget->addTab(tt, "Tables"); - - auto pg_cat_tables = new TablesPage(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); - info_schema_tables->setNamespaceFilter(TablesTableModel::InformationSchema); - info_schema_tables->setCatalog(m_database->catalog()); - ui->tabWidget->addTab(info_schema_tables, "information_schema"); + for (auto f : { "user", "pg_catalog", "information_schema" }) + ctx->moduleAction("pglab.catalog-inspector", "open", { + { "namespace-filter", f } + }); auto functions_page = new FunctionsPage(this); functions_page->setCatalog(m_database->catalog()); - ui->tabWidget->addTab(functions_page, "Functions"); + m_tabWidget->addTab(functions_page, "Functions"); auto sequences_page = new SequencesPage(this); sequences_page->setCatalog(m_database->catalog()); - ui->tabWidget->addTab(sequences_page, "Sequences"); + m_tabWidget->addTab(sequences_page, "Sequences"); - newSqlPage(); newCreateTablePage(); } catch (std::runtime_error &ex) { QMessageBox::critical(this, "Error reading database", @@ -137,71 +92,17 @@ void DatabaseWindow::catalogLoaded() } } -void DatabaseWindow::on_actionLoad_SQL_triggered() -{ - QString home_dir = QStandardPaths::locate(QStandardPaths::HomeLocation, "", QStandardPaths::LocateDirectory); - QString file_name = QFileDialog::getOpenFileName(this, - tr("Open sql query"), home_dir, tr("SQL files (*.sql *.txt)")); - if ( ! file_name.isEmpty()) { - QueryTab* qt = newSqlPage(); - qt->load(file_name); - } - -} - -void DatabaseWindow::on_actionSave_SQL_triggered() -{ - QueryTab *tab = GetActiveQueryTab(); - if (tab) { - tab->save(); - } -} - -void DatabaseWindow::on_actionSave_SQL_as_triggered() -{ - QueryTab *tab = GetActiveQueryTab(); - if (tab) { - tab->saveAs(); - } - -} - -void DatabaseWindow::on_actionSave_copy_of_SQL_as_triggered() -{ - QueryTab *tab = GetActiveQueryTab(); - if (tab) { - tab->saveCopyAs(); - } -} - -void DatabaseWindow::on_actionExport_data_triggered() -{ - QueryTab *tab = GetActiveQueryTab(); - if (tab) { - QString home_dir = QStandardPaths::locate(QStandardPaths::HomeLocation, "", QStandardPaths::LocateDirectory); - QString file_name = QFileDialog::getSaveFileName(this, - tr("Export data"), home_dir, tr("CSV file (*.csv)")); - - tab->exportData(file_name); - } -} - -void DatabaseWindow::on_actionClose_triggered() -{ - //close(); - on_tabWidget_tabCloseRequested(ui->tabWidget->currentIndex()); -} void DatabaseWindow::on_actionAbout_triggered() { QMessageBox::about(this, "pgLab 0.1", tr( - "Copyrights 2016-2017, Eelke Klein, All Rights Reserved.\n" + "Copyrights 2016-2019, Eelke Klein, All Rights Reserved.\n" "\n" "The program is provided AS IS with NO WARRANTY OF ANY KIND, " "INCLUDING THE WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS " "FOR A PARTICULAR PURPOSE.\n" "\n" - "This program is dynamically linked with Qt 5.9 Copyright (C) 2017 " + "This program is dynamically linked with Qt 5.12 Copyright (C) 2018 " "The Qt Company Ltd. https://www.qt.io/licensing/. \n" "\n" "Icons by fatcow http://www.fatcow.com/free-icons provided under Creative Commons " @@ -210,76 +111,6 @@ void DatabaseWindow::on_actionAbout_triggered() } -void DatabaseWindow::closeEvent(QCloseEvent* /*event*/) -{ - // TODO collect which files need saving -// std::vector files_to_save; -// int n = ui->tabWidget->count(); -// for (int i = 0; i < n; ++i) { -// QWidget *w = ui->tabWidget->widget(i); -// QueryTab *qt = dynamic_cast(w); -// if (qt) { -// if (qt->isChanged()) { -// files_to_save.push_back(qt->fileName()); -// } -// } -// } - -// QString s; -// for (const auto& e : files_to_save) { -// s += e + "\n"; -// } - -// QMessageBox msgBox; -// msgBox.setIcon(QMessageBox::Warning); -// msgBox.setText("The following documents need to be saved"); -// msgBox.setInformativeText(s); -// msgBox.setStandardButtons(QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel); -// msgBox.setDefaultButton(QMessageBox::Cancel); -// //int ret = -// msgBox.exec(); - - -} - -void DatabaseWindow::showEvent(QShowEvent *event) -{ - if (!event->spontaneous()) { -// m_queryTextChanged = false; - } - event->accept(); -} - -void DatabaseWindow::on_actionNew_SQL_triggered() -{ - newSqlPage(); -} - - -void DatabaseWindow::on_tabWidget_tabCloseRequested(int index) -{ - QWidget *widget = ui->tabWidget->widget(index); - PlgPage *plg_page = dynamic_cast(widget); - if (plg_page) { - if (plg_page->canClose()) { - removePage(plg_page); - ui->tabWidget->removeTab(index); - } - } - else { - // old behaviour shouldn't be needed any more when all pages have been migrated - // to PlgPage - QueryTab *qt = dynamic_cast(widget); - if (qt && qt->canClose()) { - ui->tabWidget->removeTab(index); - } - else if (index > 0) { - ui->tabWidget->removeTab(index); - } - } -} - - void DatabaseWindow::on_actionShow_connection_manager_triggered() { m_masterController->showConnectionManager(); @@ -305,82 +136,3 @@ void DatabaseWindow::on_actionCopy_triggered() } -void DatabaseWindow::on_actionCopy_as_C_string_triggered() -{ - // Find which edit is active, copy the selected text or all text if no selection present - // Put quote's around each line and add escapes. - - QueryTab *tab = GetActiveQueryTab(); - if (tab) { - tab->copyQueryAsCString(); - } -} - - -void DatabaseWindow::on_actionCopy_as_raw_Cpp_string_triggered() -{ - QueryTab *tab = GetActiveQueryTab(); - if (tab) { - tab->copyQueryAsRawCppString(); - } -} - -void DatabaseWindow::addToolBarButtonsForPage(PlgPage *page) -{ - std::vector actions = page->getToolbarActions(); - QList list; - for (auto act : actions) { - list.append(act); - } - ui->mainToolBar->addActions(list); -} - -void DatabaseWindow::removeToolBarButtonsForPage(PlgPage *page) -{ - std::vector actions = page->getToolbarActions(); - for (auto act : actions) { - ui->mainToolBar->removeAction(act); - } -} - - -void DatabaseWindow::addPage(PlgPage* page, QString caption) -{ - ui->tabWidget->addTab(page, caption); - ui->tabWidget->setCurrentWidget(page); - - //addToolBarButtonsForPage(page); -} - -void DatabaseWindow::removePage(PlgPage *) -{ - -} - -void DatabaseWindow::on_tabWidget_currentChanged(int index) -{ - // remove buttons of old page - if (m_previousPage) { - removeToolBarButtonsForPage(m_previousPage); - } - - // add buttons of new page - PlgPage * page = nullptr; - if (index >= 0) { - QWidget *widget = ui->tabWidget->widget(index); - page = dynamic_cast(widget); - if (page) { - addToolBarButtonsForPage(page); - } - } - m_previousPage = page; -} - -void DatabaseWindow::on_actionGenerate_code_triggered() -{ - QueryTab *tab = GetActiveQueryTab(); - if (tab) { - tab->generateCode(); - } - -} diff --git a/pglab/DatabaseWindow.h b/pglab/DatabaseWindow.h index 610e53f..ee748bb 100644 --- a/pglab/DatabaseWindow.h +++ b/pglab/DatabaseWindow.h @@ -1,128 +1,75 @@ #ifndef MAINWINDOW_H #define MAINWINDOW_H -#include "ASyncDBConnection.h" +#include "plugin_support/LMainWindow.h" #include "ConnectionConfig.h" #include "OpenDatabase.h" -#include -#include "ASyncWindow.h" -#include -#include -#include -#include -#include #include "Pgsql_Connection.h" -#include "QueuedBackgroundTask.h" -#include -#include -#include - -#include -#include - -namespace Ui { - class MainWindow; -} +#include "ControllableTask.h" +#include +#include namespace Pgsql { class Connection; } -class QueryTab; class MasterController; class QCloseEvent; class OpenDatabase; class PgClass; -class PlgPage; + +class QTabWidget; + /** This is the class for windows that handle tasks for a specific database/catalog * */ -class DatabaseWindow : public ASyncWindow { +class DatabaseWindow : public LMainWindow { Q_OBJECT public: - explicit DatabaseWindow(MasterController *master, QWidget *parent); + DatabaseWindow(MasterController *master, QWidget *parent); ~DatabaseWindow(); void setConfig(const ConnectionConfig &config); std::shared_ptr getDatabase() { return m_database; } - void newCrudPage(const PgClass &table); void newCodeGenPage(QString query, std::shared_ptr dbres); private: - Ui::MainWindow *ui; - ConnectionConfig m_config; std::shared_ptr m_database; MasterController *m_masterController; - PlgPage *m_previousPage = nullptr; ///< tracks which pages buttons were previously being displayed - class QLoad : public QueuedBackgroundTask { + class LoadCatalog: public ControllableTask { public: - explicit QLoad(ConnectionConfig config, CompletionFunction on_completion) - : QueuedBackgroundTask(on_completion) - , m_config(config) + LoadCatalog(ConnectionConfig config) + : m_config(config) {} - OpenDatabase::OpenDatabaseSPtr GetResult() - { - if (hasException()) rethrow(); - return result; - } - protected: - - OpenDatabase::OpenDatabaseSPtr result; - virtual void doRun() + OpenDatabase::OpenDatabaseSPtr run(TaskControl& ) override { auto res = OpenDatabase::createOpenDatabase(m_config); - result = res.get(); + return res.get(); } - private: + private: ConnectionConfig m_config; }; - std::shared_ptr loader; + QFutureWatcher loadWatcher; - - QueryTab *GetActiveQueryTab(); - - void closeEvent(QCloseEvent *event); - void showEvent(QShowEvent *event); - QueryTab *newSqlPage(); void newCreateTablePage(); +private slots: + void catalogLoaded(); - /// Called when a newly created page is added to the QTabWidget - void addPage(PlgPage* page, QString caption); - /// Called when a page is completely removed from the QTabWidget - void removePage(PlgPage *page); - - void addToolBarButtonsForPage(PlgPage *page); - void removeToolBarButtonsForPage(PlgPage *page); - //class PageData -private slots: - - void on_actionLoad_SQL_triggered(); - void on_actionSave_SQL_triggered(); - void on_actionExport_data_triggered(); - void on_actionClose_triggered(); void on_actionAbout_triggered(); - void on_actionSave_SQL_as_triggered(); - void on_actionSave_copy_of_SQL_as_triggered(); - void on_actionNew_SQL_triggered(); - void on_tabWidget_tabCloseRequested(int index); void on_actionShow_connection_manager_triggered(); void on_actionCopy_triggered(); - void on_actionCopy_as_C_string_triggered(); - void on_actionCopy_as_raw_Cpp_string_triggered(); - void on_tabWidget_currentChanged(int index); - void on_actionGenerate_code_triggered(); }; #endif // MAINWINDOW_H diff --git a/pglab/MainWindow.ui b/pglab/MainWindow.ui deleted file mode 100644 index 98fe391..0000000 --- a/pglab/MainWindow.ui +++ /dev/null @@ -1,248 +0,0 @@ - - - MainWindow - - - - 0 - 0 - 993 - 804 - - - - pglab - database - - - - - 7 - - - 4 - - - 4 - - - 4 - - - 4 - - - - - -1 - - - - - - - - - 0 - 0 - 993 - 25 - - - - - Fi&le - - - - - - - - - - - - - Help - - - - - - &Query - - - - - - Wi&ndow - - - - - - Edit - - - - - - - - - - - - - - - TopToolBarArea - - - false - - - - - - - - - - - - - - - - - - :/icons/folder.png:/icons/folder.png - - - &Load SQL - - - Ctrl+O - - - - - - :/icons/script_save.png:/icons/script_save.png - - - &Save SQL - - - Ctrl+S - - - - - - :/icons/table_save.png:/icons/table_save.png - - - &Export data - - - - - - :/icons/page_white_delete.png - - - - &Close - - - Ctrl+F4 - - - - - - :/icons/about.png - :/icons/information.png:/icons/about.png - - - &About - - - - - Sa&ve SQL as - - - - - Save copy &of SQL as - - - - - - :/icons/new_query_tab.png - :/icons/page_white_add.png:/icons/new_query_tab.png - - - &New SQL - - - Ctrl+N - - - - - &Show connection manager - - - - - - :/icons/page_white_copy.png - - - - &Copy - - - Ctrl+C - - - - - - :/icons/token_shortland_character.png - - - - Copy as C-&string - - - Ctrl+Alt+C - - - - - - :/icons/token_shortland_character.png - - - - Copy as raw C++-string - - - - - Generate code - - - - - - - - - diff --git a/pglab/PlgPage.cpp b/pglab/PlgPage.cpp deleted file mode 100644 index 9356af5..0000000 --- a/pglab/PlgPage.cpp +++ /dev/null @@ -1,16 +0,0 @@ -#include "PlgPage.h" - -//PglPage::PglPage() -//{ - -//} - -std::vector PlgPage::getToolbarActions() -{ - return std::vector(); -} - -bool PlgPage::canClose() -{ - return true; -} diff --git a/pglab/QueryTab.cpp b/pglab/QueryTool.cpp similarity index 58% rename from pglab/QueryTab.cpp rename to pglab/QueryTool.cpp index f7dad5e..3e0ef23 100644 --- a/pglab/QueryTab.cpp +++ b/pglab/QueryTool.cpp @@ -1,12 +1,10 @@ - -#include "QueryTab.h" +#include "QueryTool.h" #include "ui_QueryTab.h" #include "SqlSyntaxHighlighter.h" #include #include #include #include -#include #include #include #include @@ -15,59 +13,55 @@ #include #include "ExplainTreeModelItem.h" #include "json/json.h" -#include "DatabaseWindow.h" #include "OpenDatabase.h" #include "catalog/PgDatabaseCatalog.h" #include "QueryParamListController.h" #include "util.h" #include "GlobalIoService.h" #include "UserConfiguration.h" +#include "plugin_support/IPluginContentWidgetContext.h" -QueryTab::QueryTab(DatabaseWindow *win, QWidget *parent) : - PlgPage(parent), - ui(new Ui::QueryTab), - m_win(win), - m_dbConnection(*getGlobalAsioIoService()) + +QueryTool::QueryTool(IPluginContentWidgetContext *context_, QWidget *parent) + : PluginContentWidget(context_, parent) + , ui(new Ui::QueryTab) + , m_dbConnection(*getGlobalAsioIoService()) { ui->setupUi(this); - connect(&m_dbConnection, &ASyncDBConnection::onStateChanged, this, &QueryTab::connectionStateChanged); - connect(&m_dbConnection, &ASyncDBConnection::onNotice, this, &QueryTab::receiveNotice); + auto db = context()->getObject(); + m_config = db->config(); + m_catalog = db->catalog(); + + connect(&m_dbConnection, &ASyncDBConnection::onStateChanged, this, &QueryTool::connectionStateChanged); + connect(&m_dbConnection, &ASyncDBConnection::onNotice, this, &QueryTool::receiveNotice); ui->queryEdit->setFont(UserConfiguration::instance()->codeFont()); highlighter = new SqlSyntaxHighlighter(ui->queryEdit->document()); - auto open_database = m_win->getDatabase(); + auto open_database = context()->getObject(); if (open_database) { auto cat = open_database->catalog(); highlighter->setTypes(*cat->types()); } - connect(ui->queryEdit, &QPlainTextEdit::textChanged, this, &QueryTab::queryTextChanged); + connect(ui->queryEdit, &QPlainTextEdit::textChanged, this, &QueryTool::queryTextChanged); m_queryParamListController = new QueryParamListController(ui->paramTableView, open_database, this); connect(ui->addButton, &QPushButton::clicked, m_queryParamListController, &QueryParamListController::on_addParam); connect(ui->removeButton, &QPushButton::clicked, m_queryParamListController, &QueryParamListController::on_removeParam); + + startConnect(); } -QueryTab::~QueryTab() +QueryTool::~QueryTool() { - m_dbConnection.closeConnection(); - delete ui; } -void QueryTab::setConfig(const ConnectionConfig &config, - std::shared_ptr cat) -{ - m_config = config; - m_catalog = cat; - m_win->QueueTask([this]() { startConnect(); }); -} - -bool QueryTab::canClose() +bool QueryTool::canClose() { bool can_close; if (m_queryTextChanged) { @@ -79,7 +73,7 @@ bool QueryTab::canClose() return can_close; } -void QueryTab::newdoc() +void QueryTool::newdoc() { ui->queryEdit->clear(); setFileName(tr("new")); @@ -87,7 +81,7 @@ void QueryTab::newdoc() m_new = true; } -bool QueryTab::load(const QString &filename) +bool QueryTool::load(const QString &filename) { bool result = false; QFile file(filename); @@ -113,7 +107,7 @@ bool QueryTab::load(const QString &filename) return result; } -bool QueryTab::save() +bool QueryTool::save() { bool result; if (m_fileName.isEmpty() || m_new) { @@ -125,7 +119,7 @@ bool QueryTab::save() return result; } -bool QueryTab::saveAs() +bool QueryTool::saveAs() { bool result = false; QString filename = promptUserForSaveSqlFilename(); @@ -139,7 +133,7 @@ bool QueryTab::saveAs() return result; } -void QueryTab::saveCopyAs() +void QueryTool::saveCopyAs() { QString filename = promptUserForSaveSqlFilename(); if (!filename.isEmpty()) { @@ -147,7 +141,7 @@ void QueryTab::saveCopyAs() } } -void QueryTab::execute() +void QueryTool::execute() { if (m_dbConnection.state() == ASyncDBConnection::State::Connected) { addLog("Query clicked"); @@ -160,7 +154,16 @@ void QueryTab::execute() auto cb = [this](Expected> res, qint64 elapsedms) { - m_win->QueueTask([this, res, elapsedms]() { query_ready(res, elapsedms); }); + if (res.valid()) { + auto && dbresult = res.get(); + QMetaObject::invokeMethod(this, "query_ready", + Q_ARG(std::shared_ptr, dbresult), + Q_ARG(qint64, elapsedms)); + } + else { + /// \todo handle error + + } }; if (m_queryParamListController->empty()) @@ -170,7 +173,17 @@ void QueryTab::execute() } } -void QueryTab::explain(bool analyze) +void QueryTool::explain() +{ + explain(false); +} + +void QueryTool::analyze() +{ + explain(true); +} + +void QueryTool::explain(bool analyze) { ui->explainTreeView->setModel(nullptr); explainModel.reset(); @@ -203,7 +216,8 @@ void QueryTab::explain(bool analyze) explain = ExplainRoot::createFromJson(root); } } - m_win->QueueTask([this, explain]() { explain_ready(explain); }); + QMetaObject::invokeMethod(this, "explain_ready", + Q_ARG(ExplainRoot::SPtr, explain)); }).detach(); } } @@ -215,21 +229,21 @@ void QueryTab::explain(bool analyze) m_dbConnection.send(cmd, m_queryParamListController->params(), cb); } -void QueryTab::cancel() +void QueryTool::cancel() { m_dbConnection.cancel(); } -void QueryTab::setFileName(const QString &filename) +void QueryTool::setFileName(const QString &filename) { m_fileName = filename; QFileInfo fileInfo(filename); QString fn(fileInfo.fileName()); - setTabCaption(fn, m_fileName); + context()->setCaption(this, fn, m_fileName); } -bool QueryTab::continueWithoutSavingWarning() +bool QueryTool::continueWithoutSavingWarning() { QMessageBox msgBox; msgBox.setIcon(QMessageBox::Warning); @@ -248,7 +262,7 @@ bool QueryTab::continueWithoutSavingWarning() return ret != QMessageBox::Cancel; } -bool QueryTab::saveSqlTo(const QString &filename) +bool QueryTool::saveSqlTo(const QString &filename) { bool result = false; QFileInfo fileinfo(filename); @@ -271,52 +285,48 @@ bool QueryTab::saveSqlTo(const QString &filename) } -QString QueryTab::promptUserForSaveSqlFilename() +QString QueryTool::promptUserForSaveSqlFilename() { QString home_dir = QStandardPaths::locate(QStandardPaths::HomeLocation, "", QStandardPaths::LocateDirectory); return QFileDialog::getSaveFileName(this, tr("Save query"), home_dir, tr("SQL file (*.sql)")); } -void QueryTab::queryTextChanged() +void QueryTool::queryTextChanged() { m_queryTextChanged = true; } -void QueryTab::connectionStateChanged(ASyncDBConnection::State state) +void QueryTool::connectionStateChanged(ASyncDBConnection::State state) { - QTabWidget *tabwidget = getTabWidget(); - if (tabwidget) { - int i = tabwidget->indexOf(this); - QString iconname; - switch (state) { - case ASyncDBConnection::State::NotConnected: - case ASyncDBConnection::State::Connecting: - iconname = ":/icons/16x16/document_red.png"; - break; - case ASyncDBConnection::State::Connected: - iconname = ":/icons/16x16/document_green.png"; - break; - case ASyncDBConnection::State::QuerySend: - case ASyncDBConnection::State::CancelSend: - iconname = ":/icons/16x16/document_yellow.png"; - break; - case ASyncDBConnection::State::Terminating: - break; - } - tabwidget->setTabIcon(i, QIcon(iconname)); + QString iconname; + switch (state) { + case ASyncDBConnection::State::NotConnected: + case ASyncDBConnection::State::Connecting: + iconname = "document_red.png"; + break; + case ASyncDBConnection::State::Connected: + iconname = "document_green.png"; + break; + case ASyncDBConnection::State::QuerySend: + case ASyncDBConnection::State::CancelSend: + iconname = "document_yellow.png"; + break; + case ASyncDBConnection::State::Terminating: + break; } + context()->setIcon(this, iconname); } -void QueryTab::addLog(QString s) +void QueryTool::addLog(QString s) { QTextCursor text_cursor = QTextCursor(ui->edtLog->document()); text_cursor.movePosition(QTextCursor::End); text_cursor.insertText(s + "\r\n"); } -void QueryTab::receiveNotice(Pgsql::ErrorDetails notice) +void QueryTool::receiveNotice(Pgsql::ErrorDetails notice) { ui->messagesEdit->append(QString::fromStdString(notice.errorMessage)); @@ -363,12 +373,12 @@ void QueryTab::receiveNotice(Pgsql::ErrorDetails notice) // statementPosition } -void QueryTab::startConnect() +void QueryTool::startConnect() { m_dbConnection.setupConnection(m_config); } -void QueryTab::explain_ready(ExplainRoot::SPtr explain) +void QueryTool::explain_ready(ExplainRoot::SPtr explain) { m_stopwatch.stop(); if (explain) { @@ -394,9 +404,7 @@ void QueryTab::explain_ready(ExplainRoot::SPtr explain) ui->explainTreeView->setColumnWidth(6, 600); ui->tabWidget->setCurrentWidget(ui->explainTab); - auto w = dynamic_cast(this->window()); - if (w) - w->statusBar()->showMessage(tr("Explain ready.")); + context()->showStatusMessage(tr("Explain ready.")); } else { addLog("Explain no result"); @@ -405,7 +413,7 @@ void QueryTab::explain_ready(ExplainRoot::SPtr explain) } } -QString QueryTab::getCommand() const +QString QueryTool::getCommand() const { QString command; QTextCursor cursor = ui->queryEdit->textCursor(); @@ -418,115 +426,109 @@ QString QueryTab::getCommand() const return command; } -std::string QueryTab::getCommandUtf8() const +std::string QueryTool::getCommandUtf8() const { return getCommand().toUtf8().data(); } -QTabWidget *QueryTab::getTabWidget() -{ - QWidget * w = parentWidget(); - QWidget * p = w->parentWidget(); - QTabWidget *tw = dynamic_cast(p); - return tw; -} +//QTabWidget *QueryTab::getTabWidget() +//{ +// QWidget * w = parentWidget(); +// QWidget * p = w->parentWidget(); +// QTabWidget *tw = dynamic_cast(p); +// return tw; +//} -void QueryTab::setTabCaption(const QString &caption, const QString &tooltip) +//void QueryTab::setTabCaption(const QString &caption, const QString &tooltip) +//{ +// QTabWidget *tabwidget = getTabWidget(); +// if (tabwidget) { +// int i = tabwidget->indexOf(this); +// if (i >= 0) { +// tabwidget->setTabText(i, caption); +// tabwidget->setTabToolTip(i, tooltip); +// } +// } + +//} + +void QueryTool::query_ready(std::shared_ptr dbres, qint64 elapsedms) { - QTabWidget *tabwidget = getTabWidget(); - if (tabwidget) { - int i = tabwidget->indexOf(this); - if (i >= 0) { - tabwidget->setTabText(i, caption); - tabwidget->setTabToolTip(i, tooltip); + if (dbres) { + addLog("query_ready with result"); + auto st = dbres->resultStatus(); + if (st == PGRES_TUPLES_OK) { + //int n_rows = dbres->getRows(); + //QString rowcount_str = QString("rows: %1").arg(dbres->getRows()); + + auto result_model = std::make_shared(nullptr , dbres, + m_catalog); + TuplesResultWidget *trw = new TuplesResultWidget; + trw->setResult(result_model, elapsedms); + resultList.push_back(trw); + ui->tabWidget->addTab(trw, "Data"); + if (resultList.size() == 1) + ui->tabWidget->setCurrentWidget(trw); + + } + else { + if (st == PGRES_COMMAND_OK) { + int tuples_affected = dbres->tuplesAffected(); + QString msg; + if (tuples_affected >= 0) + msg = tr("Query returned succesfully: %1 rows affected, execution time %2") + .arg(QString::number(tuples_affected)) + .arg(msfloatToHumanReadableString(elapsedms)); + else + msg = tr("Query returned succesfully, execution time %1") + .arg(msfloatToHumanReadableString(elapsedms)); + + ui->messagesEdit->append(msg); + + ui->tabWidget->setCurrentWidget(ui->messageTab); + } + else { +// if (st == PGRES_EMPTY_QUERY) { +// statusBar()->showMessage(tr("Empty query.")); +// } +// else if (st == PGRES_COPY_OUT) { +// statusBar()->showMessage(tr("COPY OUT.")); +// } +// else if (st == PGRES_COPY_IN) { +// statusBar()->showMessage(tr("COPY IN.")); +// } +// else if (st == PGRES_BAD_RESPONSE) { +// statusBar()->showMessage(tr("BAD RESPONSE.")); +// } +// else if (st == PGRES_NONFATAL_ERROR) { +// statusBar()->showMessage(tr("NON FATAL ERROR.")); +// } +// else if (st == PGRES_FATAL_ERROR) { +// statusBar()->showMessage(tr("FATAL ERROR.")); +// } +// else if (st == PGRES_COPY_BOTH) { +// statusBar()->showMessage(tr("COPY BOTH shouldn't happen is for replication.")); +// } +// else if (st == PGRES_SINGLE_TUPLE) { +// statusBar()->showMessage(tr("SINGLE TUPLE result.")); +// } +// else { +// statusBar()->showMessage(tr("No tuples returned, possibly an error...")); +// } + ui->tabWidget->setCurrentWidget(ui->messageTab); + auto details = dbres->diagDetails(); + markError(details); + receiveNotice(details); + } } } - + else { + m_stopwatch.stop(); + addLog("query_ready with NO result"); + } } -void QueryTab::query_ready(Expected> exp_res, qint64 elapsedms) -{ - if (exp_res.valid()) { - auto dbres = exp_res.get(); - if (dbres) { - addLog("query_ready with result"); - auto st = dbres->resultStatus(); - if (st == PGRES_TUPLES_OK) { - //int n_rows = dbres->getRows(); - //QString rowcount_str = QString("rows: %1").arg(dbres->getRows()); - - auto result_model = std::make_shared(nullptr , dbres, - m_catalog); - TuplesResultWidget *trw = new TuplesResultWidget; - trw->setResult(result_model, elapsedms); - resultList.push_back(trw); - ui->tabWidget->addTab(trw, "Data"); - if (resultList.size() == 1) - ui->tabWidget->setCurrentWidget(trw); - - } - else { - if (st == PGRES_COMMAND_OK) { - int tuples_affected = dbres->tuplesAffected(); - QString msg; - if (tuples_affected >= 0) - msg = tr("Query returned succesfully: %1 rows affected, execution time %2") - .arg(QString::number(tuples_affected)) - .arg(msfloatToHumanReadableString(elapsedms)); - else - msg = tr("Query returned succesfully, execution time %1") - .arg(msfloatToHumanReadableString(elapsedms)); - - ui->messagesEdit->append(msg); - - ui->tabWidget->setCurrentWidget(ui->messageTab); - } - else { - // if (st == PGRES_EMPTY_QUERY) { - // statusBar()->showMessage(tr("Empty query.")); - // } - // else if (st == PGRES_COPY_OUT) { - // statusBar()->showMessage(tr("COPY OUT.")); - // } - // else if (st == PGRES_COPY_IN) { - // statusBar()->showMessage(tr("COPY IN.")); - // } - // else if (st == PGRES_BAD_RESPONSE) { - // statusBar()->showMessage(tr("BAD RESPONSE.")); - // } - // else if (st == PGRES_NONFATAL_ERROR) { - // statusBar()->showMessage(tr("NON FATAL ERROR.")); - // } - // else if (st == PGRES_FATAL_ERROR) { - // statusBar()->showMessage(tr("FATAL ERROR.")); - // } - // else if (st == PGRES_COPY_BOTH) { - // statusBar()->showMessage(tr("COPY BOTH shouldn't happen is for replication.")); - // } - // else if (st == PGRES_SINGLE_TUPLE) { - // statusBar()->showMessage(tr("SINGLE TUPLE result.")); - // } - // else { - // statusBar()->showMessage(tr("No tuples returned, possibly an error...")); - // } - ui->tabWidget->setCurrentWidget(ui->messageTab); - auto details = dbres->diagDetails(); - markError(details); - receiveNotice(details); - } - } - } - else { - m_stopwatch.stop(); - addLog("query_ready with NO result"); - } - } - else { - // we have an error - } -} - -void QueryTab::markError(const Pgsql::ErrorDetails &details) +void QueryTool::markError(const Pgsql::ErrorDetails &details) { if (details.statementPosition > 0) { QTextCursor cursor = ui->queryEdit->textCursor(); @@ -567,14 +569,14 @@ void QueryTab::markError(const Pgsql::ErrorDetails &details) } } -void QueryTab::clearResult() +void QueryTool::clearResult() { for (auto e : resultList) delete e; resultList.clear(); } -void QueryTab::copyQueryAsCString() +void QueryTool::copyQueryAsCString() { // QString command; // QTextCursor cursor = ui->queryEdit->textCursor(); @@ -592,7 +594,7 @@ void QueryTab::copyQueryAsCString() #include #include -void QueryTab::copyQueryAsRawCppString() +void QueryTool::copyQueryAsRawCppString() { QString command = getCommand(); //auto sql = getAllOrSelectedSql(); @@ -600,7 +602,7 @@ void QueryTab::copyQueryAsRawCppString() QApplication::clipboard()->setText(cs); } -void QueryTab::generateCode() +void QueryTool::generateCode() { QString command = getCommand(); @@ -609,11 +611,11 @@ void QueryTab::generateCode() } if (resultList.size() == 1) { std::shared_ptr dbres = resultList[0]->GetPgsqlResult(); - m_win->newCodeGenPage(command, dbres); + //context()->newCodeGenPage(command, dbres); } } -void QueryTab::exportData(const QString &file_name) +void QueryTool::exportData(const QString &file_name) { auto widget = ui->tabWidget->currentWidget(); auto fi = std::find(resultList.begin(), resultList.end(), widget); @@ -623,57 +625,15 @@ void QueryTab::exportData(const QString &file_name) } } -void QueryTab::focusEditor() +void QueryTool::focusEditor() { ui->queryEdit->setFocus(); } - -std::vector QueryTab::getToolbarActions() +void QueryTool::exportData() { - if (actions.empty()) { - QAction *action; - // New -// action = new QAction(QIcon(":/icons/new_query_tab.png"), tr("New"), this); -// action->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_N)); -// connect(action, &QAction::triggered, this, &QueryTab::); -// actions.push_back(action); - // Load - - // Save - action = new QAction(QIcon(":/icons/script_save.png"), tr("Save SQL"), this); - action->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_S)); - connect(action, &QAction::triggered, this, &QueryTab::save); - actions.push_back(action); - // Save as (menu only) -// action = new QAction(QIcon(":/icons/script_save.png"), tr("Save SQL as"), this); -// //action->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_S)); -// connect(action, &QAction::triggered, this, &QueryTab::saveAs); -// actions.push_back(action); - // Save copy as - // Copy - // Copy as C-string - // Copy as raw cpp string - // Execute SQL - action = new QAction(QIcon(":/icons/script_go.png"), tr("Execute"), this); - action->setShortcut(QKeySequence(Qt::Key_F5)); - connect(action, &QAction::triggered, this, &QueryTab::execute); - actions.push_back(action); - // Explain - action = new QAction(QIcon(":/icons/lightbulb_off.png"), tr("Explain"), this); - action->setShortcut(QKeySequence(Qt::Key_F7)); - connect(action, &QAction::triggered, this, [this] () { explain(false); }); - actions.push_back(action); - // Explain Anaylze - action = new QAction(QIcon(":/icons/lightbulb.png"), tr("Analyze"), this); - action->setShortcut(QKeySequence(Qt::SHIFT + Qt::Key_F7)); - connect(action, &QAction::triggered, this, [this] () { explain(true); }); - actions.push_back(action); - // Cancel - action = new QAction(QIcon(":/icons/script_delete.png"), tr("Cancel"), this); - action->setShortcut(QKeySequence(Qt::ALT + Qt::Key_Pause)); - connect(action, &QAction::triggered, this, &QueryTab::cancel); - actions.push_back(action); - } - return actions; + QString home_dir = QStandardPaths::locate(QStandardPaths::HomeLocation, "", QStandardPaths::LocateDirectory); + QString file_name = QFileDialog::getSaveFileName(this, + tr("Export data"), home_dir, tr("CSV file (*.csv)")); + exportData(file_name); } diff --git a/pglab/QueryTab.h b/pglab/QueryTool.h similarity index 72% rename from pglab/QueryTab.h rename to pglab/QueryTool.h index 136ad59..a711eb2 100644 --- a/pglab/QueryTab.h +++ b/pglab/QueryTool.h @@ -8,7 +8,7 @@ #include "tuplesresultwidget.h" #include -#include "PlgPage.h" +#include "plugin_support/PluginContentWidget.h" #include namespace Ui { @@ -22,7 +22,6 @@ namespace Pgsql { class QTableView; class QTabWidget; -class DatabaseWindow; class SqlSyntaxHighlighter; class ExplainRoot; class QueryResultModel; @@ -32,29 +31,19 @@ class OpenDatabase; class QueryParamListController; class PgDatabaseCatalog; -class QueryTab : public PlgPage { +class QueryTool : public PluginContentWidget { Q_OBJECT public: - QueryTab(DatabaseWindow *win, QWidget *parent = nullptr); - ~QueryTab(); - - void setConfig(const ConnectionConfig &config, std::shared_ptrcat); + QueryTool(IPluginContentWidgetContext *context, QWidget *parent = nullptr); + ~QueryTool() override; void newdoc(); -// void open(); bool load(const QString &filename); - bool save(); - bool saveAs(); - void saveCopyAs(); - void execute(); void explain(bool analyze); - void cancel(); - bool canClose(); + bool canClose() override; - void copyQueryAsCString(); - void copyQueryAsRawCppString(); void generateCode(); void exportData(const QString &filename); @@ -63,17 +52,28 @@ public: bool isNew() const { return m_new; } void focusEditor(); - virtual std::vector getToolbarActions() override; +public slots: + void execute(); + /// Save the document under its current name, a file save dialog will be shown if this is a new document + bool save(); + /// Saves the document under a new name and continue editing the document under this new name. + bool saveAs(); + /// Save the document under a new name but continue editing the document under its old name. + void saveCopyAs(); + void copyQueryAsCString(); + void copyQueryAsRawCppString(); + void explain(); + void analyze(); + void cancel(); + void exportData(); private: using ResultTabContainer = std::vector; Ui::QueryTab *ui; - DatabaseWindow *m_win; SqlSyntaxHighlighter* highlighter; ConnectionConfig m_config; StopWatch m_stopwatch; - std::vector actions; QueryParamListController *m_queryParamListController = nullptr; @@ -98,16 +98,17 @@ private: QString getCommand() const; std::string getCommandUtf8() const; - void explain_ready(ExplainRoot::SPtr explain); - void query_ready(Expected> dbres, qint64 elapsedms); - QTabWidget *getTabWidget(); - void setTabCaption(const QString &caption, const QString &tooltip); + //QTabWidget *getTabWidget(); + //void setTabCaption(const QString &caption, const QString &tooltip); void clearResult(); void markError(const Pgsql::ErrorDetails &details); private slots: + void explain_ready(ExplainRoot::SPtr explain); + void query_ready(std::shared_ptr, qint64 elapsedms); + void queryTextChanged(); void connectionStateChanged(ASyncDBConnection::State state); void receiveNotice(Pgsql::ErrorDetails notice); @@ -115,4 +116,5 @@ private slots: void startConnect(); }; + #endif // QUERYTAB_H diff --git a/pglab/QueryToolModule.cpp b/pglab/QueryToolModule.cpp new file mode 100644 index 0000000..0cf0c4a --- /dev/null +++ b/pglab/QueryToolModule.cpp @@ -0,0 +1,109 @@ +#include "QueryToolModule.h" +#include "QueryTool.h" +#include "plugin_support/IPluginContentWidgetContext.h" +#include "plugin_support/PluginRegister.h" + +#include +#include + +void QueryToolModule::init() +{ + std::string slot_name = SLOT(QueryTool::execute()); + { + MenuAction ma("New SQL", [this] (IPluginContentWidgetContext* context) + { menuAction_new(context); }); + ma.setMenuLocation(MenuPath("File/New")); + ma.setIcon(QIcon(":/icons/new_query_tab.png")); + ma.setShortcut(QKeySequence(Qt::CTRL + Qt::Key_N)); + registerMenuAction(ma); + } + { + MenuAction ma("Open SQL", [this] (IPluginContentWidgetContext* context) + { menuAction_open(context); }); + ma.setMenuLocation(MenuPath("File/Open")); + ma.setIcon(QIcon(":/icons/folder.png")); + registerMenuAction(ma); + } + { + LWidgetAction wa("Save SQL", SLOT(save())); + wa.setMenuLocation("File/Save"); + wa.setIcon(":/icons/script_save.png"); + wa.setShortcut(QKeySequence(Qt::CTRL + Qt::Key_S)); + registerWidgetAction(wa); + } + { + LWidgetAction wa("Save SQL as", SLOT(saveAs())); + wa.setMenuLocation("File/Save"); + wa.setIcon(":/icons/script_save.png"); + registerWidgetAction(wa); + } + { + LWidgetAction wa("Save copy of SQL as", SLOT(saveCopyAs())); + wa.setMenuLocation("File/Save"); + //wa.setIcon(":/icons/script_save.png"); + registerWidgetAction(wa); + } + { + LWidgetAction wa("&Export data", SLOT(exportData())); + wa.setMenuLocation("File/Export"); + wa.setIcon(":/icons/table_save.png"); + registerWidgetAction(wa); + } + { + LWidgetAction wa("Copy as C string", SLOT(copyQueryAsCString())); + wa.setMenuLocation("Edit/Copy"); + wa.setIcon(":/icons/token_shortland_character.png"); + registerWidgetAction(wa); + } + { + LWidgetAction wa("Copy as raw C++ string", SLOT(copyQueryAsRawCppString())); + wa.setMenuLocation("Edit/Copy"); + wa.setIcon(":/icons/token_shortland_character.png"); + registerWidgetAction(wa); + } + { + LWidgetAction wa("Execute", SLOT(execute())); + wa.setMenuLocation("Query/1"); + wa.setIcon(":/icons/script_go.png"); + registerWidgetAction(wa); + } + { + LWidgetAction wa("Explain", SLOT(explain())); + wa.setMenuLocation("Query/2"); + wa.setIcon(":/icons/lightbulb_off.png"); + registerWidgetAction(wa); + } + { + LWidgetAction wa("Analyze", SLOT(analyze())); + wa.setMenuLocation("Query/1"); + wa.setIcon(":/icons/lightbulb.png"); + registerWidgetAction(wa); + } + { + LWidgetAction wa("Cancel", SLOT(cancel())); + wa.setMenuLocation("Query/1"); + wa.setIcon(":/icons/script_delete.png"); + registerWidgetAction(wa); + } +} + +void QueryToolModule::menuAction_new(IPluginContentWidgetContext* context) +{ + auto *ct = new QueryTool(context, nullptr); + context->addContentWidget(this, ct); + ct->newdoc(); +} + +void QueryToolModule::menuAction_open(IPluginContentWidgetContext* context) +{ + QString home_dir = QStandardPaths::locate(QStandardPaths::HomeLocation, "", QStandardPaths::LocateDirectory); + QString file_name = QFileDialog::getOpenFileName(context->container(), + tr("Open sql query"), home_dir, tr("SQL files (*.sql *.txt)")); + if ( ! file_name.isEmpty()) { + auto *ct = new QueryTool(context, nullptr); + context->addContentWidget(this, ct); + ct->load(file_name); + } +} + +REGISTER_PLUGIN_MODULE_CAT(QueryToolModule, "Query tool", "pglab.querytool", "database") diff --git a/pglab/QueryToolModule.h b/pglab/QueryToolModule.h new file mode 100644 index 0000000..bc77101 --- /dev/null +++ b/pglab/QueryToolModule.h @@ -0,0 +1,18 @@ +#ifndef QUERYTOOLMODULE_H +#define QUERYTOOLMODULE_H + +#include "plugin_support/PluginModule.h" + +class QueryToolModule: public PluginModule { + Q_OBJECT +public: + using PluginModule::PluginModule; + + void init() override; + void menuAction_new(IPluginContentWidgetContext* context); + void menuAction_open(IPluginContentWidgetContext* context); +private slots: +}; + + +#endif // QUERYTOOLMODULE_H diff --git a/pglab/ServerWindow.cpp b/pglab/ServerWindow.cpp index c2c49da..31cb27e 100644 --- a/pglab/ServerWindow.cpp +++ b/pglab/ServerWindow.cpp @@ -6,7 +6,7 @@ #include "catalog/PgDatabaseCatalog.h" ServerWindow::ServerWindow(MasterController *master, QWidget *parent) - : ASyncWindow(parent) + : QMainWindow(parent) , ui(new Ui::ServerWindow) , m_masterController(master) { diff --git a/pglab/ServerWindow.h b/pglab/ServerWindow.h index 9d90e28..d9a9d39 100644 --- a/pglab/ServerWindow.h +++ b/pglab/ServerWindow.h @@ -1,8 +1,8 @@ #ifndef SERVERWINDOW_H #define SERVERWINDOW_H -#include "ASyncWindow.h" #include "ConnectionConfig.h" +#include #include namespace Ui { @@ -14,7 +14,7 @@ class OpenDatabase; class DatabasesTableModel; class RolesTableModel; -class ServerWindow : public ASyncWindow { +class ServerWindow : public QMainWindow { Q_OBJECT public: explicit ServerWindow(MasterController *master, QWidget *parent ); diff --git a/pglab/main.cpp b/pglab/main.cpp index 38482a6..a78a29c 100644 --- a/pglab/main.cpp +++ b/pglab/main.cpp @@ -5,6 +5,7 @@ #endif #include #include "GlobalIoService.h" +#include "plugin_support/PluginRegister.h" int main(int argc, char *argv[]) { @@ -29,6 +30,8 @@ int main(int argc, char *argv[]) QCoreApplication::setOrganizationDomain("eelkeklein.nl"); QCoreApplication::setApplicationName("pglab"); + PluginRegister::getInstance()->initModules(); + std::thread asio_service_thread; int result = -1; { diff --git a/pglab/pglab.pro b/pglab/pglab.pro index 994bbf8..fe0a375 100644 --- a/pglab/pglab.pro +++ b/pglab/pglab.pro @@ -28,12 +28,10 @@ win32:RC_ICONS += pglab.ico SOURCES += main.cpp\ QueryResultModel.cpp \ QueryExplainModel.cpp \ - tsqueue.cpp \ CreateDatabaseDialog.cpp \ ConnectionManagerWindow.cpp \ ConnectionListModel.cpp \ BackupRestore.cpp \ - QueryTab.cpp \ stopwatch.cpp \ TuplesResultWidget.cpp \ BackupDialog.cpp \ @@ -42,7 +40,6 @@ SOURCES += main.cpp\ OpenDatabase.cpp \ SqlSyntaxHighlighter.cpp \ ServerWindow.cpp \ - ASyncWindow.cpp \ DatabasesTableModel.cpp \ RolesTableModel.cpp \ ConnectionList.cpp \ @@ -51,12 +48,10 @@ SOURCES += main.cpp\ ResultTableModelUtil.cpp \ BaseTableModel.cpp \ QueryParamListController.cpp \ - TablesPage.cpp \ TablesTableModel.cpp \ ColumnTableModel.cpp \ NamespaceFilterWidget.cpp \ NamespaceItemModel.cpp \ - ApplicationWindow.cpp \ ConstraintModel.cpp \ IconColumnDelegate.cpp \ IndexModel.cpp \ @@ -66,7 +61,6 @@ SOURCES += main.cpp\ Module.cpp \ EditorGutter.cpp \ CodeEditor.cpp \ - PlgPage.cpp \ PropertyProxyModel.cpp \ CodeGenerator.cpp \ UserConfiguration.cpp \ @@ -84,16 +78,27 @@ PropertyProxyModel.cpp \ SequenceModel.cpp \ SequencesPage.cpp \ DatabaseWindow.cpp \ - PgLabTableView.cpp + PgLabTableView.cpp \ + plugin_support/PluginModule.cpp \ + plugin_support/MenuPath.cpp \ + plugin_support/MenuLocation.cpp \ + plugin_support/ToolbarLocation.cpp \ + plugin_support/PluginRegister.cpp \ + plugin_support/PluginContentWidget.cpp \ + plugin_support/PluginContentWidgetContextBase.cpp \ + plugin_support/MenuAction.cpp \ + plugin_support/LMainWindow.cpp \ + plugin_support/LWidgetAction.cpp \ + QueryTool.cpp \ + QueryToolModule.cpp \ + CatalogInspector.cpp HEADERS += \ QueryResultModel.h \ QueryExplainModel.h \ - tsqueue.h \ CreateDatabaseDialog.h \ ConnectionManagerWindow.h \ ConnectionListModel.h \ - QueryTab.h \ stopwatch.h \ TuplesResultWidget.h \ BackupDialog.h \ @@ -102,7 +107,6 @@ HEADERS += \ OpenDatabase.h \ SqlSyntaxHighlighter.h \ ServerWindow.h \ - ASyncWindow.h \ DatabasesTableModel.h \ RolesTableModel.h \ ConnectionList.h \ @@ -111,12 +115,10 @@ HEADERS += \ ResultTableModelUtil.h \ BaseTableModel.h \ QueryParamListController.h \ - TablesPage.h \ TablesTableModel.h \ ColumnTableModel.h \ NamespaceFilterWidget.h \ NamespaceItemModel.h \ - ApplicationWindow.h \ ConstraintModel.h \ IconColumnDelegate.h \ IndexModel.h \ @@ -126,7 +128,6 @@ HEADERS += \ Module.h \ EditorGutter.h \ CodeEditor.h \ - PlgPage.h \ AbstractCommand.h \ PropertyProxyModel.h \ CustomDataRole.h \ @@ -146,9 +147,24 @@ CustomDataRole.h \ SequenceModel.h \ SequencesPage.h \ DatabaseWindow.h \ - PgLabTableView.h + PgLabTableView.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/ModuleActionParameters.h \ + plugin_support/IPluginContentWidgetContext.h \ + plugin_support/PluginContentWidgetContextBase.h \ + plugin_support/MenuAction.h \ + plugin_support/LMainWindow.h \ + plugin_support/LWidgetAction.h \ + QueryTool.h \ + QueryToolModule.h \ + CatalogInspector.h -FORMS += mainwindow.ui \ +FORMS += \ ConnectionManagerWindow.ui \ CreateDatabaseDialog.ui \ TuplesResultWidget.ui \ @@ -158,7 +174,6 @@ FORMS += mainwindow.ui \ ProcessStdioWidget.ui \ TablesPage.ui \ NamespaceFilterWidget.ui \ - ApplicationWindow.ui \ CrudTab.ui \ CodeGenerator.ui diff --git a/pglab/plugin_support/IPluginContentWidgetContext.h b/pglab/plugin_support/IPluginContentWidgetContext.h new file mode 100644 index 0000000..c976ce9 --- /dev/null +++ b/pglab/plugin_support/IPluginContentWidgetContext.h @@ -0,0 +1,98 @@ +#ifndef IPLUGINCONTENTWIDGETCONTEXT_H +#define IPLUGINCONTENTWIDGETCONTEXT_H + +#include +#include +#include +#include +#include "plugin_support/ModuleActionParameters.h" + +class OpenDatabase; +class PluginModule; +class PluginContentWidget; + + +/** This class serves to isolate the plugin from the actual construct in which it is + * used. + * + * It provides interface for operating on the context without needing to many details. + * Actual default implementation is in PluginContentWidgetContextBase. + * + * objectRegistry implementation is from Igor Tandetnik answer to the following question + * https://stackoverflow.com/questions/35413745/using-shared-ptr-with-a-generic-registry-or-shared-object-storage-or-not + */ +class IPluginContentWidgetContext { +public: + + virtual ~IPluginContentWidgetContext() = default; + /** Tells the context what to use as a caption for this content widget. + * + * Depending on the context the caption might not be visible or used as the caption + * of a window or tab. + */ + virtual void setCaption(PluginContentWidget *content, const QString &caption, const QString &hint = {}) = 0; + /** Tells the context what icon to use. + * + * In general the icon is used in a similar place as the caption. + * \param iconname Assumed to be the name of an iconresource. The system will look for different + * sizes under :/icons//iconname + */ + virtual void setIcon(PluginContentWidget *content, const QString &iconname) = 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(PluginModule *module, PluginContentWidget *widget) = 0; + virtual void removeContentWidget(PluginContentWidget *widget) = 0; + + /** Return a widget you can use as a parent + */ + virtual QWidget* container() = 0; + + template + bool addObjects(Args&&...args); + template + bool registerObject(std::shared_ptr object); + template + std::shared_ptr getObject() const; + +private: + std::map > m_objectRegistry; + +}; + +template +bool IPluginContentWidgetContext::addObjects(Args&&...args) +{ + std::type_index key(typeid(T)); + if (!m_objectRegistry.count(key)) { + auto p = std::make_shared(std::forward(args)...); + m_objectRegistry[key] = p; + return true; + } + return false; +} + +template +bool IPluginContentWidgetContext::registerObject(std::shared_ptr object) +{ + return m_objectRegistry.emplace(std::type_index(typeid(T)), object).second; +} + +template +std::shared_ptr IPluginContentWidgetContext::getObject() const +{ + auto it = m_objectRegistry.find(typeid(T)); + if (it == m_objectRegistry.end()) { + return {}; + } + return std::static_pointer_cast(it->second); +} + + +#endif // IPLUGINCONTENTWIDGETCONTEXT_H diff --git a/pglab/plugin_support/LMainWindow.cpp b/pglab/plugin_support/LMainWindow.cpp new file mode 100644 index 0000000..f782878 --- /dev/null +++ b/pglab/plugin_support/LMainWindow.cpp @@ -0,0 +1,214 @@ +#include "LMainWindow.h" +#include "PluginContentWidget.h" +#include "PluginContentWidgetContextBase.h" +#include "PluginModule.h" +#include "PluginRegister.h" + +#include +#include +#include +#include + +namespace LMainWindow_details { + + class LMainWindowContentContext: public PluginContentWidgetContextBase { + public: + explicit LMainWindowContentContext(LMainWindow *window) + : m_window(window) + {} + + void setCaption(PluginContentWidget *content, const QString &caption, const QString &hint = {}) override + { + m_window->setTabCaptionForWidget(content, caption, hint); + } + + void setIcon(PluginContentWidget *content, const QString &iconname) override + { + m_window->setTabIcon(content, iconname); + } + + void showStatusMessage(const QString &msg) override + { + m_window->statusBar()->showMessage(msg); + } + + void addContentWidget(PluginModule *module, PluginContentWidget *widget) override + { + PluginContentWidgetContextBase::addContentWidget(module, widget); + m_window->addPage(widget, ""); + } + + QWidget* container() override + { + return m_window; + } + private: + LMainWindow *m_window; + }; + +} +using namespace LMainWindow_details; + + +LMainWindow::LMainWindow(QWidget *parent) + : QMainWindow(parent) +{ + m_context = new LMainWindowContentContext(this); + + m_tabWidget = new QTabWidget(this); + m_tabWidget->setObjectName(QString::fromUtf8("tabWidget")); + m_tabWidget->setDocumentMode(true); + + setCentralWidget(m_tabWidget); + + // menu + auto menuBar = new QMenuBar(this); + setMenuBar(menuBar); + + // tooolbar + m_mainToolBar = new QToolBar(this); + addToolBar(Qt::TopToolBarArea, m_mainToolBar); + + // statusbar + auto statusBar = new QStatusBar(this); + setStatusBar(statusBar); + + m_fileMenu = new QMenu("File", this); + + createActions(); + m_mainToolBar->addAction(m_closeAction); + + connect(m_tabWidget, &QTabWidget::tabCloseRequested, this, &LMainWindow::tabWidget_tabCloseRequested); + connect(m_tabWidget, &QTabWidget::currentChanged, this, &LMainWindow::tabWidget_currentChanged); +} + +LMainWindow::~LMainWindow() +{ + delete m_context; +} + +void LMainWindow::initModuleMenus() +{ + menuBar()->addMenu(m_fileMenu); + addModuleMenuActions(); +} + +void LMainWindow::addModuleMenuActions() +{ + auto reg = PluginRegister::getInstance(); + auto mods = reg->modules(); + for (auto && mod : mods) { + auto items = mod.second->menuActions(); + for (auto && item : items) { + addMenuAction(item); + } + } +} + +void LMainWindow::createActions() +{ + { + auto action = m_closeAction = new QAction(this); + QIcon icon; + icon.addFile(QString::fromUtf8(":/icons/page_white_delete.png"), QSize(), QIcon::Normal, QIcon::On); + action->setIcon(icon); + connect(m_closeAction, &QAction::triggered, this, &LMainWindow::actionClose_triggered); + } + +} + + +void LMainWindow::addMenuAction(const MenuAction &ma) +{ + qDebug() << "add action " << ma.text(); + //auto ac = + m_fileMenu->addAction(ma.icon(), ma.text(), + [ma, this] () + { + ma.perform(m_context); + }, + ma.shortcut()); + + +// auto ac = new QAction(this); +// ac-> +} + +void LMainWindow::actionClose_triggered() +{ + m_tabWidget->tabCloseRequested(m_tabWidget->currentIndex()); +} + + +void LMainWindow::addPage(PluginContentWidget* page, QString caption) +{ + m_tabWidget->addTab(page, caption); + m_tabWidget->setCurrentWidget(page); +} + +void LMainWindow::setTabCaptionForWidget(QWidget *widget, const QString &caption, const QString &hint) +{ + auto index = m_tabWidget->indexOf(widget); + m_tabWidget->setTabText(index, caption); + m_tabWidget->setTabToolTip(index, hint); +} + +void LMainWindow::setTabIcon(QWidget *widget, const QString &iconname) +{ + auto index = m_tabWidget->indexOf(widget); + auto n = ":/icons/16x16/" + iconname; + m_tabWidget->setTabIcon(index, QIcon(n)); +} + +IPluginContentWidgetContext* LMainWindow::context() +{ + return m_context; +} + +void LMainWindow::tabWidget_tabCloseRequested(int index) +{ + QWidget *widget = m_tabWidget->widget(index); + PluginContentWidget *plg_page = dynamic_cast(widget); + if (plg_page) { + if (plg_page->canClose()) { + m_tabWidget->removeTab(index); + m_context->removeContentWidget(plg_page); + delete plg_page; + } + } + else { + // old behaviour shouldn't be needed any more when all pages have been migrated + // to PlgPage + m_tabWidget->removeTab(index); + delete widget; + } +} + +void LMainWindow::tabWidget_currentChanged(int index) +{ + // remove buttons of old page + if (m_previousPage) { + removeModuleWidgetActionsForPage(m_previousPage); + } + + // add buttons of new page + PluginContentWidget * page = nullptr; + if (index >= 0) { + QWidget *widget = m_tabWidget->widget(index); + page = dynamic_cast(widget); + if (page) { + addModuleWidgetActionsForPage(page); + } + } + m_previousPage = page; +} + +void LMainWindow::addModuleWidgetActionsForPage(PluginContentWidget *page) +{ + m_context->addWidgetActionsToToolbar(page, m_mainToolBar); +} + +void LMainWindow::removeModuleWidgetActionsForPage(PluginContentWidget *page) +{ + m_context->removeWidgetActionsFromToolbar(page, m_mainToolBar); +} diff --git a/pglab/plugin_support/LMainWindow.h b/pglab/plugin_support/LMainWindow.h new file mode 100644 index 0000000..0e30c71 --- /dev/null +++ b/pglab/plugin_support/LMainWindow.h @@ -0,0 +1,58 @@ +#ifndef LMAINWINDOW_H +#define LMAINWINDOW_H + +#include + +class IPluginContentWidgetContext; +class MenuAction; +class PluginContentWidget; + +namespace LMainWindow_details { + class LMainWindowContentContext; +} + +class LMainWindow : public QMainWindow { + Q_OBJECT +public: + + LMainWindow(QWidget *parent = nullptr); + ~LMainWindow(); + + void initModuleMenus(); + 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); + + IPluginContentWidgetContext* context(); +protected: + QTabWidget *m_tabWidget = nullptr; + QToolBar *m_mainToolBar = nullptr; + + // Standard menu's + QMenu *m_fileMenu = nullptr; + + // Standard actions + QAction *m_closeAction = nullptr; + + + void addModuleMenuActions(); + + void addModuleWidgetActionsForPage(PluginContentWidget *page); + void removeModuleWidgetActionsForPage(PluginContentWidget *page); +private: + LMainWindow_details::LMainWindowContentContext *m_context; + PluginContentWidget *m_previousPage = nullptr; ///< tracks which pages buttons were previously being displayed + + void createActions(); + void addMenuAction(const MenuAction &ma); + +private slots: + void actionClose_triggered(); + void tabWidget_tabCloseRequested(int index); + void tabWidget_currentChanged(int index); +}; + + +#endif // LMAINWINDOW_H diff --git a/pglab/plugin_support/LWidgetAction.cpp b/pglab/plugin_support/LWidgetAction.cpp new file mode 100644 index 0000000..6196143 --- /dev/null +++ b/pglab/plugin_support/LWidgetAction.cpp @@ -0,0 +1,56 @@ +#include "LWidgetAction.h" + +LWidgetAction::LWidgetAction(QString text, const char * slotname) + : m_text(std::move(text)) + , m_slotname(slotname) +{} + +const QIcon& LWidgetAction::icon() const +{ + return m_icon; +} + +const MenuLocation& LWidgetAction::menuLocation() const +{ + return m_menuLocation; +} + +void LWidgetAction::setIcon(QIcon icon) +{ + m_icon = std::move(icon); +} + +void LWidgetAction::setMenuLocation(MenuLocation menu_location) +{ + m_menuLocation = std::move(menu_location); +} + +void LWidgetAction::setShortcut(QKeySequence shortcut) +{ + m_shortCut = std::move(shortcut); +} + +void LWidgetAction::setText(QString text) +{ + m_text = std::move(text); +} + +void LWidgetAction::setToolTip(QString tooltip) +{ + m_toolTip = std::move(tooltip); +} + +const QKeySequence& LWidgetAction::shortcut() const +{ + return m_shortCut; +} + +const QString& LWidgetAction::text() const +{ + return m_text; +} + +const QString& LWidgetAction::toolTip() const +{ + return m_toolTip; +} diff --git a/pglab/plugin_support/LWidgetAction.h b/pglab/plugin_support/LWidgetAction.h new file mode 100644 index 0000000..3169061 --- /dev/null +++ b/pglab/plugin_support/LWidgetAction.h @@ -0,0 +1,44 @@ +#ifndef LWIDGETACTION_H +#define LWIDGETACTION_H + +#include "MenuLocation.h" +#include +#include +#include + + +/** Action definition for a specific widget instance, it uses a slotname + * so the action can be defined before the widget instance is created. + * The plugin mechanism will take care of instantiating a connection to the named slot. + */ +class LWidgetAction { +public: + /// + /// \param slotname, use SLOT macro to pass name of the slot + LWidgetAction(QString text, const char * slotname); + + const QIcon& icon() const; + const MenuLocation& menuLocation() const; + void setIcon(QIcon icon); + void setIcon(const char * icon) { setIcon(QIcon(icon)); } + void setMenuLocation(MenuLocation menu_location); + void setMenuLocation(const char * menu_location) { setMenuLocation(MenuPath(menu_location)); } + void setShortcut(QKeySequence shortcut); + void setText(QString text); + void setToolTip(QString tooltip); + const QKeySequence& shortcut() const; + const char* slotName() const { return m_slotname; } + const QString& text() const; + const QString& toolTip() const; + +private: + QString m_text; + QString m_toolTip; + QIcon m_icon; + QKeySequence m_shortCut; + MenuLocation m_menuLocation; + + const char * m_slotname; +}; + +#endif // LWIDGETACTION_H diff --git a/pglab/plugin_support/MenuAction.cpp b/pglab/plugin_support/MenuAction.cpp new file mode 100644 index 0000000..205b898 --- /dev/null +++ b/pglab/plugin_support/MenuAction.cpp @@ -0,0 +1,62 @@ +#include "MenuAction.h" + +MenuAction::MenuAction(QString text, Func func) + : m_text(std::move(text)) + , m_func(std::move(func)) +{} + +const QIcon& MenuAction::icon() const +{ + return m_icon; +} + +const MenuLocation& MenuAction::menuLocation() const +{ + return m_menuLocation; +} + +void MenuAction::setIcon(QIcon icon) +{ + m_icon = std::move(icon); +} + +void MenuAction::setMenuLocation(MenuLocation menu_location) +{ + m_menuLocation = std::move(menu_location); +} + +void MenuAction::setShortcut(QKeySequence shortcut) +{ + m_shortcut = std::move(shortcut); +} + +void MenuAction::setText(QString text) +{ + m_text = std::move(text); +} + +void MenuAction::setToolTip(QString tooltip) +{ + m_toolTip = std::move(tooltip); +} + +const QKeySequence& MenuAction::shortcut() const +{ + return m_shortcut; +} + +const QString& MenuAction::text() const +{ + return m_text; +} + +const QString& MenuAction::toolTip() const +{ + return m_toolTip; +} + +void MenuAction::perform(IPluginContentWidgetContext *context) const +{ + if (m_func) + m_func(context); +} diff --git a/pglab/plugin_support/MenuAction.h b/pglab/plugin_support/MenuAction.h new file mode 100644 index 0000000..d987660 --- /dev/null +++ b/pglab/plugin_support/MenuAction.h @@ -0,0 +1,48 @@ +#ifndef MENUACTION_H +#define MENUACTION_H + +#include "MenuLocation.h" +#include "ToolbarLocation.h" + +#include +#include +#include +#include + +class QAction; +class IPluginContentWidgetContext; + +/** An action for in a menu or toolbar that does not pertain to a specific + * widget. It often will create a widget for instance a New or Open action. + * It does need a context. + * + */ +class MenuAction { +public: + using Func = std::function; + + MenuAction(QString text, Func func); + + const QIcon& icon() const; + const MenuLocation& menuLocation() const; + void setIcon(QIcon icon); + void setMenuLocation(MenuLocation menu_location); + void setShortcut(QKeySequence shortcut); + void setText(QString text); + void setToolTip(QString tooltip); + const QKeySequence& shortcut() const; + const QString& text() const; + const QString& toolTip() const; + + void perform(IPluginContentWidgetContext *context) const; +private: + QString m_text; + QString m_toolTip; + QIcon m_icon; + QKeySequence m_shortcut; + MenuLocation m_menuLocation; + + Func m_func; +}; + +#endif // MENUACTION_H diff --git a/pglab/plugin_support/MenuLocation.cpp b/pglab/plugin_support/MenuLocation.cpp new file mode 100644 index 0000000..3b80754 --- /dev/null +++ b/pglab/plugin_support/MenuLocation.cpp @@ -0,0 +1,8 @@ +#include "MenuLocation.h" + +MenuLocation::MenuLocation() = default; + +MenuLocation::MenuLocation(MenuPath path, int position) + : m_path(std::move(path)) + , m_position(position) +{} diff --git a/pglab/plugin_support/MenuLocation.h b/pglab/plugin_support/MenuLocation.h new file mode 100644 index 0000000..ea49ac6 --- /dev/null +++ b/pglab/plugin_support/MenuLocation.h @@ -0,0 +1,17 @@ +#ifndef MENULOCATION_H +#define MENULOCATION_H + +#include "plugin_support/MenuPath.h" + +class MenuLocation { +public: + MenuLocation(); + MenuLocation(MenuPath path, int position = -1); + + bool isEmpty() const; +private: + MenuPath m_path; + int m_position = -1; +}; + +#endif // MENULOCATION_H diff --git a/pglab/plugin_support/MenuPath.cpp b/pglab/plugin_support/MenuPath.cpp new file mode 100644 index 0000000..8951d29 --- /dev/null +++ b/pglab/plugin_support/MenuPath.cpp @@ -0,0 +1,10 @@ +#include "MenuPath.h" + +MenuPath::MenuPath() = default; + +MenuPath::MenuPath(QString menu_path) + : m_menuPath(std::move(menu_path)) +{ +} + + diff --git a/pglab/plugin_support/MenuPath.h b/pglab/plugin_support/MenuPath.h new file mode 100644 index 0000000..98f0b96 --- /dev/null +++ b/pglab/plugin_support/MenuPath.h @@ -0,0 +1,21 @@ +#ifndef MENUPATH_H +#define MENUPATH_H + +#include +#include + + +class MenuPath { +public: + MenuPath(); + MenuPath(QString menu_path); + +private: + QString m_menuPath; + /// Contains the elements of the path, in general + /// more then 3 levels is a bad idea but small_vector can grow when required. +// boost::container::small_vector m_menuItemSeperators; +}; + + +#endif // MENUPATH_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/PluginContentWidget.cpp b/pglab/plugin_support/PluginContentWidget.cpp new file mode 100644 index 0000000..39ff62d --- /dev/null +++ b/pglab/plugin_support/PluginContentWidget.cpp @@ -0,0 +1,11 @@ +#include "PluginContentWidget.h" + +PluginContentWidget::PluginContentWidget(IPluginContentWidgetContext *context, QWidget* parent) + : QWidget(parent) + , m_context(context) +{} + +bool PluginContentWidget::canClose() +{ + return true; +} diff --git a/pglab/PlgPage.h b/pglab/plugin_support/PluginContentWidget.h similarity index 65% rename from pglab/PlgPage.h rename to pglab/plugin_support/PluginContentWidget.h index ecb31ff..663c181 100644 --- a/pglab/PlgPage.h +++ b/pglab/plugin_support/PluginContentWidget.h @@ -4,6 +4,9 @@ #include #include +class IPluginContentWidgetContext; +class PluginModule; + /// Provides a pluggable system for toolbar buttons and menu actions /// /// We will need several kind of actions @@ -15,13 +18,16 @@ /// Can we use same groupings for toolbars and menu's /// How about additional toolbars? /// -class PlgPage: public QWidget{ +class PluginContentWidget: public QWidget{ public: - using QWidget::QWidget; - + PluginContentWidget(IPluginContentWidgetContext *context, QWidget* parent = nullptr); /// Returns the toolbar buttons for this page - virtual std::vector getToolbarActions(); virtual bool canClose(); + +protected: + IPluginContentWidgetContext *context() { return m_context; } +private: + IPluginContentWidgetContext *m_context; }; #endif // PGLPAGE_H diff --git a/pglab/plugin_support/PluginContentWidgetContextBase.cpp b/pglab/plugin_support/PluginContentWidgetContextBase.cpp new file mode 100644 index 0000000..1d6703a --- /dev/null +++ b/pglab/plugin_support/PluginContentWidgetContextBase.cpp @@ -0,0 +1,103 @@ +#include "PluginContentWidgetContextBase.h" +#include "PluginContentWidget.h" +#include "PluginModule.h" +#include "PluginRegister.h" +#include "LWidgetAction.h" +#include +#include +#include +#include + + + +LWidgetData::LWidgetData(PluginModule *module) + : m_module(module) +{} + +void LWidgetData::init(PluginContentWidget *widget) +{ + auto&& widget_actions = m_module->widgetActions(); + m_widgetActions.reserve(widget_actions.size()); + for (auto&& wa : widget_actions) { + m_widgetActions.push_back(createAction(wa, widget)); + } +} + +QList LWidgetData::actions() +{ + return m_widgetActions; +} + +QAction *LWidgetData::createAction(const LWidgetAction &wa, PluginContentWidget *widget) +{ + auto ac = new QAction(wa.icon(), wa.text(), widget); + ac->setShortcut(wa.shortcut()); + ac->setToolTip(wa.toolTip()); + QObject::connect(ac, SIGNAL(triggered()), widget, wa.slotName()); + return ac; +} + + + +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); +} + +void PluginContentWidgetContextBase::addContentWidget(PluginModule *module, PluginContentWidget *widget) +{ + auto res = m_widgetLst.emplace(widget, module); + if (!res.second) + throw std::runtime_error("Unexpected conflicting key on insertiong PluginContentWidgetContextBase::addContentWidget"); + + res.first->second.init(widget); +} + +void PluginContentWidgetContextBase::removeContentWidget(PluginContentWidget *widget) +{ + auto res = m_widgetLst.find(widget); + if (res == m_widgetLst.end()) + return; + + m_widgetLst.erase(res); +} + +void PluginContentWidgetContextBase::addWidgetActionsToToolbar(PluginContentWidget *widget, QToolBar *toolbar) +{ + auto res = m_widgetLst.find(widget); + if (res == m_widgetLst.end()) + return; + + auto && actions = res->second.actions(); + toolbar->addActions(actions); +} + +void PluginContentWidgetContextBase::removeWidgetActionsFromToolbar(PluginContentWidget *widget, QToolBar *toolbar) +{ + auto res = m_widgetLst.find(widget); + if (res == m_widgetLst.end()) + return; + + auto && actions = res->second.actions(); + for (auto && ac : actions) + toolbar->removeAction(ac); + +} diff --git a/pglab/plugin_support/PluginContentWidgetContextBase.h b/pglab/plugin_support/PluginContentWidgetContextBase.h new file mode 100644 index 0000000..55428c8 --- /dev/null +++ b/pglab/plugin_support/PluginContentWidgetContextBase.h @@ -0,0 +1,50 @@ +#ifndef PLUGINCONTENTWIDGETCONTEXTBASE_H +#define PLUGINCONTENTWIDGETCONTEXTBASE_H + +#include "plugin_support/IPluginContentWidgetContext.h" +#include + +class LWidgetAction; +class QToolBar; +class QAction; + +class LWidgetData { +public: + LWidgetData(PluginModule *module); + PluginModule* module() { return m_module; } + void init(PluginContentWidget *widget); + QList actions(); + +private: + PluginModule *m_module; + /// List of actions specifically created for this widget from the widgetAction list of the module. + QList m_widgetActions; + + QAction *createAction(const LWidgetAction &wa, PluginContentWidget *widget); +}; + +/// 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; + + void addContentWidget(PluginModule *module, PluginContentWidget *widget) override; + void removeContentWidget(PluginContentWidget *widget) override; + + void addWidgetActionsToToolbar(PluginContentWidget *widget, QToolBar *toolbar); + void removeWidgetActionsFromToolbar(PluginContentWidget *widget, QToolBar *toolbar); +private: + + using WidgetLst = std::map; + + WidgetLst m_widgetLst; +}; + +#endif // PLUGINCONTENTWIDGETCONTEXTBASE_H diff --git a/pglab/plugin_support/PluginModule.cpp b/pglab/plugin_support/PluginModule.cpp new file mode 100644 index 0000000..c5a3ac1 --- /dev/null +++ b/pglab/plugin_support/PluginModule.cpp @@ -0,0 +1,41 @@ +#include "plugin_support/PluginModule.h" +#include + +PluginModule::PluginModule(QString name, QString ident) + : m_name(std::move(name)) + , m_ident(std::move(ident)) +{ +} + +void PluginModule::setDisplayCategory(QString category) +{ + m_displayCategory = std::move(category); +} + +void PluginModule::registerMenuAction(MenuAction action) +{ + qDebug() << "registerMenuAction " << action.text(); + m_menuActions.emplace_back(std::move(action)); +} + +const PluginModule::MenuActionList& PluginModule::menuActions() const +{ + return m_menuActions; +} + +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 new file mode 100644 index 0000000..9927b34 --- /dev/null +++ b/pglab/plugin_support/PluginModule.h @@ -0,0 +1,83 @@ +#ifndef PLUGIN_SUPPORTPLUGINMODULE_H +#define PLUGIN_SUPPORTPLUGINMODULE_H + +#include "ModuleActionParameters.h" +#include "MenuAction.h" +#include "LWidgetAction.h" +#include "PluginRegister.h" +#include +#include +#include +#include + +class QAction; +class IPluginContentWidgetContext; + +class PluginModule: public QObject { + Q_OBJECT +public: + using MenuActionList = std::vector; + using LWidgetActionList = std::vector; + using ModuleAction = std::function; + using ModuleActionMap = std::map; + + PluginModule(QString name, QString ident); + + virtual void init() {} + + const QString& name() const { return m_name; } + const QString& identifier() const { return m_ident; } + const QString& displayCategory() const { return m_displayCategory; } + + void setDisplayCategory(QString category); + + /// registers an action that should always be accessible from the menu + void registerMenuAction(MenuAction action); + const MenuActionList& menuActions() const; + + 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; + + void registerWidgetAction(const LWidgetAction &action) + { + m_widgetActions.push_back(action); + } + const LWidgetActionList& widgetActions() const { return m_widgetActions; } +private: + /// Name shown to end users + QString m_name; + /// Unique identifier + QString m_ident; + QString m_displayCategory; + + MenuActionList m_menuActions; + ModuleActionMap m_moduleActions; + LWidgetActionList m_widgetActions; +}; + + +template +std::shared_ptr createPluginModule(QString name, QString ident, QString category={}) +{ + auto module = std::make_shared(std::move(name), std::move(ident)); + module->setDisplayCategory(category); + + PluginRegister::getInstance()->registerModule(module); + return std::move(module); +} + +#define REGISTER_PLUGIN_MODULE(module, name, ident) \ + namespace {\ + std::weak_ptr register_variable = createPluginModule\ + (name, ident);} + +#define REGISTER_PLUGIN_MODULE_CAT(module, name, ident, category) \ + namespace {\ + std::weak_ptr register_variable = createPluginModule\ + (name, ident, category);} + + +#endif // PLUGIN_SUPPORTPLUGINMODULE_H diff --git a/pglab/plugin_support/PluginRegister.cpp b/pglab/plugin_support/PluginRegister.cpp new file mode 100644 index 0000000..8b1dd99 --- /dev/null +++ b/pglab/plugin_support/PluginRegister.cpp @@ -0,0 +1,45 @@ +#include "PluginRegister.h" +#include "plugin_support/PluginModule.h" +#include + + +PluginRegister* PluginRegister::s_pluginRegister; + +PluginRegister* PluginRegister::getInstance() +{ + static std::mutex m; + // check if set without locking first (in most cases it will be set and it will never be unset) so locking the mutex everytime + // is a waist of time. + if (!s_pluginRegister) { + // not set then lock + std::lock_guard guard(m); + // recheck in case someone else just set it + if (!s_pluginRegister) { + s_pluginRegister = new PluginRegister; + } + } + return s_pluginRegister; +} + +PluginRegister::PluginRegister() = default; + +void PluginRegister::initModules() +{ + for (auto && mod : m_moduleMap) { + mod.second->init(); + } +} + +void PluginRegister::registerModule(PluginModuleSPtr module) +{ + qDebug() << "registerModule " << 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 new file mode 100644 index 0000000..cc8449a --- /dev/null +++ b/pglab/plugin_support/PluginRegister.h @@ -0,0 +1,34 @@ +#ifndef PLUGINREGISTER_H +#define PLUGINREGISTER_H + +#include +#include +#include +#include + +class QAction; +class PluginModule; + +class PluginRegister { +public: + using PluginModuleSPtr = std::shared_ptr; + using ModuleMap = std::map; + + static PluginRegister* getInstance(); + + PluginRegister(); + void registerModule(PluginModuleSPtr module); + + void initModules(); + const ModuleMap& modules() const{ return m_moduleMap; } + + const PluginModule* findModule(const QString &module_ident) const; +private: + + ModuleMap m_moduleMap; + + static PluginRegister* s_pluginRegister; +}; + + +#endif // PLUGINREGISTER_H diff --git a/pglab/plugin_support/ToolbarLocation.cpp b/pglab/plugin_support/ToolbarLocation.cpp new file mode 100644 index 0000000..61a57b3 --- /dev/null +++ b/pglab/plugin_support/ToolbarLocation.cpp @@ -0,0 +1,14 @@ +#include "ToolbarLocation.h" + +ToolbarLocation::ToolbarLocation() = default; + +ToolbarLocation::ToolbarLocation(QString toolbar, QString group, int position) + : m_toolbar(std::move(toolbar)) + , m_group(std::move(group)) + , m_position(position) +{} + +bool ToolbarLocation::isEmpty() const +{ + return m_toolbar.isEmpty(); +} diff --git a/pglab/plugin_support/ToolbarLocation.h b/pglab/plugin_support/ToolbarLocation.h new file mode 100644 index 0000000..ce0cb3d --- /dev/null +++ b/pglab/plugin_support/ToolbarLocation.h @@ -0,0 +1,19 @@ +#ifndef TOOLBARLOCATION_H +#define TOOLBARLOCATION_H + +#include + +class ToolbarLocation { +public: + ToolbarLocation(); + ToolbarLocation(QString toolbar, QString group, int position = -1); + + bool isEmpty() const; +private: + QString m_toolbar; + QString m_group; + int m_position = -1; +}; + + +#endif // TOOLBARLOCATION_H diff --git a/pglab/tsqueue.cpp b/pglab/tsqueue.cpp deleted file mode 100644 index ef3a0f9..0000000 --- a/pglab/tsqueue.cpp +++ /dev/null @@ -1,27 +0,0 @@ -#include "tsqueue.h" - -TSQueue::TSQueue() -{} - -void TSQueue::add(t_Callable callable) -{ - std::lock_guard g(m); - futureQueue.push_back(std::move(callable)); -} - -bool TSQueue::empty() -{ - std::lock_guard g(m); - return futureQueue.empty(); -} - -TSQueue::t_Callable TSQueue::pop() -{ - std::lock_guard g(m); - auto f = std::move(futureQueue.front()); - futureQueue.pop_front(); - if (futureQueue.empty()) { - } - return f; -} - diff --git a/pglab/tsqueue.h b/pglab/tsqueue.h deleted file mode 100644 index 779624e..0000000 --- a/pglab/tsqueue.h +++ /dev/null @@ -1,26 +0,0 @@ -#ifndef TSQUEUE_H -#define TSQUEUE_H - -//#include "Win32Event.h" -#include -#include -#include - -class TSQueue { -public: - using t_Callable = std::function; - - TSQueue(); - void add(t_Callable callable); - bool empty(); - t_Callable pop(); - -private: - using t_CallableQueue = std::deque; - - std::mutex m; - t_CallableQueue futureQueue; -}; - - -#endif // TSQUEUE_H diff --git a/pglablib/catalog/PgTriggerContainer.cpp b/pglablib/catalog/PgTriggerContainer.cpp index fb32a4f..d082d4d 100644 --- a/pglablib/catalog/PgTriggerContainer.cpp +++ b/pglablib/catalog/PgTriggerContainer.cpp @@ -10,7 +10,7 @@ std::string PgTriggerContainer::getLoadQuery() const "SELECT oid, tgname, tgrelid, tgfoid, tgtype, tgenabled, tgisinternal, tgconstrrelid, \n" " tgconstrindid, tgconstraint, tgdeferrable, tginitdeferred, tgnargs, tgattr, \n" " tgargs, COALESCE(substring(pg_get_triggerdef(oid), 'WHEN (.*) EXECUTE PROCEDURE'), substring(pg_get_triggerdef(oid), 'WHEN (.*) \\$trigger')) AS whenclause"; - if (minimumVersion(90600)) { + if (minimumVersion(100000)) { q += ", tgoldtable, tgnewtable"; } q += @@ -38,7 +38,7 @@ PgTrigger PgTriggerContainer::loadElem(const Pgsql::Row &row) } } col >> v.whenclause; - if (minimumVersion(90600)) { + if (minimumVersion(100000)) { col >> v.oldtable >> v.newtable; } return v;