File indexing completed on 2024-05-12 16:46:05

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 #include "../tellico_debug.h"
0033 
0034 #include <KLocalizedString>
0035 #include <KComboBox>
0036 #include <KLineEdit>
0037 #include <KServiceTypeTrader>
0038 #include <KRegExpEditorInterface>
0039 #include <KDateComboBox>
0040 
0041 #include <QDialog>
0042 #include <QHBoxLayout>
0043 #include <QStackedWidget>
0044 #include <QPushButton>
0045 
0046 using Tellico::FilterRuleWidget;
0047 
0048 FilterRuleWidget::FilterRuleWidget(Tellico::FilterRule* rule_, QWidget* parent_)
0049     : QWidget(parent_), m_ruleDate(nullptr), m_editRegExp(nullptr), m_editRegExpDialog(nullptr), m_ruleType(General) {
0050   QHBoxLayout* l = new QHBoxLayout(this);
0051   l->setMargin(0);
0052 //  l->setSizeConstraint(QLayout::SetFixedSize);
0053   setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed);
0054 
0055   initLists();
0056   initWidget();
0057 
0058   if(rule_) {
0059     setRule(rule_);
0060   } else {
0061     reset();
0062   }
0063 }
0064 
0065 void FilterRuleWidget::initLists() {
0066   //---------- initialize list of filter fields
0067   if(m_ruleFieldList.isEmpty()) {
0068     m_ruleFieldList.append(QLatin1Char('<') + i18n("Any Field") + QLatin1Char('>'));
0069     QStringList titles = Data::Document::self()->collection()->fieldTitles();
0070     titles.sort();
0071     m_ruleFieldList += titles;
0072   }
0073 
0074 }
0075 
0076 void FilterRuleWidget::initWidget() {
0077   m_ruleField = new KComboBox(this);
0078   layout()->addWidget(m_ruleField);
0079   void (KComboBox::* activatedInt)(int) = &KComboBox::activated;
0080   connect(m_ruleField, activatedInt, this, &FilterRuleWidget::signalModified);
0081   connect(m_ruleField, activatedInt, this, &FilterRuleWidget::slotRuleFieldChanged);
0082 
0083   m_ruleFunc = new GUI::ComboBox(this);
0084   layout()->addWidget(m_ruleFunc);
0085   connect(m_ruleFunc, activatedInt, this, &FilterRuleWidget::signalModified);
0086   connect(m_ruleFunc, activatedInt, this, &FilterRuleWidget::slotRuleFunctionChanged);
0087 
0088   m_valueStack = new QStackedWidget(this);
0089   layout()->addWidget(m_valueStack);
0090 
0091   m_ruleValue = new KLineEdit(m_valueStack); //krazy:exclude=qclasses
0092   connect(m_ruleValue, &QLineEdit::textChanged, this, &FilterRuleWidget::signalModified);
0093   m_valueStack->addWidget(m_ruleValue);
0094 
0095   m_ruleDate = new KDateComboBox(m_valueStack);
0096   connect(m_ruleDate, &KDateComboBox::dateChanged, this, &FilterRuleWidget::signalModified);
0097   m_valueStack->addWidget(m_ruleDate);
0098 
0099   if(!KServiceTypeTrader::self()->query(QStringLiteral("KRegExpEditor/KRegExpEditor")).isEmpty()) {
0100     m_editRegExp = new QPushButton(i18n("Edit..."), this);
0101     connect(m_editRegExp, &QAbstractButton::clicked, this, &FilterRuleWidget::slotEditRegExp);
0102   }
0103 
0104   m_ruleField->addItems(m_ruleFieldList);
0105   updateFunctionList();
0106   slotRuleFunctionChanged(m_ruleFunc->currentIndex());
0107 }
0108 
0109 void FilterRuleWidget::slotEditRegExp() {
0110   if(!m_editRegExpDialog) {
0111     m_editRegExpDialog = KServiceTypeTrader::createInstanceFromQuery<QDialog>(QStringLiteral("KRegExpEditor/KRegExpEditor"),
0112                                                                               QString(), this);  //krazy:exclude=qclasses
0113   }
0114 
0115   if(!m_editRegExpDialog) {
0116     myWarning() << "no dialog";
0117     return;
0118   }
0119 
0120   KRegExpEditorInterface* iface = ::qobject_cast<KRegExpEditorInterface*>(m_editRegExpDialog);
0121   if(iface) {
0122     iface->setRegExp(m_ruleValue->text());
0123     if(m_editRegExpDialog->exec() == QDialog::Accepted) {
0124       m_ruleValue->setText(iface->regExp());
0125     }
0126   }
0127 }
0128 
0129 void FilterRuleWidget::slotRuleFieldChanged(int which_) {
0130   Q_UNUSED(which_);
0131   m_ruleType = General;
0132   QString fieldTitle = m_ruleField->currentText();
0133   if(fieldTitle.isEmpty() || fieldTitle[0] == QLatin1Char('<')) {
0134     m_ruleValue->setCompletionObject(nullptr);
0135     updateFunctionList();
0136     return;
0137   }
0138   Data::FieldPtr field = Data::Document::self()->collection()->fieldByTitle(fieldTitle);
0139   if(field && field->hasFlag(Data::Field::AllowCompletion)) {
0140     FieldCompletion* completion = new FieldCompletion(field->hasFlag(Data::Field::AllowMultiple));
0141     completion->setItems(Data::Document::self()->collection()->valuesByFieldName(field->name()));
0142     completion->setIgnoreCase(true);
0143     m_ruleValue->setCompletionObject(completion);
0144     m_ruleValue->setAutoDeleteCompletionObject(true);
0145   } else {
0146     m_ruleValue->setCompletionObject(nullptr);
0147   }
0148 
0149   if(field) {
0150     if(field->type() == Data::Field::Date) {
0151       m_ruleType = Date;
0152     } else if(field->type() == Data::Field::Number || field->type() == Data::Field::Rating) {
0153       m_ruleType = Number;
0154     } else if(field->type() == Data::Field::Image) {
0155       m_ruleType = Image;
0156     }
0157   }
0158   updateFunctionList();
0159 }
0160 
0161 void FilterRuleWidget::slotRuleFunctionChanged(int which_) {
0162   const QVariant data = m_ruleFunc->itemData(which_);
0163   if(m_editRegExp) {
0164     m_editRegExp->setEnabled(data == FilterRule::FuncRegExp ||
0165                              data == FilterRule::FuncNotRegExp);
0166   }
0167 
0168   // don't show the date picker if we're using regular expressions
0169   if(m_ruleType == Date && data != FilterRule::FuncRegExp && data != FilterRule::FuncNotRegExp) {
0170     m_valueStack->setCurrentWidget(m_ruleDate);
0171   } else {
0172     m_valueStack->setCurrentWidget(m_ruleValue);
0173     m_ruleValue->setPlaceholderText(QString());
0174     if(m_ruleType == Number &&
0175       (data != FilterRule::FuncRegExp && data != FilterRule::FuncNotRegExp)) {
0176       m_ruleValue->setValidator(new QIntValidator(this));
0177     } else {
0178       m_ruleValue->setValidator(nullptr);
0179     }
0180   }
0181 }
0182 
0183 void FilterRuleWidget::setRule(const Tellico::FilterRule* rule_) {
0184   if(!rule_) {
0185     reset();
0186     return;
0187   }
0188 
0189   blockSignals(true);
0190 
0191   m_ruleType = General;
0192   if(rule_->fieldName().isEmpty()) {
0193     m_ruleField->setCurrentIndex(0); // "All Fields"
0194   } else {
0195     Data::FieldPtr field = Data::Document::self()->collection()->fieldByName(rule_->fieldName());
0196     if(field && field->type() == Data::Field::Date) {
0197       m_ruleType = Date;
0198       const QDate date = QDate::fromString(rule_->pattern(), Qt::ISODate);
0199       if(date.isValid()) {
0200         m_ruleDate->setDate(date);
0201       }
0202     }
0203     const int idx = m_ruleField->findText(field ? field->title() : QString());
0204     m_ruleField->setCurrentIndex(idx);
0205   }
0206 
0207   // update the rulle fields first, before possible values
0208   slotRuleFieldChanged(m_ruleField->currentIndex());
0209 
0210   //--------------set function and contents
0211   m_ruleFunc->setCurrentData(rule_->function());
0212   m_ruleValue->setText(rule_->pattern());
0213 
0214   slotRuleFunctionChanged(m_ruleFunc->currentIndex());
0215   blockSignals(false);
0216 }
0217 
0218 Tellico::FilterRule* FilterRuleWidget::rule() const {
0219   QString fieldName; // empty string
0220   if(m_ruleField->currentIndex() > 0) { // 0 is "All Fields", field is empty
0221     fieldName = Data::Document::self()->collection()->fieldNameByTitle(m_ruleField->currentText());
0222   }
0223 
0224   QString ruleValue;
0225   if(m_valueStack->currentWidget() == m_ruleDate) {
0226     ruleValue = m_ruleDate->date().toString(Qt::ISODate);
0227   } else {
0228     ruleValue = m_ruleValue->text().trimmed();
0229   }
0230 
0231   return new FilterRule(fieldName, ruleValue,
0232                         static_cast<FilterRule::Function>(m_ruleFunc->currentData().toInt()));
0233 }
0234 
0235 void FilterRuleWidget::reset() {
0236 //  myDebug();
0237   blockSignals(true);
0238 
0239   m_ruleField->setCurrentIndex(0);
0240   m_ruleFunc->setCurrentIndex(0);
0241   m_ruleValue->clear();
0242 
0243   if(m_editRegExp) {
0244     m_editRegExp->setEnabled(false);
0245   }
0246 
0247   blockSignals(false);
0248 }
0249 
0250 void FilterRuleWidget::setFocus() {
0251   m_ruleValue->setFocus();
0252 }
0253 
0254 void FilterRuleWidget::updateFunctionList() {
0255   Q_ASSERT(m_ruleFunc);
0256   const QVariant data = m_ruleFunc->currentData();
0257   m_ruleFunc->clear();
0258   switch(m_ruleType) {
0259     case Date:
0260       m_ruleFunc->addItem(i18n("equals"), FilterRule::FuncEquals);
0261       m_ruleFunc->addItem(i18n("does not equal"), FilterRule::FuncNotEquals);
0262       m_ruleFunc->addItem(i18n("matches regular expression"), FilterRule::FuncRegExp);
0263       m_ruleFunc->addItem(i18n("does not match regular expression"), FilterRule::FuncNotRegExp);
0264       m_ruleFunc->addItem(i18nc("is before a date", "is before"), FilterRule::FuncBefore);
0265       m_ruleFunc->addItem(i18nc("is after a date", "is after"), FilterRule::FuncAfter);
0266       break;
0267     case Number:
0268       m_ruleFunc->addItem(i18n("equals"), FilterRule::FuncEquals);
0269       m_ruleFunc->addItem(i18n("does not equal"), FilterRule::FuncNotEquals);
0270       m_ruleFunc->addItem(i18n("matches regular expression"), FilterRule::FuncRegExp);
0271       m_ruleFunc->addItem(i18n("does not match regular expression"), FilterRule::FuncNotRegExp);
0272       m_ruleFunc->addItem(i18nc("is less than a number", "is less than"), FilterRule::FuncLess);
0273       m_ruleFunc->addItem(i18nc("is greater than a number", "is greater than"), FilterRule::FuncGreater);
0274       break;
0275     case Image:
0276       m_ruleFunc->addItem(i18n("image size equals"), FilterRule::FuncEquals);
0277       m_ruleFunc->addItem(i18n("image size does not equal"), FilterRule::FuncNotEquals);
0278       m_ruleFunc->addItem(i18nc("image size is less than a number", "image size is less than"), FilterRule::FuncLess);
0279       m_ruleFunc->addItem(i18nc("image size is greater than a number", "image size is greater than"), FilterRule::FuncGreater);
0280       break;
0281     case General:
0282       m_ruleFunc->addItem(i18n("contains"), FilterRule::FuncContains);
0283       m_ruleFunc->addItem(i18n("does not contain"), FilterRule::FuncNotContains);
0284       m_ruleFunc->addItem(i18n("equals"), FilterRule::FuncEquals);
0285       m_ruleFunc->addItem(i18n("does not equal"), FilterRule::FuncNotEquals);
0286       m_ruleFunc->addItem(i18n("matches regular expression"), FilterRule::FuncRegExp);
0287       m_ruleFunc->addItem(i18n("does not match regular expression"), FilterRule::FuncNotRegExp);
0288       break;
0289   }
0290   m_ruleFunc->setCurrentData(data);
0291   slotRuleFunctionChanged(m_ruleFunc->currentIndex());
0292 }