Libksieve

sieveactionwidgetlister.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 "sieveactionwidgetlister.h"
8 #include "autocreatescriptdialog.h"
9 #include "autocreatescriptutil_p.h"
10 #include "commonwidgets/sievehelpbutton.h"
11 #include "sieveactions/sieveaction.h"
12 #include "sieveactions/sieveactionlist.h"
13 #include "sieveeditorgraphicalmodewidget.h"
14 #include "sievescriptdescriptiondialog.h"
15 
16 #include <KLocalizedString>
17 #include <QIcon>
18 #include <QPushButton>
19 #include <QUrl>
20 
21 #include "libksieve_debug.h"
22 #include <QComboBox>
23 #include <QGridLayout>
24 #include <QLabel>
25 #include <QPointer>
26 #include <QToolButton>
27 #include <QWhatsThis>
28 
29 #include <autocreatescripts/sieveactions/sieveactionsetvariable.h>
30 
31 using namespace KSieveUi;
32 
33 static int MINIMUMACTION = 1;
34 static int MAXIMUMACTION = 8;
35 
36 SieveActionWidget::SieveActionWidget(SieveEditorGraphicalModeWidget *graphicalModeWidget, QWidget *parent)
37  : QWidget(parent)
38  , mSieveGraphicalModeWidget(graphicalModeWidget)
39 {
40  initWidget();
41 }
42 
43 SieveActionWidget::~SieveActionWidget()
44 {
45  qDeleteAll(mActionList);
46  mActionList.clear();
47 }
48 
49 bool SieveActionWidget::isConfigurated() const
50 {
51  return mComboBox->currentIndex() != (mComboBox->count() - 1);
52 }
53 
54 void SieveActionWidget::setFilterAction(QWidget *widget)
55 {
56  if (mLayout->itemAtPosition(1, 3)) {
57  delete mLayout->itemAtPosition(1, 3)->widget();
58  }
59 
60  if (widget) {
61  mLayout->addWidget(widget, 1, 3);
62  } else {
63  mLayout->addWidget(new QLabel(i18n("Please select an action."), this), 1, 3);
64  }
65 }
66 
67 void SieveActionWidget::generatedScript(QString &script, QStringList &required, bool onlyActions, bool inForEveryPartLoop)
68 {
69  const int index = mComboBox->currentIndex();
70  if (index != mComboBox->count() - 1) {
71  KSieveUi::SieveAction *widgetAction = mActionList.at(mComboBox->currentIndex());
72  QWidget *currentWidget = mLayout->itemAtPosition(1, 3)->widget();
73  const QStringList lstRequires = widgetAction->needRequires(currentWidget);
74  for (const QString &r : lstRequires) {
75  if (!required.contains(r)) {
76  required.append(r);
77  }
78  }
79  QString comment = widgetAction->comment();
80  QString indent;
81  if (!onlyActions) {
82  indent += AutoCreateScriptUtil::indentation();
83  }
84  if (inForEveryPartLoop) {
85  indent += AutoCreateScriptUtil::indentation();
86  }
87  if (!comment.trimmed().isEmpty()) {
88  const QVector<QStringRef> commentList = comment.splitRef(QLatin1Char('\n'));
89  for (const QStringRef &str : commentList) {
90  if (str.isEmpty()) {
91  script += QLatin1Char('\n');
92  } else {
93  script += indent + QLatin1Char('#') + str + QLatin1Char('\n');
94  }
95  }
96  }
97  script += indent + widgetAction->code(currentWidget) + QLatin1Char('\n');
98  }
99 }
100 
101 void SieveActionWidget::initWidget()
102 {
103  mLayout = new QGridLayout(this);
104  mLayout->setContentsMargins({});
105 
106  mComboBox = new QComboBox;
107  mComboBox->setEditable(false);
108  mComboBox->setMinimumWidth(50);
109  const QVector<KSieveUi::SieveAction *> list = KSieveUi::SieveActionList::actionList(mSieveGraphicalModeWidget);
112  int index = 0;
113  QStringList listCapabilities = mSieveGraphicalModeWidget->sieveCapabilities();
114  // imapflags was old name of imap4flags but still used.
115  if (listCapabilities.contains(QLatin1String("imap4flags"))) {
116  listCapabilities.append(QStringLiteral("imapflags"));
117  }
118  for (index = 0, it = list.constBegin(); it != end; ++it, ++index) {
119  if ((*it)->needCheckIfServerHasCapability()) {
120  if (listCapabilities.contains((*it)->serverNeedsCapability())) {
121  // append to the list of actions:
122  mActionList.append(*it);
123  connect(*it, &SieveAction::valueChanged, this, &SieveActionWidget::valueChanged);
124  // add (i18n-ized) name to combo box
125  mComboBox->addItem((*it)->label(), (*it)->name());
126  } else {
127  delete (*it);
128  }
129  } else {
130  // append to the list of actions:
131  mActionList.append(*it);
132  connect(*it, &SieveAction::valueChanged, this, &SieveActionWidget::valueChanged);
133  // add (i18n-ized) name to combo box
134  mComboBox->addItem((*it)->label(), (*it)->name());
135  }
136  }
137 
138  mHelpButton = new SieveHelpButton(this);
139  connect(mHelpButton, &SieveHelpButton::clicked, this, &SieveActionWidget::slotHelp);
140  mLayout->addWidget(mHelpButton, 1, 0);
141 
142  mCommentButton = new QToolButton(this);
143  mCommentButton->setToolTip(i18n("Add comment"));
144  mLayout->addWidget(mCommentButton, 1, 1);
145  mCommentButton->setIcon(QIcon::fromTheme(QStringLiteral("view-pim-notes")));
146  connect(mCommentButton, &QToolButton::clicked, this, &SieveActionWidget::slotAddComment);
147 
148  mComboBox->addItem(QLatin1String(""));
149 
150  mComboBox->setMaxCount(mComboBox->count());
151  mComboBox->setSizePolicy(QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed));
153  mComboBox->adjustSize();
154  mLayout->addWidget(mComboBox, 1, 2);
155 
156  updateGeometry();
157 
158  connect(mComboBox, QOverload<int>::of(&QComboBox::activated), this, &SieveActionWidget::slotActionChanged);
159 
160  mAdd = new QPushButton(this);
161  mAdd->setIcon(QIcon::fromTheme(QStringLiteral("list-add")));
162  mAdd->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed));
163 
164  mRemove = new QPushButton(this);
165  mRemove->setIcon(QIcon::fromTheme(QStringLiteral("list-remove")));
166  mRemove->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed));
167  mLayout->addWidget(mAdd, 1, 4);
168  mLayout->addWidget(mRemove, 1, 5);
169 
170  // redirect focus to the filter action combo box
171  setFocusProxy(mComboBox);
172 
173  connect(mAdd, &QPushButton::clicked, this, &SieveActionWidget::slotAddWidget);
174  connect(mRemove, &QPushButton::clicked, this, &SieveActionWidget::slotRemoveWidget);
175 
176  clear();
177 }
178 
179 void SieveActionWidget::slotHelp()
180 {
181  const int index = mComboBox->currentIndex();
182  if (index < mActionList.count()) {
183  KSieveUi::SieveAction *action = mActionList.at(index);
184  const QString help = action->help();
185  const QUrl href = action->href();
186  const QString fullWhatsThis = AutoCreateScriptUtil::createFullWhatsThis(help, href.toString());
187  QWhatsThis::showText(QCursor::pos(), fullWhatsThis, mHelpButton);
188  }
189 }
190 
191 void SieveActionWidget::clear()
192 {
193  mComboBox->setCurrentIndex(mComboBox->count() - 1);
194  setFilterAction(nullptr);
195  mCommentButton->setEnabled(false);
196  mHelpButton->setEnabled(false);
197 }
198 
199 void SieveActionWidget::slotAddComment()
200 {
201  const int index = mComboBox->currentIndex();
202  if (index < mActionList.count()) {
203  KSieveUi::SieveAction *action = mActionList.at(index);
204  const QString comment = action->comment();
205  QPointer<SieveScriptDescriptionDialog> dlg = new SieveScriptDescriptionDialog;
206  dlg->setDescription(comment);
207  if (dlg->exec()) {
208  action->setComment(dlg->description());
209  Q_EMIT valueChanged();
210  }
211  delete dlg;
212  }
213 }
214 
215 void SieveActionWidget::slotActionChanged(int index)
216 {
217  if (index < mActionList.count()) {
218  KSieveUi::SieveAction *action = mActionList.at(index);
219  mHelpButton->setEnabled(!action->help().isEmpty());
220  mCommentButton->setEnabled(true);
221  setFilterAction(action->createParamWidget(this));
222  // All actions after stop will not execute => don't allow to add more actions.
223  const bool enableAddAction = (action->name() != QLatin1String("stop"));
224  mAdd->setEnabled(enableAddAction);
225  } else {
226  mAdd->setEnabled(true);
227  mCommentButton->setEnabled(false);
228  setFilterAction(nullptr);
229  mHelpButton->setEnabled(false);
230  }
231  Q_EMIT valueChanged();
232 }
233 
234 void SieveActionWidget::slotAddWidget()
235 {
236  Q_EMIT valueChanged();
237  Q_EMIT addWidget(this);
238 }
239 
240 void SieveActionWidget::slotRemoveWidget()
241 {
242  Q_EMIT valueChanged();
243  Q_EMIT removeWidget(this);
244 }
245 
246 void SieveActionWidget::updateAddRemoveButton(bool addButtonEnabled, bool removeButtonEnabled)
247 {
248  mAdd->setEnabled(addButtonEnabled);
249  mRemove->setEnabled(removeButtonEnabled);
250 }
251 
252 void SieveActionWidget::setLocaleVariable(const SieveGlobalVariableActionWidget::VariableElement &var)
253 {
254  const int index = mComboBox->findData(QStringLiteral("set"));
255  if (index != -1) {
256  mComboBox->setCurrentIndex(index);
257  slotActionChanged(index);
258  auto localVar = qobject_cast<KSieveUi::SieveActionSetVariable *>(mActionList.at(index));
259  if (localVar) {
260  localVar->setLocalVariable(this, var);
261  }
262  } else {
263  // error += i18n("Script contains unsupported feature \"%1\"", actionName) + QLatin1Char('\n');
264  // qCDebug(LIBKSIEVE_LOG) << "Action " << actionName << " not supported";
265  }
266 }
267 
268 void SieveActionWidget::setAction(const QString &actionName, QXmlStreamReader &element, const QString &comment, QString &error)
269 {
270  const int index = mComboBox->findData(actionName);
271  if (index != -1) {
272  mComboBox->setCurrentIndex(index);
273  slotActionChanged(index);
274  KSieveUi::SieveAction *action = mActionList.at(index);
275  action->setParamWidgetValue(element, this, error);
276  action->setComment(comment);
277  } else {
278  error += i18n("Script contains unsupported feature \"%1\"", actionName) + QLatin1Char('\n');
279  qCDebug(LIBKSIEVE_LOG) << "Action " << actionName << " not supported";
280  element.skipCurrentElement();
281  }
282 }
283 
284 SieveActionWidgetLister::SieveActionWidgetLister(SieveEditorGraphicalModeWidget *graphicalModeWidget, QWidget *parent)
285  : KPIM::KWidgetLister(false, MINIMUMACTION, MAXIMUMACTION, parent)
286  , mSieveGraphicalModeWidget(graphicalModeWidget)
287 {
288  slotClear();
289  updateAddRemoveButton();
290 }
291 
292 SieveActionWidgetLister::~SieveActionWidgetLister()
293 {
294 }
295 
296 void SieveActionWidgetLister::slotAddWidget(QWidget *w)
297 {
298  addWidgetAfterThisWidget(w);
299  updateAddRemoveButton();
300 }
301 
302 void SieveActionWidgetLister::slotRemoveWidget(QWidget *w)
303 {
304  removeWidget(w);
305  updateAddRemoveButton();
306 }
307 
308 void SieveActionWidgetLister::updateAddRemoveButton()
309 {
310  QList<QWidget *> widgetList = widgets();
311  const int numberOfWidget(widgetList.count());
312  bool addButtonEnabled = false;
313  bool removeButtonEnabled = false;
314  if (numberOfWidget <= widgetsMinimum()) {
315  addButtonEnabled = true;
316  removeButtonEnabled = false;
317  } else if (numberOfWidget >= widgetsMaximum()) {
318  addButtonEnabled = false;
319  removeButtonEnabled = true;
320  } else {
321  addButtonEnabled = true;
322  removeButtonEnabled = true;
323  }
324  QList<QWidget *>::ConstIterator wIt = widgetList.constBegin();
325  QList<QWidget *>::ConstIterator wEnd = widgetList.constEnd();
326  for (; wIt != wEnd; ++wIt) {
327  auto w = qobject_cast<SieveActionWidget *>(*wIt);
328  w->updateAddRemoveButton(addButtonEnabled, removeButtonEnabled);
329  }
330 }
331 
332 void SieveActionWidgetLister::generatedScript(QString &script, QStringList &requireModules, bool onlyActions, bool inForEveryPartLoop)
333 {
334  const QList<QWidget *> widgetList = widgets();
335  QList<QWidget *>::ConstIterator wIt = widgetList.constBegin();
336  QList<QWidget *>::ConstIterator wEnd = widgetList.constEnd();
337  for (; wIt != wEnd; ++wIt) {
338  auto w = qobject_cast<SieveActionWidget *>(*wIt);
339  w->generatedScript(script, requireModules, onlyActions, inForEveryPartLoop);
340  }
341 }
342 
343 void SieveActionWidgetLister::reconnectWidget(SieveActionWidget *w)
344 {
345  connect(w, &SieveActionWidget::addWidget, this, &SieveActionWidgetLister::slotAddWidget, Qt::UniqueConnection);
346  connect(w, &SieveActionWidget::removeWidget, this, &SieveActionWidgetLister::slotRemoveWidget, Qt::UniqueConnection);
347  connect(w, &SieveActionWidget::valueChanged, this, &SieveActionWidgetLister::valueChanged, Qt::UniqueConnection);
348 }
349 
350 void SieveActionWidgetLister::clearWidget(QWidget *aWidget)
351 {
352  if (aWidget) {
353  auto widget = static_cast<SieveActionWidget *>(aWidget);
354  widget->clear();
355  }
356  Q_EMIT valueChanged();
357 }
358 
359 QWidget *SieveActionWidgetLister::createWidget(QWidget *parent)
360 {
361  auto w = new SieveActionWidget(mSieveGraphicalModeWidget, parent);
362  reconnectWidget(w);
363  return w;
364 }
365 
366 int SieveActionWidgetLister::actionNumber() const
367 {
368  return widgets().count();
369 }
370 
371 void SieveActionWidgetLister::loadLocalVariable(const SieveGlobalVariableActionWidget::VariableElement &var)
372 {
373  auto w = qobject_cast<SieveActionWidget *>(widgets().constLast());
374  if (w->isConfigurated()) {
375  addWidgetAfterThisWidget(widgets().constLast());
376  w = qobject_cast<SieveActionWidget *>(widgets().constLast());
377  }
378  w->setLocaleVariable(var);
379 }
380 
381 void SieveActionWidgetLister::loadScript(QXmlStreamReader &element, bool onlyActions, QString &error)
382 {
383  QString comment;
384  if (onlyActions) {
385  const QStringRef tagName = element.name();
386  if (tagName == QLatin1String("action")) {
387  if (element.attributes().hasAttribute(QLatin1String("name"))) {
388  const QString actionName = element.attributes().value(QLatin1String("name")).toString();
389  auto w = qobject_cast<SieveActionWidget *>(widgets().constLast());
390  if (w->isConfigurated()) {
391  addWidgetAfterThisWidget(widgets().constLast());
392  w = qobject_cast<SieveActionWidget *>(widgets().constLast());
393  }
394  w->setAction(actionName, element, comment, error);
395  // comment.clear();
396  } else if (tagName == QLatin1String("crlf")) {
397  element.skipCurrentElement();
398  // nothing
399  } else {
400  qCDebug(LIBKSIEVE_LOG) << " SieveActionWidgetLister::loadScript don't have name attribute " << tagName;
401  }
402  } else {
403  qCDebug(LIBKSIEVE_LOG) << " SieveActionWidgetLister::loadScript Unknown tag name " << tagName;
404  }
405  } else {
406  bool firstAction = true;
407  bool previousActionWasAComment = false;
408  while (element.readNextStartElement()) {
409  const QStringRef tagName = element.name();
410  if (tagName == QLatin1String("action") || tagName == QLatin1String("control") /*for break action*/) {
411  if (element.attributes().hasAttribute(QLatin1String("name"))) {
412  const QString actionName = element.attributes().value(QLatin1String("name")).toString();
413  if (tagName == QLatin1String("control") && actionName == QLatin1String("if")) {
414  qCDebug(LIBKSIEVE_LOG) << "We found an loop if in a loop if. Not supported";
415  error += i18n("We detected a loop if in a loop if. It's not supported") + QLatin1Char('\n');
416  }
417  if (firstAction) {
418  firstAction = false;
419  } else {
420  addWidgetAfterThisWidget(widgets().constLast());
421  }
422  auto w = qobject_cast<SieveActionWidget *>(widgets().constLast());
423  w->setAction(actionName, element, comment, error);
424  comment.clear();
425  } else {
426  qCDebug(LIBKSIEVE_LOG) << " SieveActionWidgetLister::loadScript don't have name attribute " << tagName;
427  }
428  previousActionWasAComment = false;
429  } else if (tagName == QLatin1String("comment")) {
430  if (!comment.isEmpty()) {
431  comment += QLatin1Char('\n');
432  }
433  previousActionWasAComment = true;
434  comment += element.readElementText();
435  } else if (tagName == QLatin1String("crlf")) {
436  // Add new line if previous action was a comment
437  if (previousActionWasAComment) {
438  comment += QLatin1Char('\n');
439  }
440  element.skipCurrentElement();
441  } else {
442  qCDebug(LIBKSIEVE_LOG) << " SieveActionWidgetLister::loadScript unknown tagName " << tagName;
443  }
444  }
445  }
446 }
void showText(const QPoint &pos, const QString &text, QWidget *w)
bool readNextStartElement()
QString toString() const const
QVector::const_iterator constEnd() const const
bool contains(const QString &str, Qt::CaseSensitivity cs) const const
QStringRef value(const QString &namespaceUri, const QString &name) const const
void setEditable(bool editable)
QString toString(QUrl::FormattingOptions options) const const
void clear()
void skipCurrentElement()
int count(const T &value) const const
void append(const T &value)
const QList< QKeySequence > & help()
QString readElementText(QXmlStreamReader::ReadElementTextBehaviour behaviour)
bool isEmpty() const const
QString trimmed() 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()
const T & at(int i) const const
QVector::const_iterator constBegin() const const
QPoint pos()
QVector< QStringRef > splitRef(const QString &sep, QString::SplitBehavior behavior, Qt::CaseSensitivity cs) 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 Fri Apr 16 2021 23:09:34 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.