File indexing completed on 2024-11-10 04:50:09
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 "\"<\".</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"