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"