pgLab/pglab/DatabaseWindow.cpp
eelke fd603a7434 Moving things from the application specific DatabaseWindow to generic LMainWindow (Leon framework)
To achieve flexibility the getDatabase call on the context which was application specific to
has been replaced with a type based object registry.
2019-01-05 09:49:12 +01:00

519 lines
12 KiB
C++

#include "DatabaseWindow.h"
//#include "ui_DatabaseWindow.h"
#include "TablesPage.h"
#include "FunctionsPage.h"
#include "SequencesPage.h"
#include "QueryTab.h"
#include "util.h"
#include "plugin_support/PluginContentWidget.h"
#include "plugin_support/PluginContentWidgetContextBase.h"
#include "CodeGenerator.h"
#include "MasterController.h"
#include "ScopeGuard.h"
#include "EditTableWidget.h"
#include "TaskExecutor.h"
#include <QApplication>
#include <QCloseEvent>
#include <QElapsedTimer>
#include <QFileDialog>
#include <QMenuBar>
#include <QMessageBox>
#include <QMetaMethod>
#include <QMetaObject>
#include <QStandardPaths>
#include <QStatusBar>
#include <QTextTable>
#include <QToolBar>
#include <QVBoxLayout>
#include <algorithm>
namespace pg = Pgsql;
namespace DatabaseWindow_details {
class DatabaseWindowContentContext: public PluginContentWidgetContextBase {
public:
explicit DatabaseWindowContentContext(DatabaseWindow *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);
}
// std::shared_ptr<OpenDatabase> getDatabase() override
// {
// return m_window->getDatabase();
// }
void showStatusMessage(const QString &msg) override
{
m_window->statusBar()->showMessage(msg);
}
void addContentWidget(PluginContentWidget *widget) override
{
m_window->addPage(widget, "");
}
private:
DatabaseWindow *m_window;
};
}
using namespace DatabaseWindow_details;
LMainWindow::LMainWindow(QWidget *parent)
: QMainWindow(parent)
{
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 T", this);
createActions();
m_mainToolBar->addAction(m_closeAction);
// QMetaObject::connectSlotsByName(this);
}
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, &DatabaseWindow::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));
}
DatabaseWindow::DatabaseWindow(MasterController *master, QWidget *parent)
: LMainWindow(parent)
, m_masterController(master)
{
m_context = new DatabaseWindowContentContext(this);
connect(&loadWatcher, &QFutureWatcher<LoadCatalog::Result>::finished,
this, &DatabaseWindow::catalogLoaded);
initModuleMenus();
QMetaObject::connectSlotsByName(this);
}
DatabaseWindow::~DatabaseWindow()
{
delete m_context;
}
QueryTab* DatabaseWindow::newSqlPage()
{
QueryTab *qt = new QueryTab(m_context);
qt->newdoc();
qt->focusEditor();
addPage(qt, "Tab");
return qt;
}
void DatabaseWindow::newCreateTablePage()
{
auto w = new EditTableWidget(m_database, this);
m_tabWidget->addTab(w, "Create table");
}
void DatabaseWindow::newCodeGenPage(QString query, std::shared_ptr<const Pgsql::Result> dbres)
{
auto cgtab = new CodeGenerator(m_context, this);
cgtab->Init(m_database->catalog(), query, dbres);
addPage(cgtab, "Codegen");
}
QueryTab *DatabaseWindow::GetActiveQueryTab()
{
QWidget *widget = m_tabWidget->currentWidget();
QueryTab *qt = dynamic_cast<QueryTab*>(widget);
return qt;
}
void DatabaseWindow::setConfig(const ConnectionConfig &config)
{
m_config = config;
try {
QString title = "pglab - ";
title += m_config.name().c_str();
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::catalogLoaded()
{
try {
//SCOPE_EXIT { loadFuture = {}; };
m_database = loadWatcher.future().result();
m_context->registerObject(m_database);
auto tt = new TablesPage(m_context, this);
tt->setCatalog(m_database->catalog());
m_tabWidget->addTab(tt, "Tables");
auto pg_cat_tables = new TablesPage(m_context, this);
pg_cat_tables->setNamespaceFilter(TablesTableModel::PgCatalog);
pg_cat_tables->setCatalog(m_database->catalog());
m_tabWidget->addTab(pg_cat_tables, "pg_catalog");
auto info_schema_tables = new TablesPage(m_context, this);
info_schema_tables->setNamespaceFilter(TablesTableModel::InformationSchema);
info_schema_tables->setCatalog(m_database->catalog());
m_tabWidget->addTab(info_schema_tables, "information_schema");
auto functions_page = new FunctionsPage(this);
functions_page->setCatalog(m_database->catalog());
m_tabWidget->addTab(functions_page, "Functions");
auto sequences_page = new SequencesPage(this);
sequences_page->setCatalog(m_database->catalog());
m_tabWidget->addTab(sequences_page, "Sequences");
newSqlPage();
newCreateTablePage();
} catch (std::runtime_error &ex) {
QMessageBox::critical(this, "Error reading database",
QString::fromUtf8(ex.what()));
close();
}
}
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_actionAbout_triggered()
{
QMessageBox::about(this, "pgLab 0.1", tr(
"Copyrights 2016-2018, 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::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 = m_tabWidget->widget(index);
PluginContentWidget *plg_page = dynamic_cast<PluginContentWidget*>(widget);
if (plg_page) {
if (plg_page->canClose()) {
removePage(plg_page);
m_tabWidget->removeTab(index);
delete plg_page;
}
}
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()) {
m_tabWidget->removeTab(index);
delete qt;
}
else if (index > 0) {
m_tabWidget->removeTab(index);
delete widget;
}
}
}
void DatabaseWindow::on_actionShow_connection_manager_triggered()
{
m_masterController->showConnectionManager();
}
void DatabaseWindow::on_actionCopy_triggered()
{
// What should be copied?
QWidget *w = QApplication::focusWidget();
QTableView *tv = dynamic_cast<QTableView*>(w);
if (tv) {
copySelectionToClipboard(tv);
}
else {
const QMetaObject *meta = w->metaObject();
int i = meta->indexOfSlot("copy");
if (i != -1) {
QMetaMethod method = meta->method(i);
method.invoke(w, Qt::AutoConnection);
}
}
}
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(PluginContentWidget *page)
{
std::vector<QAction*> actions = page->getToolbarActions();
QList<QAction*> list;
for (auto act : actions) {
list.append(act);
}
m_mainToolBar->addActions(list);
}
void DatabaseWindow::removeToolBarButtonsForPage(PluginContentWidget *page)
{
std::vector<QAction*> actions = page->getToolbarActions();
for (auto act : actions) {
m_mainToolBar->removeAction(act);
}
}
void DatabaseWindow::removePage(PluginContentWidget *)
{
}
void DatabaseWindow::on_tabWidget_currentChanged(int index)
{
// remove buttons of old page
if (m_previousPage) {
removeToolBarButtonsForPage(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) {
addToolBarButtonsForPage(page);
}
}
m_previousPage = page;
}
void DatabaseWindow::on_actionGenerate_code_triggered()
{
QueryTab *tab = GetActiveQueryTab();
if (tab) {
tab->generateCode();
}
}