From b3a98f6dc0509fca9b181f0e009ba72517206170 Mon Sep 17 00:00:00 2001 From: eelke Date: Tue, 27 Aug 2019 20:12:00 +0200 Subject: [PATCH] Most of functionality for connections in tree works now. Old list largely removed. --- pglab/ConnectionConfigurationWidget.cpp | 27 +- pglab/ConnectionConfigurationWidget.h | 10 +- pglab/ConnectionController.cpp | 67 ++--- pglab/ConnectionController.h | 15 +- pglab/ConnectionListModel.cpp | 317 +++++++++++++++++++----- pglab/ConnectionListModel.h | 28 ++- pglab/ConnectionManagerWindow.cpp | 37 +-- pglab/ConnectionManagerWindow.ui | 15 -- pglablib/ConnectionConfig.cpp | 32 ++- pglablib/ConnectionConfig.h | 9 +- 10 files changed, 399 insertions(+), 158 deletions(-) diff --git a/pglab/ConnectionConfigurationWidget.cpp b/pglab/ConnectionConfigurationWidget.cpp index 81fa6b6..0e656d1 100644 --- a/pglab/ConnectionConfigurationWidget.cpp +++ b/pglab/ConnectionConfigurationWidget.cpp @@ -19,7 +19,7 @@ void ConnectionConfigurationWidget::editExistingInWindow(ConnectionController *ctrl, const ConnectionConfig &cfg) { - auto w = new ConnectionConfigurationWidget; + auto w = new ConnectionConfigurationWidget(ctrl->getConnectionTreeModel()); w->setData(cfg); @@ -35,8 +35,8 @@ void ConnectionConfigurationWidget::editExistingInWindow(ConnectionController *c win->setLayout(vbox); win->connect(btn_hbox, &QDialogButtonBox::accepted, [ctrl, w, win] () { - auto cc = w->data(); - ctrl->getConnectionListModel()->save(cc); + auto [grp, cc] = w->data(); + ctrl->getConnectionTreeModel()->save(grp, cc); win->accept(); }); win->connect(btn_hbox, &QDialogButtonBox::rejected, [win] () { win->reject(); }); @@ -49,9 +49,17 @@ void ConnectionConfigurationWidget::editExistingInWindow(ConnectionController *c win->show(); } -ConnectionConfigurationWidget::ConnectionConfigurationWidget(QWidget *parent) +ConnectionConfigurationWidget::ConnectionConfigurationWidget(ConnectionTreeModel *connection_model, QWidget *parent) : QWidget(parent) { + lblGroup = new QLabel; + cmbbxGroup = new QComboBox; + cmbbxGroup->setModel(connection_model); + cmbbxGroup->setModelColumn(0); +// cmbbxGroup->setEditable(true); +// cmbbxGroup->setInsertPolicy(QComboBox::NoInsert); + lblGroup->setBuddy(cmbbxGroup); + lblName = new QLabel; SET_OBJECT_NAME(lblName); edtName = new QLineEdit ; @@ -118,7 +126,7 @@ ConnectionConfigurationWidget::ConnectionConfigurationWidget(QWidget *parent) formLayout = new QFormLayout; - setLayout(formLayout); + formLayout->addRow(lblGroup, cmbbxGroup); formLayout->addRow(lblName, edtName); formLayout->addRow(lblHost, edtHost); formLayout->addRow(lblPort, spinPort); @@ -129,12 +137,14 @@ ConnectionConfigurationWidget::ConnectionConfigurationWidget(QWidget *parent) formLayout->addRow(lblKey, edtKey); formLayout->addRow(lblRootCert, edtRootCert); formLayout->addRow(lblCrl, edtCrl); + setLayout(formLayout); retranslateUi(); } void ConnectionConfigurationWidget::retranslateUi() { + lblGroup->setText(QApplication::translate("ConnectionConfigurationWidget", "&Group", nullptr)); lblName->setText(QApplication::translate("ConnectionConfigurationWidget", "&Name", nullptr)); lblHost->setText(QApplication::translate("ConnectionConfigurationWidget", "&Host", nullptr)); lblPort->setText(QApplication::translate("ConnectionConfigurationWidget", "&Port", nullptr)); @@ -162,8 +172,11 @@ void ConnectionConfigurationWidget::setData(const ConnectionConfig &cfg) edtCrl->setText(stdStrToQ(cfg.sslCrl())); } -ConnectionConfig ConnectionConfigurationWidget::data() const +std::tuple ConnectionConfigurationWidget::data() const { + QString group; + group = cmbbxGroup->currentText(); + ConnectionConfig cfg; cfg.setUuid(m_uuid); cfg.setName(qStrToStd(edtName->text())); @@ -176,6 +189,6 @@ ConnectionConfig ConnectionConfigurationWidget::data() const cfg.setSslKey(qStrToStd(edtKey->text())); cfg.setSslRootCert(qStrToStd(edtRootCert->text())); cfg.setSslCrl(qStrToStd(edtCrl->text())); - return cfg; + return { group, cfg }; } diff --git a/pglab/ConnectionConfigurationWidget.h b/pglab/ConnectionConfigurationWidget.h index 4d4eadf..21849c1 100644 --- a/pglab/ConnectionConfigurationWidget.h +++ b/pglab/ConnectionConfigurationWidget.h @@ -3,6 +3,7 @@ #include #include +#include class ConnectionController; class ConnectionConfig; @@ -13,6 +14,7 @@ class QSpinBox; class QComboBox; class QDataWidgetMapper; +class ConnectionTreeModel; class ConnectionConfigurationWidget : public QWidget { @@ -20,17 +22,18 @@ class ConnectionConfigurationWidget : public QWidget public: static void editExistingInWindow(ConnectionController *ctrl, const ConnectionConfig &cfg); - explicit ConnectionConfigurationWidget(QWidget *parent = nullptr); + explicit ConnectionConfigurationWidget(ConnectionTreeModel *connection_model, QWidget *parent = nullptr); void retranslateUi(); void setData(const ConnectionConfig &cfg); - ConnectionConfig data() const; + std::tuple data() const; signals: private: QUuid m_uuid; - QFormLayout *formLayout; + QLabel *lblGroup; + QComboBox *cmbbxGroup; QLabel *lblName; QLineEdit *edtName; QLabel *lblHost; @@ -51,6 +54,7 @@ private: QLineEdit *edtRootCert; QLabel *lblCrl; QLineEdit *edtCrl; + QFormLayout *formLayout; public slots: }; diff --git a/pglab/ConnectionController.cpp b/pglab/ConnectionController.cpp index 281eeba..f4890f9 100644 --- a/pglab/ConnectionController.cpp +++ b/pglab/ConnectionController.cpp @@ -19,7 +19,6 @@ ConnectionController::~ConnectionController() { delete m_connectionManagerWindow; delete m_connectionTreeModel; - delete m_connectionListModel; } void ConnectionController::init() @@ -30,8 +29,6 @@ void ConnectionController::init() m_passwordManager = std::make_shared(); - m_connectionListModel = new ConnectionListModel(this); - m_connectionListModel->load(); m_connectionTreeModel = new ConnectionTreeModel(this, m_masterController->userConfigDatabase()); m_connectionTreeModel->load(); @@ -45,37 +42,47 @@ void ConnectionController::showConnectionManager() m_connectionManagerWindow->show(); } -void ConnectionController::openSqlWindowForConnection(int connection_index) +namespace { + + ConnectionConfig* getConfigFromModelIndex(QModelIndex index) + { + if (!index.isValid()) + return nullptr; + auto node = static_cast(index.internalPointer()); + return dynamic_cast(node); + } + +} + + +void ConnectionController::openSqlWindowForConnection(QModelIndex index) { + auto config = getConfigFromModelIndex(index); + if (config) { - auto res = m_connectionListModel->get(connection_index); - if (res.valid()) { - auto cc = res.get(); - - if (retrieveConnectionPassword(cc)) { - m_connectionListModel->save(cc); + if (retrieveConnectionPassword(*config)) { + m_connectionTreeModel->save(*config); // TODO instead of directly openening the mainwindow // do async connect and only open window when we have // working connection auto w = new DatabaseWindow(m_masterController, nullptr); w->setAttribute( Qt::WA_DeleteOnClose ); - w->setConfig(cc); + w->setConfig(*config); w->showMaximized(); } } } -void ConnectionController::openBackupDlgForConnection(int connection_index) +void ConnectionController::openBackupDlgForConnection(QModelIndex index) { - auto res = m_connectionListModel->get(connection_index); - if (res.valid()) { - auto cc = res.get(); - if (retrieveConnectionPassword(cc)) { - m_connectionListModel->save(cc); + auto config = getConfigFromModelIndex(index); + if (config) { + if (retrieveConnectionPassword(*config)) { + m_connectionTreeModel->save(*config); auto w = new BackupDialog(nullptr); //new ServerWindow(this, nullptr); w->setAttribute( Qt::WA_DeleteOnClose ); - w->setConfig(cc); + w->setConfig(*config); w->show(); } } @@ -88,25 +95,23 @@ void ConnectionController::createConnection() ConnectionConfigurationWidget::editExistingInWindow(this, cc); } -void ConnectionController::editConnection(int connection_index) +void ConnectionController::editConnection(QModelIndex index) { - auto res = m_connectionListModel->get(connection_index); - if (res.valid()) { - auto cc = res.get(); - ConnectionConfigurationWidget::editExistingInWindow(this, cc); + auto config = getConfigFromModelIndex(index); + if (config) { + ConnectionConfigurationWidget::editExistingInWindow(this, *config); } } -void ConnectionController::openServerWindowForConnection(int connection_index) +void ConnectionController::openServerWindowForConnection(QModelIndex index) { - auto res = m_connectionListModel->get(connection_index); - if (res.valid()) { - auto cc = res.get(); - if (retrieveConnectionPassword(cc)) { - m_connectionListModel->save(cc); + auto config = getConfigFromModelIndex(index); + if (config) { + if (retrieveConnectionPassword(*config)) { + m_connectionTreeModel->save(*config); auto w = new ServerWindow(m_masterController, nullptr); w->setAttribute( Qt::WA_DeleteOnClose ); - w->setConfig(cc); + w->setConfig(*config); w->show(); } } @@ -129,7 +134,7 @@ bool ConnectionController::retrieveConnectionPassword(ConnectionConfig &cc) } // Geen else hier want als voorgaande blok niet geretourneerd heeft moeten we wachtwoord // ook aan de gebruiker vragen zoals hier gebeurd. - QString str = ConnectionListModel::makeLongDescription(cc); + QString str = cc.makeLongDescription(); auto dlg = std::make_unique(PasswordPromptDialog::SaveOption, nullptr); dlg->setCaption(tr("Connection password prompt")); dlg->setDescription(QString(tr("Please provide password for connection %1")).arg(str)); diff --git a/pglab/ConnectionController.h b/pglab/ConnectionController.h index 06c3633..b8056bf 100644 --- a/pglab/ConnectionController.h +++ b/pglab/ConnectionController.h @@ -6,7 +6,6 @@ class MasterController; class ConnectionConfig; class ConnectionList; -class ConnectionListModel; class ConnectionTreeModel; class ConnectionManagerWindow; class PasswordManager; @@ -19,31 +18,25 @@ public: void init(); - ConnectionListModel *getConnectionListModel() - { - return m_connectionListModel; - } - ConnectionTreeModel *getConnectionTreeModel() { return m_connectionTreeModel; } void showConnectionManager(); - void openSqlWindowForConnection(int connection_index); - void openServerWindowForConnection(int connection_index); - void openBackupDlgForConnection(int connection_index); + void openSqlWindowForConnection(QModelIndex index); + void openServerWindowForConnection(QModelIndex index); + void openBackupDlgForConnection(QModelIndex index); /// Starts the form for creating a new conncetion. /// This function returns immidiatly! void createConnection(); /// Starts the form for editing a conncetion. /// This function returns immidiatly! - void editConnection(int connection_index); + void editConnection(QModelIndex index); private: MasterController *m_masterController; ConnectionList *m_connectionList = nullptr; - ConnectionListModel *m_connectionListModel = nullptr; ConnectionTreeModel *m_connectionTreeModel = nullptr; ConnectionManagerWindow *m_connectionManagerWindow = nullptr; diff --git a/pglab/ConnectionListModel.cpp b/pglab/ConnectionListModel.cpp index 24eaca5..1cb67c1 100644 --- a/pglab/ConnectionListModel.cpp +++ b/pglab/ConnectionListModel.cpp @@ -17,7 +17,7 @@ namespace { R"__( CREATE TABLE IF NOT EXISTS conngroup ( conngroup_id INTEGER PRIMARY KEY, - gname TEXT NOT NULL + gname TEXT NOT NULL UNIQUE );)__"; const char * const q_create_table_connection = @@ -39,6 +39,8 @@ CREATE TABLE IF NOT EXISTS connection ( passwordstate INTEGER NOT NULL );)__"; + + const char * const q_insert_or_replace_into_connection = R"__(INSERT OR REPLACE INTO connection VALUES (:uuid, :name, :conngroup_id, :host, :hostaddr, :port, :user, :dbname, @@ -61,7 +63,7 @@ R"__(INSERT OR REPLACE INTO connection return {true, {}}; } - std::tuple SaveConnectionConfig(QSqlDatabase &db, const ConnectionConfig &cc, int conngroup_id) + std::optional SaveConnectionConfig(QSqlDatabase &db, const ConnectionConfig &cc, int conngroup_id) { QSqlQuery q(db); q.prepare(q_insert_or_replace_into_connection); @@ -81,75 +83,74 @@ R"__(INSERT OR REPLACE INTO connection q.bindValue(":passwordstate", static_cast(cc.passwordState())); if (!q.exec()) { - auto err = q.lastError(); - return { false, err }; + return q.lastError(); } - return {true, {}}; + return {}; } /** Saves a connection configuration. Before calling this you may want to call beginGroup. */ - void SaveConnectionConfig(QSettings &settings, const ConnectionConfig &cc) - { - settings.setValue("name", stdStrToQ(cc.name())); - settings.setValue("host", stdStrToQ(cc.host())); - settings.setValue("hostaddr", stdStrToQ(cc.hostAddr())); - settings.setValue("port", cc.port()); - settings.setValue("user", stdStrToQ(cc.user())); - //settings.setValue("password", stdStrToQ(cc.password())); - settings.setValue("dbname", stdStrToQ(cc.dbname())); - settings.setValue("sslmode", static_cast(cc.sslMode())); - settings.setValue("sslcert", stdStrToQ(cc.sslCert())); - settings.setValue("sslkey", stdStrToQ(cc.sslKey())); - settings.setValue("sslrootcert", stdStrToQ(cc.sslRootCert())); - settings.setValue("sslcrl", stdStrToQ(cc.sslCrl())); - settings.setValue("passwordState", static_cast(cc.passwordState())); - } +// void SaveConnectionConfig(QSettings &settings, const ConnectionConfig &cc) +// { +// settings.setValue("name", stdStrToQ(cc.name())); +// settings.setValue("host", stdStrToQ(cc.host())); +// settings.setValue("hostaddr", stdStrToQ(cc.hostAddr())); +// settings.setValue("port", cc.port()); +// settings.setValue("user", stdStrToQ(cc.user())); +// //settings.setValue("password", stdStrToQ(cc.password())); +// settings.setValue("dbname", stdStrToQ(cc.dbname())); +// settings.setValue("sslmode", static_cast(cc.sslMode())); +// settings.setValue("sslcert", stdStrToQ(cc.sslCert())); +// settings.setValue("sslkey", stdStrToQ(cc.sslKey())); +// settings.setValue("sslrootcert", stdStrToQ(cc.sslRootCert())); +// settings.setValue("sslcrl", stdStrToQ(cc.sslCrl())); +// settings.setValue("passwordState", static_cast(cc.passwordState())); +// } - template - bool in_range(T value) - { - return value >= std::numeric_limits::min() && value <= std::numeric_limits::max(); - } +// template +// bool in_range(T value) +// { +// return value >= std::numeric_limits::min() && value <= std::numeric_limits::max(); +// } - void LoadConnectionConfig(QSettings &settings, ConnectionConfig &cc) - { - cc.setName(qvarToStdStr(settings.value("name"))); - cc.setHost(qvarToStdStr(settings.value("host"))); - cc.setHostAddr(qvarToStdStr(settings.value("hostaddr"))); - int p = settings.value("port", 5432).toInt(); - if (!in_range(p)) { - p = 0; // let the user re-enter a valid value - } +// void LoadConnectionConfig(QSettings &settings, ConnectionConfig &cc) +// { +// cc.setName(qvarToStdStr(settings.value("name"))); +// cc.setHost(qvarToStdStr(settings.value("host"))); +// cc.setHostAddr(qvarToStdStr(settings.value("hostaddr"))); +// int p = settings.value("port", 5432).toInt(); +// if (!in_range(p)) { +// p = 0; // let the user re-enter a valid value +// } - cc.setPort(static_cast(p)); - cc.setUser(qvarToStdStr(settings.value("user"))); +// cc.setPort(static_cast(p)); +// cc.setUser(qvarToStdStr(settings.value("user"))); - //cc.setPassword(qvarToStdStr(settings.value("password"))); +// //cc.setPassword(qvarToStdStr(settings.value("password"))); - cc.setDbname(qvarToStdStr(settings.value("dbname"))); - cc.setSslMode(static_cast(settings.value("sslmode").toInt())); - cc.setSslCert(qvarToStdStr(settings.value("sslcert"))); - cc.setSslKey(qvarToStdStr(settings.value("sslkey"))); - cc.setSslRootCert(qvarToStdStr(settings.value("sslrootcert"))); - cc.setSslCrl(qvarToStdStr(settings.value("sslcrl"))); +// cc.setDbname(qvarToStdStr(settings.value("dbname"))); +// cc.setSslMode(static_cast(settings.value("sslmode").toInt())); +// cc.setSslCert(qvarToStdStr(settings.value("sslcert"))); +// cc.setSslKey(qvarToStdStr(settings.value("sslkey"))); +// cc.setSslRootCert(qvarToStdStr(settings.value("sslrootcert"))); +// cc.setSslCrl(qvarToStdStr(settings.value("sslcrl"))); - PasswordState pwstate; - QVariant v = settings.value("passwordState"); - if (v.isNull()) pwstate = PasswordState::NotStored; - else pwstate = static_cast(v.toInt()); - cc.setPasswordState(pwstate); - } +// PasswordState pwstate; +// QVariant v = settings.value("passwordState"); +// if (v.isNull()) pwstate = PasswordState::NotStored; +// else pwstate = static_cast(v.toInt()); +// cc.setPasswordState(pwstate); +// } } // end of unnamed namespace - +#if false ConnectionListModel::ConnectionListModel(QObject *parent) : QAbstractListModel(parent) { @@ -390,7 +391,7 @@ QString ConnectionListModel::iniFileName() path += "/connections.ini"; return path; } - +#endif ConnectionTreeModel::ConnectionTreeModel(QObject *parent, QSqlDatabase &db) : QAbstractItemModel(parent) @@ -402,26 +403,79 @@ void ConnectionTreeModel::load() { InitConnectionTables(m_db); - auto g1 = std::make_shared(); - g1->name = "Testing"; + QSqlQuery q(m_db); + q.prepare("SELECT conngroup_id, gname FROM conngroup;"); + if (!q.exec()) { +// auto err = q_create_table.lastError(); +// return { false, err }; + throw std::runtime_error("Loading groups failed"); + } + while (q.next()) { + int id = q.value(0).toInt(); + QString name = q.value(1).toString(); - for (int i = 1; i < 3; ++i) { - auto cc = std::make_shared(); - cc->setUuid(QUuid::createUuid()); - cc->setName("testconn " + std::to_string(i)); - g1->add(cc); + auto g = std::make_shared(); + g->conngroup_id = id; + g->name = name; + m_groups.push_back(g); } - auto g2 = std::make_shared(); - g2->name = "Production"; - for (int i = 1; i < 4; ++i) { + q.prepare("SELECT uuid, cname, conngroup_id, host, hostaddr, port, " + " user, dbname, sslmode, sslcert, sslkey, sslrootcert, sslcrl, " + " passwordstate " + "FROM connection ORDER BY conngroup_id, cname;"); + if (!q.exec()) { +// auto err = q_create_table.lastError(); +// return { false, err }; + throw std::runtime_error("Loading groups failed"); + } + while (q.next()) { auto cc = std::make_shared(); - cc->setUuid(QUuid::createUuid()); - cc->setName("prodconn " + std::to_string(i)); - g2->add(cc); + cc->setUuid(q.value(0).toUuid()); + cc->setName(qvarToStdStr(q.value(1))); + cc->setHost(qvarToStdStr(q.value(3))); + cc->setHostAddr(qvarToStdStr(q.value(4))); + cc->setPort(static_cast(q.value(5).toInt())); + cc->setUser(qvarToStdStr(q.value(6))); + cc->setDbname(qvarToStdStr(q.value(7))); + cc->setSslMode(static_cast(q.value(8).toInt())); + cc->setSslCert(qvarToStdStr(q.value(9))); + cc->setSslKey(qvarToStdStr(q.value(10))); + cc->setSslRootCert(qvarToStdStr(q.value(11))); + cc->setSslCrl(qvarToStdStr(q.value(12))); + cc->setPasswordState(static_cast(q.value(13).toInt())); + + int group_id = q.value(2).toInt(); + auto find_res = std::find_if(m_groups.begin(), m_groups.end(), + [group_id] (auto item) { return item->conngroup_id == group_id; }); + if (find_res != m_groups.end()) { + (*find_res)->add(cc); + } + else { + throw std::runtime_error("conngroup missing"); + } } - m_groups = { g1, g2 }; +// auto g1 = std::make_shared(); +// g1->name = "Testing"; + +// for (int i = 1; i < 3; ++i) { +// auto cc = std::make_shared(); +// cc->setUuid(QUuid::createUuid()); +// cc->setName("testconn " + std::to_string(i)); +// g1->add(cc); +// } + +// auto g2 = std::make_shared(); +// g2->name = "Production"; +// for (int i = 1; i < 4; ++i) { +// auto cc = std::make_shared(); +// cc->setUuid(QUuid::createUuid()); +// cc->setName("prodconn " + std::to_string(i)); +// g2->add(cc); +// } + +// m_groups = { g1, g2 }; } QVariant ConnectionTreeModel::data(const QModelIndex &index, int role) const @@ -517,7 +571,8 @@ QModelIndex ConnectionTreeModel::parent(const QModelIndex &index) const auto p = config->parent(); auto find_res = std::find_if(m_groups.begin(), m_groups.end(), [p] (auto item) -> bool { return *p == *item; }); if (find_res != m_groups.end()) { - return createIndex(find_res - m_groups.begin(), 0, config->parent()); + return createIndex(find_res - m_groups.begin(), 0, + const_cast(config->parent())); } } throw std::logic_error("Should never get here"); @@ -547,7 +602,133 @@ int ConnectionTreeModel::rowCount(const QModelIndex &parent) const return result; } -int ConnectionTreeModel::columnCount(const QModelIndex &parent) const +int ConnectionTreeModel::columnCount(const QModelIndex &) const { return ColCount; } + +bool ConnectionTreeModel::removeRows(int row, int count, const QModelIndex &parent) +{ + if (parent.isValid() && count == 1) { + // should be a group + auto grp = m_groups[parent.row()]; + for (int i = 0; i < count; ++i) { + QUuid uuid = grp->connections().at(row + i)->uuid(); + QSqlQuery q(m_db); + q.prepare( + "DELETE FROM connection " + " WHERE uuid=:uuid"); + q.bindValue(":uuid", uuid); + if (!q.exec()) { + auto err = q.lastError(); + throw std::runtime_error("QqlError"); + } + } + beginRemoveRows(parent, row, row + count - 1); + SCOPE_EXIT { endRemoveRows(); }; + grp->erase(row, count); + } +} + +void ConnectionTreeModel::save(const QString &group_name, const ConnectionConfig &cc) +{ + auto [grp_idx, conn_idx] = findConfig(cc.uuid()); + if (grp_idx >= 0) { + auto grp = m_groups[grp_idx]; + if (grp->name == group_name) { + // update config + grp->update(conn_idx, cc); + // send change event + auto node = grp->connections().at(conn_idx); + dataChanged( + createIndex(conn_idx, 0, node.get()), + createIndex(conn_idx, ColCount-1, node.get())); + saveToDb(*node); + return; + } + else { + auto parent = createIndex(grp_idx, 0, grp.get()); + beginRemoveRows(parent, conn_idx, conn_idx); + SCOPE_EXIT { endRemoveRows(); }; + grp->erase(conn_idx); + } + } + // Here we can assume we have to find the new group or create a new group + // because if the connection was in the right group the function has already returned. + // We assume the model is in sync with the DB as the DB should not be shared! + int new_grp_idx = findGroup(group_name); + if (new_grp_idx < 0) { + // Group not found we are g + auto add_grp_res = addGroup(group_name); + if (std::holds_alternative(add_grp_res)) { + new_grp_idx = std::get(add_grp_res); + } + else { + throw std::runtime_error("SqlError1"); + } + } + auto new_grp = m_groups[new_grp_idx]; + + auto parent = createIndex(new_grp_idx, 0, new_grp.get()); + auto idx = new_grp->connections().size(); + beginInsertRows(parent, idx, idx); + SCOPE_EXIT { endInsertRows(); }; + auto node = std::make_shared(cc); + int node_idx = new_grp->add(node); +// dataChanged( +// createIndex(node_idx, 0, node.get()), +// createIndex(node_idx, ColCount-1, node.get())); + auto save_res = saveToDb(*node); + if (save_res) { + throw std::runtime_error("SqlError2"); + } +} + +void ConnectionTreeModel::save(const ConnectionConfig &cc) +{ + saveToDb(cc); +} + +std::tuple ConnectionTreeModel::findConfig(const QUuid uuid) const +{ + int group_idx = -1, connection_idx = -1; + + for (int grp_idx = 0; grp_idx < m_groups.size(); ++grp_idx) { + auto && grp = m_groups[grp_idx]; + auto && conns = grp->connections(); + auto find_res = std::find_if(conns.begin(), conns.end(), + [&uuid] (auto item) -> bool { return item->uuid() == uuid; }); + if (find_res != conns.end()) { + group_idx = grp_idx; + connection_idx = find_res - conns.begin(); + break; + } + } + + return { group_idx, connection_idx }; +} + +int ConnectionTreeModel::findGroup(QString name) const +{ + for (int idx = 0; idx < m_groups.size(); ++idx) { + if (m_groups[idx]->name == name) return idx; + } + return -1; +} + +std::variant ConnectionTreeModel::addGroup(QString group_name) +{ + QSqlQuery q(m_db); + q.prepare("INSERT INTO conngroup (gname) VALUES (:name)"); + q.bindValue(":name", group_name); + if (!q.exec()) { + auto err = q.lastError(); + return { err }; + } + return q.lastInsertId().toInt(); +} + +std::optional ConnectionTreeModel::saveToDb(const ConnectionConfig &cc) +{ + return SaveConnectionConfig(m_db, cc, cc.parent()->conngroup_id); +} diff --git a/pglab/ConnectionListModel.h b/pglab/ConnectionListModel.h index b053cc9..1a56d0d 100644 --- a/pglab/ConnectionListModel.h +++ b/pglab/ConnectionListModel.h @@ -8,9 +8,11 @@ #include "ConnectionConfig.h" #include "Expected.h" +#include +#include #include -//#include +#include class QSqlDatabase; class ConnectionTreeModel : public QAbstractItemModel { @@ -46,13 +48,36 @@ public: int columnCount(const QModelIndex &parent = QModelIndex()) const override; + bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex()) override; + + /** Matches cc to the list by looking at its uuid. + * + * If it is not in the list it is added. If the uuid is in the list that entry is updated. + * If the group has been changed it is moved to the right group. + * In both cases the data is also directly written to long term storage. + */ + void save(const QString &group, const ConnectionConfig &cc); + /** Save changed config, group is not allowed to change + */ + void save(const ConnectionConfig &cc); private: using Groups = QVector>; QSqlDatabase &m_db; Groups m_groups; + + /// Finds the connection with the specified uuid and returns + /// { group_index, connection_index } + std::tuple findConfig(const QUuid uuid) const; + int findGroup(QString name) const; + + /// Create a new group in the DB and place in the tree + /// dataChanged is sent by this function + std::variant addGroup(QString group_name); + std::optional saveToDb(const ConnectionConfig &cc); }; +#if false /** \brief Model class for the list of connections. * * This class also allows for the editing of the list. @@ -107,5 +132,6 @@ private: QString iniFileName(); }; +#endif #endif // CONNECTIONLISTMODEL_H diff --git a/pglab/ConnectionManagerWindow.cpp b/pglab/ConnectionManagerWindow.cpp index ec7210e..0b151b6 100644 --- a/pglab/ConnectionManagerWindow.cpp +++ b/pglab/ConnectionManagerWindow.cpp @@ -17,7 +17,6 @@ ConnectionManagerWindow::ConnectionManagerWindow(MasterController *master, QWidg , m_connectionController(master->connectionController()) { ui->setupUi(this); - ui->listView->setModel(m_connectionController->getConnectionListModel()); ui->treeView->setModel(m_connectionController->getConnectionTreeModel()); } @@ -34,23 +33,25 @@ void ConnectionManagerWindow::on_actionAdd_Connection_triggered() void ConnectionManagerWindow::on_actionDelete_connection_triggered() { - auto ci = ui->listView->selectionModel()->currentIndex(); + auto ci = ui->treeView->selectionModel()->currentIndex(); if (ci.isValid()) { - auto res = QMessageBox::question(this, "pglab", - tr("Are you sure you want to remove this connection?"), QMessageBox::Yes, QMessageBox::No); - if (res == QMessageBox::Yes) { - auto clm = m_connectionController->getConnectionListModel(); - clm->removeRow(ci.row()); + auto node = static_cast(ci.internalPointer()); + auto cc = dynamic_cast(node); + if (cc) { + auto res = QMessageBox::question(this, "pglab", + tr("Are you sure you want to remove this connection?"), QMessageBox::Yes, QMessageBox::No); + if (res == QMessageBox::Yes) { + auto cm = m_connectionController->getConnectionTreeModel(); + cm->removeRow(ci.row(), ci.parent()); + } } } } void ConnectionManagerWindow::on_actionConnect_triggered() { - auto ci = ui->listView->selectionModel()->currentIndex(); - if (ci.isValid()) { - m_connectionController->openSqlWindowForConnection(ci.row()); - } + auto ci = ui->treeView->selectionModel()->currentIndex(); + m_connectionController->openSqlWindowForConnection(ci); } void ConnectionManagerWindow::on_actionQuit_application_triggered() @@ -64,25 +65,25 @@ void ConnectionManagerWindow::on_actionQuit_application_triggered() void ConnectionManagerWindow::on_actionBackup_database_triggered() { - auto ci = ui->listView->selectionModel()->currentIndex(); - m_connectionController->openBackupDlgForConnection(ci.row()); + auto ci = ui->treeView->selectionModel()->currentIndex(); + m_connectionController->openBackupDlgForConnection(ci); } void ConnectionManagerWindow::on_actionManage_server_triggered() { - auto ci = ui->listView->selectionModel()->currentIndex(); - m_connectionController->openServerWindowForConnection(ci.row()); + auto ci = ui->treeView->selectionModel()->currentIndex(); + m_connectionController->openServerWindowForConnection(ci); } void ConnectionManagerWindow::on_listView_activated(const QModelIndex &index) { if (index.isValid()) { - m_connectionController->openSqlWindowForConnection(index.row()); + m_connectionController->openSqlWindowForConnection(index); } } void ConnectionManagerWindow::on_actionConfigure_connection_triggered() { - auto ci = ui->listView->selectionModel()->currentIndex(); - m_connectionController->editConnection(ci.row()); + auto ci = ui->treeView->selectionModel()->currentIndex(); + m_connectionController->editConnection(ci); } diff --git a/pglab/ConnectionManagerWindow.ui b/pglab/ConnectionManagerWindow.ui index cca5094..84d535f 100644 --- a/pglab/ConnectionManagerWindow.ui +++ b/pglab/ConnectionManagerWindow.ui @@ -15,21 +15,6 @@ - - - - Qt::Horizontal - - - - true - - - true - - - - diff --git a/pglablib/ConnectionConfig.cpp b/pglablib/ConnectionConfig.cpp index f2ba553..486b7f0 100644 --- a/pglablib/ConnectionConfig.cpp +++ b/pglablib/ConnectionConfig.cpp @@ -57,7 +57,7 @@ ConnectionConfig::ConnectionConfig() : m_applicationName(QCoreApplication::applicationName().toUtf8().data()) {} -ConnectionGroup *ConnectionConfig::parent() +const ConnectionGroup *ConnectionConfig::parent() const { return m_group; } @@ -301,6 +301,21 @@ void ConnectionConfig::clean() m_dirty = false; } +QString ConnectionConfig::makeLongDescription() const +{ + std::string result(name()); + result += " ("; + result += user(); + result += "@"; + result += host(); + result += ":"; + result += std::to_string(port()); + result += "/"; + result += dbname(); + result += ")"; + return stdStrToQ(result); +} + /* PGHOST behaves the same as the host connection parameter. @@ -358,8 +373,21 @@ void ConnectionConfig::strToEnv(QProcessEnvironment &env, const QString &var, co env.insert(var, stdStrToQ(val)); } -void ConnectionGroup::add(std::shared_ptr cc) +void ConnectionGroup::erase(int idx, int count) +{ + m_connections.remove(idx, count); +} + +int ConnectionGroup::add(std::shared_ptr cc) { cc->setParent(this); m_connections.push_back(cc); + return m_connections.size() - 1; +} + +void ConnectionGroup::update(int idx, const ConnectionConfig &cc) +{ + auto node = m_connections[idx]; + *node = cc; + node->setParent(this); } diff --git a/pglablib/ConnectionConfig.h b/pglablib/ConnectionConfig.h index 20e9109..181909c 100644 --- a/pglablib/ConnectionConfig.h +++ b/pglablib/ConnectionConfig.h @@ -43,7 +43,10 @@ public: using Connections = QVector>; const Connections& connections() const { return m_connections; } - void add(std::shared_ptr cc); + void erase(int idx, int count = 1); + /// Adds cc to the group and returns the index within the group. + int add(std::shared_ptr cc); + void update(int idx, const ConnectionConfig &cc); bool operator==(const ConnectionGroup &rhs) const { return conngroup_id == rhs.conngroup_id @@ -57,7 +60,7 @@ class ConnectionConfig: public ConnectionNode { public: ConnectionConfig(); // Default object containing invalid uuid - ConnectionGroup* parent(); + const ConnectionGroup* parent() const; void setParent(ConnectionGroup *grp); void setUuid(const QUuid &uuid); @@ -113,6 +116,8 @@ public: void clean(); bool operator==(QUuid id) const { return m_uuid == id; } + + QString makeLongDescription() const; private: QUuid m_uuid; std::string m_name;