Mailcommon

tagrulewidgethandler.cpp
1 /*
2  SPDX-FileCopyrightText: 2013-2023 Laurent Montel <[email protected]>
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>
24 using namespace MailCommon;
25 
26 class FillTagComboJob : public KJob
27 {
28  Q_OBJECT
29 public:
30  explicit FillTagComboJob(QComboBox *combo, QObject *parent = nullptr);
31  void start() override;
32 
33 private:
34  void onDestroyed();
35  void onTagsFetched(KJob *);
36 
37 private:
38  QComboBox *mComboBox = nullptr;
39 };
40 
41 FillTagComboJob::FillTagComboJob(QComboBox *combo, QObject *parent)
42  : KJob(parent)
43  , mComboBox(combo)
44 {
45  connect(combo, &QObject::destroyed, this, &FillTagComboJob::onDestroyed);
46 }
47 
48 void FillTagComboJob::onDestroyed()
49 {
50  mComboBox = nullptr;
51  setError(KJob::UserDefinedError);
52  qCDebug(MAILCOMMON_LOG) << "Combobox destroyed";
53  emitResult();
54 }
55 
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 
63 void 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 
90 static 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.")}};
99 static const int TagFunctionCount = sizeof(TagFunctions) / sizeof(*TagFunctions);
100 
101 //---------------------------------------------------------------------------
102 
103 QWidget *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(QStringLiteral("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 
128 QWidget *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(QStringLiteral("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(QStringLiteral("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 
160 SearchRule::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 
176 QString 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 
206 QString TagRuleWidgetHandler::prettyValue(const QByteArray &field, const QStackedWidget *funcStack, const QStackedWidget *valueStack) const
207 {
208  return value(field, funcStack, valueStack);
209 }
210 
211 //---------------------------------------------------------------------------
212 
213 bool TagRuleWidgetHandler::handlesField(const QByteArray &field) const
214 {
215  return field == "<tag>";
216 }
217 
218 //---------------------------------------------------------------------------
219 
220 void 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 
253 bool 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 
335 bool 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"
void setCurrentWidget(QWidget *widget)
QString name() const const
std::shared_ptr< SearchRule > Ptr
Defines a pointer to a search rule.
Definition: searchrule.h:29
virtual Q_SCRIPTABLE void start()=0
void result(KJob *job)
QIcon fromTheme(const QString &name)
Tag::List tags() const
virtual void setText(const QString &)
void setClearButtonEnabled(bool enable)
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
QString toString() const
void destroyed(QObject *obj)
void clear()
Q_SCRIPTABLE Q_NOREPLY void start()
bool blockSignals(bool block)
Function
Describes operators for comparison of field and contents.
Definition: searchrule.h:40
T findChild(const QString &name, Qt::FindChildOptions options) const const
QVariant itemData(int index, int role) const const
KGuiItem reset()
virtual QString errorString() const
int error() const
char * toString(const EngineQuery &query)
The filter dialog.
This file is part of the KDE documentation.
Documentation copyright © 1996-2023 The KDE developers.
Generated on Fri Sep 29 2023 03:57:42 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.