Akonadi

entityorderproxymodel.cpp
1/*
2 SPDX-FileCopyrightText: 2010 Klarälvdalens Datakonsult AB,
3 a KDAB Group company, info@kdab.net
4 SPDX-FileContributor: Stephen Kelly <stephen@kdab.com>
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
19namespace Akonadi
20{
21class EntityOrderProxyModelPrivate
22{
23public:
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
39using namespace Akonadi;
40
42 : QSortFilterProxyModel(parent)
43 , d_ptr(new EntityOrderProxyModelPrivate(this))
44{
46
47 // setSortCaseSensitivity( Qt::CaseInsensitive );
48}
49
51
53{
56 d->m_orderConfig = configGroup;
58}
59
60// reimplemented in FavoriteCollectionOrderProxyModel
61Collection EntityOrderProxyModel::parentCollection(const QModelIndex &index) const
62{
64}
65
66static QString configKey(const Collection &col)
67{
68 return !col.isValid() ? QStringLiteral("0") : QString::number(col.id());
69}
70
71bool 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
99QStringList 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
138bool 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
214QModelIndexList 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
233void 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
265QString 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
277QString 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
298void 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
312void EntityOrderProxyModel::clearTreeOrder()
313{
315 d->m_orderConfig.deleteGroup();
316 invalidate();
317}
318
319#include "moc_entityorderproxymodel.cpp"
Represents a collection of PIM items.
Definition collection.h:62
qint64 Id
Describes the unique id type.
Definition collection.h:79
static Collection fromUrl(const QUrl &url)
Creates a collection from the given url.
A model that keeps the order of entities persistent.
QModelIndexList match(const QModelIndex &start, int role, const QVariant &value, int hits=1, Qt::MatchFlags flags=Qt::MatchFlags(Qt::MatchStartsWith|Qt::MatchWrap)) const override
void setOrderConfig(const KConfigGroup &group)
Sets the config group that will be used for storing the order.
bool lessThan(const QModelIndex &left, const QModelIndex &right) const override
bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) override
~EntityOrderProxyModel() override
Destroys the entity order proxy model.
EntityOrderProxyModel(QObject *parent=nullptr)
Creates a new entity order proxy model.
static QModelIndexList modelIndexesForItem(const QAbstractItemModel *model, const Item &item)
Returns a QModelIndex in model which points to item.
static QModelIndex modelIndexForCollection(const QAbstractItemModel *model, const Collection &collection)
Returns a QModelIndex in model which points to collection.
@ ParentCollectionRole
The parent collection of the entity.
@ CollectionRole
The collection.
@ CollectionIdRole
The collection id.
Represents a PIM item stored in Akonadi storage.
Definition item.h:100
qint64 Id
Describes the unique id type.
Definition item.h:105
bool isValid() const
Returns whether the item is valid.
Definition item.cpp:88
static Item fromUrl(const QUrl &url)
Creates an item from the given url.
Definition item.cpp:386
void writeEntry(const char *key, const char *value, WriteConfigFlags pFlags=Normal)
Q_SCRIPTABLE Q_NOREPLY void start()
Helper integration between Akonadi and Qt.
KIOCORE_EXPORT QStringList list(const QString &fileClass)
void layoutAboutToBeChanged(const QList< QPersistentModelIndex > &parents, QAbstractItemModel::LayoutChangeHint hint)
void layoutChanged(const QList< QPersistentModelIndex > &parents, QAbstractItemModel::LayoutChangeHint hint)
void append(QList< T > &&value)
const_reference at(qsizetype i) const const
qsizetype count() const const
T & first()
iterator insert(const_iterator before, parameter_type value)
bool isEmpty() const const
void push_back(parameter_type value)
void removeAt(qsizetype i)
void reserve(qsizetype size)
qsizetype size() const const
QVariant data(int role) const const
bool isValid() const const
Q_EMITQ_EMIT
QObject * parent() const const
virtual QVariant data(const QModelIndex &index, int role) const const override
virtual bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) override
virtual Qt::ItemFlags flags(const QModelIndex &index) const const override
virtual bool hasChildren(const QModelIndex &parent) const const override
virtual QModelIndex index(int row, int column, const QModelIndex &parent) const const override
virtual bool lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const const
virtual QModelIndex mapFromSource(const QModelIndex &sourceIndex) const const override
virtual QModelIndex mapToSource(const QModelIndex &proxyIndex) const const override
virtual QModelIndexList match(const QModelIndex &start, int role, const QVariant &value, int hits, Qt::MatchFlags flags) const const override
void setRecursiveFilteringEnabled(bool recursive)
virtual int rowCount(const QModelIndex &parent) const const override
bool isEmpty() const const
QString number(double n, char format, int precision)
qsizetype indexOf(const QRegularExpression &re, qsizetype from) const const
DropAction
UserRole
typedef MatchFlags
qlonglong toLongLong(bool *ok) const const
T value() const const
Q_D(Todo)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Mon Nov 18 2024 12:08:29 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.