File indexing completed on 2024-11-17 04:51:15

0001 /*
0002   SPDX-FileCopyrightText: 2013-2024 Laurent Montel <montel@kde.org>
0003 
0004   SPDX-License-Identifier: GPL-2.0-or-later
0005 */
0006 
0007 #include "tagrulewidgethandler.h"
0008 #include "mailcommon_debug.h"
0009 
0010 #include <KJob>
0011 #include <KLineEdit>
0012 #include <KLocalizedString>
0013 #include <QIcon>
0014 
0015 #include <Akonadi/Tag>
0016 #include <Akonadi/TagAttribute>
0017 #include <Akonadi/TagFetchJob>
0018 #include <Akonadi/TagFetchScope>
0019 
0020 #include <KLazyLocalizedString>
0021 #include <QComboBox>
0022 #include <QLineEdit>
0023 #include <QStackedWidget>
0024 using namespace MailCommon;
0025 
0026 class FillTagComboJob : public KJob
0027 {
0028     Q_OBJECT
0029 public:
0030     explicit FillTagComboJob(QComboBox *combo, QObject *parent = nullptr);
0031     void start() override;
0032 
0033 private:
0034     void onDestroyed();
0035     void onTagsFetched(KJob *);
0036 
0037 private:
0038     QComboBox *mComboBox = nullptr;
0039 };
0040 
0041 FillTagComboJob::FillTagComboJob(QComboBox *combo, QObject *parent)
0042     : KJob(parent)
0043     , mComboBox(combo)
0044 {
0045     connect(combo, &QObject::destroyed, this, &FillTagComboJob::onDestroyed);
0046 }
0047 
0048 void FillTagComboJob::onDestroyed()
0049 {
0050     mComboBox = nullptr;
0051     setError(KJob::UserDefinedError);
0052     qCDebug(MAILCOMMON_LOG) << "Combobox destroyed";
0053     emitResult();
0054 }
0055 
0056 void FillTagComboJob::start()
0057 {
0058     auto fetchJob = new Akonadi::TagFetchJob(this);
0059     fetchJob->fetchScope().fetchAttribute<Akonadi::TagAttribute>();
0060     connect(fetchJob, &Akonadi::TagFetchJob::result, this, &FillTagComboJob::onTagsFetched);
0061 }
0062 
0063 void FillTagComboJob::onTagsFetched(KJob *job)
0064 {
0065     if (job->error()) {
0066         qCWarning(MAILCOMMON_LOG) << job->errorString();
0067         setError(KJob::UserDefinedError);
0068         emitResult();
0069     }
0070     if (!mComboBox) {
0071         qCDebug(MAILCOMMON_LOG) << "combobox already destroyed";
0072         emitResult();
0073         return;
0074     }
0075     auto fetchJob = static_cast<Akonadi::TagFetchJob *>(job);
0076     const auto lst = fetchJob->tags();
0077     for (const Akonadi::Tag &tag : lst) {
0078         QString iconName = QStringLiteral("mail-tagged");
0079         const auto attr = tag.attribute<Akonadi::TagAttribute>();
0080         if (attr) {
0081             if (!attr->iconName().isEmpty()) {
0082                 iconName = attr->iconName();
0083             }
0084         }
0085         mComboBox->addItem(QIcon::fromTheme(iconName), tag.name(), tag.url().url());
0086     }
0087     emitResult();
0088 }
0089 
0090 static const struct {
0091     SearchRule::Function id;
0092     const KLazyLocalizedString displayName;
0093 } TagFunctions[] = {{SearchRule::FuncContains, kli18n("contains")},
0094                     {SearchRule::FuncContainsNot, kli18n("does not contain")},
0095                     {SearchRule::FuncEquals, kli18n("equals")},
0096                     {SearchRule::FuncNotEqual, kli18n("does not equal")},
0097                     {SearchRule::FuncRegExp, kli18n("matches regular expr.")},
0098                     {SearchRule::FuncNotRegExp, kli18n("does not match reg. expr.")}};
0099 static const int TagFunctionCount = sizeof(TagFunctions) / sizeof(*TagFunctions);
0100 
0101 //---------------------------------------------------------------------------
0102 
0103 QWidget *TagRuleWidgetHandler::createFunctionWidget(int number, QStackedWidget *functionStack, const QObject *receiver, bool isBalooSearch) const
0104 {
0105     if (number != 0) {
0106         return nullptr;
0107     }
0108 
0109     const auto funcCombo = new QComboBox(functionStack);
0110     funcCombo->setMinimumWidth(50);
0111     funcCombo->setObjectName(QLatin1StringView("tagRuleFuncCombo"));
0112     for (int i = 0; i < TagFunctionCount; ++i) {
0113         if (isBalooSearch) {
0114             if (TagFunctions[i].id == SearchRule::FuncContains || TagFunctions[i].id == SearchRule::FuncContainsNot) {
0115                 funcCombo->addItem(TagFunctions[i].displayName.toString());
0116             }
0117         } else {
0118             funcCombo->addItem(TagFunctions[i].displayName.toString());
0119         }
0120     }
0121     funcCombo->adjustSize();
0122     QObject::connect(funcCombo, SIGNAL(activated(int)), receiver, SLOT(slotFunctionChanged()));
0123     return funcCombo;
0124 }
0125 
0126 //---------------------------------------------------------------------------
0127 
0128 QWidget *TagRuleWidgetHandler::createValueWidget(int number, QStackedWidget *valueStack, const QObject *receiver) const
0129 {
0130     if (number == 0) {
0131         auto lineEdit = new KLineEdit(valueStack);
0132         lineEdit->setClearButtonEnabled(true);
0133         lineEdit->setTrapReturnKey(true);
0134         lineEdit->setObjectName(QLatin1StringView("tagRuleRegExpLineEdit"));
0135         QObject::connect(lineEdit, SIGNAL(textChanged(QString)), receiver, SLOT(slotValueChanged()));
0136         QObject::connect(lineEdit, SIGNAL(returnPressed()), receiver, SLOT(slotReturnPressed()));
0137         return lineEdit;
0138     }
0139 
0140     if (number == 1) {
0141         const auto valueCombo = new QComboBox(valueStack);
0142         valueCombo->setMinimumWidth(50);
0143         valueCombo->setObjectName(QLatin1StringView("tagRuleValueCombo"));
0144         valueCombo->setEditable(true);
0145         valueCombo->addItem(QString()); // empty entry for user input
0146 
0147         auto job = new FillTagComboJob(valueCombo);
0148         job->start();
0149 
0150         valueCombo->adjustSize();
0151         QObject::connect(valueCombo, SIGNAL(activated(int)), receiver, SLOT(slotValueChanged()));
0152         return valueCombo;
0153     }
0154 
0155     return nullptr;
0156 }
0157 
0158 //---------------------------------------------------------------------------
0159 
0160 SearchRule::Function TagRuleWidgetHandler::function(const QByteArray &field, const QStackedWidget *functionStack) const
0161 {
0162     if (!handlesField(field)) {
0163         return SearchRule::FuncNone;
0164     }
0165 
0166     const auto funcCombo = functionStack->findChild<QComboBox *>(QStringLiteral("tagRuleFuncCombo"));
0167 
0168     if (funcCombo && funcCombo->currentIndex() >= 0) {
0169         return TagFunctions[funcCombo->currentIndex()].id;
0170     }
0171     return SearchRule::FuncNone;
0172 }
0173 
0174 //---------------------------------------------------------------------------
0175 
0176 QString TagRuleWidgetHandler::value(const QByteArray &field, const QStackedWidget *functionStack, const QStackedWidget *valueStack) const
0177 {
0178     if (!handlesField(field)) {
0179         return {};
0180     }
0181 
0182     SearchRule::Function func = function(field, functionStack);
0183     if (func == SearchRule::FuncRegExp || func == SearchRule::FuncNotRegExp) {
0184         // Use regexp line edit
0185         const KLineEdit *lineEdit = valueStack->findChild<KLineEdit *>(QStringLiteral("tagRuleRegExpLineEdit"));
0186 
0187         if (lineEdit) {
0188             return lineEdit->text();
0189         } else {
0190             return {};
0191         }
0192     }
0193 
0194     // Use combo box
0195     const auto tagCombo = valueStack->findChild<QComboBox *>(QStringLiteral("tagRuleValueCombo"));
0196 
0197     if (tagCombo) {
0198         return tagCombo->itemData(tagCombo->currentIndex()).toString();
0199     } else {
0200         return {};
0201     }
0202 }
0203 
0204 //---------------------------------------------------------------------------
0205 
0206 QString TagRuleWidgetHandler::prettyValue(const QByteArray &field, const QStackedWidget *funcStack, const QStackedWidget *valueStack) const
0207 {
0208     return value(field, funcStack, valueStack);
0209 }
0210 
0211 //---------------------------------------------------------------------------
0212 
0213 bool TagRuleWidgetHandler::handlesField(const QByteArray &field) const
0214 {
0215     return field == "<tag>";
0216 }
0217 
0218 //---------------------------------------------------------------------------
0219 
0220 void TagRuleWidgetHandler::reset(QStackedWidget *functionStack, QStackedWidget *valueStack) const
0221 {
0222     // reset the function combo box
0223     const auto funcCombo = functionStack->findChild<QComboBox *>(QStringLiteral("tagRuleFuncCombo"));
0224 
0225     if (funcCombo) {
0226         funcCombo->blockSignals(true);
0227         funcCombo->setCurrentIndex(0);
0228         funcCombo->blockSignals(false);
0229     }
0230 
0231     // reset the status value combo box and reg exp line edit
0232     auto lineEdit = valueStack->findChild<KLineEdit *>(QStringLiteral("tagRuleRegExpLineEdit"));
0233 
0234     if (lineEdit) {
0235         lineEdit->blockSignals(true);
0236         lineEdit->clear();
0237         lineEdit->blockSignals(false);
0238         lineEdit->setClearButtonEnabled(false);
0239         lineEdit->setClearButtonEnabled(true);
0240         valueStack->setCurrentWidget(lineEdit);
0241     }
0242 
0243     const auto tagCombo = valueStack->findChild<QComboBox *>(QStringLiteral("tagRuleValueCombo"));
0244     if (tagCombo) {
0245         tagCombo->blockSignals(true);
0246         tagCombo->setCurrentIndex(0);
0247         tagCombo->blockSignals(false);
0248     }
0249 }
0250 
0251 //---------------------------------------------------------------------------
0252 
0253 bool TagRuleWidgetHandler::setRule(QStackedWidget *functionStack, QStackedWidget *valueStack, const SearchRule::Ptr rule, bool isBalooSearch) const
0254 {
0255     if (!rule || !handlesField(rule->field())) {
0256         reset(functionStack, valueStack);
0257         return false;
0258     }
0259 
0260     // set the function
0261     const SearchRule::Function func = rule->function();
0262 
0263     if (isBalooSearch) {
0264         if (func != SearchRule::FuncContains && func != SearchRule::FuncContainsNot) {
0265             reset(functionStack, valueStack);
0266             return false;
0267         }
0268     }
0269 
0270     int funcIndex = 0;
0271     for (; funcIndex < TagFunctionCount; ++funcIndex) {
0272         if (func == TagFunctions[funcIndex].id) {
0273             break;
0274         }
0275     }
0276 
0277     const auto funcCombo = functionStack->findChild<QComboBox *>(QStringLiteral("tagRuleFuncCombo"));
0278 
0279     if (funcCombo) {
0280         funcCombo->blockSignals(true);
0281         if (funcIndex < TagFunctionCount) {
0282             funcCombo->setCurrentIndex(funcIndex);
0283         } else {
0284             funcCombo->setCurrentIndex(0);
0285         }
0286         funcCombo->blockSignals(false);
0287         functionStack->setCurrentWidget(funcCombo);
0288     }
0289 
0290     // set the value
0291     if (func == SearchRule::FuncRegExp || func == SearchRule::FuncNotRegExp) {
0292         // set reg exp value
0293         auto lineEdit = valueStack->findChild<KLineEdit *>(QStringLiteral("tagRuleRegExpLineEdit"));
0294 
0295         if (lineEdit) {
0296             lineEdit->blockSignals(true);
0297             lineEdit->setText(rule->contents());
0298             lineEdit->blockSignals(false);
0299             lineEdit->setClearButtonEnabled(false);
0300             lineEdit->setClearButtonEnabled(true);
0301             valueStack->setCurrentWidget(lineEdit);
0302         }
0303     } else {
0304         // set combo box value
0305         const auto tagCombo = valueStack->findChild<QComboBox *>(QStringLiteral("tagRuleValueCombo"));
0306 
0307         if (tagCombo) {
0308             tagCombo->blockSignals(true);
0309             bool found = false;
0310             // Existing tags numbered from 1
0311             for (int i = 1; i < tagCombo->count(); i++) {
0312                 if (rule->contents() == tagCombo->itemData(i).toString()) {
0313                     tagCombo->setCurrentIndex(i);
0314                     found = true;
0315                     break;
0316                 }
0317             }
0318             if (!found) {
0319                 tagCombo->setCurrentIndex(0);
0320                 // Still show tag if it was deleted from MsgTagMgr
0321                 QLineEdit *lineEdit = tagCombo->lineEdit(); // krazy:exclude=qclasses
0322                 Q_ASSERT(lineEdit);
0323                 lineEdit->setText(rule->contents());
0324             }
0325 
0326             tagCombo->blockSignals(false);
0327             valueStack->setCurrentWidget(tagCombo);
0328         }
0329     }
0330     return true;
0331 }
0332 
0333 //---------------------------------------------------------------------------
0334 
0335 bool TagRuleWidgetHandler::update(const QByteArray &field, QStackedWidget *functionStack, QStackedWidget *valueStack) const
0336 {
0337     if (!handlesField(field)) {
0338         return false;
0339     }
0340 
0341     // raise the correct function widget
0342     functionStack->setCurrentWidget(functionStack->findChild<QWidget *>(QStringLiteral("tagRuleFuncCombo")));
0343 
0344     // raise the correct value widget
0345     SearchRule::Function func = function(field, functionStack);
0346     if (func == SearchRule::FuncRegExp || func == SearchRule::FuncNotRegExp) {
0347         valueStack->setCurrentWidget(valueStack->findChild<QWidget *>(QStringLiteral("tagRuleRegExpLineEdit")));
0348     } else {
0349         valueStack->setCurrentWidget(valueStack->findChild<QWidget *>(QStringLiteral("tagRuleValueCombo")));
0350     }
0351 
0352     return true;
0353 }
0354 
0355 #include "tagrulewidgethandler.moc"