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) {