1 | /* |
2 | Copyright (c) 2007 Bruno Virlet <bruno.virlet@gmail.com> |
3 | Copyright (c) 2009 Stephen Kelly <steveire@gmail.com> |
4 | |
5 | This library is free software; you can redistribute it and/or modify it |
6 | under the terms of the GNU Library General Public License as published by |
7 | the Free Software Foundation; either version 2 of the License, or (at your |
8 | option) any later version. |
9 | |
10 | This library is distributed in the hope that it will be useful, but WITHOUT |
11 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
12 | FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public |
13 | License for more details. |
14 | |
15 | You should have received a copy of the GNU Library General Public License |
16 | along with this library; see the file COPYING.LIB. If not, write to the |
17 | Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA |
18 | 02110-1301, USA. |
19 | */ |
20 | |
21 | #include "entitymimetypefiltermodel.h" |
22 | |
23 | #include "entitytreemodel.h" |
24 | #include "mimetypechecker.h" |
25 | |
26 | #include <kdebug.h> |
27 | #include <kmimetype.h> |
28 | |
29 | #include <QtCore/QString> |
30 | #include <QtCore/QStringList> |
31 | |
32 | using namespace Akonadi; |
33 | |
34 | namespace Akonadi { |
35 | /** |
36 | * @internal |
37 | */ |
38 | class EntityMimeTypeFilterModelPrivate |
39 | { |
40 | public: |
41 | EntityMimeTypeFilterModelPrivate(EntityMimeTypeFilterModel *parent) |
42 | : q_ptr(parent) |
43 | , m_headerGroup(EntityTreeModel::EntityTreeHeaders) |
44 | { |
45 | } |
46 | |
47 | Q_DECLARE_PUBLIC(EntityMimeTypeFilterModel) |
48 | EntityMimeTypeFilterModel *q_ptr; |
49 | |
50 | QStringList includedMimeTypes; |
51 | QStringList excludedMimeTypes; |
52 | |
53 | QPersistentModelIndex m_rootIndex; |
54 | |
55 | EntityTreeModel::HeaderGroup ; |
56 | }; |
57 | |
58 | } |
59 | |
60 | EntityMimeTypeFilterModel::EntityMimeTypeFilterModel(QObject *parent) |
61 | : QSortFilterProxyModel(parent) |
62 | , d_ptr(new EntityMimeTypeFilterModelPrivate(this)) |
63 | { |
64 | } |
65 | |
66 | EntityMimeTypeFilterModel::~EntityMimeTypeFilterModel() |
67 | { |
68 | delete d_ptr; |
69 | } |
70 | |
71 | void EntityMimeTypeFilterModel::addMimeTypeInclusionFilters(const QStringList &typeList) |
72 | { |
73 | Q_D(EntityMimeTypeFilterModel); |
74 | d->includedMimeTypes << typeList; |
75 | invalidateFilter(); |
76 | } |
77 | |
78 | void EntityMimeTypeFilterModel::addMimeTypeExclusionFilters(const QStringList &typeList) |
79 | { |
80 | Q_D(EntityMimeTypeFilterModel); |
81 | d->excludedMimeTypes << typeList; |
82 | invalidateFilter(); |
83 | } |
84 | |
85 | void EntityMimeTypeFilterModel::addMimeTypeInclusionFilter(const QString &type) |
86 | { |
87 | Q_D(EntityMimeTypeFilterModel); |
88 | d->includedMimeTypes << type; |
89 | invalidateFilter(); |
90 | } |
91 | |
92 | void EntityMimeTypeFilterModel::addMimeTypeExclusionFilter(const QString &type) |
93 | { |
94 | Q_D(EntityMimeTypeFilterModel); |
95 | d->excludedMimeTypes << type; |
96 | invalidateFilter(); |
97 | } |
98 | |
99 | bool EntityMimeTypeFilterModel::filterAcceptsColumn(int sourceColumn, const QModelIndex &sourceParent) const |
100 | { |
101 | if (sourceColumn >= columnCount(mapFromSource(sourceParent))) { |
102 | return false; |
103 | } |
104 | return QSortFilterProxyModel::filterAcceptsColumn(sourceColumn, sourceParent); |
105 | } |
106 | |
107 | bool EntityMimeTypeFilterModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const |
108 | { |
109 | Q_D(const EntityMimeTypeFilterModel); |
110 | const QModelIndex idx = sourceModel()->index(sourceRow, 0, sourceParent); |
111 | |
112 | const Akonadi::Item item = idx.data(EntityTreeModel::ItemRole).value<Akonadi::Item>(); |
113 | |
114 | if (item.isValid() && !item.hasPayload()) { |
115 | kDebug() << "Item " << item.id() << " doesn't have payload" ; |
116 | return false; |
117 | } |
118 | |
119 | const QString rowMimetype = idx.data(EntityTreeModel::MimeTypeRole).toString(); |
120 | |
121 | if (d->excludedMimeTypes.contains(rowMimetype)) { |
122 | return false; |
123 | } |
124 | if (d->includedMimeTypes.isEmpty() || |
125 | d->includedMimeTypes.contains(rowMimetype)) { |
126 | return true; |
127 | } |
128 | |
129 | return false; |
130 | } |
131 | |
132 | QStringList EntityMimeTypeFilterModel::mimeTypeInclusionFilters() const |
133 | { |
134 | Q_D(const EntityMimeTypeFilterModel); |
135 | return d->includedMimeTypes; |
136 | } |
137 | |
138 | QStringList EntityMimeTypeFilterModel::mimeTypeExclusionFilters() const |
139 | { |
140 | Q_D(const EntityMimeTypeFilterModel); |
141 | return d->excludedMimeTypes; |
142 | } |
143 | |
144 | void EntityMimeTypeFilterModel::clearFilters() |
145 | { |
146 | Q_D(EntityMimeTypeFilterModel); |
147 | d->includedMimeTypes.clear(); |
148 | d->excludedMimeTypes.clear(); |
149 | invalidateFilter(); |
150 | } |
151 | |
152 | void EntityMimeTypeFilterModel::(EntityTreeModel::HeaderGroup ) |
153 | { |
154 | Q_D(EntityMimeTypeFilterModel); |
155 | d->m_headerGroup = headerGroup; |
156 | } |
157 | |
158 | QVariant EntityMimeTypeFilterModel::(int section, Qt::Orientation orientation, int role) const |
159 | { |
160 | if (!sourceModel()) { |
161 | return QVariant(); |
162 | } |
163 | |
164 | Q_D(const EntityMimeTypeFilterModel); |
165 | role += (EntityTreeModel::TerminalUserRole * d->m_headerGroup); |
166 | return sourceModel()->headerData(section, orientation, role); |
167 | } |
168 | |
169 | QModelIndexList EntityMimeTypeFilterModel::match(const QModelIndex &start, int role, const QVariant &value, int hits, Qt::MatchFlags flags) const |
170 | { |
171 | if (!sourceModel()) { |
172 | return QModelIndexList(); |
173 | } |
174 | |
175 | if (EntityTreeModel::AmazingCompletionRole != role) { |
176 | if (role < Qt::UserRole) { |
177 | return QSortFilterProxyModel::match(start, role, value, hits, flags); |
178 | } |
179 | |
180 | QModelIndexList list; |
181 | QModelIndex proxyIndex; |
182 | foreach (const QModelIndex &idx, sourceModel()->match(mapToSource(start), role, value, hits, flags)) { |
183 | proxyIndex = mapFromSource(idx); |
184 | if (proxyIndex.isValid()) { |
185 | list << proxyIndex; |
186 | } |
187 | } |
188 | |
189 | return list; |
190 | } |
191 | // We match everything in the source model because sorting will change what we should show. |
192 | const int allHits = -1; |
193 | |
194 | QModelIndexList proxyList; |
195 | QMap<int, QModelIndex> proxyMap; |
196 | const QModelIndexList sourceList = sourceModel()->match(mapToSource(start), role, value, allHits, flags); |
197 | QModelIndexList::const_iterator it; |
198 | const QModelIndexList::const_iterator begin = sourceList.constBegin(); |
199 | const QModelIndexList::const_iterator end = sourceList.constEnd(); |
200 | QModelIndex proxyIndex; |
201 | for (it = begin; it != end; ++it) { |
202 | proxyIndex = mapFromSource(*it); |
203 | |
204 | // Any filtered indexes will be invalid when mapped. |
205 | if (!proxyIndex.isValid()) { |
206 | continue; |
207 | } |
208 | |
209 | // Inserting in a QMap gives us sorting by key for free. |
210 | proxyMap.insert(proxyIndex.row(), proxyIndex); |
211 | } |
212 | |
213 | if (hits == -1) { |
214 | return proxyMap.values(); |
215 | } |
216 | |
217 | return proxyMap.values().mid(0, hits); |
218 | } |
219 | |
220 | int EntityMimeTypeFilterModel::columnCount(const QModelIndex &parent) const |
221 | { |
222 | Q_D(const EntityMimeTypeFilterModel); |
223 | |
224 | if (!sourceModel()) { |
225 | return 0; |
226 | } |
227 | |
228 | const QVariant value = sourceModel()->data(mapToSource(parent), EntityTreeModel::ColumnCountRole + (EntityTreeModel::TerminalUserRole * d->m_headerGroup)); |
229 | if (!value.isValid()) { |
230 | return 0; |
231 | } |
232 | |
233 | return value.toInt(); |
234 | } |
235 | |
236 | bool EntityMimeTypeFilterModel::hasChildren(const QModelIndex &parent) const |
237 | { |
238 | if (!sourceModel()) { |
239 | return false; |
240 | } |
241 | |
242 | // QSortFilterProxyModel implementation is buggy in that it emits rowsAboutToBeInserted etc |
243 | // only after the source model has emitted rowsInserted, instead of emitting it when the |
244 | // source model emits rowsAboutToBeInserted. That means that the source and the proxy are out |
245 | // of sync around the time of insertions, so we can't use the optimization below. |
246 | return rowCount(parent) > 0; |
247 | #if 0 |
248 | |
249 | if (!parent.isValid()) { |
250 | return sourceModel()->hasChildren(parent); |
251 | } |
252 | |
253 | Q_D(const EntityMimeTypeFilterModel); |
254 | if (EntityTreeModel::ItemListHeaders == d->m_headerGroup) { |
255 | return false; |
256 | } |
257 | |
258 | if (EntityTreeModel::CollectionTreeHeaders == d->m_headerGroup) { |
259 | QModelIndex childIndex = parent.child(0, 0); |
260 | while (childIndex.isValid()) { |
261 | Collection col = childIndex.data(EntityTreeModel::CollectionRole).value<Collection>(); |
262 | if (col.isValid()) { |
263 | return true; |
264 | } |
265 | childIndex = childIndex.sibling(childIndex.row() + 1, childIndex.column()); |
266 | } |
267 | } |
268 | return false; |
269 | #endif |
270 | } |
271 | |
272 | bool EntityMimeTypeFilterModel::canFetchMore(const QModelIndex &parent) const |
273 | { |
274 | Q_D(const EntityMimeTypeFilterModel); |
275 | if (EntityTreeModel::CollectionTreeHeaders == d->m_headerGroup) { |
276 | return false; |
277 | } |
278 | return QSortFilterProxyModel::canFetchMore(parent); |
279 | } |
280 | |