Libksieve

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

KDE's Doxygen guidelines are available online.