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

0001 /*
0002   SPDX-FileCopyrightText: 2014-2024 Laurent Montel <montel@kde.org>
0003 
0004   SPDX-License-Identifier: GPL-2.0-or-later
0005 */
0006 
0007 #include "kmfilterlistbox.h"
0008 #include "filteractions/filteractiondict.h"
0009 #include "filtermanager.h"
0010 #include "invalidfilters/invalidfilterdialog.h"
0011 #include "invalidfilters/invalidfilterinfo.h"
0012 #include "mailcommon_debug.h"
0013 #include "mailfilter.h"
0014 #include <KListWidgetSearchLine>
0015 #include <KLocalizedString>
0016 #include <KMessageBox>
0017 #include <QHBoxLayout>
0018 #include <QInputDialog>
0019 #include <QKeyEvent>
0020 #include <QListWidget>
0021 #include <QPointer>
0022 #include <QPushButton>
0023 #include <QShortcut>
0024 #include <QVBoxLayout>
0025 
0026 //=============================================================================
0027 //
0028 // class KMFilterListBox (the filter list manipulator)
0029 //
0030 //=============================================================================
0031 using namespace MailCommon;
0032 KMFilterListBox::KMFilterListBox(const QString &title, QWidget *parent)
0033     : QGroupBox(title, parent)
0034 {
0035     auto layout = new QVBoxLayout(this);
0036 
0037     //----------- the list box
0038     mListWidget = new QListWidget(this);
0039     mListWidget->setMinimumWidth(150);
0040     mListWidget->setWhatsThis(
0041         i18n("<qt><p>This is the list of defined filters. "
0042              "They are processed top-to-bottom.</p>"
0043              "<p>Click on any filter to edit it "
0044              "using the controls in the right-hand half "
0045              "of the dialog.</p></qt>"));
0046     mListWidget->setDragDropMode(QAbstractItemView::InternalMove);
0047     mListWidget->setSelectionMode(QAbstractItemView::ExtendedSelection);
0048     connect(mListWidget->model(), &QAbstractItemModel::rowsMoved, this, &KMFilterListBox::slotRowsMoved);
0049 
0050     mSearchListWidget = new KListWidgetSearchLine(this, mListWidget);
0051     mSearchListWidget->setPlaceholderText(i18nc("@info Displayed grayed-out inside the textbox, verb to search", "Search"));
0052     mSearchListWidget->installEventFilter(this);
0053     layout->addWidget(mSearchListWidget);
0054     layout->addWidget(mListWidget);
0055 
0056     //----------- the first row of buttons
0057     auto hb = new QWidget(this);
0058     auto hbHBoxLayout = new QHBoxLayout(hb);
0059     hbHBoxLayout->setContentsMargins({});
0060     hbHBoxLayout->setSpacing(4);
0061 
0062     mBtnTop = new QPushButton(QString(), hb);
0063     hbHBoxLayout->addWidget(mBtnTop);
0064     mBtnTop->setIcon(QIcon::fromTheme(QStringLiteral("go-top")));
0065     mBtnTop->setMinimumSize(mBtnTop->sizeHint() * 1.2);
0066 
0067     mBtnUp = new QPushButton(QString(), hb);
0068     hbHBoxLayout->addWidget(mBtnUp);
0069     mBtnUp->setAutoRepeat(true);
0070     mBtnUp->setIcon(QIcon::fromTheme(QStringLiteral("go-up")));
0071     mBtnUp->setMinimumSize(mBtnUp->sizeHint() * 1.2);
0072     mBtnDown = new QPushButton(QString(), hb);
0073     hbHBoxLayout->addWidget(mBtnDown);
0074     mBtnDown->setAutoRepeat(true);
0075     mBtnDown->setIcon(QIcon::fromTheme(QStringLiteral("go-down")));
0076     mBtnDown->setMinimumSize(mBtnDown->sizeHint() * 1.2);
0077 
0078     mBtnBottom = new QPushButton(QString(), hb);
0079     hbHBoxLayout->addWidget(mBtnBottom);
0080     mBtnBottom->setIcon(QIcon::fromTheme(QStringLiteral("go-bottom")));
0081     mBtnBottom->setMinimumSize(mBtnBottom->sizeHint() * 1.2);
0082 
0083     mBtnUp->setToolTip(i18nc("Move selected filter up.", "Up"));
0084     mBtnDown->setToolTip(i18nc("Move selected filter down.", "Down"));
0085     mBtnTop->setToolTip(i18nc("Move selected filter to the top.", "Top"));
0086     mBtnBottom->setToolTip(i18nc("Move selected filter to the bottom.", "Bottom"));
0087     mBtnUp->setWhatsThis(
0088         i18n("<qt><p>Click this button to move the currently-"
0089              "selected filter <em>up</em> one in the list above.</p>"
0090              "<p>This is useful since the order of the filters in the list "
0091              "determines the order in which they are tried on messages: "
0092              "The topmost filter gets tried first.</p>"
0093              "<p>If you have clicked this button accidentally, you can undo this "
0094              "by clicking on the <em>Down</em> button.</p></qt>"));
0095     mBtnDown->setWhatsThis(
0096         i18n("<qt><p>Click this button to move the currently-"
0097              "selected filter <em>down</em> one in the list above.</p>"
0098              "<p>This is useful since the order of the filters in the list "
0099              "determines the order in which they are tried on messages: "
0100              "The topmost filter gets tried first.</p>"
0101              "<p>If you have clicked this button accidentally, you can undo this "
0102              "by clicking on the <em>Up</em> button.</p></qt>"));
0103     mBtnBottom->setWhatsThis(
0104         i18n("<qt><p>Click this button to move the currently-"
0105              "selected filter to bottom of list.</p>"
0106              "<p>This is useful since the order of the filters in the list "
0107              "determines the order in which they are tried on messages: "
0108              "The topmost filter gets tried first.</p></qt>"));
0109     mBtnTop->setWhatsThis(
0110         i18n("<qt><p>Click this button to move the currently-"
0111              "selected filter to top of list.</p>"
0112              "<p>This is useful since the order of the filters in the list "
0113              "determines the order in which they are tried on messages: "
0114              "The topmost filter gets tried first.</p></qt>"));
0115 
0116     layout->addWidget(hb);
0117 
0118     //----------- the second row of buttons
0119     hb = new QWidget(this);
0120     hbHBoxLayout = new QHBoxLayout(hb);
0121     hbHBoxLayout->setContentsMargins({});
0122     hbHBoxLayout->setSpacing(4);
0123     mBtnNew = new QPushButton(hb);
0124     hbHBoxLayout->addWidget(mBtnNew);
0125     mBtnNew->setIcon(QIcon::fromTheme(QStringLiteral("document-new")));
0126     mBtnNew->setMinimumSize(mBtnNew->sizeHint() * 1.2);
0127     mBtnCopy = new QPushButton(hb);
0128     hbHBoxLayout->addWidget(mBtnCopy);
0129     mBtnCopy->setIcon(QIcon::fromTheme(QStringLiteral("edit-copy")));
0130     mBtnCopy->setMinimumSize(mBtnCopy->sizeHint() * 1.2);
0131     mBtnDelete = new QPushButton(hb);
0132     hbHBoxLayout->addWidget(mBtnDelete);
0133     mBtnDelete->setIcon(QIcon::fromTheme(QStringLiteral("edit-delete")));
0134     mBtnDelete->setMinimumSize(mBtnDelete->sizeHint() * 1.2);
0135     mBtnRename = new QPushButton(hb);
0136     mBtnRename->setIcon(QIcon::fromTheme(QStringLiteral("edit-rename")));
0137     mBtnRename->setMinimumSize(mBtnDelete->sizeHint() * 1.2);
0138 
0139     hbHBoxLayout->addWidget(mBtnRename);
0140     mBtnNew->setToolTip(i18nc("@action:button in filter list manipulator", "New"));
0141     mBtnCopy->setToolTip(i18n("Copy"));
0142     mBtnDelete->setToolTip(i18n("Delete"));
0143     mBtnRename->setToolTip(i18n("Rename"));
0144     mBtnNew->setWhatsThis(
0145         i18n("<qt><p>Click this button to create a new filter.</p>"
0146              "<p>The filter will be inserted just before the currently-"
0147              "selected one, but you can always change that "
0148              "later on.</p>"
0149              "<p>If you have clicked this button accidentally, you can undo this "
0150              "by clicking on the <em>Delete</em> button.</p></qt>"));
0151     mBtnCopy->setWhatsThis(
0152         i18n("<qt><p>Click this button to copy a filter.</p>"
0153              "<p>If you have clicked this button accidentally, you can undo this "
0154              "by clicking on the <em>Delete</em> button.</p></qt>"));
0155     mBtnDelete->setWhatsThis(
0156         i18n("<qt><p>Click this button to <em>delete</em> the currently-"
0157              "selected filter from the list above.</p>"
0158              "<p>There is no way to get the filter back once "
0159              "it is deleted, but you can always leave the "
0160              "dialog by clicking <em>Cancel</em> to discard the "
0161              "changes made.</p></qt>"));
0162     mBtnRename->setWhatsThis(
0163         i18n("<qt><p>Click this button to rename the currently-selected filter.</p>"
0164              "<p>Filters are named automatically, as long as they start with "
0165              "\"&lt;\".</p>"
0166              "<p>If you have renamed a filter accidentally and want automatic "
0167              "naming back, click this button and select <em>Clear</em> followed "
0168              "by <em>OK</em> in the appearing dialog.</p></qt>"));
0169 
0170     layout->addWidget(hb);
0171 
0172     auto shortcut = new QShortcut(this);
0173     shortcut->setKey(Qt::Key_Delete);
0174     connect(shortcut, &QShortcut::activated, this, &KMFilterListBox::slotDelete);
0175 
0176     //----------- now connect everything
0177     connect(mListWidget, &QListWidget::currentRowChanged, this, &KMFilterListBox::slotSelected);
0178     connect(mListWidget, &QListWidget::itemDoubleClicked, this, &KMFilterListBox::slotRename);
0179     connect(mListWidget, &QListWidget::itemChanged, this, &KMFilterListBox::slotFilterEnabledChanged);
0180 
0181     connect(mListWidget, &QListWidget::itemSelectionChanged, this, &KMFilterListBox::slotSelectionChanged);
0182 
0183     connect(mBtnUp, &QPushButton::clicked, this, &KMFilterListBox::slotUp);
0184     connect(mBtnDown, &QPushButton::clicked, this, &KMFilterListBox::slotDown);
0185     connect(mBtnTop, &QPushButton::clicked, this, &KMFilterListBox::slotTop);
0186     connect(mBtnBottom, &QPushButton::clicked, this, &KMFilterListBox::slotBottom);
0187 
0188     connect(mBtnNew, &QPushButton::clicked, this, &KMFilterListBox::slotNew);
0189     connect(mBtnCopy, &QPushButton::clicked, this, &KMFilterListBox::slotCopy);
0190     connect(mBtnDelete, &QPushButton::clicked, this, &KMFilterListBox::slotDelete);
0191     connect(mBtnRename, &QPushButton::clicked, this, &KMFilterListBox::slotRename);
0192 
0193     // the dialog should call loadFilterList()
0194     // when all signals are connected.
0195     enableControls();
0196 }
0197 
0198 KMFilterListBox::~KMFilterListBox() = default;
0199 
0200 bool KMFilterListBox::eventFilter(QObject *obj, QEvent *event)
0201 {
0202     if (event->type() == QEvent::KeyPress && obj == mSearchListWidget) {
0203         auto key = static_cast<QKeyEvent *>(event);
0204         if ((key->key() == Qt::Key_Enter) || (key->key() == Qt::Key_Return)) {
0205             event->accept();
0206             return true;
0207         }
0208     }
0209     return QGroupBox::eventFilter(obj, event);
0210 }
0211 
0212 bool KMFilterListBox::itemIsValid(QListWidgetItem *item) const
0213 {
0214     if (!item) {
0215         qCDebug(MAILCOMMON_LOG) << "Called while no filter is selected, ignoring.";
0216         return false;
0217     }
0218     if (item->isHidden()) {
0219         return false;
0220     }
0221     return true;
0222 }
0223 
0224 void KMFilterListBox::slotFilterEnabledChanged(QListWidgetItem *item)
0225 {
0226     if (!item) {
0227         qCDebug(MAILCOMMON_LOG) << "Called while no filter is selected, ignoring.";
0228         return;
0229     }
0230     auto itemFilter = static_cast<QListWidgetFilterItem *>(item);
0231     MailCommon::MailFilter *filter = itemFilter->filter();
0232     filter->setEnabled((item->checkState() == Qt::Checked));
0233     Q_EMIT filterUpdated(filter);
0234 }
0235 
0236 void KMFilterListBox::slotRowsMoved(const QModelIndex &, int sourcestart, int sourceEnd, const QModelIndex &, int destinationRow)
0237 {
0238     Q_UNUSED(sourceEnd)
0239     Q_UNUSED(sourcestart)
0240     Q_UNUSED(destinationRow)
0241     enableControls();
0242 
0243     Q_EMIT filterOrderAltered();
0244 }
0245 
0246 void KMFilterListBox::createFilter(const QByteArray &field, const QString &value)
0247 {
0248     SearchRule::Ptr newRule = SearchRule::createInstance(field, SearchRule::FuncContains, value);
0249 
0250     auto newFilter = new MailFilter();
0251     newFilter->pattern()->append(newRule);
0252     newFilter->pattern()->setName(QStringLiteral("<%1>: %2").arg(QString::fromLatin1(field), value));
0253 
0254     FilterActionDesc *desc = MailCommon::FilterManager::filterActionDict()->value(QStringLiteral("transfer"));
0255     if (desc) {
0256         newFilter->actions()->append(desc->create());
0257     }
0258 
0259     insertFilter(newFilter);
0260     enableControls();
0261 }
0262 
0263 void KMFilterListBox::slotUpdateFilterName()
0264 {
0265     QListWidgetItem *item = mListWidget->currentItem();
0266     if (!item) {
0267         qCDebug(MAILCOMMON_LOG) << "Called while no filter is selected, ignoring.";
0268         return;
0269     }
0270     auto itemFilter = static_cast<QListWidgetFilterItem *>(item);
0271     MailCommon::MailFilter *filter = itemFilter->filter();
0272 
0273     SearchPattern *p = filter->pattern();
0274     if (!p) {
0275         return;
0276     }
0277 
0278     QString shouldBeName = p->name();
0279     QString displayedName = itemFilter->text();
0280 
0281     if (shouldBeName.trimmed().isEmpty()) {
0282         filter->setAutoNaming(true);
0283     }
0284 
0285     if (filter->isAutoNaming()) {
0286         // auto-naming of patterns
0287         if (!p->isEmpty() && p->first() && !p->first()->field().trimmed().isEmpty()) {
0288             shouldBeName = QStringLiteral("<%1>: %2").arg(QString::fromLatin1(p->first()->field()), p->first()->contents());
0289         } else {
0290             shouldBeName = QLatin1Char('<') + i18n("unnamed") + QLatin1Char('>');
0291         }
0292         p->setName(shouldBeName);
0293     }
0294 
0295     if (displayedName == shouldBeName) {
0296         return;
0297     }
0298 
0299     filter->setToolbarName(shouldBeName);
0300 
0301     mListWidget->blockSignals(true);
0302     itemFilter->setText(shouldBeName);
0303     mListWidget->blockSignals(false);
0304 }
0305 
0306 void KMFilterListBox::slotAccepted()
0307 {
0308     applyFilterChanged(true);
0309 }
0310 
0311 void KMFilterListBox::slotApplied()
0312 {
0313     applyFilterChanged(false);
0314 }
0315 
0316 void KMFilterListBox::applyFilterChanged(bool closeAfterSaving)
0317 {
0318     if (mListWidget->currentItem()) {
0319         Q_EMIT applyWidgets();
0320         slotSelected(mListWidget->currentRow());
0321     }
0322 
0323     // by now all edit widgets should have written back
0324     // their widget's data into our filter list.
0325 
0326     bool wasCanceled = false;
0327     const QList<MailFilter *> newFilters = filtersForSaving(closeAfterSaving, wasCanceled);
0328     if (!wasCanceled) {
0329         MailCommon::FilterManager::instance()->setFilters(newFilters);
0330     }
0331 }
0332 
0333 QList<MailFilter *> KMFilterListBox::filtersForSaving(bool closeAfterSaving, bool &wasCanceled) const
0334 {
0335     Q_EMIT const_cast<KMFilterListBox *>(this)->applyWidgets(); // signals aren't const
0336     QList<MailFilter *> filters;
0337     QStringList emptyFilters;
0338     QList<MailCommon::InvalidFilterInfo> listInvalidFilters;
0339     const int numberOfFilter(mListWidget->count());
0340     for (int i = 0; i < numberOfFilter; ++i) {
0341         auto itemFilter = static_cast<QListWidgetFilterItem *>(mListWidget->item(i));
0342         auto f = new MailFilter(*itemFilter->filter()); // deep copy
0343 
0344         const QString information = f->purify();
0345         if (!f->isEmpty() && information.isEmpty()) {
0346             // the filter is valid:
0347             filters.append(f);
0348         } else {
0349             // the filter is invalid:
0350             emptyFilters << f->name();
0351             listInvalidFilters.append(MailCommon::InvalidFilterInfo(f->name(), information));
0352             delete f;
0353         }
0354     }
0355 
0356     // report on invalid filters:
0357     if (!emptyFilters.empty()) {
0358         QPointer<MailCommon::InvalidFilterDialog> dlg = new MailCommon::InvalidFilterDialog(nullptr);
0359         dlg->setInvalidFilters(listInvalidFilters);
0360         if (!dlg->exec()) {
0361             if (closeAfterSaving) {
0362                 Q_EMIT abortClosing();
0363             }
0364             wasCanceled = true;
0365         }
0366         delete dlg;
0367     }
0368     return filters;
0369 }
0370 
0371 void KMFilterListBox::slotSelectionChanged()
0372 {
0373     if (mListWidget->selectedItems().count() > 1) {
0374         Q_EMIT resetWidgets();
0375     }
0376     enableControls();
0377 }
0378 
0379 void KMFilterListBox::slotSelected(int aIdx)
0380 {
0381     if (aIdx >= 0 && aIdx < mListWidget->count()) {
0382         auto itemFilter = static_cast<QListWidgetFilterItem *>(mListWidget->item(aIdx));
0383         MailFilter *f = itemFilter->filter();
0384 
0385         if (f) {
0386             Q_EMIT filterSelected(f);
0387         } else {
0388             Q_EMIT resetWidgets();
0389         }
0390     } else {
0391         Q_EMIT resetWidgets();
0392     }
0393     enableControls();
0394 }
0395 
0396 void KMFilterListBox::slotNew()
0397 {
0398     QListWidgetItem *item = mListWidget->currentItem();
0399     if (item && item->isHidden()) {
0400         return;
0401     }
0402     // just insert a new filter.
0403     insertFilter(new MailFilter());
0404     enableControls();
0405 }
0406 
0407 void KMFilterListBox::slotCopy()
0408 {
0409     QListWidgetItem *item = mListWidget->currentItem();
0410     if (!itemIsValid(item)) {
0411         return;
0412     }
0413 
0414     // make sure that all changes are written to the filter before we copy it
0415     Q_EMIT applyWidgets();
0416     auto itemFilter = static_cast<QListWidgetFilterItem *>(item);
0417 
0418     MailFilter *filter = itemFilter->filter();
0419 
0420     // enableControls should make sure this method is
0421     // never called when no filter is selected.
0422     Q_ASSERT(filter);
0423 
0424     // inserts a copy of the current filter.
0425     auto copyFilter = new MailFilter(*filter);
0426     copyFilter->generateRandomIdentifier();
0427     copyFilter->setShortcut(QKeySequence());
0428 
0429     insertFilter(copyFilter);
0430     enableControls();
0431 }
0432 
0433 void KMFilterListBox::slotDelete()
0434 {
0435     QListWidgetItem *itemFirst = mListWidget->currentItem();
0436     if (!itemIsValid(itemFirst)) {
0437         return;
0438     }
0439     const bool uniqFilterSelected = (mListWidget->selectedItems().count() == 1);
0440 
0441     auto itemFilter = static_cast<QListWidgetFilterItem *>(itemFirst);
0442     MailCommon::MailFilter *filter = itemFilter->filter();
0443     const QString question =
0444         uniqFilterSelected ? i18n("Do you want to remove the filter \"%1\"?", filter->pattern()->name()) : i18n("Do you want to remove selected filters?");
0445     const QString dialogTitle = uniqFilterSelected ? i18n("Remove Filter") : i18n("Remove Filters");
0446     const int answer = KMessageBox::questionTwoActions(this, question, dialogTitle, KStandardGuiItem::remove(), KStandardGuiItem::cancel());
0447     if (answer == KMessageBox::ButtonCode::SecondaryAction) {
0448         return;
0449     }
0450 
0451     const int oIdxSelItem = mListWidget->currentRow();
0452     QList<MailCommon::MailFilter *> lst;
0453 
0454     Q_EMIT resetWidgets();
0455 
0456     const QList<QListWidgetItem *> lstItems = mListWidget->selectedItems();
0457     for (QListWidgetItem *item : lstItems) {
0458         auto itemFilter = static_cast<QListWidgetFilterItem *>(item);
0459 
0460         MailCommon::MailFilter *filter = itemFilter->filter();
0461         lst << filter;
0462 
0463         // remove the filter from both the listbox
0464         QListWidgetItem *item2 = mListWidget->takeItem(mListWidget->row(item));
0465         delete item2;
0466     }
0467     const int count = mListWidget->count();
0468     // and set the new current item.
0469     if (count > oIdxSelItem) {
0470         // oIdxItem is still a valid index
0471         mListWidget->setCurrentRow(oIdxSelItem);
0472     } else if (count) {
0473         // oIdxSelIdx is no longer valid, but the
0474         // list box isn't empty
0475         mListWidget->setCurrentRow(count - 1);
0476     }
0477 
0478     // work around a problem when deleting the first item in a QListWidget:
0479     // after takeItem, slotSelectionChanged is emitted with 1, but the row 0
0480     // remains selected and another selectCurrentRow(0) does not trigger the
0481     // selectionChanged signal
0482     // (qt-copy as of 2006-12-22 / gungl)
0483     if (oIdxSelItem == 0) {
0484         slotSelected(0);
0485     }
0486     enableControls();
0487 
0488     Q_EMIT filterRemoved(lst);
0489 }
0490 
0491 void KMFilterListBox::slotTop()
0492 {
0493     QList<QListWidgetItem *> listWidgetItem = selectedFilter();
0494     if (listWidgetItem.isEmpty()) {
0495         return;
0496     }
0497 
0498     const int numberOfItem(listWidgetItem.count());
0499     if ((numberOfItem == 1) && (mListWidget->currentRow() == 0)) {
0500         qCDebug(MAILCOMMON_LOG) << "Called while the _topmost_ filter is selected, ignoring.";
0501         return;
0502     }
0503 
0504     QListWidgetItem *item = nullptr;
0505     bool wasMoved = false;
0506     for (int i = 0; i < numberOfItem; ++i) {
0507         const int posItem = mListWidget->row(listWidgetItem.at(i));
0508         if (posItem == i) {
0509             continue;
0510         }
0511         item = mListWidget->takeItem(mListWidget->row(listWidgetItem.at(i)));
0512         mListWidget->insertItem(i, item);
0513         wasMoved = true;
0514     }
0515 
0516     if (wasMoved) {
0517         enableControls();
0518         Q_EMIT filterOrderAltered();
0519     }
0520 }
0521 
0522 QList<QListWidgetItem *> KMFilterListBox::selectedFilter()
0523 {
0524     QList<QListWidgetItem *> listWidgetItem;
0525     const int numberOfFilters = mListWidget->count();
0526     for (int i = 0; i < numberOfFilters; ++i) {
0527         if (mListWidget->item(i)->isSelected() && !mListWidget->item(i)->isHidden()) {
0528             listWidgetItem << mListWidget->item(i);
0529         }
0530     }
0531     return listWidgetItem;
0532 }
0533 
0534 QStringList KMFilterListBox::selectedFilterId(SearchRule::RequiredPart &requiredPart, const QString &resource) const
0535 {
0536     QStringList listFilterId;
0537     requiredPart = SearchRule::Envelope;
0538     const int numberOfFilters = mListWidget->count();
0539     for (int i = 0; i < numberOfFilters; ++i) {
0540         if (mListWidget->item(i)->isSelected() && !mListWidget->item(i)->isHidden()) {
0541             MailFilter *filter = static_cast<QListWidgetFilterItem *>(mListWidget->item(i))->filter();
0542             if (!filter->isEmpty()) {
0543                 const QString id = filter->identifier();
0544                 listFilterId << id;
0545                 requiredPart = qMax(requiredPart, static_cast<QListWidgetFilterItem *>(mListWidget->item(i))->filter()->requiredPart(resource));
0546             }
0547         }
0548     }
0549     return listFilterId;
0550 }
0551 
0552 void KMFilterListBox::slotBottom()
0553 {
0554     const QList<QListWidgetItem *> listWidgetItem = selectedFilter();
0555     if (listWidgetItem.isEmpty()) {
0556         return;
0557     }
0558 
0559     const int numberOfElement(mListWidget->count());
0560     const int numberOfItem(listWidgetItem.count());
0561     if ((numberOfItem == 1) && (mListWidget->currentRow() == numberOfElement - 1)) {
0562         qCDebug(MAILCOMMON_LOG) << "Called while the _last_ filter is selected, ignoring.";
0563         return;
0564     }
0565 
0566     QListWidgetItem *item = nullptr;
0567     int j = 0;
0568     bool wasMoved = false;
0569     for (int i = numberOfItem - 1; i >= 0; --i, j++) {
0570         const int posItem = mListWidget->row(listWidgetItem.at(i));
0571         if (posItem == (numberOfElement - 1 - j)) {
0572             continue;
0573         }
0574         item = mListWidget->takeItem(mListWidget->row(listWidgetItem.at(i)));
0575         mListWidget->insertItem(numberOfElement - j, item);
0576         wasMoved = true;
0577     }
0578 
0579     if (wasMoved) {
0580         enableControls();
0581         Q_EMIT filterOrderAltered();
0582     }
0583 }
0584 
0585 void KMFilterListBox::slotUp()
0586 {
0587     const QList<QListWidgetItem *> listWidgetItem = selectedFilter();
0588     if (listWidgetItem.isEmpty()) {
0589         return;
0590     }
0591 
0592     const int numberOfItem(listWidgetItem.count());
0593     if ((numberOfItem == 1) && (mListWidget->currentRow() == 0)) {
0594         qCDebug(MAILCOMMON_LOG) << "Called while the _topmost_ filter is selected, ignoring.";
0595         return;
0596     }
0597     bool wasMoved = false;
0598 
0599     for (int i = 0; i < numberOfItem; ++i) {
0600         const int posItem = mListWidget->row(listWidgetItem.at(i));
0601         if (posItem == i) {
0602             continue;
0603         }
0604         swapNeighbouringFilters(posItem, posItem - 1);
0605         wasMoved = true;
0606     }
0607     if (wasMoved) {
0608         enableControls();
0609         Q_EMIT filterOrderAltered();
0610     }
0611 }
0612 
0613 void KMFilterListBox::slotDown()
0614 {
0615     const QList<QListWidgetItem *> listWidgetItem = selectedFilter();
0616     if (listWidgetItem.isEmpty()) {
0617         return;
0618     }
0619 
0620     const int numberOfElement(mListWidget->count());
0621     const int numberOfItem(listWidgetItem.count());
0622     if ((numberOfItem == 1) && (mListWidget->currentRow() == numberOfElement - 1)) {
0623         qCDebug(MAILCOMMON_LOG) << "Called while the _last_ filter is selected, ignoring.";
0624         return;
0625     }
0626 
0627     int j = 0;
0628     bool wasMoved = false;
0629     for (int i = numberOfItem - 1; i >= 0; --i, j++) {
0630         const int posItem = mListWidget->row(listWidgetItem.at(i));
0631         if (posItem == (numberOfElement - 1 - j)) {
0632             continue;
0633         }
0634         swapNeighbouringFilters(posItem, posItem + 1);
0635         wasMoved = true;
0636     }
0637 
0638     if (wasMoved) {
0639         enableControls();
0640         Q_EMIT filterOrderAltered();
0641     }
0642 }
0643 
0644 void KMFilterListBox::slotRename()
0645 {
0646     QListWidgetItem *item = mListWidget->currentItem();
0647     if (!itemIsValid(item)) {
0648         return;
0649     }
0650     auto itemFilter = static_cast<QListWidgetFilterItem *>(item);
0651 
0652     bool okPressed = false;
0653     MailFilter *filter = itemFilter->filter();
0654 
0655     // enableControls should make sure this method is
0656     // never called when no filter is selected.
0657     Q_ASSERT(filter);
0658 
0659     // allow empty names - those will turn auto-naming on again
0660     QString newName = QInputDialog::getText(window(),
0661                                             i18n("Rename Filter"),
0662                                             i18n("Rename filter \"%1\" to:\n(leave the field empty for automatic naming)", filter->pattern()->name()), /*label*/
0663                                             QLineEdit::Normal,
0664                                             filter->pattern()->name(), /* initial value */
0665                                             &okPressed);
0666 
0667     if (!okPressed) {
0668         return;
0669     }
0670 
0671     if (newName.isEmpty()) {
0672         // bait for slotUpdateFilterName to
0673         // use automatic naming again.
0674         filter->pattern()->setName(QStringLiteral("<>"));
0675         filter->setAutoNaming(true);
0676     } else {
0677         filter->pattern()->setName(newName);
0678         filter->setAutoNaming(false);
0679     }
0680 
0681     slotUpdateFilterName();
0682 
0683     Q_EMIT filterUpdated(filter);
0684 }
0685 
0686 void KMFilterListBox::enableControls()
0687 {
0688     const int currentIndex = mListWidget->currentRow();
0689     const bool theFirst = (currentIndex == 0);
0690     const int numberOfElement(mListWidget->count());
0691     const bool theLast = (currentIndex >= numberOfElement - 1);
0692     const bool aFilterIsSelected = (currentIndex >= 0);
0693 
0694     const int numberOfSelectedItem(mListWidget->selectedItems().count());
0695     const bool uniqFilterSelected = (numberOfSelectedItem == 1);
0696     const bool allItemSelected = (numberOfSelectedItem == numberOfElement);
0697 
0698     mBtnUp->setEnabled(aFilterIsSelected && ((uniqFilterSelected && !theFirst) || (!uniqFilterSelected)) && !allItemSelected);
0699     mBtnDown->setEnabled(aFilterIsSelected && ((uniqFilterSelected && !theLast) || (!uniqFilterSelected)) && !allItemSelected);
0700 
0701     mBtnCopy->setEnabled(aFilterIsSelected && uniqFilterSelected);
0702     mBtnDelete->setEnabled(aFilterIsSelected);
0703     mBtnRename->setEnabled(aFilterIsSelected && uniqFilterSelected);
0704 
0705     mBtnTop->setEnabled(aFilterIsSelected && ((uniqFilterSelected && !theFirst) || (!uniqFilterSelected)) && !allItemSelected);
0706 
0707     mBtnBottom->setEnabled(aFilterIsSelected && ((uniqFilterSelected && !theLast) || (!uniqFilterSelected)) && !allItemSelected);
0708     if (aFilterIsSelected) {
0709         mListWidget->scrollToItem(mListWidget->currentItem());
0710     }
0711 }
0712 
0713 void KMFilterListBox::loadFilterList(bool createDummyFilter)
0714 {
0715     Q_ASSERT(mListWidget);
0716     setEnabled(false);
0717     Q_EMIT resetWidgets();
0718     // we don't want the insertion to
0719     // cause flicker in the edit widgets.
0720     blockSignals(true);
0721 
0722     // clear both lists
0723     mListWidget->clear();
0724 
0725     const QList<MailFilter *> filters = MailCommon::FilterManager::instance()->filters();
0726     for (MailFilter *filter : filters) {
0727         auto item = new QListWidgetFilterItem(filter->pattern()->name(), mListWidget);
0728         item->setFilter(new MailFilter(*filter));
0729         mListWidget->addItem(item);
0730     }
0731 
0732     blockSignals(false);
0733     setEnabled(true);
0734 
0735     // create an empty filter when there's none, to avoid a completely
0736     // disabled dialog (usability tests indicated that the new-filter
0737     // button is too hard to find that way):
0738     const int numberOfItem(mListWidget->count());
0739     if (numberOfItem == 0) {
0740         if (createDummyFilter) {
0741             slotNew();
0742         }
0743     } else {
0744         mListWidget->setCurrentRow(0);
0745     }
0746 
0747     enableControls();
0748 }
0749 
0750 void KMFilterListBox::insertFilter(MailFilter *aFilter)
0751 {
0752     // must be really a filter...
0753     Q_ASSERT(aFilter);
0754     const int currentIndex = mListWidget->currentRow();
0755     // if mIdxSelItem < 0, QListBox::insertItem will append.
0756     auto item = new QListWidgetFilterItem(aFilter->pattern()->name());
0757     item->setFilter(aFilter);
0758     mListWidget->insertItem(currentIndex, item);
0759     mListWidget->clearSelection();
0760     if (currentIndex < 0) {
0761         mListWidget->setCurrentRow(mListWidget->count() - 1);
0762     } else {
0763         // insert just before selected
0764         mListWidget->setCurrentRow(currentIndex);
0765     }
0766 
0767     Q_EMIT filterCreated();
0768     Q_EMIT filterOrderAltered();
0769 }
0770 
0771 void KMFilterListBox::appendFilter(MailFilter *aFilter)
0772 {
0773     auto item = new QListWidgetFilterItem(aFilter->pattern()->name(), mListWidget);
0774 
0775     item->setFilter(aFilter);
0776     mListWidget->addItem(item);
0777 
0778     Q_EMIT filterCreated();
0779 }
0780 
0781 void KMFilterListBox::swapNeighbouringFilters(int untouchedOne, int movedOne)
0782 {
0783     // must be neighbours...
0784     Q_ASSERT(untouchedOne - movedOne == 1 || movedOne - untouchedOne == 1);
0785 
0786     // untouchedOne is at idx. to move it down(up),
0787     // remove item at idx+(-)1 w/o deleting it.
0788     QListWidgetItem *item = mListWidget->takeItem(movedOne);
0789     // now selected item is at idx(idx-1), so
0790     // insert the other item at idx, ie. above(below).
0791     mListWidget->insertItem(untouchedOne, item);
0792 }
0793 
0794 QListWidgetFilterItem::QListWidgetFilterItem(const QString &text, QListWidget *parent)
0795     : QListWidgetItem(text, parent)
0796 {
0797 }
0798 
0799 QListWidgetFilterItem::~QListWidgetFilterItem()
0800 {
0801     delete mFilter;
0802 }
0803 
0804 void QListWidgetFilterItem::setFilter(MailCommon::MailFilter *filter)
0805 {
0806     mFilter = filter;
0807     setCheckState(filter->isEnabled() ? Qt::Checked : Qt::Unchecked);
0808 }
0809 
0810 MailCommon::MailFilter *QListWidgetFilterItem::filter() const
0811 {
0812     return mFilter;
0813 }
0814 
0815 #include "moc_kmfilterlistbox.cpp"