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
33namespace Akonadi
34{
35
36class EntityOrderProxyModelPrivate
37{
38public:
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
56using namespace Akonadi;
57
58EntityOrderProxyModel::EntityOrderProxyModel(QObject *parent)
59 : QSortFilterProxyModel(parent)
60 , d_ptr(new EntityOrderProxyModelPrivate(this))
61{
62 setDynamicSortFilter(true);
63 //setSortCaseSensitivity( Qt::CaseInsensitive );
64}
65
66EntityOrderProxyModel::~EntityOrderProxyModel()
67{
68 delete d_ptr;
69}
70
71void EntityOrderProxyModel::setOrderConfig(KConfigGroup &configGroup)
72{
73 Q_D(EntityOrderProxyModel);
74 layoutAboutToBeChanged();
75 d->m_orderConfig = configGroup;
76 layoutChanged();
77}
78
79bool 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
111bool 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
209QModelIndexList 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
227void 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
259QString 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
271QString 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
285void EntityOrderProxyModel::saveOrder()
286{
287 Q_D(EntityOrderProxyModel);
288 d->saveOrder(QModelIndex());
289 d->m_orderConfig.sync();
290}
291
292void 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
306void EntityOrderProxyModel::clearTreeOrder()
307{
308 Q_D(EntityOrderProxyModel);
309 d->m_orderConfig.deleteGroup();
310 invalidate();
311}
312