diff --git a/CMakeLists.txt b/CMakeLists.txt index b8625ad2..2c1485cc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -145,6 +145,7 @@ set(SQLB_MOC_HDR src/sqltextedit.h src/docktextedit.h src/DbStructureModel.h + src/dbstructureqitemviewfacade.h src/Application.h src/CipherDialog.h src/ExportSqlDialog.h @@ -202,6 +203,7 @@ set(SQLB_SRC src/docktextedit.cpp src/csvparser.cpp src/DbStructureModel.cpp + src/dbstructureqitemviewfacade.cpp src/main.cpp src/Application.cpp src/CipherDialog.cpp diff --git a/src/MainWindow.cpp b/src/MainWindow.cpp index a1d42764..0a524cd9 100644 --- a/src/MainWindow.cpp +++ b/src/MainWindow.cpp @@ -80,6 +80,8 @@ MainWindow::MainWindow(QWidget* parent) MainWindow::~MainWindow() { + delete dbSelected; + delete dockDbSelected; delete ui; } @@ -117,6 +119,10 @@ void MainWindow::init() }); #endif + // create facade objects to dbTreeWidgets + dbSelected = new DbStructureQItemViewFacade(*ui->dbTreeWidget); + dockDbSelected = new DbStructureQItemViewFacade(*ui->treeSchemaDock); + // Connect SQL logging and database state setting to main window connect(&db, &DBBrowserDB::dbChanged, this, &MainWindow::dbState, Qt::QueuedConnection); connect(&db, &DBBrowserDB::sqlExecuted, this, &MainWindow::logSql, Qt::QueuedConnection); @@ -446,9 +452,7 @@ void MainWindow::init() // Action for switching the table via the Database Structure tab connect(ui->actionPopupSchemaDockBrowseTable, &QAction::triggered, [this]() { - sqlb::ObjectIdentifier obj(ui->treeSchemaDock->model()->data(ui->treeSchemaDock->currentIndex().sibling(ui->treeSchemaDock->currentIndex().row(), DbStructureModel::ColumnSchema), Qt::EditRole).toString().toStdString(), - ui->treeSchemaDock->model()->data(ui->treeSchemaDock->currentIndex().sibling(ui->treeSchemaDock->currentIndex().row(), DbStructureModel::ColumnName), Qt::EditRole).toString().toStdString()); - switchToBrowseDataTab(obj); + switchToBrowseDataTab(dockDbSelected->object()); refresh(); // Required in case the Browse Data tab already was the active main tab }); @@ -842,9 +846,8 @@ void MainWindow::compact() void MainWindow::deleteObject() { // Get name and type of object to delete - sqlb::ObjectIdentifier name(ui->dbTreeWidget->model()->data(ui->dbTreeWidget->currentIndex().sibling(ui->dbTreeWidget->currentIndex().row(), DbStructureModel::ColumnSchema), Qt::EditRole).toString().toStdString(), - ui->dbTreeWidget->model()->data(ui->dbTreeWidget->currentIndex().sibling(ui->dbTreeWidget->currentIndex().row(), DbStructureModel::ColumnName), Qt::EditRole).toString().toStdString()); - QString type = ui->dbTreeWidget->model()->data(ui->dbTreeWidget->currentIndex().sibling(ui->dbTreeWidget->currentIndex().row(), DbStructureModel::ColumnObjectType), Qt::EditRole).toString(); + sqlb::ObjectIdentifier obj = dbSelected->object(); + QString type = dbSelected->objectType(); // Due to different grammar in languages (e.g. gender or declension), each message must be given separately to translation. QString message; @@ -858,11 +861,11 @@ void MainWindow::deleteObject() message = tr("Are you sure you want to delete the index '%1'?"); // Ask user if he really wants to delete that table - if(QMessageBox::warning(this, QApplication::applicationName(), message.arg(QString::fromStdString(name.name())), + if(QMessageBox::warning(this, QApplication::applicationName(), message.arg(QString::fromStdString(obj.name())), QMessageBox::Yes | QMessageBox::No, QMessageBox::No) == QMessageBox::Yes) { // Delete the table - QString statement = QString("DROP %1 %2;").arg(type.toUpper(), QString::fromStdString(name.toString())); + QString statement = QString("DROP %1 %2;").arg(type.toUpper(), QString::fromStdString(obj.toString())); if(!db.executeSQL(statement.toStdString())) { if (type == "table") @@ -885,13 +888,12 @@ void MainWindow::deleteObject() void MainWindow::editObject() { - if(!ui->dbTreeWidget->selectionModel()->hasSelection()) + if(!dbSelected->hasSelection()) return; // Get name and type of the object to edit - sqlb::ObjectIdentifier name(ui->dbTreeWidget->model()->data(ui->dbTreeWidget->currentIndex().sibling(ui->dbTreeWidget->currentIndex().row(), DbStructureModel::ColumnSchema), Qt::EditRole).toString().toStdString(), - ui->dbTreeWidget->model()->data(ui->dbTreeWidget->currentIndex().sibling(ui->dbTreeWidget->currentIndex().row(), DbStructureModel::ColumnName), Qt::EditRole).toString().toStdString()); - QString type = ui->dbTreeWidget->model()->data(ui->dbTreeWidget->currentIndex().sibling(ui->dbTreeWidget->currentIndex().row(), DbStructureModel::ColumnObjectType), Qt::EditRole).toString(); + sqlb::ObjectIdentifier obj = dbSelected->object(); + QString type = dbSelected->objectType(); if(type == "table") { @@ -912,12 +914,12 @@ void MainWindow::editObject() db.setPragma("foreign_keys", "0"); } - EditTableDialog dialog(db, name, false, this); + EditTableDialog dialog(db, obj, false, this); bool ok = dialog.exec(); // If foreign_keys were enabled, we must commit or rollback the transaction so the foreign_keys pragma can be restored. if (foreign_keys == "1") { - if (!db.querySingleValueFromDb("PRAGMA " + sqlb::escapeIdentifier(name.schema()) + ".foreign_key_check").isNull()) { + if (!db.querySingleValueFromDb("PRAGMA " + sqlb::escapeIdentifier(obj.schema()) + ".foreign_key_check").isNull()) { // Raise warning for accepted modification. When rejected, warn user also since we know now that the table has problems, // but it wasn't our fault. if (ok) @@ -937,25 +939,25 @@ void MainWindow::editObject() if(ok) { for(const auto& t : allTableBrowserWidgets()) { - if(t->currentlyBrowsedTableName() == name) + if(t->currentlyBrowsedTableName() == obj) t->clearFilters(); } refreshTableBrowsers(); } } else if(type == "index") { - EditIndexDialog dialog(db, name, false, this); + EditIndexDialog dialog(db, obj, false, this); if(dialog.exec()) refreshTableBrowsers(); } else if(type == "view") { - sqlb::ViewPtr view = db.getObjectByName(name); - runSqlNewTab(QString("DROP VIEW %1;\n%2").arg(QString::fromStdString(name.toString())).arg(QString::fromStdString(view->sql())), - tr("Edit View %1").arg(QString::fromStdString(name.toDisplayString())), + sqlb::ViewPtr view = db.getObjectByName(obj); + runSqlNewTab(QString("DROP VIEW %1;\n%2").arg(QString::fromStdString(obj.toString())).arg(QString::fromStdString(view->sql())), + tr("Edit View %1").arg(QString::fromStdString(obj.toDisplayString())), "https://www.sqlite.org/lang_createview.html", /* autoRun */ false); } else if(type == "trigger") { - sqlb::TriggerPtr trigger = db.getObjectByName(name); - runSqlNewTab(QString("DROP TRIGGER %1;\n%2").arg(QString::fromStdString(name.toString())).arg(QString::fromStdString(trigger->sql())), - tr("Edit Trigger %1").arg(QString::fromStdString(name.toDisplayString())), + sqlb::TriggerPtr trigger = db.getObjectByName(obj); + runSqlNewTab(QString("DROP TRIGGER %1;\n%2").arg(QString::fromStdString(obj.toString())).arg(QString::fromStdString(trigger->sql())), + tr("Edit Trigger %1").arg(QString::fromStdString(obj.toDisplayString())), "https://www.sqlite.org/lang_createtrigger.html", /* autoRun */ false); } @@ -1337,12 +1339,10 @@ void MainWindow::exportTableToCSV() sqlb::ObjectIdentifier current_table; if(ui->mainTab->currentWidget() == ui->structure) { - QString type = ui->dbTreeWidget->model()->data(ui->dbTreeWidget->currentIndex().sibling(ui->dbTreeWidget->currentIndex().row(), DbStructureModel::ColumnObjectType)).toString(); + QString type = dbSelected->objectType(); if(type == "table" || type == "view") { - QString schema = ui->dbTreeWidget->model()->data(ui->dbTreeWidget->currentIndex().sibling(ui->dbTreeWidget->currentIndex().row(), DbStructureModel::ColumnSchema)).toString(); - QString name = ui->dbTreeWidget->model()->data(ui->dbTreeWidget->currentIndex().sibling(ui->dbTreeWidget->currentIndex().row(), DbStructureModel::ColumnName)).toString(); - current_table = sqlb::ObjectIdentifier(schema.toStdString(), name.toStdString()); + current_table = dbSelected->object(); } } else if(ui->mainTab->currentWidget() == ui->browser) { current_table = currentlyBrowsedTableName(); @@ -1359,12 +1359,10 @@ void MainWindow::exportTableToJson() sqlb::ObjectIdentifier current_table; if(ui->mainTab->currentWidget() == ui->structure) { - QString type = ui->dbTreeWidget->model()->data(ui->dbTreeWidget->currentIndex().sibling(ui->dbTreeWidget->currentIndex().row(), DbStructureModel::ColumnObjectType)).toString(); + QString type = dbSelected->objectType(); if(type == "table" || type == "view") { - QString schema = ui->dbTreeWidget->model()->data(ui->dbTreeWidget->currentIndex().sibling(ui->dbTreeWidget->currentIndex().row(), DbStructureModel::ColumnSchema)).toString(); - QString name = ui->dbTreeWidget->model()->data(ui->dbTreeWidget->currentIndex().sibling(ui->dbTreeWidget->currentIndex().row(), DbStructureModel::ColumnName)).toString(); - current_table = sqlb::ObjectIdentifier(schema.toStdString(), name.toStdString()); + current_table = dbSelected->object(); } } else if(ui->mainTab->currentWidget() == ui->browser) { current_table = currentlyBrowsedTableName(); @@ -1502,11 +1500,10 @@ void MainWindow::openPreferences() //** Db Tree Context Menu void MainWindow::createTreeContextMenu(const QPoint &qPoint) { - if(!ui->dbTreeWidget->selectionModel()->hasSelection()) + if(!dbSelected->hasSelection()) return; - QString type = ui->dbTreeWidget->model()->data(ui->dbTreeWidget->currentIndex().sibling(ui->dbTreeWidget->currentIndex().row(), 1)).toString(); - + QString type = dbSelected->objectType(); if(type == "table" || type == "view" || type == "trigger" || type == "index") popupTableMenu->exec(ui->dbTreeWidget->mapToGlobal(qPoint)); } @@ -1515,9 +1512,10 @@ void MainWindow::createTreeContextMenu(const QPoint &qPoint) void MainWindow::createSchemaDockContextMenu(const QPoint &qPoint) { bool enable_browse_table = false; - if(ui->treeSchemaDock->selectionModel()->hasSelection()) + + if(dockDbSelected->hasSelection()) { - QString type = ui->treeSchemaDock->model()->data(ui->treeSchemaDock->currentIndex().sibling(ui->treeSchemaDock->currentIndex().row(), DbStructureModel::ColumnObjectType), Qt::EditRole).toString(); + QString type = dockDbSelected->objectType(); if(type == "table" || type == "view") enable_browse_table = true; } @@ -1533,11 +1531,11 @@ void MainWindow::changeTreeSelection() ui->editModifyObjectAction->setEnabled(false); ui->actionEditBrowseTable->setEnabled(false); - if(!ui->dbTreeWidget->currentIndex().isValid()) + if(!dbSelected->hasSelection()) return; // Change the text and tooltips of the actions - QString type = ui->dbTreeWidget->model()->data(ui->dbTreeWidget->currentIndex().sibling(ui->dbTreeWidget->currentIndex().row(), 1)).toString(); + QString type = dbSelected->objectType(); if (type.isEmpty()) { @@ -3142,11 +3140,11 @@ void MainWindow::switchToBrowseDataTab(sqlb::ObjectIdentifier tableToBrowse) if(tableToBrowse.isEmpty()) { // Cancel here if there is no selection - if(!ui->dbTreeWidget->selectionModel()->hasSelection()) + if(!dbSelected->hasSelection()) return; - tableToBrowse.setSchema(ui->dbTreeWidget->model()->data(ui->dbTreeWidget->currentIndex().sibling(ui->dbTreeWidget->currentIndex().row(), DbStructureModel::ColumnSchema), Qt::EditRole).toString().toStdString()); - tableToBrowse.setName(ui->dbTreeWidget->model()->data(ui->dbTreeWidget->currentIndex().sibling(ui->dbTreeWidget->currentIndex().row(), DbStructureModel::ColumnName), Qt::EditRole).toString().toStdString()); + tableToBrowse.setSchema(dbSelected->schema().toStdString()); + tableToBrowse.setName(dbSelected->name().toStdString()); } TableBrowserDock* d = newTableBrowserTab(tableToBrowse); @@ -3167,11 +3165,11 @@ void MainWindow::switchToBrowseDataTab(sqlb::ObjectIdentifier tableToBrowse) void MainWindow::copyCurrentCreateStatement() { // Cancel if no field is currently selected - if(!ui->dbTreeWidget->selectionModel()->hasSelection()) + if(!dbSelected->hasSelection()) return; - // Get the CREATE statement from the Schema column - QString stmt = ui->dbTreeWidget->model()->data(ui->dbTreeWidget->currentIndex().sibling(ui->dbTreeWidget->currentIndex().row(), 3), Qt::EditRole).toString(); + // Get the CREATE statement from the SQL column + QString stmt = dbSelected->sql(); // Copy the statement to the global application clipboard QApplication::clipboard()->setText(stmt); diff --git a/src/MainWindow.h b/src/MainWindow.h index ff531a6d..16cda19f 100644 --- a/src/MainWindow.h +++ b/src/MainWindow.h @@ -1,6 +1,7 @@ #ifndef MAINWINDOW_H #define MAINWINDOW_H +#include "dbstructureqitemviewfacade.h" #include "sqlitedb.h" #include @@ -90,6 +91,16 @@ private: QAction* clearRecentFilesAction; QAction* recentSeparatorAct; + /** + * @brief dbSelected provides a simple read only interface to the actual selected node of ui->dbTreeWidget + */ + DbStructureQItemViewFacade *dbSelected; + + /** + * @brief dockDbSelected provides a simple read only interface to the actual selected node of ui->treeSchemaDock + */ + DbStructureQItemViewFacade *dockDbSelected; + EditDialog* editDock; PlotDock* plotDock; RemoteDock* remoteDock; diff --git a/src/dbstructureqitemviewfacade.cpp b/src/dbstructureqitemviewfacade.cpp new file mode 100644 index 00000000..3d6dac27 --- /dev/null +++ b/src/dbstructureqitemviewfacade.cpp @@ -0,0 +1,59 @@ +#include "DbStructureModel.h" +#include "dbstructureqitemviewfacade.h" + +DbStructureQItemViewFacade::DbStructureQItemViewFacade(QAbstractItemView& aitemView) + : QObject(&aitemView), + m_itemView(aitemView) +{ + +} + +const QAbstractItemView& DbStructureQItemViewFacade::itemView() const +{ + return m_itemView; +} + +bool DbStructureQItemViewFacade::hasSelection() const +{ + return m_itemView.selectionModel()->hasSelection(); +} + +QString DbStructureQItemViewFacade::displayName() const +{ + QAbstractItemModel *model = m_itemView.model(); + QModelIndex index = m_itemView.currentIndex(); + return model->data(index.sibling(index.row(), DbStructureModel::ColumnName), Qt::DisplayRole).toString(); +} + +QString DbStructureQItemViewFacade::name() const +{ + QAbstractItemModel *model = m_itemView.model(); + QModelIndex index = m_itemView.currentIndex(); + return model->data(index.sibling(index.row(), DbStructureModel::ColumnName), Qt::EditRole).toString(); +} + +QString DbStructureQItemViewFacade::objectType() const +{ + QAbstractItemModel *model = m_itemView.model(); + QModelIndex index = m_itemView.currentIndex(); + return model->data(index.sibling(index.row(), DbStructureModel::ColumnObjectType), Qt::EditRole).toString(); +} + +QString DbStructureQItemViewFacade::schema() const +{ + QAbstractItemModel *model = m_itemView.model(); + QModelIndex index = m_itemView.currentIndex(); + return model->data(index.sibling(index.row(), DbStructureModel::ColumnSchema), Qt::EditRole).toString(); +} + +QString DbStructureQItemViewFacade::sql() const +{ + QAbstractItemModel *model = m_itemView.model(); + QModelIndex index = m_itemView.currentIndex(); + return model->data(index.sibling(index.row(), DbStructureModel::ColumnSQL), Qt::EditRole).toString(); +} + +sqlb::ObjectIdentifier DbStructureQItemViewFacade::object() const +{ + return sqlb::ObjectIdentifier(schema().toStdString(), name().toStdString()); +} diff --git a/src/dbstructureqitemviewfacade.h b/src/dbstructureqitemviewfacade.h new file mode 100644 index 00000000..efdddfbd --- /dev/null +++ b/src/dbstructureqitemviewfacade.h @@ -0,0 +1,107 @@ +#ifndef DBSTRUCTUREQITEMVIEWFACADE_H +#define DBSTRUCTUREQITEMVIEWFACADE_H + +#include +#include +#include +#include +#include + +#include + + +/** + * @brief The DbStructureQItemViewFacade class + * Provides a simple read only interface to a QAbstractItemView (e.g. QTreeView) that holds a model() of type DBStructureModel. + * + * It is designed to simplify the access to the actual selected node. + * + * The Class follows the facade design pattern. + * But it doesn't control the lifecycle of the itemView it is connected to. + * + */ +class DbStructureQItemViewFacade : public QObject +{ + Q_OBJECT +public: + explicit DbStructureQItemViewFacade(QAbstractItemView& aitemView); + + /** + * @brief itemView returns the itemView that is hiding behind the facade. + * ItemView is connected on construction, the facadeObject has to be destroyed before the itemView. + * + * @return connected itemView + */ + const QAbstractItemView& itemView() const; + + /** + * @brief hasSelection returns true, if the itemView() has a selected Item. + * itemView().selectionModel().hasSelection() + * + * @return true, if the itemView() has a selected Item. + ***/ + bool hasSelection() const; + + /** + * @brief displayName returns the displayName of the itemViews selectedItem. + * + * It is taken from the model().data() with column=ColumnName and role=Qt::DisplayRole + * + * For the display role and the browsable branch of the tree we want to show the column name including the schema name if necessary (i.e. + * for schemata != "main"). For the normal structure branch of the tree we don't want to add the schema name because it's already obvious from + * the position of the item in the tree. + * + * @return displayName of selectedItem + */ + QString displayName() const; + + /** + * @brief returns the name of the itemViews selectedItem without decorations. + * It is taken from the model().data() with column=ColumnName and role=Qt::EditRole + * + * @return name of selectedItem + */ + QString name() const; + + /** + * @brief objectType returns the objectType of the itemViews selectedItem. + * + * It is taken from the model().data() with column=ColumnObjectType and role=Qt::EditRole + * + * @return + */ + QString objectType() const; + + + /** + * @brief schema returns the schema of the itemViews selectedItem. + * + * It is taken from the model().data() with column=ColumnSchema and role=Qt::EditRole + * + * @return + */ + QString schema() const; + + /** + * @brief sql returns the sql statement of the itemViews selectedItem. + * + * It is taken from the model().data() with column=ColumnSQL and role=Qt::EditRole + * + * @return + */ + QString sql() const; + + /** + * @brief Object returns a sqlb::ObjectIdentifier to the selected itemView item. + * Object identifier consisting of schema name and object name. + * + * @return sqlb::ObjectIdentifier to selected item + */ + sqlb::ObjectIdentifier object() const; + +private: + QAbstractItemView& m_itemView; + +}; + +#endif diff --git a/src/src.pro b/src/src.pro index 388e5225..4b879c67 100644 --- a/src/src.pro +++ b/src/src.pro @@ -26,6 +26,7 @@ HEADERS += \ RemoteLocalFilesModel.h \ RemoteNetwork.h \ TableBrowserDock.h \ + dbstructureqitemviewfacade.h \ sqlitedb.h \ MainWindow.h \ EditIndexDialog.h \ @@ -91,6 +92,7 @@ SOURCES += \ RemoteLocalFilesModel.cpp \ RemoteNetwork.cpp \ TableBrowserDock.cpp \ + dbstructureqitemviewfacade.cpp \ sqlitedb.cpp \ MainWindow.cpp \ EditIndexDialog.cpp \