pgLab/pglab/DatabaseWindow.cpp
eelke 1a208a6a2d Crud page has now reload action.
F5 key is bound to the execute query, reload catalog and reload crud
actions. By using addAction to add these actions to the relevant pages
the ambiguity of the shortcut is resolved.
2019-10-13 07:31:48 +02:00

624 lines
16 KiB
C++

#include "DatabaseWindow.h"
#include "util.h"
#include "CrudTab.h"
#include "widgets/CatalogTablesPage.h"
#include "OpenDatabase.h"
#include "catalog/PgDatabaseCatalog.h"
#include "ConnectionController.h"
#include "MasterController.h"
#include "TaskExecutor.h"
#include <QAction>
#include <QApplication>
#include <QFileDialog>
#include <QMenuBar>
#include <QMessageBox>
#include <QMetaMethod>
#include <QStandardPaths>
#include <QStatusBar>
#include <QTableView>
#include "EditTableWidget.h"
#include "CodeGenerator.h"
#include "QueryTool.h"
namespace pg = Pgsql;
DatabaseWindow::DatabaseWindow(MasterController *master, QWidget *parent)
: QMainWindow(parent)
, m_masterController(master)
{
connect(&loadWatcher, &QFutureWatcher<LoadCatalog::Result>::finished,
this, &DatabaseWindow::catalogLoaded);
m_tabWidget = new QTabWidget(this);
m_tabWidget->setObjectName("m_tabWidget");
setCentralWidget(m_tabWidget);
createActions();
initMenus();
QMetaObject::connectSlotsByName(this);
}
DatabaseWindow::~DatabaseWindow() = default;
void DatabaseWindow::addPage(QWidget* page, QString caption)
{
m_tabWidget->addTab(page, caption);
m_tabWidget->setCurrentWidget(page);
}
void DatabaseWindow::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 DatabaseWindow::setTabIcon(QWidget *widget, const QString &iconname)
{
auto index = m_tabWidget->indexOf(widget);
auto n = ":/icons/16x16/" + iconname;
m_tabWidget->setTabIcon(index, QIcon(n));
}
void DatabaseWindow::newCodeGenPage(QString query, std::shared_ptr<const Pgsql::Result> dbres)
{
auto cgtab = new CodeGenerator(this);
cgtab->Init(m_database->catalog(), query, dbres);
addPage(cgtab, "Codegen");
}
QueryTool *DatabaseWindow::GetActiveQueryTool()
{
auto widget = m_tabWidget->currentWidget();
auto qt = dynamic_cast<QueryTool*>(widget);
return qt;
}
CrudTab *DatabaseWindow::GetActiveCrud()
{
auto widget = m_tabWidget->currentWidget();
auto ct = dynamic_cast<CrudTab*>(widget);
return ct;
}
void DatabaseWindow::setConfig(const ConnectionConfig &config)
{
m_config = config;
try {
QString title = "pglab - ";
title += m_config.name();
setWindowTitle(title);
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()));
close();
}
}
void DatabaseWindow::createActions()
{
{
QIcon icon;
icon.addFile(QString::fromUtf8(":/icons/about.png"), QSize(), QIcon::Normal, QIcon::On);
auto action = actionAbout = new QAction(icon, tr("About"), this);
action->setObjectName("actionAbout");
}
{
QIcon icon;
icon.addFile(QString::fromUtf8(":/icons/script_delete.png"), QSize(), QIcon::Normal, QIcon::On);
auto action = actionCancelQuery = new QAction(icon, tr("Cancel query"), this);
action->setObjectName("actionCancelQuery");
}
{
QIcon icon;
icon.addFile(QString::fromUtf8(":/icons/page_white_delete.png"), QSize(), QIcon::Normal, QIcon::On);
auto action = actionClose = new QAction(icon, tr("Close"), this);
action->setObjectName("actionClose");
action->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_W));
}
{
QIcon icon;
icon.addFile(QString::fromUtf8(":/icons/page_white_copy.png"), QSize(), QIcon::Normal, QIcon::On);
auto action = actionCopy = new QAction(icon, tr("Copy"), this);
action->setObjectName("actionCopy");
action->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_C));
}
{
QIcon icon;
icon.addFile(QString::fromUtf8(":/icons/token_shortland_character.png"), QSize(), QIcon::Normal, QIcon::On);
auto action = actionCopyAsCString = new QAction(icon, tr("Copy as C string"), this);
action->setObjectName("actionCopyAsCString");
action->setShortcut(QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_C));
}
{
QIcon icon;
icon.addFile(QString::fromUtf8(":/icons/token_shortland_character.png"), QSize(), QIcon::Normal, QIcon::On);
auto action = actionCopyAsRawCppString = new QAction(icon, tr("Copy as raw C++-string"), this);
action->setObjectName("actionCopyAsRawCppString");
}
{
QIcon icon;
icon.addFile(QString::fromUtf8(":/icons/script_go.png"), QSize(), QIcon::Normal, QIcon::On);
auto action = actionExecuteQuery = new QAction(icon, tr("Execute query"), this);
action->setObjectName("actionExecuteQuery");
action->setShortcut(QKeySequence(Qt::Key_F5));
action->setShortcutContext(Qt::WidgetWithChildrenShortcut);
}
{
QIcon icon;
icon.addFile(QString::fromUtf8(":/icons/lightbulb_off.png"), QSize(), QIcon::Normal, QIcon::On);
auto action = actionExplain = new QAction(icon, tr("Explain"), this);
action->setObjectName("actionExplain");
action->setShortcut(QKeySequence(Qt::Key_F7));
}
{
QIcon icon;
icon.addFile(QString::fromUtf8(":/icons/lightbulb.png"), QSize(), QIcon::Normal, QIcon::On);
auto action = actionExplainAnalyze = new QAction(icon, tr("Explain analyze"), this);
action->setObjectName("actionExplainAnalyze");
action->setShortcut(QKeySequence(Qt::SHIFT + Qt::Key_F7));
}
{
QIcon icon;
icon.addFile(QString::fromUtf8(":/icons/table_save.png"), QSize(), QIcon::Normal, QIcon::On);
auto action = actionExportData = new QAction(icon, tr("Export data"), this);
action->setObjectName("actionExportData");
}
{
auto action = actionGenerateCode = new QAction(tr("Generate code"), this);
action->setObjectName("actionGenerateCode");
}
{
QIcon icon;
icon.addFile(QString::fromUtf8(":/icons/page_white_add.png"), QSize(), QIcon::Normal, QIcon::On);
auto action = actionInspectInformationSchema = new QAction(icon, tr("Inspect information_schema"), this);
action->setObjectName("actionInspectInformationSchema");
}
{
QIcon icon;
icon.addFile(QString::fromUtf8(":/icons/page_white_add.png"), QSize(), QIcon::Normal, QIcon::On);
auto action = actionInspectPgCatalog = new QAction(icon, tr("Inspect pg_catalog"), this);
action->setObjectName("actionInspectPgCatalog");
}
{
QIcon icon;
icon.addFile(QString::fromUtf8(":/icons/page_white_add.png"), QSize(), QIcon::Normal, QIcon::On);
auto action = actionInspectUserSchemas = new QAction(icon, tr("Inspect user schemas"), this);
action->setObjectName("actionInspectUserSchemas");
}
{
QIcon icon;
icon.addFile(QString::fromUtf8(":/icons/new_query_tab.png"), QSize(), QIcon::Normal, QIcon::On);
auto action = actionNewSql = new QAction(icon, tr("New Query"), this);
action->setObjectName("actionNewSql");
action->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_N));
}
{
QIcon icon;
icon.addFile(QString::fromUtf8(":/icons/folder.png"), QSize(), QIcon::Normal, QIcon::On);
auto action = actionOpenSql = new QAction(icon, tr("Open Query"), this);
action->setObjectName("actionOpenSql");
action->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_O));
}
{
QIcon icon;
//icon.addFile(QString::fromUtf8(":/icons/folder.png"), QSize(), QIcon::Normal, QIcon::On);
auto action = actionPasteLangString = new QAction(icon, tr("Paste lang string"), this);
action->setObjectName("actionPasteLangString");
action->setShortcut(QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_V));
}
{
QIcon icon;
auto action = actionRefreshCatalog = new QAction(icon, tr("Refresh"), this);
action->setShortcut(QKeySequence(Qt::Key_F5));
action->setShortcutContext(Qt::WidgetWithChildrenShortcut);
action->setObjectName("actionRefreshCatalog");
}
{
auto action = actionRefreshCrud = new QAction(QIcon(), tr("Refresh"), this);
action->setShortcut(QKeySequence(Qt::Key_F5));
action->setShortcutContext(Qt::WidgetWithChildrenShortcut);
action->setObjectName("actionRefreshCrud");
}
{
QIcon icon;
icon.addFile(QString::fromUtf8(":/icons/script_save.png"), QSize(), QIcon::Normal, QIcon::On);
auto action = actionSaveSql = new QAction(icon, tr("Save query"), this);
action->setObjectName("actionSaveSql");
action->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_S));
}
{
auto action = actionSaveSqlAs = new QAction(tr("Save query as"), this);
action->setObjectName("actionSaveSqlAs");
}
{
auto action = actionSaveCopyOfSqlAs = new QAction(tr("Save copy of query as"), this);
action->setObjectName("actionSaveCopyOfSqlAs");
}
{
auto action = actionShowConnectionManager = new QAction(tr("Show connection manager"), this);
action->setObjectName("actionShowConnectionManager");
}
}
void DatabaseWindow::initMenus()
{
auto mb = new QMenuBar(this);
menuFile = mb->addMenu(tr("File"));
menuFile->addActions({
actionNewSql,
actionOpenSql,
seperator(),
actionSaveSql,
actionSaveSqlAs,
actionSaveCopyOfSqlAs,
seperator(),
actionExportData,
seperator(),
actionClose
});
menuEdit = mb->addMenu(tr("Edit"));
menuEdit->addActions({
actionCopy,
actionCopyAsCString,
actionCopyAsRawCppString,
// standard Paste missing Ctrl+V works however by default
actionPasteLangString,
actionGenerateCode
});
menuQuery = mb->addMenu(tr("Query"));
menuQuery->addActions({
actionExecuteQuery,
actionExplain,
actionExplainAnalyze,
actionCancelQuery
});
menuCatalog = mb->addMenu(tr("Catalog"));
menuCatalog->addActions({
actionRefreshCatalog
});
menuCrud = mb->addMenu(tr("CRUD"));
menuCrud->addActions({
actionRefreshCrud
});
menuWindow = mb->addMenu(tr("Window"));
menuWindow->addActions({
actionInspectUserSchemas,
actionInspectPgCatalog,
actionInspectInformationSchema,
seperator(),
actionShowConnectionManager
});
menuHelp = mb->addMenu(tr("Help"));
menuHelp->addActions({
seperator(),
actionAbout
});
setMenuBar(mb);
}
QAction *DatabaseWindow::seperator()
{
auto ac = new QAction(this);
ac->setSeparator(true);
return ac;
}
void DatabaseWindow::newCreateTablePage()
{
auto w = new EditTableWidget(m_database, this);
m_tabWidget->addTab(w, "Create table");
}
void DatabaseWindow::newCrudPage(Oid tableoid)
{
CrudTab *ct = new CrudTab(this, this);
ct->addAction(actionRefreshCrud);
addPage(ct, "crud");
ct->setConfig(tableoid);
}
void DatabaseWindow::newCatalogInspectorPage(QString caption, NamespaceFilter filter)
{
auto ct = new CatalogInspector(m_database, this);
ct->addAction(actionRefreshCatalog);
addPage(ct, caption);
ct->setNamespaceFilter(filter);
connect(ct->tablesPage(), &CatalogTablesPage::tableSelected, this, &DatabaseWindow::tableSelected);
}
void DatabaseWindow::closeTab(int index)
{
QWidget *widget = m_tabWidget->widget(index);
auto qt = dynamic_cast<QueryTool*>(widget);
if (qt && qt->canClose()) {
m_tabWidget->removeTab(index);
}
else if (index >= 0) {
m_tabWidget->removeTab(index);
}
}
void DatabaseWindow::catalogLoaded()
{
try {
m_database = loadWatcher.future().result();
// for (auto f : { "user", "pg_catalog", "information_schema" }) {
// // TODO open inspector windows
// }
// newCreateTablePage();
on_actionNewSql_triggered();
} catch (const OpenDatabaseException &ex) {
QMessageBox::critical(this, "Error reading database", ex.text());
close();
}
}
void DatabaseWindow::tableSelected(Oid tableoid)
{
newCrudPage(tableoid);
}
void DatabaseWindow::on_actionAbout_triggered()
{
QMessageBox::about(this, "pgLab 0.1", tr(
"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.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 "
"attribution 3.0 license."
));
}
void DatabaseWindow::on_actionCancelQuery_triggered()
{
auto query_tool = GetActiveQueryTool();
if (query_tool) {
query_tool->cancel();
}
}
void DatabaseWindow::on_actionClose_triggered()
{
m_tabWidget->tabCloseRequested(m_tabWidget->currentIndex());
}
void DatabaseWindow::on_actionCopy_triggered()
{
QWidget *w = QApplication::focusWidget();
QTableView *tv = dynamic_cast<QTableView*>(w);
if (tv) {
copySelectionToClipboard(tv);
}
else {
const QMetaObject *meta = w->metaObject();
int i = meta->indexOfMethod("copy()");
if (i != -1) {
QMetaMethod method = meta->method(i);
method.invoke(w, Qt::AutoConnection);
}
}
}
void DatabaseWindow::on_actionCopyAsCString_triggered()
{
auto query_tool = GetActiveQueryTool();
if (query_tool) {
query_tool->copyQueryAsCString();
}
}
void DatabaseWindow::on_actionCopyAsRawCppString_triggered()
{
auto query_tool = GetActiveQueryTool();
if (query_tool) {
query_tool->copyQueryAsRawCppString();
}
}
void DatabaseWindow::on_actionExecuteQuery_triggered()
{
auto query_tool = GetActiveQueryTool();
if (query_tool) {
query_tool->execute();
}
}
void DatabaseWindow::on_actionExplain_triggered()
{
auto query_tool = GetActiveQueryTool();
if (query_tool) {
query_tool->explain(false);
}
}
void DatabaseWindow::on_actionExplainAnalyze_triggered()
{
auto query_tool = GetActiveQueryTool();
if (query_tool) {
query_tool->explain(true);
}
}
void DatabaseWindow::on_actionExportData_triggered()
{
auto query_tool = GetActiveQueryTool();
if (query_tool) {
query_tool->exportData();
}
}
void DatabaseWindow::on_actionGenerateCode_triggered()
{
auto query_tool = GetActiveQueryTool();
if (query_tool) {
query_tool->generateCode();
//newCodeGenPage
}
}
void DatabaseWindow::on_actionInspectInformationSchema_triggered()
{
newCatalogInspectorPage("information_schema", NamespaceFilter::InformationSchema);
}
void DatabaseWindow::on_actionInspectPgCatalog_triggered()
{
newCatalogInspectorPage("pg_catalog", NamespaceFilter::PgCatalog);
}
void DatabaseWindow::on_actionInspectUserSchemas_triggered()
{
newCatalogInspectorPage("Schema", NamespaceFilter::User);
}
void DatabaseWindow::on_actionNewSql_triggered()
{
auto *ct = new QueryTool(this, this);
ct->addAction(actionExecuteQuery);
addPage(ct, "query");
ct->newdoc();
}
void DatabaseWindow::on_actionOpenSql_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()) {
auto *ct = new QueryTool(this, this);
if (ct->load(file_name)) {
addPage(ct, "");
}
else {
delete ct;
}
}
}
void DatabaseWindow::on_actionPasteLangString_triggered()
{
auto query_tool = GetActiveQueryTool();
if (query_tool) {
query_tool->pasteLangString();
}
}
void DatabaseWindow::on_actionRefreshCatalog_triggered()
{
m_database->refresh();
}
void DatabaseWindow::on_actionRefreshCrud_triggered()
{
auto crud = GetActiveCrud();
if (crud) {
crud->refresh();
}
}
void DatabaseWindow::on_actionSaveSql_triggered()
{
auto query_tool = GetActiveQueryTool();
if (query_tool) {
query_tool->save();
}
}
void DatabaseWindow::on_actionSaveSqlAs_triggered()
{
auto query_tool = GetActiveQueryTool();
if (query_tool) {
query_tool->saveAs();
}
}
void DatabaseWindow::on_actionSaveCopyOfSqlAs_triggered()
{
auto query_tool = GetActiveQueryTool();
if (query_tool) {
query_tool->saveCopyAs();
}
}
void DatabaseWindow::on_actionShowConnectionManager_triggered()
{
m_masterController->connectionController()->showConnectionManager();
}
void DatabaseWindow::on_m_tabWidget_tabCloseRequested(int index)
{
closeTab(index);
}
void DatabaseWindow::on_m_tabWidget_currentChanged(int)
{
auto widget = m_tabWidget->currentWidget();
auto qt = dynamic_cast<QueryTool*>(widget);
auto ct = dynamic_cast<CrudTab*>(widget);
auto ci = dynamic_cast<CatalogInspector*>(widget);
menuQuery->menuAction()->setVisible(qt != nullptr);
menuCatalog->menuAction()->setVisible(ci != nullptr);
menuCrud->menuAction()->setVisible(ct != nullptr);
}
void DatabaseWindow::setTitleForWidget(QWidget *widget, QString title, QString hint)
{
int i = m_tabWidget->indexOf(widget);
if (i >= 0) {
m_tabWidget->setTabText(i, title);
m_tabWidget->setTabToolTip(i, hint);
}
}
void DatabaseWindow::setIconForWidget(QWidget *widget, QIcon icon)
{
int i = m_tabWidget->indexOf(widget);
if (i >= 0) {
m_tabWidget->setTabIcon(i, icon);
}
}
std::shared_ptr<OpenDatabase> DatabaseWindow::openDatabase()
{
return m_database;
}
void DatabaseWindow::showStatusBarMessage(QString message)
{
statusBar()->showMessage(message);
}