File indexing completed on 2024-12-22 04:56:53

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 "newmailnotifierselectcollectionwidget.h"
0008 
0009 #include <Akonadi/AttributeFactory>
0010 #include <Akonadi/CollectionFilterProxyModel>
0011 #include <Akonadi/CollectionModifyJob>
0012 #include <Akonadi/NewMailNotifierAttribute>
0013 #include <QSortFilterProxyModel>
0014 
0015 #include <Akonadi/ChangeRecorder>
0016 #include <Akonadi/EntityTreeModel>
0017 #include <KLineEditEventHandler>
0018 #include <KMime/Message>
0019 
0020 #include "newmailnotifier_debug.h"
0021 #include <KLineEdit>
0022 #include <KLocalizedString>
0023 #include <QPushButton>
0024 
0025 #include <QHBoxLayout>
0026 #include <QLabel>
0027 #include <QTreeView>
0028 #include <QVBoxLayout>
0029 
0030 NewMailNotifierCollectionProxyModel::NewMailNotifierCollectionProxyModel(QObject *parent)
0031     : QIdentityProxyModel(parent)
0032 {
0033 }
0034 
0035 QVariant NewMailNotifierCollectionProxyModel::data(const QModelIndex &index, int role) const
0036 {
0037     if (role == Qt::CheckStateRole) {
0038         if (index.isValid()) {
0039             const auto collection = data(index, Akonadi::EntityTreeModel::CollectionRole).value<Akonadi::Collection>();
0040             // Make top-level collections uncheckable
0041             if (collection.parentCollection() == Akonadi::Collection::root()) {
0042                 return {};
0043             }
0044             if (mNotificationCollection.contains(collection)) {
0045                 return mNotificationCollection.value(collection) ? Qt::Checked : Qt::Unchecked;
0046             } else {
0047                 const auto attr = collection.attribute<Akonadi::NewMailNotifierAttribute>();
0048                 if (!attr || !attr->ignoreNewMail()) {
0049                     return Qt::Checked;
0050                 }
0051                 return Qt::Unchecked;
0052             }
0053         }
0054     }
0055     return QIdentityProxyModel::data(index, role);
0056 }
0057 
0058 bool NewMailNotifierCollectionProxyModel::setData(const QModelIndex &index, const QVariant &value, int role)
0059 {
0060     if (role == Qt::CheckStateRole) {
0061         if (index.isValid()) {
0062             const auto collection = data(index, Akonadi::EntityTreeModel::CollectionRole).value<Akonadi::Collection>();
0063             mNotificationCollection[collection] = (value == Qt::Checked);
0064             Q_EMIT dataChanged(index, index);
0065             return true;
0066         }
0067     }
0068 
0069     return QIdentityProxyModel::setData(index, value, role);
0070 }
0071 
0072 Qt::ItemFlags NewMailNotifierCollectionProxyModel::flags(const QModelIndex &index) const
0073 {
0074     if (index.isValid()) {
0075         return QIdentityProxyModel::flags(index) | Qt::ItemIsUserCheckable;
0076     } else {
0077         return QIdentityProxyModel::flags(index);
0078     }
0079 }
0080 
0081 QHash<Akonadi::Collection, bool> NewMailNotifierCollectionProxyModel::notificationCollection() const
0082 {
0083     return mNotificationCollection;
0084 }
0085 
0086 NewMailNotifierSelectCollectionWidget::NewMailNotifierSelectCollectionWidget(QWidget *parent)
0087     : QWidget(parent)
0088     , mFolderView(new QTreeView(this))
0089     , mChangeRecorder(new Akonadi::ChangeRecorder(this))
0090     , mCollectionFilter(new QSortFilterProxyModel(this))
0091     , mNewMailNotifierProxyModel(new NewMailNotifierCollectionProxyModel(this))
0092 {
0093     Akonadi::AttributeFactory::registerAttribute<Akonadi::NewMailNotifierAttribute>();
0094     auto vbox = new QVBoxLayout(this);
0095 
0096     auto label = new QLabel(i18n("Select which folders to monitor for new message notifications:"));
0097     vbox->addWidget(label);
0098 
0099     // Create a new change recorder.
0100     mChangeRecorder->setMimeTypeMonitored(KMime::Message::mimeType());
0101     mChangeRecorder->fetchCollection(true);
0102     mChangeRecorder->setAllMonitored(true);
0103 
0104     mModel = new Akonadi::EntityTreeModel(mChangeRecorder, this);
0105     // Set the model to show only collections, not items.
0106     mModel->setItemPopulationStrategy(Akonadi::EntityTreeModel::NoItemPopulation);
0107     connect(mModel, &Akonadi::EntityTreeModel::collectionTreeFetched, this, &NewMailNotifierSelectCollectionWidget::slotCollectionTreeFetched);
0108 
0109     auto mimeTypeProxy = new Akonadi::CollectionFilterProxyModel(this);
0110     mimeTypeProxy->setExcludeVirtualCollections(true);
0111     mimeTypeProxy->setDynamicSortFilter(true);
0112     mimeTypeProxy->addMimeTypeFilters(QStringList() << KMime::Message::mimeType());
0113     mimeTypeProxy->setSourceModel(mModel);
0114 
0115     mNewMailNotifierProxyModel->setSourceModel(mimeTypeProxy);
0116 
0117     mCollectionFilter->setRecursiveFilteringEnabled(true);
0118     mCollectionFilter->setSourceModel(mNewMailNotifierProxyModel);
0119     mCollectionFilter->setDynamicSortFilter(true);
0120     mCollectionFilter->setFilterCaseSensitivity(Qt::CaseInsensitive);
0121     mCollectionFilter->setSortRole(Qt::DisplayRole);
0122     mCollectionFilter->setSortCaseSensitivity(Qt::CaseSensitive);
0123     mCollectionFilter->setSortLocaleAware(true);
0124 
0125     auto searchLine = new KLineEdit(this);
0126     KLineEditEventHandler::catchReturnKey(searchLine);
0127     searchLine->setPlaceholderText(i18n("Search..."));
0128     searchLine->setClearButtonEnabled(true);
0129     connect(searchLine, &QLineEdit::textChanged, this, &NewMailNotifierSelectCollectionWidget::slotSetCollectionFilter);
0130 
0131     vbox->addWidget(searchLine);
0132 
0133     mFolderView->setEditTriggers(QAbstractItemView::NoEditTriggers);
0134     mFolderView->setAlternatingRowColors(true);
0135     vbox->addWidget(mFolderView);
0136 
0137     mFolderView->setModel(mCollectionFilter);
0138 
0139     auto hbox = new QHBoxLayout;
0140     vbox->addLayout(hbox);
0141 
0142     auto button = new QPushButton(i18n("&Select All"), this);
0143     connect(button, &QPushButton::clicked, this, &NewMailNotifierSelectCollectionWidget::slotSelectAllCollections);
0144     hbox->addWidget(button);
0145 
0146     button = new QPushButton(i18n("&Unselect All"), this);
0147     connect(button, &QPushButton::clicked, this, &NewMailNotifierSelectCollectionWidget::slotUnselectAllCollections);
0148     hbox->addWidget(button);
0149     hbox->addStretch(1);
0150 }
0151 
0152 NewMailNotifierSelectCollectionWidget::~NewMailNotifierSelectCollectionWidget() = default;
0153 
0154 void NewMailNotifierSelectCollectionWidget::slotCollectionTreeFetched()
0155 {
0156     mCollectionFilter->sort(0, Qt::AscendingOrder);
0157     mFolderView->expandAll();
0158 }
0159 
0160 void NewMailNotifierSelectCollectionWidget::slotSetCollectionFilter(const QString &filter)
0161 {
0162     mCollectionFilter->setFilterWildcard(filter);
0163     mFolderView->expandAll();
0164 }
0165 
0166 void NewMailNotifierSelectCollectionWidget::slotSelectAllCollections()
0167 {
0168     forceStatus(QModelIndex(), true);
0169 }
0170 
0171 void NewMailNotifierSelectCollectionWidget::slotUnselectAllCollections()
0172 {
0173     forceStatus(QModelIndex(), false);
0174 }
0175 
0176 void NewMailNotifierSelectCollectionWidget::forceStatus(const QModelIndex &parent, bool status)
0177 {
0178     const int nbCol = mNewMailNotifierProxyModel->rowCount(parent);
0179     for (int i = 0; i < nbCol; ++i) {
0180         const QModelIndex child = mNewMailNotifierProxyModel->index(i, 0, parent);
0181         mNewMailNotifierProxyModel->setData(child, status ? Qt::Checked : Qt::Unchecked, Qt::CheckStateRole);
0182         forceStatus(child, status);
0183     }
0184 }
0185 
0186 void NewMailNotifierSelectCollectionWidget::updateCollectionsRecursive()
0187 {
0188     QHashIterator<Akonadi::Collection, bool> i(mNewMailNotifierProxyModel->notificationCollection());
0189     while (i.hasNext()) {
0190         i.next();
0191         Akonadi::Collection collection = i.key();
0192         auto attr = collection.attribute<Akonadi::NewMailNotifierAttribute>();
0193         Akonadi::CollectionModifyJob *modifyJob = nullptr;
0194         const bool selected = i.value();
0195         if (selected && attr && attr->ignoreNewMail()) {
0196             collection.removeAttribute<Akonadi::NewMailNotifierAttribute>();
0197             modifyJob = new Akonadi::CollectionModifyJob(collection);
0198             modifyJob->setProperty("AttributeAdded", true);
0199         } else if (!selected && (!attr || !attr->ignoreNewMail())) {
0200             attr = collection.attribute<Akonadi::NewMailNotifierAttribute>(Akonadi::Collection::AddIfMissing);
0201             attr->setIgnoreNewMail(true);
0202             modifyJob = new Akonadi::CollectionModifyJob(collection);
0203             modifyJob->setProperty("AttributeAdded", false);
0204         }
0205 
0206         if (modifyJob) {
0207             connect(modifyJob, &Akonadi::CollectionModifyJob::finished, this, &NewMailNotifierSelectCollectionWidget::slotModifyJobDone);
0208         }
0209     }
0210 }
0211 
0212 void NewMailNotifierSelectCollectionWidget::slotModifyJobDone(KJob *job)
0213 {
0214     auto modifyJob = qobject_cast<Akonadi::CollectionModifyJob *>(job);
0215     if (modifyJob && job->error()) {
0216         if (job->property("AttributeAdded").toBool()) {
0217             qCWarning(NEWMAILNOTIFIER_LOG) << "Failed to append NewMailNotifierAttribute to collection" << modifyJob->collection().id() << ":"
0218                                            << job->errorString();
0219         } else {
0220             qCWarning(NEWMAILNOTIFIER_LOG) << "Failed to remove NewMailNotifierAttribute from collection" << modifyJob->collection().id() << ":"
0221                                            << job->errorString();
0222         }
0223     }
0224 }
0225 
0226 #include "moc_newmailnotifierselectcollectionwidget.cpp"