File indexing completed on 2024-12-29 04:54:44

0001 /*
0002    SPDX-FileCopyrightText: 2013-2024 Laurent Montel <montel@kde.org>
0003 
0004    SPDX-License-Identifier: LGPL-2.0-or-later
0005 */
0006 
0007 #include "sieveconditionwidgetlister.h"
0008 #include "autocreatescriptutil_p.h"
0009 #include "commonwidgets/sievehelpbutton.h"
0010 #include "libksieveui_debug.h"
0011 #include "sieveconditions/sievecondition.h"
0012 #include "sieveconditions/sieveconditionlist.h"
0013 #include "sieveeditorgraphicalmodewidget.h"
0014 
0015 #include <KLocalizedString>
0016 #include <QComboBox>
0017 #include <QIcon>
0018 #include <QPointer>
0019 #include <QPushButton>
0020 #include <QUrl>
0021 
0022 #include "sievescriptdescriptiondialog.h"
0023 #include <QGridLayout>
0024 #include <QLabel>
0025 #include <QWhatsThis>
0026 
0027 using namespace KSieveUi;
0028 
0029 static const int MINIMUMCONDITION = 1;
0030 static const int MAXIMUMCONDITION = 8;
0031 
0032 SieveConditionWidget::SieveConditionWidget(SieveEditorGraphicalModeWidget *sieveGraphicalModeWidget, QWidget *parent)
0033     : QWidget(parent)
0034     , mSieveGraphicalModeWidget(sieveGraphicalModeWidget)
0035 {
0036     initWidget();
0037 }
0038 
0039 SieveConditionWidget::~SieveConditionWidget()
0040 {
0041     qDeleteAll(mConditionList);
0042     mConditionList.clear();
0043 }
0044 
0045 void SieveConditionWidget::setFilterCondition(QWidget *widget)
0046 {
0047     if (mLayout->itemAtPosition(1, 3)) {
0048         delete mLayout->itemAtPosition(1, 3)->widget();
0049     }
0050 
0051     if (widget) {
0052         mLayout->addWidget(widget, 1, 3);
0053     } else {
0054         mLayout->addWidget(new QLabel(i18n("Please select an condition."), this), 1, 3);
0055     }
0056 }
0057 
0058 void SieveConditionWidget::generatedScript(QString &script, QStringList &required, bool inForEveryPartLoop)
0059 {
0060     Q_UNUSED(inForEveryPartLoop)
0061     const int index = mComboBox->currentIndex();
0062     if (index != mComboBox->count() - 1) {
0063         KSieveUi::SieveCondition *widgetCondition = mConditionList.at(mComboBox->currentIndex());
0064         QWidget *currentWidget = mLayout->itemAtPosition(1, 3)->widget();
0065         const QStringList lstRequires = widgetCondition->needRequires(currentWidget);
0066         for (const QString &r : lstRequires) {
0067             if (!required.contains(r)) {
0068                 required.append(r);
0069             }
0070         }
0071         script += mConditionList.at(mComboBox->currentIndex())->code(currentWidget) + QLatin1Char('\n');
0072     }
0073 }
0074 
0075 void SieveConditionWidget::initWidget()
0076 {
0077     mLayout = new QGridLayout(this);
0078     mLayout->setContentsMargins({});
0079 
0080     mComboBox = new QComboBox;
0081     mComboBox->setMinimumWidth(50);
0082     mComboBox->setEditable(false);
0083 
0084     const QList<KSieveUi::SieveCondition *> list = KSieveUi::SieveConditionList::conditionList(mSieveGraphicalModeWidget);
0085     for (const auto &action : list) {
0086         if (action->needCheckIfServerHasCapability()) {
0087             if (mSieveGraphicalModeWidget->sieveCapabilities().contains(action->serverNeedsCapability())) {
0088                 // append to the list of actions:
0089                 mConditionList.append(action);
0090                 connect(action, &SieveCondition::valueChanged, this, &SieveConditionWidget::valueChanged);
0091                 // add (i18n-ized) name to combo box
0092                 mComboBox->addItem(action->label(), action->name());
0093             } else {
0094                 delete action;
0095             }
0096         } else {
0097             // append to the list of actions:
0098             mConditionList.append(action);
0099             connect(action, &SieveCondition::valueChanged, this, &SieveConditionWidget::valueChanged);
0100             // add (i18n-ized) name to combo box
0101             mComboBox->addItem(action->label(), action->name());
0102         }
0103     }
0104 
0105     mHelpButton = new SieveHelpButton(this);
0106     mLayout->addWidget(mHelpButton, 1, 0);
0107     connect(mHelpButton, &SieveHelpButton::clicked, this, &SieveConditionWidget::slotHelp);
0108 
0109     mCommentButton = new QToolButton(this);
0110     mCommentButton->setToolTip(i18n("Add comment"));
0111     mLayout->addWidget(mCommentButton, 1, 1);
0112     mCommentButton->setIcon(QIcon::fromTheme(QStringLiteral("view-pim-notes")));
0113     connect(mCommentButton, &QToolButton::clicked, this, &SieveConditionWidget::slotAddComment);
0114 
0115     mComboBox->addItem(QLatin1StringView(""));
0116     mLayout->addWidget(mComboBox, 1, 2);
0117     connect(mComboBox, &QComboBox::activated, this, &SieveConditionWidget::slotConditionChanged);
0118 
0119     mComboBox->setMaxCount(mComboBox->count());
0120     mComboBox->setSizePolicy(QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed));
0121     setSizePolicy(QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed));
0122     mComboBox->adjustSize();
0123 
0124     mAdd = new QPushButton(this);
0125     mAdd->setIcon(QIcon::fromTheme(QStringLiteral("list-add")));
0126     mAdd->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed));
0127 
0128     mRemove = new QPushButton(this);
0129     mRemove->setIcon(QIcon::fromTheme(QStringLiteral("list-remove")));
0130     mRemove->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed));
0131 
0132     mLayout->addWidget(mAdd, 1, 4);
0133     mLayout->addWidget(mRemove, 1, 5);
0134 
0135     // redirect focus to the filter action combo box
0136     setFocusProxy(mComboBox);
0137 
0138     connect(mAdd, &QPushButton::clicked, this, &SieveConditionWidget::slotAddWidget);
0139     connect(mRemove, &QPushButton::clicked, this, &SieveConditionWidget::slotRemoveWidget);
0140 
0141     clear();
0142 }
0143 
0144 void SieveConditionWidget::slotAddComment()
0145 {
0146     const int index = mComboBox->currentIndex();
0147     if (index < mConditionList.count()) {
0148         KSieveUi::SieveCondition *condition = mConditionList.at(index);
0149         const QString comment = condition->comment();
0150         QPointer<SieveScriptDescriptionDialog> dlg = new SieveScriptDescriptionDialog(this);
0151         dlg->setDescription(comment);
0152         if (dlg->exec()) {
0153             condition->setComment(dlg->description());
0154             Q_EMIT valueChanged();
0155         }
0156         delete dlg;
0157     }
0158 }
0159 
0160 void SieveConditionWidget::slotHelp()
0161 {
0162     const int index = mComboBox->currentIndex();
0163     if (index < mConditionList.count()) {
0164         KSieveUi::SieveCondition *condition = mConditionList.at(index);
0165         const QString help = condition->help();
0166         const QUrl href = condition->href();
0167         const QString fullWhatsThis = AutoCreateScriptUtil::createFullWhatsThis(help, href.toString());
0168         QWhatsThis::showText(QCursor::pos(), fullWhatsThis, mHelpButton);
0169     }
0170 }
0171 
0172 void SieveConditionWidget::slotConditionChanged(int index)
0173 {
0174     if (index < mConditionList.count()) {
0175         KSieveUi::SieveCondition *condition = mConditionList.at(index);
0176         mHelpButton->setEnabled(!condition->help().isEmpty());
0177         setFilterCondition(condition->createParamWidget(this));
0178         mCommentButton->setEnabled(true);
0179     } else {
0180         setFilterCondition(nullptr);
0181         mHelpButton->setEnabled(false);
0182         mCommentButton->setEnabled(false);
0183     }
0184     Q_EMIT valueChanged();
0185 }
0186 
0187 void SieveConditionWidget::slotAddWidget()
0188 {
0189     Q_EMIT addWidget(this);
0190     Q_EMIT valueChanged();
0191 }
0192 
0193 void SieveConditionWidget::slotRemoveWidget()
0194 {
0195     Q_EMIT removeWidget(this);
0196     Q_EMIT valueChanged();
0197 }
0198 
0199 void SieveConditionWidget::clear()
0200 {
0201     mComboBox->setCurrentIndex(mComboBox->count() - 1);
0202     setFilterCondition(nullptr);
0203     mHelpButton->setEnabled(false);
0204     mCommentButton->setEnabled(false);
0205 }
0206 
0207 void SieveConditionWidget::updateAddRemoveButton(bool addButtonEnabled, bool removeButtonEnabled)
0208 {
0209     mAdd->setEnabled(addButtonEnabled);
0210     mRemove->setEnabled(removeButtonEnabled);
0211 }
0212 
0213 void SieveConditionWidget::setCondition(const QString &conditionName, QXmlStreamReader &element, bool notCondition, QString &error)
0214 {
0215     const int index = mComboBox->findData(conditionName);
0216     if (index != -1) {
0217         mComboBox->setCurrentIndex(index);
0218         slotConditionChanged(index);
0219         KSieveUi::SieveCondition *condition = mConditionList.at(index);
0220         condition->setParamWidgetValue(element, this, notCondition, error);
0221     } else {
0222         error += i18n("Script contains unsupported feature \"%1\"", conditionName) + QLatin1Char('\n');
0223         qCDebug(LIBKSIEVEUI_LOG) << "Condition " << conditionName << " not supported";
0224         element.skipCurrentElement();
0225     }
0226 }
0227 
0228 SieveConditionWidgetLister::SieveConditionWidgetLister(SieveEditorGraphicalModeWidget *sieveGraphicalModeWidget, QWidget *parent)
0229     : KPIM::KWidgetLister(false, MINIMUMCONDITION, MAXIMUMCONDITION, parent)
0230     , mSieveGraphicalModeWidget(sieveGraphicalModeWidget)
0231 {
0232     slotClear();
0233     updateAddRemoveButton();
0234 }
0235 
0236 SieveConditionWidgetLister::~SieveConditionWidgetLister() = default;
0237 
0238 void SieveConditionWidgetLister::slotAddWidget(QWidget *w)
0239 {
0240     addWidgetAfterThisWidget(w);
0241     updateAddRemoveButton();
0242 }
0243 
0244 void SieveConditionWidgetLister::slotRemoveWidget(QWidget *w)
0245 {
0246     removeWidget(w);
0247     updateAddRemoveButton();
0248 }
0249 
0250 void SieveConditionWidgetLister::updateAddRemoveButton()
0251 {
0252     QList<QWidget *> widgetList = widgets();
0253     const int numberOfWidget(widgetList.count());
0254     bool addButtonEnabled = false;
0255     bool removeButtonEnabled = false;
0256     if (numberOfWidget <= widgetsMinimum()) {
0257         addButtonEnabled = true;
0258         removeButtonEnabled = false;
0259     } else if (numberOfWidget >= widgetsMaximum()) {
0260         addButtonEnabled = false;
0261         removeButtonEnabled = true;
0262     } else {
0263         addButtonEnabled = true;
0264         removeButtonEnabled = true;
0265     }
0266     QList<QWidget *>::ConstIterator wIt = widgetList.constBegin();
0267     QList<QWidget *>::ConstIterator wEnd = widgetList.constEnd();
0268     for (; wIt != wEnd; ++wIt) {
0269         auto w = qobject_cast<SieveConditionWidget *>(*wIt);
0270         w->updateAddRemoveButton(addButtonEnabled, removeButtonEnabled);
0271     }
0272 }
0273 
0274 void SieveConditionWidgetLister::reconnectWidget(SieveConditionWidget *w)
0275 {
0276     connect(w, &SieveConditionWidget::addWidget, this, &SieveConditionWidgetLister::slotAddWidget, Qt::UniqueConnection);
0277     connect(w, &SieveConditionWidget::removeWidget, this, &SieveConditionWidgetLister::slotRemoveWidget, Qt::UniqueConnection);
0278     connect(w, &SieveConditionWidget::valueChanged, this, &SieveConditionWidgetLister::valueChanged, Qt::UniqueConnection);
0279 }
0280 
0281 void SieveConditionWidgetLister::clearWidget(QWidget *aWidget)
0282 {
0283     if (aWidget) {
0284         auto widget = static_cast<SieveConditionWidget *>(aWidget);
0285         widget->clear();
0286         updateAddRemoveButton();
0287     }
0288     Q_EMIT valueChanged();
0289 }
0290 
0291 QWidget *SieveConditionWidgetLister::createWidget(QWidget *parent)
0292 {
0293     auto w = new SieveConditionWidget(mSieveGraphicalModeWidget, parent);
0294     reconnectWidget(w);
0295     return w;
0296 }
0297 
0298 void SieveConditionWidgetLister::generatedScript(QString &script, int &numberOfCondition, QStringList &requireModules, bool inForEveryPartLoop)
0299 {
0300     const QList<QWidget *> widgetList = widgets();
0301     QList<QWidget *>::ConstIterator wIt = widgetList.constBegin();
0302     QList<QWidget *>::ConstIterator wEnd = widgetList.constEnd();
0303     bool wasFirst = true;
0304     for (; wIt != wEnd; ++wIt) {
0305         QString condition;
0306         auto w = qobject_cast<SieveConditionWidget *>(*wIt);
0307         w->generatedScript(condition, requireModules, inForEveryPartLoop);
0308         if (!condition.isEmpty()) {
0309             if (!wasFirst) {
0310                 if (inForEveryPartLoop) {
0311                     script += AutoCreateScriptUtil::indentation();
0312                 }
0313                 script += QLatin1StringView(", ");
0314             }
0315             script += condition;
0316             wasFirst = false;
0317             ++numberOfCondition;
0318         }
0319     }
0320 }
0321 
0322 int SieveConditionWidgetLister::conditionNumber() const
0323 {
0324     return widgets().count();
0325 }
0326 
0327 void SieveConditionWidgetLister::loadTest(QXmlStreamReader &element, bool notCondition, QString &error)
0328 {
0329     if (notCondition) {
0330         element.readNextStartElement();
0331     }
0332     if (element.attributes().hasAttribute(QLatin1StringView("name"))) {
0333         const QString conditionName = element.attributes().value(QLatin1StringView("name")).toString();
0334         auto w = qobject_cast<SieveConditionWidget *>(widgets().constLast());
0335         w->setCondition(conditionName, element, notCondition, error);
0336     }
0337     if (notCondition) {
0338         element.skipCurrentElement();
0339     }
0340 }
0341 
0342 void SieveConditionWidgetLister::loadScript(QXmlStreamReader &element, bool uniqTest, bool notCondition, QString &error)
0343 {
0344     if (uniqTest) {
0345         loadTest(element, notCondition, error);
0346     } else {
0347         bool firstCondition = true;
0348         if (notCondition) {
0349             element.readNextStartElement();
0350         }
0351         while (element.readNextStartElement()) {
0352             const QStringView tagName = element.name();
0353             if (tagName == QLatin1StringView("testlist")) {
0354                 while (element.readNextStartElement()) {
0355                     const QStringView testTagName = element.name();
0356                     if (testTagName == QLatin1StringView("test")) {
0357                         if (element.attributes().hasAttribute(QLatin1StringView("name"))) {
0358                             QString conditionName = element.attributes().value(QLatin1StringView("name")).toString();
0359                             if (firstCondition) {
0360                                 firstCondition = false;
0361                             } else {
0362                                 addWidgetAfterThisWidget(widgets().constLast());
0363                             }
0364                             auto w = qobject_cast<SieveConditionWidget *>(widgets().constLast());
0365                             if (conditionName == QLatin1StringView("not")) {
0366                                 notCondition = true;
0367                                 element.readNextStartElement();
0368                                 if (element.attributes().hasAttribute(QLatin1StringView("name"))) {
0369                                     conditionName = element.attributes().value(QLatin1StringView("name")).toString();
0370                                 }
0371                                 w->setCondition(conditionName, element, notCondition, error);
0372                                 element.skipCurrentElement();
0373                             } else {
0374                                 notCondition = false;
0375                                 w->setCondition(conditionName, element, notCondition, error);
0376                             }
0377                         }
0378                     } else if (testTagName == QLatin1StringView("crlf")) {
0379                         element.skipCurrentElement();
0380                         // nothing
0381                     } else if (testTagName == QLatin1StringView("comment")) {
0382                         qDebug() << "Need to implement comment here ";
0383                         element.skipCurrentElement();
0384                         // nothing
0385                         // implement in the future ?
0386                     } else {
0387                         qCDebug(LIBKSIEVEUI_LOG) << " SieveConditionWidgetLister::loadScript unknown condition tag: " << testTagName;
0388                     }
0389                 }
0390             }
0391         }
0392     }
0393 }
0394 
0395 #include "moc_sieveconditionwidgetlister.cpp"