1 /*
2
3 SPDX-FileCopyrightText: 2010-2021 Laurent Montel <montel@kde.org>
4
5 SPDX-License-Identifier: GPL-2.0-or-later
6 */
7
8 #include "entitycollectionorderproxymodel.h"
9 #include "hierarchicalfoldermatcher_p.h"
10 #include "kernel/mailkernel.h"
11 #include "mailcommon_debug.h"
12 #include "util/mailutil.h"
13 #include <Akonadi/AgentManager>
14 #include <Akonadi/Collection>
15 #include <Akonadi/EntityTreeModel>
16 #include <Akonadi/KMime/SpecialMailCollections>
17
18 #include <QRegularExpression>
19
20 using namespace MailCommon;
21 class Q_DECL_HIDDEN MailCommon::EntityCollectionOrderProxyModel::EntityCollectionOrderProxyModelPrivate
22 {
23 public:
EntityCollectionOrderProxyModelPrivate()24 EntityCollectionOrderProxyModelPrivate()
25 {
26 }
27
collectionRank(const Akonadi::Collection & collection)28 int collectionRank(const Akonadi::Collection &collection)
29 {
30 const Akonadi::Collection::Id id = collection.id();
31 const int cachedRank = collectionRanks.value(id, -1);
32 if (cachedRank != -1) {
33 return cachedRank;
34 }
35
36 int rank = 100;
37 if (Kernel::folderIsInbox(collection)) {
38 rank = 1;
39 } else if (Kernel::self()->folderIsDraftOrOutbox(collection)) {
40 if (Kernel::self()->folderIsDrafts(collection)) {
41 rank = 5;
42 } else {
43 rank = 2;
44 }
45 } else if (Kernel::self()->folderIsSentMailFolder(collection)) {
46 rank = 3;
47 } else if (Kernel::self()->folderIsTrash(collection)) {
48 rank = 4;
49 } else if (Kernel::self()->folderIsTemplates(collection)) {
50 rank = 6;
51 } else if (MailCommon::Util::isVirtualCollection(collection)) {
52 rank = 200;
53 } else if (collection.parentCollection() == Akonadi::Collection::root() && MailCommon::Util::isUnifiedMailboxesAgent(collection)) {
54 // special treatment for Unified Mailboxes: they are *always* on top
55 rank = 0;
56 } else if (!topLevelOrder.isEmpty()) {
57 if (collection.parentCollection() == Akonadi::Collection::root()) {
58 const QString resource = collection.resource();
59 if (resource.isEmpty()) {
60 qCDebug(MAILCOMMON_LOG) << " collection has not resource: " << collection;
61 // Don't save in collectionranks because we don't have resource name => pb.
62 return rank;
63 }
64 const int order = topLevelOrder.indexOf(resource);
65 if (order != -1) {
66 rank = order + 1; /* top-level rank "0" belongs to Unified Mailboxes */
67 }
68 }
69 }
70 collectionRanks.insert(id, rank);
71 return rank;
72 }
73
74 QMap<Akonadi::Collection::Id, int> collectionRanks;
75 QStringList topLevelOrder;
76 HierarchicalFolderMatcher matcher;
77 bool manualSortingActive = false;
78 };
79
EntityCollectionOrderProxyModel(QObject * parent)80 EntityCollectionOrderProxyModel::EntityCollectionOrderProxyModel(QObject *parent)
81 : EntityOrderProxyModel(parent)
82 , d(new EntityCollectionOrderProxyModelPrivate())
83 {
84 setSortCaseSensitivity(Qt::CaseInsensitive);
85 connect(Akonadi::SpecialMailCollections::self(),
86 &Akonadi::SpecialMailCollections::defaultCollectionsChanged,
87 this,
88 &EntityCollectionOrderProxyModel::slotSpecialCollectionsChanged);
89 connect(Akonadi::SpecialMailCollections::self(),
90 &Akonadi::SpecialMailCollections::collectionsChanged,
91 this,
92 &EntityCollectionOrderProxyModel::slotSpecialCollectionsChanged);
93 }
94
~EntityCollectionOrderProxyModel()95 EntityCollectionOrderProxyModel::~EntityCollectionOrderProxyModel()
96 {
97 if (d->manualSortingActive) {
98 saveOrder();
99 }
100 }
101
slotSpecialCollectionsChanged()102 void EntityCollectionOrderProxyModel::slotSpecialCollectionsChanged()
103 {
104 if (!d->manualSortingActive) {
105 d->collectionRanks.clear();
106 invalidate();
107 }
108 }
109
setTopLevelOrder(const QStringList & list)110 void EntityCollectionOrderProxyModel::setTopLevelOrder(const QStringList &list)
111 {
112 d->topLevelOrder = list;
113 clearRanks();
114 }
115
clearRanks()116 void EntityCollectionOrderProxyModel::clearRanks()
117 {
118 d->collectionRanks.clear();
119 invalidate();
120 }
121
lessThan(const QModelIndex & left,const QModelIndex & right) const122 bool EntityCollectionOrderProxyModel::lessThan(const QModelIndex &left, const QModelIndex &right) const
123 {
124 const auto leftData = left.data(Akonadi::EntityTreeModel::CollectionRole).value<Akonadi::Collection>();
125 const auto rightData = right.data(Akonadi::EntityTreeModel::CollectionRole).value<Akonadi::Collection>();
126 if (!d->manualSortingActive) {
127 const int rankLeft = d->collectionRank(leftData);
128 const int rankRight = d->collectionRank(rightData);
129
130 if (rankLeft < rankRight) {
131 return true;
132 } else if (rankLeft > rankRight) {
133 return false;
134 }
135
136 return QSortFilterProxyModel::lessThan(left, right);
137 }
138
139 if (MailCommon::Util::isUnifiedMailboxesAgent(leftData)) {
140 return true;
141 } else {
142 return EntityOrderProxyModel::lessThan(left, right);
143 }
144 }
145
setManualSortingActive(bool active)146 void EntityCollectionOrderProxyModel::setManualSortingActive(bool active)
147 {
148 if (d->manualSortingActive == active) {
149 return;
150 }
151
152 d->manualSortingActive = active;
153 d->collectionRanks.clear();
154 invalidate();
155 }
156
isManualSortingActive() const157 bool EntityCollectionOrderProxyModel::isManualSortingActive() const
158 {
159 return d->manualSortingActive;
160 }
161
setFolderMatcher(const HierarchicalFolderMatcher & matcher)162 void EntityCollectionOrderProxyModel::setFolderMatcher(const HierarchicalFolderMatcher &matcher)
163 {
164 d->matcher = matcher;
165 invalidateFilter();
166 }
167
filterAcceptsRow(int sourceRow,const QModelIndex & sourceParent) const168 bool EntityCollectionOrderProxyModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const
169 {
170 if (d->matcher.isNull()) {
171 return EntityOrderProxyModel::filterAcceptsRow(sourceRow, sourceParent);
172 }
173 const QModelIndex sourceIndex = sourceModel()->index(sourceRow, filterKeyColumn(), sourceParent);
174 return d->matcher.matches(sourceModel(), sourceIndex, filterRole());
175 }
176