Akonadi

entityorderproxymodel.cpp
1 /*
2  SPDX-FileCopyrightText: 2010 Klarälvdalens Datakonsult AB,
3  a KDAB Group company, [email protected]
4  SPDX-FileContributor: Stephen Kelly <[email protected]>
5 
6  SPDX-License-Identifier: LGPL-2.0-or-later
7 */
8 
9 #include "entityorderproxymodel.h"
10 
11 #include <QMimeData>
12 
13 #include <KConfigGroup>
14 #include <QUrl>
15 
16 #include "entitytreemodel.h"
17 #include "item.h"
18 
19 namespace Akonadi
20 {
21 class EntityOrderProxyModelPrivate
22 {
23 public:
24  explicit EntityOrderProxyModelPrivate(EntityOrderProxyModel *qq)
25  : q_ptr(qq)
26  {
27  }
28 
29  void saveOrder(const QModelIndex &index);
30 
31  KConfigGroup m_orderConfig;
32 
33  Q_DECLARE_PUBLIC(EntityOrderProxyModel)
34  EntityOrderProxyModel *const q_ptr;
35 };
36 
37 } // namespace Akonadi
38 
39 using namespace Akonadi;
40 
42  : QSortFilterProxyModel(parent)
43  , d_ptr(new EntityOrderProxyModelPrivate(this))
44 {
47  // setSortCaseSensitivity( Qt::CaseInsensitive );
48 }
49 
51 
53 {
56  d->m_orderConfig = configGroup;
58 }
59 
60 // reimplemented in FavoriteCollectionOrderProxyModel
61 Collection EntityOrderProxyModel::parentCollection(const QModelIndex &index) const
62 {
64 }
65 
66 static QString configKey(const Collection &col)
67 {
68  return !col.isValid() ? QStringLiteral("0") : QString::number(col.id());
69 }
70 
71 bool EntityOrderProxyModel::lessThan(const QModelIndex &left, const QModelIndex &right) const
72 {
74 
75  if (!d->m_orderConfig.isValid()) {
76  return QSortFilterProxyModel::lessThan(left, right);
77  }
78  const Collection col = parentCollection(left);
79 
80  const QStringList list = d->m_orderConfig.readEntry(configKey(col), QStringList());
81 
82  if (list.isEmpty()) {
83  return QSortFilterProxyModel::lessThan(left, right);
84  }
85 
86  const QString leftValue = configString(left);
87  const QString rightValue = configString(right);
88 
89  const int leftPosition = list.indexOf(leftValue);
90  const int rightPosition = list.indexOf(rightValue);
91 
92  if (leftPosition < 0 || rightPosition < 0) {
93  return QSortFilterProxyModel::lessThan(left, right);
94  }
95 
96  return leftPosition < rightPosition;
97 }
98 
99 QStringList EntityOrderProxyModel::configStringsForDroppedUrls(const QList<QUrl> &urls, const Akonadi::Collection &parentCol, bool *containsMove) const
100 {
101  QStringList droppedList;
102  droppedList.reserve(urls.count());
103  for (const QUrl &url : urls) {
104  const Collection col = Collection::fromUrl(url);
105 
106  if (!col.isValid()) {
107  Item item = Item::fromUrl(url);
108  if (!item.isValid()) {
109  continue;
110  }
111 
112  const QModelIndexList list = EntityTreeModel::modelIndexesForItem(this, item);
113  if (list.isEmpty()) {
114  continue;
115  }
116 
117  if (!*containsMove && parentCollection(list.first()).id() != parentCol.id()) {
118  *containsMove = true;
119  }
120 
121  droppedList << configString(list.first());
122  } else {
124  if (!idx.isValid()) {
125  continue;
126  }
127 
128  if (!*containsMove && parentCollection(idx).id() != parentCol.id()) {
129  *containsMove = true;
130  }
131 
132  droppedList << configString(idx);
133  }
134  }
135  return droppedList;
136 }
137 
138 bool EntityOrderProxyModel::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent)
139 {
141 
142  if (!d->m_orderConfig.isValid()) {
143  return QSortFilterProxyModel::dropMimeData(data, action, row, column, parent);
144  }
145 
146  if (!data->hasFormat(QStringLiteral("text/uri-list"))) {
147  return QSortFilterProxyModel::dropMimeData(data, action, row, column, parent);
148  }
149 
150  if (row == -1) {
151  return QSortFilterProxyModel::dropMimeData(data, action, row, column, parent);
152  }
153 
154  const QList<QUrl> urls = data->urls();
155  if (urls.isEmpty()) {
156  return false;
157  }
158 
159  Collection parentCol;
160 
161  if (parent.isValid()) {
162  parentCol = parent.data(EntityTreeModel::CollectionRole).value<Collection>();
163  } else {
164  if (!hasChildren(parent)) {
165  return QSortFilterProxyModel::dropMimeData(data, action, row, column, parent);
166  }
167 
168  const QModelIndex targetIndex = index(0, column, parent);
169  parentCol = parentCollection(targetIndex);
170  }
171 
172  bool containsMove = false;
173  QStringList droppedList = configStringsForDroppedUrls(urls, parentCol, &containsMove);
174 
175  // Dropping new favorite folders
176  if (droppedList.isEmpty()) {
177  const bool ok = QSortFilterProxyModel::dropMimeData(data, action, row, column, parent);
178  if (ok) {
179  droppedList = configStringsForDroppedUrls(urls, parentCol, &containsMove);
180  }
181  }
182 
183  QStringList existingList;
184  if (d->m_orderConfig.hasKey(QString::number(parentCol.id()))) {
185  existingList = d->m_orderConfig.readEntry(configKey(parentCol), QStringList());
186  } else {
187  const int rowCount = this->rowCount(parent);
188  existingList.reserve(rowCount);
189  for (int row = 0; row < rowCount; ++row) {
190  static const int column = 0;
191  const QModelIndex idx = this->index(row, column, parent);
192  existingList.append(configString(idx));
193  }
194  }
195  const int numberOfDroppedElement(droppedList.size());
196  for (int i = 0; i < numberOfDroppedElement; ++i) {
197  const QString &droppedItem = droppedList.at(i);
198  const int existingIndex = existingList.indexOf(droppedItem);
199  existingList.removeAt(existingIndex);
200  existingList.insert(row + i - (existingIndex > row ? 0 : 1), droppedList.at(i));
201  }
202 
203  d->m_orderConfig.writeEntry(configKey(parentCol), existingList);
204 
205  if (containsMove) {
206  bool result = QSortFilterProxyModel::dropMimeData(data, action, row, column, parent);
207  invalidate();
208  return result;
209  }
210  invalidate();
211  return true;
212 }
213 
214 QModelIndexList EntityOrderProxyModel::match(const QModelIndex &start, int role, const QVariant &value, int hits, Qt::MatchFlags flags) const
215 {
216  if (role < Qt::UserRole) {
217  return QSortFilterProxyModel::match(start, role, value, hits, flags);
218  }
219 
220  QModelIndexList list;
221  QModelIndex proxyIndex;
222  const auto matches = sourceModel()->match(mapToSource(start), role, value, hits, flags);
223  for (const auto &idx : matches) {
224  proxyIndex = mapFromSource(idx);
225  if (proxyIndex.isValid()) {
226  list.push_back(proxyIndex);
227  }
228  }
229 
230  return list;
231 }
232 
233 void EntityOrderProxyModelPrivate::saveOrder(const QModelIndex &parent)
234 {
235  Q_Q(const EntityOrderProxyModel);
236  int rowCount = q->rowCount(parent);
237 
238  if (rowCount == 0) {
239  return;
240  }
241 
242  static const int column = 0;
243  QModelIndex childIndex = q->index(0, column, parent);
244 
245  const QString parentKey = q->parentConfigString(childIndex);
246 
247  if (parentKey.isEmpty()) {
248  return;
249  }
250 
252 
253  list << q->configString(childIndex);
254  saveOrder(childIndex);
255  list.reserve(list.count() + rowCount);
256  for (int row = 1; row < rowCount; ++row) {
257  childIndex = q->index(row, column, parent);
258  list << q->configString(childIndex);
259  saveOrder(childIndex);
260  }
261 
262  m_orderConfig.writeEntry(parentKey, list);
263 }
264 
265 QString EntityOrderProxyModel::parentConfigString(const QModelIndex &index) const
266 {
267  const Collection col = parentCollection(index);
268 
269  Q_ASSERT(col.isValid());
270  if (!col.isValid()) {
271  return QString();
272  }
273 
274  return QString::number(col.id());
275 }
276 
277 QString EntityOrderProxyModel::configString(const QModelIndex &index) const
278 {
280  if (iId != -1) {
281  return QLatin1Char('i') + QString::number(iId);
282  }
284  if (cId != -1) {
285  return QLatin1Char('c') + QString::number(cId);
286  }
287  Q_ASSERT(!"Invalid entity");
288  return QString();
289 }
290 
292 {
294  d->saveOrder(QModelIndex());
295  d->m_orderConfig.sync();
296 }
297 
298 void EntityOrderProxyModel::clearOrder(const QModelIndex &parent)
299 {
301 
302  const QString parentKey = parentConfigString(index(0, 0, parent));
303 
304  if (parentKey.isEmpty()) {
305  return;
306  }
307 
308  d->m_orderConfig.deleteEntry(parentKey);
309  invalidate();
310 }
311 
312 void EntityOrderProxyModel::clearTreeOrder()
313 {
315  d->m_orderConfig.deleteGroup();
316  invalidate();
317 }
bool isValid() const
Returns whether the item is valid.
Definition: item.cpp:88
void append(const T &value)
T & first()
static Collection fromUrl(const QUrl &url)
Creates a collection from the given url.
Definition: collection.cpp:267
UserRole
QString number(int n, int base)
Q_EMITQ_EMIT
int count(const T &value) const const
T value() const const
virtual bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) override
void layoutChanged(const QList< QPersistentModelIndex > &parents, QAbstractItemModel::LayoutChangeHint hint)
Q_SCRIPTABLE Q_NOREPLY void start()
@ CollectionIdRole
The collection id.
Represents a collection of PIM items.
Definition: collection.h:61
void push_back(const T &value)
qlonglong toLongLong(bool *ok) const const
KIOFILEWIDGETS_EXPORT QStringList list(const QString &fileClass)
QModelIndexList match(const QModelIndex &start, int role, const QVariant &value, int hits=1, Qt::MatchFlags flags=Qt::MatchFlags(Qt::MatchStartsWith|Qt::MatchWrap)) const override
virtual QModelIndex mapToSource(const QModelIndex &proxyIndex) const const override
void removeAt(int i)
A model that keeps the order of entities persistent.
void reserve(int alloc)
qint64 Id
Describes the unique id type.
Definition: item.h:110
void setRecursiveFilteringEnabled(bool recursive)
QVariant data(int role) const const
int size() const const
virtual Qt::ItemFlags flags(const QModelIndex &index) const const override
@ CollectionRole
The collection.
bool lessThan(const QModelIndex &left, const QModelIndex &right) const override
static Item fromUrl(const QUrl &url)
Creates an item from the given url.
Definition: item.cpp:391
bool isEmpty() const const
static QModelIndexList modelIndexesForItem(const QAbstractItemModel *model, const Item &item)
Returns a QModelIndex in model which points to item.
const T & at(int i) const const
virtual bool hasChildren(const QModelIndex &parent) const const override
virtual int rowCount(const QModelIndex &parent) const const override
virtual QModelIndexList match(const QModelIndex &start, int role, const QVariant &value, int hits, Qt::MatchFlags flags) const const override
bool isEmpty() const const
void setDynamicSortFilter(bool enable)
EntityOrderProxyModel(QObject *parent=nullptr)
Creates a new entity order proxy model.
void setOrderConfig(const KConfigGroup &group)
Sets the config group that will be used for storing the order.
int indexOf(QStringView str, int from) const const
bool isValid() const const
virtual bool lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const const
void insert(int i, const T &value)
virtual QModelIndex index(int row, int column, const QModelIndex &parent) const const override
bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) override
DropAction
virtual QModelIndex mapFromSource(const QModelIndex &sourceIndex) const const override
virtual QVariant data(const QModelIndex &index, int role) const const override
static QModelIndex modelIndexForCollection(const QAbstractItemModel *model, const Collection &collection)
Returns a QModelIndex in model which points to collection.
void layoutAboutToBeChanged(const QList< QPersistentModelIndex > &parents, QAbstractItemModel::LayoutChangeHint hint)
~EntityOrderProxyModel() override
Destroys the entity order proxy model.
typedef MatchFlags
@ ParentCollectionRole
The parent collection of the entity.
qint64 Id
Describes the unique id type.
Definition: collection.h:79
QObject * parent() const const
Represents a PIM item stored in Akonadi storage.
Definition: item.h:104
Q_D(Todo)
Helper integration between Akonadi and Qt.
This file is part of the KDE documentation.
Documentation copyright © 1996-2022 The KDE developers.
Generated on Mon Jun 27 2022 04:01:06 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.