From d19741f111172e18ed0ffa396947185ca497ddce Mon Sep 17 00:00:00 2001 From: Eelke Klein Date: Sun, 15 Jan 2017 21:01:40 +0100 Subject: [PATCH] Connection manager can now open a query window for selected connection. Query window has now buttons with icons made in the designer for better looks. Depending on received responses from the database the tabcontrol with the message, data and explain tab now switches to the appropriate tab. --- .gitignore | 3 +- PgsqlConn.cpp | 7 +++ PgsqlConn.h | 24 +++++----- asyncdbconnection.cpp | 19 ++++++-- asyncdbconnection.h | 7 ++- connectionconfig.cpp | 24 +++++----- connectionlistmodel.cpp | 35 +++++++++++++- connectionlistmodel.h | 4 ++ connectionmanagerwindow.cpp | 37 +++++++++++---- connectionmanagerwindow.h | 4 ++ connectionmanagerwindow.ui | 12 +++++ databasewindow.cpp | 91 +++++++++++++++++++++++++++++++++++- databasewindow.h | 23 ++++++++- databasewindow.ui | 5 ++ expected.h | 77 +++++++++++++++--------------- icons/desktop.ini | 9 ++++ icons/folder.png | Bin 0 -> 1086 bytes icons/lightbulb.png | Bin 0 -> 1496 bytes icons/script_delete.png | Bin 0 -> 1507 bytes icons/script_go.png | Bin 0 -> 1347 bytes icons/script_save.png | Bin 0 -> 1409 bytes icons/table_save.png | Bin 0 -> 1437 bytes mainwindow.cpp | 58 ++++++++++++++++------- mainwindow.h | 20 ++++---- mainwindow.ui | 57 ++++++++++++++++++++-- resources.qrc | 8 +++- 26 files changed, 408 insertions(+), 116 deletions(-) create mode 100644 icons/folder.png create mode 100644 icons/lightbulb.png create mode 100644 icons/script_delete.png create mode 100644 icons/script_go.png create mode 100644 icons/script_save.png create mode 100644 icons/table_save.png diff --git a/.gitignore b/.gitignore index 6433272..d09de83 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,5 @@ release/ DIST/ Makefile Makefile.Debug -Makefile.Release \ No newline at end of file +Makefile.Release +desktop.ini diff --git a/PgsqlConn.cpp b/PgsqlConn.cpp index a0037cf..8a73292 100644 --- a/PgsqlConn.cpp +++ b/PgsqlConn.cpp @@ -236,6 +236,13 @@ bool Connection::connectStart(const char* params) return conn != nullptr; } +bool Connection::connectStart(const char * const *keywords, + const char * const *values) +{ + conn = PQconnectStartParams(keywords, values, 0); + return conn != nullptr; +} + PostgresPollingStatusType Connection::connectPoll() { return PQconnectPoll(conn); diff --git a/PgsqlConn.h b/PgsqlConn.h index 1826f96..c92797d 100644 --- a/PgsqlConn.h +++ b/PgsqlConn.h @@ -19,18 +19,18 @@ namespace Pgsql { */ - class ConnectionParams { - public: - std::string host; - std::string hostaddr; - unsigned short port = 5432; - std::string dbname; - std::string user; - std::string password; - int connect_timeout = -1; ///< -1 omit (ie uses default) - std::string application_name; +// class ConnectionParams { +// public: +// std::string host; +// std::string hostaddr; +// unsigned short port = 5432; +// std::string dbname; +// std::string user; +// std::string password; +// int connect_timeout = -1; ///< -1 omit (ie uses default) +// std::string application_name; - }; +// }; class ErrorDetails { public: @@ -172,6 +172,8 @@ namespace Pgsql { { return connectStart(params.toUtf8().data()); } + bool connectStart(const char * const *keywords, + const char * const *values); PostgresPollingStatusType connectPoll(); ConnStatusType status(); diff --git a/asyncdbconnection.cpp b/asyncdbconnection.cpp index da5e3cc..dd685bd 100644 --- a/asyncdbconnection.cpp +++ b/asyncdbconnection.cpp @@ -7,13 +7,23 @@ ASyncDBConnection::ASyncDBConnection() } -void ASyncDBConnection::setupConnection(const std::string &connstring) +//void ASyncDBConnection::setupConnection(const std::string &connstring) +//{ +// if (m_thread.joinable()) { +// m_threadData.stop(); +// m_thread.join(); +// } +// m_threadData.m_initString = connstring; +// m_thread = std::thread([this] () { m_threadData.run(); }); +//} + +void ASyncDBConnection::setupConnection(const ConnectionConfig &config) { if (m_thread.joinable()) { m_threadData.stop(); m_thread.join(); } - m_threadData.m_initString = connstring; + m_threadData.m_config = config; m_thread = std::thread([this] () { m_threadData.run(); }); } @@ -100,7 +110,10 @@ bool ASyncDBConnection::Thread::makeConnection() while (!terminateRequested) { // start connecting - bool ok = m_connection.connectStart(m_initString + " client_encoding=utf8"); + //bool ok = m_connection.connectStart(m_initString + " client_encoding=utf8"); + auto keywords = m_config.getKeywords(); + auto values = m_config.getValues(); + bool ok = m_connection.connectStart(keywords, values); auto start = std::chrono::steady_clock::now(); if (ok && m_connection.status() != CONNECTION_BAD) { int sock = m_connection.socket(); diff --git a/asyncdbconnection.h b/asyncdbconnection.h index e9b9425..2e78dc8 100644 --- a/asyncdbconnection.h +++ b/asyncdbconnection.h @@ -3,6 +3,7 @@ #include "PgsqlConn.h" #include "win32event.h" +#include "connectionconfig.h" #include #include #include @@ -26,7 +27,8 @@ public: ASyncDBConnection(); - void setupConnection(const std::string &connstring); +// void setupConnection(const std::string &connstring); + void setupConnection(const ConnectionConfig &config); void closeConnection(); void setStateCallback(on_state_callback state_callback); @@ -77,7 +79,8 @@ private: {} } m_commandQueue; - std::string m_initString; +// std::string m_initString; + ConnectionConfig m_config; Thread(); diff --git a/connectionconfig.cpp b/connectionconfig.cpp index bce1b6b..68fc714 100644 --- a/connectionconfig.cpp +++ b/connectionconfig.cpp @@ -53,7 +53,6 @@ std::vector ConnectionConfig::s_keywords = { ConnectionConfig::ConnectionConfig() : m_applicationName(QCoreApplication::applicationName().toUtf8().data()) - , m_values(s_keywords.size(), nullptr) {} void ConnectionConfig::setName(std::string desc) @@ -69,7 +68,6 @@ const std::string& ConnectionConfig::name() const void ConnectionConfig::setHost(std::string host) { m_host = std::move(host); - m_values[0] = valuePtr(m_host); } const std::string& ConnectionConfig::host() const @@ -80,7 +78,6 @@ const std::string& ConnectionConfig::host() const void ConnectionConfig::setHostAddr(std::string v) { m_hostaddr = std::move(v); - m_values[1] = valuePtr(m_hostaddr); } const std::string& ConnectionConfig::hostAddr() const @@ -91,7 +88,6 @@ const std::string& ConnectionConfig::hostAddr() const void ConnectionConfig::setPort(unsigned short port) { m_port = std::to_string(port); - m_values[2] = valuePtr(m_port); } unsigned short ConnectionConfig::port() const @@ -102,7 +98,6 @@ unsigned short ConnectionConfig::port() const void ConnectionConfig::setUser(std::string v) { m_user = std::move(v); - m_values[3] = valuePtr(m_user); } const std::string& ConnectionConfig::user() const @@ -113,7 +108,6 @@ const std::string& ConnectionConfig::user() const void ConnectionConfig::setPassword(std::string v) { m_password = std::move(v); - m_values[4] = valuePtr(m_password); } const std::string& ConnectionConfig::password() const @@ -124,7 +118,6 @@ const std::string& ConnectionConfig::password() const void ConnectionConfig::setDbname(std::string v) { m_dbname = std::move(v); - m_values[5] = valuePtr(m_dbname); } const std::string& ConnectionConfig::dbname() const @@ -135,7 +128,6 @@ const std::string& ConnectionConfig::dbname() const void ConnectionConfig::setSslMode(SslMode m) { m_sslMode = SslModeToString(m); - m_values[6] = valuePtr(m_sslMode); } SslMode ConnectionConfig::sslMode() const @@ -146,7 +138,6 @@ SslMode ConnectionConfig::sslMode() const void ConnectionConfig::setSslCert(std::string v) { m_sslCert = std::move(v); - m_values[7] = valuePtr(m_sslCert); } const std::string& ConnectionConfig::sslCert() const @@ -157,7 +148,6 @@ const std::string& ConnectionConfig::sslCert() const void ConnectionConfig::setSslKey(std::string v) { m_sslKey = std::move(v); - m_values[8] = valuePtr(m_sslKey); } const std::string& ConnectionConfig::sslKey() const @@ -168,7 +158,6 @@ const std::string& ConnectionConfig::sslKey() const void ConnectionConfig::setSslRootCert(std::string v) { m_sslRootCert = std::move(v); - m_values[9] = valuePtr(m_sslRootCert); } const std::string& ConnectionConfig::sslRootCert() const @@ -179,7 +168,6 @@ const std::string& ConnectionConfig::sslRootCert() const void ConnectionConfig::setSslCrl(std::string v) { m_sslCrl = std::move(v); - m_values[10] = valuePtr(m_sslCrl); } const std::string& ConnectionConfig::sslCrl() const @@ -195,6 +183,18 @@ const char * const * ConnectionConfig::getKeywords() const const char * const * ConnectionConfig::getValues() const { + m_values.resize(s_keywords.size(), nullptr); + m_values[0] = valuePtr(m_host); + m_values[1] = valuePtr(m_hostaddr); + m_values[2] = valuePtr(m_port); + m_values[3] = valuePtr(m_user); + m_values[4] = valuePtr(m_password); + m_values[5] = valuePtr(m_dbname); + m_values[6] = valuePtr(m_sslMode); + m_values[7] = valuePtr(m_sslCert); + m_values[8] = valuePtr(m_sslKey); + m_values[9] = valuePtr(m_sslRootCert); + m_values[10] = valuePtr(m_sslCrl); m_values[11] = "utf8"; m_values[12] = valuePtr(m_applicationName); diff --git a/connectionlistmodel.cpp b/connectionlistmodel.cpp index 30f567c..7b7e02c 100644 --- a/connectionlistmodel.cpp +++ b/connectionlistmodel.cpp @@ -66,7 +66,11 @@ ConnectionListModel::ConnectionListModel(QObject *parent) int ConnectionListModel::rowCount(const QModelIndex &parent) const { - return m_connections.size(); + int result = 0; + if (parent == QModelIndex()) { + result = m_connections.size(); + } + return result; } int ConnectionListModel::columnCount(const QModelIndex &/*parent*/) const @@ -150,7 +154,12 @@ bool ConnectionListModel::setData(const QModelIndex &index, const QVariant &valu Qt::ItemFlags ConnectionListModel::flags(const QModelIndex &index) const { - return Qt::ItemIsSelectable | Qt::ItemIsEditable | Qt::ItemIsEnabled; + Qt::ItemFlags result; + int row = index.row(); + if (row >= 0 && row < m_connections.size()) { + result = Qt::ItemIsSelectable | Qt::ItemIsEditable | Qt::ItemIsEnabled; + } + return result; } @@ -176,6 +185,28 @@ void ConnectionListModel::add(const ConnectionConfig &cfg) emit dataChanged(idx, idx); } +Expected ConnectionListModel::get(int row) +{ + if (row >= 0 && row < m_connections.size()) { + return m_connections.at(row).m_config; + } + else { + return Expected::fromException(std::out_of_range("Invalid row")); + } +} + +//void ConnectionListModel::del(const int idx) +bool ConnectionListModel::removeRows(int row, int count, const QModelIndex &parent) +{ + bool result = false; + if (row >= 0 && row < m_connections.size()) { + auto f = m_connections.begin() + row; + m_connections.erase(f, f + count); + result = true; + } + return result; +} + /// \todo should return an expected as creation of the folder can fail QString ConnectionListModel::iniFileName() { diff --git a/connectionlistmodel.h b/connectionlistmodel.h index 8dfce0e..4397b63 100644 --- a/connectionlistmodel.h +++ b/connectionlistmodel.h @@ -8,6 +8,7 @@ #include #include "connectionconfig.h" +#include "expected.h" class ConnectionListModel : public QAbstractListModel { @@ -23,9 +24,12 @@ public: virtual Qt::ItemFlags flags(const QModelIndex &index) const override; void add(const ConnectionConfig &cfg); + Expected get(int row); + virtual bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex()) override; void load(); void save(); + private: class LijstElem { public: diff --git a/connectionmanagerwindow.cpp b/connectionmanagerwindow.cpp index b7db899..c36850a 100644 --- a/connectionmanagerwindow.cpp +++ b/connectionmanagerwindow.cpp @@ -1,6 +1,8 @@ #include "connectionmanagerwindow.h" #include "ui_connectionmanagerwindow.h" +#include "mainwindow.h" #include +#include #include #include "connectionlistmodel.h" @@ -42,24 +44,15 @@ void ConnectionManagerWindow::on_currentChanged(const QModelIndex ¤t, const QModelIndex &previous) { int currow = current.row(); -// int prevrow = previous.row(); m_mapper->setCurrentIndex(currow); -// ui->lineEdit->setText(QString::number(currow)); -// ui->lineEdit_2->setText(QString::number(prevrow)); - -// if(selection.indexes().isEmpty()) { -// clearMyView(); -// } else { -// displayModelIndexInMyView(selection.indexes().first()); -// } } void ConnectionManagerWindow::on_actionDelete_connection_triggered() { auto ci = ui->listView->selectionModel()->currentIndex(); if (ci.isValid()) { - //m_listModel->removeRow(ci.row()); + m_listModel->removeRow(ci.row()); } } @@ -76,3 +69,27 @@ void ConnectionManagerWindow::setupWidgetMappings() m_mapper->addMapping(ui->edtDbname, 6); m_mapper->toFirst(); } + +void ConnectionManagerWindow::on_actionConnect_triggered() +{ + // Open a window for this connection, maybe we should first check the connection? + auto ci = ui->listView->selectionModel()->currentIndex(); + auto cc = m_listModel->get(ci.row()); + if (cc.valid()) { + auto w = new MainWindow; + w->setAttribute( Qt::WA_DeleteOnClose ); + w->setConfig(cc.get()); + w->show(); + } +} + +void ConnectionManagerWindow::on_actionQuit_application_triggered() +{ + auto res = QMessageBox::question(this, "pglab", + tr("Close ALL windows?"), QMessageBox::Yes, QMessageBox::No); + if (res == QMessageBox::Yes) { + QApplication::quit(); + } + + //closeAllWindows(); +} diff --git a/connectionmanagerwindow.h b/connectionmanagerwindow.h index b6145be..da73943 100644 --- a/connectionmanagerwindow.h +++ b/connectionmanagerwindow.h @@ -25,6 +25,10 @@ private slots: void on_currentChanged(const QModelIndex ¤t, const QModelIndex &previous); void on_actionDelete_connection_triggered(); + void on_actionConnect_triggered(); + + void on_actionQuit_application_triggered(); + private: Ui::ConnectionManagerWindow *ui; ConnectionListModel *m_listModel = nullptr; diff --git a/connectionmanagerwindow.ui b/connectionmanagerwindow.ui index 671dd7c..33042f3 100644 --- a/connectionmanagerwindow.ui +++ b/connectionmanagerwindow.ui @@ -197,6 +197,13 @@ 25 + + + File + + + + @@ -250,6 +257,11 @@ Connect + + + Quit application + + diff --git a/databasewindow.cpp b/databasewindow.cpp index 1dda978..949a7fd 100644 --- a/databasewindow.cpp +++ b/databasewindow.cpp @@ -1,14 +1,103 @@ -#include "databasewindow.h" +#include "databasewindow.h" #include "ui_databasewindow.h" +#include DatabaseWindow::DatabaseWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::DatabaseWindow) { ui->setupUi(this); + + m_dbConnection.setStateCallback([this](ASyncDBConnection::State st) + { + QueueTask([this, st]() { connectionStateChanged(st); }); + }); + + m_dbConnection.setNoticeCallback([this](Pgsql::ErrorDetails details) + { + QueueTask([this, details]() { receiveNotice(details); }); + }); } DatabaseWindow::~DatabaseWindow() { + m_dbConnection.closeConnection(); + m_dbConnection.setStateCallback(nullptr); delete ui; } + +void DatabaseWindow::setConfig(const ConnectionConfig &config) +{ + m_config = config; + QString title = "pglab - "; + title += m_config.name().c_str(); + setWindowTitle(title); + QueueTask([this]() { startConnect(); }); +} + +void DatabaseWindow::QueueTask(TSQueue::t_Callable c) +{ + m_taskQueue.add(c); + // Theoretically this needs to be only called if the queue was empty because otherwise it already would + // be busy emptying the queue. For now however I think it is safer to call it just to make sure. + QMetaObject::invokeMethod(this, "processCallableQueue", Qt::QueuedConnection); // queues on main thread +} + +void DatabaseWindow::processCallableQueue() +{ + if (!m_taskQueue.empty()) { + auto c = m_taskQueue.pop(); + c(); + if (!m_taskQueue.empty()) { + QTimer::singleShot(0, this, SLOT(processCallableQueue())); + } + } +} + +void DatabaseWindow::startConnect() +{ + m_dbConnection.setupConnection(m_config); +} + +void DatabaseWindow::connectionStateChanged(ASyncDBConnection::State state) +{ + QString status_str; + switch (state) { + case ASyncDBConnection::State::NotConnected: + status_str = tr("Geen verbinding"); + break; + case ASyncDBConnection::State::Connecting: + status_str = tr("Verbinden"); + break; + case ASyncDBConnection::State::Connected: + status_str = tr("Verbonden"); + break; + case ASyncDBConnection::State::QuerySend: + status_str = tr("Query verstuurd"); + break; + case ASyncDBConnection::State::CancelSend: + status_str = tr("Query geannuleerd"); + break; + } +// addLog(status_str); + statusBar()->showMessage(status_str); +} + + +void DatabaseWindow::receiveNotice(Pgsql::ErrorDetails notice) +{ +// QTextCursor cursor = ui->messagesEdit->textCursor(); +// cursor.movePosition(QTextCursor::End, QTextCursor::MoveAnchor); + +// QTextTable *table = cursor.insertTable(4, 2); +// if (table) { +// table->cellAt(1, 0).firstCursorPosition().insertText("State"); +// table->cellAt(1, 1).firstCursorPosition().insertText(QString::fromStdString(notice.state)); +// table->cellAt(2, 0).firstCursorPosition().insertText("Primary"); +// table->cellAt(2, 1).firstCursorPosition().insertText(QString::fromStdString(notice.messagePrimary)); +// table->cellAt(3, 0).firstCursorPosition().insertText("Detail"); +// table->cellAt(3, 1).firstCursorPosition().insertText(QString::fromStdString(notice.messageDetail)); +// } + +} + diff --git a/databasewindow.h b/databasewindow.h index 98ddda3..6addd3d 100644 --- a/databasewindow.h +++ b/databasewindow.h @@ -1,8 +1,11 @@ -#ifndef DATABASEWINDOW_H +#ifndef DATABASEWINDOW_H #define DATABASEWINDOW_H +#include "asyncdbconnection.h" +#include "tsqueue.h" #include + namespace Ui { class DatabaseWindow; } @@ -15,8 +18,26 @@ public: explicit DatabaseWindow(QWidget *parent = 0); ~DatabaseWindow(); + void setConfig(const ConnectionConfig &config); + /* Meant to be called from other threads to pass a code block + * that has to be executed in the context of the thread of the window. + */ + void QueueTask(TSQueue::t_Callable c); + private: Ui::DatabaseWindow *ui; + TSQueue m_taskQueue; + ASyncDBConnection m_dbConnection; + ConnectionConfig m_config; + + + void connectionStateChanged(ASyncDBConnection::State state); + void receiveNotice(Pgsql::ErrorDetails notice); + + void startConnect(); +private slots: + + void processCallableQueue(); }; #endif // DATABASEWINDOW_H diff --git a/databasewindow.ui b/databasewindow.ui index 62d1c1d..516bae8 100644 --- a/databasewindow.ui +++ b/databasewindow.ui @@ -21,6 +21,11 @@ Tab 1 + + + + + diff --git a/expected.h b/expected.h index 18de30d..9beedf6 100644 --- a/expected.h +++ b/expected.h @@ -4,77 +4,78 @@ template class Expected { union { - T ham; - std::exception_ptr spam; + T m_value; + std::exception_ptr m_error; }; - bool gotHam; + bool m_valid; Expected() {} // internal use public: Expected(const T& rhs) - : ham(rhs), gotHam(true) + : m_value(rhs), m_valid(true) {} Expected(T&& rhs) - : ham(std::move(rhs)) - , gotHam(true) + : m_value(std::move(rhs)) + , m_valid(true) {} + Expected(const Expected& ths) - : gotHam(rhs.gotHam) + : m_valid(rhs.valid) { - if (gotHam) { - new (&ham) T(rhs.ham); + if (m_valid) { + new (&m_value) T(rhs.ham); } else { - new (&spam) std::exception_ptr(rhs.spam); + new (&m_error) std::exception_ptr(rhs.spam); } } Expected(Expected &&rhs) - : gotHam(rhs.getHam) + : m_valid(rhs.m_valid) { - if (gotHam) { - new (&ham) T(std::move(rhs.ham)); + if (m_valid) { + new (&m_value) T(std::move(rhs.m_value)); } else { - new (&spam) std::exception_ptr(std::move(rhs.spam)); + new (&m_error) std::exception_ptr(std::move(rhs.m_error)); } } ~Expected() { - if (gotHam) { - ham.~T(); + if (m_valid) { + m_value.~T(); } else { using std::exception_ptr; - spam.~exception_ptr(); + m_error.~exception_ptr(); } } void swap(Expected& rhs) { - if (gotHam) { - if (rhs.gotHam) { + if (m_valid) { + if (rhs.m_valid) { using std::swamp; - swap(ham, rhs.ham); + swap(m_value, rhs.m_value); } else { - auto t = std::move(rhs.spam); - new(&rhs.ham) T(std::move(ham)); - new(&spam) std::exception_ptr(t); - std::swap(gotHam, rhs.getHam); + auto t = std::move(rhs.m_error); + new(&rhs.m_value) T(std::move(m_value)); + new(&m_error) std::exception_ptr(t); + std::swap(m_valid, rhs.getHam); } } else { - if (rhs.gotHam) { + if (rhs.m_valid) { rhs.swap(*this); } else { - spam.swap(rhs.spam); - std::swap(gotHam, rhs.gotHam); + m_error.swap(rhs.m_error); + std::swap(m_valid, rhs.m_valid); } } } @@ -91,8 +92,8 @@ public: static Expected fromException(std::exception_ptr p) { Expected result; - result.gotHam = false; - new (&result.spam) std::exception_ptr(std::move(p)); + result.m_valid = false; + new (&result.m_error) std::exception_ptr(std::move(p)); return result; } @@ -103,31 +104,31 @@ public: bool valid() const { - return gotHam; + return m_valid; } T& get() { - if (!gotHam) { - std::rethrow_exception(spam); + if (!m_valid) { + std::rethrow_exception(m_error); } - return ham; + return m_value; } const T& get() const { - if (!gotHam) { - std::rethrow_exception(spam); + if (!m_valid) { + std::rethrow_exception(m_error); } - return ham; + return m_value; } template bool hasException() const { try { - if (!gotHam) { - std::rethrow_exception(spam); + if (!m_valid) { + std::rethrow_exception(m_error); } } catch (const E& object) { diff --git a/icons/desktop.ini b/icons/desktop.ini index dd2bbf0..a0f2f95 100644 --- a/icons/desktop.ini +++ b/icons/desktop.ini @@ -2,3 +2,12 @@ server_go.png=@server_go.png,0 server_add.png=@server_add.png,0 server_delete.png=@server_delete.png,0 +script_lightning.png=@script_lightning.png,0 +script_delete.png=@script_delete.png,0 +script_go.png=@script_go.png,0 +database.png=@database.png,0 +script.png=@script.png,0 +folder.png=@folder.png,0 +script_save.png=@script_save.png,0 +lightbulb.png=@lightbulb.png,0 +table_save.png=@table_save.png,0 diff --git a/icons/folder.png b/icons/folder.png new file mode 100644 index 0000000000000000000000000000000000000000..f37bb3efebe42b1ac7b96c552f115d5a07e58180 GIT binary patch literal 1086 zcmV-E1i|}>P)hPdN^ks^x1!O4u;Z4-8&9zz0@X08Gvobnu-YeZg(HuM%xw8uWU( zg1JYxege;5=x7fB6(%kQMq=)wc-SNq^N%4N;-ms&P)ZAjVGkKE`r+|A5> zuY)#vfZc8;n}an!i`^d|BVctC#(`z&$vc5ER1zhXEw{S}vGO^fMcb6e&=GbO~jqKK{C46F19V@E?e zu|0pt*m@K*S1@#X^lMFjV<(h0a(`t=|2Beg_$hd;`9Eg6Sbq_Tvs$YtWFMA?)5Rw~ z13m!Z7C4HJbLf8~L1iYrjwr&quQ<#;j;Yi=q$g(2``Ozy0J^r-T%N~m1syMM;`edJ z4l8@#Gm$R(>k6*k&!1VyzJY<#qb$~L>2Xgmy1}O(q3AEnPPe3sV@V8sH}dm>{&fae z%ah3zUg~>=t69blGkcCmCuY)r;&HzGgBu`lDXB@2Po~lPHrsQV=F^D79Hf6Hox-hT z>71Xv3KsN+-%jowl~7#{uVs#RaR~S3%LHk|!oZPBySU5*J)i-+$l1pk5103*Mv&)B zaZY=ExekDrKxodR&-gDu#n-=z|I-l9|D3KifPV!T0RL}b>2q6X2><{907*qoM6N<$ Ef?F#E#sB~S literal 0 HcmV?d00001 diff --git a/icons/lightbulb.png b/icons/lightbulb.png new file mode 100644 index 0000000000000000000000000000000000000000..17181a9b97241d120176f63a9468340dece8be43 GIT binary patch literal 1496 zcmV;}1t?gFiw?W^Y5;PtNLC@jnBfQh+$+V1xT_& zCSwN7!8W8qf8xxk5Q1Z=)sMg4zGp&kDql_%9&do}%6 z5DNn4v}Dx8znIse0R4lziBmz8oX&wL;9MN~bVaMLrV15a4^Vjz`PG2&5ReW7ndr5| zYs9l;NS1Y2mu(dCQz!#2zgJm1(_clYa}rUSGi2PzUSC_2(?EP%mULLxKN4O`jLk%e zk^#sen`RzgJeP5!Md^-sAF5@0B}l@34;t7^hJ6UrW&AJ zN>?&4QB+q?j6}|##1{C^rMVZ~8YsoIBCceR2JzzYdY%1ZsViR;ou5`7_T2%5$Np z7LR3Ec2~#Qb*#%a%B=w&eB+G1w4u9g&oAc*L_2bZz^uRw4C$19p%S?;6j#yc_gXxb z+578-^>1zK<$EAx+d#PhO#31ker;pV*8@TQM=f{~{z^9%HPzD@O+bqGV~P9|0@VAp z;3=@onvag{B%PqGYu*e1*!Ovy4esw6{yZ5zPf=5~o7@RuUCjoW;Y_4sL9`zk{(@vL z*{#>Sk?~>xTmiA6t?y_qahbH#|6f890njeu{T+SXHejO|!^HxasE348${#_B8Qf)s z)v{?$XJ5(p)x?eVy_5&Z_4Q!59ZASe2TgPIPdB5Sv94&L((R|FC$eQ0Yg8)XRV;7+ z{=l%TdIKsQIk)OTI-5f@8Y7BA*JFqiY2lH5+dp`7HOXg-c@>wE0Z;JKP(V?_kQEnP ziU-+j26{|KJP}7SPTzO}dOWt0WT_=0cB^Xu@4Wo{Lk+clG~H2$L`p|Clf~TGjcBZ` zK}^@^X&%O|t?xZhirrE&z{NFk`s_JMzZ*-JETtCgK-bZumh(BY@5GpgyROxGleYr^ zSD3zF-dt3;XP~dYAA;Tp3!0k|4u{eE$M4Xh8tm4az8wIpIy&BaqO-cD{?Vn2@25Nq z9O>ynET*Ap)@)ob20BYQ0VUS}U#nZ*UALy%TiGdufWzs;!j={^&%Fzgu~6q{8{b}2 z=<2=MV>pqqDx#*<&%FAH*B5x0f#_PK>$|Tuv2%W-MW#A}H~Zss^58DyBWkd}Jm^;m yhU{-{+Wosl046fww%_}()1I6z@EQW zJu5dB4GY}2vFeFcdrtAOy6)b2gomOAKhS)g7rs_gD-Nh+0iY)Hi?^BG|!i(4GQpoBrAzT(A5`%!jsn%nc{q?_~$P7+=nK45F!63)8s!= z7pNq-wqS%PHTyFqo*@C4oO!%%jRa`I%o2aY0g{Pwp9JzvH~3) zM4ved7o1kDj)>9L zFJA@k`yWEdbKAsNu_%JgO$$4Y{!m}{n6_(4ee;HtATt7h+q>AG=R(3X?IgsqArQ1{ zIwF;up!|9Qvnt<$(b9zQ-d#xExHkC0qEh6~dmJ-2Z^ijfs@By#3bv%7dA$H3Wn_Rd z@oZwCCu+bT7bJ%=;Z#XMqOl(APh@F$86`w2-o3TofV44H=C*Y03&>pMQj@&#Xq`*mtnHyCCU0B#I)5 zs=aMuOmpU+ok$!#fGJB?pj4B0h&(0zCjy?`GDU=anMX%3W(?|uBeRLrM*inNeFJZ? zA7<-CFmfBBQ7SWGvH{1!_MG@ntnn6l;F6>Tnfd`nxl(6orBro_N#;Z^!%!Jal}>oI z&CYtBgXgjaOz+ck!P;9>_%#7QD}DR>c*HR6rXx<&8kBt4D__BGfwBous6Vd&CnX{> z6ojPe!xty9hSoaXX97T8%rFz7H=85XF9Zy!=SAwV`yxeN71V#mG+@h7}?P@{c#9~+!l02oGTT&*YO(ha- zAk>S#q$x6^NSKh#_6to7zK36$UX%|Hb!jmI5hFJYXL0+Na(>BVg!k>nq{(HJhFxH? zOs3c;PML@XS<#4j8BMKMP}3RM_Dit8NiYyKIxk%gpRL=r(Xe(VPq{Rh`HUfNEx)SsgN?}!sEva`7|N&w<{ z-;3qN^D9by8wwS9CQqxFHDONZ3;+u!^R2GE080P>002ov JPDHLkV1jJR!`uJ> literal 0 HcmV?d00001 diff --git a/icons/script_go.png b/icons/script_go.png new file mode 100644 index 0000000000000000000000000000000000000000..d3b52c92d661fc32768caec081ba54e6fba615ae GIT binary patch literal 1347 zcmV-J1-$x+P)h&RTTd2+?md<0*lNRS{J2_>bmH7QDr8nsb>@dHCJhSVA{RIF*@Fa2ermBdDa(OL?IrUh)M zh6+YQ6Hsf~(g&niyR>E7-PxJzxwEtOwY%M}M7_z`ow+l2&Ue1^WI_o1my4zF4)@o# z91MZKX4wKFO(lxLppke~#Z*I6J4fDr@(69)OPy7HuQxZp($XVt#F`A5V#lFwk^AO1 z!dI~?e@Wo}T^~I9c;gvyqt;ywCq!V{6s;Goh`gGwTBLz$5&&8xt6=ZT8&*3v5!;3> zmQTGDl`Hsh_!atC=w_K+Iz2}e_v^NKVZ6##wD?d z05D&5{rVW?Cea_$&z6}1!aXnz?oD5u-xi*O4hCNxhW`;Qk`gD zlD5K#8qqx%g!yH|xl{qDET?dj-z}1oezuI#JIjFVemZ3kWdbjdTcMNzkkS5iH~Spx zWdO3YT)>d+Wq9~4hRp^79nlO}6iiCo)XdD?wlc$muw^+#$g&d&eU(a`5DE!jBpyZ1 z0l=0!kW*HVpJZ011i+?BTtW=|Ic>ru7ewL;DQBGM%>>u7py`>=W1`7_uk$f!_7fZT zVdHHm-g*F^M>|7u7jT%1Ww9V`CK~5bZ9AMF5C0gUxBBn#L1Y$Dz|{m7I7zGLEj^V}B1Ii|PSu zg+ku%8EwS&k{6+hx{z6YhY$D2-F|BZ5f4QDc#d~N!HFYrz?nzRV>{&)_ zHH7Zo9)u_Gt;`D5F=cA3>(IHuEtU5aVOmZ!|l@b_z3}^sT})vdRmt zc<6dI0|dqWO#j8Ez=uyYZLde`^Usm*ejQ56xAM z8(`^DE0^R}*QjBVtC$_j{}li-wKLr7>kF0tFV2A9_yIY!YHj~-iIcLsyzW@Ke|7TK4xSfL)F^7kD zb-$kk{<^Rw$VG0VC=4{b3{^4J+Yw{%sdptA^h0>UyR7qFjnGnKb3Ihklin zdwOK(>5fQ2;NgzWwVOZtMK0C2=c6++He<@KFI|&WJCAkK0aXtGnpIZw+6xagIzKGi z23wX+JzXkS;P;^$P*bMEXgmRpVLyQG+klV!UI2Va?pxn}Zyi{esHTvmXAe!od9=yd zzpq2|Mgq2daRF4!1q7wz--Z_ez3uLZkG`ZVI{iEVMY5p~P!4eF^1m?f=K75T0H*tB zoQTZ_fQ7bxxN4Si6X~DbpOvTp=`NT|3A$Z%H+Ei&S3Dm8E*YTp0$C*UoZm4ZnyJdps^wyq^}={%qQr4d~9;PMc}G5%IY z`j;94jWf799tTB=$y~;h7l4o~R2kGU0R;#T{S^Ylk%9ArF<=v;=O~)R0^qjhyRV_b z2T`O?6d3}RMrl+V*iSq6ek%>RtzA)=RlpmZt*0IS^k zK~r->$d#b?_H z$wu#pfPl_nL+3XwWD_|7K(Ml{I$%Ib2*-q&N2)s(jnDmxfHeoNKChUv89*m)LI%cWOfYeAz9`e)mJ47qB_ZHfz_KJf z{OGzgfDAH-Bk^Xf_h*bwr;Z^NZx@ zXwGzMF0U20>hH|mS6c=}u2kieG)@tEWrmd$S3LD~_eS>)K6q%;{@E_OVaFpyl8$fdE^LKae4A=Df z3=R*}mOIQB(}K`xs8*1(r4R(&Tk1Gm8JU6uogZV3P+;5km%ublY%nA~Q{mXKjp4Gh zLNH?v^$$$$&Kat#?spUA*{ zBSC>3F?SS{S>y3~qf;-Cl`T)m-Kk9E6A(l!2uHK%Gl~)L263D8o|Jmc#8H{|hhxpH7|)$!`q` P00000NkvXXu0mjfJHCzb literal 0 HcmV?d00001 diff --git a/icons/table_save.png b/icons/table_save.png new file mode 100644 index 0000000000000000000000000000000000000000..0440ef911ec5c06f67e6526e82cfd4f6c33eee2f GIT binary patch literal 1437 zcmV;O1!DS%P)fQqM5w7h_^c15AVp9Nr2(6uG?AKY8x{K? zh*)jXYDBOx3RcDZ(A|4y#&hQWVD!z4x25cfL92J7?xH0I=F#IXST=S-+zI zl!jDe=2x3LeT_Ir(B=RIp-=ug(*GDjZeU;9^75qqzZ%U&a|V-J*c)i&rhUE-sWu>S z9$63BSK*~U--b6@p2ho|zs5RnjhMOuu=^wM=fX)~IUvAv)K7vZ2q^QMvk;@MX%b!^ zZpQq;=tz`&0%4>MUVaNWw}COX9#5u0Fa-^46<)vmCP>ZkC9E!7k+zfrucg%D696AP z(0zxY##vm1mH;?Wo`(ZH?cmD#aT2zUg)~7R3;|lAPC!KnkT*1WL`l%N67Ad6g%bnj zg+V8>%!)n)D`znR9v)n&>pF2@(Wqw;@CS2f%^8amz%B&kFoV3HKTs4|UAV&gu)eSi z0s(fS{H;lXs1-Ab!7;8J$LBzzqO`6T*LVeZasS;ZM_5r=JkjeJunZB7&76mUM|P&f zwi#SUM5r@E5E(VXNoYW`$e4m~rJKf;Um`LXq(#VeIt0vv>8c`P+ceH~+O1gxa>9E9 zOKdFUQeiQyZV|PII5#+HC)5Cmk8s<=m1fDEDmx2~fASqxLI9Sq68IJy2aZqAgOnf*KWpYYG82hE_0tuLt5$R^c)QK?(jqv!N*Qt zzzTX-%C+OO-^1g3?o8n}XSGA%2`R1;Z$%zXHC_5PgwtM=$3PDq`wGUt`8nLX3_Tnl znw*QP4qsHjIBz{P`CEg4FBT6z+7%-pSUOEM5*rbq;x%z@5zfA_4=Tv6>t+i!YM(7h zC?biyhd+K09}962T4|LSSJPx9LLP|FQ%TN*fg7w*DCju40DnDy6Pzl~Vk+`*S2Z3( z_xAO4LFYS5aBg@zdeba59~0pyiqLp3nudW0v5%HL5e6Et(FHAyws}~tDkwB^a9s@# z?B3%Mh@?Ida8W>H^LI7$GmD9EsBc#~1inz-!?OBx&kl2t3kZ-0K0Lc%?pG=jn$Wcp zMf&NP)0qT%yE~1|N>zqJkCkGTaVRyr8u!FU`jU}~N7XGbUv)HWAp))o_jf#mw((Hn zam9?0&1zMLqcIIotYwA>Ex$_AH7deDxYX8&K%T+OB?mfh6yPRHF51nY0Lf;}Rbkj; zvNK|^>>*x;>j$CvzLrL!-n~F?&z<+udcQ9Xtu|IPBBdPuU zGxJ8hp$1Td*P{sO9wh)pWG^v3F1jhGmtI++bD~!_=vt9A&I3<9yt8Kh9-Ezq!S41H ze(K8wAWBsXK^|uqxQ)TUJ+158pYpduoMi0l-(d{vRn<)pumy*!wI{pUGx5IrwgVqO z4^><$y{Ds1z>iBSaO5zZusJ+4{2Vxr0|H5Qv{^9zi-L+$@emY+UTz+lDmf_enxAID z9-r90Eh?}0%|&TPWj2r97B-Zcilz`18*@+<+I)Oap)2GlJUr%s>e5Jm7hYRxA00Pr z_O@52rfe>twU~p}k_8G0+|Xp10=BJWqX#@4 r(|queryEdit->setFont(font); highlighter.reset(new SqlHighlighter(ui->queryEdit->document())); - ui->queryEdit->setPlainText(test_query); - ui->connectionStringEdit->setText("user=postgres dbname=foutrapport password=admin"); +// ui->queryEdit->setPlainText(test_query); +// ui->connectionStringEdit->setText("user=postgres dbname=foutrapport password=admin"); - QAction *action; - action = ui->mainToolBar->addAction("connect"); - connect(action, &QAction::triggered, this, &MainWindow::startConnect); +// QAction *action; +// action = ui->mainToolBar->addAction("connect"); +// connect(action, &QAction::triggered, this, &MainWindow::startConnect); - action = ui->mainToolBar->addAction("execute"); - connect(action, &QAction::triggered, this, &MainWindow::performQuery); +// action = ui->mainToolBar->addAction("explain"); +// connect(action, &QAction::triggered, this, &MainWindow::performExplain); - action = ui->mainToolBar->addAction("explain"); - connect(action, &QAction::triggered, this, &MainWindow::performExplain); - - action = ui->mainToolBar->addAction("cancel"); - connect(action, &QAction::triggered, this, &MainWindow::cancel_query); +// action = ui->mainToolBar->addAction("cancel"); +// connect(action, &QAction::triggered, this, &MainWindow::cancel_query); m_dbConnection.setStateCallback([this](ASyncDBConnection::State st) { @@ -130,6 +127,15 @@ MainWindow::~MainWindow() delete ui; } +void MainWindow::setConfig(const ConnectionConfig &config) +{ + m_config = config; + QString title = "pglab - "; + title += m_config.name().c_str(); + setWindowTitle(title); + QueueTask([this]() { startConnect(); }); +} + void MainWindow::QueueTask(TSQueue::t_Callable c) { m_taskQueue.add(c); @@ -175,8 +181,9 @@ void MainWindow::connectionStateChanged(ASyncDBConnection::State state) void MainWindow::startConnect() { - std::string connstr = ui->connectionStringEdit->text().toUtf8().data(); - m_dbConnection.setupConnection(connstr); +// std::string connstr = ui->connectionStringEdit->text().toUtf8().data(); +// m_dbConnection.setupConnection(connstr); + m_dbConnection.setupConnection(m_config); } void MainWindow::performQuery() @@ -206,6 +213,7 @@ void MainWindow::query_ready(std::shared_ptr dbres) if (st == PGRES_TUPLES_OK) { resultModel.reset(new QueryResultModel(nullptr , dbres)); ui->ResultView->setModel(resultModel.get()); + ui->tabWidget->setCurrentWidget(ui->dataTab); statusBar()->showMessage(tr("Query ready.")); } else { @@ -239,6 +247,7 @@ void MainWindow::query_ready(std::shared_ptr dbres) else { statusBar()->showMessage(tr("No tuples returned, possibly an error...")); } + ui->tabWidget->setCurrentWidget(ui->messageTab); receiveNotice(dbres->diagDetails()); } } @@ -303,10 +312,12 @@ void MainWindow::explain_ready(ExplainRoot::SPtr explain) ui->explainTreeView->setColumnWidth(4, 80); ui->explainTreeView->setColumnWidth(5, 80); ui->explainTreeView->setColumnWidth(6, 600); + ui->tabWidget->setCurrentWidget(ui->explainTab); statusBar()->showMessage(tr("Explain ready.")); } else { addLog("Explain no result"); + ui->tabWidget->setCurrentWidget(ui->messageTab); statusBar()->showMessage(tr("Explain failed.")); } } @@ -321,10 +332,6 @@ void MainWindow::receiveNotice(Pgsql::ErrorDetails notice) QTextCursor cursor = ui->messagesEdit->textCursor(); cursor.movePosition(QTextCursor::End, QTextCursor::MoveAnchor); - -// QString msg; -// cursor.insertText("TEST\r\n"); - QTextTable *table = cursor.insertTable(4, 2); if (table) { table->cellAt(1, 0).firstCursorPosition().insertText("State"); @@ -466,3 +473,18 @@ void Copy( ) QApplication.clipboard().setText(selected_text); } #endif + +void MainWindow::on_actionExecute_SQL_triggered() +{ + performQuery(); +} + +void MainWindow::on_actionExplain_Analyze_triggered() +{ + performExplain(); +} + +void MainWindow::on_actionCancel_triggered() +{ + cancel_query(); +} diff --git a/mainwindow.h b/mainwindow.h index c0b9e0c..241b00e 100644 --- a/mainwindow.h +++ b/mainwindow.h @@ -2,6 +2,7 @@ #define MAINWINDOW_H #include "asyncdbconnection.h" +#include "connectionconfig.h" #include "tsqueue.h" #include #include @@ -9,6 +10,8 @@ #include #include #include "PgsqlConn.h" +#include +#include class ExplainRoot; class QueryResultModel; @@ -20,28 +23,18 @@ namespace Ui { } namespace Pgsql { - class Connection; - } -#include -#include - -class TaskQueue { -public: - -private: -}; class MainWindow : public QMainWindow { Q_OBJECT - public: explicit MainWindow(QWidget *parent = 0); ~MainWindow(); + void setConfig(const ConnectionConfig &config); /* Meant to be called from other threads to pass a code block * that has to be executed in the context of the thread of the window. */ @@ -52,6 +45,8 @@ private: QLabel *m_timeElapsedLabel; std::unique_ptr m_timer; std::chrono::time_point m_startTime; + ConnectionConfig m_config; + void startTimer(); void endTimer(); @@ -88,6 +83,9 @@ private slots: void on_actionExport_data_triggered(); void on_actionClose_triggered(); void on_actionAbout_triggered(); + void on_actionExecute_SQL_triggered(); + void on_actionExplain_Analyze_triggered(); + void on_actionCancel_triggered(); }; #endif // MAINWINDOW_H diff --git a/mainwindow.ui b/mainwindow.ui index aed18b7..9731ea7 100644 --- a/mainwindow.ui +++ b/mainwindow.ui @@ -11,7 +11,7 @@ - MainWindow + pglab - database @@ -27,9 +27,6 @@ 8 - - - @@ -211,19 +208,38 @@ false + + + + + + + + + + :/icons/folder.png:/icons/folder.png + Load SQL + + + :/icons/script_save.png:/icons/script_save.png + Save SQL + + + :/icons/table_save.png:/icons/table_save.png + Export data @@ -238,8 +254,39 @@ About + + + + :/icons/script_go.png + + + + Execute SQL + + + + + + :/icons/script_delete.png + + + + Cancel + + + + + + :/icons/lightbulb.png:/icons/lightbulb.png + + + Explain Analyze + + - + + + diff --git a/resources.qrc b/resources.qrc index 5cecd03..ad70bd6 100644 --- a/resources.qrc +++ b/resources.qrc @@ -1,7 +1,13 @@  - + icons/server_add.png icons/server_delete.png icons/server_go.png + icons/script_delete.png + icons/script_go.png + icons/folder.png + icons/script_save.png + icons/lightbulb.png + icons/table_save.png