File indexing completed on 2024-05-12 05:09:47

0001 /***************************************************************************
0002     Copyright (C) 2003-2009 Robby Stephenson <robby@periapsis.org>
0003  ***************************************************************************/
0004 
0005 /***************************************************************************
0006  *                                                                         *
0007  *   This program is free software; you can redistribute it and/or         *
0008  *   modify it under the terms of the GNU General Public License as        *
0009  *   published by the Free Software Foundation; either version 2 of        *
0010  *   the License or (at your option) version 3 or any later version        *
0011  *   accepted by the membership of KDE e.V. (or its successor approved     *
0012  *   by the membership of KDE e.V.), which shall act as a proxy            *
0013  *   defined in Section 14 of version 3 of the license.                    *
0014  *                                                                         *
0015  *   This program is distributed in the hope that it will be useful,       *
0016  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
0017  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
0018  *   GNU General Public License for more details.                          *
0019  *                                                                         *
0020  *   You should have received a copy of the GNU General Public License     *
0021  *   along with this program.  If not, see <http://www.gnu.org/licenses/>. *
0022  *                                                                         *
0023  ***************************************************************************/
0024 
0025 // The layout borrows heavily from kmsearchpatternedit.cpp in kmail
0026 // which is authored by Marc Mutz <Marc@Mutz.com> under the GPL
0027 
0028 #include "filterrulewidget.h"
0029 #include "combobox.h"
0030 #include "../document.h"
0031 #include "../fieldcompletion.h"
0032 
0033 #include <KLocalizedString>
0034 #include <KComboBox>
0035 #include <KLineEdit>
0036 #include <KDateComboBox>
0037 
0038 #include <QDialog>
0039 #include <QHBoxLayout>
0040 #include <QStackedWidget>
0041 #include <QPushButton>
0042 
0043 using Tellico::FilterRuleWidget;
0044 
0045 FilterRuleWidget::FilterRuleWidget(Tellico::FilterRule* rule_, QWidget* parent_)
0046     : QWidget(parent_), m_ruleDate(nullptr), m_ruleType(General) {
0047   QHBoxLayout* l = new QHBoxLayout(this);
0048   l->setMargin(0);
0049 //  l->setSizeConstraint(QLayout::SetFixedSize);
0050   setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed);
0051 
0052   initLists();
0053   initWidget();
0054 
0055   if(rule_) {
0056     setRule(rule_);
0057   } else {
0058     reset();
0059   }
0060 }
0061 
0062 QString FilterRuleWidget::anyFieldString() {
0063   static const QString anyField = QLatin1Char('<') + i18n("Any Field") + QLatin1Char('>');
0064   return anyField;
0065 }
0066 
0067 void FilterRuleWidget::initLists() {
0068   //---------- initialize list of filter fields
0069   if(m_ruleFieldList.isEmpty()) {
0070     m_ruleFieldList.append(anyFieldString());
0071     QStringList titles = Data::Document::self()->collection()->fieldTitles();
0072     titles.sort();
0073     m_ruleFieldList += titles;
0074   }
0075 
0076 }
0077 
0078 void FilterRuleWidget::initWidget() {
0079   m_ruleField = new KComboBox(this);
0080   layout()->addWidget(m_ruleField);
0081   void (KComboBox::* activatedInt)(int) = &KComboBox::activated;
0082   // change the rule field before emitting modified
0083   connect(m_ruleField, activatedInt, this, &FilterRuleWidget::slotRuleFieldChanged);
0084   connect(m_ruleField, activatedInt, this, &FilterRuleWidget::signalModified);
0085 
0086   m_ruleFunc = new GUI::ComboBox(this);
0087   layout()->addWidget(m_ruleFunc);
0088   connect(m_ruleFunc, activatedInt, this, &FilterRuleWidget::slotRuleFunctionChanged);
0089   connect(m_ruleFunc, activatedInt, this, &FilterRuleWidget::signalModified);
0090 
0091   m_valueStack = new QStackedWidget(this);
0092   layout()->addWidget(m_valueStack);
0093 
0094   m_ruleValue = new KLineEdit(m_valueStack); //krazy:exclude=qclasses
0095   connect(m_ruleValue, &QLineEdit::textChanged, this, &FilterRuleWidget::signalModified);
0096   m_valueStack->addWidget(m_ruleValue);
0097 
0098   m_ruleDate = new KDateComboBox(m_valueStack);
0099   connect(m_ruleDate, &KDateComboBox::dateChanged, this, &FilterRuleWidget::signalModified);
0100   m_valueStack->addWidget(m_ruleDate);
0101 
0102   m_ruleField->addItems(m_ruleFieldList);
0103   updateFunctionList();
0104   slotRuleFunctionChanged(m_ruleFunc->currentIndex());
0105 }
0106 
0107 void FilterRuleWidget::slotRuleFieldChanged(int which_) {
0108   Q_UNUSED(which_);
0109   m_ruleType = General;
0110   const QString fieldTitle = m_ruleField->currentText();
0111   if(fieldTitle.isEmpty() || fieldTitle == anyFieldString()) {
0112     m_ruleValue->setCompletionObject(nullptr);
0113     updateFunctionList();
0114     return;
0115   }
0116   Data::FieldPtr field = Data::Document::self()->collection()->fieldByTitle(fieldTitle);
0117   if(field && field->hasFlag(Data::Field::AllowCompletion)) {
0118     FieldCompletion* completion = new FieldCompletion(field->hasFlag(Data::Field::AllowMultiple));
0119     completion->setItems(Data::Document::self()->collection()->valuesByFieldName(field->name()));
0120     completion->setIgnoreCase(true);
0121     m_ruleValue->setCompletionObject(completion);
0122     m_ruleValue->setAutoDeleteCompletionObject(true);
0123   } else {
0124     m_ruleValue->setCompletionObject(nullptr);
0125   }
0126 
0127   if(field) {
0128     if(field->type() == Data::Field::Date) {
0129       m_ruleType = Date;
0130     } else if(field->type() == Data::Field::Number || field->type() == Data::Field::Rating) {
0131       m_ruleType = Number;
0132     } else if(field->type() == Data::Field::Image) {
0133       m_ruleType = Image;
0134     } else if(field->type() == Data::Field::Bool) {
0135       m_ruleType = Bool;
0136     }
0137   }
0138   updateFunctionList();
0139 }
0140 
0141 void FilterRuleWidget::slotRuleFunctionChanged(int which_) {
0142   const QVariant data = m_ruleFunc->itemData(which_);
0143 
0144   // don't show the date picker if we're using regular expressions
0145   if(m_ruleType == Date && data != FilterRule::FuncRegExp && data != FilterRule::FuncNotRegExp) {
0146     m_valueStack->setCurrentWidget(m_ruleDate);
0147   } else {
0148     m_valueStack->setCurrentWidget(m_ruleValue);
0149     m_ruleValue->setPlaceholderText(m_ruleType == Bool ? QChar(0x2611): QString());
0150     if(m_ruleType == Number &&
0151       (data != FilterRule::FuncRegExp && data != FilterRule::FuncNotRegExp)) {
0152       m_ruleValue->setValidator(new QIntValidator(this));
0153     } else {
0154       m_ruleValue->setValidator(nullptr);
0155     }
0156     // Bool rules don't use a value
0157     m_ruleValue->setEnabled(m_ruleType != Bool);
0158   }
0159 }
0160 
0161 void FilterRuleWidget::setRule(const Tellico::FilterRule* rule_) {
0162   if(!rule_) {
0163     reset();
0164     return;
0165   }
0166 
0167   blockSignals(true);
0168 
0169   m_ruleType = General;
0170   if(rule_->fieldName().isEmpty()) {
0171     m_ruleField->setCurrentIndex(0); // "All Fields"
0172   } else {
0173     Data::FieldPtr field = Data::Document::self()->collection()->fieldByName(rule_->fieldName());
0174     if(field && field->type() == Data::Field::Date) {
0175       m_ruleType = Date;
0176       const QDate date = QDate::fromString(rule_->pattern(), Qt::ISODate);
0177       if(date.isValid()) {
0178         m_ruleDate->setDate(date);
0179       }
0180     }
0181     const int idx = m_ruleField->findText(field ? field->title() : QString());
0182     m_ruleField->setCurrentIndex(idx);
0183   }
0184 
0185   // update the rule fields first, before possible values
0186   slotRuleFieldChanged(m_ruleField->currentIndex());
0187 
0188   //--------------set function and contents
0189   m_ruleFunc->setCurrentData(rule_->function());
0190   m_ruleValue->setText(m_ruleType == Bool ? QString() : rule_->pattern());
0191 
0192   slotRuleFunctionChanged(m_ruleFunc->currentIndex());
0193   blockSignals(false);
0194 }
0195 
0196 Tellico::FilterRule* FilterRuleWidget::rule() const {
0197   QString fieldName; // empty string
0198   if(m_ruleField->currentIndex() > 0) { // 0 is "All Fields", field is empty
0199     fieldName = Data::Document::self()->collection()->fieldNameByTitle(m_ruleField->currentText());
0200   }
0201 
0202   QString ruleValue;
0203   if(m_valueStack->currentWidget() == m_ruleDate) {
0204     ruleValue = m_ruleDate->date().toString(Qt::ISODate);
0205   } else {
0206     // bool values only get checked against true
0207     ruleValue = m_ruleType == Bool ? QStringLiteral("true") : m_ruleValue->text().trimmed();
0208   }
0209 
0210   return new FilterRule(fieldName, ruleValue,
0211                         static_cast<FilterRule::Function>(m_ruleFunc->currentData().toInt()));
0212 }
0213 
0214 void FilterRuleWidget::reset() {
0215   blockSignals(true);
0216 
0217   m_ruleField->setCurrentIndex(0);
0218   m_ruleFunc->setCurrentIndex(0);
0219   m_ruleValue->clear();
0220 
0221   blockSignals(false);
0222 }
0223 
0224 void FilterRuleWidget::setFocus() {
0225   m_ruleValue->setFocus();
0226 }
0227 
0228 void FilterRuleWidget::updateFunctionList() {
0229   Q_ASSERT(m_ruleFunc);
0230   const QVariant data = m_ruleFunc->currentData();
0231   m_ruleFunc->clear();
0232   switch(m_ruleType) {
0233     case Date:
0234       m_ruleFunc->addItem(i18n("equals"), FilterRule::FuncEquals);
0235       m_ruleFunc->addItem(i18n("does not equal"), FilterRule::FuncNotEquals);
0236       m_ruleFunc->addItem(i18n("matches regular expression"), FilterRule::FuncRegExp);
0237       m_ruleFunc->addItem(i18n("does not match regular expression"), FilterRule::FuncNotRegExp);
0238       m_ruleFunc->addItem(i18nc("is before a date", "is before"), FilterRule::FuncBefore);
0239       m_ruleFunc->addItem(i18nc("is after a date", "is after"), FilterRule::FuncAfter);
0240       break;
0241     case Number:
0242       m_ruleFunc->addItem(i18n("equals"), FilterRule::FuncEquals);
0243       m_ruleFunc->addItem(i18n("does not equal"), FilterRule::FuncNotEquals);
0244       m_ruleFunc->addItem(i18n("matches regular expression"), FilterRule::FuncRegExp);
0245       m_ruleFunc->addItem(i18n("does not match regular expression"), FilterRule::FuncNotRegExp);
0246       m_ruleFunc->addItem(i18nc("is less than a number", "is less than"), FilterRule::FuncLess);
0247       m_ruleFunc->addItem(i18nc("is greater than a number", "is greater than"), FilterRule::FuncGreater);
0248       break;
0249     case Image:
0250       m_ruleFunc->addItem(i18n("image size equals"), FilterRule::FuncEquals);
0251       m_ruleFunc->addItem(i18n("image size does not equal"), FilterRule::FuncNotEquals);
0252       m_ruleFunc->addItem(i18nc("image size is less than a number", "image size is less than"), FilterRule::FuncLess);
0253       m_ruleFunc->addItem(i18nc("image size is greater than a number", "image size is greater than"), FilterRule::FuncGreater);
0254       break;
0255     case Bool:
0256       m_ruleFunc->addItem(i18n("equals"), FilterRule::FuncEquals);
0257       m_ruleFunc->addItem(i18n("does not equal"), FilterRule::FuncNotEquals);
0258       break;
0259     case General:
0260       m_ruleFunc->addItem(i18n("contains"), FilterRule::FuncContains);
0261       m_ruleFunc->addItem(i18n("does not contain"), FilterRule::FuncNotContains);
0262       m_ruleFunc->addItem(i18n("equals"), FilterRule::FuncEquals);
0263       m_ruleFunc->addItem(i18n("does not equal"), FilterRule::FuncNotEquals);
0264       m_ruleFunc->addItem(i18n("matches regular expression"), FilterRule::FuncRegExp);
0265       m_ruleFunc->addItem(i18n("does not match regular expression"), FilterRule::FuncNotRegExp);
0266       break;
0267   }
0268   m_ruleFunc->setCurrentData(data);
0269   slotRuleFunctionChanged(m_ruleFunc->currentIndex());
0270 }