File indexing completed on 2024-09-22 04:50:01

0001 /*
0002   SPDX-FileCopyrightText: 2010 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.com>
0003   SPDX-FileCopyrightText: 2010 Andras Mantia <andras@kdab.com>
0004 
0005   SPDX-License-Identifier: GPL-2.0-or-later
0006 */
0007 
0008 #include "filteractionwidget.h"
0009 #include "filter/filteractions/filteraction.h"
0010 #include "filter/filteractions/filteractiondict.h"
0011 #include "filter/filtermanager.h"
0012 #include "filter/mailfilter.h"
0013 #include "mailcommon_debug.h"
0014 
0015 #include <KLocalizedString>
0016 #include <QComboBox>
0017 #include <QIcon>
0018 #include <QPushButton>
0019 
0020 #include <QGridLayout>
0021 #include <QLabel>
0022 
0023 using namespace MailCommon;
0024 
0025 //=============================================================================
0026 //
0027 // class FilterActionWidget
0028 //
0029 //=============================================================================
0030 
0031 class Q_DECL_HIDDEN FilterActionWidget::FilterActionWidgetPrivate
0032 {
0033 public:
0034     FilterActionWidgetPrivate(FilterActionWidget *qq)
0035         : q(qq)
0036     {
0037     }
0038 
0039     ~FilterActionWidgetPrivate()
0040     {
0041         qDeleteAll(mActionList);
0042         mActionList.clear();
0043     }
0044 
0045     void setFilterAction(QWidget *widget = nullptr);
0046 
0047     void slotFilterTypeChanged(int index);
0048     void slotAddWidget();
0049     void slotRemoveWidget();
0050 
0051     FilterActionWidget *const q;
0052     QList<MailCommon::FilterAction *> mActionList;
0053     QComboBox *mComboBox = nullptr;
0054     QPushButton *mAdd = nullptr;
0055     QPushButton *mRemove = nullptr;
0056 
0057     QGridLayout *mLayout = nullptr;
0058 };
0059 
0060 void FilterActionWidget::FilterActionWidgetPrivate::setFilterAction(QWidget *widget)
0061 {
0062     if (mLayout->itemAtPosition(1, 2)) {
0063         delete mLayout->itemAtPosition(1, 2)->widget();
0064     }
0065 
0066     if (widget) {
0067         mLayout->addWidget(widget, 1, 2);
0068     } else {
0069         mLayout->addWidget(new QLabel(i18n("Please select an action."), q), 1, 2);
0070     }
0071 }
0072 
0073 void FilterActionWidget::FilterActionWidgetPrivate::slotAddWidget()
0074 {
0075     Q_EMIT q->addFilterWidget(q);
0076     Q_EMIT q->filterModified();
0077 }
0078 
0079 void FilterActionWidget::FilterActionWidgetPrivate::slotRemoveWidget()
0080 {
0081     Q_EMIT q->removeFilterWidget(q);
0082     Q_EMIT q->filterModified();
0083 }
0084 
0085 void FilterActionWidget::FilterActionWidgetPrivate::slotFilterTypeChanged(int index)
0086 {
0087     setFilterAction(index < mActionList.count() ? mActionList.at(index)->createParamWidget(q) : nullptr);
0088 }
0089 
0090 FilterActionWidget::FilterActionWidget(QWidget *parent)
0091     : QWidget(parent)
0092     , d(new FilterActionWidgetPrivate(this))
0093 {
0094     auto mainLayout = new QHBoxLayout(this);
0095     mainLayout->setContentsMargins({});
0096     auto widget = new QWidget(this);
0097     mainLayout->addWidget(widget);
0098 
0099     d->mLayout = new QGridLayout(widget);
0100     d->mLayout->setContentsMargins({});
0101 
0102     d->mComboBox = new QComboBox(widget);
0103     d->mComboBox->setMinimumWidth(50);
0104     d->mComboBox->setEditable(false);
0105     Q_ASSERT(d->mComboBox);
0106     d->mLayout->addWidget(d->mComboBox, 1, 1);
0107     d->mAdd = new QPushButton(widget);
0108     d->mAdd->setIcon(QIcon::fromTheme(QStringLiteral("list-add")));
0109     d->mAdd->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed));
0110 
0111     d->mRemove = new QPushButton(widget);
0112     d->mRemove->setIcon(QIcon::fromTheme(QStringLiteral("list-remove")));
0113     d->mRemove->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed));
0114 
0115     mainLayout->setSpacing(4);
0116 
0117     int index;
0118     QList<FilterActionDesc *> list = MailCommon::FilterManager::filterActionDict()->list();
0119     QList<FilterActionDesc *>::const_iterator it;
0120     QList<FilterActionDesc *>::const_iterator end(list.constEnd());
0121     for (index = 0, it = list.constBegin(); it != end; ++it, ++index) {
0122         // create an instance:
0123         FilterAction *action = (*it)->create();
0124 
0125         // append to the list of actions:
0126         d->mActionList.append(action);
0127 
0128         // add (i18n-ized) name to combo box
0129         d->mComboBox->addItem((*it)->label, (*it)->name);
0130 
0131         // Register the FilterAction modification signal
0132         connect(action, &FilterAction::filterActionModified, this, &FilterActionWidget::filterModified);
0133     }
0134 
0135     // widget for the case where no action is selected.
0136     d->mComboBox->addItem(QStringLiteral(" "));
0137     d->mComboBox->setCurrentIndex(index);
0138 
0139     // don't show scroll bars.
0140     d->mComboBox->setMaxCount(d->mComboBox->count());
0141 
0142     // layout management:
0143     // o the combo box is not to be made larger than it's sizeHint(),
0144     //   the parameter widget should grow instead.
0145     // o the whole widget takes all space horizontally, but is fixed vertically.
0146     d->mComboBox->adjustSize();
0147     d->mComboBox->setSizePolicy(QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed));
0148     setSizePolicy(QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed));
0149     updateGeometry();
0150 
0151     // redirect focus to the filter action combo box
0152     setFocusProxy(d->mComboBox);
0153 
0154     // now connect the combo box and the widget stack
0155     connect(d->mComboBox, &QComboBox::activated, this, [this](int index) {
0156         d->slotFilterTypeChanged(index);
0157     });
0158 
0159     connect(d->mComboBox, &QComboBox::activated, this, &FilterActionWidget::filterModified);
0160 
0161     connect(d->mAdd, &QPushButton::clicked, this, [this]() {
0162         d->slotAddWidget();
0163     });
0164     connect(d->mRemove, &QPushButton::clicked, this, [this]() {
0165         d->slotRemoveWidget();
0166     });
0167 
0168     d->setFilterAction();
0169     d->mLayout->addWidget(d->mAdd, 1, 3);
0170     d->mLayout->addWidget(d->mRemove, 1, 4);
0171 }
0172 
0173 FilterActionWidget::~FilterActionWidget() = default;
0174 
0175 void FilterActionWidget::updateAddRemoveButton(bool addButtonEnabled, bool removeButtonEnabled)
0176 {
0177     d->mAdd->setEnabled(addButtonEnabled);
0178     d->mRemove->setEnabled(removeButtonEnabled);
0179 }
0180 
0181 void FilterActionWidget::setAction(const FilterAction *action)
0182 {
0183     bool found = false;
0184     const int count = d->mComboBox->count() - 1; // last entry is the empty one
0185 
0186     const QString name = action ? action->name() : QString();
0187 
0188     // find the index of typeOf(action) in mComboBox
0189     // and clear the other widgets on the way.
0190     for (int i = 0; i < count; ++i) {
0191         if (action && d->mComboBox->itemData(i) == name) {
0192             d->setFilterAction(d->mActionList.at(i)->createParamWidget(this));
0193 
0194             //...set the parameter widget to the settings
0195             // of aAction...
0196             action->setParamWidgetValue(d->mLayout->itemAtPosition(1, 2)->widget());
0197 
0198             //...and show the correct entry of
0199             // the combo box
0200             d->mComboBox->setCurrentIndex(i); // (mm) also raise the widget, but doesn't
0201             found = true;
0202         }
0203     }
0204 
0205     if (found) {
0206         return;
0207     }
0208 
0209     // not found, so set the empty widget
0210     d->setFilterAction();
0211 
0212     d->mComboBox->setCurrentIndex(count); // last item
0213 }
0214 
0215 FilterAction *FilterActionWidget::action() const
0216 {
0217     // look up the action description via the label
0218     // returned by KComboBox::currentText()...
0219     FilterActionDesc *description = MailCommon::FilterManager::filterActionDict()->value(d->mComboBox->itemData(d->mComboBox->currentIndex()).toString());
0220 
0221     if (description) {
0222         // ...create an instance...
0223         FilterAction *action = description->create();
0224         if (action) {
0225             // ...and apply the setting of the parameter widget.
0226             action->applyParamWidgetValue(d->mLayout->itemAtPosition(1, 2)->widget());
0227             return action;
0228         }
0229     }
0230 
0231     return nullptr;
0232 }
0233 
0234 //=============================================================================
0235 //
0236 // class FilterActionWidgetLister (the filter action editor)
0237 //
0238 //=============================================================================
0239 
0240 class FilterActionWidgetLister::FilterActionWidgetListerPrivate
0241 {
0242 public:
0243     FilterActionWidgetListerPrivate(FilterActionWidgetLister *qq)
0244         : q(qq)
0245     {
0246     }
0247 
0248     void regenerateActionListFromWidgets();
0249 
0250     FilterActionWidgetLister *const q;
0251     QList<MailCommon::FilterAction *> *mActionList = nullptr;
0252 };
0253 
0254 void FilterActionWidgetLister::FilterActionWidgetListerPrivate::regenerateActionListFromWidgets()
0255 {
0256     if (!mActionList) {
0257         return;
0258     }
0259 
0260     mActionList->clear();
0261 
0262     const auto lst = q->widgets();
0263     for (const QWidget *widget : lst) {
0264         FilterAction *action = qobject_cast<const FilterActionWidget *>(widget)->action();
0265         if (action) {
0266             mActionList->append(action);
0267         }
0268     }
0269     q->updateAddRemoveButton();
0270 }
0271 
0272 FilterActionWidgetLister::FilterActionWidgetLister(QWidget *parent)
0273     : KWidgetLister(false, 1, MailFilter::filterActionsMaximumSize(), parent)
0274     , d(new FilterActionWidgetListerPrivate(this))
0275 {
0276 }
0277 
0278 FilterActionWidgetLister::~FilterActionWidgetLister() = default;
0279 
0280 void FilterActionWidgetLister::setActionList(QList<FilterAction *> *list)
0281 {
0282     Q_ASSERT(list);
0283     if (d->mActionList && d->mActionList != list) {
0284         d->regenerateActionListFromWidgets();
0285     }
0286 
0287     d->mActionList = list;
0288 
0289     static_cast<QWidget *>(parent())->setEnabled(true);
0290 
0291     if (!widgets().isEmpty()) { // move this below next 'if'?
0292         widgets().constFirst()->blockSignals(true);
0293     }
0294 
0295     if (list->isEmpty()) {
0296         slotClear();
0297         connectWidget(widgets().constFirst(), nullptr);
0298         widgets().constFirst()->blockSignals(false);
0299         return;
0300     }
0301 
0302     int superfluousItems = (int)d->mActionList->count() - widgetsMaximum();
0303     if (superfluousItems > 0) {
0304         qCDebug(MAILCOMMON_LOG) << "FilterActionWidgetLister: Clipping action list to" << widgetsMaximum() << "items!";
0305 
0306         for (; superfluousItems; superfluousItems--) {
0307             d->mActionList->removeLast();
0308         }
0309     }
0310 
0311     // set the right number of widgets
0312     setNumberOfShownWidgetsTo(d->mActionList->count());
0313 
0314     // load the actions into the widgets
0315     QList<QWidget *> widgetList = widgets();
0316     QList<FilterAction *>::const_iterator aEnd(d->mActionList->constEnd());
0317     QList<QWidget *>::ConstIterator wIt = widgetList.constBegin();
0318     QList<QWidget *>::ConstIterator wEnd = widgetList.constEnd();
0319     for (QList<FilterAction *>::const_iterator aIt = d->mActionList->constBegin(); (aIt != aEnd && wIt != wEnd); ++aIt, ++wIt) {
0320         connectWidget((*wIt), (*aIt));
0321     }
0322     widgets().constFirst()->blockSignals(false);
0323     updateAddRemoveButton();
0324 }
0325 
0326 void FilterActionWidgetLister::connectWidget(QWidget *widget, FilterAction *filterAction)
0327 {
0328     auto w = qobject_cast<FilterActionWidget *>(widget);
0329     if (filterAction) {
0330         w->setAction(filterAction);
0331     }
0332     connect(w, &FilterActionWidget::filterModified, this, &FilterActionWidgetLister::filterModified, Qt::UniqueConnection);
0333     reconnectWidget(w);
0334 }
0335 
0336 void FilterActionWidgetLister::slotAddWidget(QWidget *w)
0337 {
0338     addWidgetAfterThisWidget(w);
0339     updateAddRemoveButton();
0340 }
0341 
0342 void FilterActionWidgetLister::slotRemoveWidget(QWidget *w)
0343 {
0344     removeWidget(w);
0345     updateAddRemoveButton();
0346 }
0347 
0348 void FilterActionWidgetLister::updateAddRemoveButton()
0349 {
0350     QList<QWidget *> widgetList = widgets();
0351     const int numberOfWidget(widgetList.count());
0352     bool addButtonEnabled = false;
0353     bool removeButtonEnabled = false;
0354     if (numberOfWidget <= widgetsMinimum()) {
0355         addButtonEnabled = true;
0356         removeButtonEnabled = false;
0357     } else if (numberOfWidget >= widgetsMaximum()) {
0358         addButtonEnabled = false;
0359         removeButtonEnabled = true;
0360     } else {
0361         addButtonEnabled = true;
0362         removeButtonEnabled = true;
0363     }
0364     QList<QWidget *>::ConstIterator wIt = widgetList.constBegin();
0365     QList<QWidget *>::ConstIterator wEnd = widgetList.constEnd();
0366     for (; wIt != wEnd; ++wIt) {
0367         auto w = qobject_cast<FilterActionWidget *>(*wIt);
0368         w->updateAddRemoveButton(addButtonEnabled, removeButtonEnabled);
0369     }
0370 }
0371 
0372 void FilterActionWidgetLister::updateActionList()
0373 {
0374     d->regenerateActionListFromWidgets();
0375 }
0376 
0377 void FilterActionWidgetLister::reset()
0378 {
0379     if (d->mActionList) {
0380         d->regenerateActionListFromWidgets();
0381     }
0382 
0383     d->mActionList = nullptr;
0384     slotClear();
0385 
0386     static_cast<QWidget *>(parent())->setEnabled(false);
0387 }
0388 
0389 void FilterActionWidgetLister::reconnectWidget(FilterActionWidget *w)
0390 {
0391     connect(w, &FilterActionWidget::addFilterWidget, this, &FilterActionWidgetLister::slotAddWidget, Qt::UniqueConnection);
0392 
0393     connect(w, &FilterActionWidget::removeFilterWidget, this, &FilterActionWidgetLister::slotRemoveWidget, Qt::UniqueConnection);
0394 }
0395 
0396 QWidget *FilterActionWidgetLister::createWidget(QWidget *parent)
0397 {
0398     auto w = new FilterActionWidget(parent);
0399     reconnectWidget(w);
0400     return w;
0401 }
0402 
0403 void FilterActionWidgetLister::clearWidget(QWidget *widget)
0404 {
0405     if (widget) {
0406         auto w = static_cast<FilterActionWidget *>(widget);
0407         w->setAction(nullptr);
0408         w->disconnect(this);
0409         reconnectWidget(w);
0410         updateAddRemoveButton();
0411     }
0412 }
0413 
0414 #include "moc_filteractionwidget.cpp"