Akonadi

entityorderproxymodel.cpp
1 /*
2  Copyright (C) 2010 Klarälvdalens Datakonsult AB,
3  a KDAB Group company, [email protected],
4  author Stephen Kelly <[email protected]>
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 <KConfigGroup>
27 #include <QUrl>
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  void saveOrder(const QModelIndex &index);
45 
46  KConfigGroup m_orderConfig;
47 
48  Q_DECLARE_PUBLIC(EntityOrderProxyModel)
49  EntityOrderProxyModel *const q_ptr;
50 
51 };
52 
53 }
54 
55 using namespace Akonadi;
56 
58  : QSortFilterProxyModel(parent)
59  , d_ptr(new EntityOrderProxyModelPrivate(this))
60 {
61  setRecursiveFilteringEnabled(true);
62  setDynamicSortFilter(true);
63  //setSortCaseSensitivity( Qt::CaseInsensitive );
64 }
65 
67 {
68  delete d_ptr;
69 }
70 
72 {
74  Q_EMIT layoutAboutToBeChanged();
75  d->m_orderConfig = configGroup;
76  Q_EMIT layoutChanged();
77 }
78 
79 // reimplemented in FavoriteCollectionOrderProxyModel
80 Collection EntityOrderProxyModel::parentCollection(const QModelIndex &index) const
81 {
83 }
84 
85 static QString configKey(const Collection &col)
86 {
87  return !col.isValid() ? QStringLiteral("0") : QString::number(col.id());
88 }
89 
90 bool EntityOrderProxyModel::lessThan(const QModelIndex &left, const QModelIndex &right) const
91 {
92  Q_D(const EntityOrderProxyModel);
93 
94  if (!d->m_orderConfig.isValid()) {
95  return QSortFilterProxyModel::lessThan(left, right);
96  }
97  const Collection col = parentCollection(left);
98 
99  const QStringList list = d->m_orderConfig.readEntry(configKey(col), QStringList());
100 
101  if (list.isEmpty()) {
102  return QSortFilterProxyModel::lessThan(left, right);
103  }
104 
105  const QString leftValue = configString(left);
106  const QString rightValue = configString(right);
107 
108  const int leftPosition = list.indexOf(leftValue);
109  const int rightPosition = list.indexOf(rightValue);
110 
111  if (leftPosition < 0 || rightPosition < 0) {
112  return QSortFilterProxyModel::lessThan(left, right);
113  }
114 
115  return leftPosition < rightPosition;
116 }
117 
118 QStringList EntityOrderProxyModel::configStringsForDroppedUrls(const QList<QUrl> &urls, const Akonadi::Collection &parentCol, bool *containsMove) const
119 {
120  QStringList droppedList;
121  droppedList.reserve(urls.count());
122  for (const QUrl &url : urls) {
123  Collection col = Collection::fromUrl(url);
124 
125  if (!col.isValid()) {
126  Item item = Item::fromUrl(url);
127  if (!item.isValid()) {
128  continue;
129  }
130 
131  const QModelIndexList list = EntityTreeModel::modelIndexesForItem(this, item);
132  if (list.isEmpty()) {
133  continue;
134  }
135 
136  if (!*containsMove && parentCollection(list.first()).id() != parentCol.id()) {
137  *containsMove = true;
138  }
139 
140  droppedList << configString(list.first());
141  } else {
143  if (!idx.isValid()) {
144  continue;
145  }
146 
147  if (!*containsMove && parentCollection(idx).id() != parentCol.id()) {
148  *containsMove = true;
149  }
150 
151  droppedList << configString(idx);
152  }
153  }
154  return droppedList;
155 }
156 
157 bool EntityOrderProxyModel::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent)
158 {
160 
161  if (!d->m_orderConfig.isValid()) {
162  return QSortFilterProxyModel::dropMimeData(data, action, row, column, parent);
163  }
164 
165  if (!data->hasFormat(QStringLiteral("text/uri-list"))) {
166  return QSortFilterProxyModel::dropMimeData(data, action, row, column, parent);
167  }
168 
169  if (row == -1) {
170  return QSortFilterProxyModel::dropMimeData(data, action, row, column, parent);
171  }
172 
173 
174  const QList<QUrl> urls = data->urls();
175  if (urls.isEmpty()) {
176  return false;
177  }
178 
179  Collection parentCol;
180 
181  if (parent.isValid()) {
182  parentCol = parent.data(EntityTreeModel::CollectionRole).value<Collection>();
183  } else {
184  if (!hasChildren(parent)) {
185  return QSortFilterProxyModel::dropMimeData(data, action, row, column, parent);
186  }
187 
188  const QModelIndex targetIndex = index(0, column, parent);
189  parentCol = parentCollection(targetIndex);
190  }
191 
192 
193  bool containsMove = false;
194  QStringList droppedList = configStringsForDroppedUrls(urls, parentCol, &containsMove);
195 
196  // Dropping new favorite folders
197  if (droppedList.isEmpty()) {
198  const bool ok = QSortFilterProxyModel::dropMimeData(data, action, row, column, parent);
199  if (ok) {
200  droppedList = configStringsForDroppedUrls(urls, parentCol, &containsMove);
201  }
202  }
203 
204  QStringList existingList;
205  if (d->m_orderConfig.hasKey(QString::number(parentCol.id()))) {
206  existingList = d->m_orderConfig.readEntry(configKey(parentCol), QStringList());
207  } else {
208  const int rowCount = this->rowCount(parent);
209  existingList.reserve(rowCount);
210  for (int row = 0; row < rowCount; ++row) {
211  static const int column = 0;
212  const QModelIndex idx = this->index(row, column, parent);
213  existingList.append(configString(idx));
214  }
215  }
216  const int numberOfDroppedElement(droppedList.size());
217  for (int i = 0; i < numberOfDroppedElement; ++i) {
218  const QString droppedItem = droppedList.at(i);
219  const int existingIndex = existingList.indexOf(droppedItem);
220  existingList.removeAt(existingIndex);
221  existingList.insert(row + i - (existingIndex > row ? 0 : 1), droppedList.at(i));
222  }
223 
224  d->m_orderConfig.writeEntry(configKey(parentCol), existingList);
225 
226  if (containsMove) {
227  bool result = QSortFilterProxyModel::dropMimeData(data, action, row, column, parent);
228  invalidate();
229  return result;
230  }
231  invalidate();
232  return true;
233 }
234 
235 QModelIndexList EntityOrderProxyModel::match(const QModelIndex &start, int role, const QVariant &value, int hits, Qt::MatchFlags flags) const
236 {
237  if (role < Qt::UserRole) {
238  return QSortFilterProxyModel::match(start, role, value, hits, flags);
239  }
240 
241  QModelIndexList list;
242  QModelIndex proxyIndex;
243  const auto matches = sourceModel()->match(mapToSource(start), role, value, hits, flags);
244  for (const auto &idx : matches) {
245  proxyIndex = mapFromSource(idx);
246  if (proxyIndex.isValid()) {
247  list.push_back(proxyIndex);
248  }
249  }
250 
251  return list;
252 }
253 
254 void EntityOrderProxyModelPrivate::saveOrder(const QModelIndex &parent)
255 {
256  Q_Q(const EntityOrderProxyModel);
257  int rowCount = q->rowCount(parent);
258 
259  if (rowCount == 0) {
260  return;
261  }
262 
263  static const int column = 0;
264  QModelIndex childIndex = q->index(0, column, parent);
265 
266  const QString parentKey = q->parentConfigString(childIndex);
267 
268  if (parentKey.isEmpty()) {
269  return;
270  }
271 
272  QStringList list;
273 
274  list << q->configString(childIndex);
275  saveOrder(childIndex);
276  list.reserve(list.count() + rowCount);
277  for (int row = 1; row < rowCount; ++row) {
278  childIndex = q->index(row, column, parent);
279  list << q->configString(childIndex);
280  saveOrder(childIndex);
281  }
282 
283  m_orderConfig.writeEntry(parentKey, list);
284 }
285 
286 QString EntityOrderProxyModel::parentConfigString(const QModelIndex &index) const
287 {
288  const Collection col = parentCollection(index);
289 
290  Q_ASSERT(col.isValid());
291  if (!col.isValid()) {
292  return QString();
293  }
294 
295  return QString::number(col.id());
296 }
297 
298 QString EntityOrderProxyModel::configString(const QModelIndex &index) const
299 {
300  Item::Id iId = index.data(EntityTreeModel::ItemIdRole).toLongLong();
301  if (iId != -1) {
302  return QLatin1Char('i') + QString::number(iId);
303  }
305  if (cId != -1) {
306  return QLatin1Char('c') + QString::number(cId);
307  }
308  Q_ASSERT(!"Invalid entity");
309  return QString();
310 }
311 
313 {
315  d->saveOrder(QModelIndex());
316  d->m_orderConfig.sync();
317 }
318 
319 void EntityOrderProxyModel::clearOrder(const QModelIndex &parent)
320 {
322 
323  const QString parentKey = parentConfigString(index(0, 0, parent));
324 
325  if (parentKey.isEmpty()) {
326  return;
327  }
328 
329  d->m_orderConfig.deleteEntry(parentKey);
330  invalidate();
331 }
332 
333 void EntityOrderProxyModel::clearTreeOrder()
334 {
336  d->m_orderConfig.deleteGroup();
337  invalidate();
338 }
qlonglong toLongLong(bool *ok) const const
virtual bool lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const const
bool isValid() const
Returns whether the collection is valid.
Definition: collection.cpp:137
typedef MatchFlags
virtual bool hasFormat(const QString &mimeType) const const
bool lessThan(const QModelIndex &left, const QModelIndex &right) const override
void reserve(int alloc)
Represents a collection of PIM items.
Definition: collection.h:76
qint64 Id
Describes the unique id type.
Definition: collection.h:82
virtual QModelIndexList match(const QModelIndex &start, int role, const QVariant &value, int hits, Qt::MatchFlags flags) const const override
const T & at(int i) const const
void removeAt(int i)
void setOrderConfig(const KConfigGroup &group)
Sets the config group that will be used for storing the order.
T value() const const
void writeEntry(const QString &key, const QVariant &value, WriteConfigFlags pFlags=Normal)
virtual bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) override
int size() const const
bool isValid() const const
QString number(int n, int base)
int count(const T &value) const const
void append(const T &value)
A model that keeps the order of entities persistent.
bool isEmpty() const const
bool isEmpty() const const
The parent collection of the entity.
QModelIndexList match(const QModelIndex &start, int role, const QVariant &value, int hits=1, Qt::MatchFlags flags=Qt::MatchFlags(Qt::MatchStartsWith|Qt::MatchWrap)) const override
int indexOf(QStringView str, int from) const const
static QModelIndex modelIndexForCollection(const QAbstractItemModel *model, const Collection &collection)
Returns a QModelIndex in model which points to collection.
~EntityOrderProxyModel() override
Destroys the entity order proxy model.
Id id() const
Returns the unique identifier of the collection.
Definition: collection.cpp:112
QVariant data(int role) const const
QList< QUrl > urls() const const
void insert(int i, const T &value)
Helper integration between Akonadi and Qt.
static QModelIndexList modelIndexesForItem(const QAbstractItemModel *model, const Item &item)
Returns a QModelIndex in model which points to item.
bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) override
static Collection fromUrl(const QUrl &url)
Creates a collection from the given url.
Definition: collection.cpp:283
This file is part of the KDE documentation.
Documentation copyright © 1996-2020 The KDE developers.
Generated on Fri Jun 5 2020 23:08:54 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.