mirror of
https://github.com/sqlitebrowser/sqlitebrowser.git
synced 2026-01-20 02:50:46 -06:00
Issue #1463: give assistance to users in using the filters
Added contextual menus in filter line box and cells for assisting in using filters and for discovering existing filters. The menu in the filter line adds "What's This" option and helper options that add the operator and a selected placeholder (?) for easy editing. Special options for NULL and empty strings are also added. Those work directly. The menu in the cells works as the current "Use as Filter" but for different operators. In this case, only the range operator is incomplete. Currently, not all the operators make sense for strings because we take them as LIKE filters. At least the Not Equal filter (<>) should work, but the other would also make sense. This will be addressed in future commits.
This commit is contained in:
@@ -154,13 +154,32 @@ ExtendedTableWidget::ExtendedTableWidget(QWidget* parent) :
|
||||
|
||||
// Set up table view context menu
|
||||
m_contextMenu = new QMenu(this);
|
||||
|
||||
QAction* filterAction = new QAction(tr("Use as Exact Filter"), m_contextMenu);
|
||||
QAction* containingAction = new QAction(tr("Containing"), m_contextMenu);
|
||||
QAction* notEqualToAction = new QAction(tr("Not equal to"), m_contextMenu);
|
||||
QAction* greaterThanAction = new QAction(tr("Greater than"), m_contextMenu);
|
||||
QAction* lessThanAction = new QAction(tr("Less than"), m_contextMenu);
|
||||
QAction* greaterEqualAction = new QAction(tr("Greater or equal"), m_contextMenu);
|
||||
QAction* lessEqualAction = new QAction(tr("Less or equal"), m_contextMenu);
|
||||
QAction* inRangeAction = new QAction(tr("Between this and..."), m_contextMenu);
|
||||
|
||||
QAction* nullAction = new QAction(tr("Set to NULL"), m_contextMenu);
|
||||
QAction* copyAction = new QAction(QIcon(":/icons/copy"), tr("Copy"), m_contextMenu);
|
||||
QAction* copyWithHeadersAction = new QAction(QIcon(":/icons/special_copy"), tr("Copy with Headers"), m_contextMenu);
|
||||
QAction* copyAsSQLAction = new QAction(QIcon(":/icons/sql_copy"), tr("Copy as SQL"), m_contextMenu);
|
||||
QAction* pasteAction = new QAction(QIcon(":/icons/paste"), tr("Paste"), m_contextMenu);
|
||||
QAction* filterAction = new QAction(tr("Use as Filter"), m_contextMenu);
|
||||
|
||||
m_contextMenu->addAction(filterAction);
|
||||
QMenu* filterMenu = m_contextMenu->addMenu(tr("Use in Filter Expression"));
|
||||
filterMenu->addAction(containingAction);
|
||||
filterMenu->addAction(notEqualToAction);
|
||||
filterMenu->addAction(greaterThanAction);
|
||||
filterMenu->addAction(lessThanAction);
|
||||
filterMenu->addAction(greaterEqualAction);
|
||||
filterMenu->addAction(lessEqualAction);
|
||||
filterMenu->addAction(inRangeAction);
|
||||
|
||||
m_contextMenu->addSeparator();
|
||||
m_contextMenu->addAction(nullAction);
|
||||
m_contextMenu->addSeparator();
|
||||
@@ -189,6 +208,7 @@ ExtendedTableWidget::ExtendedTableWidget(QWidget* parent) :
|
||||
// Deactivate context menu options if there is no model set
|
||||
bool enabled = model();
|
||||
filterAction->setEnabled(enabled);
|
||||
filterMenu->setEnabled(enabled);
|
||||
copyAction->setEnabled(enabled);
|
||||
copyWithHeadersAction->setEnabled(enabled);
|
||||
copyAsSQLAction->setEnabled(enabled);
|
||||
@@ -202,8 +222,30 @@ ExtendedTableWidget::ExtendedTableWidget(QWidget* parent) :
|
||||
m_contextMenu->popup(viewport()->mapToGlobal(pos));
|
||||
});
|
||||
connect(filterAction, &QAction::triggered, [&]() {
|
||||
useAsFilter();
|
||||
useAsFilter(QString ("="));
|
||||
});
|
||||
connect(containingAction, &QAction::triggered, [&]() {
|
||||
useAsFilter(QString (""));
|
||||
});
|
||||
connect(notEqualToAction, &QAction::triggered, [&]() {
|
||||
useAsFilter(QString ("<>"));
|
||||
});
|
||||
connect(greaterThanAction, &QAction::triggered, [&]() {
|
||||
useAsFilter(QString (">"));
|
||||
});
|
||||
connect(lessThanAction, &QAction::triggered, [&]() {
|
||||
useAsFilter(QString ("<"));
|
||||
});
|
||||
connect(greaterEqualAction, &QAction::triggered, [&]() {
|
||||
useAsFilter(QString (">="));
|
||||
});
|
||||
connect(lessEqualAction, &QAction::triggered, [&]() {
|
||||
useAsFilter(QString ("<="));
|
||||
});
|
||||
connect(inRangeAction, &QAction::triggered, [&]() {
|
||||
useAsFilter(QString ("~"), /* binary */ true);
|
||||
});
|
||||
|
||||
connect(nullAction, &QAction::triggered, [&]() {
|
||||
for(const QModelIndex& index : selectedIndexes())
|
||||
model()->setData(index, QVariant());
|
||||
@@ -547,7 +589,7 @@ void ExtendedTableWidget::paste()
|
||||
}
|
||||
}
|
||||
|
||||
void ExtendedTableWidget::useAsFilter()
|
||||
void ExtendedTableWidget::useAsFilter(const QString& filterOperator, bool binary)
|
||||
{
|
||||
QModelIndex index = selectionModel()->currentIndex();
|
||||
SqliteTableModel* m = qobject_cast<SqliteTableModel*>(model());
|
||||
@@ -557,13 +599,20 @@ void ExtendedTableWidget::useAsFilter()
|
||||
return;
|
||||
|
||||
QVariant data = model()->data(index, Qt::EditRole);
|
||||
|
||||
QString value;
|
||||
if (data.isNull())
|
||||
m_tableHeader->setFilter(index.column(), "=NULL");
|
||||
value = "NULL";
|
||||
else if (data.toString().isEmpty())
|
||||
m_tableHeader->setFilter(index.column(), "=''");
|
||||
value = "''";
|
||||
else
|
||||
m_tableHeader->setFilter(index.column(), "=" + data.toString());
|
||||
value = data.toString();
|
||||
|
||||
// If binary operator, the cell data is used as first value and
|
||||
// the second value must be added by the user.
|
||||
if (binary)
|
||||
m_tableHeader->setFilter(index.column(), value + filterOperator);
|
||||
else
|
||||
m_tableHeader->setFilter(index.column(), filterOperator + value);
|
||||
}
|
||||
|
||||
void ExtendedTableWidget::keyPressEvent(QKeyEvent* event)
|
||||
|
||||
@@ -50,11 +50,11 @@ signals:
|
||||
void selectedRowsToBeDeleted();
|
||||
|
||||
private:
|
||||
void copy(const bool withHeaders, const bool inSQL );
|
||||
void copy(const bool withHeaders, const bool inSQL);
|
||||
void paste();
|
||||
QString escapeCopiedData(const QByteArray& data) const;
|
||||
|
||||
void useAsFilter();
|
||||
void useAsFilter(const QString& filterOperator, bool binary = false);
|
||||
|
||||
typedef QList<QByteArray> QByteArrayList;
|
||||
static QList<QByteArrayList> m_buffer;
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
|
||||
#include <QTimer>
|
||||
#include <QKeyEvent>
|
||||
#include <QMenu>
|
||||
#include <QWhatsThis>
|
||||
|
||||
FilterLineEdit::FilterLineEdit(QWidget* parent, QList<FilterLineEdit*>* filters, int columnnum) : QLineEdit(parent), filterList(filters), columnNumber(columnnum)
|
||||
{
|
||||
@@ -35,6 +37,10 @@ FilterLineEdit::FilterLineEdit(QWidget* parent, QList<FilterLineEdit*>* filters,
|
||||
// Immediately emit the delayed filter value changed signal if the user presses the enter or the return key or
|
||||
// the line edit widget loses focus
|
||||
connect(this, SIGNAL(editingFinished()), this, SLOT(delayedSignalTimerTriggered()));
|
||||
|
||||
// Prepare for adding the What's This information and filter helper actions to the context menu
|
||||
setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
connect(this, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(showContextMenu(const QPoint &)));
|
||||
}
|
||||
|
||||
void FilterLineEdit::delayedSignalTimerTriggered()
|
||||
@@ -87,3 +93,91 @@ void FilterLineEdit::setText(const QString& text)
|
||||
QLineEdit::setText(text);
|
||||
delayedSignalTimerTriggered();
|
||||
}
|
||||
|
||||
void FilterLineEdit::setFilterHelper(const QString& filterOperator)
|
||||
{
|
||||
setText(filterOperator + "?");
|
||||
// Select the value for easy editing of the expression
|
||||
setSelection(filterOperator.length(), filterOperator.length());
|
||||
}
|
||||
|
||||
void FilterLineEdit::showContextMenu(const QPoint &pos)
|
||||
{
|
||||
|
||||
// This has to be created here, otherwise the set of enabled options would not update accordingly.
|
||||
QMenu* editContextMenu = createStandardContextMenu();
|
||||
editContextMenu->addSeparator();
|
||||
|
||||
QMenu* filterMenu = editContextMenu->addMenu(tr("Set Filter Expression"));
|
||||
|
||||
QAction* whatsThisAction = new QAction(QIcon(":/icons/whatis"), tr("What's This?"), editContextMenu);
|
||||
connect(whatsThisAction, &QAction::triggered, [&]() {
|
||||
QWhatsThis::showText(pos, whatsThis(), this);
|
||||
});
|
||||
|
||||
QAction* isNullAction = new QAction(tr("Is NULL"), editContextMenu);
|
||||
connect(isNullAction, &QAction::triggered, [&]() {
|
||||
setText("=NULL");
|
||||
});
|
||||
|
||||
QAction* isNotNullAction = new QAction(tr("Is not NULL"), editContextMenu);
|
||||
connect(isNotNullAction, &QAction::triggered, [&]() {
|
||||
setText("<>NULL");
|
||||
});
|
||||
|
||||
QAction* isEmptyAction = new QAction(tr("Is empty"), editContextMenu);
|
||||
connect(isEmptyAction, &QAction::triggered, [&]() {
|
||||
setText("=''");
|
||||
});
|
||||
|
||||
QAction* isNotEmptyAction = new QAction(tr("Is not empty"), editContextMenu);
|
||||
connect(isNotEmptyAction, &QAction::triggered, [&]() {
|
||||
setText("<>''");
|
||||
});
|
||||
|
||||
QAction* equalToAction = new QAction(tr("Equal to..."), editContextMenu);
|
||||
connect(equalToAction, &QAction::triggered, [&]() {
|
||||
setFilterHelper(QString ("="));
|
||||
});
|
||||
QAction* notEqualToAction = new QAction(tr("Not equal to..."), editContextMenu);
|
||||
connect(notEqualToAction, &QAction::triggered, [&]() {
|
||||
setFilterHelper(QString ("<>"));
|
||||
});
|
||||
QAction* greaterThanAction = new QAction(tr("Greater than..."), editContextMenu);
|
||||
connect(greaterThanAction, &QAction::triggered, [&]() {
|
||||
setFilterHelper(QString (">"));
|
||||
});
|
||||
QAction* lessThanAction = new QAction(tr("Less than..."), editContextMenu);
|
||||
connect(lessThanAction, &QAction::triggered, [&]() {
|
||||
setFilterHelper(QString ("<"));
|
||||
});
|
||||
QAction* greaterEqualAction = new QAction(tr("Greater or equal..."), editContextMenu);
|
||||
connect(greaterEqualAction, &QAction::triggered, [&]() {
|
||||
setFilterHelper(QString (">="));
|
||||
});
|
||||
QAction* lessEqualAction = new QAction(tr("Less or equal..."), editContextMenu);
|
||||
connect(lessEqualAction, &QAction::triggered, [&]() {
|
||||
setFilterHelper(QString ("<="));
|
||||
});
|
||||
QAction* inRangeAction = new QAction(tr("In range..."), editContextMenu);
|
||||
connect(inRangeAction, &QAction::triggered, [&]() {
|
||||
setFilterHelper(QString ("?~"));
|
||||
});
|
||||
|
||||
filterMenu->addAction(whatsThisAction);
|
||||
filterMenu->addSeparator();
|
||||
filterMenu->addAction(isNullAction);
|
||||
filterMenu->addAction(isNotNullAction);
|
||||
filterMenu->addAction(isEmptyAction);
|
||||
filterMenu->addAction(isNotEmptyAction);
|
||||
filterMenu->addSeparator();
|
||||
filterMenu->addAction(equalToAction);
|
||||
filterMenu->addAction(notEqualToAction);
|
||||
filterMenu->addAction(greaterThanAction);
|
||||
filterMenu->addAction(lessThanAction);
|
||||
filterMenu->addAction(greaterEqualAction);
|
||||
filterMenu->addAction(lessEqualAction);
|
||||
filterMenu->addAction(inRangeAction);
|
||||
|
||||
editContextMenu->exec(mapToGlobal(pos));
|
||||
}
|
||||
|
||||
@@ -26,12 +26,16 @@ signals:
|
||||
|
||||
protected:
|
||||
void keyReleaseEvent(QKeyEvent* event);
|
||||
void setFilterHelper(const QString& filterOperator);
|
||||
|
||||
private:
|
||||
QList<FilterLineEdit*>* filterList;
|
||||
int columnNumber;
|
||||
QTimer* delaySignalTimer;
|
||||
QString lastValue;
|
||||
|
||||
private slots:
|
||||
void showContextMenu(const QPoint &pos);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user