Warning, file /office/skrooge/skgbasegui/skgfilteredtableview.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).

0001 /***************************************************************************
0002  * SPDX-FileCopyrightText: 2022 S. MANKOWSKI stephane@mankowski.fr
0003  * SPDX-FileCopyrightText: 2022 G. DE BURE support@mankowski.fr
0004  * SPDX-License-Identifier: GPL-3.0-or-later
0005  ***************************************************************************/
0006 /** @file
0007  * filetered SKGTableView.
0008  *
0009  * @author Stephane MANKOWSKI / Guillaume DE BURE
0010  */
0011 #include "skgfilteredtableview.h"
0012 
0013 #include <qdom.h>
0014 #include <qlineedit.h>
0015 
0016 #include "skgmainpanel.h"
0017 #include "skgobjectmodelbase.h"
0018 #include "skgsortfilterproxymodel.h"
0019 
0020 SKGFilteredTableView::SKGFilteredTableView(QWidget* iParent)
0021     : QWidget(iParent), m_objectModel(nullptr), m_refreshNeeded(true)
0022 {
0023     ui.setupUi(this);
0024     ui.kTitle->hide();
0025     ui.kResetInternalFilter->hide();
0026     connect(ui.kResetInternalFilter, &QToolButton::clicked, this, &SKGFilteredTableView::resetFilter);
0027     ui.kResetInternalFilter->setIcon(SKGServices::fromTheme(QStringLiteral("dialog-cancel")));
0028     ui.kConfigure->setIcon(SKGServices::fromTheme(QStringLiteral("configure")));
0029 
0030     ui.kConfigure->setPopupMode(QToolButton::InstantPopup);
0031     ui.kConfigure->setAutoRaise(true);
0032     ui.kConfigure->setMenu(ui.kView->getHeaderMenu());
0033 
0034     m_timer.setSingleShot(true);
0035     connect(&m_timer, &QTimer::timeout, this, &SKGFilteredTableView::onTextFilterChanged, Qt::QueuedConnection);
0036     connect(ui.kFilterEdit, &QLineEdit::textChanged, this, [ = ]() {
0037         m_timer.start(300);
0038     });
0039     connect(ui.kShow, &SKGShow::stateChanged, this, &SKGFilteredTableView::onFilterChanged, Qt::QueuedConnection);
0040     if (SKGMainPanel::getMainPanel() != nullptr) {
0041         connect(SKGMainPanel::getMainPanel(), &SKGMainPanel::currentPageChanged, this, &SKGFilteredTableView::pageChanged, Qt::QueuedConnection);
0042     }
0043 }
0044 
0045 SKGFilteredTableView::~SKGFilteredTableView()
0046 {
0047     m_objectModel = nullptr;
0048 }
0049 
0050 QString SKGFilteredTableView::getState()
0051 {
0052     QDomDocument doc(QStringLiteral("SKGML"));
0053     QDomElement root = doc.createElement(QStringLiteral("parameters"));
0054     doc.appendChild(root);
0055 
0056     root.setAttribute(QStringLiteral("show"), ui.kShow->getState());
0057     root.setAttribute(QStringLiteral("filter"), getSearchField()->text());
0058 
0059     // Memorize table settings
0060     root.setAttribute(QStringLiteral("view"), ui.kView->getState());
0061     return doc.toString();
0062 }
0063 
0064 void SKGFilteredTableView::setState(const QString& iState)
0065 {
0066     QDomDocument doc(QStringLiteral("SKGML"));
0067     doc.setContent(iState);
0068     QDomElement root = doc.documentElement();
0069 
0070     QString show2 = root.attribute(QStringLiteral("show"));
0071     QString filter = root.attribute(QStringLiteral("filter"));
0072 
0073     if (!show2.isEmpty()) {
0074         ui.kShow->setState(show2);
0075     }
0076     getSearchField()->setText(filter);
0077 
0078     if (m_objectModel != nullptr) {
0079         bool previous = m_objectModel->blockRefresh(true);
0080         onFilterChanged();
0081         m_objectModel->blockRefresh(previous);
0082     }
0083 
0084     // !!! Must be done here after onFilterChanged
0085     ui.kView->setState(root.attribute(QStringLiteral("view")));
0086 }
0087 
0088 SKGShow* SKGFilteredTableView::getShowWidget() const
0089 {
0090     return ui.kShow;
0091 }
0092 
0093 SKGTreeView* SKGFilteredTableView::getView() const
0094 {
0095     return ui.kView;
0096 }
0097 
0098 QLineEdit* SKGFilteredTableView::getSearchField() const
0099 {
0100     return ui.kFilterEdit;
0101 }
0102 
0103 void SKGFilteredTableView::onFilterChanged()
0104 {
0105     QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
0106 
0107     // Update model
0108     if ((m_objectModel != nullptr) && ui.kShow->isEnabled() && m_objectModel->setFilter(ui.kShow->getWhereClause())) {
0109         m_objectModel->dataModified();
0110     }
0111 
0112     QApplication::restoreOverrideCursor();
0113 }
0114 
0115 void SKGFilteredTableView::pageChanged()
0116 {
0117     if (m_refreshNeeded) {
0118         dataModified(QLatin1String(""), 0);
0119     }
0120 }
0121 
0122 void SKGFilteredTableView::dataModified(const QString& iTableName, int iIdTransaction)
0123 {
0124     Q_UNUSED(iIdTransaction)
0125 
0126     if (((m_objectModel != nullptr) && iTableName == m_objectModel->getTable()) || iTableName.isEmpty()) {
0127         SKGTabPage* page = SKGTabPage::parentTabPage(this);
0128         if (page != nullptr && SKGMainPanel::getMainPanel() != nullptr && page != SKGMainPanel::getMainPanel()->currentPage()) {
0129             m_refreshNeeded = true;
0130             return;
0131         }
0132         m_refreshNeeded = false;
0133 
0134         if (getView()->isAutoResized()) {
0135             getView()->resizeColumnsToContentsDelayed();
0136         }
0137 
0138         getView()->onSelectionChanged();
0139     }
0140 }
0141 
0142 void SKGFilteredTableView::setModel(SKGObjectModelBase* iModel)
0143 {
0144     m_objectModel = iModel;
0145     if (m_objectModel != nullptr) {
0146         auto modelProxy = new SKGSortFilterProxyModel(this);
0147         modelProxy->setSourceModel(m_objectModel);
0148         modelProxy->setSortRole(Qt::UserRole);
0149         modelProxy->setDynamicSortFilter(true);
0150 
0151         connect(modelProxy, &SKGSortFilterProxyModel::rowsInserted, ui.kView, &SKGTreeView::scroolOnSelection);
0152         ui.kView->setModel(modelProxy);
0153 
0154         onTextFilterChanged();
0155 
0156         ui.kView->sortByColumn(0, Qt::AscendingOrder);
0157         connect(m_objectModel, &SKGObjectModelBase::beforeReset, ui.kView, &SKGTreeView::saveSelection);
0158         connect(m_objectModel, &SKGObjectModelBase::afterReset, ui.kView, &SKGTreeView::resetSelection);
0159         connect(m_objectModel->getDocument(), &SKGDocument::tableModified, this, &SKGFilteredTableView::dataModified, Qt::QueuedConnection);
0160     }
0161     dataModified(QLatin1String(""), 0);
0162 }
0163 
0164 void SKGFilteredTableView::resetFilter()
0165 {
0166     getShowWidget()->setEnabled(true);
0167     ui.kTitle->hide();
0168     ui.kResetInternalFilter->hide();
0169 
0170     m_objectModel->setFilter(QLatin1String(""));
0171     m_objectModel->refresh();
0172 }
0173 
0174 void SKGFilteredTableView::setFilter(const QIcon& iIcon, const QString& iText, const QString& iWhereClause)
0175 {
0176     if ((m_objectModel != nullptr) && !iWhereClause.isEmpty()) {
0177         getShowWidget()->setEnabled(false);
0178 
0179         QFontMetrics fm(fontMetrics());
0180         ui.kTitle->setComment("<html><body><b>" % SKGServices::stringToHtml(fm.elidedText(iText, Qt::ElideMiddle, 2000)) % "</b></body></html>");
0181         ui.kTitle->setToolTip(iText);
0182         ui.kResetInternalFilter->show();
0183 
0184         ui.kTitle->setIcon(iIcon, KTitleWidget::ImageLeft);
0185 
0186         m_objectModel->setFilter(iWhereClause);
0187         m_objectModel->refresh();
0188     }
0189 }
0190 
0191 void SKGFilteredTableView::onTextFilterChanged()
0192 {
0193     auto filter = ui.kFilterEdit->text();
0194     auto modelProxy = qobject_cast<SKGSortFilterProxyModel*>(ui.kView->model());
0195     if (modelProxy != nullptr) {
0196         modelProxy->setFilterKeyColumn(-1);
0197         modelProxy->setFilterCaseSensitivity(Qt::CaseInsensitive);
0198         modelProxy->setFilterFixedString(filter);
0199 
0200         QStringList attributes;
0201         QAbstractItemModel* model = modelProxy->sourceModel();
0202         if (model != nullptr) {
0203             int nbcol = model->columnCount();
0204             attributes.reserve(nbcol);
0205             for (int j = 0; j < nbcol; ++j) {
0206                 attributes.append(model->headerData(j, Qt::Horizontal).toString());
0207             }
0208         }
0209         auto tooltip = i18nc("Tooltip", "<html><head/><body><p>Searching is case-insensitive. So table, Table, and TABLE are all the same.<br/>"
0210                              "If you just put a word or series of words in the search box, the application will filter the table to keep all lines having these words (logical operator AND). <br/>"
0211                              "If you want to add (logical operator OR) some lines, you must prefix your word by '+'.<br/>"
0212                              "If you want to remove (logical operator NOT) some lines, you must prefix your word by '-'.<br/>"
0213                              "If you want to search only on some columns, you must prefix your word by the beginning of column name like: col1:word.<br/>"
0214                              "If you want to search only on one column, you must prefix your word by the column name and a dot like: col1.:word.<br/>"
0215                              "If you want to use the character ':' in value, you must specify the column name like this: col1:value:rest.<br/>"
0216                              "If you want to search for a phrase or something that contains spaces, you must put it in quotes, like: 'yes, this is a phrase'.</p>"
0217                              "<p>You can also use operators '&lt;', '&gt;', '&lt;=', '&gt;=', '=' and '#' (for regular expression).</p>"
0218                              "<p><span style=\"font-weight:600; text-decoration: underline;\">Examples:</span><br/>"
0219                              "+val1 +val2 =&gt; Keep lines containing val1 OR val2<br/>"
0220                              "+val1 -val2 =&gt; Keep lines containing val1 but NOT val2<br/>"
0221                              "'abc def' =&gt; Keep lines containing the sentence 'abc def' <br/>"
0222                              "'-att:abc def' =&gt; Remove lines having a column name starting by abc and containing 'abc def' <br/>"
0223                              "abc:def =&gt; Keep lines having a column name starting by abc and containing def<br/>"
0224                              ":abc:def =&gt; Keep lines containing 'abc:def'<br/>"
0225                              "Date&gt;2015-03-01 =&gt; Keep lines where at least one attribute starting by Date is greater than 2015-03-01<br/>"
0226                              "Date.&gt;2015-03-01 =&gt; Keep lines where at the Date attribute is greater than 2015-03-01<br/>"
0227                              "Amount&lt;10 =&gt;Keep lines where at least one attribute starting by Amount is less than 10<br/>"
0228                              "Amount=10 =&gt;Keep lines where at least one attribute starting by Amount is equal to 10<br/>"
0229                              "Amount&lt;=10 =&gt;Keep lines where at least one attribute starting by Amount is less or equal to 10<br/>"
0230                              "abc#^d.*f$ =&gt; Keep lines having a column name starting by abc and matching the regular expression ^d.*f$</p>"
0231                              "<span style=\"font-weight:600; text-decoration: underline;\">Your filter is understood like this:</span><br/>"
0232                              "%1</body></html>", SKGServices::searchCriteriasToWhereClause(SKGServices::stringToSearchCriterias(filter), attributes, m_objectModel->getDocument(), true));
0233         ui.kFilterEdit->setToolTip(tooltip);
0234     }
0235 }