File indexing completed on 2024-06-23 05:06:44

0001 /*
0002     SPDX-FileCopyrightText: 2007 Volker Krause <vkrause@kde.org>
0003 
0004     SPDX-License-Identifier: LGPL-2.0-or-later
0005 */
0006 
0007 #include "collectionutils.h"
0008 #include "entityhiddenattribute.h"
0009 #include "entitytreemodel.h"
0010 #include "specialcollectionattribute.h"
0011 #include "subscriptionmodel_p.h"
0012 
0013 #include "shared/akranges.h"
0014 #include <qnamespace.h>
0015 
0016 #include <QFont>
0017 #include <QSortFilterProxyModel>
0018 
0019 using namespace Akonadi;
0020 using namespace AkRanges;
0021 
0022 namespace
0023 {
0024 class FilterProxyModel : public QSortFilterProxyModel
0025 {
0026     Q_OBJECT
0027 
0028 public:
0029     FilterProxyModel()
0030     {
0031         setDynamicSortFilter(true);
0032     }
0033 
0034     void setShowHidden(bool showHidden)
0035     {
0036         if (mShowHidden != showHidden) {
0037             mShowHidden = showHidden;
0038             invalidateFilter();
0039         }
0040     }
0041 
0042     bool showHidden() const
0043     {
0044         return mShowHidden;
0045     }
0046 
0047 protected:
0048     bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const override
0049     {
0050         const auto source_index = sourceModel()->index(source_row, 0, source_parent);
0051         const auto col = source_index.data(EntityTreeModel::CollectionRole).value<Collection>();
0052         if (mShowHidden) {
0053             return true;
0054         }
0055 
0056         return !col.hasAttribute<EntityHiddenAttribute>();
0057     }
0058 
0059 private:
0060     bool mShowHidden = false;
0061 };
0062 
0063 } // namespace
0064 
0065 /**
0066  * @internal
0067  */
0068 class Akonadi::SubscriptionModelPrivate
0069 {
0070 public:
0071     explicit SubscriptionModelPrivate(Monitor *monitor)
0072         : etm(monitor)
0073     {
0074         etm.setShowSystemEntities(true); // show hidden collections
0075         etm.setItemPopulationStrategy(EntityTreeModel::NoItemPopulation);
0076         etm.setCollectionFetchStrategy(EntityTreeModel::FetchCollectionsRecursive);
0077 
0078         proxy.setSourceModel(&etm);
0079     }
0080 
0081     Collection::List changedSubscriptions(bool subscribed) const
0082     {
0083         return Views::range(subscriptions.constKeyValueBegin(), subscriptions.constKeyValueEnd()) | Views::filter([subscribed](const auto &val) {
0084                    return val.second == subscribed;
0085                })
0086             | Views::transform([](const auto &val) {
0087                    return Collection{val.first};
0088                })
0089             | Actions::toQVector;
0090     }
0091 
0092     bool isSubscribable(const Collection &col)
0093     {
0094         if (CollectionUtils::isStructural(col) || col.isVirtual() || CollectionUtils::isUnifiedMailbox(col)) {
0095             return false;
0096         }
0097         if (col.hasAttribute<SpecialCollectionAttribute>()) {
0098             return false;
0099         }
0100         if (col.contentMimeTypes().isEmpty()) {
0101             return false;
0102         }
0103         return true;
0104     }
0105 
0106 public:
0107     EntityTreeModel etm;
0108     FilterProxyModel proxy;
0109     QHash<Collection::Id, bool> subscriptions;
0110 };
0111 
0112 SubscriptionModel::SubscriptionModel(Monitor *monitor, QObject *parent)
0113     : QIdentityProxyModel(parent)
0114     , d(new SubscriptionModelPrivate(monitor))
0115 {
0116     QIdentityProxyModel::setSourceModel(&d->proxy);
0117 
0118     connect(&d->etm, &EntityTreeModel::collectionTreeFetched, this, &SubscriptionModel::modelLoaded);
0119 }
0120 
0121 SubscriptionModel::~SubscriptionModel() = default;
0122 
0123 void SubscriptionModel::setSourceModel(QAbstractItemModel * /*sourceModel*/)
0124 {
0125     // no-op
0126 }
0127 
0128 QVariant SubscriptionModel::data(const QModelIndex &index, int role) const
0129 {
0130     switch (role) {
0131     case Qt::CheckStateRole: {
0132         const auto col = index.data(EntityTreeModel::CollectionRole).value<Collection>();
0133         if (!d->isSubscribable(col)) {
0134             return QVariant();
0135         }
0136         // Check if we have "override" for the subscription state stored
0137         const auto it = d->subscriptions.constFind(col.id());
0138         if (it != d->subscriptions.cend()) {
0139             return (*it) ? Qt::Checked : Qt::Unchecked;
0140         } else {
0141             // Fallback to the current state of the collection
0142             return col.enabled() ? Qt::Checked : Qt::Unchecked;
0143         }
0144     }
0145     case SubscriptionChangedRole: {
0146         const auto col = index.data(EntityTreeModel::CollectionIdRole).toLongLong();
0147         return d->subscriptions.contains(col);
0148     }
0149     case Qt::FontRole: {
0150         const auto col = index.data(EntityTreeModel::CollectionIdRole).toLongLong();
0151         auto font = QIdentityProxyModel::data(index, role).value<QFont>();
0152         font.setBold(d->subscriptions.contains(col));
0153         return font;
0154     }
0155     }
0156 
0157     return QIdentityProxyModel::data(index, role);
0158 }
0159 
0160 Qt::ItemFlags SubscriptionModel::flags(const QModelIndex &index) const
0161 {
0162     Qt::ItemFlags flags = QIdentityProxyModel::flags(index);
0163     const auto col = index.data(EntityTreeModel::CollectionRole).value<Collection>();
0164     if (d->isSubscribable(col)) {
0165         return flags | Qt::ItemIsUserCheckable;
0166     }
0167     return flags;
0168 }
0169 
0170 bool SubscriptionModel::setData(const QModelIndex &index, const QVariant &value, int role)
0171 {
0172     if (role == Qt::CheckStateRole) {
0173         const auto col = index.data(EntityTreeModel::CollectionRole).value<Collection>();
0174         if (!d->isSubscribable(col)) {
0175             return true; // No change
0176         }
0177         if (col.enabled() == (value == Qt::Checked)) { // No change compared to the underlying model
0178             d->subscriptions.remove(col.id());
0179         } else {
0180             d->subscriptions[col.id()] = (value == Qt::Checked);
0181         }
0182         Q_EMIT dataChanged(index, index);
0183         return true;
0184     }
0185     return QIdentityProxyModel::setData(index, value, role);
0186 }
0187 
0188 Akonadi::Collection::List SubscriptionModel::subscribed() const
0189 {
0190     return d->changedSubscriptions(true);
0191 }
0192 
0193 Akonadi::Collection::List SubscriptionModel::unsubscribed() const
0194 {
0195     return d->changedSubscriptions(false);
0196 }
0197 
0198 void SubscriptionModel::setShowHiddenCollections(bool showHidden)
0199 {
0200     d->proxy.setShowHidden(showHidden);
0201 }
0202 
0203 bool SubscriptionModel::showHiddenCollections() const
0204 {
0205     return d->proxy.showHidden();
0206 }
0207 
0208 #include "subscriptionmodel.moc"
0209 
0210 #include "moc_subscriptionmodel_p.cpp"