Libksieve

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

KDE's Doxygen guidelines are available online.