diff --git a/core/autotablemodel.h b/core/autotablemodel.h index b00c404..307319f 100644 --- a/core/autotablemodel.h +++ b/core/autotablemodel.h @@ -9,14 +9,15 @@ #include "define.h" #include "core_global.h" #include "exprevaluator.h" +#include "itablemodel.h" template -class AutoTableModel : public QAbstractTableModel +class AutoTableModel : public ITableModel { public: explicit AutoTableModel(QObject *parent = NULL) - :QAbstractTableModel(parent) + :ITableModel(parent) { filtered = false; } @@ -141,8 +142,8 @@ public: m_list = list; } -public slots: - void filter(const QString &filter) +protected: + void handleFilter(const QString &filter) override { beginResetModel(); if (!filtered) @@ -152,13 +153,13 @@ public slots: } ExprEvaluator ev; - m_list.clear(); - std::copy_if(ALL(m_fullList), m_list.begin(), [&filter, &ev](QSharedPointer obj){ return ev.evaluate((QObject*)obj.pointer, filter); }); + auto it = std::copy_if(ALL(m_fullList), m_list.begin(), [&filter, &ev](QSharedPointer obj){ return ev.evaluate((QObject*)obj.data(), filter); }); + m_list.erase(it, m_list.end()); endResetModel(); } - void restore() + void handleRestore() override { if (filtered) { diff --git a/core/context.cpp b/core/context.cpp index c5d80af..dc607a8 100644 --- a/core/context.cpp +++ b/core/context.cpp @@ -70,6 +70,7 @@ void Context::openDb(const QString &path) { if (m_db != NULL) { delete m_db; + m_solved.clear(); m_dbOpened = false; } @@ -177,6 +178,7 @@ void Context::createSchema(IPlugin *plugin, const QSqlDatabase &db, const QMappluginId()) || i >= ver) { QString sql = plugin->schemas()[i]; + sql = sql.trimmed(); QSqlQuery q(db); QStringList sqlList = sql.split(";"); diff --git a/core/core.pro b/core/core.pro index fafd34f..0dd72c2 100644 --- a/core/core.pro +++ b/core/core.pro @@ -39,7 +39,10 @@ SOURCES += \ permissionservice.cpp \ filterui.cpp \ exprevaluator.cpp \ - samestringvalidator.cpp + samestringvalidator.cpp \ + savefilterdialog.cpp \ + filterdialog.cpp \ + itablemodel.cpp HEADERS += core.h\ core_global.h \ @@ -75,7 +78,10 @@ HEADERS += core.h\ permissionservice.h \ filterui.h \ exprevaluator.h \ - samestringvalidator.h + samestringvalidator.h \ + savefilterdialog.h \ + filterdialog.h \ + itablemodel.h unix { target.path = /usr/lib @@ -105,7 +111,9 @@ FORMS += \ users/userform.ui \ columndialog.ui \ roles/rolesform.ui \ - filterui.ui + filterui.ui \ + savefilterdialog.ui \ + filterdialog.ui OTHER_FILES += \ users/metaData.json \ diff --git a/core/exprevaluator.cpp b/core/exprevaluator.cpp index 3bfb131..16c0686 100644 --- a/core/exprevaluator.cpp +++ b/core/exprevaluator.cpp @@ -13,6 +13,7 @@ ExprEvaluator::ExprEvaluator() m_operations["||"] = [](QVariant left, QVariant right) { return left.toBool() || right.toBool(); }; m_operations["&&"] = [](QVariant left, QVariant right) { return left.toBool() && right.toBool(); }; + m_caseSensitive = false; } #else const QMap > ExprEvaluator::m_operations = { @@ -30,12 +31,62 @@ const QMap > ExprEvaluator::m_o ExprEvaluator::ExprEvaluator() { + m_caseSensitive = false; } #endif -bool ExprEvaluator::evaluate(const QObject *data, const QString &expression) +bool ExprEvaluator::evaluate(QObject *object, const QString &exp) { - return true; + if (exp.contains("&&") && exp.contains("||")) + { + if (exp.indexOf("&&") < exp.indexOf("||")) + { + return subEval("&&", exp, object); + } + else + { + return subEval("||", exp, object); + } + } + else if (exp.contains("&&")) + { + return subEval("&&", exp, object); + } + else if (exp.contains("||")) + { + return subEval("||", exp, object); + } + else + { + QVariant value; + QString oper; + QVariant cond; + parseExpr(exp, value, oper, cond, object); + return m_operations[oper](value, cond); + } +} + +void ExprEvaluator::setCaseSensitive(bool caseSensitive) +{ + m_caseSensitive = caseSensitive; +} + +bool ExprEvaluator::subEval(const QString &oper, const QString &expresion, QObject *object) +{ + QString ex = expresion.left(expresion.indexOf(oper)); + QVariant value; + QString o; + QVariant cond; + parseExpr(ex, value, o, cond, object); + return m_operations[oper](m_operations[o](value, cond), evaluate(object, expresion.mid(expresion.indexOf(oper) + 2).trimmed())); +} + +void ExprEvaluator::parseExpr(const QString &exp, QVariant &value, QString &oper, QVariant &condition, QObject *object) +{ + QStringList expCat = exp.trimmed().split(" "); + value = object->property(expCat[0].toStdString().c_str()); + oper = expCat[1]; + condition = expCat[2].replace("%20", " "); } diff --git a/core/exprevaluator.h b/core/exprevaluator.h index 61fcc62..36e3b21 100644 --- a/core/exprevaluator.h +++ b/core/exprevaluator.h @@ -6,12 +6,15 @@ #include #include -class ExprEvaluator +#include "core_global.h" + +class CORESHARED_EXPORT ExprEvaluator { public: ExprEvaluator(); - bool evaluate(const QObject *data, const QString &expression); + bool evaluate(QObject *object, const QString &exp); + void setCaseSensitive(bool caseSensitive); private: #ifdef _MSC_VER @@ -21,6 +24,9 @@ private: #endif bool m_caseSensitive; + + bool subEval(const QString &oper, const QString &expresion, QObject *object); + void parseExpr(const QString &exp, QVariant &value, QString &oper, QVariant &condition, QObject *object); }; #endif // EXPREVALUATOR_H diff --git a/core/filterdialog.cpp b/core/filterdialog.cpp new file mode 100644 index 0000000..1fee5a0 --- /dev/null +++ b/core/filterdialog.cpp @@ -0,0 +1,64 @@ +#include "filterdialog.h" +#include "ui_filterdialog.h" + +#include +#include +#include + +#include "context.h" + +FilterDialog::FilterDialog(QWidget *parent) : + QDialog(parent), + ui(new Ui::FilterDialog) +{ + ui->setupUi(this); +} + +FilterDialog::~FilterDialog() +{ + delete ui; +} + +QString FilterDialog::pluginId() const +{ + return m_pluginId; +} + +void FilterDialog::setPluginId(const QString &pluginId) +{ + m_pluginId = pluginId; + fillTable(); +} + +QTableWidget *FilterDialog::table() +{ + return ui->tableWidget; +} + +void FilterDialog::fillTable() +{ + ui->tableWidget->clear(); + QMap filters = Context::instance().settings()->value("filters/" + pluginId()).toMap(); + + int row = 0; + ui->tableWidget->setRowCount(filters.keys().size()); + foreach (QString name, filters.keys()) { + QTableWidgetItem *itemName = new QTableWidgetItem(name); + ui->tableWidget->setItem(row, 0, itemName); + QTableWidgetItem *itemFilter = new QTableWidgetItem(filters[name].toString()); + ui->tableWidget->setItem(row, 1, itemFilter); + + QToolButton *btnRemove = new QToolButton(); + btnRemove->setText("Remove"); + ui->tableWidget->setCellWidget(row, 2, btnRemove); + connect(btnRemove, &QToolButton::clicked, [this, btnRemove](){ + int rowToDel = ui->tableWidget->rowAt(btnRemove->y()); + ui->tableWidget->removeRow(rowToDel); + }); + + row++; + } + + ui->tableWidget->setColumnWidth(1, 200); + ui->tableWidget->setHorizontalHeaderLabels(QStringList() << tr("Name") << tr("Filter") << ""); +} diff --git a/core/filterdialog.h b/core/filterdialog.h new file mode 100644 index 0000000..6ade5cc --- /dev/null +++ b/core/filterdialog.h @@ -0,0 +1,31 @@ +#ifndef FILTERDIALOG_H +#define FILTERDIALOG_H + +#include +#include +#include + +namespace Ui { +class FilterDialog; +} + +class FilterDialog : public QDialog +{ + Q_OBJECT + +public: + explicit FilterDialog(QWidget *parent = 0); + ~FilterDialog(); + + QString pluginId() const; + void setPluginId(const QString &pluginId); + QTableWidget *table(); + +private: + Ui::FilterDialog *ui; + QString m_pluginId; + + void fillTable(); +}; + +#endif // FILTERDIALOG_H diff --git a/core/filterdialog.ui b/core/filterdialog.ui new file mode 100644 index 0000000..88f9604 --- /dev/null +++ b/core/filterdialog.ui @@ -0,0 +1,80 @@ + + + FilterDialog + + + + 0 + 0 + 556 + 370 + + + + Manage filters + + + true + + + + + + Qt::SolidLine + + + 3 + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + buttonBox + accepted() + FilterDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + FilterDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/core/filterui.cpp b/core/filterui.cpp index 01cbac7..4647307 100644 --- a/core/filterui.cpp +++ b/core/filterui.cpp @@ -10,6 +10,13 @@ #include #include #include +#include +#include +#include + +#include "savefilterdialog.h" +#include "filterdialog.h" +#include "context.h" FilterUi::FilterUi(QWidget *parent, QObject *entity) : QWidget(parent), @@ -17,6 +24,7 @@ FilterUi::FilterUi(QWidget *parent, QObject *entity) : { ui->setupUi(this); m_entity = entity; + m_buildGrid = true; if (entity == NULL) { @@ -100,6 +108,17 @@ void FilterUi::addRow(bool isOr) propertyChanged(row, oper, 0); } +QString FilterUi::pluginId() const +{ + return m_pluginId; +} + +void FilterUi::setPluginId(const QString &pluginId) +{ + m_pluginId = pluginId; + fillCombo(); +} + QString FilterUi::buildExpression() const { QString expr; @@ -122,7 +141,7 @@ QString FilterUi::buildExpression() const if (cellWidget != NULL) { - expr += " " + cellWidget->property(cellWidget->metaObject()->userProperty().name()).toString(); + expr += " " + cellWidget->property(cellWidget->metaObject()->userProperty().name()).toString().replace(" ", "%20"); } } @@ -206,3 +225,148 @@ void FilterUi::propertyChanged(int row, QComboBox *oper, int index) ui->tableWidget->setCellWidget(row, 3, cellWidget); } } + +void FilterUi::fillCombo() +{ + m_buildGrid = false; + ui->comboLoad->clear(); + QMap filter = Context::instance().settings()->value("filters/" + pluginId()).toMap(); + + ui->comboLoad->addItem("<>", "--"); + foreach (QString key, filter.keys()) { + ui->comboLoad->addItem(key, filter[key]); + } + m_buildGrid = true; +} + +void FilterUi::buildGrid(const QString &expr) +{ + if (!isVisible() || !m_buildGrid) + { + return; + } + + ui->tableWidget->clear(); + for (int i = 0; i < ui->tableWidget->rowCount(); i++) + { + ui->tableWidget->removeRow(i); + } + ui->tableWidget->setRowCount(0); + QStringList expList = expr.split(" "); + bool newLine = true; + + for (int i = 0; i < expList.count(); i++) + { + if (i == 0) + { + addRow(false); + } + + int rowIndex = ui->tableWidget->rowCount() - 1; + + if (newLine) + { + QComboBox *prop = qobject_cast(ui->tableWidget->cellWidget(rowIndex, 1)); + if (prop == NULL) + { + continue; + } + + int j; + for (j = 0; j < m_entity->metaObject()->propertyCount(); j++) + { + if (expList[i] == m_entity->metaObject()->property(j).name()) + { + break; + } + } + prop->setCurrentIndex(j - 1); + newLine = false; + } + + if (expList[i] == "==" || expList[i] == "!=" || expList[i] == "%" || expList[i] == "<" || expList[i] == "<=" || expList[i] == ">" || expList[i] == ">=") + { + QComboBox *oper = qobject_cast(ui->tableWidget->cellWidget(rowIndex, 2)); + for (int j = 0; j < oper->count(); j++) + { + oper->setCurrentIndex(j); + if (oper->itemText(j) == expList[i]) + { + break; + } + } + } + + if (expList[i] == "&&") + { + addRow(false); + newLine = true; + continue; + } + + if (expList[i] == "||") + { + addRow(true); + newLine = true; + continue; + } + + QWidget *cellWidget = ui->tableWidget->cellWidget(rowIndex, 3); + + if (cellWidget != NULL) + { + cellWidget->setProperty(cellWidget->metaObject()->userProperty().name(), expList[i]); + } + } + +} + +void FilterUi::on_btnSave_clicked() +{ + SaveFilterDialog *dlg = new SaveFilterDialog(this); + dlg->open(); + connect(dlg, &SaveFilterDialog::accepted, [this, dlg]() { + QMap filter = Context::instance().settings()->value("filters/" + pluginId()).toMap(); + filter[dlg->filterName()] = buildExpression(); + Context::instance().settings()->setValue("filters/" + pluginId(), QVariant::fromValue(filter)); + fillCombo(); + dlg->deleteLater(); + }); +} + +void FilterUi::on_comboLoad_currentIndexChanged(int index) +{ + if (ui->comboLoad->itemData(index).toString() == "--") + { + ui->tableWidget->clear(); + for (int i = 0; i < ui->tableWidget->rowCount(); i++) + { + ui->tableWidget->removeRow(i); + } + ui->tableWidget->setRowCount(0); + + addRow(false); + } + else + { + buildGrid(ui->comboLoad->itemData(index).toString()); + } +} + +void FilterUi::on_btnManage_clicked() +{ + FilterDialog *filterDialog = new FilterDialog(this); + filterDialog->setPluginId(pluginId()); + filterDialog->open(); + + connect(filterDialog, &FilterDialog::accepted, [this, filterDialog](){ + QMap filters; + for (int i = 0; i < filterDialog->table()->rowCount(); i++) + { + filters[filterDialog->table()->item(i, 0)->text()] = filterDialog->table()->item(i, 1)->text(); + } + Context::instance().settings()->setValue("filters/" + pluginId(), QVariant::fromValue(filters)); + fillCombo(); + filterDialog->deleteLater(); + }); +} diff --git a/core/filterui.h b/core/filterui.h index 7095a32..c72ce59 100644 --- a/core/filterui.h +++ b/core/filterui.h @@ -19,6 +19,9 @@ public: explicit FilterUi(QWidget *parent = 0, QObject *entity = NULL); ~FilterUi(); + QString pluginId() const; + void setPluginId(const QString &pluginId); + private slots: void on_actionAdd_condition_AND_triggered(); void on_actionAdd_condition_OR_triggered(); @@ -26,6 +29,12 @@ private slots: void on_actionRemove_condition_triggered(); void on_btnGo_toggled(bool checked); + void on_btnSave_clicked(); + + void on_comboLoad_currentIndexChanged(int index); + + void on_btnManage_clicked(); + signals: void applyFilter(const QString &filter); void restoreData(); @@ -35,9 +44,13 @@ private: void addRow(bool isOr); QObject *m_entity; QMenu *m_contextMenu; + QString m_pluginId; + bool m_buildGrid; QString buildExpression() const; void propertyChanged(int row, QComboBox *oper, int index); + void fillCombo(); + void buildGrid(const QString &expr); }; #endif // FILTERUI_H diff --git a/core/filterui.ui b/core/filterui.ui index 8758a45..5c3e4a9 100644 --- a/core/filterui.ui +++ b/core/filterui.ui @@ -65,6 +65,35 @@ 0 + + + + Apply + + + 1 + + + Go + + + + :/icons/ok.svg:/icons/ok.svg + + + + 24 + 24 + + + + true + + + true + + + @@ -73,22 +102,67 @@ - + + + + 150 + 0 + + + + false + + - + + + Save + + + 1 + - Go + Save - + + + :/icons/save.svg:/icons/save.svg + + + + 24 + 24 + + + true - + + + Manage + + + 1 + - Save + Manage + + + + :/icons/list.svg:/icons/list.svg + + + + 24 + 24 + + + + true diff --git a/core/gridform.h b/core/gridform.h index cc8cb6b..cb80568 100644 --- a/core/gridform.h +++ b/core/gridform.h @@ -53,6 +53,8 @@ public: Q_ASSERT(m_tableModel == NULL); m_tableModel = tableModel; + connect(m_filterUi, SIGNAL(applyFilter(QString)), m_tableModel, SLOT(filter(QString))); + connect(m_filterUi, SIGNAL(restoreData()), m_tableModel, SLOT(restore())); } void setFormHandler(IFormHandler *handler) { diff --git a/core/icons/list.svg b/core/icons/list.svg new file mode 100644 index 0000000..1289f91 --- /dev/null +++ b/core/icons/list.svg @@ -0,0 +1,6 @@ + \ No newline at end of file diff --git a/core/icons/ok.svg b/core/icons/ok.svg new file mode 100644 index 0000000..4985ee5 --- /dev/null +++ b/core/icons/ok.svg @@ -0,0 +1,4 @@ + \ No newline at end of file diff --git a/core/icons/save.svg b/core/icons/save.svg new file mode 100644 index 0000000..b81916e --- /dev/null +++ b/core/icons/save.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/core/igridform.cpp b/core/igridform.cpp index b784401..560a794 100644 --- a/core/igridform.cpp +++ b/core/igridform.cpp @@ -26,6 +26,11 @@ IGridForm::~IGridForm() void IGridForm::setPluginId(const QString &pluginId) { m_pluginId = pluginId; + + if (m_filterUi != NULL) + { + m_filterUi->setPluginId(pluginId); + } } QString IGridForm::pluginId() diff --git a/core/itablemodel.cpp b/core/itablemodel.cpp new file mode 100644 index 0000000..bf408f4 --- /dev/null +++ b/core/itablemodel.cpp @@ -0,0 +1,18 @@ +#include "itablemodel.h" + +ITableModel::ITableModel(QObject *parent) + :QAbstractTableModel(parent) +{ + +} + +void ITableModel::filter(const QString &filter) +{ + handleFilter(filter); +} + +void ITableModel::restore() +{ + handleRestore(); +} + diff --git a/core/itablemodel.h b/core/itablemodel.h new file mode 100644 index 0000000..f952daf --- /dev/null +++ b/core/itablemodel.h @@ -0,0 +1,24 @@ +#ifndef ITABLEMODEL_H +#define ITABLEMODEL_H + +#include +#include + +#include "core_global.h" + +class CORESHARED_EXPORT ITableModel : public QAbstractTableModel +{ + Q_OBJECT +public: + explicit ITableModel(QObject *parent = NULL); + +protected: + virtual void handleFilter(const QString &filter) = 0; + virtual void handleRestore() = 0; + +public slots: + void filter(const QString &filter); + void restore(); +}; + +#endif // ITABLEMODEL_H diff --git a/core/rc.qrc b/core/rc.qrc index ab51ecb..2bd5c27 100644 --- a/core/rc.qrc +++ b/core/rc.qrc @@ -9,5 +9,8 @@ icons/remove.svg icons/filter.svg icons/print.svg + icons/save.svg + icons/ok.svg + icons/list.svg diff --git a/core/savefilterdialog.cpp b/core/savefilterdialog.cpp new file mode 100644 index 0000000..1f3912f --- /dev/null +++ b/core/savefilterdialog.cpp @@ -0,0 +1,19 @@ +#include "savefilterdialog.h" +#include "ui_savefilterdialog.h" + +SaveFilterDialog::SaveFilterDialog(QWidget *parent) : + QDialog(parent), + ui(new Ui::SaveFilterDialog) +{ + ui->setupUi(this); +} + +SaveFilterDialog::~SaveFilterDialog() +{ + delete ui; +} + +QString SaveFilterDialog::filterName() +{ + return ui->filterName->text(); +} diff --git a/core/savefilterdialog.h b/core/savefilterdialog.h new file mode 100644 index 0000000..f95e1d5 --- /dev/null +++ b/core/savefilterdialog.h @@ -0,0 +1,27 @@ +#ifndef SAVEFILTERDIALOG_H +#define SAVEFILTERDIALOG_H + +#include +#include + +namespace Ui { +class SaveFilterDialog; +} + +class SaveFilterDialog : public QDialog +{ + Q_OBJECT + +public: + explicit SaveFilterDialog(QWidget *parent = 0); + ~SaveFilterDialog(); + + QString filterName(); + +private slots: + +private: + Ui::SaveFilterDialog *ui; +}; + +#endif // SAVEFILTERDIALOG_H diff --git a/core/savefilterdialog.ui b/core/savefilterdialog.ui new file mode 100644 index 0000000..d14ed04 --- /dev/null +++ b/core/savefilterdialog.ui @@ -0,0 +1,77 @@ + + + SaveFilterDialog + + + + 0 + 0 + 395 + 158 + + + + Filter name + + + true + + + + + + Filter name + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + buttonBox + accepted() + SaveFilterDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + SaveFilterDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + +