File indexing completed on 2024-11-10 04:40:36
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"