Libksieve

sieveconditionwidgetlister.cpp
1 /*
2  SPDX-FileCopyrightText: 2013-2021 Laurent Montel <[email protected]>
3 
4  SPDX-License-Identifier: LGPL-2.0-or-later
5 */
6 
7 #include "sieveconditionwidgetlister.h"
8 #include "autocreatescriptdialog.h"
9 #include "autocreatescriptutil_p.h"
10 #include "commonwidgets/sievehelpbutton.h"
11 #include "libksieve_debug.h"
12 #include "sieveconditions/sievecondition.h"
13 #include "sieveconditions/sieveconditionlist.h"
14 #include "sieveeditorgraphicalmodewidget.h"
15 
16 #include <KLocalizedString>
17 #include <QComboBox>
18 #include <QIcon>
19 #include <QPointer>
20 #include <QPushButton>
21 #include <QUrl>
22 
23 #include "sievescriptdescriptiondialog.h"
24 #include <QGridLayout>
25 #include <QLabel>
26 #include <QWhatsThis>
27 
28 using namespace KSieveUi;
29 
30 static const int MINIMUMCONDITION = 1;
31 static const int MAXIMUMCONDITION = 8;
32 
33 SieveConditionWidget::SieveConditionWidget(SieveEditorGraphicalModeWidget *sieveGraphicalModeWidget, QWidget *parent)
34  : QWidget(parent)
35  , mSieveGraphicalModeWidget(sieveGraphicalModeWidget)
36 {
37  initWidget();
38 }
39 
40 SieveConditionWidget::~SieveConditionWidget()
41 {
42  qDeleteAll(mConditionList);
43  mConditionList.clear();
44 }
45 
46 void SieveConditionWidget::setFilterCondition(QWidget *widget)
47 {
48  if (mLayout->itemAtPosition(1, 3)) {
49  delete mLayout->itemAtPosition(1, 3)->widget();
50  }
51 
52  if (widget) {
53  mLayout->addWidget(widget, 1, 3);
54  } else {
55  mLayout->addWidget(new QLabel(i18n("Please select an condition."), this), 1, 3);
56  }
57 }
58 
59 void SieveConditionWidget::generatedScript(QString &script, QStringList &required, bool inForEveryPartLoop)
60 {
61  Q_UNUSED(inForEveryPartLoop)
62  const int index = mComboBox->currentIndex();
63  if (index != mComboBox->count() - 1) {
64  KSieveUi::SieveCondition *widgetCondition = mConditionList.at(mComboBox->currentIndex());
65  QWidget *currentWidget = mLayout->itemAtPosition(1, 3)->widget();
66  const QStringList lstRequires = widgetCondition->needRequires(currentWidget);
67  for (const QString &r : lstRequires) {
68  if (!required.contains(r)) {
69  required.append(r);
70  }
71  }
72  script += mConditionList.at(mComboBox->currentIndex())->code(currentWidget) + QLatin1Char('\n');
73  }
74 }
75 
76 void SieveConditionWidget::initWidget()
77 {
78  mLayout = new QGridLayout(this);
79  mLayout->setContentsMargins({});
80 
81  mComboBox = new QComboBox;
82  mComboBox->setMinimumWidth(50);
83  mComboBox->setEditable(false);
84 
85  const QList<KSieveUi::SieveCondition *> list = KSieveUi::SieveConditionList::conditionList(mSieveGraphicalModeWidget);
88  int index = 0;
89  for (index = 0, it = list.constBegin(); it != end; ++it, ++index) {
90  if ((*it)->needCheckIfServerHasCapability()) {
91  if (mSieveGraphicalModeWidget->sieveCapabilities().contains((*it)->serverNeedsCapability())) {
92  // append to the list of actions:
93  mConditionList.append(*it);
94  connect(*it, &SieveCondition::valueChanged, this, &SieveConditionWidget::valueChanged);
95  // add (i18n-ized) name to combo box
96  mComboBox->addItem((*it)->label(), (*it)->name());
97  } else {
98  delete (*it);
99  }
100  } else {
101  // append to the list of actions:
102  mConditionList.append(*it);
103  connect(*it, &SieveCondition::valueChanged, this, &SieveConditionWidget::valueChanged);
104  // add (i18n-ized) name to combo box
105  mComboBox->addItem((*it)->label(), (*it)->name());
106  }
107  }
108 
109  mHelpButton = new SieveHelpButton(this);
110  mLayout->addWidget(mHelpButton, 1, 0);
111  connect(mHelpButton, &SieveHelpButton::clicked, this, &SieveConditionWidget::slotHelp);
112 
113  mCommentButton = new QToolButton(this);
114  mCommentButton->setToolTip(i18n("Add comment"));
115  mLayout->addWidget(mCommentButton, 1, 1);
116  mCommentButton->setIcon(QIcon::fromTheme(QStringLiteral("view-pim-notes")));
117  connect(mCommentButton, &QToolButton::clicked, this, &SieveConditionWidget::slotAddComment);
118 
119  mComboBox->addItem(QLatin1String(""));
120  mLayout->addWidget(mComboBox, 1, 2);
121  connect(mComboBox, QOverload<int>::of(&QComboBox::activated), this, &SieveConditionWidget::slotConditionChanged);
122 
123  mComboBox->setMaxCount(mComboBox->count());
124  mComboBox->setSizePolicy(QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed));
126  mComboBox->adjustSize();
127 
128  mAdd = new QPushButton(this);
129  mAdd->setIcon(QIcon::fromTheme(QStringLiteral("list-add")));
130  mAdd->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed));
131 
132  mRemove = new QPushButton(this);
133  mRemove->setIcon(QIcon::fromTheme(QStringLiteral("list-remove")));
134  mRemove->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed));
135 
136  mLayout->addWidget(mAdd, 1, 4);
137  mLayout->addWidget(mRemove, 1, 5);
138 
139  // redirect focus to the filter action combo box
140  setFocusProxy(mComboBox);
141 
142  connect(mAdd, &QPushButton::clicked, this, &SieveConditionWidget::slotAddWidget);
143  connect(mRemove, &QPushButton::clicked, this, &SieveConditionWidget::slotRemoveWidget);
144 
145  clear();
146 }
147 
148 void SieveConditionWidget::slotAddComment()
149 {
150  const int index = mComboBox->currentIndex();
151  if (index < mConditionList.count()) {
152  KSieveUi::SieveCondition *condition = mConditionList.at(index);
153  const QString comment = condition->comment();
154  QPointer<SieveScriptDescriptionDialog> dlg = new SieveScriptDescriptionDialog;
155  dlg->setDescription(comment);
156  if (dlg->exec()) {
157  condition->setComment(dlg->description());
158  Q_EMIT valueChanged();
159  }
160  delete dlg;
161  }
162 }
163 
164 void SieveConditionWidget::slotHelp()
165 {
166  const int index = mComboBox->currentIndex();
167  if (index < mConditionList.count()) {
168  KSieveUi::SieveCondition *condition = mConditionList.at(index);
169  const QString help = condition->help();
170  const QUrl href = condition->href();
171  const QString fullWhatsThis = AutoCreateScriptUtil::createFullWhatsThis(help, href.toString());
172  QWhatsThis::showText(QCursor::pos(), fullWhatsThis, mHelpButton);
173  }
174 }
175 
176 void SieveConditionWidget::slotConditionChanged(int index)
177 {
178  if (index < mConditionList.count()) {
179  KSieveUi::SieveCondition *condition = mConditionList.at(index);
180  mHelpButton->setEnabled(!condition->help().isEmpty());
181  setFilterCondition(condition->createParamWidget(this));
182  mCommentButton->setEnabled(true);
183  } else {
184  setFilterCondition(nullptr);
185  mHelpButton->setEnabled(false);
186  mCommentButton->setEnabled(false);
187  }
188  Q_EMIT valueChanged();
189 }
190 
191 void SieveConditionWidget::slotAddWidget()
192 {
193  Q_EMIT addWidget(this);
194  Q_EMIT valueChanged();
195 }
196 
197 void SieveConditionWidget::slotRemoveWidget()
198 {
199  Q_EMIT removeWidget(this);
200  Q_EMIT valueChanged();
201 }
202 
203 void SieveConditionWidget::clear()
204 {
205  mComboBox->setCurrentIndex(mComboBox->count() - 1);
206  setFilterCondition(nullptr);
207  mHelpButton->setEnabled(false);
208  mCommentButton->setEnabled(false);
209 }
210 
211 void SieveConditionWidget::updateAddRemoveButton(bool addButtonEnabled, bool removeButtonEnabled)
212 {
213  mAdd->setEnabled(addButtonEnabled);
214  mRemove->setEnabled(removeButtonEnabled);
215 }
216 
217 void SieveConditionWidget::setCondition(const QString &conditionName, QXmlStreamReader &element, bool notCondition, QString &error)
218 {
219  const int index = mComboBox->findData(conditionName);
220  if (index != -1) {
221  mComboBox->setCurrentIndex(index);
222  slotConditionChanged(index);
223  KSieveUi::SieveCondition *condition = mConditionList.at(index);
224  condition->setParamWidgetValue(element, this, notCondition, error);
225  } else {
226  error += i18n("Script contains unsupported feature \"%1\"", conditionName) + QLatin1Char('\n');
227  qCDebug(LIBKSIEVE_LOG) << "Condition " << conditionName << " not supported";
228  element.skipCurrentElement();
229  }
230 }
231 
232 SieveConditionWidgetLister::SieveConditionWidgetLister(SieveEditorGraphicalModeWidget *sieveGraphicalModeWidget, QWidget *parent)
233  : KPIM::KWidgetLister(false, MINIMUMCONDITION, MAXIMUMCONDITION, parent)
234  , mSieveGraphicalModeWidget(sieveGraphicalModeWidget)
235 {
236  slotClear();
237  updateAddRemoveButton();
238 }
239 
240 SieveConditionWidgetLister::~SieveConditionWidgetLister()
241 {
242 }
243 
244 void SieveConditionWidgetLister::slotAddWidget(QWidget *w)
245 {
246  addWidgetAfterThisWidget(w);
247  updateAddRemoveButton();
248 }
249 
250 void SieveConditionWidgetLister::slotRemoveWidget(QWidget *w)
251 {
252  removeWidget(w);
253  updateAddRemoveButton();
254 }
255 
256 void SieveConditionWidgetLister::updateAddRemoveButton()
257 {
258  QList<QWidget *> widgetList = widgets();
259  const int numberOfWidget(widgetList.count());
260  bool addButtonEnabled = false;
261  bool removeButtonEnabled = false;
262  if (numberOfWidget <= widgetsMinimum()) {
263  addButtonEnabled = true;
264  removeButtonEnabled = false;
265  } else if (numberOfWidget >= widgetsMaximum()) {
266  addButtonEnabled = false;
267  removeButtonEnabled = true;
268  } else {
269  addButtonEnabled = true;
270  removeButtonEnabled = true;
271  }
272  QList<QWidget *>::ConstIterator wIt = widgetList.constBegin();
273  QList<QWidget *>::ConstIterator wEnd = widgetList.constEnd();
274  for (; wIt != wEnd; ++wIt) {
275  auto w = qobject_cast<SieveConditionWidget *>(*wIt);
276  w->updateAddRemoveButton(addButtonEnabled, removeButtonEnabled);
277  }
278 }
279 
280 void SieveConditionWidgetLister::reconnectWidget(SieveConditionWidget *w)
281 {
282  connect(w, &SieveConditionWidget::addWidget, this, &SieveConditionWidgetLister::slotAddWidget, Qt::UniqueConnection);
283  connect(w, &SieveConditionWidget::removeWidget, this, &SieveConditionWidgetLister::slotRemoveWidget, Qt::UniqueConnection);
284  connect(w, &SieveConditionWidget::valueChanged, this, &SieveConditionWidgetLister::valueChanged, Qt::UniqueConnection);
285 }
286 
287 void SieveConditionWidgetLister::clearWidget(QWidget *aWidget)
288 {
289  if (aWidget) {
290  auto widget = static_cast<SieveConditionWidget *>(aWidget);
291  widget->clear();
292  }
293  Q_EMIT valueChanged();
294 }
295 
296 QWidget *SieveConditionWidgetLister::createWidget(QWidget *parent)
297 {
298  auto w = new SieveConditionWidget(mSieveGraphicalModeWidget, parent);
299  reconnectWidget(w);
300  return w;
301 }
302 
303 void SieveConditionWidgetLister::generatedScript(QString &script, int &numberOfCondition, QStringList &requireModules, bool inForEveryPartLoop)
304 {
305  const QList<QWidget *> widgetList = widgets();
306  QList<QWidget *>::ConstIterator wIt = widgetList.constBegin();
307  QList<QWidget *>::ConstIterator wEnd = widgetList.constEnd();
308  bool wasFirst = true;
309  for (; wIt != wEnd; ++wIt) {
310  QString condition;
311  auto w = qobject_cast<SieveConditionWidget *>(*wIt);
312  w->generatedScript(condition, requireModules, inForEveryPartLoop);
313  if (!condition.isEmpty()) {
314  if (!wasFirst) {
315  if (inForEveryPartLoop) {
316  script += AutoCreateScriptUtil::indentation();
317  }
318  script += QLatin1String(", ");
319  }
320  script += condition;
321  wasFirst = false;
322  ++numberOfCondition;
323  }
324  }
325 }
326 
327 int SieveConditionWidgetLister::conditionNumber() const
328 {
329  return widgets().count();
330 }
331 
332 void SieveConditionWidgetLister::loadTest(QXmlStreamReader &element, bool notCondition, QString &error)
333 {
334  if (notCondition) {
335  element.readNextStartElement();
336  }
337  if (element.attributes().hasAttribute(QLatin1String("name"))) {
338  const QString conditionName = element.attributes().value(QLatin1String("name")).toString();
339  auto w = qobject_cast<SieveConditionWidget *>(widgets().constLast());
340  w->setCondition(conditionName, element, notCondition, error);
341  }
342  if (notCondition) {
343  element.skipCurrentElement();
344  }
345 }
346 
347 void SieveConditionWidgetLister::loadScript(QXmlStreamReader &element, bool uniqTest, bool notCondition, QString &error)
348 {
349  if (uniqTest) {
350  loadTest(element, notCondition, error);
351  } else {
352  bool firstCondition = true;
353  if (notCondition) {
354  element.readNextStartElement();
355  }
356  while (element.readNextStartElement()) {
357  const QStringRef tagName = element.name();
358  if (tagName == QLatin1String("testlist")) {
359  while (element.readNextStartElement()) {
360  const QStringRef testTagName = element.name();
361  if (testTagName == QLatin1String("test")) {
362  if (element.attributes().hasAttribute(QLatin1String("name"))) {
363  QString conditionName = element.attributes().value(QLatin1String("name")).toString();
364  if (firstCondition) {
365  firstCondition = false;
366  } else {
367  addWidgetAfterThisWidget(widgets().constLast());
368  }
369  auto w = qobject_cast<SieveConditionWidget *>(widgets().constLast());
370  if (conditionName == QLatin1String("not")) {
371  notCondition = true;
372  element.readNextStartElement();
373  if (element.attributes().hasAttribute(QLatin1String("name"))) {
374  conditionName = element.attributes().value(QLatin1String("name")).toString();
375  }
376  w->setCondition(conditionName, element, notCondition, error);
377  element.skipCurrentElement();
378  } else {
379  notCondition = false;
380  w->setCondition(conditionName, element, notCondition, error);
381  }
382  }
383  } else if (testTagName == QLatin1String("crlf")) {
384  element.skipCurrentElement();
385  // nothing
386  } else if (testTagName == QLatin1String("comment")) {
387  qDebug() << "Need to implement comment here ";
388  element.skipCurrentElement();
389  // nothing
390  // implement in the future ?
391  } else {
392  qCDebug(LIBKSIEVE_LOG) << " SieveConditionWidgetLister::loadScript unknown condition tag: " << testTagName;
393  }
394  }
395  }
396  }
397  }
398 }
void showText(const QPoint &pos, const QString &text, QWidget *w)
bool readNextStartElement()
QString toString() const const
void setMinimumWidth(int minw)
const T & at(int i) const const
bool contains(const QString &str, Qt::CaseSensitivity cs) const const
QStringRef value(const QString &namespaceUri, const QString &name) const const
QString toString(QUrl::FormattingOptions options) const const
void skipCurrentElement()
int count(const T &value) const const
void append(const T &value)
const QList< QKeySequence > & help()
bool isEmpty() const const
void clicked(bool checked)
void activated(int index)
bool hasAttribute(const QString &qualifiedName) const const
QString i18n(const char *text, const TYPE &arg...)
const QList< QKeySequence > & end()
QPoint pos()
int count() const const
const QChar at(int position) const const
QXmlStreamAttributes attributes() const const
QIcon fromTheme(const QString &name)
QList::const_iterator constEnd() const const
QList::const_iterator constBegin() const const
UniqueConnection
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
T qobject_cast(QObject *object)
QObject * parent() const const
QStringRef name() const const
Q_EMITQ_EMIT
KIOFILEWIDGETS_EXPORT QStringList list(const QString &fileClass)
This file is part of the KDE documentation.
Documentation copyright © 1996-2021 The KDE developers.
Generated on Sun Apr 11 2021 23:09:36 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.