mirror of
https://github.com/sqlitebrowser/sqlitebrowser.git
synced 2026-01-20 02:50:46 -06:00
Merge branch 'partial-data-fetch'
This commit is contained in:
@@ -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
|
||||
|
||||
|
||||
@@ -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;i<fields.count();i++)
|
||||
for(int i=1;i<tableModel.columnCount();i++)
|
||||
{
|
||||
stream << quoteChar << fields.at(i) << quoteChar;
|
||||
if(i < fields.count() - 1)
|
||||
stream << quoteChar << tableModel.headerData(i, Qt::Horizontal).toString() << quoteChar;
|
||||
if(i < tableModel.columnCount() - 1)
|
||||
stream << sepChar;
|
||||
else
|
||||
stream << newlineChar;
|
||||
@@ -70,15 +73,13 @@ void ExportCsvDialog::accept()
|
||||
}
|
||||
|
||||
// Get and write actual data
|
||||
rowList data = pdb->browseRecs;
|
||||
for(int i=0;i<data.size();i++)
|
||||
for(int i=0;i<tableModel.totalRowCount();i++)
|
||||
{
|
||||
QList<QByteArray> row = data[i];
|
||||
for(int j=1;j<row.size();j++)
|
||||
for(int j=1;j<tableModel.columnCount();j++)
|
||||
{
|
||||
QString content = row[j];
|
||||
QString content = tableModel.data(tableModel.index(i, j)).toString();
|
||||
stream << quoteChar << content.replace(quoteChar, quotequoteChar) << quoteChar;
|
||||
if(j < row.count() - 1)
|
||||
if(j < tableModel.columnCount() - 1)
|
||||
stream << sepChar;
|
||||
else
|
||||
stream << newlineChar;
|
||||
|
||||
@@ -3,10 +3,14 @@
|
||||
#include <QKeySequence>
|
||||
#include <QKeyEvent>
|
||||
#include "ExtendedTableWidget.h"
|
||||
#include "sqlitetablemodel.h"
|
||||
#include <QScrollBar>
|
||||
#include <QHeaderView>
|
||||
|
||||
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<SqliteTableModel*>(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());
|
||||
}
|
||||
|
||||
@@ -13,8 +13,12 @@ public:
|
||||
private:
|
||||
void copy();
|
||||
|
||||
private slots:
|
||||
void vscrollbarChanged(int value);
|
||||
|
||||
protected:
|
||||
virtual void keyPressEvent(QKeyEvent* event);
|
||||
virtual void updateGeometries();
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
78
src/FilterTableHeader.cpp
Normal file
78
src/FilterTableHeader.cpp
Normal file
@@ -0,0 +1,78 @@
|
||||
#include "FilterTableHeader.h"
|
||||
#include <QLineEdit>
|
||||
#include <QTableView>
|
||||
#include <QScrollBar>
|
||||
|
||||
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;i<filterWidgets.size();i++)
|
||||
delete filterWidgets.at(i);
|
||||
filterWidgets.clear();
|
||||
|
||||
// And generate a bunch of new ones
|
||||
for(int i=0;i<number;i++)
|
||||
{
|
||||
QLineEdit* l = new QLineEdit(this);
|
||||
l->setPlaceholderText(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;i<filterWidgets.size();++i)
|
||||
{
|
||||
// Get the current widget, move it and reisize it
|
||||
QWidget* w = filterWidgets.at(i);
|
||||
w->move(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);
|
||||
}
|
||||
35
src/FilterTableHeader.h
Normal file
35
src/FilterTableHeader.h
Normal file
@@ -0,0 +1,35 @@
|
||||
#ifndef __FILTERTABLEHEADER_H__
|
||||
#define __FILTERTABLEHEADER_H__
|
||||
|
||||
#include <QHeaderView>
|
||||
#include <QList>
|
||||
|
||||
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<QLineEdit*> filterWidgets;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -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() + 1)));
|
||||
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 - 1);
|
||||
}
|
||||
}
|
||||
|
||||
void FindDialog::closeEvent(QCloseEvent*)
|
||||
{
|
||||
emit goingAway();
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
#ifndef __FINDDIALOG_H__
|
||||
#define __FINDDIALOG_H__
|
||||
|
||||
#include <QDialog>
|
||||
#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
|
||||
@@ -1,193 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>FindDialog</class>
|
||||
<widget class="QDialog" name="FindDialog">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>288</width>
|
||||
<height>351</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Find</string>
|
||||
</property>
|
||||
<property name="sizeGripEnabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="QComboBox" name="comboColumn">
|
||||
<property name="toolTip">
|
||||
<string>Field to be searched</string>
|
||||
</property>
|
||||
<property name="whatsThis">
|
||||
<string>Use this control to select the field to be searched in the current table</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="comboOperator">
|
||||
<property name="toolTip">
|
||||
<string>Search criteria: use 'contains' for partial matches</string>
|
||||
</property>
|
||||
<property name="whatsThis">
|
||||
<string>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.</string>
|
||||
</property>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>=</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>contains</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>></string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>>=</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string><=</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string><</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string><></string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||
<item>
|
||||
<widget class="QLineEdit" name="editSearchString">
|
||||
<property name="toolTip">
|
||||
<string>Enter values or words to search</string>
|
||||
</property>
|
||||
<property name="whatsThis">
|
||||
<string>This is a place to enter the word or number to be searched in the database</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="buttonSearch">
|
||||
<property name="toolTip">
|
||||
<string>Perform the search</string>
|
||||
</property>
|
||||
<property name="whatsThis">
|
||||
<string>This button starts the search process</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>&Search</string>
|
||||
</property>
|
||||
<property name="default">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QTableWidget" name="tableResults">
|
||||
<property name="whatsThis">
|
||||
<string>Results of the search will appear in this area. Click on a result to select the corresponding record in the database</string>
|
||||
</property>
|
||||
<property name="editTriggers">
|
||||
<set>QAbstractItemView::NoEditTriggers</set>
|
||||
</property>
|
||||
<property name="showDropIndicator" stdset="0">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="alternatingRowColors">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="selectionMode">
|
||||
<enum>QAbstractItemView::NoSelection</enum>
|
||||
</property>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Records</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Data</string>
|
||||
</property>
|
||||
</column>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="labelNumberResults">
|
||||
<property name="text">
|
||||
<string>Found: 0</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<tabstops>
|
||||
<tabstop>comboColumn</tabstop>
|
||||
<tabstop>comboOperator</tabstop>
|
||||
<tabstop>editSearchString</tabstop>
|
||||
<tabstop>buttonSearch</tabstop>
|
||||
<tabstop>tableResults</tabstop>
|
||||
</tabstops>
|
||||
<resources/>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>buttonSearch</sender>
|
||||
<signal>clicked()</signal>
|
||||
<receiver>FindDialog</receiver>
|
||||
<slot>find()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>233</x>
|
||||
<y>45</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>267</x>
|
||||
<y>30</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>tableResults</sender>
|
||||
<signal>itemClicked(QTableWidgetItem*)</signal>
|
||||
<receiver>FindDialog</receiver>
|
||||
<slot>recordSelected(QTableWidgetItem*)</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>100</x>
|
||||
<y>176</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>86</x>
|
||||
<y>58</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
<slots>
|
||||
<slot>find()</slot>
|
||||
<slot>recordSelected(QTableWidgetItem*)</slot>
|
||||
</slots>
|
||||
</ui>
|
||||
@@ -10,6 +10,7 @@
|
||||
#include <QStandardItemModel>
|
||||
#include <QDragEnterEvent>
|
||||
#include <QScrollBar>
|
||||
#include <QSortFilterProxyModel>
|
||||
|
||||
#include "CreateIndexDialog.h"
|
||||
#include "AboutDialog.h"
|
||||
@@ -18,19 +19,20 @@
|
||||
#include "ExportCsvDialog.h"
|
||||
#include "PreferencesDialog.h"
|
||||
#include "EditDialog.h"
|
||||
#include "FindDialog.h"
|
||||
#include "SQLiteSyntaxHighlighter.h"
|
||||
#include "sqltextedit.h"
|
||||
#include "sqlitetablemodel.h"
|
||||
#include "FilterTableHeader.h"
|
||||
|
||||
MainWindow::MainWindow(QWidget* parent)
|
||||
: QMainWindow(parent),
|
||||
ui(new Ui::MainWindow),
|
||||
browseTableModel(new QStandardItemModel(this)),
|
||||
m_browseTableModel(new SqliteTableModel(this, &db)),
|
||||
sqliteHighlighterTabSql(0),
|
||||
sqliteHighlighterLogUser(0),
|
||||
sqliteHighlighterLogApp(0),
|
||||
editWin(new EditDialog(this)),
|
||||
findWin(0)
|
||||
gotoValidator(new QIntValidator(0, 0, this))
|
||||
{
|
||||
ui->setupUi(this);
|
||||
init();
|
||||
@@ -55,19 +57,22 @@ 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
|
||||
ui->dataTable->setModel(browseTableModel);
|
||||
ui->dataTable->setModel(m_browseTableModel);
|
||||
|
||||
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);
|
||||
@@ -96,10 +101,10 @@ 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)));
|
||||
connect(editWin, SIGNAL(updateRecordText(int, int, QByteArray)), this, SLOT(updateRecordText(int, int, QByteArray)));
|
||||
|
||||
// Load window settings
|
||||
restoreGeometry(PreferencesDialog::getSettingsValue("MainWindow", "geometry").toByteArray());
|
||||
@@ -268,45 +273,48 @@ void MainWindow::populateStructure()
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::populateTable( const QString & tablename, bool keepColumnWidths)
|
||||
void MainWindow::populateTable( const QString & tablename)
|
||||
{
|
||||
bool mustreset = false;
|
||||
QApplication::setOverrideCursor( Qt::WaitCursor );
|
||||
if (tablename.compare(db.curBrowseTableName)!=0)
|
||||
// Remove the model-view link if the table name is empty in order to remove any data from the view
|
||||
if(tablename.isEmpty())
|
||||
{
|
||||
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(""));
|
||||
ui->dataTable->setModel(0);
|
||||
return;
|
||||
}
|
||||
|
||||
QApplication::setOverrideCursor(Qt::WaitCursor);
|
||||
|
||||
// Set model
|
||||
ui->dataTable->setModel(m_browseTableModel);
|
||||
|
||||
// Set new table
|
||||
m_browseTableModel->setTable(tablename);
|
||||
ui->dataTable->setColumnHidden(0, true);
|
||||
|
||||
// Reset sorting
|
||||
curBrowseOrderByIndex = 0;
|
||||
curBrowseOrderByMode = Qt::AscendingOrder;
|
||||
m_browseTableModel->sort(curBrowseOrderByIndex, curBrowseOrderByMode);
|
||||
|
||||
// Get table layout
|
||||
db.browseTable(tablename);
|
||||
|
||||
// Update the filter row
|
||||
qobject_cast<FilterTableHeader*>(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);
|
||||
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();
|
||||
// Set the recordset label
|
||||
setRecordsetLabel();
|
||||
|
||||
// Reset the edit dialog
|
||||
if(editWin)
|
||||
editWin->reset();
|
||||
|
||||
QApplication::restoreOverrideCursor();
|
||||
}
|
||||
|
||||
@@ -325,8 +333,9 @@ void MainWindow::resetBrowser()
|
||||
int pos = ui->comboBrowseTable->findText(sCurrentTable);
|
||||
pos = pos == -1 ? 0 : pos;
|
||||
ui->comboBrowseTable->setCurrentIndex(pos);
|
||||
curBrowseOrderByIndex = 1;
|
||||
curBrowseOrderByMode = ORDERMODE_ASC;
|
||||
curBrowseOrderByIndex = 0;
|
||||
curBrowseOrderByMode = Qt::AscendingOrder;
|
||||
m_browseTableModel->sort(curBrowseOrderByIndex, curBrowseOrderByMode);
|
||||
populateTable(ui->comboBrowseTable->currentText());
|
||||
}
|
||||
|
||||
@@ -368,234 +377,107 @@ 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<QByteArray> 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;i<browseTableModel->columnCount();++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)
|
||||
{
|
||||
// Are there even that many lines?
|
||||
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();
|
||||
|
||||
// Select it
|
||||
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->scrollTo(ui->dataTable->currentIndex());
|
||||
QApplication::restoreOverrideCursor();
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
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(updateTableView(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(), true);
|
||||
}
|
||||
|
||||
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 = "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(" ");
|
||||
//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();
|
||||
populateTable(ui->comboBrowseTable->currentText());
|
||||
}
|
||||
|
||||
void MainWindow::createTable()
|
||||
@@ -698,18 +580,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<QByteArray>& 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_browseTableModel->index(row, col), newtext);
|
||||
}
|
||||
|
||||
void MainWindow::editWinAway()
|
||||
@@ -719,13 +590,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<QByteArray> 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();
|
||||
}
|
||||
|
||||
@@ -741,7 +608,7 @@ void MainWindow::doubleClickTable(const QModelIndex& index)
|
||||
if(db.getObjectByName(ui->comboBrowseTable->currentText()).gettype() != "table")
|
||||
return;
|
||||
|
||||
editText(index.row(), index.column());
|
||||
editText(index);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1138,9 +1005,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_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
|
||||
|
||||
@@ -4,17 +4,14 @@
|
||||
#include <QMainWindow>
|
||||
#include "sqlitedb.h"
|
||||
|
||||
#define ORDERMODE_ASC 0
|
||||
#define ORDERMODE_DESC 1
|
||||
|
||||
class QDragEnterEvent;
|
||||
class EditDialog;
|
||||
class FindDialog;
|
||||
class SQLiteSyntaxHighlighter;
|
||||
class QStandardItemModel;
|
||||
class QIntValidator;
|
||||
class QLabel;
|
||||
class QModelIndex;
|
||||
class SqliteTableModel;
|
||||
|
||||
namespace Ui {
|
||||
class MainWindow;
|
||||
@@ -52,7 +49,7 @@ private:
|
||||
|
||||
Ui::MainWindow* ui;
|
||||
|
||||
QStandardItemModel *browseTableModel;
|
||||
SqliteTableModel* m_browseTableModel;
|
||||
QStandardItemModel *queryResultListModel;
|
||||
QMenu *popupTableMenu;
|
||||
QMenu *recentFilesMenu;
|
||||
@@ -68,10 +65,9 @@ private:
|
||||
QAction *recentSeparatorAct;
|
||||
|
||||
int curBrowseOrderByIndex;
|
||||
int curBrowseOrderByMode;
|
||||
Qt::SortOrder curBrowseOrderByMode;
|
||||
|
||||
EditDialog* editWin;
|
||||
FindDialog* findWin;
|
||||
QIntValidator* gotoValidator;
|
||||
|
||||
DBBrowserDB db;
|
||||
@@ -99,22 +95,18 @@ 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();
|
||||
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();
|
||||
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();
|
||||
@@ -124,7 +116,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();
|
||||
|
||||
@@ -116,29 +116,6 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QToolButton" name="buttonFind">
|
||||
<property name="toolTip">
|
||||
<string>Open or close the floating find window</string>
|
||||
</property>
|
||||
<property name="whatsThis">
|
||||
<string>This button toggles the appearance of the Find window, used to search records in the database view</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="icons/icons.qrc">
|
||||
<normaloff>:/icons/searchfind</normaloff>:/icons/searchfind</iconset>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>Ctrl+F</string>
|
||||
</property>
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QToolButton" name="buttonRefresh">
|
||||
<property name="toolTip">
|
||||
@@ -205,6 +182,9 @@
|
||||
<property name="whatsThis">
|
||||
<string>This is the database view. You can double-click any record to edit its contents in the cell editor window.</string>
|
||||
</property>
|
||||
<property name="editTriggers">
|
||||
<set>QAbstractItemView::NoEditTriggers</set>
|
||||
</property>
|
||||
<property name="selectionMode">
|
||||
<enum>QAbstractItemView::ExtendedSelection</enum>
|
||||
</property>
|
||||
@@ -1364,7 +1344,6 @@
|
||||
<tabstops>
|
||||
<tabstop>dbTreeWidget</tabstop>
|
||||
<tabstop>comboBrowseTable</tabstop>
|
||||
<tabstop>buttonFind</tabstop>
|
||||
<tabstop>buttonRefresh</tabstop>
|
||||
<tabstop>buttonNewRecord</tabstop>
|
||||
<tabstop>buttonDeleteRecord</tabstop>
|
||||
@@ -1578,22 +1557,6 @@
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>buttonFind</sender>
|
||||
<signal>toggled(bool)</signal>
|
||||
<receiver>MainWindow</receiver>
|
||||
<slot>browseFind(bool)</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>141</x>
|
||||
<y>81</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>399</x>
|
||||
<y>299</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>buttonRefresh</sender>
|
||||
<signal>clicked()</signal>
|
||||
|
||||
@@ -22,7 +22,6 @@
|
||||
<file alias="trigger">script.png</file>
|
||||
<file alias="trigger_create">script_add.png</file>
|
||||
<file alias="trigger_delete">script_delete.png</file>
|
||||
<file alias="searchfind">magnifier.png</file>
|
||||
<file alias="settings">wrench.png</file>
|
||||
<file alias="whatis">help.png</file>
|
||||
</qresource>
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 615 B |
138
src/sqlitedb.cpp
138
src/sqlitedb.cpp
@@ -1,5 +1,5 @@
|
||||
#include "sqlitedb.h"
|
||||
|
||||
#include "sqlitetablemodel.h"
|
||||
#include "MainWindow.h"
|
||||
|
||||
#include <QFile>
|
||||
@@ -234,8 +234,11 @@ bool DBBrowserDB::dump(const QString& filename)
|
||||
unsigned int numRecordsTotal = 0, numRecordsCurrent = 0;
|
||||
QList<DBBrowserObject> tables = objMap.values("table");
|
||||
for(QList<DBBrowserObject>::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<data.size();row++)
|
||||
for(int row=0;row<tableModel.totalRowCount();row++)
|
||||
{
|
||||
stream << "INSERT INTO `" << (*it).getname() << "` VALUES(";
|
||||
for(int col=1;col<data[row].size();col++)
|
||||
for(int col=1;col<tableModel.columnCount();col++)
|
||||
{
|
||||
QString content = data[row][col];
|
||||
QString content = tableModel.data(tableModel.index(row, col)).toString();
|
||||
content.replace("'", "''");
|
||||
if(content.isNull())
|
||||
content = "NULL";
|
||||
@@ -275,7 +280,7 @@ bool DBBrowserDB::dump(const QString& filename)
|
||||
content = "''";
|
||||
|
||||
stream << content;
|
||||
if(col < data[row].count() - 1)
|
||||
if(col < tableModel.columnCount() - 1)
|
||||
stream << ",";
|
||||
else
|
||||
stream << ");\n";
|
||||
@@ -385,7 +390,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 +409,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<QByteArray> 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);
|
||||
@@ -436,19 +439,13 @@ bool DBBrowserDB::deleteRecord( int wrow)
|
||||
return ok;
|
||||
}
|
||||
|
||||
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 (!hasValidBrowseSet) return false;
|
||||
if (!isOpen()) return false;
|
||||
|
||||
|
||||
lastErrorMessage = QString("no error");
|
||||
|
||||
QList<QByteArray>& 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);
|
||||
|
||||
QString sql = QString("UPDATE `%1` SET `%2`=? WHERE rowid=%3;").arg(table).arg(column).arg(row);
|
||||
|
||||
logSQL(sql, kLogMsg_App);
|
||||
setRestorePoint();
|
||||
@@ -457,7 +454,7 @@ bool DBBrowserDB::updateRecord(int wrow, int wcol, const QByteArray& wtext)
|
||||
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)
|
||||
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;
|
||||
@@ -466,7 +463,6 @@ bool DBBrowserDB::updateRecord(int wrow, int wcol, const QByteArray& wtext)
|
||||
|
||||
if(success == 1)
|
||||
{
|
||||
cv = wtext;
|
||||
return true;
|
||||
} else {
|
||||
lastErrorMessage = sqlite3_errmsg(_db);
|
||||
@@ -475,12 +471,11 @@ bool DBBrowserDB::updateRecord(int wrow, int wcol, const QByteArray& wtext)
|
||||
}
|
||||
}
|
||||
|
||||
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 );
|
||||
browseFields = testFields;
|
||||
hasValidBrowseSet = true;
|
||||
curBrowseTableName = tablename;
|
||||
@@ -711,87 +706,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<QByteArray> 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<ncol;++e)
|
||||
{
|
||||
QByteArray rv = QByteArray(static_cast<const char*>(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;
|
||||
const char *tail;
|
||||
|
||||
int ncol;
|
||||
|
||||
// char *errmsg;
|
||||
int err=0;
|
||||
resultMap res;
|
||||
lastErrorMessage = QObject::tr("no error");
|
||||
logSQL(wstatement, kLogMsg_App);
|
||||
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<ncol; ++e){
|
||||
r = QString::fromUtf8((const char *) sqlite3_column_text(vm, e));
|
||||
if (e==0){
|
||||
rownum = r.toInt();
|
||||
rowIdMap::iterator mit = idmap.find(rownum);
|
||||
recnum = *mit;
|
||||
}
|
||||
}
|
||||
res.insert(recnum, r);
|
||||
}
|
||||
|
||||
sqlite3_finalize(vm);
|
||||
}else{
|
||||
lastErrorMessage = QString::fromUtf8((const char*)sqlite3_errmsg(_db));
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
QStringList DBBrowserDB::getBrowsableObjectNames() const
|
||||
{
|
||||
objectMap::ConstIterator it;
|
||||
|
||||
@@ -99,9 +99,9 @@ public:
|
||||
*/
|
||||
QString getTableSQL(const QString& sTable);
|
||||
void updateSchema() ;
|
||||
bool addRecord(const QString& sTableName);
|
||||
bool deleteRecord(int wrow);
|
||||
bool updateRecord(int wrow, int wcol, const QByteArray& wtext);
|
||||
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 browseTable( const QString & tablename, const QString& orderby = "rowid" );
|
||||
|
||||
bool createTable(const QString& name, const QList<DBBrowserField>& structure);
|
||||
@@ -116,7 +116,6 @@ public:
|
||||
objectMap getBrowsableObjects() const;
|
||||
DBBrowserObject getObjectByName(const QString& name) const;
|
||||
QStringList getIndexNames() const;
|
||||
resultMap getFindResults( const QString & wstatement);
|
||||
int getRecordCount() const;
|
||||
bool isOpen() const;
|
||||
void setDirty(bool dirtyval);
|
||||
@@ -144,12 +143,8 @@ public:
|
||||
|
||||
MainWindow* mainWindow;
|
||||
|
||||
|
||||
private:
|
||||
bool dirty;
|
||||
void getTableRecords( const QString & tablename, const QString& orderby = "rowid" );
|
||||
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
291
src/sqlitetablemodel.cpp
Normal file
291
src/sqlitetablemodel.cpp
Normal file
@@ -0,0 +1,291 @@
|
||||
#include "sqlitetablemodel.h"
|
||||
#include "sqlitedb.h"
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
SqliteTableModel::SqliteTableModel(QObject* parent, DBBrowserDB* db)
|
||||
: QAbstractTableModel(parent)
|
||||
, m_db(db)
|
||||
, m_rowCount(0)
|
||||
, m_iSortColumn(0)
|
||||
, m_sSortOrder("ASC")
|
||||
, m_chunkSize(50000)
|
||||
{
|
||||
}
|
||||
|
||||
void SqliteTableModel::setChunkSize(size_t chunksize)
|
||||
{
|
||||
m_chunkSize = 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));
|
||||
|
||||
m_mWhere.clear();
|
||||
|
||||
buildQuery();
|
||||
}
|
||||
|
||||
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);
|
||||
m_db->logSQL(sCountQuery, kLogMsg_App);
|
||||
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)
|
||||
{
|
||||
QString sCount = QString::fromUtf8((const char *) sqlite3_column_text(stmt, 0));
|
||||
m_rowCount = sCount.toInt();
|
||||
}
|
||||
}
|
||||
sqlite3_finalize(stmt);
|
||||
|
||||
// now fetch the first entries
|
||||
clearCache();
|
||||
fetchData(0, m_chunkSize);
|
||||
|
||||
emit layoutChanged();
|
||||
}
|
||||
|
||||
int SqliteTableModel::rowCount(const QModelIndex&) const
|
||||
{
|
||||
return m_data.size(); // current fetched row count
|
||||
}
|
||||
|
||||
int SqliteTableModel::totalRowCount() const
|
||||
{
|
||||
return m_rowCount;
|
||||
}
|
||||
|
||||
int SqliteTableModel::columnCount(const QModelIndex&) const
|
||||
{
|
||||
return m_headers.size();
|
||||
}
|
||||
|
||||
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("%1").arg(section + 1);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
// If this row is not in the cache yet get it first
|
||||
while(index.row() >= m_data.size() && canFetchMore())
|
||||
const_cast<SqliteTableModel*>(this)->fetchMore(); // Nothing evil to see here, move along
|
||||
|
||||
return m_data.at(index.row()).at(index.column());
|
||||
} else {
|
||||
return QVariant();
|
||||
}
|
||||
}
|
||||
|
||||
bool SqliteTableModel::setData(const QModelIndex& index, const QVariant& value, int role)
|
||||
{
|
||||
if(index.isValid() && role == Qt::EditRole)
|
||||
{
|
||||
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 {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SqliteTableModel::canFetchMore(const QModelIndex&) const
|
||||
{
|
||||
return m_data.size() < m_rowCount;
|
||||
}
|
||||
|
||||
void SqliteTableModel::fetchMore(const QModelIndex&)
|
||||
{
|
||||
int row = m_data.size();
|
||||
fetchData(row, row + m_chunkSize);
|
||||
}
|
||||
|
||||
Qt::ItemFlags SqliteTableModel::flags(const QModelIndex& index) const
|
||||
{
|
||||
if(!index.isValid())
|
||||
return Qt::ItemIsEnabled;
|
||||
|
||||
return QAbstractTableModel::flags(index)/* | Qt::ItemIsEditable*/;
|
||||
}
|
||||
|
||||
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");
|
||||
|
||||
// Set the new query (but only if a table has already been set
|
||||
if(m_sTable != "")
|
||||
buildQuery();
|
||||
}
|
||||
|
||||
bool SqliteTableModel::insertRows(int row, int count, const QModelIndex& parent)
|
||||
{
|
||||
beginInsertRows(parent, row, row + count - 1);
|
||||
|
||||
QByteArrayList blank_data;
|
||||
for(int i=0;i<m_headers.size();i++)
|
||||
blank_data.push_back("");
|
||||
|
||||
for(int i=0;i<count;i++)
|
||||
{
|
||||
m_data.insert(row, blank_data);
|
||||
m_data[row].replace(0, QByteArray::number(m_db->addRecord(m_sTable)));
|
||||
}
|
||||
|
||||
m_rowCount += count;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
m_rowCount -= count;
|
||||
|
||||
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-from);
|
||||
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)
|
||||
{
|
||||
QByteArrayList rowdata;
|
||||
for (int i = 0; i < m_headers.size(); ++i)
|
||||
rowdata.append(QByteArray(static_cast<const char*>(sqlite3_column_blob(stmt, i)), sqlite3_column_bytes(stmt, i)));
|
||||
m_data.push_back(rowdata);
|
||||
}
|
||||
}
|
||||
sqlite3_finalize(stmt);
|
||||
|
||||
beginInsertRows(QModelIndex(), currentsize, m_data.size()-1);
|
||||
endInsertRows();
|
||||
}
|
||||
|
||||
void SqliteTableModel::buildQuery()
|
||||
{
|
||||
QString where;
|
||||
if(m_mWhere.size())
|
||||
{
|
||||
where = "WHERE 1=1";
|
||||
for(QMap<int, QString>::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, 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("%1 %2").arg(op).arg(val));
|
||||
|
||||
// Build the new query
|
||||
buildQuery();
|
||||
}
|
||||
|
||||
void SqliteTableModel::clearCache()
|
||||
{
|
||||
beginRemoveRows(QModelIndex(), 0, m_data.size()-1);
|
||||
m_data.clear();
|
||||
endRemoveRows();
|
||||
}
|
||||
70
src/sqlitetablemodel.h
Normal file
70
src/sqlitetablemodel.h
Normal file
@@ -0,0 +1,70 @@
|
||||
#ifndef SQLITETABLEMODEL_H
|
||||
#define SQLITETABLEMODEL_H
|
||||
|
||||
#include <QAbstractTableModel>
|
||||
#include <QStringList>
|
||||
|
||||
class DBBrowserDB;
|
||||
|
||||
class SqliteTableModel : public QAbstractTableModel
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit SqliteTableModel(QObject *parent = 0, DBBrowserDB* db = 0);
|
||||
|
||||
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 = 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;
|
||||
void fetchMore(const QModelIndex &parent = QModelIndex());
|
||||
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);
|
||||
void sort(int column, Qt::SortOrder order = Qt::AscendingOrder);
|
||||
|
||||
Qt::ItemFlags flags(const QModelIndex& index) const;
|
||||
|
||||
typedef QList<QByteArray> QByteArrayList;
|
||||
|
||||
signals:
|
||||
|
||||
public slots:
|
||||
void updateFilter(int column, const QString& value);
|
||||
|
||||
private:
|
||||
void fetchData(unsigned int from, unsigned to);
|
||||
void clearCache();
|
||||
|
||||
void buildQuery();
|
||||
|
||||
DBBrowserDB* m_db;
|
||||
int m_rowCount;
|
||||
QStringList m_headers;
|
||||
QList<QByteArrayList> m_data;
|
||||
|
||||
QString m_sQuery;
|
||||
QString m_sTable;
|
||||
int m_iSortColumn;
|
||||
QString m_sSortOrder;
|
||||
QMap<int, QString> 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;
|
||||
};
|
||||
|
||||
#endif // SQLITETABLEMODEL_H
|
||||
11
src/src.pro
11
src/src.pro
@@ -16,7 +16,6 @@ HEADERS += \
|
||||
AboutDialog.h \
|
||||
EditTableDialog.h \
|
||||
PreferencesDialog.h \
|
||||
FindDialog.h \
|
||||
EditDialog.h \
|
||||
ExportCsvDialog.h \
|
||||
ImportCsvDialog.h \
|
||||
@@ -25,7 +24,9 @@ HEADERS += \
|
||||
ExtendedTableWidget.h \
|
||||
grammar/Sqlite3Lexer.hpp \
|
||||
grammar/Sqlite3Parser.hpp \
|
||||
grammar/sqlite3TokenTypes.hpp
|
||||
grammar/sqlite3TokenTypes.hpp \
|
||||
sqlitetablemodel.h \
|
||||
FilterTableHeader.h
|
||||
|
||||
SOURCES += \
|
||||
sqlitedb.cpp \
|
||||
@@ -35,7 +36,6 @@ SOURCES += \
|
||||
EditTableDialog.cpp \
|
||||
PreferencesDialog.cpp \
|
||||
AboutDialog.cpp \
|
||||
FindDialog.cpp \
|
||||
EditDialog.cpp \
|
||||
ExportCsvDialog.cpp \
|
||||
ImportCsvDialog.cpp \
|
||||
@@ -43,7 +43,9 @@ SOURCES += \
|
||||
sqlitetypes.cpp \
|
||||
ExtendedTableWidget.cpp \
|
||||
grammar/Sqlite3Lexer.cpp \
|
||||
grammar/Sqlite3Parser.cpp
|
||||
grammar/Sqlite3Parser.cpp \
|
||||
sqlitetablemodel.cpp \
|
||||
FilterTableHeader.cpp
|
||||
|
||||
# create a unittest option
|
||||
CONFIG(unittest) {
|
||||
@@ -85,7 +87,6 @@ FORMS += \
|
||||
AboutDialog.ui \
|
||||
EditTableDialog.ui \
|
||||
PreferencesDialog.ui \
|
||||
FindDialog.ui \
|
||||
EditDialog.ui \
|
||||
ExportCsvDialog.ui \
|
||||
ImportCsvDialog.ui
|
||||
|
||||
Reference in New Issue
Block a user