Mailcommon

tagrulewidgethandler.cpp
1/*
2 SPDX-FileCopyrightText: 2013-2024 Laurent Montel <montel@kde.org>
3
4 SPDX-License-Identifier: GPL-2.0-or-later
5*/
6
7#include "tagrulewidgethandler.h"
8#include "mailcommon_debug.h"
9
10#include <KJob>
11#include <KLineEdit>
12#include <KLocalizedString>
13#include <QIcon>
14
15#include <Akonadi/Tag>
16#include <Akonadi/TagAttribute>
17#include <Akonadi/TagFetchJob>
18#include <Akonadi/TagFetchScope>
19
20#include <KLazyLocalizedString>
21#include <QComboBox>
22#include <QLineEdit>
23#include <QStackedWidget>
24using namespace MailCommon;
25
26class FillTagComboJob : public KJob
27{
29public:
30 explicit FillTagComboJob(QComboBox *combo, QObject *parent = nullptr);
31 void start() override;
32
33private:
34 void onDestroyed();
35 void onTagsFetched(KJob *);
36
37private:
38 QComboBox *mComboBox = nullptr;
39};
40
41FillTagComboJob::FillTagComboJob(QComboBox *combo, QObject *parent)
42 : KJob(parent)
43 , mComboBox(combo)
44{
45 connect(combo, &QObject::destroyed, this, &FillTagComboJob::onDestroyed);
46}
47
48void FillTagComboJob::onDestroyed()
49{
50 mComboBox = nullptr;
51 setError(KJob::UserDefinedError);
52 qCDebug(MAILCOMMON_LOG) << "Combobox destroyed";
53 emitResult();
54}
55
56void FillTagComboJob::start()
57{
58 auto fetchJob = new Akonadi::TagFetchJob(this);
59 fetchJob->fetchScope().fetchAttribute<Akonadi::TagAttribute>();
60 connect(fetchJob, &Akonadi::TagFetchJob::result, this, &FillTagComboJob::onTagsFetched);
61}
62
63void FillTagComboJob::onTagsFetched(KJob *job)
64{
65 if (job->error()) {
66 qCWarning(MAILCOMMON_LOG) << job->errorString();
67 setError(KJob::UserDefinedError);
68 emitResult();
69 }
70 if (!mComboBox) {
71 qCDebug(MAILCOMMON_LOG) << "combobox already destroyed";
72 emitResult();
73 return;
74 }
75 auto fetchJob = static_cast<Akonadi::TagFetchJob *>(job);
76 const auto lst = fetchJob->tags();
77 for (const Akonadi::Tag &tag : lst) {
78 QString iconName = QStringLiteral("mail-tagged");
79 const auto attr = tag.attribute<Akonadi::TagAttribute>();
80 if (attr) {
81 if (!attr->iconName().isEmpty()) {
82 iconName = attr->iconName();
83 }
84 }
85 mComboBox->addItem(QIcon::fromTheme(iconName), tag.name(), tag.url().url());
86 }
87 emitResult();
88}
89
90static const struct {
92 const KLazyLocalizedString displayName;
93} TagFunctions[] = {{SearchRule::FuncContains, kli18n("contains")},
94 {SearchRule::FuncContainsNot, kli18n("does not contain")},
95 {SearchRule::FuncEquals, kli18n("equals")},
96 {SearchRule::FuncNotEqual, kli18n("does not equal")},
97 {SearchRule::FuncRegExp, kli18n("matches regular expr.")},
98 {SearchRule::FuncNotRegExp, kli18n("does not match reg. expr.")}};
99static const int TagFunctionCount = sizeof(TagFunctions) / sizeof(*TagFunctions);
100
101//---------------------------------------------------------------------------
102
103QWidget *TagRuleWidgetHandler::createFunctionWidget(int number, QStackedWidget *functionStack, const QObject *receiver, bool isBalooSearch) const
104{
105 if (number != 0) {
106 return nullptr;
107 }
108
109 const auto funcCombo = new QComboBox(functionStack);
110 funcCombo->setMinimumWidth(50);
111 funcCombo->setObjectName(QLatin1StringView("tagRuleFuncCombo"));
112 for (int i = 0; i < TagFunctionCount; ++i) {
113 if (isBalooSearch) {
114 if (TagFunctions[i].id == SearchRule::FuncContains || TagFunctions[i].id == SearchRule::FuncContainsNot) {
115 funcCombo->addItem(TagFunctions[i].displayName.toString());
116 }
117 } else {
118 funcCombo->addItem(TagFunctions[i].displayName.toString());
119 }
120 }
121 funcCombo->adjustSize();
122 QObject::connect(funcCombo, SIGNAL(activated(int)), receiver, SLOT(slotFunctionChanged()));
123 return funcCombo;
124}
125
126//---------------------------------------------------------------------------
127
128QWidget *TagRuleWidgetHandler::createValueWidget(int number, QStackedWidget *valueStack, const QObject *receiver) const
129{
130 if (number == 0) {
131 auto lineEdit = new KLineEdit(valueStack);
132 lineEdit->setClearButtonEnabled(true);
133 lineEdit->setTrapReturnKey(true);
134 lineEdit->setObjectName(QLatin1StringView("tagRuleRegExpLineEdit"));
135 QObject::connect(lineEdit, SIGNAL(textChanged(QString)), receiver, SLOT(slotValueChanged()));
136 QObject::connect(lineEdit, SIGNAL(returnPressed()), receiver, SLOT(slotReturnPressed()));
137 return lineEdit;
138 }
139
140 if (number == 1) {
141 const auto valueCombo = new QComboBox(valueStack);
142 valueCombo->setMinimumWidth(50);
143 valueCombo->setObjectName(QLatin1StringView("tagRuleValueCombo"));
144 valueCombo->setEditable(true);
145 valueCombo->addItem(QString()); // empty entry for user input
146
147 auto job = new FillTagComboJob(valueCombo);
148 job->start();
149
150 valueCombo->adjustSize();
151 QObject::connect(valueCombo, SIGNAL(activated(int)), receiver, SLOT(slotValueChanged()));
152 return valueCombo;
153 }
154
155 return nullptr;
156}
157
158//---------------------------------------------------------------------------
159
160SearchRule::Function TagRuleWidgetHandler::function(const QByteArray &field, const QStackedWidget *functionStack) const
161{
162 if (!handlesField(field)) {
163 return SearchRule::FuncNone;
164 }
165
166 const auto funcCombo = functionStack->findChild<QComboBox *>(QStringLiteral("tagRuleFuncCombo"));
167
168 if (funcCombo && funcCombo->currentIndex() >= 0) {
169 return TagFunctions[funcCombo->currentIndex()].id;
170 }
171 return SearchRule::FuncNone;
172}
173
174//---------------------------------------------------------------------------
175
176QString TagRuleWidgetHandler::value(const QByteArray &field, const QStackedWidget *functionStack, const QStackedWidget *valueStack) const
177{
178 if (!handlesField(field)) {
179 return {};
180 }
181
182 SearchRule::Function func = function(field, functionStack);
183 if (func == SearchRule::FuncRegExp || func == SearchRule::FuncNotRegExp) {
184 // Use regexp line edit
185 const KLineEdit *lineEdit = valueStack->findChild<KLineEdit *>(QStringLiteral("tagRuleRegExpLineEdit"));
186
187 if (lineEdit) {
188 return lineEdit->text();
189 } else {
190 return {};
191 }
192 }
193
194 // Use combo box
195 const auto tagCombo = valueStack->findChild<QComboBox *>(QStringLiteral("tagRuleValueCombo"));
196
197 if (tagCombo) {
198 return tagCombo->itemData(tagCombo->currentIndex()).toString();
199 } else {
200 return {};
201 }
202}
203
204//---------------------------------------------------------------------------
205
206QString TagRuleWidgetHandler::prettyValue(const QByteArray &field, const QStackedWidget *funcStack, const QStackedWidget *valueStack) const
207{
208 return value(field, funcStack, valueStack);
209}
210
211//---------------------------------------------------------------------------
212
213bool TagRuleWidgetHandler::handlesField(const QByteArray &field) const
214{
215 return field == "<tag>";
216}
217
218//---------------------------------------------------------------------------
219
220void TagRuleWidgetHandler::reset(QStackedWidget *functionStack, QStackedWidget *valueStack) const
221{
222 // reset the function combo box
223 const auto funcCombo = functionStack->findChild<QComboBox *>(QStringLiteral("tagRuleFuncCombo"));
224
225 if (funcCombo) {
226 funcCombo->blockSignals(true);
227 funcCombo->setCurrentIndex(0);
228 funcCombo->blockSignals(false);
229 }
230
231 // reset the status value combo box and reg exp line edit
232 auto lineEdit = valueStack->findChild<KLineEdit *>(QStringLiteral("tagRuleRegExpLineEdit"));
233
234 if (lineEdit) {
235 lineEdit->blockSignals(true);
236 lineEdit->clear();
237 lineEdit->blockSignals(false);
238 lineEdit->setClearButtonEnabled(false);
239 lineEdit->setClearButtonEnabled(true);
240 valueStack->setCurrentWidget(lineEdit);
241 }
242
243 const auto tagCombo = valueStack->findChild<QComboBox *>(QStringLiteral("tagRuleValueCombo"));
244 if (tagCombo) {
245 tagCombo->blockSignals(true);
246 tagCombo->setCurrentIndex(0);
247 tagCombo->blockSignals(false);
248 }
249}
250
251//---------------------------------------------------------------------------
252
253bool TagRuleWidgetHandler::setRule(QStackedWidget *functionStack, QStackedWidget *valueStack, const SearchRule::Ptr rule, bool isBalooSearch) const
254{
255 if (!rule || !handlesField(rule->field())) {
256 reset(functionStack, valueStack);
257 return false;
258 }
259
260 // set the function
261 const SearchRule::Function func = rule->function();
262
263 if (isBalooSearch) {
264 if (func != SearchRule::FuncContains && func != SearchRule::FuncContainsNot) {
265 reset(functionStack, valueStack);
266 return false;
267 }
268 }
269
270 int funcIndex = 0;
271 for (; funcIndex < TagFunctionCount; ++funcIndex) {
272 if (func == TagFunctions[funcIndex].id) {
273 break;
274 }
275 }
276
277 const auto funcCombo = functionStack->findChild<QComboBox *>(QStringLiteral("tagRuleFuncCombo"));
278
279 if (funcCombo) {
280 funcCombo->blockSignals(true);
281 if (funcIndex < TagFunctionCount) {
282 funcCombo->setCurrentIndex(funcIndex);
283 } else {
284 funcCombo->setCurrentIndex(0);
285 }
286 funcCombo->blockSignals(false);
287 functionStack->setCurrentWidget(funcCombo);
288 }
289
290 // set the value
291 if (func == SearchRule::FuncRegExp || func == SearchRule::FuncNotRegExp) {
292 // set reg exp value
293 auto lineEdit = valueStack->findChild<KLineEdit *>(QStringLiteral("tagRuleRegExpLineEdit"));
294
295 if (lineEdit) {
296 lineEdit->blockSignals(true);
297 lineEdit->setText(rule->contents());
298 lineEdit->blockSignals(false);
299 lineEdit->setClearButtonEnabled(false);
300 lineEdit->setClearButtonEnabled(true);
301 valueStack->setCurrentWidget(lineEdit);
302 }
303 } else {
304 // set combo box value
305 const auto tagCombo = valueStack->findChild<QComboBox *>(QStringLiteral("tagRuleValueCombo"));
306
307 if (tagCombo) {
308 tagCombo->blockSignals(true);
309 bool found = false;
310 // Existing tags numbered from 1
311 for (int i = 1; i < tagCombo->count(); i++) {
312 if (rule->contents() == tagCombo->itemData(i).toString()) {
313 tagCombo->setCurrentIndex(i);
314 found = true;
315 break;
316 }
317 }
318 if (!found) {
319 tagCombo->setCurrentIndex(0);
320 // Still show tag if it was deleted from MsgTagMgr
321 QLineEdit *lineEdit = tagCombo->lineEdit(); // krazy:exclude=qclasses
322 Q_ASSERT(lineEdit);
323 lineEdit->setText(rule->contents());
324 }
325
326 tagCombo->blockSignals(false);
327 valueStack->setCurrentWidget(tagCombo);
328 }
329 }
330 return true;
331}
332
333//---------------------------------------------------------------------------
334
335bool TagRuleWidgetHandler::update(const QByteArray &field, QStackedWidget *functionStack, QStackedWidget *valueStack) const
336{
337 if (!handlesField(field)) {
338 return false;
339 }
340
341 // raise the correct function widget
342 functionStack->setCurrentWidget(functionStack->findChild<QWidget *>(QStringLiteral("tagRuleFuncCombo")));
343
344 // raise the correct value widget
345 SearchRule::Function func = function(field, functionStack);
346 if (func == SearchRule::FuncRegExp || func == SearchRule::FuncNotRegExp) {
347 valueStack->setCurrentWidget(valueStack->findChild<QWidget *>(QStringLiteral("tagRuleRegExpLineEdit")));
348 } else {
349 valueStack->setCurrentWidget(valueStack->findChild<QWidget *>(QStringLiteral("tagRuleValueCombo")));
350 }
351
352 return true;
353}
354
355#include "tagrulewidgethandler.moc"
Tag::List tags() const
virtual QString errorString() const
void emitResult()
int error() const
void result(KJob *job)
void setError(int errorCode)
virtual Q_SCRIPTABLE void start()=0
QString toString() const
virtual void setText(const QString &)
std::shared_ptr< SearchRule > Ptr
Defines a pointer to a search rule.
Definition searchrule.h:29
Function
Describes operators for comparison of field and contents.
Definition searchrule.h:40
char * toString(const EngineQuery &query)
The filter dialog.
void addItem(const QIcon &icon, const QString &text, const QVariant &userData)
QVariant itemData(int index, int role) const const
QIcon fromTheme(const QString &name)
QString name() const const
void clear()
void setClearButtonEnabled(bool enable)
Q_OBJECTQ_OBJECT
bool blockSignals(bool block)
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
void destroyed(QObject *obj)
T findChild(const QString &name, Qt::FindChildOptions options) const const
QObject * parent() const const
void setCurrentWidget(QWidget *widget)
QFuture< ArgsType< Signal > > connect(Sender *sender, Signal signal)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Tue Mar 26 2024 11:14:01 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.