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 "sieveactionwidgetlister.h" 0008 #include "autocreatescriptutil_p.h" 0009 #include "commonwidgets/sievehelpbutton.h" 0010 #include "sieveactions/sieveaction.h" 0011 #include "sieveactions/sieveactionlist.h" 0012 #include "sieveeditorgraphicalmodewidget.h" 0013 #include "sievescriptdescriptiondialog.h" 0014 0015 #include <KLocalizedString> 0016 #include <QIcon> 0017 #include <QPushButton> 0018 #include <QUrl> 0019 0020 #include "libksieveui_debug.h" 0021 #include <QComboBox> 0022 #include <QGridLayout> 0023 #include <QLabel> 0024 #include <QPointer> 0025 #include <QToolButton> 0026 #include <QWhatsThis> 0027 0028 #include "autocreatescripts/sieveactions/sieveactionsetvariable.h" 0029 0030 using namespace KSieveUi; 0031 0032 static int MINIMUMACTION = 1; 0033 static int MAXIMUMACTION = 8; 0034 0035 SieveActionWidget::SieveActionWidget(SieveEditorGraphicalModeWidget *graphicalModeWidget, QWidget *parent) 0036 : QWidget(parent) 0037 , mSieveGraphicalModeWidget(graphicalModeWidget) 0038 { 0039 initWidget(); 0040 } 0041 0042 SieveActionWidget::~SieveActionWidget() 0043 { 0044 qDeleteAll(mActionList); 0045 mActionList.clear(); 0046 } 0047 0048 bool SieveActionWidget::isConfigurated() const 0049 { 0050 return mComboBox->currentIndex() != (mComboBox->count() - 1); 0051 } 0052 0053 void SieveActionWidget::setFilterAction(QWidget *widget) 0054 { 0055 if (mLayout->itemAtPosition(1, 3)) { 0056 delete mLayout->itemAtPosition(1, 3)->widget(); 0057 } 0058 0059 if (widget) { 0060 mLayout->addWidget(widget, 1, 3); 0061 } else { 0062 mLayout->addWidget(new QLabel(i18n("Please select an action."), this), 1, 3); 0063 } 0064 } 0065 0066 void SieveActionWidget::generatedScript(QString &script, QStringList &required, bool onlyActions, bool inForEveryPartLoop) 0067 { 0068 const int index = mComboBox->currentIndex(); 0069 if (index != mComboBox->count() - 1) { 0070 KSieveUi::SieveAction *widgetAction = mActionList.at(mComboBox->currentIndex()); 0071 QWidget *currentWidget = mLayout->itemAtPosition(1, 3)->widget(); 0072 const QStringList lstRequires = widgetAction->needRequires(currentWidget); 0073 for (const QString &r : lstRequires) { 0074 if (!required.contains(r)) { 0075 required.append(r); 0076 } 0077 } 0078 QString comment = widgetAction->comment(); 0079 QString indent; 0080 if (!onlyActions) { 0081 indent += AutoCreateScriptUtil::indentation(); 0082 } 0083 if (inForEveryPartLoop) { 0084 indent += AutoCreateScriptUtil::indentation(); 0085 } 0086 if (!comment.trimmed().isEmpty()) { 0087 const QList<QStringView> commentList = QStringView(comment).split(QLatin1Char('\n')); 0088 for (const QStringView str : commentList) { 0089 if (str.isEmpty()) { 0090 script += QLatin1Char('\n'); 0091 } else { 0092 script += indent + QLatin1Char('#') + str + QLatin1Char('\n'); 0093 } 0094 } 0095 } 0096 script += indent + widgetAction->code(currentWidget) + QLatin1Char('\n'); 0097 } 0098 } 0099 0100 void SieveActionWidget::initWidget() 0101 { 0102 mLayout = new QGridLayout(this); 0103 mLayout->setContentsMargins({}); 0104 0105 mComboBox = new QComboBox; 0106 mComboBox->setEditable(false); 0107 mComboBox->setMinimumWidth(50); 0108 const QList<KSieveUi::SieveAction *> list = KSieveUi::SieveActionList::actionList(mSieveGraphicalModeWidget); 0109 QStringList listCapabilities = mSieveGraphicalModeWidget->sieveCapabilities(); 0110 // imapflags was old name of imap4flags but still used. 0111 if (listCapabilities.contains(QLatin1StringView("imap4flags"))) { 0112 listCapabilities.append(QStringLiteral("imapflags")); 0113 } 0114 for (const auto &action : list) { 0115 if (action->needCheckIfServerHasCapability()) { 0116 if (listCapabilities.contains(action->serverNeedsCapability())) { 0117 // append to the list of actions: 0118 mActionList.append(action); 0119 connect(action, &SieveAction::valueChanged, this, &SieveActionWidget::valueChanged); 0120 // add (i18n-ized) name to combo box 0121 mComboBox->addItem(action->label(), action->name()); 0122 } else { 0123 delete (action); 0124 } 0125 } else { 0126 // append to the list of actions: 0127 mActionList.append(action); 0128 connect(action, &SieveAction::valueChanged, this, &SieveActionWidget::valueChanged); 0129 // add (i18n-ized) name to combo box 0130 mComboBox->addItem(action->label(), action->name()); 0131 } 0132 } 0133 0134 mHelpButton = new SieveHelpButton(this); 0135 connect(mHelpButton, &SieveHelpButton::clicked, this, &SieveActionWidget::slotHelp); 0136 mLayout->addWidget(mHelpButton, 1, 0); 0137 0138 mCommentButton = new QToolButton(this); 0139 mCommentButton->setToolTip(i18n("Add comment")); 0140 mLayout->addWidget(mCommentButton, 1, 1); 0141 mCommentButton->setIcon(QIcon::fromTheme(QStringLiteral("view-pim-notes"))); 0142 connect(mCommentButton, &QToolButton::clicked, this, &SieveActionWidget::slotAddComment); 0143 0144 mComboBox->addItem(QLatin1StringView("")); 0145 0146 mComboBox->setMaxCount(mComboBox->count()); 0147 mComboBox->setSizePolicy(QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed)); 0148 setSizePolicy(QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed)); 0149 mComboBox->adjustSize(); 0150 mLayout->addWidget(mComboBox, 1, 2); 0151 0152 updateGeometry(); 0153 0154 connect(mComboBox, &QComboBox::activated, this, &SieveActionWidget::slotActionChanged); 0155 0156 mAdd = new QPushButton(this); 0157 mAdd->setIcon(QIcon::fromTheme(QStringLiteral("list-add"))); 0158 mAdd->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed)); 0159 0160 mRemove = new QPushButton(this); 0161 mRemove->setIcon(QIcon::fromTheme(QStringLiteral("list-remove"))); 0162 mRemove->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed)); 0163 mLayout->addWidget(mAdd, 1, 4); 0164 mLayout->addWidget(mRemove, 1, 5); 0165 0166 // redirect focus to the filter action combo box 0167 setFocusProxy(mComboBox); 0168 0169 connect(mAdd, &QPushButton::clicked, this, &SieveActionWidget::slotAddWidget); 0170 connect(mRemove, &QPushButton::clicked, this, &SieveActionWidget::slotRemoveWidget); 0171 0172 clear(); 0173 } 0174 0175 void SieveActionWidget::slotHelp() 0176 { 0177 const int index = mComboBox->currentIndex(); 0178 if (index < mActionList.count()) { 0179 KSieveUi::SieveAction *action = mActionList.at(index); 0180 const QString help = action->help(); 0181 const QUrl href = action->href(); 0182 const QString fullWhatsThis = AutoCreateScriptUtil::createFullWhatsThis(help, href.toString()); 0183 QWhatsThis::showText(QCursor::pos(), fullWhatsThis, mHelpButton); 0184 } 0185 } 0186 0187 void SieveActionWidget::clear() 0188 { 0189 mComboBox->setCurrentIndex(mComboBox->count() - 1); 0190 setFilterAction(nullptr); 0191 mCommentButton->setEnabled(false); 0192 mHelpButton->setEnabled(false); 0193 } 0194 0195 void SieveActionWidget::slotAddComment() 0196 { 0197 const int index = mComboBox->currentIndex(); 0198 if (index < mActionList.count()) { 0199 KSieveUi::SieveAction *action = mActionList.at(index); 0200 const QString comment = action->comment(); 0201 QPointer<SieveScriptDescriptionDialog> dlg = new SieveScriptDescriptionDialog; 0202 dlg->setDescription(comment); 0203 if (dlg->exec()) { 0204 action->setComment(dlg->description()); 0205 Q_EMIT valueChanged(); 0206 } 0207 delete dlg; 0208 } 0209 } 0210 0211 void SieveActionWidget::slotActionChanged(int index) 0212 { 0213 if (index < mActionList.count()) { 0214 KSieveUi::SieveAction *action = mActionList.at(index); 0215 mHelpButton->setEnabled(!action->help().isEmpty()); 0216 mCommentButton->setEnabled(true); 0217 setFilterAction(action->createParamWidget(this)); 0218 // All actions after stop will not execute => don't allow to add more actions. 0219 const bool enableAddAction = (action->name() != QLatin1StringView("stop")); 0220 mAdd->setEnabled(enableAddAction); 0221 } else { 0222 mAdd->setEnabled(true); 0223 mCommentButton->setEnabled(false); 0224 setFilterAction(nullptr); 0225 mHelpButton->setEnabled(false); 0226 } 0227 Q_EMIT valueChanged(); 0228 } 0229 0230 void SieveActionWidget::slotAddWidget() 0231 { 0232 Q_EMIT valueChanged(); 0233 Q_EMIT addWidget(this); 0234 } 0235 0236 void SieveActionWidget::slotRemoveWidget() 0237 { 0238 Q_EMIT valueChanged(); 0239 Q_EMIT removeWidget(this); 0240 } 0241 0242 void SieveActionWidget::updateAddRemoveButton(bool addButtonEnabled, bool removeButtonEnabled) 0243 { 0244 mAdd->setEnabled(addButtonEnabled); 0245 mRemove->setEnabled(removeButtonEnabled); 0246 } 0247 0248 void SieveActionWidget::setLocaleVariable(const SieveGlobalVariableActionWidget::VariableElement &var) 0249 { 0250 const int index = mComboBox->findData(QStringLiteral("set")); 0251 if (index != -1) { 0252 mComboBox->setCurrentIndex(index); 0253 slotActionChanged(index); 0254 auto localVar = qobject_cast<KSieveUi::SieveActionSetVariable *>(mActionList.at(index)); 0255 if (localVar) { 0256 localVar->setLocalVariable(this, var); 0257 } 0258 } else { 0259 // error += i18n("Script contains unsupported feature \"%1\"", actionName) + QLatin1Char('\n'); 0260 // qCDebug(LIBKSIEVEUI_LOG) << "Action " << actionName << " not supported"; 0261 } 0262 } 0263 0264 void SieveActionWidget::setAction(const QString &actionName, QXmlStreamReader &element, const QString &comment, QString &error) 0265 { 0266 const int index = mComboBox->findData(actionName); 0267 if (index != -1) { 0268 mComboBox->setCurrentIndex(index); 0269 slotActionChanged(index); 0270 KSieveUi::SieveAction *action = mActionList.at(index); 0271 action->setParamWidgetValue(element, this, error); 0272 action->setComment(comment); 0273 } else { 0274 error += i18n("Script contains unsupported feature \"%1\"", actionName) + QLatin1Char('\n'); 0275 qCDebug(LIBKSIEVEUI_LOG) << "Action " << actionName << " not supported"; 0276 element.skipCurrentElement(); 0277 } 0278 } 0279 0280 SieveActionWidgetLister::SieveActionWidgetLister(SieveEditorGraphicalModeWidget *graphicalModeWidget, QWidget *parent) 0281 : KPIM::KWidgetLister(false, MINIMUMACTION, MAXIMUMACTION, parent) 0282 , mSieveGraphicalModeWidget(graphicalModeWidget) 0283 { 0284 slotClear(); 0285 updateAddRemoveButton(); 0286 } 0287 0288 SieveActionWidgetLister::~SieveActionWidgetLister() = default; 0289 0290 void SieveActionWidgetLister::slotAddWidget(QWidget *w) 0291 { 0292 addWidgetAfterThisWidget(w); 0293 updateAddRemoveButton(); 0294 } 0295 0296 void SieveActionWidgetLister::slotRemoveWidget(QWidget *w) 0297 { 0298 removeWidget(w); 0299 updateAddRemoveButton(); 0300 } 0301 0302 void SieveActionWidgetLister::updateAddRemoveButton() 0303 { 0304 QList<QWidget *> widgetList = widgets(); 0305 const int numberOfWidget(widgetList.count()); 0306 bool addButtonEnabled = false; 0307 bool removeButtonEnabled = false; 0308 if (numberOfWidget <= widgetsMinimum()) { 0309 addButtonEnabled = true; 0310 removeButtonEnabled = false; 0311 } else if (numberOfWidget >= widgetsMaximum()) { 0312 addButtonEnabled = false; 0313 removeButtonEnabled = true; 0314 } else { 0315 addButtonEnabled = true; 0316 removeButtonEnabled = true; 0317 } 0318 QList<QWidget *>::ConstIterator wIt = widgetList.constBegin(); 0319 QList<QWidget *>::ConstIterator wEnd = widgetList.constEnd(); 0320 for (; wIt != wEnd; ++wIt) { 0321 auto w = qobject_cast<SieveActionWidget *>(*wIt); 0322 w->updateAddRemoveButton(addButtonEnabled, removeButtonEnabled); 0323 } 0324 } 0325 0326 void SieveActionWidgetLister::generatedScript(QString &script, QStringList &requireModules, bool onlyActions, bool inForEveryPartLoop) 0327 { 0328 const QList<QWidget *> widgetList = widgets(); 0329 QList<QWidget *>::ConstIterator wIt = widgetList.constBegin(); 0330 QList<QWidget *>::ConstIterator wEnd = widgetList.constEnd(); 0331 for (; wIt != wEnd; ++wIt) { 0332 auto w = qobject_cast<SieveActionWidget *>(*wIt); 0333 w->generatedScript(script, requireModules, onlyActions, inForEveryPartLoop); 0334 } 0335 } 0336 0337 void SieveActionWidgetLister::reconnectWidget(SieveActionWidget *w) 0338 { 0339 connect(w, &SieveActionWidget::addWidget, this, &SieveActionWidgetLister::slotAddWidget, Qt::UniqueConnection); 0340 connect(w, &SieveActionWidget::removeWidget, this, &SieveActionWidgetLister::slotRemoveWidget, Qt::UniqueConnection); 0341 connect(w, &SieveActionWidget::valueChanged, this, &SieveActionWidgetLister::valueChanged, Qt::UniqueConnection); 0342 } 0343 0344 void SieveActionWidgetLister::clearWidget(QWidget *aWidget) 0345 { 0346 if (aWidget) { 0347 auto widget = static_cast<SieveActionWidget *>(aWidget); 0348 widget->clear(); 0349 updateAddRemoveButton(); 0350 } 0351 Q_EMIT valueChanged(); 0352 } 0353 0354 QWidget *SieveActionWidgetLister::createWidget(QWidget *parent) 0355 { 0356 auto w = new SieveActionWidget(mSieveGraphicalModeWidget, parent); 0357 reconnectWidget(w); 0358 return w; 0359 } 0360 0361 int SieveActionWidgetLister::actionNumber() const 0362 { 0363 return widgets().count(); 0364 } 0365 0366 void SieveActionWidgetLister::loadLocalVariable(const SieveGlobalVariableActionWidget::VariableElement &var) 0367 { 0368 auto w = qobject_cast<SieveActionWidget *>(widgets().constLast()); 0369 if (w->isConfigurated()) { 0370 addWidgetAfterThisWidget(widgets().constLast()); 0371 w = qobject_cast<SieveActionWidget *>(widgets().constLast()); 0372 } 0373 w->setLocaleVariable(var); 0374 } 0375 0376 void SieveActionWidgetLister::loadScript(QXmlStreamReader &element, bool onlyActions, QString &error) 0377 { 0378 QString comment; 0379 if (onlyActions) { 0380 const QStringView tagName = element.name(); 0381 if (tagName == QLatin1StringView("action")) { 0382 if (element.attributes().hasAttribute(QLatin1StringView("name"))) { 0383 const QString actionName = element.attributes().value(QLatin1StringView("name")).toString(); 0384 auto w = qobject_cast<SieveActionWidget *>(widgets().constLast()); 0385 if (w->isConfigurated()) { 0386 addWidgetAfterThisWidget(widgets().constLast()); 0387 w = qobject_cast<SieveActionWidget *>(widgets().constLast()); 0388 } 0389 w->setAction(actionName, element, comment, error); 0390 // comment.clear(); 0391 } else if (tagName == QLatin1StringView("crlf")) { 0392 element.skipCurrentElement(); 0393 // nothing 0394 } else { 0395 qCDebug(LIBKSIEVEUI_LOG) << " SieveActionWidgetLister::loadScript don't have name attribute " << tagName; 0396 } 0397 } else { 0398 qCDebug(LIBKSIEVEUI_LOG) << " SieveActionWidgetLister::loadScript Unknown tag name " << tagName; 0399 } 0400 } else { 0401 bool firstAction = true; 0402 bool previousActionWasAComment = false; 0403 while (element.readNextStartElement()) { 0404 const QStringView tagName = element.name(); 0405 if (tagName == QLatin1StringView("action") || tagName == QLatin1StringView("control") /*for break action*/) { 0406 if (element.attributes().hasAttribute(QLatin1StringView("name"))) { 0407 const QString actionName = element.attributes().value(QLatin1StringView("name")).toString(); 0408 if (tagName == QLatin1StringView("control") && actionName == QLatin1StringView("if")) { 0409 qCDebug(LIBKSIEVEUI_LOG) << "We found an loop if in a loop if. Not supported"; 0410 error += i18n("We detected a loop if in a loop if. It's not supported") + QLatin1Char('\n'); 0411 } 0412 if (firstAction) { 0413 firstAction = false; 0414 } else { 0415 addWidgetAfterThisWidget(widgets().constLast()); 0416 } 0417 auto w = qobject_cast<SieveActionWidget *>(widgets().constLast()); 0418 w->setAction(actionName, element, comment, error); 0419 comment.clear(); 0420 } else { 0421 qCDebug(LIBKSIEVEUI_LOG) << " SieveActionWidgetLister::loadScript don't have name attribute " << tagName; 0422 } 0423 previousActionWasAComment = false; 0424 } else if (tagName == QLatin1StringView("comment")) { 0425 if (!comment.isEmpty()) { 0426 comment += QLatin1Char('\n'); 0427 } 0428 previousActionWasAComment = true; 0429 comment += element.readElementText(); 0430 } else if (tagName == QLatin1StringView("crlf")) { 0431 // Add new line if previous action was a comment 0432 if (previousActionWasAComment) { 0433 comment += QLatin1Char('\n'); 0434 } 0435 element.skipCurrentElement(); 0436 } else { 0437 qCDebug(LIBKSIEVEUI_LOG) << " SieveActionWidgetLister::loadScript unknown tagName " << tagName; 0438 } 0439 } 0440 } 0441 } 0442 0443 #include "moc_sieveactionwidgetlister.cpp"