pgLab/pglab/DatabaseWindow.cpp
eelke 15bee33076 Made step to remove ASyncWindow in favour of usage of Future and FutureWatcher.
This should allow concurrency in the plugins to be independent from their container.

Contains also some work on the system for registering plugins.
2018-12-30 15:46:15 +01:00

443 lines
10 KiB
C++

#include "DatabaseWindow.h"
#include "ui_MainWindow.h"
#include "TablesPage.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 "plugin_support/PluginContentWidget.h"
#include "CodeGenerator.h"
#include "MasterController.h"
#include "CrudTab.h"
#include "WorkManager.h"
#include "ScopeGuard.h"
#include "EditTableWidget.h"
#include "IPluginContentWidgetContext.h"
#include "TaskExecutor.h"
namespace pg = Pgsql;
namespace DatabaseWindow_details {
class DatabaseWindowContentContext: public IPluginContentWidgetContext {
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 QueueTask(TSQueue::t_Callable ) override
{
// m_window->QueueTask(c);
}
void showStatusMessage(const QString &msg) override
{
m_window->statusBar()->showMessage(msg);
}
private:
DatabaseWindow *m_window;
};
}
using namespace DatabaseWindow_details;
DatabaseWindow::DatabaseWindow(MasterController *master, QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
, m_context(new DatabaseWindowContentContext(this))
, m_masterController(master)
{
ui->setupUi(this);
ui->tabWidget->setDocumentMode(true);
connect(&loadWatcher, &QFutureWatcher<LoadCatalog::Result>::finished,
this, &DatabaseWindow::catalogLoaded);
}
DatabaseWindow::~DatabaseWindow()
{
delete m_context;
delete ui;
}
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);
ui->tabWidget->addTab(w, "Create table");
}
void DatabaseWindow::newCrudPage(const PgClass &table)
{
CrudTab *ct = new CrudTab(m_context, this);
ct->setConfig(m_database, table);
addPage(ct, table.objectName());
}
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 = ui->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();
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");
auto functions_page = new FunctionsPage(this);
functions_page->setCatalog(m_database->catalog());
ui->tabWidget->addTab(functions_page, "Functions");
auto sequences_page = new SequencesPage(this);
sequences_page->setCatalog(m_database->catalog());
ui->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_actionClose_triggered()
{
//close();
on_tabWidget_tabCloseRequested(ui->tabWidget->currentIndex());
}
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 = ui->tabWidget->widget(index);
PluginContentWidget *plg_page = dynamic_cast<PluginContentWidget*>(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();
}
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);
}
ui->mainToolBar->addActions(list);
}
void DatabaseWindow::removeToolBarButtonsForPage(PluginContentWidget *page)
{
std::vector<QAction*> actions = page->getToolbarActions();
for (auto act : actions) {
ui->mainToolBar->removeAction(act);
}
}
void DatabaseWindow::addPage(PluginContentWidget* page, QString caption)
{
ui->tabWidget->addTab(page, caption);
ui->tabWidget->setCurrentWidget(page);
//addToolBarButtonsForPage(page);
}
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 = ui->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();
}
}
void DatabaseWindow::setTabCaptionForWidget(QWidget *widget, const QString &caption, const QString &hint)
{
auto index = ui->tabWidget->indexOf(widget);
ui->tabWidget->setTabText(index, caption);
ui->tabWidget->setTabToolTip(index, hint);
}
void DatabaseWindow::setTabIcon(QWidget *widget, const QString &iconname)
{
auto index = ui->tabWidget->indexOf(widget);
auto n = ":/icons/16x16/" + iconname;
ui->tabWidget->setTabIcon(index, QIcon(n));
}