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"