1 | /* |
2 | Copyright (C) 2010 Klarälvdalens Datakonsult AB, |
3 | a KDAB Group company, info@kdab.net, |
4 | author Stephen Kelly <stephen@kdab.com> |
5 | |
6 | This library is free software; you can redistribute it and/or modify it |
7 | under the terms of the GNU Library General Public License as published by |
8 | the Free Software Foundation; either version 2 of the License, or (at your |
9 | option) any later version. |
10 | |
11 | This library is distributed in the hope that it will be useful, but WITHOUT |
12 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
13 | FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public |
14 | License for more details. |
15 | |
16 | You should have received a copy of the GNU Library General Public License |
17 | along with this library; see the file COPYING.LIB. If not, write to the |
18 | Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA |
19 | 02110-1301, USA. |
20 | */ |
21 | |
22 | #include "entityorderproxymodel.h" |
23 | |
24 | #include <QMimeData> |
25 | |
26 | #include <KDE/KConfigGroup> |
27 | #include <KUrl> |
28 | |
29 | #include "collection.h" |
30 | #include "item.h" |
31 | #include "entitytreemodel.h" |
32 | |
33 | namespace Akonadi |
34 | { |
35 | |
36 | class EntityOrderProxyModelPrivate |
37 | { |
38 | public: |
39 | EntityOrderProxyModelPrivate(EntityOrderProxyModel *qq) |
40 | : q_ptr(qq) |
41 | { |
42 | |
43 | } |
44 | |
45 | void saveOrder(const QModelIndex &index); |
46 | |
47 | KConfigGroup m_orderConfig; |
48 | |
49 | Q_DECLARE_PUBLIC(EntityOrderProxyModel) |
50 | EntityOrderProxyModel *const q_ptr; |
51 | |
52 | }; |
53 | |
54 | } |
55 | |
56 | using namespace Akonadi; |
57 | |
58 | EntityOrderProxyModel::EntityOrderProxyModel(QObject *parent) |
59 | : QSortFilterProxyModel(parent) |
60 | , d_ptr(new EntityOrderProxyModelPrivate(this)) |
61 | { |
62 | setDynamicSortFilter(true); |
63 | //setSortCaseSensitivity( Qt::CaseInsensitive ); |
64 | } |
65 | |
66 | EntityOrderProxyModel::~EntityOrderProxyModel() |
67 | { |
68 | delete d_ptr; |
69 | } |
70 | |
71 | void EntityOrderProxyModel::setOrderConfig(KConfigGroup &configGroup) |
72 | { |
73 | Q_D(EntityOrderProxyModel); |
74 | layoutAboutToBeChanged(); |
75 | d->m_orderConfig = configGroup; |
76 | layoutChanged(); |
77 | } |
78 | |
79 | bool EntityOrderProxyModel::lessThan(const QModelIndex &left, const QModelIndex &right) const |
80 | { |
81 | Q_D(const EntityOrderProxyModel); |
82 | |
83 | if (!d->m_orderConfig.isValid()) { |
84 | return QSortFilterProxyModel::lessThan(left, right); |
85 | } |
86 | Collection col = left.data(EntityTreeModel::ParentCollectionRole).value<Collection>(); |
87 | |
88 | if (!d->m_orderConfig.hasKey(QString::number(col.id()))) { |
89 | return QSortFilterProxyModel::lessThan(left, right); |
90 | } |
91 | |
92 | const QStringList list = d->m_orderConfig.readEntry(QString::number(col.id()), QStringList()); |
93 | |
94 | if (list.isEmpty()) { |
95 | return QSortFilterProxyModel::lessThan(left, right); |
96 | } |
97 | |
98 | const QString leftValue = configString(left); |
99 | const QString rightValue = configString(right); |
100 | |
101 | const int leftPosition = list.indexOf(leftValue); |
102 | const int rightPosition = list.indexOf(rightValue); |
103 | |
104 | if (leftPosition < 0 || rightPosition < 0) { |
105 | return QSortFilterProxyModel::lessThan(left, right); |
106 | } |
107 | |
108 | return leftPosition < rightPosition; |
109 | } |
110 | |
111 | bool EntityOrderProxyModel::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) |
112 | { |
113 | Q_D(EntityOrderProxyModel); |
114 | |
115 | if (!d->m_orderConfig.isValid()) { |
116 | return QSortFilterProxyModel::dropMimeData(data, action, row, column, parent); |
117 | } |
118 | |
119 | if (!data->hasFormat(QLatin1String("text/uri-list" ))) { |
120 | return QSortFilterProxyModel::dropMimeData(data, action, row, column, parent); |
121 | } |
122 | |
123 | if (row == -1) { |
124 | return QSortFilterProxyModel::dropMimeData(data, action, row, column, parent); |
125 | } |
126 | |
127 | bool containsMove = false; |
128 | |
129 | const KUrl::List urls = KUrl::List::fromMimeData(data); |
130 | |
131 | Collection parentCol; |
132 | |
133 | if (parent.isValid()) { |
134 | parentCol = parent.data(EntityTreeModel::CollectionRole).value<Collection>(); |
135 | } else { |
136 | if (!hasChildren(parent)) { |
137 | return QSortFilterProxyModel::dropMimeData(data, action, row, column, parent); |
138 | } |
139 | |
140 | const QModelIndex targetIndex = index(0, column, parent); |
141 | |
142 | parentCol = targetIndex.data(EntityTreeModel::ParentCollectionRole).value<Collection>(); |
143 | } |
144 | |
145 | QStringList droppedList; |
146 | foreach (const KUrl &url, urls) { |
147 | Collection col = Collection::fromUrl(url); |
148 | |
149 | if (!col.isValid()) { |
150 | Item item = Item::fromUrl(url); |
151 | if (!item.isValid()) { |
152 | continue; |
153 | } |
154 | |
155 | const QModelIndexList list = EntityTreeModel::modelIndexesForItem(this, item); |
156 | if (list.isEmpty()) { |
157 | continue; |
158 | } |
159 | |
160 | if (!containsMove && list.first().data(EntityTreeModel::ParentCollectionRole).value<Collection>().id() != parentCol.id()) { |
161 | containsMove = true; |
162 | } |
163 | |
164 | droppedList << configString(list.first()); |
165 | } else { |
166 | const QModelIndex idx = EntityTreeModel::modelIndexForCollection(this, col); |
167 | if (!idx.isValid()) { |
168 | continue; |
169 | } |
170 | |
171 | if (!containsMove && idx.data(EntityTreeModel::ParentCollectionRole).value<Collection>().id() != parentCol.id()) { |
172 | containsMove = true; |
173 | } |
174 | |
175 | droppedList << configString(idx); |
176 | } |
177 | } |
178 | |
179 | QStringList existingList; |
180 | if (d->m_orderConfig.hasKey(QString::number(parentCol.id()))) { |
181 | existingList = d->m_orderConfig.readEntry(QString::number(parentCol.id()), QStringList()); |
182 | } else { |
183 | const int rowCount = this->rowCount(parent); |
184 | for (int row = 0; row < rowCount; ++row) { |
185 | static const int column = 0; |
186 | const QModelIndex idx = this->index(row, column, parent); |
187 | existingList.append(configString(idx)); |
188 | } |
189 | } |
190 | const int numberOfDroppedElement(droppedList.size()); |
191 | for (int i = 0; i < numberOfDroppedElement; ++i) { |
192 | const QString droppedItem = droppedList.at(i); |
193 | const int existingIndex = existingList.indexOf(droppedItem); |
194 | existingList.removeAt(existingIndex); |
195 | existingList.insert(row + i - (existingIndex > row ? 0 : 1), droppedList.at(i)); |
196 | } |
197 | |
198 | d->m_orderConfig.writeEntry(QString::number(parentCol.id()), existingList); |
199 | |
200 | if (containsMove) { |
201 | bool result = QSortFilterProxyModel::dropMimeData(data, action, row, column, parent); |
202 | invalidate(); |
203 | return result; |
204 | } |
205 | invalidate(); |
206 | return true; |
207 | } |
208 | |
209 | QModelIndexList EntityOrderProxyModel::match(const QModelIndex &start, int role, const QVariant &value, int hits, Qt::MatchFlags flags) const |
210 | { |
211 | if (role < Qt::UserRole) { |
212 | return QSortFilterProxyModel::match(start, role, value, hits, flags); |
213 | } |
214 | |
215 | QModelIndexList list; |
216 | QModelIndex proxyIndex; |
217 | foreach (const QModelIndex &idx, sourceModel()->match(mapToSource(start), role, value, hits, flags)) { |
218 | proxyIndex = mapFromSource(idx); |
219 | if (proxyIndex.isValid()) { |
220 | list << proxyIndex; |
221 | } |
222 | } |
223 | |
224 | return list; |
225 | } |
226 | |
227 | void EntityOrderProxyModelPrivate::saveOrder(const QModelIndex &parent) |
228 | { |
229 | Q_Q(const EntityOrderProxyModel); |
230 | int rowCount = q->rowCount(parent); |
231 | |
232 | if (rowCount == 0) { |
233 | return; |
234 | } |
235 | |
236 | static const int column = 0; |
237 | QModelIndex childIndex = q->index(0, column, parent); |
238 | |
239 | QString parentKey = q->parentConfigString(childIndex); |
240 | |
241 | if (parentKey.isEmpty()) { |
242 | return; |
243 | } |
244 | |
245 | QStringList list; |
246 | |
247 | list << q->configString(childIndex); |
248 | saveOrder(childIndex); |
249 | |
250 | for (int row = 1; row < rowCount; ++row) { |
251 | childIndex = q->index(row, column, parent); |
252 | list << q->configString(childIndex); |
253 | saveOrder(childIndex); |
254 | } |
255 | |
256 | m_orderConfig.writeEntry(parentKey, list); |
257 | } |
258 | |
259 | QString EntityOrderProxyModel::parentConfigString(const QModelIndex &index) const |
260 | { |
261 | const Collection col = index.data(EntityTreeModel::ParentCollectionRole).value<Collection>(); |
262 | |
263 | Q_ASSERT(col.isValid()); |
264 | if (!col.isValid()) { |
265 | return QString(); |
266 | } |
267 | |
268 | return QString::number(col.id()); |
269 | } |
270 | |
271 | QString EntityOrderProxyModel::configString(const QModelIndex &index) const |
272 | { |
273 | Entity::Id eId = index.data(EntityTreeModel::ItemIdRole).toLongLong(); |
274 | if (eId != -1) { |
275 | return QString::fromLatin1("i" ) + QString::number(eId); |
276 | } |
277 | eId = index.data(EntityTreeModel::CollectionIdRole).toLongLong(); |
278 | if (eId != -1) { |
279 | return QString::fromLatin1("c" ) + QString::number(eId); |
280 | } |
281 | Q_ASSERT(!"Invalid entity" ); |
282 | return QString(); |
283 | } |
284 | |
285 | void EntityOrderProxyModel::saveOrder() |
286 | { |
287 | Q_D(EntityOrderProxyModel); |
288 | d->saveOrder(QModelIndex()); |
289 | d->m_orderConfig.sync(); |
290 | } |
291 | |
292 | void EntityOrderProxyModel::clearOrder(const QModelIndex &parent) |
293 | { |
294 | Q_D(EntityOrderProxyModel); |
295 | |
296 | const QString parentKey = parentConfigString(index(0, 0, parent)); |
297 | |
298 | if (parentKey.isEmpty()) { |
299 | return; |
300 | } |
301 | |
302 | d->m_orderConfig.deleteEntry(parentKey); |
303 | invalidate(); |
304 | } |
305 | |
306 | void EntityOrderProxyModel::clearTreeOrder() |
307 | { |
308 | Q_D(EntityOrderProxyModel); |
309 | d->m_orderConfig.deleteGroup(); |
310 | invalidate(); |
311 | } |
312 | |