Caused by the fact the the tab was not freed. Now the widgets on the tabs are freed when they are closed which in turns frees (and closes) the connection objects.
580 lines
16 KiB
C++
580 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 <QCloseEvent>
|
|
#include <QFileDialog>
|
|
#include <QMenuBar>
|
|
#include <QMessageBox>
|
|
#include <QMetaMethod>
|
|
#include <QMimeData>
|
|
#include <QStandardPaths>
|
|
#include <QStatusBar>
|
|
#include <QTableView>
|
|
|
|
#include "EditTableWidget.h"
|
|
#include "CodeGenerator.h"
|
|
#include "QueryTool.h"
|
|
#include <serverinspector/ServerInspector.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);
|
|
|
|
connect(m_tabWidget, &QTabWidget::tabCloseRequested, this, &DatabaseWindow::m_tabWidget_tabCloseRequested);
|
|
connect(m_tabWidget, &QTabWidget::currentChanged, this, &DatabaseWindow::m_tabWidget_currentChanged);
|
|
|
|
createActions();
|
|
initMenus();
|
|
|
|
setAcceptDrops(true);
|
|
}
|
|
|
|
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::closeEvent(QCloseEvent *event)
|
|
{
|
|
for (int idx = 0; idx < m_tabWidget->count(); ++idx) {
|
|
if (!canCloseTab(idx)) {
|
|
event->ignore();
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
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();
|
|
}
|
|
}
|
|
|
|
QAction* DatabaseWindow::createAction(QString iconname, QString caption, void (DatabaseWindow::*func)())
|
|
{
|
|
QIcon icon;
|
|
icon.addFile(iconname, QSize(), QIcon::Normal, QIcon::On);
|
|
QAction *action = new QAction(icon, caption, this);
|
|
connect(action, &QAction::triggered, this, func);
|
|
return action;
|
|
}
|
|
|
|
QAction* DatabaseWindow::createAction(QString caption, void (DatabaseWindow::*func)())
|
|
{
|
|
QAction *action = new QAction(caption, this);
|
|
connect(action, &QAction::triggered, this, func);
|
|
return action;
|
|
}
|
|
|
|
void DatabaseWindow::createActions()
|
|
{
|
|
actionAbout = createAction(":/icons/about.png", tr("About"), &DatabaseWindow::actionAbout_triggered);
|
|
actionCancelQuery = createAction(":/icons/script_delete.png", tr("Cancel query"), &DatabaseWindow::actionCancelQuery_triggered);
|
|
actionClose = createAction(":/icons/page_white_delete.png", tr("Close"), &DatabaseWindow::actionClose_triggered);
|
|
actionCopy = createAction(":/icons/page_white_copy.png", tr("Copy"), &DatabaseWindow::actionCopy_triggered);
|
|
actionCopyAsCString = createAction(":/icons/token_shortland_character.png", tr("Copy as C string"), &DatabaseWindow::actionCopyAsCString_triggered);
|
|
actionCopyAsRawCppString = createAction(":/icons/token_shortland_character.png", tr("Copy as raw C++-string"), &DatabaseWindow::actionCopyAsRawCppString_triggered);
|
|
actionExecuteQuery = createAction(":/icons/script_go.png", tr("Execute query"), &DatabaseWindow::actionExecuteQuery_triggered);
|
|
actionExplain = createAction(":/icons/lightbulb_off.png", tr("Explain"), &DatabaseWindow::actionExplain_triggered);
|
|
actionExplainAnalyze = createAction(":/icons/lightbulb.png", tr("Explain analyze"), &DatabaseWindow::actionExplainAnalyze_triggered);
|
|
actionExportData = createAction(":/icons/table_save.png", tr("Export data"), &DatabaseWindow::actionExportData_triggered);
|
|
actionGenerateCode = createAction(tr("Generate code"), &DatabaseWindow::actionGenerateCode_triggered);
|
|
actionInspectInformationSchema = createAction(":/icons/page_white_add.png", tr("Inspect information_schema"), &DatabaseWindow::actionInspectInformationSchema_triggered);
|
|
actionInspectPgCatalog = createAction(":/icons/page_white_add.png", tr("Inspect pg_catalog"), &DatabaseWindow::actionInspectPgCatalog_triggered);
|
|
actionInspectUserSchemas = createAction(":/icons/page_white_add.png", tr("Inspect user schemas"), &DatabaseWindow::actionInspectUserSchemas_triggered);
|
|
actionServerInspector = createAction(":/icons/page_white_add.png", tr("Inspect server"), &DatabaseWindow::actionServerInspector_triggered);
|
|
actionNewSql = createAction(":/icons/new_query_tab.png", tr("New Query"), &DatabaseWindow::actionNewSql_triggered);
|
|
actionOpenSql = createAction(":/icons/folder.png", tr("Open Query"), &DatabaseWindow::actionOpenSql_triggered);
|
|
actionSaveSql = createAction(":/icons/script_save.png", tr("Save Query"), &DatabaseWindow::actionSaveSql_triggered);
|
|
actionPasteLangString = createAction(tr("Paste lang string"), &DatabaseWindow::actionPasteLangString_triggered);
|
|
actionRefreshCatalog = createAction(tr("Refresh"), &DatabaseWindow::actionRefreshCatalog_triggered);
|
|
actionRefreshCrud = createAction(tr("Refresh"), &DatabaseWindow::actionRefreshCrud_triggered);
|
|
actionSaveSqlAs = createAction(tr("Save query as"), &DatabaseWindow::actionSaveSqlAs_triggered);
|
|
actionSaveCopyOfSqlAs = createAction(tr("Save copy of query as"), &DatabaseWindow::actionSaveCopyOfSqlAs_triggered);
|
|
actionShowConnectionManager = createAction(tr("Show connection manager"), &DatabaseWindow::actionShowConnectionManager_triggered);
|
|
|
|
actionClose->setShortcut(QKeySequence(Qt::CTRL | Qt::Key_W));
|
|
actionCopy->setShortcut(QKeySequence(Qt::CTRL | Qt::Key_C));
|
|
actionCopyAsCString->setShortcut(QKeySequence(Qt::CTRL | Qt::SHIFT | Qt::Key_C));
|
|
actionExecuteQuery->setShortcut(QKeySequence(Qt::Key_F5));
|
|
actionExplain->setShortcut(QKeySequence(Qt::Key_F7));
|
|
actionExplainAnalyze->setShortcut(QKeySequence(Qt::SHIFT | Qt::Key_F7));
|
|
actionNewSql->setShortcut(QKeySequence(Qt::CTRL | Qt::Key_N));
|
|
actionOpenSql->setShortcut(QKeySequence(Qt::CTRL | Qt::Key_O));
|
|
actionSaveSql->setShortcut(QKeySequence(Qt::CTRL | Qt::Key_S));
|
|
}
|
|
|
|
|
|
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,
|
|
actionServerInspector,
|
|
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::newServerInspectorPage()
|
|
{
|
|
auto si = new ServerInspector(m_database, this);
|
|
addPage(si, tr("Server"));
|
|
}
|
|
|
|
void DatabaseWindow::closeTab(int index)
|
|
{
|
|
if (index < 0)
|
|
return;
|
|
|
|
if (canCloseTab(index)) {
|
|
QWidget *widget = m_tabWidget->widget(index);
|
|
m_tabWidget->removeTab(index);
|
|
delete widget;
|
|
}
|
|
}
|
|
|
|
bool DatabaseWindow::canCloseTab(int index) const
|
|
{
|
|
QWidget *widget = m_tabWidget->widget(index);
|
|
auto mp = dynamic_cast<ManagedPage*>(widget);
|
|
if (mp) {
|
|
return mp->CanClose(true);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void DatabaseWindow::openSqlFile(QString file_name)
|
|
{
|
|
if ( ! file_name.isEmpty()) {
|
|
auto *ct = new QueryTool(this, this);
|
|
if (ct->load(file_name)) {
|
|
ct->addAction(actionExecuteQuery);
|
|
addPage(ct, ct->title());
|
|
}
|
|
else {
|
|
delete ct;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
void DatabaseWindow::catalogLoaded()
|
|
{
|
|
try {
|
|
m_database = loadWatcher.future().result();
|
|
|
|
// for (auto f : { "user", "pg_catalog", "information_schema" }) {
|
|
// // TODO open inspector windows
|
|
// }
|
|
// newCreateTablePage();
|
|
actionNewSql_triggered();
|
|
} catch (const OpenDatabaseException &ex) {
|
|
QMessageBox::critical(this, "Error reading database", ex.text());
|
|
close();
|
|
}
|
|
}
|
|
|
|
void DatabaseWindow::tableSelected(Oid tableoid)
|
|
{
|
|
newCrudPage(tableoid);
|
|
}
|
|
|
|
|
|
void DatabaseWindow::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::actionCancelQuery_triggered()
|
|
{
|
|
auto query_tool = GetActiveQueryTool();
|
|
if (query_tool) {
|
|
query_tool->cancel();
|
|
}
|
|
}
|
|
|
|
void DatabaseWindow::actionClose_triggered()
|
|
{
|
|
m_tabWidget->tabCloseRequested(m_tabWidget->currentIndex());
|
|
}
|
|
|
|
void DatabaseWindow::actionCopy_triggered()
|
|
{
|
|
QWidget *w = QApplication::focusWidget();
|
|
if (w == nullptr)
|
|
return;
|
|
|
|
QTableView *tv = dynamic_cast<QTableView*>(w);
|
|
if (tv)
|
|
copySelectionToClipboard(tv);
|
|
else
|
|
InvokeCopyIfPresent(w);
|
|
}
|
|
|
|
void DatabaseWindow::InvokeCopyIfPresent(QWidget *w)
|
|
{
|
|
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::actionCopyAsCString_triggered()
|
|
{
|
|
auto query_tool = GetActiveQueryTool();
|
|
if (query_tool)
|
|
query_tool->copyQueryAsCString();
|
|
}
|
|
|
|
void DatabaseWindow::actionCopyAsRawCppString_triggered()
|
|
{
|
|
auto query_tool = GetActiveQueryTool();
|
|
if (query_tool)
|
|
query_tool->copyQueryAsRawCppString();
|
|
}
|
|
|
|
void DatabaseWindow::actionExecuteQuery_triggered()
|
|
{
|
|
auto query_tool = GetActiveQueryTool();
|
|
if (query_tool)
|
|
query_tool->execute();
|
|
}
|
|
|
|
void DatabaseWindow::actionExplain_triggered()
|
|
{
|
|
auto query_tool = GetActiveQueryTool();
|
|
if (query_tool)
|
|
query_tool->explain(false);
|
|
}
|
|
|
|
void DatabaseWindow::actionExplainAnalyze_triggered()
|
|
{
|
|
auto query_tool = GetActiveQueryTool();
|
|
if (query_tool)
|
|
query_tool->explain(true);
|
|
}
|
|
|
|
void DatabaseWindow::actionExportData_triggered()
|
|
{
|
|
auto query_tool = GetActiveQueryTool();
|
|
if (query_tool)
|
|
query_tool->exportData();
|
|
}
|
|
|
|
void DatabaseWindow::actionGenerateCode_triggered()
|
|
{
|
|
auto query_tool = GetActiveQueryTool();
|
|
if (query_tool)
|
|
query_tool->generateCode();
|
|
}
|
|
|
|
void DatabaseWindow::actionInspectInformationSchema_triggered()
|
|
{
|
|
newCatalogInspectorPage("information_schema", NamespaceFilter::InformationSchema);
|
|
}
|
|
|
|
void DatabaseWindow::actionInspectPgCatalog_triggered()
|
|
{
|
|
newCatalogInspectorPage("pg_catalog", NamespaceFilter::PgCatalog);
|
|
}
|
|
|
|
void DatabaseWindow::actionInspectUserSchemas_triggered()
|
|
{
|
|
newCatalogInspectorPage("Schema", NamespaceFilter::User);
|
|
}
|
|
|
|
void DatabaseWindow::actionServerInspector_triggered()
|
|
{
|
|
newServerInspectorPage();
|
|
}
|
|
|
|
void DatabaseWindow::actionNewSql_triggered()
|
|
{
|
|
auto *ct = new QueryTool(this, this);
|
|
ct->addAction(actionExecuteQuery);
|
|
addPage(ct, "new");
|
|
ct->newdoc();
|
|
}
|
|
|
|
void DatabaseWindow::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)"));
|
|
openSqlFile(file_name);
|
|
}
|
|
|
|
void DatabaseWindow::actionPasteLangString_triggered()
|
|
{
|
|
auto query_tool = GetActiveQueryTool();
|
|
if (query_tool)
|
|
query_tool->pasteLangString();
|
|
}
|
|
|
|
void DatabaseWindow::actionRefreshCatalog_triggered()
|
|
{
|
|
m_database->refresh();
|
|
}
|
|
|
|
void DatabaseWindow::actionRefreshCrud_triggered()
|
|
{
|
|
auto crud = GetActiveCrud();
|
|
if (crud)
|
|
crud->refresh();
|
|
}
|
|
|
|
void DatabaseWindow::actionSaveSql_triggered()
|
|
{
|
|
auto query_tool = GetActiveQueryTool();
|
|
if (query_tool)
|
|
query_tool->save();
|
|
}
|
|
|
|
void DatabaseWindow::actionSaveSqlAs_triggered()
|
|
{
|
|
auto query_tool = GetActiveQueryTool();
|
|
if (query_tool)
|
|
query_tool->saveAs();
|
|
}
|
|
|
|
void DatabaseWindow::actionSaveCopyOfSqlAs_triggered()
|
|
{
|
|
auto query_tool = GetActiveQueryTool();
|
|
if (query_tool)
|
|
query_tool->saveCopyAs();
|
|
}
|
|
|
|
void DatabaseWindow::actionShowConnectionManager_triggered()
|
|
{
|
|
m_masterController->connectionController()->showConnectionManager();
|
|
}
|
|
|
|
void DatabaseWindow::m_tabWidget_tabCloseRequested(int index)
|
|
{
|
|
closeTab(index);
|
|
}
|
|
|
|
void DatabaseWindow::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);
|
|
}
|
|
|
|
void DatabaseWindow::dragEnterEvent(QDragEnterEvent *event)
|
|
{
|
|
if (event->mimeData()->hasUrls())
|
|
event->acceptProposedAction();
|
|
}
|
|
|
|
void DatabaseWindow::dropEvent(QDropEvent *event)
|
|
{
|
|
foreach (const QUrl &url, event->mimeData()->urls()) {
|
|
QString file_name = url.toLocalFile();
|
|
qDebug() << "Dropped file:" << file_name;
|
|
openSqlFile(file_name);
|
|
}
|
|
}
|