File indexing completed on 2024-11-24 04:48:16

0001 /*
0002   This file is part of KOrganizer.
0003 
0004   SPDX-FileCopyrightText: 2001 Cornelius Schumacher <schumacher@kde.org>
0005   SPDX-FileCopyrightText: 2004 Reinhold Kainhofer <reinhold@kainhofer.com>
0006   SPDX-FileCopyrightText: 2005 Thomas Zander <zander@kde.org>
0007 
0008   SPDX-License-Identifier: GPL-2.0-or-later WITH Qt-Commercial-exception-1.0
0009 */
0010 
0011 #include "filtereditdialog.h"
0012 #include "korganizer_debug.h"
0013 #include <KStatefulBrush>
0014 
0015 #include <KCalendarCore/CalFilter>
0016 
0017 #include <KLineEditEventHandler>
0018 
0019 #include <Akonadi/TagSelectionDialog>
0020 
0021 #include <PimCommon/PimUtil>
0022 
0023 #include <KColorScheme>
0024 #include <KMessageBox>
0025 
0026 #include <QDialogButtonBox>
0027 
0028 FilterEditDialog::FilterEditDialog(QList<KCalendarCore::CalFilter *> *filters, QWidget *parent)
0029     : QDialog(parent)
0030 {
0031     setWindowTitle(i18nc("@title::window", "Edit Calendar Filters"));
0032     auto mainLayout = new QVBoxLayout(this);
0033 
0034     auto buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel | QDialogButtonBox::Apply, this);
0035     mOkButton = buttonBox->button(QDialogButtonBox::Ok);
0036     mApplyButton = buttonBox->button(QDialogButtonBox::Apply);
0037     mOkButton->setDefault(true);
0038     mOkButton->setShortcut(Qt::CTRL | Qt::Key_Return);
0039     connect(buttonBox, &QDialogButtonBox::accepted, this, &FilterEditDialog::slotOk);
0040     connect(buttonBox, &QDialogButtonBox::rejected, this, &FilterEditDialog::reject);
0041     mainLayout->addWidget(mFilterEdit = new FilterEdit(filters, this));
0042     mainLayout->addWidget(buttonBox);
0043 
0044     connect(mFilterEdit, &FilterEdit::dataConsistent, this, &FilterEditDialog::setDialogConsistent);
0045     updateFilterList();
0046     connect(mFilterEdit, &FilterEdit::editCategories, this, &FilterEditDialog::editCategories);
0047     connect(mFilterEdit, &FilterEdit::filterChanged, this, &FilterEditDialog::filterChanged);
0048     connect(buttonBox->button(QDialogButtonBox::Apply), &QPushButton::clicked, this, &FilterEditDialog::slotApply);
0049 }
0050 
0051 FilterEditDialog::~FilterEditDialog()
0052 {
0053     delete mFilterEdit;
0054     mFilterEdit = nullptr;
0055 }
0056 
0057 void FilterEditDialog::updateFilterList()
0058 {
0059     mFilterEdit->updateFilterList();
0060 }
0061 
0062 void FilterEditDialog::slotApply()
0063 {
0064     mFilterEdit->saveChanges();
0065 }
0066 
0067 void FilterEditDialog::slotOk()
0068 {
0069     slotApply();
0070     accept();
0071 }
0072 
0073 void FilterEditDialog::setDialogConsistent(bool consistent)
0074 {
0075     mOkButton->setEnabled(consistent);
0076     mApplyButton->setEnabled(consistent);
0077 }
0078 
0079 FilterEdit::FilterEdit(QList<KCalendarCore::CalFilter *> *filters, QWidget *parent)
0080     : QWidget(parent)
0081 {
0082     setupUi(this);
0083     searchline->setListWidget(mRulesList);
0084     KLineEditEventHandler::catchReturnKey(mNameLineEdit);
0085     mDetailsFrame->setEnabled(false);
0086     mFilters = filters;
0087     mNewButton->setWhatsThis(i18nc("@info:whatsthis", "Press this button to define a new filter."));
0088     mDeleteButton->setWhatsThis(i18nc("@info:whatsthis", "Press this button to remove the currently active filter."));
0089 
0090     connect(mRulesList, &QListWidget::itemSelectionChanged, this, qOverload<>(&FilterEdit::filterSelected));
0091 
0092     connect(mNewButton, &QPushButton::clicked, this, &FilterEdit::bNewPressed);
0093     connect(mDeleteButton, &QPushButton::clicked, this, &FilterEdit::bDeletePressed);
0094     connect(mNameLineEdit, &QLineEdit::textChanged, this, &FilterEdit::updateSelectedName);
0095     connect(mCatEditButton, &QPushButton::clicked, this, &FilterEdit::editCategorySelection);
0096     connect(mCompletedCheck, &QCheckBox::toggled, mCompletedTimeSpanLabel, &QLabel::setEnabled);
0097     connect(mCompletedCheck, &QCheckBox::toggled, mCompletedTimeSpan, &QSpinBox::setEnabled);
0098 }
0099 
0100 FilterEdit::~FilterEdit() = default;
0101 
0102 void FilterEdit::updateFilterList()
0103 {
0104     mRulesList->clear();
0105     if (!mFilters || mFilters->empty()) {
0106         mDetailsFrame->setEnabled(false);
0107         Q_EMIT dataConsistent(false);
0108     } else {
0109         QList<KCalendarCore::CalFilter *>::iterator i;
0110         QList<KCalendarCore::CalFilter *>::iterator end(mFilters->end());
0111         for (i = mFilters->begin(); i != end; ++i) {
0112             if (*i) {
0113                 mRulesList->addItem((*i)->name());
0114             }
0115         }
0116         if (mRulesList->currentRow() != -1) {
0117             KCalendarCore::CalFilter *f = mFilters->at(mRulesList->currentRow());
0118             if (f) {
0119                 filterSelected(f);
0120             }
0121         }
0122         Q_EMIT dataConsistent(true);
0123     }
0124     if (mFilters && !mFilters->isEmpty() && !mCurrent) {
0125         filterSelected(mFilters->at(0));
0126     }
0127     if (mFilters) {
0128         mDeleteButton->setEnabled(!mFilters->isEmpty());
0129     }
0130 }
0131 
0132 void FilterEdit::saveChanges()
0133 {
0134     if (!mCurrent) {
0135         return;
0136     }
0137 
0138     mCurrent->setName(mNameLineEdit->text());
0139     int criteria = 0;
0140     if (mCompletedCheck->isChecked()) {
0141         criteria |= KCalendarCore::CalFilter::HideCompletedTodos;
0142     }
0143     if (mRecurringCheck->isChecked()) {
0144         criteria |= KCalendarCore::CalFilter::HideRecurring;
0145     }
0146     if (mCatShowCheck->isChecked()) {
0147         criteria |= KCalendarCore::CalFilter::ShowCategories;
0148     }
0149     if (mHideInactiveTodosCheck->isChecked()) {
0150         criteria |= KCalendarCore::CalFilter::HideInactiveTodos;
0151     }
0152     if (mHideTodosNotAssignedToMeCheck->isChecked()) {
0153         criteria |= KCalendarCore::CalFilter::HideNoMatchingAttendeeTodos;
0154     }
0155     mCurrent->setCriteria(criteria);
0156     mCurrent->setCompletedTimeSpan(mCompletedTimeSpan->value());
0157 
0158     QStringList categoryList;
0159     const int numberOfCat(mCatList->count());
0160     categoryList.reserve(numberOfCat);
0161     for (int i = 0; i < numberOfCat; ++i) {
0162         QListWidgetItem *item = mCatList->item(i);
0163         if (item) {
0164             categoryList.append(item->text());
0165         }
0166     }
0167     mCurrent->setCategoryList(categoryList);
0168     Q_EMIT filterChanged();
0169 }
0170 
0171 void FilterEdit::filterSelected()
0172 {
0173     if (mRulesList->currentRow() < mFilters->count()) {
0174         mDetailsFrame->setEnabled(true);
0175         filterSelected(mFilters->at(mRulesList->currentRow()));
0176     }
0177 }
0178 
0179 void FilterEdit::filterSelected(KCalendarCore::CalFilter *filter)
0180 {
0181     if (!filter || filter == mCurrent) {
0182         return;
0183     }
0184     qCDebug(KORGANIZER_LOG) << "Selected filter" << filter->name();
0185     saveChanges();
0186 
0187     mCurrent = filter;
0188     mNameLineEdit->blockSignals(true);
0189     mNameLineEdit->setText(mCurrent->name());
0190     mNameLineEdit->blockSignals(false);
0191     mDetailsFrame->setEnabled(true);
0192     mCompletedCheck->setChecked(mCurrent->criteria() & KCalendarCore::CalFilter::HideCompletedTodos);
0193     mCompletedTimeSpan->setValue(mCurrent->completedTimeSpan());
0194     mRecurringCheck->setChecked(mCurrent->criteria() & KCalendarCore::CalFilter::HideRecurring);
0195     mHideInactiveTodosCheck->setChecked(mCurrent->criteria() & KCalendarCore::CalFilter::HideInactiveTodos);
0196     mHideTodosNotAssignedToMeCheck->setChecked(mCurrent->criteria() & KCalendarCore::CalFilter::HideNoMatchingAttendeeTodos);
0197 
0198     if (mCurrent->criteria() & KCalendarCore::CalFilter::ShowCategories) {
0199         mCatShowCheck->setChecked(true);
0200     } else {
0201         mCatHideCheck->setChecked(true);
0202     }
0203     mCatList->clear();
0204     mCatList->addItems(mCurrent->categoryList());
0205 }
0206 
0207 void FilterEdit::bNewPressed()
0208 {
0209     mDetailsFrame->setEnabled(true);
0210     saveChanges();
0211     QStringList filterNames;
0212     const int numRules = mRulesList->count();
0213     filterNames.reserve(numRules);
0214     for (int i = 0; i < numRules; ++i) {
0215         filterNames << mRulesList->item(i)->text();
0216     }
0217     QString newFilterName;
0218     for (int i = 1;; ++i) {
0219         newFilterName = i18nc("@label default filter name", "New Filter %1", i);
0220         if (!filterNames.contains(newFilterName)) {
0221             break;
0222         }
0223     }
0224 
0225     auto newFilter = new KCalendarCore::CalFilter(newFilterName);
0226     mFilters->append(newFilter);
0227     updateFilterList();
0228     mRulesList->setCurrentRow(mRulesList->count() - 1);
0229     Q_EMIT filterChanged();
0230 }
0231 
0232 void FilterEdit::bDeletePressed()
0233 {
0234     if (!mRulesList->currentItem()) { // nothing selected
0235         return;
0236     }
0237     if (mFilters->isEmpty()) { // We need at least a default filter object.
0238         return;
0239     }
0240 
0241     if (KMessageBox::warningContinueCancel(this,
0242                                            i18nc("@info", "Do you really want to permanently remove the filter \"%1\"?", mCurrent->name()),
0243                                            i18nc("@title:window", "Delete Filter?"),
0244                                            KStandardGuiItem::del())
0245         == KMessageBox::Cancel) {
0246         return;
0247     }
0248 
0249     int selected = mRulesList->currentRow();
0250     KCalendarCore::CalFilter *filter = mFilters->at(selected);
0251     mFilters->removeAll(filter);
0252     delete filter;
0253     mCurrent = nullptr;
0254     updateFilterList();
0255     mRulesList->setCurrentRow(qMin(mRulesList->count() - 1, selected));
0256     Q_EMIT filterChanged();
0257 }
0258 
0259 void FilterEdit::updateSelectedName(const QString &newText)
0260 {
0261     mRulesList->blockSignals(true);
0262     QListWidgetItem *item = mRulesList->currentItem();
0263     if (item) {
0264         item->setText(newText);
0265     }
0266     mRulesList->blockSignals(false);
0267     if (correctName(newText)) {
0268         Q_EMIT dataConsistent(false);
0269         return;
0270     }
0271     bool allOk = true;
0272 
0273     for (KCalendarCore::CalFilter *i : std::as_const(*mFilters)) {
0274         if (i && i->name().isEmpty()) {
0275             allOk = false;
0276         }
0277     }
0278 
0279     Q_EMIT dataConsistent(allOk);
0280 }
0281 
0282 bool FilterEdit::correctName(const QString &newText)
0283 {
0284     bool negative = false;
0285 #ifndef QT_NO_STYLE_STYLESHEET
0286     QString styleSheet;
0287     if (mNegativeBackground.isEmpty()) {
0288         const KStatefulBrush bgBrush = KStatefulBrush(KColorScheme::View, KColorScheme::NegativeBackground);
0289         mNegativeBackground = QStringLiteral("QLineEdit{ background-color:%1 }").arg(bgBrush.brush(mNameLineEdit->palette()).color().name());
0290     }
0291     if (!newText.isEmpty()) {
0292         const int val = mRulesList->count();
0293         for (int i = 0; i < val; ++i) {
0294             QListWidgetItem *item = mRulesList->item(i);
0295             if (item && (mRulesList->currentItem() != item)) {
0296                 if (newText == item->text()) {
0297                     negative = true;
0298                     break;
0299                 }
0300             }
0301         }
0302     } else {
0303         negative = true;
0304     }
0305     if (negative) {
0306         styleSheet = mNegativeBackground;
0307     }
0308     mNameLineEdit->setStyleSheet(styleSheet);
0309 #endif
0310     return negative;
0311 }
0312 
0313 void FilterEdit::editCategorySelection()
0314 {
0315     if (!mCurrent) {
0316         return;
0317     }
0318 
0319     if (!mCategorySelectDialog) {
0320         mCategorySelectDialog = new Akonadi::TagSelectionDialog(this);
0321         QDialogButtonBox *buttons = mCategorySelectDialog->buttons();
0322         if (buttons) {
0323             buttons->setStandardButtons(QDialogButtonBox::Ok | QDialogButtonBox::Cancel | QDialogButtonBox::Help);
0324             connect(mCategorySelectDialog, &Akonadi::TagSelectionDialog::accepted, this, &FilterEdit::updateCategorySelection);
0325             connect(buttons->button(QDialogButtonBox::Help), &QPushButton::clicked, this, &FilterEdit::slotHelp);
0326         }
0327     }
0328     Akonadi::Tag::List tags;
0329     const auto names = mCurrent->categoryList();
0330     tags.resize(names.size());
0331     std::transform(names.cbegin(), names.cend(), std::back_inserter(tags), [](const QString &name) {
0332         return Akonadi::Tag{name};
0333     });
0334     mCategorySelectDialog->setSelection(tags);
0335 
0336     mCategorySelectDialog->show();
0337 }
0338 
0339 void FilterEdit::slotHelp()
0340 {
0341     PimCommon::Util::invokeHelp(QStringLiteral("korganizer/filters-view.html"));
0342 }
0343 
0344 void FilterEdit::updateCategorySelection()
0345 {
0346     const auto tags = mCategorySelectDialog->selection();
0347     QStringList categories;
0348     categories.reserve(tags.size());
0349     std::transform(tags.cbegin(), tags.cend(), std::back_inserter(categories), std::bind(&Akonadi::Tag::name, std::placeholders::_1));
0350     mCatList->clear();
0351     mCatList->addItems(categories);
0352     mCurrent->setCategoryList(categories);
0353 }
0354 
0355 #include "moc_filtereditdialog.cpp"