From 50bf52d586a1188c89a6f707354ef2025be2b23c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Salawa?= Date: Wed, 20 Nov 2024 01:56:41 +0100 Subject: [PATCH] #5079 Data cell focus and current data page is maintained during data reload. --- ChangeLog.md | 1 + .../coreSQLiteStudio/schemaresolver.cpp | 11 ++-- .../datagrid/sqldatasourcequerymodel.cpp | 4 +- .../datagrid/sqlquerymodel.cpp | 51 ++++++++++++++++++- .../guiSQLiteStudio/datagrid/sqlquerymodel.h | 18 ++++++- SQLiteStudio3/guiSQLiteStudio/dataview.cpp | 16 +++--- SQLiteStudio3/guiSQLiteStudio/dataview.h | 3 +- .../guiSQLiteStudio/windows/editorwindow.cpp | 2 +- .../guiSQLiteStudio/windows/tablewindow.cpp | 6 +-- .../guiSQLiteStudio/windows/viewwindow.cpp | 2 +- 10 files changed, 89 insertions(+), 25 deletions(-) diff --git a/ChangeLog.md b/ChangeLog.md index e03b46bc65..b4c27bc435 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -5,6 +5,7 @@ - ADDED: #5041 Added support for column list syntax in CREATE VIEW. - ADDED: #4774 Added checking if data types are matched in both columns while creating foreign column. This applies for Column Dialog Foreign Key, Table Foreign Key and also in the Query Executor (user will receive warning if executed query breaks the Foreign Key data type alignment). - ADDED: #4928 During exporting data in Export Dialog, the progress bar now shows number of rows already exported in real time (still filesystem may buffer writing operation, so data may appear in the output file with delay). +- ADDED: #5079 Data cell focus and current data page is maintained during data reload. - CHANGE: #4740 Dialog geometry is saved & restored for several dialog windows (i.e. Import, Export, Populate and more...). - CHANGE: #4861 When loading SQL script into SQL Editor, contents of current editor are retained. Instead another editor is open to load SQL script, with exception when current editor is empty - in that case it is used for loading. - CHANGE: #4861 When trying to load SQLite database as SQL script into SQL Editor window, application detects it and opens Database dialog for that file instead. diff --git a/SQLiteStudio3/coreSQLiteStudio/schemaresolver.cpp b/SQLiteStudio3/coreSQLiteStudio/schemaresolver.cpp index 121153bdb1..81147bda0d 100644 --- a/SQLiteStudio3/coreSQLiteStudio/schemaresolver.cpp +++ b/SQLiteStudio3/coreSQLiteStudio/schemaresolver.cpp @@ -1127,17 +1127,17 @@ StrHash SchemaResolver::getAllObjectDetails(const QList SchemaResolver::getParsedIndexesForTable(const QString& database, const QString& table) { - static_qstring(idxForTableTpl, "SELECT sql, name FROM %1.sqlite_master WHERE type = 'index' AND lower(tbl_name) = lower('%2');"); + static_qstring(idxForTableTpl, "SELECT sql, name FROM %1.sqlite_master WHERE type = 'index' AND lower(tbl_name) = lower(?);"); - QString query = idxForTableTpl.arg(getPrefixDb(database), escapeString(table)); - SqlQueryPtr results = db->exec(query, dbFlags); + QString query = idxForTableTpl.arg(getPrefixDb(database)); + SqlQueryPtr results = db->exec(query, {table}, dbFlags); QList createIndexList; for (SqlResultsRowPtr row : results->getAll()) { QString ddl = row->value(0).toString(); QString name = row->value(1).toString(); - if (isFilteredOut(name, "index")) + if (ddl.isEmpty() || isFilteredOut(name, "index")) continue; SqliteQueryPtr query = getParsedDdl(ddl); @@ -1147,7 +1147,8 @@ QList SchemaResolver::getParsedIndexesForTable(const QStri SqliteCreateIndexPtr createIndex = query.dynamicCast(); if (!createIndex) { - qWarning() << "Parsed DDL was not a CREATE INDEX statement, while queried for indexes."; + qWarning() << "Parsed DDL was not a CREATE INDEX statement, while queried for indexes. Queried db & table:" + << database << table << "Index name:" << name << "DDL:" << ddl; continue; } createIndexList << createIndex; diff --git a/SQLiteStudio3/guiSQLiteStudio/datagrid/sqldatasourcequerymodel.cpp b/SQLiteStudio3/guiSQLiteStudio/datagrid/sqldatasourcequerymodel.cpp index 7088970822..e02e77aff9 100644 --- a/SQLiteStudio3/guiSQLiteStudio/datagrid/sqldatasourcequerymodel.cpp +++ b/SQLiteStudio3/guiSQLiteStudio/datagrid/sqldatasourcequerymodel.cpp @@ -39,7 +39,7 @@ void SqlDataSourceQueryModel::applyFilter(const QString& value, FilterValueProce conditions << wrapObjIfNeeded(column->getAliasedName())+" "+valueProc(value); queryExecutor->setFilters(conditions.join(" OR ")); - executeQuery(); + executeQuery(true); } void SqlDataSourceQueryModel::applyFilter(const QStringList& values, FilterValueProcessor valueProc) @@ -135,7 +135,7 @@ void SqlDataSourceQueryModel::resetFilter() { // setQuery("SELECT * FROM "+getDataSource()); queryExecutor->setFilters(QString()); - executeQuery(); + executeQuery(true); } QString SqlDataSourceQueryModel::getDatabasePrefix() diff --git a/SQLiteStudio3/guiSQLiteStudio/datagrid/sqlquerymodel.cpp b/SQLiteStudio3/guiSQLiteStudio/datagrid/sqlquerymodel.cpp index 4490959b0d..3c44603043 100644 --- a/SQLiteStudio3/guiSQLiteStudio/datagrid/sqlquerymodel.cpp +++ b/SQLiteStudio3/guiSQLiteStudio/datagrid/sqlquerymodel.cpp @@ -81,7 +81,7 @@ void SqlQueryModel::setAsyncMode(bool enabled) queryExecutor->setAsyncMode(enabled); } -void SqlQueryModel::executeQuery() +void SqlQueryModel::executeQuery(bool enforcePage0) { if (queryExecutor->isExecutionInProgress()) { @@ -91,7 +91,7 @@ void SqlQueryModel::executeQuery() queryExecutor->setSkipRowCounting(false); queryExecutor->setSortOrder(sortOrder); - queryExecutor->setPage(0); + queryExecutor->setPage(page > -1 && !enforcePage0 ? page : 0); queryExecutor->setForceSimpleMode(simpleExecutionMode); reloading = false; @@ -157,6 +157,23 @@ bool SqlQueryModel::isEmptyQuery() const return true; } +void SqlQueryModel::restoreFocusedCell() +{ + if (!storedFocus.isValid() || getCurrentPage() != storedFocus.forPage || getRowsPerPage() != storedFocus.forRowsPerPage || + queryExecutor->getFilters() != storedFocus.forFilter) + { + forgetFocusedCell(); + return; + } + + QModelIndex idx = index(storedFocus.row, storedFocus.column); + if (idx.isValid()) + { + view->setCurrentIndex(idx); + view->scrollTo(idx, QAbstractItemView::EnsureVisible); + } +} + void SqlQueryModel::internalExecutionStopped() { reloading = false; @@ -712,6 +729,21 @@ void SqlQueryModel::detachDependencyTables() dbListToDetach.clear(); } +void SqlQueryModel::rememberFocusedCell() +{ + QModelIndex idx = getView()->currentIndex(); + storedFocus.row = idx.row(); + storedFocus.column = idx.column(); + storedFocus.forPage = getCurrentPage(); + storedFocus.forRowsPerPage = getRowsPerPage(); + storedFocus.forFilter = queryExecutor->getFilters(); +} + +void SqlQueryModel::forgetFocusedCell() +{ + storedFocus.reset(); +} + QString SqlQueryModel::generateSelectQueryForItems(const QList& items) { QHash values = toValuesGroupedByColumns(items); @@ -1439,6 +1471,7 @@ void SqlQueryModel::handleExecFinished(SqlQueryPtr results) results.clear(); detachDatabases(); } + restoreFocusedCell(); } void SqlQueryModel::handleExecFailed(int code, QString errorMessage) @@ -2298,3 +2331,17 @@ QString SqlQueryModel::SelectCellsQueryBuilder::getDatabase() const { return database; } + +bool SqlQueryModel::StoredFocus::isValid() +{ + return row > -1 && column > -1; +} + +void SqlQueryModel::StoredFocus::reset() +{ + row = -1; + column = -1; + forFilter.clear(); + forRowsPerPage = -1; + forPage = -1; +} diff --git a/SQLiteStudio3/guiSQLiteStudio/datagrid/sqlquerymodel.h b/SQLiteStudio3/guiSQLiteStudio/datagrid/sqlquerymodel.h index 43222f1987..7fb6938378 100644 --- a/SQLiteStudio3/guiSQLiteStudio/datagrid/sqlquerymodel.h +++ b/SQLiteStudio3/guiSQLiteStudio/datagrid/sqlquerymodel.h @@ -65,6 +65,8 @@ class GUI_API_EXPORT SqlQueryModel : public QStandardItemModel bool isExecutionInProgress() const; StrHash attachDependencyTables(); void detachDependencyTables(); + void rememberFocusedCell(); + void forgetFocusedCell(); /** * @brief Disables or re-enables async query execution @@ -244,6 +246,18 @@ class GUI_API_EXPORT SqlQueryModel : public QStandardItemModel int argSquence = 0; }; + struct StoredFocus + { + int row = -1; + int column = -1; + int forPage = -1; + int forRowsPerPage = -1; + QString forFilter; + + bool isValid(); + void reset(); + }; + /** * @brief commitAddedRow Inserts new row to a table. * @param itemsInRow All cells for the new row. @@ -316,6 +330,7 @@ class GUI_API_EXPORT SqlQueryModel : public QStandardItemModel QueryExecutor* queryExecutor = nullptr; Db* db = nullptr; QList columns; + StoredFocus storedFocus; /** * @brief tablesInUse @@ -385,6 +400,7 @@ class GUI_API_EXPORT SqlQueryModel : public QStandardItemModel void notifyItemEditionEnded(const QModelIndex& idx); int getRowsPerPage() const; bool isEmptyQuery() const; + void restoreFocusedCell(); QString query; QHash queryParams; @@ -522,7 +538,7 @@ class GUI_API_EXPORT SqlQueryModel : public QStandardItemModel void prevPage(); void nextPage(); void lastPage(); - void executeQuery(); + void executeQuery(bool enforcePage0 = false); void interrupt(); void commit(); void rollback(); diff --git a/SQLiteStudio3/guiSQLiteStudio/dataview.cpp b/SQLiteStudio3/guiSQLiteStudio/dataview.cpp index e7ae9b54ce..8ec00d0703 100644 --- a/SQLiteStudio3/guiSQLiteStudio/dataview.cpp +++ b/SQLiteStudio3/guiSQLiteStudio/dataview.cpp @@ -682,12 +682,6 @@ void DataView::setFormViewEnabled(bool enabled) setTabEnabled(1, enabled); } -void DataView::readData() -{ - setNavigationState(false); - model->executeQuery(); -} - bool DataView::isUncommitted() const { return uncommittedGrid || uncommittedForm; @@ -718,10 +712,16 @@ void DataView::totalRowsAndPagesAvailable() updateNavigationState(); } -void DataView::refreshData() +void DataView::refreshData(bool keepFocus) { totalPagesAvailable = false; - readData(); + if (keepFocus) + model->rememberFocusedCell(); + else + model->forgetFocusedCell(); + + setNavigationState(false); + model->executeQuery(!keepFocus); } void DataView::insertRow() diff --git a/SQLiteStudio3/guiSQLiteStudio/dataview.h b/SQLiteStudio3/guiSQLiteStudio/dataview.h index d8f71dcd99..43b650766a 100644 --- a/SQLiteStudio3/guiSQLiteStudio/dataview.h +++ b/SQLiteStudio3/guiSQLiteStudio/dataview.h @@ -138,7 +138,6 @@ class GUI_API_EXPORT DataView : public QTabWidget, public ExtActionContainer void updateResultsCount(int resultsCount); void updateCurrentFormViewRow(); void setFormViewEnabled(bool enabled); - void readData(); void initFormViewForNewRow(); void formViewFocusFirstEditor(); void recreateFilterInputs(); @@ -179,7 +178,7 @@ class GUI_API_EXPORT DataView : public QTabWidget, public ExtActionContainer signals: public slots: - void refreshData(); + void refreshData(bool keepFocus = true); private slots: void dataLoadingEnded(bool successful); diff --git a/SQLiteStudio3/guiSQLiteStudio/windows/editorwindow.cpp b/SQLiteStudio3/guiSQLiteStudio/windows/editorwindow.cpp index 27f4cca480..bcf88f0a26 100644 --- a/SQLiteStudio3/guiSQLiteStudio/windows/editorwindow.cpp +++ b/SQLiteStudio3/guiSQLiteStudio/windows/editorwindow.cpp @@ -473,7 +473,7 @@ void EditorWindow::execQuery(bool explain, QueryExecMode querySelectionMode) resultsModel->setQuery(sql); resultsModel->setParams(bindParams); resultsModel->setQueryCountLimitForSmartMode(queryLimitForSmartExecution); - ui->dataView->refreshData(); + ui->dataView->refreshData(false); updateState(); if (resultsDisplayMode == ResultsDisplayMode::SEPARATE_TAB) diff --git a/SQLiteStudio3/guiSQLiteStudio/windows/tablewindow.cpp b/SQLiteStudio3/guiSQLiteStudio/windows/tablewindow.cpp index 7044df943b..8b70cae693 100644 --- a/SQLiteStudio3/guiSQLiteStudio/windows/tablewindow.cpp +++ b/SQLiteStudio3/guiSQLiteStudio/windows/tablewindow.cpp @@ -1313,7 +1313,7 @@ void TableWindow::importTable() ImportDialog dialog(this); dialog.setDbAndTable(db, table); if (dialog.exec() == QDialog::Accepted && dataLoaded) - ui->dataView->refreshData(); + ui->dataView->refreshData(false); } void TableWindow::populateTable() @@ -1321,7 +1321,7 @@ void TableWindow::populateTable() PopulateDialog dialog(this); dialog.setDbAndTable(db, table); if (dialog.exec() == QDialog::Accepted && dataLoaded) - ui->dataView->refreshData(); + ui->dataView->refreshData(false); } void TableWindow::createSimilarTable() @@ -1354,7 +1354,7 @@ void TableWindow::tabChanged(int newTab) } if (!dataLoaded) - ui->dataView->refreshData(); + ui->dataView->refreshData(false); } } diff --git a/SQLiteStudio3/guiSQLiteStudio/windows/viewwindow.cpp b/SQLiteStudio3/guiSQLiteStudio/windows/viewwindow.cpp index c1eb5d9e0c..73575c4482 100644 --- a/SQLiteStudio3/guiSQLiteStudio/windows/viewwindow.cpp +++ b/SQLiteStudio3/guiSQLiteStudio/windows/viewwindow.cpp @@ -627,7 +627,7 @@ void ViewWindow::tabChanged(int tabIdx) } if (!dataLoaded) - ui->dataView->refreshData(); + ui->dataView->refreshData(false); return; }