Merge branch 'plugin-architecture'

This commit is contained in:
eelke 2019-01-06 17:08:50 +01:00
commit bcf0cf1c6c
59 changed files with 1668 additions and 1348 deletions

View file

@ -12,8 +12,9 @@ template <class T>
class ControllableTask
{
public:
using Result = T;
virtual ~ControllableTask() {}
virtual T run(TaskControl& control) = 0;
virtual Result run(TaskControl& control) = 0;
};
#endif // CONTROLLABLETASK_H

View file

@ -4,6 +4,14 @@
namespace {
class registerMetaTypes {
public:
registerMetaTypes()
{
qRegisterMetaType<ExplainRoot::SPtr>();
}
} registerMetaTypes_instance;
ExplainTreeModelItemPtr createPlanItemFromJson(Json::Value &plan)
{

View file

@ -1,9 +1,9 @@
#pragma once
#include <QList>
//#include <QVariant>
#include <vector>
#include <memory>
#include <QMetaType>
namespace Json {
@ -161,3 +161,4 @@ public:
};
Q_DECLARE_METATYPE(ExplainRoot::SPtr);

View file

@ -7,14 +7,16 @@
#include <QFutureInterface>
#include <QRunnable>
#include <QThreadPool>
#include "ControllableTask.h"
#include "TaskControl.h"
template <typename T>
class RunControllableTask : public QFutureInterface<T> , public QRunnable
{
public:
RunControllableTask(ControllableTask<T>* tsk) : task(tsk) { }
virtial ~RunControllableTask() { delete task; }
virtual ~RunControllableTask() { delete task; }
QFuture<T> start()
{

View file

@ -7,8 +7,7 @@
#include <QFutureInterfaceBase>
class TaskControl
{
class TaskControl {
public:
TaskControl(QFutureInterfaceBase *f) : fu(f) { }
bool shouldRun() const { return !fu->isCanceled(); }

View file

@ -6,7 +6,7 @@
*/
#include "ControllableTask.h"
#include "RunControllableTask.h"
#include <QFuture>
/**
* @brief The TaskExecutor class
*

View file

@ -1,47 +0,0 @@
#include "WorkManager.h"
#include <QRunnable>
#include <QThreadPool>
#include <deque>
#include <functional>
#include <mutex>
class WorkManagerImpl: public WorkManager {
public:
void addRunnable(QRunnable *runnable) override;
void addWork(Work work) override;
};
std::shared_ptr<WorkManager> WorkManager::getWorkManager()
{
static std::shared_ptr<WorkManager> wm = std::make_shared<WorkManagerImpl>();
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)));
}

View file

@ -1,22 +0,0 @@
#ifndef WORKMANAGER_H
#define WORKMANAGER_H
#include <functional>
#include <memory>
class QRunnable;
class WorkManager {
public:
static std::shared_ptr<WorkManager> getWorkManager();
using Work = std::function<void()>;
virtual ~WorkManager() = default;
virtual void addRunnable(QRunnable *runnable) = 0;
virtual void addWork(Work work) = 0;
};
#endif // WORKMANAGER_H

View file

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

View file

@ -1,25 +0,0 @@
#include "ASyncWindow.h"
#include <QTimer>
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()));
}
}
}

View file

@ -1,24 +0,0 @@
#ifndef ASYNCWINDOW_H
#define ASYNCWINDOW_H
#include <QMainWindow>
#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

View file

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

View file

@ -1,22 +0,0 @@
#ifndef APPLICATIONWINDOW_H
#define APPLICATIONWINDOW_H
#include <QMainWindow>
namespace Ui {
class ApplicationWindow;
}
class ApplicationWindow : public QMainWindow
{
Q_OBJECT
public:
explicit ApplicationWindow(QWidget *parent = 0);
~ApplicationWindow();
private:
Ui::ApplicationWindow *ui;
};
#endif // APPLICATIONWINDOW_H

View file

@ -1,145 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>ApplicationWindow</class>
<widget class="QMainWindow" name="ApplicationWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>314</width>
<height>672</height>
</rect>
</property>
<property name="windowTitle">
<string>MainWindow</string>
</property>
<widget class="QWidget" name="centralwidget">
<layout class="QVBoxLayout" name="verticalLayout">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QFrame" name="frame">
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QFrame" name="frame_2">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QComboBox" name="comboBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Maximum">
<horstretch>1</horstretch>
<verstretch>1</verstretch>
</sizepolicy>
</property>
<property name="frame">
<bool>false</bool>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="toolButton">
<property name="text">
<string>...</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QFrame" name="frame_3">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<widget class="QMenuBar" name="menubar">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>314</width>
<height>25</height>
</rect>
</property>
</widget>
<widget class="QStatusBar" name="statusbar"/>
</widget>
<resources/>
<connections/>
</ui>

View file

@ -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 <QStringBuilder>
#include <unordered_set>
#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<QPersistentModelIndex> &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<OpenDatabase>();
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<PgDatabaseCatalog> cat)
void CatalogInspector::setCatalog(std::shared_ptr<PgDatabaseCatalog> cat)
{
m_catalog = cat;
m_tablesModel->setCatalog(cat);
@ -143,13 +146,28 @@ void TablesPage::setCatalog(std::shared_ptr<PgDatabaseCatalog> 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 &current, const QModelIndex &previous)
void CatalogInspector::tableListTable_currentRowChanged(const QModelIndex &current, const QModelIndex &previous)
{
if (current.row() != previous.row()) {
if (current.isValid()) {
@ -162,7 +180,7 @@ void TablesPage::tableListTable_currentRowChanged(const QModelIndex &current, co
}
void TablesPage::tableListTable_layoutChanged(const QList<QPersistentModelIndex> &, QAbstractItemModel::LayoutChangeHint )
void CatalogInspector::tableListTable_layoutChanged(const QList<QPersistentModelIndex> &, QAbstractItemModel::LayoutChangeHint )
{
auto&& index = ui->tableListTable->selectionModel()->currentIndex();
if (index.isValid()) {
@ -174,7 +192,7 @@ void TablesPage::tableListTable_layoutChanged(const QList<QPersistentModelIndex>
}
void TablesPage::selectedTableChanged(const std::optional<PgClass> &table)
void CatalogInspector::selectedTableChanged(const std::optional<PgClass> &table)
{
m_columnsPage->setData(m_catalog, table);
@ -190,7 +208,7 @@ void TablesPage::selectedTableChanged(const std::optional<PgClass> &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<int> 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<int> 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<PgClass> &table)
void CatalogInspector::updateSqlTab(const std::optional<PgClass> &table)
{
if (!table.has_value()) {
m_sqlCodePreview->clear();
@ -286,3 +305,31 @@ void TablesPage::updateSqlTab(const std::optional<PgClass> &table)
//
m_sqlCodePreview->setPlainText(drop_sql % "\n\n" % create_sql);
}
void CatalogInspectorModule::init()
{
registerModuleAction("open",
[this] (IPluginContentWidgetContext* context,
const ModuleActionParameters &params)
{
moduleAction_open(context, params);
});
}
void CatalogInspectorModule::moduleAction_open(
IPluginContentWidgetContext* context,
const ModuleActionParameters &params
)
{
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")

View file

@ -6,6 +6,8 @@
#include <optional>
#include <QItemSelection>
#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<PgDatabaseCatalog> 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<PgDatabaseCatalog> 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<PgClass> &table);
void updateSqlTab(const std::optional<PgClass> &table);
@ -60,7 +55,6 @@ private slots:
void tableListTable_currentRowChanged(const QModelIndex &current, const QModelIndex &previous);
void tableListTable_layoutChanged(const QList<QPersistentModelIndex> &parents, QAbstractItemModel::LayoutChangeHint hint);
// void constraintsTable_currentRowChanged(const QModelIndex &current, 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 &params);
};
#endif // TABLESPAGE_H

View file

@ -5,8 +5,8 @@
#include "UserConfiguration.h"
#include <QTextStream>
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<PgDatabaseCatalog> catalog, QString que
m_query = std::move(query);
m_dbres = std::move(dbres);
generateCode();
}
void CodeGenerator::on_updateCodeButton_clicked()
{
generateCode();

View file

@ -2,7 +2,7 @@
#define CODEGENERATOR_H
#include <QWidget>
#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<PgDatabaseCatalog> catalog, QString query, std::shared_ptr<const Pgsql::Result> dbres);

View file

@ -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 <QtConcurrent>
#include <QFuture>
@ -19,8 +17,8 @@
#include <string>
#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<Pgsql::Result>, dbres));
}
}
else {
@ -196,7 +195,7 @@ void CrudModel::loadIntoModel(std::shared_ptr<Pgsql::Result> data)
void CrudModel::initRowMapping()
{
m_rowMapping.resize(m_rowCount);
m_rowMapping.resize(static_cast<size_t>(m_rowCount));
for (int i = 0; i < m_rowCount; ++i)
m_rowMapping[i] = RowMapping{ i };
}

View file

@ -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<OpenDatabase> db, const PgClass &table);
@ -231,8 +230,6 @@ private:
};
using RowMappingVector = std::vector<RowMapping>;
ASyncWindow * m_asyncWindow;
std::shared_ptr<OpenDatabase> m_database;
std::optional<PgClass> m_table;
std::optional<PgConstraint> m_primaryKey;
@ -266,13 +263,6 @@ private:
/// call on initial load to fill in the mappings
void initRowMapping();
// using RedirectVec = std::vector<int>;
// /// In sync with the actual table, used to efficiently find the correct row in the result
// RedirectVec m_redirectVector;
void loadIntoModel(std::shared_ptr<Pgsql::Result> 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<Pgsql::Result> data);
void connectionStateChanged(ASyncDBConnection::State state);
// void queryResult(std::shared_ptr<Pgsql::Result> result);
// void queryError();
// void dataProcessingFutureFinished();
};
#endif // CRUDMODEL_H

View file

@ -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 <QDebug>
#include <QMenu>
#include <QMessageBox>
#include <iterator>
#include <set>
#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<OpenDatabase> db, const PgClass &table)
void CrudTab::setConfig(Oid oid) //std::shared_ptr<OpenDatabase> db, const PgClass &table)
{
m_db = db;
m_table = table;
m_crudModel->setConfig(db, table);
m_db = context()->getObject<OpenDatabase>(); // 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<QAction*> 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 &params)
{
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 &params
)
{
// 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")

View file

@ -3,7 +3,8 @@
#include "catalog/PgClass.h"
#include <QWidget>
#include "PlgPage.h"
#include "plugin_support/PluginContentWidget.h"
#include "plugin_support/PluginModule.h"
#include <memory>
#include <optional>
@ -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<OpenDatabase> db, const PgClass &table);
void setConfig(Oid oid);
public slots:
void refresh();
virtual std::vector<QAction*> getToolbarActions() override;
private:
Ui::CrudTab *ui;
DatabaseWindow *m_window;
std::shared_ptr<OpenDatabase> m_db;
std::optional<PgClass> m_table;
@ -40,9 +36,21 @@ private:
std::vector<QAction*> actions;
private slots:
// void tableView_currentRowChanged(const QModelIndex &current, 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 &params);
};
#endif // CRUDTAB_H

View file

@ -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 <QApplication>
#include <QMessageBox>
#include <QMetaMethod>
#include <QTableView>
// Pages that should become modules
#include "EditTableWidget.h"
#include "CodeGenerator.h"
#include "FunctionsPage.h"
#include "SequencesPage.h"
#include <QStandardPaths>
#include <QFileDialog>
#include <QMessageBox>
#include <QTextTable>
#include <QElapsedTimer>
#include <algorithm>
#include <QCloseEvent>
#include <QMetaObject>
#include <QMetaMethod>
#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<LoadCatalog::Result>::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<const Pgsql::Result> 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<QueryTab*>(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<QLoad>(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<QString> 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<QueryTab*>(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<PlgPage*>(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<QueryTab*>(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<QAction*> actions = page->getToolbarActions();
QList<QAction*> list;
for (auto act : actions) {
list.append(act);
}
ui->mainToolBar->addActions(list);
}
void DatabaseWindow::removeToolBarButtonsForPage(PlgPage *page)
{
std::vector<QAction*> 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<PlgPage*>(widget);
if (page) {
addToolBarButtonsForPage(page);
}
}
m_previousPage = page;
}
void DatabaseWindow::on_actionGenerate_code_triggered()
{
QueryTab *tab = GetActiveQueryTab();
if (tab) {
tab->generateCode();
}
}

View file

@ -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 <QLabel>
#include "ASyncWindow.h"
#include <QLabel>
#include <QRunnable>
#include <QSocketNotifier>
#include <memory>
#include <future>
#include "Pgsql_Connection.h"
#include "QueuedBackgroundTask.h"
#include <chrono>
#include <deque>
#include <mutex>
#include <QMutex>
#include <QWaitCondition>
namespace Ui {
class MainWindow;
}
#include "ControllableTask.h"
#include <QFutureWatcher>
#include <memory>
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<OpenDatabase> getDatabase() { return m_database; }
void newCrudPage(const PgClass &table);
void newCodeGenPage(QString query, std::shared_ptr<const Pgsql::Result> dbres);
private:
Ui::MainWindow *ui;
ConnectionConfig m_config;
std::shared_ptr<OpenDatabase> 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<OpenDatabase::OpenDatabaseSPtr> {
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<QLoad> loader;
QFutureWatcher<LoadCatalog::Result> 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

View file

@ -1,248 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MainWindow</class>
<widget class="QMainWindow" name="MainWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>993</width>
<height>804</height>
</rect>
</property>
<property name="windowTitle">
<string>pglab - database</string>
</property>
<widget class="QWidget" name="centralWidget">
<layout class="QVBoxLayout" name="verticalLayout_3">
<property name="spacing">
<number>7</number>
</property>
<property name="leftMargin">
<number>4</number>
</property>
<property name="topMargin">
<number>4</number>
</property>
<property name="rightMargin">
<number>4</number>
</property>
<property name="bottomMargin">
<number>4</number>
</property>
<item>
<widget class="QTabWidget" name="tabWidget">
<property name="currentIndex">
<number>-1</number>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QMenuBar" name="menuBar">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>993</width>
<height>25</height>
</rect>
</property>
<widget class="QMenu" name="menuTest">
<property name="title">
<string>Fi&amp;le</string>
</property>
<addaction name="actionNew_SQL"/>
<addaction name="actionLoad_SQL"/>
<addaction name="actionSave_SQL"/>
<addaction name="actionSave_SQL_as"/>
<addaction name="actionSave_copy_of_SQL_as"/>
<addaction name="actionExport_data"/>
<addaction name="separator"/>
<addaction name="actionClose"/>
</widget>
<widget class="QMenu" name="menuHelp">
<property name="title">
<string>Help</string>
</property>
<addaction name="actionAbout"/>
</widget>
<widget class="QMenu" name="menuQuery">
<property name="title">
<string>&amp;Query</string>
</property>
<addaction name="separator"/>
</widget>
<widget class="QMenu" name="menuView">
<property name="title">
<string>Wi&amp;ndow</string>
</property>
<addaction name="actionShow_connection_manager"/>
</widget>
<widget class="QMenu" name="menuEdit">
<property name="title">
<string>Edit</string>
</property>
<addaction name="actionCopy"/>
<addaction name="actionCopy_as_C_string"/>
<addaction name="actionCopy_as_raw_Cpp_string"/>
<addaction name="actionGenerate_code"/>
</widget>
<addaction name="menuTest"/>
<addaction name="menuEdit"/>
<addaction name="menuQuery"/>
<addaction name="menuView"/>
<addaction name="menuHelp"/>
</widget>
<widget class="QToolBar" name="mainToolBar">
<attribute name="toolBarArea">
<enum>TopToolBarArea</enum>
</attribute>
<attribute name="toolBarBreak">
<bool>false</bool>
</attribute>
<addaction name="actionNew_SQL"/>
<addaction name="actionLoad_SQL"/>
<addaction name="actionSave_SQL"/>
<addaction name="actionExport_data"/>
<addaction name="actionClose"/>
<addaction name="separator"/>
<addaction name="actionCopy"/>
<addaction name="actionCopy_as_C_string"/>
<addaction name="separator"/>
<addaction name="separator"/>
<addaction name="actionAbout"/>
</widget>
<widget class="QStatusBar" name="statusBar"/>
<action name="actionLoad_SQL">
<property name="icon">
<iconset resource="resources.qrc">
<normaloff>:/icons/folder.png</normaloff>:/icons/folder.png</iconset>
</property>
<property name="text">
<string>&amp;Load SQL</string>
</property>
<property name="shortcut">
<string>Ctrl+O</string>
</property>
</action>
<action name="actionSave_SQL">
<property name="icon">
<iconset resource="resources.qrc">
<normaloff>:/icons/script_save.png</normaloff>:/icons/script_save.png</iconset>
</property>
<property name="text">
<string>&amp;Save SQL</string>
</property>
<property name="shortcut">
<string>Ctrl+S</string>
</property>
</action>
<action name="actionExport_data">
<property name="icon">
<iconset resource="resources.qrc">
<normaloff>:/icons/table_save.png</normaloff>:/icons/table_save.png</iconset>
</property>
<property name="text">
<string>&amp;Export data</string>
</property>
</action>
<action name="actionClose">
<property name="icon">
<iconset>
<normalon>:/icons/page_white_delete.png</normalon>
</iconset>
</property>
<property name="text">
<string>&amp;Close</string>
</property>
<property name="shortcut">
<string>Ctrl+F4</string>
</property>
</action>
<action name="actionAbout">
<property name="icon">
<iconset resource="resources.qrc">
<normaloff>:/icons/about.png</normaloff>
<normalon>:/icons/information.png</normalon>:/icons/about.png</iconset>
</property>
<property name="text">
<string>&amp;About</string>
</property>
</action>
<action name="actionSave_SQL_as">
<property name="text">
<string>Sa&amp;ve SQL as</string>
</property>
</action>
<action name="actionSave_copy_of_SQL_as">
<property name="text">
<string>Save copy &amp;of SQL as</string>
</property>
</action>
<action name="actionNew_SQL">
<property name="icon">
<iconset resource="resources.qrc">
<normaloff>:/icons/new_query_tab.png</normaloff>
<normalon>:/icons/page_white_add.png</normalon>:/icons/new_query_tab.png</iconset>
</property>
<property name="text">
<string>&amp;New SQL</string>
</property>
<property name="shortcut">
<string>Ctrl+N</string>
</property>
</action>
<action name="actionShow_connection_manager">
<property name="text">
<string>&amp;Show connection manager</string>
</property>
</action>
<action name="actionCopy">
<property name="icon">
<iconset>
<normalon>:/icons/page_white_copy.png</normalon>
</iconset>
</property>
<property name="text">
<string>&amp;Copy</string>
</property>
<property name="shortcut">
<string>Ctrl+C</string>
</property>
</action>
<action name="actionCopy_as_C_string">
<property name="icon">
<iconset>
<normalon>:/icons/token_shortland_character.png</normalon>
</iconset>
</property>
<property name="text">
<string>Copy as C-&amp;string</string>
</property>
<property name="shortcut">
<string>Ctrl+Alt+C</string>
</property>
</action>
<action name="actionCopy_as_raw_Cpp_string">
<property name="icon">
<iconset>
<normalon>:/icons/token_shortland_character.png</normalon>
</iconset>
</property>
<property name="text">
<string>Copy as raw C++-string</string>
</property>
</action>
<action name="actionGenerate_code">
<property name="text">
<string>Generate code</string>
</property>
</action>
</widget>
<layoutdefault spacing="6" margin="11"/>
<resources>
<include location="resources.qrc"/>
</resources>
<connections/>
</ui>

View file

@ -1,16 +0,0 @@
#include "PlgPage.h"
//PglPage::PglPage()
//{
//}
std::vector<QAction*> PlgPage::getToolbarActions()
{
return std::vector<QAction*>();
}
bool PlgPage::canClose()
{
return true;
}

View file

@ -1,12 +1,10 @@

#include "QueryTab.h"
#include "QueryTool.h"
#include "ui_QueryTab.h"
#include "SqlSyntaxHighlighter.h"
#include <QStandardPaths>
#include <QPushButton>
#include <QAction>
#include <QFileDialog>
#include <QStatusBar>
#include <QMessageBox>
#include <QTabWidget>
#include <QTextCodec>
@ -15,59 +13,55 @@
#include <QClipboard>
#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<OpenDatabase>();
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<OpenDatabase>();
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<PgDatabaseCatalog> 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<std::shared_ptr<Pgsql::Result>> 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<Pgsql::Result>, 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<QMainWindow*>(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<QTabWidget*>(p);
return tw;
}
//QTabWidget *QueryTab::getTabWidget()
//{
// QWidget * w = parentWidget();
// QWidget * p = w->parentWidget();
// QTabWidget *tw = dynamic_cast<QTabWidget*>(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<Pgsql::Result> 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<QueryResultModel>(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<std::shared_ptr<Pgsql::Result>> 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<QueryResultModel>(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 <codebuilder/CodeBuilder.h>
#include <codebuilder/DefaultConfigs.h>
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<const Pgsql::Result> 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<QAction*> 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);
}

View file

@ -8,7 +8,7 @@
#include "tuplesresultwidget.h"
#include <QWidget>
#include "PlgPage.h"
#include "plugin_support/PluginContentWidget.h"
#include <memory>
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_ptr<PgDatabaseCatalog>cat);
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<QAction*> 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<TuplesResultWidget*>;
Ui::QueryTab *ui;
DatabaseWindow *m_win;
SqlSyntaxHighlighter* highlighter;
ConnectionConfig m_config;
StopWatch m_stopwatch;
std::vector<QAction*> 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<std::shared_ptr<Pgsql::Result>> 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<Pgsql::Result>, 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

109
pglab/QueryToolModule.cpp Normal file
View file

@ -0,0 +1,109 @@
#include "QueryToolModule.h"
#include "QueryTool.h"
#include "plugin_support/IPluginContentWidgetContext.h"
#include "plugin_support/PluginRegister.h"
#include <QStandardPaths>
#include <QFileDialog>
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")

18
pglab/QueryToolModule.h Normal file
View file

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

View file

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

View file

@ -1,8 +1,8 @@
#ifndef SERVERWINDOW_H
#define SERVERWINDOW_H
#include "ASyncWindow.h"
#include "ConnectionConfig.h"
#include <QMainWindow>
#include <memory>
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 );

View file

@ -5,6 +5,7 @@
#endif
#include <memory>
#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;
{

View file

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

View file

@ -0,0 +1,98 @@
#ifndef IPLUGINCONTENTWIDGETCONTEXT_H
#define IPLUGINCONTENTWIDGETCONTEXT_H
#include <QString>
#include <map>
#include <memory>
#include <typeindex>
#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/<size>/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<typename T, class...Args>
bool addObjects(Args&&...args);
template<typename T>
bool registerObject(std::shared_ptr<T> object);
template<typename T>
std::shared_ptr<T> getObject() const;
private:
std::map<std::type_index, std::shared_ptr<void> > m_objectRegistry;
};
template<typename T, class...Args>
bool IPluginContentWidgetContext::addObjects(Args&&...args)
{
std::type_index key(typeid(T));
if (!m_objectRegistry.count(key)) {
auto p = std::make_shared<T>(std::forward<Args>(args)...);
m_objectRegistry[key] = p;
return true;
}
return false;
}
template<typename T>
bool IPluginContentWidgetContext::registerObject(std::shared_ptr<T> object)
{
return m_objectRegistry.emplace(std::type_index(typeid(T)), object).second;
}
template<typename T>
std::shared_ptr<T> IPluginContentWidgetContext::getObject() const
{
auto it = m_objectRegistry.find(typeid(T));
if (it == m_objectRegistry.end()) {
return {};
}
return std::static_pointer_cast<T>(it->second);
}
#endif // IPLUGINCONTENTWIDGETCONTEXT_H

View file

@ -0,0 +1,214 @@
#include "LMainWindow.h"
#include "PluginContentWidget.h"
#include "PluginContentWidgetContextBase.h"
#include "PluginModule.h"
#include "PluginRegister.h"
#include <QDebug>
#include <QMenuBar>
#include <QStatusBar>
#include <QToolBar>
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<PluginContentWidget*>(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<PluginContentWidget*>(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);
}

View file

@ -0,0 +1,58 @@
#ifndef LMAINWINDOW_H
#define LMAINWINDOW_H
#include <QMainWindow>
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

View file

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

View file

@ -0,0 +1,44 @@
#ifndef LWIDGETACTION_H
#define LWIDGETACTION_H
#include "MenuLocation.h"
#include <QIcon>
#include <QKeySequence>
#include <QString>
/** 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

View file

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

View file

@ -0,0 +1,48 @@
#ifndef MENUACTION_H
#define MENUACTION_H
#include "MenuLocation.h"
#include "ToolbarLocation.h"
#include <QIcon>
#include <QKeySequence>
#include <QString>
#include <functional>
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<void(IPluginContentWidgetContext *context)>;
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

View file

@ -0,0 +1,8 @@
#include "MenuLocation.h"
MenuLocation::MenuLocation() = default;
MenuLocation::MenuLocation(MenuPath path, int position)
: m_path(std::move(path))
, m_position(position)
{}

View file

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

View file

@ -0,0 +1,10 @@
#include "MenuPath.h"
MenuPath::MenuPath() = default;
MenuPath::MenuPath(QString menu_path)
: m_menuPath(std::move(menu_path))
{
}

View file

@ -0,0 +1,21 @@
#ifndef MENUPATH_H
#define MENUPATH_H
#include <QString>
#include <boost/container/small_vector.hpp>
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<int, 3> m_menuItemSeperators;
};
#endif // MENUPATH_H

View file

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

View file

@ -0,0 +1,11 @@
#include "PluginContentWidget.h"
PluginContentWidget::PluginContentWidget(IPluginContentWidgetContext *context, QWidget* parent)
: QWidget(parent)
, m_context(context)
{}
bool PluginContentWidget::canClose()
{
return true;
}

View file

@ -4,6 +4,9 @@
#include <QWidget>
#include <vector>
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<QAction*> getToolbarActions();
virtual bool canClose();
protected:
IPluginContentWidgetContext *context() { return m_context; }
private:
IPluginContentWidgetContext *m_context;
};
#endif // PGLPAGE_H

View file

@ -0,0 +1,103 @@
#include "PluginContentWidgetContextBase.h"
#include "PluginContentWidget.h"
#include "PluginModule.h"
#include "PluginRegister.h"
#include "LWidgetAction.h"
#include <QAction>
#include <QDebug>
#include <QToolBar>
#include <vector>
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<QAction *> 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);
}

View file

@ -0,0 +1,50 @@
#ifndef PLUGINCONTENTWIDGETCONTEXTBASE_H
#define PLUGINCONTENTWIDGETCONTEXTBASE_H
#include "plugin_support/IPluginContentWidgetContext.h"
#include <QList>
class LWidgetAction;
class QToolBar;
class QAction;
class LWidgetData {
public:
LWidgetData(PluginModule *module);
PluginModule* module() { return m_module; }
void init(PluginContentWidget *widget);
QList<QAction *> actions();
private:
PluginModule *m_module;
/// List of actions specifically created for this widget from the widgetAction list of the module.
QList<QAction *> 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<PluginContentWidget*, LWidgetData>;
WidgetLst m_widgetLst;
};
#endif // PLUGINCONTENTWIDGETCONTEXTBASE_H

View file

@ -0,0 +1,41 @@
#include "plugin_support/PluginModule.h"
#include <QDebug>
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;
}

View file

@ -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 <QObject>
#include <functional>
#include <map>
#include <vector>
class QAction;
class IPluginContentWidgetContext;
class PluginModule: public QObject {
Q_OBJECT
public:
using MenuActionList = std::vector<MenuAction>;
using LWidgetActionList = std::vector<LWidgetAction>;
using ModuleAction = std::function<void(IPluginContentWidgetContext*, const ModuleActionParameters &)>;
using ModuleActionMap = std::map<QString, ModuleAction>;
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 <typename T>
std::shared_ptr<PluginModule> createPluginModule(QString name, QString ident, QString category={})
{
auto module = std::make_shared<T>(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<PluginModule> register_variable = createPluginModule<module>\
(name, ident);}
#define REGISTER_PLUGIN_MODULE_CAT(module, name, ident, category) \
namespace {\
std::weak_ptr<PluginModule> register_variable = createPluginModule<module>\
(name, ident, category);}
#endif // PLUGIN_SUPPORTPLUGINMODULE_H

View file

@ -0,0 +1,45 @@
#include "PluginRegister.h"
#include "plugin_support/PluginModule.h"
#include <QDebug>
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<std::mutex> 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();
}

View file

@ -0,0 +1,34 @@
#ifndef PLUGINREGISTER_H
#define PLUGINREGISTER_H
#include <memory>
#include <map>
#include <mutex>
#include <QString>
class QAction;
class PluginModule;
class PluginRegister {
public:
using PluginModuleSPtr = std::shared_ptr<PluginModule>;
using ModuleMap = std::map<QString, PluginModuleSPtr>;
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

View file

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

View file

@ -0,0 +1,19 @@
#ifndef TOOLBARLOCATION_H
#define TOOLBARLOCATION_H
#include <QString>
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

View file

@ -1,27 +0,0 @@
#include "tsqueue.h"
TSQueue::TSQueue()
{}
void TSQueue::add(t_Callable callable)
{
std::lock_guard<std::mutex> g(m);
futureQueue.push_back(std::move(callable));
}
bool TSQueue::empty()
{
std::lock_guard<std::mutex> g(m);
return futureQueue.empty();
}
TSQueue::t_Callable TSQueue::pop()
{
std::lock_guard<std::mutex> g(m);
auto f = std::move(futureQueue.front());
futureQueue.pop_front();
if (futureQueue.empty()) {
}
return f;
}

View file

@ -1,26 +0,0 @@
#ifndef TSQUEUE_H
#define TSQUEUE_H
//#include "Win32Event.h"
#include <deque>
#include <functional>
#include <mutex>
class TSQueue {
public:
using t_Callable = std::function<void()>;
TSQueue();
void add(t_Callable callable);
bool empty();
t_Callable pop();
private:
using t_CallableQueue = std::deque<t_Callable>;
std::mutex m;
t_CallableQueue futureQueue;
};
#endif // TSQUEUE_H

View file

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