Initial commit of BudgetPro
This commit is contained in:
@@ -0,0 +1,243 @@
|
||||
#include "filterlineedit.h"
|
||||
//#include "Settings.h"
|
||||
|
||||
#include <QTimer>
|
||||
#include <QKeyEvent>
|
||||
#include <QMenu>
|
||||
#include <QWhatsThis>
|
||||
#include <QDesktopServices>
|
||||
#include <QUrl>
|
||||
|
||||
FilterLineEdit::FilterLineEdit(QWidget* parent, std::vector<FilterLineEdit*>* filters, size_t columnnum) :
|
||||
QLineEdit(parent),
|
||||
filterList(filters),
|
||||
columnNumber(columnnum),
|
||||
conditional_format(true)
|
||||
{
|
||||
setPlaceholderText(tr("Filter"));
|
||||
setClearButtonEnabled(true);
|
||||
setProperty("column", static_cast<int>(columnnum)); // Store the column number for later use
|
||||
|
||||
// Introduce a timer for delaying the signal triggered whenever the user changes the filter value.
|
||||
// The idea here is that the textChanged() event isn't connected to the update filter slot directly anymore
|
||||
// but instead there this timer mechanism in between: whenever the user changes the filter the delay timer
|
||||
// is (re)started. As soon as the user stops typing the timer has a chance to trigger and call the
|
||||
// delayedSignalTimerTriggered() method which then stops the timer and emits the delayed signal.
|
||||
delaySignalTimer = new QTimer(this);
|
||||
delaySignalTimer->setInterval(1000 /*Settings::getValue("databrowser", "filter_delay").toInt()*/); // This is the milliseconds of not-typing we want to wait before triggering
|
||||
connect(this, &FilterLineEdit::textChanged, delaySignalTimer, static_cast<void (QTimer::*)()>(&QTimer::start));
|
||||
connect(delaySignalTimer, &QTimer::timeout, this, &FilterLineEdit::delayedSignalTimerTriggered);
|
||||
|
||||
setWhatsThis(tr("These input fields allow you to perform quick filters in the currently selected table.\n"
|
||||
"By default, the rows containing the input text are filtered out.\n"
|
||||
"The following operators are also supported:\n"
|
||||
"%\tWildcard\n"
|
||||
">\tGreater than\n"
|
||||
"<\tLess than\n"
|
||||
">=\tEqual to or greater\n"
|
||||
"<=\tEqual to or less\n"
|
||||
"=\tEqual to: exact match\n"
|
||||
"<>\tUnequal: exact inverse match\n"
|
||||
"x~y\tRange: values between x and y\n"
|
||||
"/regexp/\tValues matching the regular expression"));
|
||||
|
||||
// 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, &FilterLineEdit::editingFinished, this, &FilterLineEdit::delayedSignalTimerTriggered);
|
||||
|
||||
// Prepare for adding the What's This information and filter helper actions to the context menu
|
||||
setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
connect(this, &FilterLineEdit::customContextMenuRequested, this, &FilterLineEdit::showContextMenu);
|
||||
}
|
||||
|
||||
void FilterLineEdit::delayedSignalTimerTriggered()
|
||||
{
|
||||
// Stop the timer first to avoid triggering in intervals
|
||||
delaySignalTimer->stop();
|
||||
|
||||
// Only emit text changed signal if the text has actually changed in comparison to the last emitted signal. This is necessary
|
||||
// because this method is also called whenever the line edit loses focus and not only when its text has definitely been changed.
|
||||
if(text() != lastValue)
|
||||
{
|
||||
// Emit the delayed signal using the current value
|
||||
emit delayedTextChanged(text());
|
||||
|
||||
// Remember this value for the next time
|
||||
lastValue = text();
|
||||
}
|
||||
}
|
||||
|
||||
void FilterLineEdit::keyReleaseEvent(QKeyEvent* event)
|
||||
{
|
||||
bool actedUponKey = false;
|
||||
if(event->key() == Qt::Key_Tab)
|
||||
{
|
||||
if(filterList && columnNumber < filterList->size() - 1)
|
||||
{
|
||||
filterList->at(columnNumber + 1)->setFocus();
|
||||
} else {
|
||||
QWidget* grandParent = parentWidget()->parentWidget();
|
||||
// Pass focus to the table (grandparent).
|
||||
// Parent would be the table header, which would cycle back to the first
|
||||
// column (due to proxying).
|
||||
if(grandParent)
|
||||
grandParent->setFocus();
|
||||
}
|
||||
actedUponKey = true;
|
||||
} else if(event->key() == Qt::Key_Backtab) {
|
||||
if(filterList && columnNumber > 0)
|
||||
{
|
||||
filterList->at(columnNumber - 1)->setFocus();
|
||||
actedUponKey = true;
|
||||
}
|
||||
}
|
||||
if(!actedUponKey)
|
||||
QLineEdit::keyReleaseEvent(event);
|
||||
}
|
||||
|
||||
void FilterLineEdit::focusInEvent(QFocusEvent* event)
|
||||
{
|
||||
QLineEdit::focusInEvent(event);
|
||||
emit filterFocused();
|
||||
}
|
||||
|
||||
void FilterLineEdit::clear()
|
||||
{
|
||||
// When programmatically clearing the line edit's value make sure the effects are applied immediately, i.e.
|
||||
// bypass the delayed signal timer
|
||||
QLineEdit::clear();
|
||||
delayedSignalTimerTriggered();
|
||||
}
|
||||
|
||||
void FilterLineEdit::setText(const QString& text)
|
||||
{
|
||||
// When programmatically setting the line edit's value make sure the effects are applied immediately, i.e.
|
||||
// bypass the delayed signal timer
|
||||
QLineEdit::setText(text);
|
||||
delayedSignalTimerTriggered();
|
||||
}
|
||||
|
||||
void FilterLineEdit::setFilterHelper(const QString& filterOperator, const QString& operatorSuffix)
|
||||
{
|
||||
setText(filterOperator + "?" + operatorSuffix);
|
||||
// Select the value for easy editing of the expression
|
||||
setSelection(filterOperator.length(), 1);
|
||||
}
|
||||
|
||||
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, this, [&]() {
|
||||
QDesktopServices::openUrl(QUrl("https://github.com/sqlitebrowser/sqlitebrowser/wiki/Using-the-Filters"));
|
||||
QWhatsThis::showText(pos, whatsThis(), this);
|
||||
});
|
||||
|
||||
QAction* isNullAction = new QAction(tr("Is NULL"), editContextMenu);
|
||||
connect(isNullAction, &QAction::triggered, this, [&]() {
|
||||
setText("=NULL");
|
||||
});
|
||||
|
||||
QAction* isNotNullAction = new QAction(tr("Is not NULL"), editContextMenu);
|
||||
connect(isNotNullAction, &QAction::triggered, this, [&]() {
|
||||
setText("<>NULL");
|
||||
});
|
||||
|
||||
QAction* isEmptyAction = new QAction(tr("Is empty"), editContextMenu);
|
||||
connect(isEmptyAction, &QAction::triggered, this, [&]() {
|
||||
setText("=''");
|
||||
});
|
||||
|
||||
QAction* isNotEmptyAction = new QAction(tr("Is not empty"), editContextMenu);
|
||||
connect(isNotEmptyAction, &QAction::triggered, this, [&]() {
|
||||
setText("<>''");
|
||||
});
|
||||
// Simplify this if we ever support a NOT LIKE filter
|
||||
QAction* notContainingAction = new QAction(tr("Not containing..."), editContextMenu);
|
||||
connect(notContainingAction, &QAction::triggered, this, [&]() {
|
||||
setFilterHelper(QString ("/^((?!"), QString(").)*$/"));
|
||||
});
|
||||
QAction* equalToAction = new QAction(tr("Equal to..."), editContextMenu);
|
||||
connect(equalToAction, &QAction::triggered, this, [&]() {
|
||||
setFilterHelper(QString ("="));
|
||||
});
|
||||
QAction* notEqualToAction = new QAction(tr("Not equal to..."), editContextMenu);
|
||||
connect(notEqualToAction, &QAction::triggered, this, [&]() {
|
||||
setFilterHelper(QString ("<>"));
|
||||
});
|
||||
QAction* greaterThanAction = new QAction(tr("Greater than..."), editContextMenu);
|
||||
connect(greaterThanAction, &QAction::triggered, this, [&]() {
|
||||
setFilterHelper(QString (">"));
|
||||
});
|
||||
QAction* lessThanAction = new QAction(tr("Less than..."), editContextMenu);
|
||||
connect(lessThanAction, &QAction::triggered, this, [&]() {
|
||||
setFilterHelper(QString ("<"));
|
||||
});
|
||||
QAction* greaterEqualAction = new QAction(tr("Greater or equal..."), editContextMenu);
|
||||
connect(greaterEqualAction, &QAction::triggered, this, [&]() {
|
||||
setFilterHelper(QString (">="));
|
||||
});
|
||||
QAction* lessEqualAction = new QAction(tr("Less or equal..."), editContextMenu);
|
||||
connect(lessEqualAction, &QAction::triggered, this, [&]() {
|
||||
setFilterHelper(QString ("<="));
|
||||
});
|
||||
QAction* inRangeAction = new QAction(tr("In range..."), editContextMenu);
|
||||
connect(inRangeAction, &QAction::triggered, this, [&]() {
|
||||
setFilterHelper(QString ("?~"));
|
||||
});
|
||||
|
||||
QAction* regexpAction = new QAction(tr("Regular expression..."), editContextMenu);
|
||||
connect(regexpAction, &QAction::triggered, this, [&]() {
|
||||
setFilterHelper(QString ("/"), QString ("/"));
|
||||
});
|
||||
|
||||
|
||||
if(conditional_format)
|
||||
{
|
||||
QAction* conditionalFormatAction;
|
||||
if (text().isEmpty()) {
|
||||
conditionalFormatAction = new QAction(QIcon(":/icons/clear_cond_formats"), tr("Clear All Conditional Formats"), editContextMenu);
|
||||
connect(conditionalFormatAction, &QAction::triggered, this, [&]() {
|
||||
emit clearAllCondFormats();
|
||||
});
|
||||
} else {
|
||||
conditionalFormatAction = new QAction(QIcon(":/icons/add_cond_format"), tr("Use for Conditional Format"), editContextMenu);
|
||||
connect(conditionalFormatAction, &QAction::triggered, this, [&]() {
|
||||
emit addFilterAsCondFormat(text());
|
||||
});
|
||||
}
|
||||
QAction* editCondFormatsAction = new QAction(QIcon(":/icons/edit_cond_formats"), tr("Edit Conditional Formats..."), editContextMenu);
|
||||
connect(editCondFormatsAction, &QAction::triggered, this, [&]() {
|
||||
emit editCondFormats();
|
||||
});
|
||||
editContextMenu->addSeparator();
|
||||
|
||||
editContextMenu->addAction(conditionalFormatAction);
|
||||
editContextMenu->addAction(editCondFormatsAction);
|
||||
}
|
||||
|
||||
|
||||
filterMenu->addAction(whatsThisAction);
|
||||
filterMenu->addSeparator();
|
||||
filterMenu->addAction(isNullAction);
|
||||
filterMenu->addAction(isNotNullAction);
|
||||
filterMenu->addAction(isEmptyAction);
|
||||
filterMenu->addAction(isNotEmptyAction);
|
||||
filterMenu->addSeparator();
|
||||
filterMenu->addAction(notContainingAction);
|
||||
filterMenu->addAction(equalToAction);
|
||||
filterMenu->addAction(notEqualToAction);
|
||||
filterMenu->addAction(greaterThanAction);
|
||||
filterMenu->addAction(lessThanAction);
|
||||
filterMenu->addAction(greaterEqualAction);
|
||||
filterMenu->addAction(lessEqualAction);
|
||||
filterMenu->addAction(inRangeAction);
|
||||
filterMenu->addAction(regexpAction);
|
||||
editContextMenu->exec(mapToGlobal(pos));
|
||||
}
|
||||
Reference in New Issue
Block a user