Mailcommon

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

KDE's Doxygen guidelines are available online.