Libksieve

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

KDE's Doxygen guidelines are available online.