#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 #include #include #include #include #include #include #include #include #include #include #include #include "EditTableWidget.h" #include "CodeGenerator.h" #include "QueryTool.h" #include namespace pg = Pgsql; DatabaseWindow::DatabaseWindow(MasterController *master, QWidget *parent) : QMainWindow(parent) , m_masterController(master) { 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 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(widget); return qt; } CrudTab *DatabaseWindow::GetActiveCrud() { auto widget = m_tabWidget->currentWidget(); auto ct = dynamic_cast(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 cfg = m_config; auto qthis = QPointer(this); QtConcurrent::run([cfg] { return OpenDatabase::createOpenDatabase(cfg); }).then(qApp, [qthis](OpenDatabase::OpenDatabaseSPtr db) { if (qthis) { qthis.data()->catalogLoaded(db); } }); } 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) { if (!m_database) return; // would be better if we queued the operation for later 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(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(OpenDatabase::OpenDatabaseSPtr db) { try { m_database = db; 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(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(widget); auto ct = dynamic_cast(widget); auto ci = dynamic_cast(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 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); } }