From 93cce3d87f1a638d69a12a78502f8c69ce03496b Mon Sep 17 00:00:00 2001 From: Peinthor Rene Date: Sat, 6 Apr 2013 12:22:50 +0200 Subject: [PATCH 01/27] first working prototype --- src/MainWindow.cpp | 156 ++++++++++++++++++++------------------- src/MainWindow.h | 2 + src/sqlitetablemodel.cpp | 152 ++++++++++++++++++++++++++++++++++++++ src/sqlitetablemodel.h | 42 +++++++++++ src/src.pro | 6 +- 5 files changed, 281 insertions(+), 77 deletions(-) create mode 100644 src/sqlitetablemodel.cpp create mode 100644 src/sqlitetablemodel.h diff --git a/src/MainWindow.cpp b/src/MainWindow.cpp index 94575d82..2a906480 100644 --- a/src/MainWindow.cpp +++ b/src/MainWindow.cpp @@ -21,6 +21,7 @@ #include "FindDialog.h" #include "SQLiteSyntaxHighlighter.h" #include "sqltextedit.h" +#include "sqlitetablemodel.h" MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent), @@ -51,6 +52,8 @@ void MainWindow::init() // Init the SQL log dock db.mainWindow = this; + m_browseTableModel = new SqliteTableModel(this, &db); + // Set up the db tree widget ui->dbTreeWidget->setColumnHidden(1, true); ui->dbTreeWidget->setColumnWidth(0, 300); @@ -63,7 +66,8 @@ void MainWindow::init() createSyntaxHighlighters(); // Set up DB models - ui->dataTable->setModel(browseTableModel); + //ui->dataTable->setModel(browseTableModel); + ui->dataTable->setModel(m_browseTableModel); queryResultListModel = new QStandardItemModel(this); ui->queryResultTableView->setModel(queryResultListModel); @@ -270,43 +274,45 @@ void MainWindow::populateStructure() void MainWindow::populateTable( const QString & tablename, bool keepColumnWidths) { - bool mustreset = false; QApplication::setOverrideCursor( Qt::WaitCursor ); - if (tablename.compare(db.curBrowseTableName)!=0) - { - mustreset = true; - curBrowseOrderByIndex = 1; - curBrowseOrderByMode = ORDERMODE_ASC; - } + if(!tablename.isEmpty()) + m_browseTableModel->setQuery(QString("SELECT * FROM `%1`").arg(tablename)); +// bool mustreset = false; +// if (tablename.compare(db.curBrowseTableName)!=0) +// { +// mustreset = true; +// curBrowseOrderByIndex = 1; +// curBrowseOrderByMode = ORDERMODE_ASC; +// } - QString orderby = QString::number(curBrowseOrderByIndex) + " " + (curBrowseOrderByMode == ORDERMODE_ASC ? "ASC" : "DESC"); - if(!db.browseTable(tablename, orderby)) - { - browseTableModel->setRowCount(0); - browseTableModel->setColumnCount(0); - QApplication::restoreOverrideCursor(); - if(findWin) - findWin->resetFields(db.getTableFields("")); - return; - } +// QString orderby = QString::number(curBrowseOrderByIndex) + " " + (curBrowseOrderByMode == ORDERMODE_ASC ? "ASC" : "DESC"); +// if(!db.browseTable(tablename, orderby)) +// { +// browseTableModel->setRowCount(0); +// browseTableModel->setColumnCount(0); +// QApplication::restoreOverrideCursor(); +// if(findWin) +// findWin->resetFields(db.getTableFields("")); +// return; +// } - // Activate the add and delete record buttons and editing only if a table has been selected - bool is_table = db.getObjectByName(tablename).gettype() == "table"; - ui->buttonNewRecord->setEnabled(is_table); - ui->buttonDeleteRecord->setEnabled(is_table); - ui->dataTable->setEditTriggers(is_table ? QAbstractItemView::DoubleClicked | QAbstractItemView::AnyKeyPressed | QAbstractItemView::EditKeyPressed : QAbstractItemView::NoEditTriggers); +// // Activate the add and delete record buttons and editing only if a table has been selected +// bool is_table = db.getObjectByName(tablename).gettype() == "table"; +// ui->buttonNewRecord->setEnabled(is_table); +// ui->buttonDeleteRecord->setEnabled(is_table); +// ui->dataTable->setEditTriggers(is_table ? QAbstractItemView::DoubleClicked | QAbstractItemView::AnyKeyPressed | QAbstractItemView::EditKeyPressed : QAbstractItemView::NoEditTriggers); - if (mustreset){ - updateTableView(0, keepColumnWidths); - if (findWin) findWin->resetFields(db.getTableFields(db.curBrowseTableName)); - } else { - updateTableView(-1, keepColumnWidths); - } - //got to keep findWin in synch - if(findWin) - findWin->resetFields(); - if(editWin) - editWin->reset(); +// if (mustreset){ +// updateTableView(0, keepColumnWidths); +// if (findWin) findWin->resetFields(db.getTableFields(db.curBrowseTableName)); +// } else { +// updateTableView(-1, keepColumnWidths); +// } +// //got to keep findWin in synch +// if(findWin) +// findWin->resetFields(); +// if(editWin) +// editWin->reset(); QApplication::restoreOverrideCursor(); } @@ -423,52 +429,52 @@ void MainWindow::updateTableView(int lineToSelect, bool keepColumnWidths) { QApplication::setOverrideCursor( Qt::WaitCursor ); - browseTableModel->setRowCount(db.getRecordCount()); - browseTableModel->setColumnCount(db.browseFields.count()); - browseTableModel->setHorizontalHeaderLabels(db.browseFields); +// browseTableModel->setRowCount(db.getRecordCount()); +// browseTableModel->setColumnCount(db.browseFields.count()); +// browseTableModel->setHorizontalHeaderLabels(db.browseFields); - rowList tab = db.browseRecs; - int maxRecs = db.getRecordCount(); - gotoValidator->setRange(0, maxRecs); +// rowList tab = db.browseRecs; +// int maxRecs = db.getRecordCount(); +// gotoValidator->setRange(0, maxRecs); - if ( maxRecs > 0 ) { +// if ( maxRecs > 0 ) { - int rowNum = 0; - int colNum = 0; - QString rowLabel; - for (int i = 0; i < tab.size(); ++i) - { - rowLabel.setNum(rowNum+1); - browseTableModel->setVerticalHeaderItem(rowNum, new QStandardItem(rowLabel)); - colNum = 0; - QList rt = tab[i]; - for (int e = 1; e < rt.size(); ++e) - { - QString content = rt[e]; +// int rowNum = 0; +// int colNum = 0; +// QString rowLabel; +// for (int i = 0; i < tab.size(); ++i) +// { +// rowLabel.setNum(rowNum+1); +// browseTableModel->setVerticalHeaderItem(rowNum, new QStandardItem(rowLabel)); +// colNum = 0; +// QList rt = tab[i]; +// for (int e = 1; e < rt.size(); ++e) +// { +// QString content = rt[e]; - QStandardItem* item = new QStandardItem(content); - item->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled); - item->setToolTip(wrapText(content)); - browseTableModel->setItem( rowNum, colNum, item); - colNum++; - } - rowNum++; - if (rowNum==maxRecs) break; - } - } +// QStandardItem* item = new QStandardItem(content); +// item->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled); +// item->setToolTip(wrapText(content)); +// browseTableModel->setItem( rowNum, colNum, item); +// colNum++; +// } +// rowNum++; +// if (rowNum==maxRecs) break; +// } +// } - if(!keepColumnWidths) { - for(int i=0;icolumnCount();++i) - { - ui->dataTable->resizeColumnToContents(i); - if( ui->dataTable->columnWidth(i) > 400 ) - ui->dataTable->setColumnWidth(i, 400); - } - } - if (lineToSelect!=-1){ - selectTableLine(lineToSelect); - } - setRecordsetLabel(); +// if(!keepColumnWidths) { +// for(int i=0;icolumnCount();++i) +// { +// ui->dataTable->resizeColumnToContents(i); +// if( ui->dataTable->columnWidth(i) > 400 ) +// ui->dataTable->setColumnWidth(i, 400); +// } +// } +// if (lineToSelect!=-1){ +// selectTableLine(lineToSelect); +// } +// setRecordsetLabel(); QApplication::restoreOverrideCursor(); } diff --git a/src/MainWindow.h b/src/MainWindow.h index 2a9c38f1..a95674b7 100644 --- a/src/MainWindow.h +++ b/src/MainWindow.h @@ -15,6 +15,7 @@ class QStandardItemModel; class QIntValidator; class QLabel; class QModelIndex; +class SqliteTableModel; namespace Ui { class MainWindow; @@ -53,6 +54,7 @@ private: Ui::MainWindow* ui; QStandardItemModel *browseTableModel; + SqliteTableModel* m_browseTableModel; QStandardItemModel *queryResultListModel; QMenu *popupTableMenu; QMenu *recentFilesMenu; diff --git a/src/sqlitetablemodel.cpp b/src/sqlitetablemodel.cpp new file mode 100644 index 00000000..a5c79a7d --- /dev/null +++ b/src/sqlitetablemodel.cpp @@ -0,0 +1,152 @@ +#include "sqlitetablemodel.h" + +#include "sqlitedb.h" + +#include + +SqliteTableModel::SqliteTableModel(QObject* parent, DBBrowserDB* db) + : QAbstractTableModel(parent) + , m_db(db) + , m_rowCount(0) + , m_columnCount(0) + , m_chunkSize(1000) +{ +} + +void SqliteTableModel::setChunkSize(size_t chunksize) +{ + m_chunkSize = chunksize; +} + +void SqliteTableModel::setQuery(const QString& sQuery) +{ + if(!m_db->isOpen()) + return; + + sqlite3_stmt *stmt; + m_rowCount = 0; + m_sQuery = sQuery; + + // do a count query to get the full row count in a fast manner + QString sCountQuery = QString("SELECT COUNT(*) FROM (%1);").arg(sQuery); + QByteArray utf8Query = sCountQuery.toUtf8(); + int status = sqlite3_prepare_v2(m_db->_db, utf8Query, utf8Query.size(), &stmt, NULL); + + if(SQLITE_OK == status) + { + status = sqlite3_step(stmt); + if(SQLITE_ROW == status) + { + m_columnCount = sqlite3_data_count(stmt); + QString sCount = QString::fromUtf8((const char *) sqlite3_column_text(stmt, 0)); + m_rowCount = sCount.toInt(); + } + } + sqlite3_finalize(stmt); + + // now fetch the first 100 entries and get headers + m_headers.clear(); + m_columnCount = 0; + QString sLimitQuery = QString("%1 LIMIT 0, %2;").arg(sQuery).arg(m_chunkSize); + utf8Query = sLimitQuery.toUtf8(); + status = sqlite3_prepare_v2(m_db->_db, utf8Query, utf8Query.size(), &stmt, NULL); + + if(SQLITE_OK == status) + { + status = sqlite3_step(stmt); + + if(SQLITE_ROW == status) + { + m_columnCount = sqlite3_data_count(stmt); + for (int i = 0; i < m_columnCount; ++i) + m_headers.append(QString::fromUtf8((const char *)sqlite3_column_name(stmt, i))); + + int row = 0; + // row data starts here + do + { + QStringList rowdata; + for (int i = 0; i < m_columnCount; ++i) + rowdata.append(QString::fromUtf8((const char *)sqlite3_column_text(stmt, i))); + m_data.insert(row++, rowdata); + } while(sqlite3_step(stmt) == SQLITE_ROW); + } + } + sqlite3_finalize(stmt); + + emit layoutChanged(); +} + +int SqliteTableModel::rowCount(const QModelIndex &parent) const +{ + (void)parent; + return m_data.size(); // current fetched row count +} + +int SqliteTableModel::columnCount(const QModelIndex &parent) const +{ + (void)parent; + return m_columnCount; +} + +QVariant SqliteTableModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + if (role != Qt::DisplayRole) + return QVariant(); + + if (orientation == Qt::Horizontal) + return m_headers.at(section); + else + return QString("Row %1").arg(section); +} + +QVariant SqliteTableModel::data(const QModelIndex &index, int role) const +{ + if (!index.isValid()) + return QVariant(); + + if (index.row() >= m_rowCount) + return QVariant(); + + if (role == Qt::DisplayRole) + { + return m_data[index.row()].at(index.column()); + } + else + return QVariant(); +} + +bool SqliteTableModel::canFetchMore(const QModelIndex &parent) const +{ + return m_data.size() < m_rowCount; +} + +void SqliteTableModel::fetchMore(const QModelIndex& parent) +{ + int currentsize = m_data.size(); + int row = m_data.size(); + QString sLimitQuery = QString("%1 LIMIT %2, %3;").arg(m_sQuery).arg(row).arg(row + m_chunkSize); + QByteArray utf8Query = sLimitQuery.toUtf8(); + sqlite3_stmt *stmt; + int status = sqlite3_prepare_v2(m_db->_db, utf8Query, utf8Query.size(), &stmt, NULL); + + if(SQLITE_OK == status) + { + status = sqlite3_step(stmt); + + if(SQLITE_ROW == status) + { + do + { + QStringList rowdata; + for (int i = 0; i < m_columnCount; ++i) + rowdata.append(QString::fromUtf8((const char *)sqlite3_column_text(stmt, i))); + Q_ASSERT(m_headers.size() == rowdata.size()); + m_data.insert(row++, rowdata); + } while(sqlite3_step(stmt) == SQLITE_ROW); + } + } + sqlite3_finalize(stmt); + beginInsertRows(parent, currentsize, m_data.size()); + endInsertRows(); +} diff --git a/src/sqlitetablemodel.h b/src/sqlitetablemodel.h new file mode 100644 index 00000000..e0accf8d --- /dev/null +++ b/src/sqlitetablemodel.h @@ -0,0 +1,42 @@ +#ifndef SQLITETABLEMODEL_H +#define SQLITETABLEMODEL_H + +#include +#include + +class DBBrowserDB; + +class SqliteTableModel : public QAbstractTableModel +{ + Q_OBJECT +public: + explicit SqliteTableModel(QObject *parent = 0, DBBrowserDB* db = 0); + + int rowCount(const QModelIndex &parent) const; + int columnCount(const QModelIndex &parent) const; + QVariant headerData(int section, Qt::Orientation orientation, int role) const; + QVariant data(const QModelIndex &index, int role) const; + bool canFetchMore(const QModelIndex &parent) const; + void fetchMore(const QModelIndex &parent); + size_t queryMore(size_t offset); + + void setQuery(const QString& sQuery); + void setChunkSize(size_t chunksize); + +signals: + +public slots: + + +private: + DBBrowserDB* m_db; + int m_rowCount; + int m_columnCount; + QStringList m_headers; + QMap m_data; + QString m_sQuery; + + size_t m_chunkSize; +}; + +#endif // SQLITETABLEMODEL_H diff --git a/src/src.pro b/src/src.pro index 9433049d..2787f12e 100644 --- a/src/src.pro +++ b/src/src.pro @@ -25,7 +25,8 @@ HEADERS += \ ExtendedTableWidget.h \ grammar/Sqlite3Lexer.hpp \ grammar/Sqlite3Parser.hpp \ - grammar/sqlite3TokenTypes.hpp + grammar/sqlite3TokenTypes.hpp \ + sqlitetablemodel.h SOURCES += \ sqlitedb.cpp \ @@ -43,7 +44,8 @@ SOURCES += \ sqlitetypes.cpp \ ExtendedTableWidget.cpp \ grammar/Sqlite3Lexer.cpp \ - grammar/Sqlite3Parser.cpp + grammar/Sqlite3Parser.cpp \ + sqlitetablemodel.cpp # create a unittest option CONFIG(unittest) { From f25892d6d2adb157d75f513e0337a89eaafa25b3 Mon Sep 17 00:00:00 2001 From: Martin Kleusberg Date: Sat, 6 Apr 2013 22:09:36 +0200 Subject: [PATCH 02/27] SqliteTableModel: Do a few fixes and add sorting again Fix empty rows being shown in the main window when changing to a table with less rows than the old one. Add sorting in the browse tab again. Do some minor clean ups. --- src/MainWindow.cpp | 18 ++++++++++-------- src/MainWindow.h | 7 +++---- src/MainWindow.ui | 2 +- src/sqlitetablemodel.cpp | 2 +- 4 files changed, 15 insertions(+), 14 deletions(-) diff --git a/src/MainWindow.cpp b/src/MainWindow.cpp index 2a906480..3da472a6 100644 --- a/src/MainWindow.cpp +++ b/src/MainWindow.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include "CreateIndexDialog.h" #include "AboutDialog.h" @@ -27,6 +28,8 @@ MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent), ui(new Ui::MainWindow), browseTableModel(new QStandardItemModel(this)), + m_browseTableModel(new SqliteTableModel(this, &db)), + m_browseTableSortProxy(new QSortFilterProxyModel(this)), sqliteHighlighterTabSql(0), sqliteHighlighterLogUser(0), sqliteHighlighterLogApp(0), @@ -52,8 +55,6 @@ void MainWindow::init() // Init the SQL log dock db.mainWindow = this; - m_browseTableModel = new SqliteTableModel(this, &db); - // Set up the db tree widget ui->dbTreeWidget->setColumnHidden(1, true); ui->dbTreeWidget->setColumnWidth(0, 300); @@ -66,8 +67,8 @@ void MainWindow::init() createSyntaxHighlighters(); // Set up DB models - //ui->dataTable->setModel(browseTableModel); - ui->dataTable->setModel(m_browseTableModel); + m_browseTableSortProxy->setSourceModel(m_browseTableModel); + ui->dataTable->setModel(m_browseTableSortProxy); queryResultListModel = new QStandardItemModel(this); ui->queryResultTableView->setModel(queryResultListModel); @@ -332,7 +333,8 @@ void MainWindow::resetBrowser() pos = pos == -1 ? 0 : pos; ui->comboBrowseTable->setCurrentIndex(pos); curBrowseOrderByIndex = 1; - curBrowseOrderByMode = ORDERMODE_ASC; + curBrowseOrderByMode = Qt::AscendingOrder; + m_browseTableSortProxy->sort(curBrowseOrderByIndex, curBrowseOrderByMode); populateTable(ui->comboBrowseTable->currentText()); } @@ -1144,9 +1146,9 @@ void MainWindow::browseTableHeaderClicked(int logicalindex) return; // instead of the column name we just use the column index, +2 because 'rowid, *' is the projection - curBrowseOrderByIndex = logicalindex + 2; - curBrowseOrderByMode = curBrowseOrderByMode == ORDERMODE_ASC ? ORDERMODE_DESC : ORDERMODE_ASC; - populateTable(ui->comboBrowseTable->currentText(), true); + curBrowseOrderByIndex = logicalindex; + curBrowseOrderByMode = curBrowseOrderByMode == Qt::AscendingOrder ? Qt::DescendingOrder : Qt::AscendingOrder; + m_browseTableSortProxy->sort(curBrowseOrderByIndex, curBrowseOrderByMode); // select the first item in the column so the header is bold // we might try to select the last selected item diff --git a/src/MainWindow.h b/src/MainWindow.h index a95674b7..65905314 100644 --- a/src/MainWindow.h +++ b/src/MainWindow.h @@ -4,9 +4,6 @@ #include #include "sqlitedb.h" -#define ORDERMODE_ASC 0 -#define ORDERMODE_DESC 1 - class QDragEnterEvent; class EditDialog; class FindDialog; @@ -16,6 +13,7 @@ class QIntValidator; class QLabel; class QModelIndex; class SqliteTableModel; +class QSortFilterProxyModel; namespace Ui { class MainWindow; @@ -55,6 +53,7 @@ private: QStandardItemModel *browseTableModel; SqliteTableModel* m_browseTableModel; + QSortFilterProxyModel* m_browseTableSortProxy; QStandardItemModel *queryResultListModel; QMenu *popupTableMenu; QMenu *recentFilesMenu; @@ -70,7 +69,7 @@ private: QAction *recentSeparatorAct; int curBrowseOrderByIndex; - int curBrowseOrderByMode; + Qt::SortOrder curBrowseOrderByMode; EditDialog* editWin; FindDialog* findWin; diff --git a/src/MainWindow.ui b/src/MainWindow.ui index dbe2da9c..57bfb24a 100644 --- a/src/MainWindow.ui +++ b/src/MainWindow.ui @@ -312,7 +312,7 @@ 0 0 - 763 + 278 444 diff --git a/src/sqlitetablemodel.cpp b/src/sqlitetablemodel.cpp index a5c79a7d..acbb8ab4 100644 --- a/src/sqlitetablemodel.cpp +++ b/src/sqlitetablemodel.cpp @@ -37,7 +37,6 @@ void SqliteTableModel::setQuery(const QString& sQuery) status = sqlite3_step(stmt); if(SQLITE_ROW == status) { - m_columnCount = sqlite3_data_count(stmt); QString sCount = QString::fromUtf8((const char *) sqlite3_column_text(stmt, 0)); m_rowCount = sCount.toInt(); } @@ -46,6 +45,7 @@ void SqliteTableModel::setQuery(const QString& sQuery) // now fetch the first 100 entries and get headers m_headers.clear(); + m_data.clear(); m_columnCount = 0; QString sLimitQuery = QString("%1 LIMIT 0, %2;").arg(sQuery).arg(m_chunkSize); utf8Query = sLimitQuery.toUtf8(); From 00b9f4027124efa9e2a135982cafb82766d9418f Mon Sep 17 00:00:00 2001 From: Martin Kleusberg Date: Sat, 6 Apr 2013 23:12:53 +0200 Subject: [PATCH 03/27] SqliteTableModel: Make editing of data possible again --- src/MainWindow.cpp | 27 ++++++--------------------- src/MainWindow.h | 2 +- src/MainWindow.ui | 3 +++ src/sqlitedb.cpp | 32 ++++++++++++++++++++++++++++++++ src/sqlitedb.h | 1 + src/sqlitetablemodel.cpp | 33 ++++++++++++++++++++++++++++++++- src/sqlitetablemodel.h | 5 +++++ 7 files changed, 80 insertions(+), 23 deletions(-) diff --git a/src/MainWindow.cpp b/src/MainWindow.cpp index 3da472a6..75eccfde 100644 --- a/src/MainWindow.cpp +++ b/src/MainWindow.cpp @@ -104,7 +104,7 @@ void MainWindow::init() connect(ui->dataTable->horizontalHeader(), SIGNAL(sectionClicked(int)), this, SLOT(browseTableHeaderClicked(int))); connect(ui->dataTable->verticalScrollBar(), SIGNAL(valueChanged(int)), this, SLOT(setRecordsetLabel())); connect(editWin, SIGNAL(goingAway()), this, SLOT(editWinAway())); - connect(editWin, SIGNAL(updateRecordText(int, int, QByteArray)), this, SLOT(updateRecordText(int, int , QByteArray))); + connect(editWin, SIGNAL(updateRecordText(int, int, QByteArray)), this, SLOT(updateRecordText(int, int, QByteArray))); // Load window settings restoreGeometry(PreferencesDialog::getSettingsValue("MainWindow", "geometry").toByteArray()); @@ -277,7 +277,7 @@ void MainWindow::populateTable( const QString & tablename, bool keepColumnWidths { QApplication::setOverrideCursor( Qt::WaitCursor ); if(!tablename.isEmpty()) - m_browseTableModel->setQuery(QString("SELECT * FROM `%1`").arg(tablename)); + m_browseTableModel->setTable(tablename); // bool mustreset = false; // if (tablename.compare(db.curBrowseTableName)!=0) // { @@ -706,18 +706,7 @@ void MainWindow::helpAbout() void MainWindow::updateRecordText(int row, int col, const QByteArray& newtext) { - if (!db.updateRecord(row, col, newtext)){ - QMessageBox::information( this, QApplication::applicationName(), - tr("Data could not be updated:\n") + db.lastErrorMessage); - } - - rowList tab = db.browseRecs; - QList& rt = tab[row]; - QByteArray& cv = rt[col+1];//must account for rowid - - QStandardItem* item = new QStandardItem(QString(cv)); - item->setToolTip( wrapText(cv) ); - browseTableModel->setItem(row, col, item); + m_browseTableModel->setData(m_browseTableSortProxy->mapToSource(m_browseTableSortProxy->index(row, col)), newtext); } void MainWindow::editWinAway() @@ -727,13 +716,9 @@ void MainWindow::editWinAway() ui->dataTable->setCurrentIndex(ui->dataTable->currentIndex().sibling(editWin->getCurrentRow(), editWin->getCurrentCol())); } -void MainWindow::editText(int row, int col) +void MainWindow::editText(const QModelIndex& index) { - rowList tab = db.browseRecs; - QList rt = tab[row]; - QByteArray cv = rt[col+1];//must account for rowid - - editWin->loadText(cv , row, col); + editWin->loadText(index.data().toByteArray(), index.row(), index.column()); editWin->show(); } @@ -749,7 +734,7 @@ void MainWindow::doubleClickTable(const QModelIndex& index) if(db.getObjectByName(ui->comboBrowseTable->currentText()).gettype() != "table") return; - editText(index.row(), index.column()); + editText(index); } /* diff --git a/src/MainWindow.h b/src/MainWindow.h index 65905314..ef63b4ef 100644 --- a/src/MainWindow.h +++ b/src/MainWindow.h @@ -125,7 +125,7 @@ private slots: virtual void helpAbout(); virtual void updateRecordText(int row, int col, const QByteArray& newtext); virtual void editWinAway(); - virtual void editText( int row, int col ); + virtual void editText(const QModelIndex& index); virtual void doubleClickTable(const QModelIndex& index); virtual void executeQuery(); virtual void importTableFromCSV(); diff --git a/src/MainWindow.ui b/src/MainWindow.ui index 57bfb24a..b6a1995d 100644 --- a/src/MainWindow.ui +++ b/src/MainWindow.ui @@ -205,6 +205,9 @@ This is the database view. You can double-click any record to edit its contents in the cell editor window. + + QAbstractItemView::NoEditTriggers + QAbstractItemView::ExtendedSelection diff --git a/src/sqlitedb.cpp b/src/sqlitedb.cpp index 195d93c7..c64bb4ae 100644 --- a/src/sqlitedb.cpp +++ b/src/sqlitedb.cpp @@ -475,6 +475,38 @@ bool DBBrowserDB::updateRecord(int wrow, int wcol, const QByteArray& wtext) } } +bool DBBrowserDB::updateRecord(const QString& table, const QString& column, int row, const QByteArray& value) +{ + if (!isOpen()) return false; + + lastErrorMessage = QString("no error"); + + QString sql = QString("UPDATE `%1` SET `%2`=? WHERE rowid=%3;").arg(table).arg(column).arg(row); + + logSQL(sql, kLogMsg_App); + setRestorePoint(); + + sqlite3_stmt* stmt; + int success = 1; + if(sqlite3_prepare_v2(_db, sql.toUtf8(), -1, &stmt, 0) != SQLITE_OK) + success = 0; + if(success == 1 && sqlite3_bind_text(stmt, 1, value.constData(), value.length(), SQLITE_STATIC) != SQLITE_OK) + success = -1; + if(success == 1 && sqlite3_step(stmt) != SQLITE_DONE) + success = -1; + if(success != 0 && sqlite3_finalize(stmt) != SQLITE_OK) + success = -1; + + if(success == 1) + { + return true; + } else { + lastErrorMessage = sqlite3_errmsg(_db); + qCritical() << "updateRecord: " << lastErrorMessage; + return false; + } +} + bool DBBrowserDB::browseTable( const QString & tablename, const QString& orderby ) { QStringList testFields = getTableFields( tablename ); diff --git a/src/sqlitedb.h b/src/sqlitedb.h index bccc0cc2..f1796b54 100644 --- a/src/sqlitedb.h +++ b/src/sqlitedb.h @@ -101,6 +101,7 @@ public: void updateSchema() ; bool addRecord(const QString& sTableName); bool deleteRecord(int wrow); + bool updateRecord(const QString& table, const QString& column, int row, const QByteArray& value); bool updateRecord(int wrow, int wcol, const QByteArray& wtext); bool browseTable( const QString & tablename, const QString& orderby = "rowid" ); diff --git a/src/sqlitetablemodel.cpp b/src/sqlitetablemodel.cpp index acbb8ab4..8c7e9360 100644 --- a/src/sqlitetablemodel.cpp +++ b/src/sqlitetablemodel.cpp @@ -1,5 +1,4 @@ #include "sqlitetablemodel.h" - #include "sqlitedb.h" #include @@ -18,6 +17,12 @@ void SqliteTableModel::setChunkSize(size_t chunksize) m_chunkSize = chunksize; } +void SqliteTableModel::setTable(const QString& table) +{ + m_sTable = table; + setQuery(QString("SELECT * FROM `%1`").arg(table)); +} + void SqliteTableModel::setQuery(const QString& sQuery) { if(!m_db->isOpen()) @@ -116,6 +121,24 @@ QVariant SqliteTableModel::data(const QModelIndex &index, int role) const return QVariant(); } +bool SqliteTableModel::setData(const QModelIndex& index, const QVariant& value, int role) +{ + if(index.isValid() && role == Qt::EditRole) + { + m_data[index.row()].replace(index.column(), value.toString()); + + if(m_db->updateRecord(m_sTable, m_headers.at(index.column()), index.row()+1, value.toByteArray())) + { + emit(dataChanged(index, index)); + return true; + } else { + return false; + } + } + + return false; +} + bool SqliteTableModel::canFetchMore(const QModelIndex &parent) const { return m_data.size() < m_rowCount; @@ -150,3 +173,11 @@ void SqliteTableModel::fetchMore(const QModelIndex& parent) beginInsertRows(parent, currentsize, m_data.size()); endInsertRows(); } + +Qt::ItemFlags SqliteTableModel::flags(const QModelIndex& index) const +{ + if(!index.isValid()) + return Qt::ItemIsEnabled; + + return QAbstractTableModel::flags(index) | Qt::ItemIsEditable; +} diff --git a/src/sqlitetablemodel.h b/src/sqlitetablemodel.h index e0accf8d..35614564 100644 --- a/src/sqlitetablemodel.h +++ b/src/sqlitetablemodel.h @@ -16,13 +16,17 @@ public: int columnCount(const QModelIndex &parent) const; QVariant headerData(int section, Qt::Orientation orientation, int role) const; QVariant data(const QModelIndex &index, int role) const; + bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole); bool canFetchMore(const QModelIndex &parent) const; void fetchMore(const QModelIndex &parent); size_t queryMore(size_t offset); void setQuery(const QString& sQuery); + void setTable(const QString& table); void setChunkSize(size_t chunksize); + Qt::ItemFlags flags(const QModelIndex& index) const; + signals: public slots: @@ -35,6 +39,7 @@ private: QStringList m_headers; QMap m_data; QString m_sQuery; + QString m_sTable; size_t m_chunkSize; }; From 5d2139ce993199fcc547e040c3e6c47cda26e658 Mon Sep 17 00:00:00 2001 From: Martin Kleusberg Date: Sat, 6 Apr 2013 23:40:47 +0200 Subject: [PATCH 04/27] Kind of fix the find dialog when using the SqliteTableModel --- src/FindDialog.cpp | 4 +-- src/MainWindow.cpp | 78 +++++++++++++++++----------------------------- src/MainWindow.h | 2 +- src/sqlitedb.cpp | 29 +++++------------ 4 files changed, 38 insertions(+), 75 deletions(-) diff --git a/src/FindDialog.cpp b/src/FindDialog.cpp index ade4c713..c32a07e8 100644 --- a/src/FindDialog.cpp +++ b/src/FindDialog.cpp @@ -23,7 +23,7 @@ void FindDialog::showResults(const resultMap& rmap) for(it=rmap.begin(),rowNum=0;it!=rmap.end();++it,rowNum++) { QString firstline = it.value().section('\n', 0, 0); - ui->tableResults->setItem(rowNum, 0, new QTableWidgetItem(QString::number(it.key() + 1))); + ui->tableResults->setItem(rowNum, 0, new QTableWidgetItem(QString::number(it.key()))); ui->tableResults->setItem(rowNum, 1, new QTableWidgetItem(firstline)); } QString results = tr("Found: %1").arg(ui->tableResults->rowCount()); @@ -51,7 +51,7 @@ void FindDialog::recordSelected(QTableWidgetItem* witem) if(witem) { int recNum = ui->tableResults->item(witem->row(), 0)->text().toInt(); - emit showrecord(recNum - 1); + emit showrecord(recNum); } } diff --git a/src/MainWindow.cpp b/src/MainWindow.cpp index 75eccfde..22219fcd 100644 --- a/src/MainWindow.cpp +++ b/src/MainWindow.cpp @@ -273,47 +273,34 @@ void MainWindow::populateStructure() } } -void MainWindow::populateTable( const QString & tablename, bool keepColumnWidths) +void MainWindow::populateTable( const QString & tablename) { - QApplication::setOverrideCursor( Qt::WaitCursor ); + QApplication::setOverrideCursor(Qt::WaitCursor); + + // Set new table if(!tablename.isEmpty()) m_browseTableModel->setTable(tablename); -// bool mustreset = false; -// if (tablename.compare(db.curBrowseTableName)!=0) -// { -// mustreset = true; -// curBrowseOrderByIndex = 1; -// curBrowseOrderByMode = ORDERMODE_ASC; -// } -// QString orderby = QString::number(curBrowseOrderByIndex) + " " + (curBrowseOrderByMode == ORDERMODE_ASC ? "ASC" : "DESC"); -// if(!db.browseTable(tablename, orderby)) -// { -// browseTableModel->setRowCount(0); -// browseTableModel->setColumnCount(0); -// QApplication::restoreOverrideCursor(); -// if(findWin) -// findWin->resetFields(db.getTableFields("")); -// return; -// } + // Reset sorting + curBrowseOrderByIndex = 1; + curBrowseOrderByMode = Qt::AscendingOrder; + m_browseTableSortProxy->sort(curBrowseOrderByIndex, curBrowseOrderByMode); -// // Activate the add and delete record buttons and editing only if a table has been selected -// bool is_table = db.getObjectByName(tablename).gettype() == "table"; -// ui->buttonNewRecord->setEnabled(is_table); -// ui->buttonDeleteRecord->setEnabled(is_table); -// ui->dataTable->setEditTriggers(is_table ? QAbstractItemView::DoubleClicked | QAbstractItemView::AnyKeyPressed | QAbstractItemView::EditKeyPressed : QAbstractItemView::NoEditTriggers); + // Get table layout + db.browseTable(tablename); + + // Activate the add and delete record buttons and editing only if a table has been selected + bool is_table = db.getObjectByName(tablename).gettype() == "table"; + ui->buttonNewRecord->setEnabled(is_table); + ui->buttonDeleteRecord->setEnabled(is_table); + ui->dataTable->setEditTriggers(is_table ? QAbstractItemView::DoubleClicked | QAbstractItemView::AnyKeyPressed | QAbstractItemView::EditKeyPressed : QAbstractItemView::NoEditTriggers); + + //got to keep findWin in synch + if(findWin) + findWin->resetFields(); + if(editWin) + editWin->reset(); -// if (mustreset){ -// updateTableView(0, keepColumnWidths); -// if (findWin) findWin->resetFields(db.getTableFields(db.curBrowseTableName)); -// } else { -// updateTableView(-1, keepColumnWidths); -// } -// //got to keep findWin in synch -// if(findWin) -// findWin->resetFields(); -// if(editWin) -// editWin->reset(); QApplication::restoreOverrideCursor(); } @@ -483,9 +470,8 @@ void MainWindow::updateTableView(int lineToSelect, bool keepColumnWidths) void MainWindow::selectTableLine(int lineToSelect) { ui->dataTable->clearSelection(); - ui->dataTable->selectRow(lineToSelect); - ui->dataTable->setCurrentIndex(ui->dataTable->currentIndex().sibling(lineToSelect, 0)); - ui->dataTable->scrollTo(ui->dataTable->currentIndex().sibling(lineToSelect, 0)); + ui->dataTable->selectRow(m_browseTableSortProxy->mapFromSource(m_browseTableModel->index(lineToSelect, 0)).row()); + ui->dataTable->scrollTo(ui->dataTable->currentIndex()); } void MainWindow::navigatePrevious() @@ -538,7 +524,7 @@ void MainWindow::browseFind(bool open) { findWin = new FindDialog(this); connect(findWin, SIGNAL(lookfor(const QString&, const QString&, const QString&)), this, SLOT(lookfor(const QString&, const QString&, const QString&))); - connect(findWin, SIGNAL(showrecord(int)),this, SLOT(updateTableView(int))); + connect(findWin, SIGNAL(showrecord(int)),this, SLOT(selectTableLine(int))); connect(findWin, SIGNAL(goingAway()),this, SLOT(browseFindAway())); } findWin->resetFields(db.getTableFields(db.curBrowseTableName)); @@ -556,7 +542,7 @@ void MainWindow::browseFindAway() void MainWindow::browseRefresh() { - populateTable(ui->comboBrowseTable->currentText(), true); + populateTable(ui->comboBrowseTable->currentText()); } void MainWindow::lookfor( const QString & wfield, const QString & woperator, const QString & wsearchterm ) @@ -580,15 +566,7 @@ void MainWindow::lookfor( const QString & wfield, const QString & woperator, con finalsearchterm = QString(newsearchterm); } QApplication::setOverrideCursor( Qt::WaitCursor ); - QString statement = "SELECT rowid, "; - statement.append(wfield); - statement.append(" FROM "); - statement.append(db.curBrowseTableName); - statement.append(" WHERE "); - statement.append(wfield); - statement.append(" "); - statement.append(finaloperator); - statement.append(" "); + QString statement = QString("SELECT rowid, `%1` FROM `%2` WHERE `%3` %4 ").arg(wfield).arg(db.curBrowseTableName).arg(wfield).arg(finaloperator); //searchterm needs to be quoted if it is not a number bool ok = false; finalsearchterm.toDouble(&ok); @@ -600,7 +578,7 @@ void MainWindow::lookfor( const QString & wfield, const QString & woperator, con } else {//append the number, unquoted statement.append(finalsearchterm); } - statement.append(" ORDER BY rowid; "); + statement.append(" ORDER BY rowid;"); resultMap res = db.getFindResults(statement); findWin->showResults(res); QApplication::restoreOverrideCursor(); diff --git a/src/MainWindow.h b/src/MainWindow.h index ef63b4ef..9f1fb7f8 100644 --- a/src/MainWindow.h +++ b/src/MainWindow.h @@ -100,7 +100,7 @@ private slots: virtual void fileOpen(); virtual void fileNew(); virtual void populateStructure(); - virtual void populateTable(const QString & tablename , bool keepColumnWidths = false); + virtual void populateTable(const QString& tablename); virtual void resetBrowser(); virtual void fileClose(); virtual void fileExit(); diff --git a/src/sqlitedb.cpp b/src/sqlitedb.cpp index c64bb4ae..6e009209 100644 --- a/src/sqlitedb.cpp +++ b/src/sqlitedb.cpp @@ -507,12 +507,12 @@ bool DBBrowserDB::updateRecord(const QString& table, const QString& column, int } } -bool DBBrowserDB::browseTable( const QString & tablename, const QString& orderby ) +bool DBBrowserDB::browseTable( const QString & tablename, const QString& /*orderby*/ ) { QStringList testFields = getTableFields( tablename ); if (testFields.count()>0) {//table exists - getTableRecords( tablename, orderby ); + //getTableRecords( tablename, orderby ); browseFields = testFields; hasValidBrowseSet = true; curBrowseTableName = tablename; @@ -789,9 +789,6 @@ resultMap DBBrowserDB::getFindResults( const QString & wstatement) sqlite3_stmt *vm; const char *tail; - int ncol; - - // char *errmsg; int err=0; resultMap res; lastErrorMessage = QObject::tr("no error"); @@ -799,25 +796,13 @@ resultMap DBBrowserDB::getFindResults( const QString & wstatement) QByteArray statementutf8 = wstatement.toUtf8(); err=sqlite3_prepare_v2(_db, statementutf8, statementutf8.length(), &vm, &tail); - if (err == SQLITE_OK){ - int rownum = 0; - int recnum = 0; - QString r; - while ( sqlite3_step(vm) == SQLITE_ROW ){ - ncol = sqlite3_data_count(vm); - for (int e=0; e Date: Sun, 7 Apr 2013 17:30:35 +0200 Subject: [PATCH 05/27] More improvements and fixes to the new SqliteTableModel Change the way sorting is done to correctly sort all data even if it hasn't been loaded completely yet. Fix the navigation label and buttons by showing the correct information again and fixing the validator of the input field. Log all SQL statements executed from within the SqliteTableModel. Silence a few warnings. --- src/MainWindow.cpp | 62 ++++++++++++++++++++++++---------------- src/MainWindow.h | 2 -- src/sqlitetablemodel.cpp | 31 ++++++++++++++++++-- src/sqlitetablemodel.h | 6 +++- 4 files changed, 71 insertions(+), 30 deletions(-) diff --git a/src/MainWindow.cpp b/src/MainWindow.cpp index 22219fcd..62f0932a 100644 --- a/src/MainWindow.cpp +++ b/src/MainWindow.cpp @@ -29,12 +29,12 @@ MainWindow::MainWindow(QWidget* parent) ui(new Ui::MainWindow), browseTableModel(new QStandardItemModel(this)), m_browseTableModel(new SqliteTableModel(this, &db)), - m_browseTableSortProxy(new QSortFilterProxyModel(this)), sqliteHighlighterTabSql(0), sqliteHighlighterLogUser(0), sqliteHighlighterLogApp(0), editWin(new EditDialog(this)), - findWin(0) + findWin(0), + gotoValidator(new QIntValidator(0, 0, this)) { ui->setupUi(this); init(); @@ -59,16 +59,14 @@ void MainWindow::init() ui->dbTreeWidget->setColumnHidden(1, true); ui->dbTreeWidget->setColumnWidth(0, 300); - // Create the validator for the goto line edit - gotoValidator = new QIntValidator(0, 0, this); + // Set the validator for the goto line edit ui->editGoto->setValidator(gotoValidator); // Create the SQL sytax highlighters createSyntaxHighlighters(); // Set up DB models - m_browseTableSortProxy->setSourceModel(m_browseTableModel); - ui->dataTable->setModel(m_browseTableSortProxy); + ui->dataTable->setModel(m_browseTableModel); queryResultListModel = new QStandardItemModel(this); ui->queryResultTableView->setModel(queryResultListModel); @@ -284,7 +282,7 @@ void MainWindow::populateTable( const QString & tablename) // Reset sorting curBrowseOrderByIndex = 1; curBrowseOrderByMode = Qt::AscendingOrder; - m_browseTableSortProxy->sort(curBrowseOrderByIndex, curBrowseOrderByMode); + m_browseTableModel->sort(curBrowseOrderByIndex, curBrowseOrderByMode); // Get table layout db.browseTable(tablename); @@ -295,6 +293,9 @@ void MainWindow::populateTable( const QString & tablename) ui->buttonDeleteRecord->setEnabled(is_table); ui->dataTable->setEditTriggers(is_table ? QAbstractItemView::DoubleClicked | QAbstractItemView::AnyKeyPressed | QAbstractItemView::EditKeyPressed : QAbstractItemView::NoEditTriggers); + // Set the recordset label + setRecordsetLabel(); + //got to keep findWin in synch if(findWin) findWin->resetFields(); @@ -321,7 +322,7 @@ void MainWindow::resetBrowser() ui->comboBrowseTable->setCurrentIndex(pos); curBrowseOrderByIndex = 1; curBrowseOrderByMode = Qt::AscendingOrder; - m_browseTableSortProxy->sort(curBrowseOrderByIndex, curBrowseOrderByMode); + m_browseTableModel->sort(curBrowseOrderByIndex, curBrowseOrderByMode); populateTable(ui->comboBrowseTable->currentText()); } @@ -463,14 +464,15 @@ void MainWindow::updateTableView(int lineToSelect, bool keepColumnWidths) // if (lineToSelect!=-1){ // selectTableLine(lineToSelect); // } -// setRecordsetLabel(); + + setRecordsetLabel(); QApplication::restoreOverrideCursor(); } void MainWindow::selectTableLine(int lineToSelect) { ui->dataTable->clearSelection(); - ui->dataTable->selectRow(m_browseTableSortProxy->mapFromSource(m_browseTableModel->index(lineToSelect, 0)).row()); + ui->dataTable->selectRow(lineToSelect); ui->dataTable->scrollTo(ui->dataTable->currentIndex()); } @@ -478,41 +480,51 @@ void MainWindow::navigatePrevious() { int curRow = ui->dataTable->currentIndex().row(); curRow -= 100; - if(curRow < 0) curRow = 0; - updateTableView(curRow); + if(curRow < 0) + curRow = 0; + selectTableLine(curRow); } void MainWindow::navigateNext() { + // TODO: Fetch more data from DB if necessary + int curRow = ui->dataTable->currentIndex().row(); curRow += 100; - if(curRow >= browseTableModel->rowCount()) - curRow = browseTableModel->rowCount()-1; - updateTableView(curRow); + if(curRow >= m_browseTableModel->totalRowCount()) + curRow = m_browseTableModel->totalRowCount() - 1; + selectTableLine(curRow); } void MainWindow::navigateGoto() { - QString typed = ui->editGoto->text(); - bool ok; - int dec = typed.toInt( &ok); - if (dec==0) dec=1; - if (dec>db.getRecordCount()) dec = db.getRecordCount(); + // TODO: Fetch more data from DB if necessary - updateTableView(dec-1); - ui->editGoto->setText(QString::number(dec,10)); + int row = ui->editGoto->text().toInt(); + if(row <= 0) + row = 1; + if(row > m_browseTableModel->totalRowCount()) + row = m_browseTableModel->totalRowCount(); + + selectTableLine(row - 1); + ui->editGoto->setText(QString::number(row)); } void MainWindow::setRecordsetLabel() { + // Get all the numbers, i.e. the number of the first row and the last row as well as the total number of rows int from = ui->dataTable->verticalHeader()->visualIndexAt(0) + 1; int to = ui->dataTable->verticalHeader()->visualIndexAt(ui->dataTable->height()) - 1; - int total = browseTableModel->rowCount(); + int total = m_browseTableModel->totalRowCount(); if(to == -2) to = total; + // Update the validator of the goto row field + gotoValidator->setRange(0, total); + + // Update the label showing the current position ui->labelRecordset->setText(tr("%1 - %2 of %3").arg(from).arg(to).arg(total)); } @@ -684,7 +696,7 @@ void MainWindow::helpAbout() void MainWindow::updateRecordText(int row, int col, const QByteArray& newtext) { - m_browseTableModel->setData(m_browseTableSortProxy->mapToSource(m_browseTableSortProxy->index(row, col)), newtext); + m_browseTableModel->setData(m_browseTableModel->index(row, col), newtext); } void MainWindow::editWinAway() @@ -1111,7 +1123,7 @@ void MainWindow::browseTableHeaderClicked(int logicalindex) // instead of the column name we just use the column index, +2 because 'rowid, *' is the projection curBrowseOrderByIndex = logicalindex; curBrowseOrderByMode = curBrowseOrderByMode == Qt::AscendingOrder ? Qt::DescendingOrder : Qt::AscendingOrder; - m_browseTableSortProxy->sort(curBrowseOrderByIndex, curBrowseOrderByMode); + m_browseTableModel->sort(curBrowseOrderByIndex, curBrowseOrderByMode); // select the first item in the column so the header is bold // we might try to select the last selected item diff --git a/src/MainWindow.h b/src/MainWindow.h index 9f1fb7f8..ecfcb057 100644 --- a/src/MainWindow.h +++ b/src/MainWindow.h @@ -13,7 +13,6 @@ class QIntValidator; class QLabel; class QModelIndex; class SqliteTableModel; -class QSortFilterProxyModel; namespace Ui { class MainWindow; @@ -53,7 +52,6 @@ private: QStandardItemModel *browseTableModel; SqliteTableModel* m_browseTableModel; - QSortFilterProxyModel* m_browseTableSortProxy; QStandardItemModel *queryResultListModel; QMenu *popupTableMenu; QMenu *recentFilesMenu; diff --git a/src/sqlitetablemodel.cpp b/src/sqlitetablemodel.cpp index 8c7e9360..a6bc00b2 100644 --- a/src/sqlitetablemodel.cpp +++ b/src/sqlitetablemodel.cpp @@ -8,6 +8,8 @@ SqliteTableModel::SqliteTableModel(QObject* parent, DBBrowserDB* db) , m_db(db) , m_rowCount(0) , m_columnCount(0) + , m_iSortColumn(0) + , m_sSortOrder("ASC") , m_chunkSize(1000) { } @@ -34,6 +36,7 @@ void SqliteTableModel::setQuery(const QString& sQuery) // do a count query to get the full row count in a fast manner QString sCountQuery = QString("SELECT COUNT(*) FROM (%1);").arg(sQuery); + m_db->logSQL(sCountQuery, kLogMsg_App); QByteArray utf8Query = sCountQuery.toUtf8(); int status = sqlite3_prepare_v2(m_db->_db, utf8Query, utf8Query.size(), &stmt, NULL); @@ -53,6 +56,7 @@ void SqliteTableModel::setQuery(const QString& sQuery) m_data.clear(); m_columnCount = 0; QString sLimitQuery = QString("%1 LIMIT 0, %2;").arg(sQuery).arg(m_chunkSize); + m_db->logSQL(sLimitQuery, kLogMsg_App); utf8Query = sLimitQuery.toUtf8(); status = sqlite3_prepare_v2(m_db->_db, utf8Query, utf8Query.size(), &stmt, NULL); @@ -88,6 +92,11 @@ int SqliteTableModel::rowCount(const QModelIndex &parent) const return m_data.size(); // current fetched row count } +int SqliteTableModel::totalRowCount() const +{ + return m_rowCount; +} + int SqliteTableModel::columnCount(const QModelIndex &parent) const { (void)parent; @@ -102,7 +111,7 @@ QVariant SqliteTableModel::headerData(int section, Qt::Orientation orientation, if (orientation == Qt::Horizontal) return m_headers.at(section); else - return QString("Row %1").arg(section); + return QString("%1").arg(section + 1); } QVariant SqliteTableModel::data(const QModelIndex &index, int role) const @@ -139,7 +148,7 @@ bool SqliteTableModel::setData(const QModelIndex& index, const QVariant& value, return false; } -bool SqliteTableModel::canFetchMore(const QModelIndex &parent) const +bool SqliteTableModel::canFetchMore(const QModelIndex&) const { return m_data.size() < m_rowCount; } @@ -149,6 +158,7 @@ void SqliteTableModel::fetchMore(const QModelIndex& parent) int currentsize = m_data.size(); int row = m_data.size(); QString sLimitQuery = QString("%1 LIMIT %2, %3;").arg(m_sQuery).arg(row).arg(row + m_chunkSize); + m_db->logSQL(sLimitQuery, kLogMsg_App); QByteArray utf8Query = sLimitQuery.toUtf8(); sqlite3_stmt *stmt; int status = sqlite3_prepare_v2(m_db->_db, utf8Query, utf8Query.size(), &stmt, NULL); @@ -181,3 +191,20 @@ Qt::ItemFlags SqliteTableModel::flags(const QModelIndex& index) const return QAbstractTableModel::flags(index) | Qt::ItemIsEditable; } + +void SqliteTableModel::sort(int column, Qt::SortOrder order) +{ + // Save sort order + m_iSortColumn = column; + m_sSortOrder = (order == Qt::AscendingOrder ? "ASC" : "DESC"); + + // Set the new query (but only if a table has already been set + if(m_sTable != "") + { + setQuery(QString("SELECT * FROM `%1` ORDER BY `%2` %3") + .arg(m_sTable) + .arg(m_headers.at(m_iSortColumn)) + .arg(m_sSortOrder) + ); + } +} diff --git a/src/sqlitetablemodel.h b/src/sqlitetablemodel.h index 35614564..bed548d3 100644 --- a/src/sqlitetablemodel.h +++ b/src/sqlitetablemodel.h @@ -13,6 +13,7 @@ public: explicit SqliteTableModel(QObject *parent = 0, DBBrowserDB* db = 0); int rowCount(const QModelIndex &parent) const; + int totalRowCount() const; int columnCount(const QModelIndex &parent) const; QVariant headerData(int section, Qt::Orientation orientation, int role) const; QVariant data(const QModelIndex &index, int role) const; @@ -24,13 +25,13 @@ public: void setQuery(const QString& sQuery); void setTable(const QString& table); void setChunkSize(size_t chunksize); + void sort(int column, Qt::SortOrder order = Qt::AscendingOrder); Qt::ItemFlags flags(const QModelIndex& index) const; signals: public slots: - private: DBBrowserDB* m_db; @@ -38,8 +39,11 @@ private: int m_columnCount; QStringList m_headers; QMap m_data; + QString m_sQuery; QString m_sTable; + int m_iSortColumn; + QString m_sSortOrder; size_t m_chunkSize; }; From 73bda97ce9869104c784d51f0920de6398d4bd16 Mon Sep 17 00:00:00 2001 From: Martin Kleusberg Date: Sun, 7 Apr 2013 17:51:56 +0200 Subject: [PATCH 06/27] SqliteTableModel: Fix editing --- src/sqlitetablemodel.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sqlitetablemodel.cpp b/src/sqlitetablemodel.cpp index a6bc00b2..32ad62c3 100644 --- a/src/sqlitetablemodel.cpp +++ b/src/sqlitetablemodel.cpp @@ -22,7 +22,7 @@ void SqliteTableModel::setChunkSize(size_t chunksize) void SqliteTableModel::setTable(const QString& table) { m_sTable = table; - setQuery(QString("SELECT * FROM `%1`").arg(table)); + setQuery(QString("SELECT rowid,* FROM `%1`").arg(table)); } void SqliteTableModel::setQuery(const QString& sQuery) @@ -136,7 +136,7 @@ bool SqliteTableModel::setData(const QModelIndex& index, const QVariant& value, { m_data[index.row()].replace(index.column(), value.toString()); - if(m_db->updateRecord(m_sTable, m_headers.at(index.column()), index.row()+1, value.toByteArray())) + if(m_db->updateRecord(m_sTable, m_headers.at(index.column()), index.sibling(index.row(), 0).data().toInt(), value.toByteArray())) { emit(dataChanged(index, index)); return true; @@ -189,7 +189,7 @@ Qt::ItemFlags SqliteTableModel::flags(const QModelIndex& index) const if(!index.isValid()) return Qt::ItemIsEnabled; - return QAbstractTableModel::flags(index) | Qt::ItemIsEditable; + return QAbstractTableModel::flags(index)/* | Qt::ItemIsEditable*/; } void SqliteTableModel::sort(int column, Qt::SortOrder order) From 77cf98fd530581dd167a2cde85c15ed1e950554a Mon Sep 17 00:00:00 2001 From: Martin Kleusberg Date: Sun, 7 Apr 2013 19:08:40 +0200 Subject: [PATCH 07/27] SqliteTableModel: Inserting and deleting rows, clean up, bug fixes Add some basic code to support inserting and deleting rows again. Fix a few bugs here and there. Do some general code clean up. --- src/MainWindow.cpp | 113 +++++++-------------------------------- src/MainWindow.h | 2 - src/sqlitedb.cpp | 14 +++-- src/sqlitedb.h | 4 +- src/sqlitetablemodel.cpp | 43 ++++++++++++--- src/sqlitetablemodel.h | 7 ++- 6 files changed, 68 insertions(+), 115 deletions(-) diff --git a/src/MainWindow.cpp b/src/MainWindow.cpp index 62f0932a..9d469dc3 100644 --- a/src/MainWindow.cpp +++ b/src/MainWindow.cpp @@ -27,7 +27,6 @@ MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent), ui(new Ui::MainWindow), - browseTableModel(new QStandardItemModel(this)), m_browseTableModel(new SqliteTableModel(this, &db)), sqliteHighlighterTabSql(0), sqliteHighlighterLogUser(0), @@ -278,9 +277,10 @@ void MainWindow::populateTable( const QString & tablename) // Set new table if(!tablename.isEmpty()) m_browseTableModel->setTable(tablename); + ui->dataTable->setColumnHidden(0, true); // Reset sorting - curBrowseOrderByIndex = 1; + curBrowseOrderByIndex = 0; curBrowseOrderByMode = Qt::AscendingOrder; m_browseTableModel->sort(curBrowseOrderByIndex, curBrowseOrderByMode); @@ -320,7 +320,7 @@ void MainWindow::resetBrowser() int pos = ui->comboBrowseTable->findText(sCurrentTable); pos = pos == -1 ? 0 : pos; ui->comboBrowseTable->setCurrentIndex(pos); - curBrowseOrderByIndex = 1; + curBrowseOrderByIndex = 0; curBrowseOrderByMode = Qt::AscendingOrder; m_browseTableModel->sort(curBrowseOrderByIndex, curBrowseOrderByMode); populateTable(ui->comboBrowseTable->currentText()); @@ -364,111 +364,34 @@ void MainWindow::closeEvent( QCloseEvent* event ) void MainWindow::addRecord() { - if (db.addRecord(db.curBrowseTableName)) + int row = m_browseTableModel->rowCount(); + if(m_browseTableModel->insertRow(row)) { - populateTable(db.curBrowseTableName); - //added record will be the last one in view - updateTableView(db.getRecordCount()-1); - } - else - { - QMessageBox::information( this, QApplication::applicationName(), - tr("Error adding record:\n") + db.lastErrorMessage); + selectTableLine(row); + } else { + QMessageBox::warning( this, QApplication::applicationName(), tr("Error adding record:\n") + db.lastErrorMessage); } } - void MainWindow::deleteRecord() { - if(ui->dataTable->currentIndex().row() != -1) + if(ui->dataTable->currentIndex().isValid()) { - int lastselected = ui->dataTable->currentIndex().row(); - db.deleteRecord(lastselected); - populateTable(db.curBrowseTableName); - int nextselected = lastselected ; - if (nextselected > db.getRecordCount()){ - nextselected = db.getRecordCount(); - } - if (nextselected>0){ - selectTableLine(nextselected); + int row = ui->dataTable->currentIndex().row(); + if(m_browseTableModel->removeRow(row)) + { + populateTable(db.curBrowseTableName); + if(row > m_browseTableModel->totalRowCount()) + row = m_browseTableModel->totalRowCount(); + selectTableLine(row); + } else { + QMessageBox::warning( this, QApplication::applicationName(), tr("Error deleting record:\n") + db.lastErrorMessage); } } else { QMessageBox::information( this, QApplication::applicationName(), tr("Please select a record first")); } } -#define WRAP_SIZE 80 -QString wrapText(const QString& text) -{ - QString wrap; - int textSize = text.size(); - - int cur = 0; - while( wrap.size() < textSize) - { - wrap += text.mid(cur, WRAP_SIZE); - cur += WRAP_SIZE; - if( textSize - cur > WRAP_SIZE) - wrap += '\n'; - } - - return wrap; -} - -void MainWindow::updateTableView(int lineToSelect, bool keepColumnWidths) -{ - QApplication::setOverrideCursor( Qt::WaitCursor ); - -// browseTableModel->setRowCount(db.getRecordCount()); -// browseTableModel->setColumnCount(db.browseFields.count()); -// browseTableModel->setHorizontalHeaderLabels(db.browseFields); - -// rowList tab = db.browseRecs; -// int maxRecs = db.getRecordCount(); -// gotoValidator->setRange(0, maxRecs); - -// if ( maxRecs > 0 ) { - -// int rowNum = 0; -// int colNum = 0; -// QString rowLabel; -// for (int i = 0; i < tab.size(); ++i) -// { -// rowLabel.setNum(rowNum+1); -// browseTableModel->setVerticalHeaderItem(rowNum, new QStandardItem(rowLabel)); -// colNum = 0; -// QList rt = tab[i]; -// for (int e = 1; e < rt.size(); ++e) -// { -// QString content = rt[e]; - -// QStandardItem* item = new QStandardItem(content); -// item->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled); -// item->setToolTip(wrapText(content)); -// browseTableModel->setItem( rowNum, colNum, item); -// colNum++; -// } -// rowNum++; -// if (rowNum==maxRecs) break; -// } -// } - -// if(!keepColumnWidths) { -// for(int i=0;icolumnCount();++i) -// { -// ui->dataTable->resizeColumnToContents(i); -// if( ui->dataTable->columnWidth(i) > 400 ) -// ui->dataTable->setColumnWidth(i, 400); -// } -// } -// if (lineToSelect!=-1){ -// selectTableLine(lineToSelect); -// } - - setRecordsetLabel(); - QApplication::restoreOverrideCursor(); -} - void MainWindow::selectTableLine(int lineToSelect) { ui->dataTable->clearSelection(); diff --git a/src/MainWindow.h b/src/MainWindow.h index ecfcb057..0faf0a11 100644 --- a/src/MainWindow.h +++ b/src/MainWindow.h @@ -50,7 +50,6 @@ private: Ui::MainWindow* ui; - QStandardItemModel *browseTableModel; SqliteTableModel* m_browseTableModel; QStandardItemModel *queryResultListModel; QMenu *popupTableMenu; @@ -104,7 +103,6 @@ private slots: virtual void fileExit(); virtual void addRecord(); virtual void deleteRecord(); - virtual void updateTableView(int lineToSelect , bool keepColumnWidths = false); virtual void selectTableLine( int lineToSelect ); virtual void navigatePrevious(); virtual void navigateNext(); diff --git a/src/sqlitedb.cpp b/src/sqlitedb.cpp index 6e009209..477bb676 100644 --- a/src/sqlitedb.cpp +++ b/src/sqlitedb.cpp @@ -385,7 +385,7 @@ bool DBBrowserDB::executeMultiSQL(const QString& statement, bool dirty, bool log return true; } -bool DBBrowserDB::addRecord(const QString& sTableName) +int DBBrowserDB::addRecord(const QString& sTableName) { char *errmsg; if (!isOpen()) return false; @@ -404,23 +404,21 @@ bool DBBrowserDB::addRecord(const QString& sTableName) { lastErrorMessage = QString::fromUtf8(errmsg); qCritical() << "addRecord: " << lastErrorMessage; - return false; + return -1; + } else { + return sqlite3_last_insert_rowid(_db); } - return true; } -bool DBBrowserDB::deleteRecord( int wrow) +bool DBBrowserDB::deleteRecord(const QString& table, int rowid) { char * errmsg; if (!hasValidBrowseSet) return false; if (!isOpen()) return false; bool ok = false; - rowList tab = browseRecs; - QList rt = tab[wrow]; - QString rowid = rt[0]; lastErrorMessage = QString("no error"); - QString statement = QString("DELETE FROM `%1` WHERE rowid=%2;").arg(curBrowseTableName).arg(rowid); + QString statement = QString("DELETE FROM `%1` WHERE rowid=%2;").arg(table).arg(rowid); if (_db){ logSQL(statement, kLogMsg_App); diff --git a/src/sqlitedb.h b/src/sqlitedb.h index f1796b54..ef67373c 100644 --- a/src/sqlitedb.h +++ b/src/sqlitedb.h @@ -99,8 +99,8 @@ public: */ QString getTableSQL(const QString& sTable); void updateSchema() ; - bool addRecord(const QString& sTableName); - bool deleteRecord(int wrow); + int addRecord(const QString& sTableName); + bool deleteRecord(const QString& table, int rowid); bool updateRecord(const QString& table, const QString& column, int row, const QByteArray& value); bool updateRecord(int wrow, int wcol, const QByteArray& wtext); bool browseTable( const QString & tablename, const QString& orderby = "rowid" ); diff --git a/src/sqlitetablemodel.cpp b/src/sqlitetablemodel.cpp index 32ad62c3..21aa3eb2 100644 --- a/src/sqlitetablemodel.cpp +++ b/src/sqlitetablemodel.cpp @@ -70,14 +70,13 @@ void SqliteTableModel::setQuery(const QString& sQuery) for (int i = 0; i < m_columnCount; ++i) m_headers.append(QString::fromUtf8((const char *)sqlite3_column_name(stmt, i))); - int row = 0; // row data starts here do { QStringList rowdata; for (int i = 0; i < m_columnCount; ++i) rowdata.append(QString::fromUtf8((const char *)sqlite3_column_text(stmt, i))); - m_data.insert(row++, rowdata); + m_data.push_back(rowdata); } while(sqlite3_step(stmt) == SQLITE_ROW); } } @@ -124,7 +123,7 @@ QVariant SqliteTableModel::data(const QModelIndex &index, int role) const if (role == Qt::DisplayRole) { - return m_data[index.row()].at(index.column()); + return m_data.at(index.row()).at(index.column()); } else return QVariant(); @@ -136,7 +135,7 @@ bool SqliteTableModel::setData(const QModelIndex& index, const QVariant& value, { m_data[index.row()].replace(index.column(), value.toString()); - if(m_db->updateRecord(m_sTable, m_headers.at(index.column()), index.sibling(index.row(), 0).data().toInt(), value.toByteArray())) + if(m_db->updateRecord(m_sTable, m_headers.at(index.column()), m_data[index.row()].at(0).toInt(), value.toByteArray())) { emit(dataChanged(index, index)); return true; @@ -175,7 +174,7 @@ void SqliteTableModel::fetchMore(const QModelIndex& parent) for (int i = 0; i < m_columnCount; ++i) rowdata.append(QString::fromUtf8((const char *)sqlite3_column_text(stmt, i))); Q_ASSERT(m_headers.size() == rowdata.size()); - m_data.insert(row++, rowdata); + m_data.push_back(rowdata); } while(sqlite3_step(stmt) == SQLITE_ROW); } } @@ -201,10 +200,42 @@ void SqliteTableModel::sort(int column, Qt::SortOrder order) // Set the new query (but only if a table has already been set if(m_sTable != "") { - setQuery(QString("SELECT * FROM `%1` ORDER BY `%2` %3") + setQuery(QString("SELECT rowid,* FROM `%1` ORDER BY `%2` %3") .arg(m_sTable) .arg(m_headers.at(m_iSortColumn)) .arg(m_sSortOrder) ); } } + +bool SqliteTableModel::insertRows(int row, int count, const QModelIndex& parent) +{ + beginInsertRows(parent, row, row + count - 1); + + QStringList blank_data; + for(int i=0;iaddRecord(m_sTable))); + } + + endInsertRows(); + return true; +} + +bool SqliteTableModel::removeRows(int row, int count, const QModelIndex& parent) +{ + beginRemoveRows(parent, row, row + count - 1); + + for(int i=count-1;i>=0;i--) + { + m_db->deleteRecord(m_sTable, m_data.at(row + i).at(0).toInt()); + m_data.removeAt(row + i); + } + + endRemoveRows(); + return true; +} diff --git a/src/sqlitetablemodel.h b/src/sqlitetablemodel.h index bed548d3..895378ec 100644 --- a/src/sqlitetablemodel.h +++ b/src/sqlitetablemodel.h @@ -12,7 +12,7 @@ class SqliteTableModel : public QAbstractTableModel public: explicit SqliteTableModel(QObject *parent = 0, DBBrowserDB* db = 0); - int rowCount(const QModelIndex &parent) const; + int rowCount(const QModelIndex &parent = QModelIndex()) const; int totalRowCount() const; int columnCount(const QModelIndex &parent) const; QVariant headerData(int section, Qt::Orientation orientation, int role) const; @@ -22,6 +22,9 @@ public: void fetchMore(const QModelIndex &parent); size_t queryMore(size_t offset); + bool insertRows(int row, int count, const QModelIndex& parent = QModelIndex()); + bool removeRows(int row, int count, const QModelIndex& parent = QModelIndex()); + void setQuery(const QString& sQuery); void setTable(const QString& table); void setChunkSize(size_t chunksize); @@ -38,7 +41,7 @@ private: int m_rowCount; int m_columnCount; QStringList m_headers; - QMap m_data; + QList m_data; QString m_sQuery; QString m_sTable; From 225e5a1c7409068042dbb8218e819425f59accd2 Mon Sep 17 00:00:00 2001 From: Martin Kleusberg Date: Sun, 7 Apr 2013 19:21:03 +0200 Subject: [PATCH 08/27] SqliteTableModel: Don't crash when opening an empty table --- src/sqlitetablemodel.cpp | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/sqlitetablemodel.cpp b/src/sqlitetablemodel.cpp index 21aa3eb2..0bb58593 100644 --- a/src/sqlitetablemodel.cpp +++ b/src/sqlitetablemodel.cpp @@ -22,6 +22,11 @@ void SqliteTableModel::setChunkSize(size_t chunksize) void SqliteTableModel::setTable(const QString& table) { m_sTable = table; + + m_headers.clear(); + m_headers.push_back("rowid"); + m_headers.append(m_db->getTableFields(table)); + setQuery(QString("SELECT rowid,* FROM `%1`").arg(table)); } @@ -51,8 +56,7 @@ void SqliteTableModel::setQuery(const QString& sQuery) } sqlite3_finalize(stmt); - // now fetch the first 100 entries and get headers - m_headers.clear(); + // now fetch the first 100 entries m_data.clear(); m_columnCount = 0; QString sLimitQuery = QString("%1 LIMIT 0, %2;").arg(sQuery).arg(m_chunkSize); @@ -67,9 +71,6 @@ void SqliteTableModel::setQuery(const QString& sQuery) if(SQLITE_ROW == status) { m_columnCount = sqlite3_data_count(stmt); - for (int i = 0; i < m_columnCount; ++i) - m_headers.append(QString::fromUtf8((const char *)sqlite3_column_name(stmt, i))); - // row data starts here do { From 9d02480294dbc637fd7a2652024b19678a9a3d02 Mon Sep 17 00:00:00 2001 From: Martin Kleusberg Date: Sun, 7 Apr 2013 20:49:54 +0200 Subject: [PATCH 09/27] SqliteTableModel: Simplify code --- src/sqlitetablemodel.cpp | 90 ++++++++++++++-------------------------- src/sqlitetablemodel.h | 3 +- 2 files changed, 34 insertions(+), 59 deletions(-) diff --git a/src/sqlitetablemodel.cpp b/src/sqlitetablemodel.cpp index 0bb58593..6ebb4687 100644 --- a/src/sqlitetablemodel.cpp +++ b/src/sqlitetablemodel.cpp @@ -7,7 +7,6 @@ SqliteTableModel::SqliteTableModel(QObject* parent, DBBrowserDB* db) : QAbstractTableModel(parent) , m_db(db) , m_rowCount(0) - , m_columnCount(0) , m_iSortColumn(0) , m_sSortOrder("ASC") , m_chunkSize(1000) @@ -56,39 +55,15 @@ void SqliteTableModel::setQuery(const QString& sQuery) } sqlite3_finalize(stmt); - // now fetch the first 100 entries + // now fetch the first entries m_data.clear(); - m_columnCount = 0; - QString sLimitQuery = QString("%1 LIMIT 0, %2;").arg(sQuery).arg(m_chunkSize); - m_db->logSQL(sLimitQuery, kLogMsg_App); - utf8Query = sLimitQuery.toUtf8(); - status = sqlite3_prepare_v2(m_db->_db, utf8Query, utf8Query.size(), &stmt, NULL); - - if(SQLITE_OK == status) - { - status = sqlite3_step(stmt); - - if(SQLITE_ROW == status) - { - m_columnCount = sqlite3_data_count(stmt); - // row data starts here - do - { - QStringList rowdata; - for (int i = 0; i < m_columnCount; ++i) - rowdata.append(QString::fromUtf8((const char *)sqlite3_column_text(stmt, i))); - m_data.push_back(rowdata); - } while(sqlite3_step(stmt) == SQLITE_ROW); - } - } - sqlite3_finalize(stmt); + fetchData(0, m_chunkSize); emit layoutChanged(); } -int SqliteTableModel::rowCount(const QModelIndex &parent) const +int SqliteTableModel::rowCount(const QModelIndex&) const { - (void)parent; return m_data.size(); // current fetched row count } @@ -97,10 +72,9 @@ int SqliteTableModel::totalRowCount() const return m_rowCount; } -int SqliteTableModel::columnCount(const QModelIndex &parent) const +int SqliteTableModel::columnCount(const QModelIndex&) const { - (void)parent; - return m_columnCount; + return m_headers.size(); } QVariant SqliteTableModel::headerData(int section, Qt::Orientation orientation, int role) const @@ -153,35 +127,10 @@ bool SqliteTableModel::canFetchMore(const QModelIndex&) const return m_data.size() < m_rowCount; } -void SqliteTableModel::fetchMore(const QModelIndex& parent) +void SqliteTableModel::fetchMore(const QModelIndex&) { - int currentsize = m_data.size(); int row = m_data.size(); - QString sLimitQuery = QString("%1 LIMIT %2, %3;").arg(m_sQuery).arg(row).arg(row + m_chunkSize); - m_db->logSQL(sLimitQuery, kLogMsg_App); - QByteArray utf8Query = sLimitQuery.toUtf8(); - sqlite3_stmt *stmt; - int status = sqlite3_prepare_v2(m_db->_db, utf8Query, utf8Query.size(), &stmt, NULL); - - if(SQLITE_OK == status) - { - status = sqlite3_step(stmt); - - if(SQLITE_ROW == status) - { - do - { - QStringList rowdata; - for (int i = 0; i < m_columnCount; ++i) - rowdata.append(QString::fromUtf8((const char *)sqlite3_column_text(stmt, i))); - Q_ASSERT(m_headers.size() == rowdata.size()); - m_data.push_back(rowdata); - } while(sqlite3_step(stmt) == SQLITE_ROW); - } - } - sqlite3_finalize(stmt); - beginInsertRows(parent, currentsize, m_data.size()); - endInsertRows(); + fetchData(row, row + m_chunkSize); } Qt::ItemFlags SqliteTableModel::flags(const QModelIndex& index) const @@ -240,3 +189,28 @@ bool SqliteTableModel::removeRows(int row, int count, const QModelIndex& parent) endRemoveRows(); return true; } + +void SqliteTableModel::fetchData(unsigned int from, unsigned to) +{ + int currentsize = m_data.size(); + + QString sLimitQuery = QString("%1 LIMIT %2, %3;").arg(m_sQuery).arg(from).arg(to); + m_db->logSQL(sLimitQuery, kLogMsg_App); + QByteArray utf8Query = sLimitQuery.toUtf8(); + sqlite3_stmt *stmt; + int status = sqlite3_prepare_v2(m_db->_db, utf8Query, utf8Query.size(), &stmt, NULL); + + if(SQLITE_OK == status) + { + while(sqlite3_step(stmt) == SQLITE_ROW) + { + QStringList rowdata; + for (int i = 0; i < m_headers.size(); ++i) + rowdata.append(QString::fromUtf8((const char *)sqlite3_column_text(stmt, i))); + m_data.push_back(rowdata); + } + } + sqlite3_finalize(stmt); + beginInsertRows(QModelIndex(), currentsize, m_data.size()); + endInsertRows(); +} diff --git a/src/sqlitetablemodel.h b/src/sqlitetablemodel.h index 895378ec..8cbcab2d 100644 --- a/src/sqlitetablemodel.h +++ b/src/sqlitetablemodel.h @@ -37,9 +37,10 @@ signals: public slots: private: + void fetchData(unsigned int from, unsigned to); + DBBrowserDB* m_db; int m_rowCount; - int m_columnCount; QStringList m_headers; QList m_data; From 13db875acd942337ff652e759e2f29f3e627e581 Mon Sep 17 00:00:00 2001 From: Martin Kleusberg Date: Sun, 7 Apr 2013 20:55:56 +0200 Subject: [PATCH 10/27] Clear the table view in the browse tab when closing a database file --- src/MainWindow.cpp | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/MainWindow.cpp b/src/MainWindow.cpp index 9d469dc3..066ba703 100644 --- a/src/MainWindow.cpp +++ b/src/MainWindow.cpp @@ -272,11 +272,20 @@ void MainWindow::populateStructure() void MainWindow::populateTable( const QString & tablename) { + // Remove the model-view link if the table name is empty in order to remove any data from the view + if(tablename.isEmpty()) + { + ui->dataTable->setModel(0); + return; + } + QApplication::setOverrideCursor(Qt::WaitCursor); + // Set model + ui->dataTable->setModel(m_browseTableModel); + // Set new table - if(!tablename.isEmpty()) - m_browseTableModel->setTable(tablename); + m_browseTableModel->setTable(tablename); ui->dataTable->setColumnHidden(0, true); // Reset sorting From 217ca805a11a21689efef5c6e2f1ea73a208b285 Mon Sep 17 00:00:00 2001 From: Peinthor Rene Date: Sun, 7 Apr 2013 02:01:19 +0200 Subject: [PATCH 11/27] fix correct row insert --- src/sqlitetablemodel.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sqlitetablemodel.cpp b/src/sqlitetablemodel.cpp index 6ebb4687..e20a8c6a 100644 --- a/src/sqlitetablemodel.cpp +++ b/src/sqlitetablemodel.cpp @@ -211,6 +211,6 @@ void SqliteTableModel::fetchData(unsigned int from, unsigned to) } } sqlite3_finalize(stmt); - beginInsertRows(QModelIndex(), currentsize, m_data.size()); + beginInsertRows(QModelIndex(), currentsize + 1, m_data.size()); endInsertRows(); } From 9f2b0a60bd1e8213191d71993ad4d54baccf8280 Mon Sep 17 00:00:00 2001 From: Peinthor Rene Date: Sun, 7 Apr 2013 22:53:29 +0200 Subject: [PATCH 12/27] python script that creates a ~250mb test db with 1mio records --- tests/createtestdb.py | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100755 tests/createtestdb.py diff --git a/tests/createtestdb.py b/tests/createtestdb.py new file mode 100755 index 00000000..8b9a1141 --- /dev/null +++ b/tests/createtestdb.py @@ -0,0 +1,34 @@ +#!/usr/bin/python +import sys +import os +import random +import string +import sqlite3 + +CREATE = """ +CREATE TABLE IF NOT EXISTS hugetable ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + weirdtext TEXT, + crazynumber REAL +); +""" + +def main(): + if len(sys.argv) != 2: + print("please specify the db filename") + + with sqlite3.connect(sys.argv[1]) as c: + c.executescript(CREATE) + + rowcount = 1000000 + for i in range(rowcount): + text = "".join( [random.choice(string.ascii_letters) for i in range(200)] ) + num = random.random() * random.randint(0, 2930) + c.execute("INSERT INTO hugetable(weirdtext, crazynumber) VALUES ( :t, :n);", {"t": text, "n": num}) + if i % 1000 == 0: + print("inserted", i, "of", rowcount) + c.commit() + + +if __name__ == "__main__": + main() From 6742f5632da92604ff0a8d81645f462239d258f6 Mon Sep 17 00:00:00 2001 From: Martin Kleusberg Date: Tue, 9 Apr 2013 18:52:37 +0200 Subject: [PATCH 13/27] Add a filter row to the table view in the browse tab Add a row of line edits between the table header and the actual content of the table in the browse tab. Show one input widget per table column in this row. Add a live search which hides any table rows which do not fit to the current filter settings. Why add this to the parital-data-fetch branch? Because this is an attempt to get rid of the find dialog which is broken again and not even worth fixing. Also there is not much to break in this branch at the moment ;) --- src/FilterTableHeader.cpp | 78 +++++++++++++++++++++++++++++++++++++++ src/FilterTableHeader.h | 35 ++++++++++++++++++ src/MainWindow.cpp | 10 ++++- src/sqlitetablemodel.cpp | 38 +++++++++++++++---- src/sqlitetablemodel.h | 6 ++- src/src.pro | 6 ++- 6 files changed, 161 insertions(+), 12 deletions(-) create mode 100644 src/FilterTableHeader.cpp create mode 100644 src/FilterTableHeader.h diff --git a/src/FilterTableHeader.cpp b/src/FilterTableHeader.cpp new file mode 100644 index 00000000..926396dc --- /dev/null +++ b/src/FilterTableHeader.cpp @@ -0,0 +1,78 @@ +#include "FilterTableHeader.h" +#include +#include +#include + +FilterTableHeader::FilterTableHeader(QTableView* parent) : + QHeaderView(Qt::Horizontal, parent) +{ + // Activate the click signals to allow sorting + setClickable(true); + + // Do some connects: Basically just resize and reposition the input widgets whenever anything changes + connect(this, SIGNAL(sectionResized(int,int,int)), this, SLOT(adjustPositions())); + connect(parent->horizontalScrollBar(), SIGNAL(valueChanged(int)), this, SLOT(adjustPositions())); + connect(parent->verticalScrollBar(), SIGNAL(valueChanged(int)), this, SLOT(adjustPositions())); +} + +void FilterTableHeader::generateFilters(int number) +{ + // Delete all the current filter widgets + for(int i=0;isetPlaceholderText(tr("Filter")); + l->setProperty("column", i); // Store the column number for later use + l->setVisible(i>0); // This hides the first input widget which belongs to the hidden rowid column + connect(l, SIGNAL(textChanged(QString)), this, SLOT(inputChanged(QString))); + filterWidgets.push_back(l); + } + + // Position them correctly + adjustPositions(); +} + +QSize FilterTableHeader::sizeHint() const +{ + // For the size hint just take the value of the standard implementation and add the height of a input widget to it if necessary + QSize s = QHeaderView::sizeHint(); + if(filterWidgets.size()) + s.setHeight(s.height() + filterWidgets.at(0)->sizeHint().height() + 5); // The 5 adds just adds some extra space + return s; +} + +void FilterTableHeader::updateGeometries() +{ + // If there are any input widgets add a viewport margin to the header to generate some empty space for them which is not affected by scrolling + if(filterWidgets.size()) + setViewportMargins(0, 0, 0, filterWidgets.at(0)->sizeHint().height()); + else + setViewportMargins(0, 0, 0, 0); + + // Now just call the parent implementation and reposition the input widgets + QHeaderView::updateGeometries(); + adjustPositions(); +} + +void FilterTableHeader::adjustPositions() +{ + // Loop through all widgets + for(int i=0;imove(sectionPosition(i) - offset(), filterWidgets.at(i)->sizeHint().height() + 2); // The two adds some extra space between the header label and the input widget + w->resize(sectionSize(i), filterWidgets.at(i)->sizeHint().height()); + } +} + +void FilterTableHeader::inputChanged(const QString& new_value) +{ + // Just get the column number and the new value and send them to anybody interested in filter changes + emit filterChanged(sender()->property("column").toInt(), new_value); +} diff --git a/src/FilterTableHeader.h b/src/FilterTableHeader.h new file mode 100644 index 00000000..1a7aa050 --- /dev/null +++ b/src/FilterTableHeader.h @@ -0,0 +1,35 @@ +#ifndef __FILTERTABLEHEADER_H__ +#define __FILTERTABLEHEADER_H__ + +#include +#include + +class QLineEdit; +class QTableView; + +class FilterTableHeader : public QHeaderView +{ + Q_OBJECT + +public: + explicit FilterTableHeader(QTableView* parent = 0); + virtual QSize sizeHint() const; + +public slots: + void generateFilters(int number); + void adjustPositions(); + +signals: + void filterChanged(int column, QString value); + +protected: + virtual void updateGeometries(); + +private slots: + void inputChanged(const QString& new_value); + +private: + QList filterWidgets; +}; + +#endif diff --git a/src/MainWindow.cpp b/src/MainWindow.cpp index 066ba703..14744e4f 100644 --- a/src/MainWindow.cpp +++ b/src/MainWindow.cpp @@ -23,6 +23,7 @@ #include "SQLiteSyntaxHighlighter.h" #include "sqltextedit.h" #include "sqlitetablemodel.h" +#include "FilterTableHeader.h" MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent), @@ -70,6 +71,10 @@ void MainWindow::init() queryResultListModel = new QStandardItemModel(this); ui->queryResultTableView->setModel(queryResultListModel); + FilterTableHeader* tableHeader = new FilterTableHeader(ui->dataTable); + connect(tableHeader, SIGNAL(filterChanged(int,QString)), m_browseTableModel, SLOT(updateFilter(int,QString))); + ui->dataTable->setHorizontalHeader(tableHeader); + // Create the actions for the recently opened dbs list for(int i = 0; i < MaxRecentFiles; ++i) { recentFileActs[i] = new QAction(this); @@ -98,7 +103,7 @@ void MainWindow::init() ui->statusbar->addPermanentWidget(statusEncodingLabel); // Connect some more signals and slots - connect(ui->dataTable->horizontalHeader(), SIGNAL(sectionClicked(int)), this, SLOT(browseTableHeaderClicked(int))); + connect(tableHeader, SIGNAL(sectionClicked(int)), this, SLOT(browseTableHeaderClicked(int))); connect(ui->dataTable->verticalScrollBar(), SIGNAL(valueChanged(int)), this, SLOT(setRecordsetLabel())); connect(editWin, SIGNAL(goingAway()), this, SLOT(editWinAway())); connect(editWin, SIGNAL(updateRecordText(int, int, QByteArray)), this, SLOT(updateRecordText(int, int, QByteArray))); @@ -296,6 +301,9 @@ void MainWindow::populateTable( const QString & tablename) // Get table layout db.browseTable(tablename); + // Update the filter row + qobject_cast(ui->dataTable->horizontalHeader())->generateFilters(m_browseTableModel->columnCount()); + // Activate the add and delete record buttons and editing only if a table has been selected bool is_table = db.getObjectByName(tablename).gettype() == "table"; ui->buttonNewRecord->setEnabled(is_table); diff --git a/src/sqlitetablemodel.cpp b/src/sqlitetablemodel.cpp index e20a8c6a..4689f1c2 100644 --- a/src/sqlitetablemodel.cpp +++ b/src/sqlitetablemodel.cpp @@ -26,7 +26,9 @@ void SqliteTableModel::setTable(const QString& table) m_headers.push_back("rowid"); m_headers.append(m_db->getTableFields(table)); - setQuery(QString("SELECT rowid,* FROM `%1`").arg(table)); + m_mWhere.clear(); + + buildQuery(); } void SqliteTableModel::setQuery(const QString& sQuery) @@ -149,13 +151,7 @@ void SqliteTableModel::sort(int column, Qt::SortOrder order) // Set the new query (but only if a table has already been set if(m_sTable != "") - { - setQuery(QString("SELECT rowid,* FROM `%1` ORDER BY `%2` %3") - .arg(m_sTable) - .arg(m_headers.at(m_iSortColumn)) - .arg(m_sSortOrder) - ); - } + buildQuery(); } bool SqliteTableModel::insertRows(int row, int count, const QModelIndex& parent) @@ -214,3 +210,29 @@ void SqliteTableModel::fetchData(unsigned int from, unsigned to) beginInsertRows(QModelIndex(), currentsize + 1, m_data.size()); endInsertRows(); } + +void SqliteTableModel::buildQuery() +{ + QString where; + if(m_mWhere.size()) + { + where = "WHERE 1=1"; + for(QMap::const_iterator i=m_mWhere.constBegin();i!=m_mWhere.constEnd();++i) + where.append(QString(" AND `%1` %2").arg(m_headers.at(i.key())).arg(i.value())); + } + + QString sql = QString("SELECT rowid,* FROM `%1` %2 ORDER BY `%3` %4").arg(m_sTable).arg(where).arg(m_headers.at(m_iSortColumn)).arg(m_sSortOrder); + setQuery(sql); +} + +void SqliteTableModel::updateFilter(int column, QString value) +{ + // If the value was set to an empty string remove any filter for this column. Otherwise insert a new filter rule or replace the old one if there is already one + if(value.isEmpty()) + m_mWhere.remove(column); + else + m_mWhere.insert(column, QString("LIKE '%1'").arg(value.replace('\'', ""))); // TODO: Add some code here to detect fancy filter values like '>5' and change the operator in these cases + + // Build the new query + buildQuery(); +} diff --git a/src/sqlitetablemodel.h b/src/sqlitetablemodel.h index 8cbcab2d..c9d86c36 100644 --- a/src/sqlitetablemodel.h +++ b/src/sqlitetablemodel.h @@ -14,7 +14,7 @@ public: int rowCount(const QModelIndex &parent = QModelIndex()) const; int totalRowCount() const; - int columnCount(const QModelIndex &parent) const; + int columnCount(const QModelIndex &parent = QModelIndex()) const; QVariant headerData(int section, Qt::Orientation orientation, int role) const; QVariant data(const QModelIndex &index, int role) const; bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole); @@ -35,10 +35,13 @@ public: signals: public slots: + void updateFilter(int column, QString value); private: void fetchData(unsigned int from, unsigned to); + void buildQuery(); + DBBrowserDB* m_db; int m_rowCount; QStringList m_headers; @@ -48,6 +51,7 @@ private: QString m_sTable; int m_iSortColumn; QString m_sSortOrder; + QMap m_mWhere; size_t m_chunkSize; }; diff --git a/src/src.pro b/src/src.pro index 2787f12e..47cbc3e7 100644 --- a/src/src.pro +++ b/src/src.pro @@ -26,7 +26,8 @@ HEADERS += \ grammar/Sqlite3Lexer.hpp \ grammar/Sqlite3Parser.hpp \ grammar/sqlite3TokenTypes.hpp \ - sqlitetablemodel.h + sqlitetablemodel.h \ + FilterTableHeader.h SOURCES += \ sqlitedb.cpp \ @@ -45,7 +46,8 @@ SOURCES += \ ExtendedTableWidget.cpp \ grammar/Sqlite3Lexer.cpp \ grammar/Sqlite3Parser.cpp \ - sqlitetablemodel.cpp + sqlitetablemodel.cpp \ + FilterTableHeader.cpp # create a unittest option CONFIG(unittest) { From 44a635167d199485dc5210577c4fd0905f85c459 Mon Sep 17 00:00:00 2001 From: Martin Kleusberg Date: Tue, 9 Apr 2013 19:28:21 +0200 Subject: [PATCH 14/27] Allow different comparison operators in the new filter row Allow other comparison methods than just the LIKE operator when using the new filter row. --- src/sqlitetablemodel.cpp | 35 +++++++++++++++++++++++++++++++++-- src/sqlitetablemodel.h | 2 +- 2 files changed, 34 insertions(+), 3 deletions(-) diff --git a/src/sqlitetablemodel.cpp b/src/sqlitetablemodel.cpp index 4689f1c2..87e48350 100644 --- a/src/sqlitetablemodel.cpp +++ b/src/sqlitetablemodel.cpp @@ -225,13 +225,44 @@ void SqliteTableModel::buildQuery() setQuery(sql); } -void SqliteTableModel::updateFilter(int column, QString value) +void SqliteTableModel::updateFilter(int column, const QString& value) { + // Check for any special comparison operators at the beginning of the value string. If there are none default to LIKE. + QString op = "LIKE"; + QString val; + if(value.left(2) == ">=" || value.left(2) == "<=" || value.left(2) == "<>") + { + bool ok; + value.mid(2).toFloat(&ok); + if(ok) + { + op = value.left(2); + val = value.mid(2); + } + } else if(value.left(1) == ">" || value.left(1) == "<") { + bool ok; + value.mid(1).toFloat(&ok); + if(ok) + { + op = value.left(1); + val = value.mid(1); + } + } else { + if(value.left(1) == "=") + { + op = "="; + val = value.mid(1); + } else { + val = value; + } + val = QString("'%1'").arg(val.replace("'", "")); + } + // If the value was set to an empty string remove any filter for this column. Otherwise insert a new filter rule or replace the old one if there is already one if(value.isEmpty()) m_mWhere.remove(column); else - m_mWhere.insert(column, QString("LIKE '%1'").arg(value.replace('\'', ""))); // TODO: Add some code here to detect fancy filter values like '>5' and change the operator in these cases + m_mWhere.insert(column, QString("%1 %2").arg(op).arg(val)); // Build the new query buildQuery(); diff --git a/src/sqlitetablemodel.h b/src/sqlitetablemodel.h index c9d86c36..7644e0a1 100644 --- a/src/sqlitetablemodel.h +++ b/src/sqlitetablemodel.h @@ -35,7 +35,7 @@ public: signals: public slots: - void updateFilter(int column, QString value); + void updateFilter(int column, const QString& value); private: void fetchData(unsigned int from, unsigned to); From 2463cf3d5debf4c31e5757038bf30ba639f84570 Mon Sep 17 00:00:00 2001 From: Martin Kleusberg Date: Tue, 9 Apr 2013 19:36:17 +0200 Subject: [PATCH 15/27] Get rid of the find dialog Remove the old find dialog. It's fully replaced by the new filter row now. Good riddance! --- src/FindDialog.cpp | 61 ------------- src/FindDialog.h | 38 -------- src/FindDialog.ui | 193 ---------------------------------------- src/MainWindow.cpp | 69 +------------- src/MainWindow.h | 5 -- src/MainWindow.ui | 42 +-------- src/icons/icons.qrc | 1 - src/icons/magnifier.png | Bin 615 -> 0 bytes src/src.pro | 3 - 9 files changed, 2 insertions(+), 410 deletions(-) delete mode 100644 src/FindDialog.cpp delete mode 100644 src/FindDialog.h delete mode 100644 src/FindDialog.ui delete mode 100644 src/icons/magnifier.png diff --git a/src/FindDialog.cpp b/src/FindDialog.cpp deleted file mode 100644 index c32a07e8..00000000 --- a/src/FindDialog.cpp +++ /dev/null @@ -1,61 +0,0 @@ -#include "FindDialog.h" -#include "ui_FindDialog.h" - -FindDialog::FindDialog(QWidget* parent) - : QDialog(parent), - ui(new Ui::FindDialog) -{ - ui->setupUi(this); -} - -FindDialog::~FindDialog() -{ - delete ui; -} - -void FindDialog::showResults(const resultMap& rmap) -{ - ui->tableResults->setSortingEnabled(false); - ui->tableResults->clearContents(); - resultMap::const_iterator it; - int rowNum; - ui->tableResults->setRowCount(rmap.size()); - for(it=rmap.begin(),rowNum=0;it!=rmap.end();++it,rowNum++) - { - QString firstline = it.value().section('\n', 0, 0); - ui->tableResults->setItem(rowNum, 0, new QTableWidgetItem(QString::number(it.key()))); - ui->tableResults->setItem(rowNum, 1, new QTableWidgetItem(firstline)); - } - QString results = tr("Found: %1").arg(ui->tableResults->rowCount()); - ui->labelNumberResults->setText(results); - ui->tableResults->setSortingEnabled(true); -} - -void FindDialog::find() -{ - emit lookfor(ui->comboColumn->currentText(), ui->comboOperator->currentText(), ui->editSearchString->text()); -} - -void FindDialog::resetFields(const QStringList& fieldlist) -{ - ui->comboColumn->clear(); - ui->comboColumn->addItems(fieldlist); - ui->editSearchString->setText(""); - ui->comboOperator->setCurrentIndex(0); - ui->tableResults->clearContents(); - ui->labelNumberResults->setText(tr("Found: 0")); -} - -void FindDialog::recordSelected(QTableWidgetItem* witem) -{ - if(witem) - { - int recNum = ui->tableResults->item(witem->row(), 0)->text().toInt(); - emit showrecord(recNum); - } -} - -void FindDialog::closeEvent(QCloseEvent*) -{ - emit goingAway(); -} diff --git a/src/FindDialog.h b/src/FindDialog.h deleted file mode 100644 index 46b0eef1..00000000 --- a/src/FindDialog.h +++ /dev/null @@ -1,38 +0,0 @@ -#ifndef __FINDDIALOG_H__ -#define __FINDDIALOG_H__ - -#include -#include "sqlitedb.h" -class QTableWidgetItem; - -namespace Ui { -class FindDialog; -} - -class FindDialog : public QDialog -{ - Q_OBJECT - -public: - explicit FindDialog(QWidget* parent = 0); - ~FindDialog(); - -public slots: - virtual void showResults(const resultMap& rmap); - virtual void resetFields(const QStringList& fieldlist = QStringList()); - -private slots: - virtual void find(); - virtual void recordSelected(QTableWidgetItem* witem); - virtual void closeEvent(QCloseEvent*); - -signals: - void lookfor(const QString&, const QString&, const QString&); - void showrecord(int); - void goingAway(); - -private: - Ui::FindDialog* ui; -}; - -#endif diff --git a/src/FindDialog.ui b/src/FindDialog.ui deleted file mode 100644 index aa86b6be..00000000 --- a/src/FindDialog.ui +++ /dev/null @@ -1,193 +0,0 @@ - - - FindDialog - - - - 0 - 0 - 288 - 351 - - - - Find - - - true - - - - - - - - Field to be searched - - - Use this control to select the field to be searched in the current table - - - - - - - Search criteria: use 'contains' for partial matches - - - This control is used to select the search criteria used to look for the search term in the database. Use '=' or 'contains' to find words, and the comparison symbols to filter numeric data. - - - - = - - - - - contains - - - - - > - - - - - >= - - - - - <= - - - - - < - - - - - <> - - - - - - - - - - - - Enter values or words to search - - - This is a place to enter the word or number to be searched in the database - - - - - - - Perform the search - - - This button starts the search process - - - &Search - - - true - - - - - - - - - Results of the search will appear in this area. Click on a result to select the corresponding record in the database - - - QAbstractItemView::NoEditTriggers - - - false - - - true - - - QAbstractItemView::NoSelection - - - - Records - - - - - Data - - - - - - - - Found: 0 - - - - - - - comboColumn - comboOperator - editSearchString - buttonSearch - tableResults - - - - - buttonSearch - clicked() - FindDialog - find() - - - 233 - 45 - - - 267 - 30 - - - - - tableResults - itemClicked(QTableWidgetItem*) - FindDialog - recordSelected(QTableWidgetItem*) - - - 100 - 176 - - - 86 - 58 - - - - - - find() - recordSelected(QTableWidgetItem*) - - diff --git a/src/MainWindow.cpp b/src/MainWindow.cpp index 14744e4f..10d8c5c7 100644 --- a/src/MainWindow.cpp +++ b/src/MainWindow.cpp @@ -19,7 +19,6 @@ #include "ExportCsvDialog.h" #include "PreferencesDialog.h" #include "EditDialog.h" -#include "FindDialog.h" #include "SQLiteSyntaxHighlighter.h" #include "sqltextedit.h" #include "sqlitetablemodel.h" @@ -33,7 +32,6 @@ MainWindow::MainWindow(QWidget* parent) sqliteHighlighterLogUser(0), sqliteHighlighterLogApp(0), editWin(new EditDialog(this)), - findWin(0), gotoValidator(new QIntValidator(0, 0, this)) { ui->setupUi(this); @@ -313,9 +311,7 @@ void MainWindow::populateTable( const QString & tablename) // Set the recordset label setRecordsetLabel(); - //got to keep findWin in synch - if(findWin) - findWin->resetFields(); + // Reset the edit dialog if(editWin) editWin->reset(); @@ -468,74 +464,11 @@ void MainWindow::setRecordsetLabel() ui->labelRecordset->setText(tr("%1 - %2 of %3").arg(from).arg(to).arg(total)); } -void MainWindow::browseFind(bool open) -{ - if(open) - { - if(!findWin) - { - findWin = new FindDialog(this); - connect(findWin, SIGNAL(lookfor(const QString&, const QString&, const QString&)), this, SLOT(lookfor(const QString&, const QString&, const QString&))); - connect(findWin, SIGNAL(showrecord(int)),this, SLOT(selectTableLine(int))); - connect(findWin, SIGNAL(goingAway()),this, SLOT(browseFindAway())); - } - findWin->resetFields(db.getTableFields(db.curBrowseTableName)); - findWin->show(); - } else { - if(findWin) - findWin->hide(); - } -} - -void MainWindow::browseFindAway() -{ - ui->buttonFind->toggle(); -} - void MainWindow::browseRefresh() { populateTable(ui->comboBrowseTable->currentText()); } -void MainWindow::lookfor( const QString & wfield, const QString & woperator, const QString & wsearchterm ) -{ - if (!db.isOpen()){ - QMessageBox::information( this, QApplication::applicationName(), tr("There is no database opened. Please open or create a new database file.")); - return; - } - - //we may need to modify woperator and wsearchterm, so use copies - QString finaloperator = woperator; - QString finalsearchterm = wsearchterm; - - //special case for CONTAINS operator: use LIKE and surround the search word with % characters - if(woperator.compare(tr("contains")) == 0) - { - finaloperator = QString("LIKE"); - QString newsearchterm = "%"; - newsearchterm.append(wsearchterm); - newsearchterm.append("%"); - finalsearchterm = QString(newsearchterm); - } - QApplication::setOverrideCursor( Qt::WaitCursor ); - QString statement = QString("SELECT rowid, `%1` FROM `%2` WHERE `%3` %4 ").arg(wfield).arg(db.curBrowseTableName).arg(wfield).arg(finaloperator); - //searchterm needs to be quoted if it is not a number - bool ok = false; - finalsearchterm.toDouble(&ok); - if (!ok) finalsearchterm.toInt(&ok, 10); - if (!ok) {//not a number, quote it - char * formSQL = sqlite3_mprintf("%Q",(const char *) finalsearchterm.toUtf8()); - statement.append(formSQL); - if (formSQL) sqlite3_free(formSQL); - } else {//append the number, unquoted - statement.append(finalsearchterm); - } - statement.append(" ORDER BY rowid;"); - resultMap res = db.getFindResults(statement); - findWin->showResults(res); - QApplication::restoreOverrideCursor(); -} - void MainWindow::createTable() { if (!db.isOpen()){ diff --git a/src/MainWindow.h b/src/MainWindow.h index 0faf0a11..97925056 100644 --- a/src/MainWindow.h +++ b/src/MainWindow.h @@ -6,7 +6,6 @@ class QDragEnterEvent; class EditDialog; -class FindDialog; class SQLiteSyntaxHighlighter; class QStandardItemModel; class QIntValidator; @@ -69,7 +68,6 @@ private: Qt::SortOrder curBrowseOrderByMode; EditDialog* editWin; - FindDialog* findWin; QIntValidator* gotoValidator; DBBrowserDB db; @@ -108,10 +106,7 @@ private slots: virtual void navigateNext(); virtual void navigateGoto(); virtual void setRecordsetLabel(); - virtual void browseFind( bool open ); - virtual void browseFindAway(); virtual void browseRefresh(); - virtual void lookfor( const QString & wfield, const QString & woperator, const QString & wsearchterm ); virtual void createTable(); virtual void createIndex(); virtual void compact(); diff --git a/src/MainWindow.ui b/src/MainWindow.ui index b6a1995d..d21187b6 100644 --- a/src/MainWindow.ui +++ b/src/MainWindow.ui @@ -116,29 +116,6 @@ - - - - Open or close the floating find window - - - This button toggles the appearance of the Find window, used to search records in the database view - - - - - - - :/icons/searchfind:/icons/searchfind - - - Ctrl+F - - - true - - - @@ -315,7 +292,7 @@ 0 0 - 278 + 763 444 @@ -1367,7 +1344,6 @@ dbTreeWidget comboBrowseTable - buttonFind buttonRefresh buttonNewRecord buttonDeleteRecord @@ -1581,22 +1557,6 @@ - - buttonFind - toggled(bool) - MainWindow - browseFind(bool) - - - 141 - 81 - - - 399 - 299 - - - buttonRefresh clicked() diff --git a/src/icons/icons.qrc b/src/icons/icons.qrc index b515de78..ed3d0978 100644 --- a/src/icons/icons.qrc +++ b/src/icons/icons.qrc @@ -22,7 +22,6 @@ script.png script_add.png script_delete.png - magnifier.png wrench.png help.png diff --git a/src/icons/magnifier.png b/src/icons/magnifier.png deleted file mode 100644 index cf3d97f75e9cde9c143980d89272fe61fc2d64ee..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 615 zcmV-t0+{`YP)gNuvOO$0ks zMIj=HnnBRUR?tKXG11rxCU4&7dG4NbuvR2_mEvc)n?Cow;~Wve|KR^>9@p5l)|QB+ z$jmun3q#x>;ss-PW_mnr2MHVzLAl1RW&0?VkixF*4t!St0YVb2wnKdU(kmOHiL;aW zK8Xte%(k>MVGG$E4no6dcNnb>BhVHHGD&1pv4YZ68kE2V03t5#PCEFm7=ad$6)+3B zTCmn*?A?=u(o~ET7~-7g0)ZB=6|lumi4}B}MLgy~Ysy6)Q5%Al7|05&1z3Jpu>cF8 z3?VXs*3<}%h3`5Wld)N2zJnk%Agw<~3k)sPTLFd=F5;d8-bj-09SkQuynfflNcZLN z!^_37fdZvzrq=9~mp*($%mcDRKC&qvaaZuX+C=AT6O*~tHl>0mcP<_q>-z%$xO(@! zYluq5a8VQI$S@4?r*v;gPo!QQ%pX3A#>xx4t=w-L6COWx?aj&`f+!YePsFtj=hOQR zP3=E2j@9L7s8;T^&s?u(Hdpu?CubjMrGn{t_37>9$|AD)QE08weJlKn8|OyjL~7oP zC8mPT`jzuH*Dh^I0048RGafUIT)4H~*m8m>egI0iH=(LB%b@@O002ovPDHLkV1lw0 B3 Date: Wed, 10 Apr 2013 18:18:08 +0200 Subject: [PATCH 16/27] SqliteTableModel: Restore BLOB support and clean up DBSQLiteDB --- src/sqlitedb.cpp | 80 ---------------------------------------- src/sqlitedb.h | 5 --- src/sqlitetablemodel.cpp | 10 ++--- src/sqlitetablemodel.h | 4 +- 4 files changed, 8 insertions(+), 91 deletions(-) diff --git a/src/sqlitedb.cpp b/src/sqlitedb.cpp index 477bb676..ad27a977 100644 --- a/src/sqlitedb.cpp +++ b/src/sqlitedb.cpp @@ -434,45 +434,6 @@ bool DBBrowserDB::deleteRecord(const QString& table, int rowid) return ok; } -bool DBBrowserDB::updateRecord(int wrow, int wcol, const QByteArray& wtext) -{ - if (!hasValidBrowseSet) return false; - if (!isOpen()) return false; - - lastErrorMessage = QString("no error"); - - QList& rt = browseRecs[wrow]; - QString rowid = rt[0]; - QByteArray& cv = rt[wcol+1];//must account for rowid - QString ct = browseFields.at(wcol); - - QString sql = QString("UPDATE `%1` SET `%2`=? WHERE rowid=%4;").arg(curBrowseTableName).arg(ct).arg(rowid); - - logSQL(sql, kLogMsg_App); - setRestorePoint(); - - sqlite3_stmt* stmt; - int success = 1; - if(sqlite3_prepare_v2(_db, sql.toUtf8(), -1, &stmt, 0) != SQLITE_OK) - success = 0; - if(success == 1 && sqlite3_bind_text(stmt, 1, wtext.constData(), wtext.length(), SQLITE_STATIC) != SQLITE_OK) - success = -1; - if(success == 1 && sqlite3_step(stmt) != SQLITE_DONE) - success = -1; - if(success != 0 && sqlite3_finalize(stmt) != SQLITE_OK) - success = -1; - - if(success == 1) - { - cv = wtext; - return true; - } else { - lastErrorMessage = sqlite3_errmsg(_db); - qCritical() << "updateRecord: " << lastErrorMessage; - return false; - } -} - bool DBBrowserDB::updateRecord(const QString& table, const QString& column, int row, const QByteArray& value) { if (!isOpen()) return false; @@ -741,47 +702,6 @@ bool DBBrowserDB::renameTable(const QString& from_table, const QString& to_table } } -void DBBrowserDB::getTableRecords( const QString & tablename, const QString& orderby ) -{ - sqlite3_stmt* stmt; - - int ncol; - QList r; - browseRecs.clear(); - idmap.clear(); - lastErrorMessage = QObject::tr("no error"); - - QString sql = QString("SELECT rowid, * FROM `%1` ORDER BY %2;").arg(tablename).arg(orderby); - logSQL(sql, kLogMsg_App); - if(sqlite3_prepare_v2(_db, sql.toUtf8(), -1, &stmt, 0) != SQLITE_OK) - { - lastErrorMessage = QObject::tr("could not get fields"); - return; - } - - int rownum = 0; - - while(sqlite3_step(stmt) == SQLITE_ROW) - { - r.clear(); - ncol = sqlite3_data_count(stmt); - for(int e=0;e(sqlite3_column_blob(stmt, e)), sqlite3_column_bytes(stmt, e)); - r.append(rv); - - if(e == 0) - { - idmap.insert(rv.toInt(), rownum); - rownum++; - } - } - browseRecs.append(r); - } - - sqlite3_finalize(stmt); -} - resultMap DBBrowserDB::getFindResults( const QString & wstatement) { sqlite3_stmt *vm; diff --git a/src/sqlitedb.h b/src/sqlitedb.h index ef67373c..842659aa 100644 --- a/src/sqlitedb.h +++ b/src/sqlitedb.h @@ -102,7 +102,6 @@ public: int addRecord(const QString& sTableName); bool deleteRecord(const QString& table, int rowid); bool updateRecord(const QString& table, const QString& column, int row, const QByteArray& value); - bool updateRecord(int wrow, int wcol, const QByteArray& wtext); bool browseTable( const QString & tablename, const QString& orderby = "rowid" ); bool createTable(const QString& name, const QList& structure); @@ -145,12 +144,8 @@ public: MainWindow* mainWindow; - private: bool dirty; - void getTableRecords( const QString & tablename, const QString& orderby = "rowid" ); - - }; #endif diff --git a/src/sqlitetablemodel.cpp b/src/sqlitetablemodel.cpp index 87e48350..845299a6 100644 --- a/src/sqlitetablemodel.cpp +++ b/src/sqlitetablemodel.cpp @@ -110,7 +110,7 @@ bool SqliteTableModel::setData(const QModelIndex& index, const QVariant& value, { if(index.isValid() && role == Qt::EditRole) { - m_data[index.row()].replace(index.column(), value.toString()); + m_data[index.row()].replace(index.column(), value.toByteArray()); if(m_db->updateRecord(m_sTable, m_headers.at(index.column()), m_data[index.row()].at(0).toInt(), value.toByteArray())) { @@ -158,14 +158,14 @@ bool SqliteTableModel::insertRows(int row, int count, const QModelIndex& parent) { beginInsertRows(parent, row, row + count - 1); - QStringList blank_data; + QByteArrayList blank_data; for(int i=0;iaddRecord(m_sTable))); + m_data[row].replace(0, QByteArray::number(m_db->addRecord(m_sTable))); } endInsertRows(); @@ -200,9 +200,9 @@ void SqliteTableModel::fetchData(unsigned int from, unsigned to) { while(sqlite3_step(stmt) == SQLITE_ROW) { - QStringList rowdata; + QByteArrayList rowdata; for (int i = 0; i < m_headers.size(); ++i) - rowdata.append(QString::fromUtf8((const char *)sqlite3_column_text(stmt, i))); + rowdata.append(QByteArray(static_cast(sqlite3_column_blob(stmt, i)), sqlite3_column_bytes(stmt, i))); m_data.push_back(rowdata); } } diff --git a/src/sqlitetablemodel.h b/src/sqlitetablemodel.h index 7644e0a1..1a1dbcfa 100644 --- a/src/sqlitetablemodel.h +++ b/src/sqlitetablemodel.h @@ -32,6 +32,8 @@ public: Qt::ItemFlags flags(const QModelIndex& index) const; + typedef QList QByteArrayList; + signals: public slots: @@ -45,7 +47,7 @@ private: DBBrowserDB* m_db; int m_rowCount; QStringList m_headers; - QList m_data; + QList m_data; QString m_sQuery; QString m_sTable; From f45b89b6c9e07c05d97f787085df0579d713e07f Mon Sep 17 00:00:00 2001 From: Martin Kleusberg Date: Wed, 10 Apr 2013 18:51:18 +0200 Subject: [PATCH 17/27] Fix SQL export to work with the SqliteTableModel --- src/sqlitedb.cpp | 48 ++++++++++++------------------------------ src/sqlitedb.h | 1 - src/sqlitetablemodel.h | 6 +++--- 3 files changed, 17 insertions(+), 38 deletions(-) diff --git a/src/sqlitedb.cpp b/src/sqlitedb.cpp index ad27a977..38d2a3b6 100644 --- a/src/sqlitedb.cpp +++ b/src/sqlitedb.cpp @@ -1,5 +1,5 @@ #include "sqlitedb.h" - +#include "sqlitetablemodel.h" #include "MainWindow.h" #include @@ -234,8 +234,11 @@ bool DBBrowserDB::dump(const QString& filename) unsigned int numRecordsTotal = 0, numRecordsCurrent = 0; QList tables = objMap.values("table"); for(QList::ConstIterator it=tables.begin();it!=tables.end();++it) - numRecordsTotal += getFindResults( - QString("SELECT COUNT(*) FROM `%1`;").arg((*it).getname())).value(0).toInt(); + { + SqliteTableModel tableModel(0, this); + tableModel.setTable((*it).getname()); + numRecordsTotal += tableModel.totalRowCount(); + } QProgressDialog progress(QObject::tr("Exporting database to SQL file..."), QObject::tr("Cancel"), 0, numRecordsTotal); progress.setWindowModality(Qt::ApplicationModal); @@ -256,16 +259,18 @@ bool DBBrowserDB::dump(const QString& filename) stream << (*it).getsql() << ";\n"; // Get data of this table - browseTable((*it).getname()); + SqliteTableModel tableModel(0, this); + tableModel.setTable((*it).getname()); + while(tableModel.canFetchMore()) + tableModel.fetchMore(); // Dump all the content of the table - rowList data = browseRecs; - for(int row=0;row Date: Wed, 10 Apr 2013 18:59:37 +0200 Subject: [PATCH 18/27] Fix CSV export to work with SqliteTableModel --- src/ExportCsvDialog.cpp | 23 ++++++++++++----------- src/sqlitedb.cpp | 1 - src/sqlitetablemodel.h | 2 +- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/ExportCsvDialog.cpp b/src/ExportCsvDialog.cpp index adeb053a..c97beadf 100644 --- a/src/ExportCsvDialog.cpp +++ b/src/ExportCsvDialog.cpp @@ -6,6 +6,7 @@ #include "ui_ExportCsvDialog.h" #include "sqlitedb.h" #include "PreferencesDialog.h" +#include "sqlitetablemodel.h" ExportCsvDialog::ExportCsvDialog(DBBrowserDB* db, QWidget* parent) : QDialog(parent), @@ -39,7 +40,10 @@ void ExportCsvDialog::accept() if(fileName.size() > 0) { // Get data from selected table - pdb->browseTable(ui->comboTable->currentText()); + SqliteTableModel tableModel(this, pdb); + tableModel.setTable(ui->comboTable->currentText()); + while(tableModel.canFetchMore()) + tableModel.fetchMore(); // Prepare the quote and separating characters QString quoteChar = ui->comboQuoteCharacter->currentText(); @@ -58,11 +62,10 @@ void ExportCsvDialog::accept() // Put field names in first row if user wants to have them if(ui->checkHeader->isChecked()) { - QStringList fields = pdb->browseFields; - for(int i=0;ibrowseRecs; - for(int i=0;i row = data[i]; - for(int j=1;j0) {//table exists - //getTableRecords( tablename, orderby ); browseFields = testFields; hasValidBrowseSet = true; curBrowseTableName = tablename; diff --git a/src/sqlitetablemodel.h b/src/sqlitetablemodel.h index 5a3e1172..3cecacca 100644 --- a/src/sqlitetablemodel.h +++ b/src/sqlitetablemodel.h @@ -15,7 +15,7 @@ public: int rowCount(const QModelIndex &parent = QModelIndex()) const; int totalRowCount() const; int columnCount(const QModelIndex &parent = QModelIndex()) const; - QVariant headerData(int section, Qt::Orientation orientation, int role) const; + QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole); bool canFetchMore(const QModelIndex &parent = QModelIndex()) const; From 334cc26c57982a63e16f8463fbf3e81bae744a9b Mon Sep 17 00:00:00 2001 From: Martin Kleusberg Date: Wed, 10 Apr 2013 19:27:16 +0200 Subject: [PATCH 19/27] SqliteTableModel: Speed up on large table in certain circumstances --- src/sqlitetablemodel.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/sqlitetablemodel.cpp b/src/sqlitetablemodel.cpp index 845299a6..e0cacb5c 100644 --- a/src/sqlitetablemodel.cpp +++ b/src/sqlitetablemodel.cpp @@ -145,6 +145,10 @@ Qt::ItemFlags SqliteTableModel::flags(const QModelIndex& index) const void SqliteTableModel::sort(int column, Qt::SortOrder order) { + // Don't do anything when the sort order hasn't changed + if(m_iSortColumn == column && m_sSortOrder == (order == Qt::AscendingOrder ? "ASC" : "DESC")) + return; + // Save sort order m_iSortColumn = column; m_sSortOrder = (order == Qt::AscendingOrder ? "ASC" : "DESC"); From e5013da153c169d7c5921277c60fabf85a9caf11 Mon Sep 17 00:00:00 2001 From: Martin Kleusberg Date: Wed, 10 Apr 2013 19:28:48 +0200 Subject: [PATCH 20/27] Update README --- README.rst | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/README.rst b/README.rst index 13f1211f..8accffd4 100644 --- a/README.rst +++ b/README.rst @@ -29,6 +29,9 @@ What's been done since then - Cleaned up the code, reducing the SLOC quite a bit - Added basic support for triggers and views - Added pragma editing +- Added BLOB support +- Added a new filter row for searching +- Improved performance when opening large tables - Fixed a ton of bugs - Probably more @@ -43,8 +46,6 @@ What's still to do - Further improvement of the UI, adding more features and making it easier to use - Inline editing of records instead of having to use a special dialog for it -- Avoid loading all records of a table to avoid problems when opening a very - big table - Feel free to add more issues at https://github.com/rp-/sqlitebrowser/issues From f73e3f0546e8dce2180d3f95994e273ebc1f685b Mon Sep 17 00:00:00 2001 From: Martin Kleusberg Date: Wed, 10 Apr 2013 20:44:00 +0200 Subject: [PATCH 21/27] SqliteTableModel: Fix really stupid bug in insertRows() and removeRows() --- src/sqlitetablemodel.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/sqlitetablemodel.cpp b/src/sqlitetablemodel.cpp index e0cacb5c..faf54fe7 100644 --- a/src/sqlitetablemodel.cpp +++ b/src/sqlitetablemodel.cpp @@ -172,6 +172,8 @@ bool SqliteTableModel::insertRows(int row, int count, const QModelIndex& parent) m_data[row].replace(0, QByteArray::number(m_db->addRecord(m_sTable))); } + m_rowCount += count; + endInsertRows(); return true; } @@ -186,6 +188,8 @@ bool SqliteTableModel::removeRows(int row, int count, const QModelIndex& parent) m_data.removeAt(row + i); } + m_rowCount -= count; + endRemoveRows(); return true; } From c7e6684fc4e94af967926e4a67794993eea799a1 Mon Sep 17 00:00:00 2001 From: Martin Kleusberg Date: Thu, 11 Apr 2013 14:08:59 +0200 Subject: [PATCH 22/27] SqliteTableModel: Allow data() and setData() on rows not cached yet --- src/MainWindow.cpp | 9 +++++++++ src/sqlitetablemodel.cpp | 17 ++++++++++++----- 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/src/MainWindow.cpp b/src/MainWindow.cpp index 10d8c5c7..401be01f 100644 --- a/src/MainWindow.cpp +++ b/src/MainWindow.cpp @@ -407,6 +407,15 @@ void MainWindow::deleteRecord() void MainWindow::selectTableLine(int lineToSelect) { + // Are there even that many lines? + if(lineToSelect >= m_browseTableModel->totalRowCount()) + return; + + // Make sure this line has already been fetched + while(lineToSelect >= m_browseTableModel->rowCount() && m_browseTableModel->canFetchMore()) + m_browseTableModel->fetchMore(); + + // Select it ui->dataTable->clearSelection(); ui->dataTable->selectRow(lineToSelect); ui->dataTable->scrollTo(ui->dataTable->currentIndex()); diff --git a/src/sqlitetablemodel.cpp b/src/sqlitetablemodel.cpp index faf54fe7..f1585e5c 100644 --- a/src/sqlitetablemodel.cpp +++ b/src/sqlitetablemodel.cpp @@ -100,20 +100,26 @@ QVariant SqliteTableModel::data(const QModelIndex &index, int role) const if (role == Qt::DisplayRole) { + // If this row is not in the cache yet get it first + while(index.row() >= m_data.size() && canFetchMore()) + const_cast(this)->fetchMore(); // Nothing evil to see here, move along + return m_data.at(index.row()).at(index.column()); - } - else + } else { return QVariant(); + } } bool SqliteTableModel::setData(const QModelIndex& index, const QVariant& value, int role) { if(index.isValid() && role == Qt::EditRole) { - m_data[index.row()].replace(index.column(), value.toByteArray()); - if(m_db->updateRecord(m_sTable, m_headers.at(index.column()), m_data[index.row()].at(0).toInt(), value.toByteArray())) { + // Only update the cache if this row has already been read, if not there's no need to do any changes to the cache + if(index.row() < m_data.size()) + m_data[index.row()].replace(index.column(), value.toByteArray()); + emit(dataChanged(index, index)); return true; } else { @@ -215,7 +221,8 @@ void SqliteTableModel::fetchData(unsigned int from, unsigned to) } } sqlite3_finalize(stmt); - beginInsertRows(QModelIndex(), currentsize + 1, m_data.size()); + + beginInsertRows(QModelIndex(), currentsize, m_data.size()-1); endInsertRows(); } From 7145872f663bdac85f9084d5087be5b5510acedf Mon Sep 17 00:00:00 2001 From: Martin Kleusberg Date: Thu, 11 Apr 2013 14:23:30 +0200 Subject: [PATCH 23/27] SqliteTableModel: Fix wrong LIMIT value in fetchData() --- src/sqlitetablemodel.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sqlitetablemodel.cpp b/src/sqlitetablemodel.cpp index f1585e5c..79a27204 100644 --- a/src/sqlitetablemodel.cpp +++ b/src/sqlitetablemodel.cpp @@ -204,7 +204,7 @@ void SqliteTableModel::fetchData(unsigned int from, unsigned to) { int currentsize = m_data.size(); - QString sLimitQuery = QString("%1 LIMIT %2, %3;").arg(m_sQuery).arg(from).arg(to); + QString sLimitQuery = QString("%1 LIMIT %2, %3;").arg(m_sQuery).arg(from).arg(to-from); m_db->logSQL(sLimitQuery, kLogMsg_App); QByteArray utf8Query = sLimitQuery.toUtf8(); sqlite3_stmt *stmt; From ae6e452f3624aefdfafed6b042663ddafa088fac Mon Sep 17 00:00:00 2001 From: Martin Kleusberg Date: Thu, 11 Apr 2013 16:00:54 +0200 Subject: [PATCH 24/27] SqliteTableModel: Notify view and parent class code when clearing cache Call beginRemoveRows() and endRemoveRows() when emptying the cache. This silences those warnings from QAbstractItemModel::endInsertRows() and fixes a crash when exiting the program both ocurring when the data was resorted. --- src/sqlitetablemodel.cpp | 9 ++++++++- src/sqlitetablemodel.h | 1 + 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/sqlitetablemodel.cpp b/src/sqlitetablemodel.cpp index 79a27204..390b675c 100644 --- a/src/sqlitetablemodel.cpp +++ b/src/sqlitetablemodel.cpp @@ -58,7 +58,7 @@ void SqliteTableModel::setQuery(const QString& sQuery) sqlite3_finalize(stmt); // now fetch the first entries - m_data.clear(); + clearCache(); fetchData(0, m_chunkSize); emit layoutChanged(); @@ -282,3 +282,10 @@ void SqliteTableModel::updateFilter(int column, const QString& value) // Build the new query buildQuery(); } + +void SqliteTableModel::clearCache() +{ + beginRemoveRows(QModelIndex(), 0, m_data.size()-1); + m_data.clear(); + endRemoveRows(); +} diff --git a/src/sqlitetablemodel.h b/src/sqlitetablemodel.h index 3cecacca..037290c5 100644 --- a/src/sqlitetablemodel.h +++ b/src/sqlitetablemodel.h @@ -41,6 +41,7 @@ public slots: private: void fetchData(unsigned int from, unsigned to); + void clearCache(); void buildQuery(); From 49239dad1f77d967cdda80e01fe2ebb2f18037c4 Mon Sep 17 00:00:00 2001 From: Martin Kleusberg Date: Thu, 11 Apr 2013 17:45:14 +0200 Subject: [PATCH 25/27] ExtendedTableWidget: Make scrolling less awkward with lazy population Improve the lazy population fatchData() calls when using the vertical scrollbar to make scrolling a lot smoother. Still not perfect but definitely a lot better than before. --- src/ExtendedTableWidget.cpp | 35 +++++++++++++++++++++++++++++++++++ src/ExtendedTableWidget.h | 4 ++++ 2 files changed, 39 insertions(+) diff --git a/src/ExtendedTableWidget.cpp b/src/ExtendedTableWidget.cpp index cb0f64f2..29426793 100644 --- a/src/ExtendedTableWidget.cpp +++ b/src/ExtendedTableWidget.cpp @@ -3,10 +3,14 @@ #include #include #include "ExtendedTableWidget.h" +#include "sqlitetablemodel.h" +#include +#include ExtendedTableWidget::ExtendedTableWidget(QWidget* parent) : QTableView(parent) { + connect(verticalScrollBar(), SIGNAL(valueChanged(int)), this, SLOT(vscrollbarChanged(int))); } void ExtendedTableWidget::copy() @@ -53,3 +57,34 @@ void ExtendedTableWidget::keyPressEvent(QKeyEvent* event) else QTableView::keyPressEvent(event); } + +void ExtendedTableWidget::updateGeometries() +{ + // Call the parent implementation first - it does most of the actual logic + QTableView::updateGeometries(); + + // Check if a model has already been set yet + if(model()) + { + // If so and if it is a SqliteTableModel and if the parent implementation of this method decided that a scrollbar is needed, update its maximum value + SqliteTableModel* m = qobject_cast(model()); + if(m && verticalScrollBar()->maximum()) + verticalScrollBar()->setMaximum(m->totalRowCount()); + } +} + +void ExtendedTableWidget::vscrollbarChanged(int value) +{ + // Cancel if there is no model set yet - this shouldn't happen (because without a model there should be no scrollbar) but just to be sure... + if(!model()) + return; + + // How many rows are visible right now? + int row_top = rowAt(0) == -1 ? 0 : rowAt(0); + int row_bottom = rowAt(height()) == -1 ? model()->rowCount() : rowAt(height()); + int num_visible_rows = row_bottom - row_top; + + // Fetch more data from the DB if necessary + if((value + num_visible_rows) >= model()->rowCount() && model()->canFetchMore(QModelIndex())) + model()->fetchMore(QModelIndex()); +} diff --git a/src/ExtendedTableWidget.h b/src/ExtendedTableWidget.h index 7e8adbbf..8cec83d8 100644 --- a/src/ExtendedTableWidget.h +++ b/src/ExtendedTableWidget.h @@ -13,8 +13,12 @@ public: private: void copy(); +private slots: + void vscrollbarChanged(int value); + protected: virtual void keyPressEvent(QKeyEvent* event); + virtual void updateGeometries(); }; #endif From 6f3f21fbd72458a533c788177b9bf12ee86bcd6f Mon Sep 17 00:00:00 2001 From: Peinthor Rene Date: Fri, 12 Apr 2013 06:33:03 +0200 Subject: [PATCH 26/27] increase default chunk size this speeds up going to record 900000 significantly --- src/sqlitetablemodel.cpp | 2 +- src/sqlitetablemodel.h | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/sqlitetablemodel.cpp b/src/sqlitetablemodel.cpp index 390b675c..aca5c64b 100644 --- a/src/sqlitetablemodel.cpp +++ b/src/sqlitetablemodel.cpp @@ -9,7 +9,7 @@ SqliteTableModel::SqliteTableModel(QObject* parent, DBBrowserDB* db) , m_rowCount(0) , m_iSortColumn(0) , m_sSortOrder("ASC") - , m_chunkSize(1000) + , m_chunkSize(50000) { } diff --git a/src/sqlitetablemodel.h b/src/sqlitetablemodel.h index 037290c5..3ccefc99 100644 --- a/src/sqlitetablemodel.h +++ b/src/sqlitetablemodel.h @@ -56,6 +56,14 @@ private: QString m_sSortOrder; QMap m_mWhere; + /** + * @brief m_chunkSize Size of the next chunk fetch more will try to fetch. + * This value should be rather high, because our query + * uses LIMIT and sqlite3 will still execute the whole query and + * just skip the not wanted rows, but the execution will + * still take nearly the same time as doing the query at all up + * to that row count. + */ size_t m_chunkSize; }; From ce4a93ea1fa11085811d4bae325969b408349a6e Mon Sep 17 00:00:00 2001 From: Peinthor Rene Date: Sun, 14 Apr 2013 21:41:48 +0200 Subject: [PATCH 27/27] set override cursor if selecting a line --- src/MainWindow.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/MainWindow.cpp b/src/MainWindow.cpp index 401be01f..e0046e20 100644 --- a/src/MainWindow.cpp +++ b/src/MainWindow.cpp @@ -411,6 +411,7 @@ void MainWindow::selectTableLine(int lineToSelect) if(lineToSelect >= m_browseTableModel->totalRowCount()) return; + QApplication::setOverrideCursor( Qt::WaitCursor ); // Make sure this line has already been fetched while(lineToSelect >= m_browseTableModel->rowCount() && m_browseTableModel->canFetchMore()) m_browseTableModel->fetchMore(); @@ -419,6 +420,7 @@ void MainWindow::selectTableLine(int lineToSelect) ui->dataTable->clearSelection(); ui->dataTable->selectRow(lineToSelect); ui->dataTable->scrollTo(ui->dataTable->currentIndex()); + QApplication::restoreOverrideCursor(); } void MainWindow::navigatePrevious()