File indexing completed on 2024-11-10 04:40:33
0001 /* 0002 SPDX-FileCopyrightText: 2010 Klarälvdalens Datakonsult AB, 0003 a KDAB Group company, info@kdab.net 0004 SPDX-FileContributor: Stephen Kelly <stephen@kdab.com> 0005 0006 SPDX-License-Identifier: LGPL-2.0-or-later 0007 */ 0008 0009 #include "entityorderproxymodel.h" 0010 0011 #include <QMimeData> 0012 0013 #include <KConfigGroup> 0014 #include <QUrl> 0015 0016 #include "entitytreemodel.h" 0017 #include "item.h" 0018 0019 namespace Akonadi 0020 { 0021 class EntityOrderProxyModelPrivate 0022 { 0023 public: 0024 explicit EntityOrderProxyModelPrivate(EntityOrderProxyModel *qq) 0025 : q_ptr(qq) 0026 { 0027 } 0028 0029 void saveOrder(const QModelIndex &index); 0030 0031 KConfigGroup m_orderConfig; 0032 0033 Q_DECLARE_PUBLIC(EntityOrderProxyModel) 0034 EntityOrderProxyModel *const q_ptr; 0035 }; 0036 0037 } // namespace Akonadi 0038 0039 using namespace Akonadi; 0040 0041 EntityOrderProxyModel::EntityOrderProxyModel(QObject *parent) 0042 : QSortFilterProxyModel(parent) 0043 , d_ptr(new EntityOrderProxyModelPrivate(this)) 0044 { 0045 setRecursiveFilteringEnabled(true); 0046 setDynamicSortFilter(true); 0047 // setSortCaseSensitivity( Qt::CaseInsensitive ); 0048 } 0049 0050 EntityOrderProxyModel::~EntityOrderProxyModel() = default; 0051 0052 void EntityOrderProxyModel::setOrderConfig(const KConfigGroup &configGroup) 0053 { 0054 Q_D(EntityOrderProxyModel); 0055 Q_EMIT layoutAboutToBeChanged(); 0056 d->m_orderConfig = configGroup; 0057 Q_EMIT layoutChanged(); 0058 } 0059 0060 // reimplemented in FavoriteCollectionOrderProxyModel 0061 Collection EntityOrderProxyModel::parentCollection(const QModelIndex &index) const 0062 { 0063 return index.data(EntityTreeModel::ParentCollectionRole).value<Collection>(); 0064 } 0065 0066 static QString configKey(const Collection &col) 0067 { 0068 return !col.isValid() ? QStringLiteral("0") : QString::number(col.id()); 0069 } 0070 0071 bool EntityOrderProxyModel::lessThan(const QModelIndex &left, const QModelIndex &right) const 0072 { 0073 Q_D(const EntityOrderProxyModel); 0074 0075 if (!d->m_orderConfig.isValid()) { 0076 return QSortFilterProxyModel::lessThan(left, right); 0077 } 0078 const Collection col = parentCollection(left); 0079 0080 const QStringList list = d->m_orderConfig.readEntry(configKey(col), QStringList()); 0081 0082 if (list.isEmpty()) { 0083 return QSortFilterProxyModel::lessThan(left, right); 0084 } 0085 0086 const QString leftValue = configString(left); 0087 const QString rightValue = configString(right); 0088 0089 const int leftPosition = list.indexOf(leftValue); 0090 const int rightPosition = list.indexOf(rightValue); 0091 0092 if (leftPosition < 0 || rightPosition < 0) { 0093 return QSortFilterProxyModel::lessThan(left, right); 0094 } 0095 0096 return leftPosition < rightPosition; 0097 } 0098 0099 QStringList EntityOrderProxyModel::configStringsForDroppedUrls(const QList<QUrl> &urls, const Akonadi::Collection &parentCol, bool *containsMove) const 0100 { 0101 QStringList droppedList; 0102 droppedList.reserve(urls.count()); 0103 for (const QUrl &url : urls) { 0104 const Collection col = Collection::fromUrl(url); 0105 0106 if (!col.isValid()) { 0107 Item item = Item::fromUrl(url); 0108 if (!item.isValid()) { 0109 continue; 0110 } 0111 0112 const QModelIndexList list = EntityTreeModel::modelIndexesForItem(this, item); 0113 if (list.isEmpty()) { 0114 continue; 0115 } 0116 0117 if (!*containsMove && parentCollection(list.first()).id() != parentCol.id()) { 0118 *containsMove = true; 0119 } 0120 0121 droppedList << configString(list.first()); 0122 } else { 0123 const QModelIndex idx = EntityTreeModel::modelIndexForCollection(this, col); 0124 if (!idx.isValid()) { 0125 continue; 0126 } 0127 0128 if (!*containsMove && parentCollection(idx).id() != parentCol.id()) { 0129 *containsMove = true; 0130 } 0131 0132 droppedList << configString(idx); 0133 } 0134 } 0135 return droppedList; 0136 } 0137 0138 bool EntityOrderProxyModel::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) 0139 { 0140 Q_D(EntityOrderProxyModel); 0141 0142 if (!d->m_orderConfig.isValid()) { 0143 return QSortFilterProxyModel::dropMimeData(data, action, row, column, parent); 0144 } 0145 0146 if (!data->hasFormat(QStringLiteral("text/uri-list"))) { 0147 return QSortFilterProxyModel::dropMimeData(data, action, row, column, parent); 0148 } 0149 0150 if (row == -1) { 0151 return QSortFilterProxyModel::dropMimeData(data, action, row, column, parent); 0152 } 0153 0154 const QList<QUrl> urls = data->urls(); 0155 if (urls.isEmpty()) { 0156 return false; 0157 } 0158 0159 Collection parentCol; 0160 0161 if (parent.isValid()) { 0162 parentCol = parent.data(EntityTreeModel::CollectionRole).value<Collection>(); 0163 } else { 0164 if (!hasChildren(parent)) { 0165 return QSortFilterProxyModel::dropMimeData(data, action, row, column, parent); 0166 } 0167 0168 const QModelIndex targetIndex = index(0, column, parent); 0169 parentCol = parentCollection(targetIndex); 0170 } 0171 0172 bool containsMove = false; 0173 QStringList droppedList = configStringsForDroppedUrls(urls, parentCol, &containsMove); 0174 0175 // Dropping new favorite folders 0176 if (droppedList.isEmpty()) { 0177 const bool ok = QSortFilterProxyModel::dropMimeData(data, action, row, column, parent); 0178 if (ok) { 0179 droppedList = configStringsForDroppedUrls(urls, parentCol, &containsMove); 0180 } 0181 } 0182 0183 QStringList existingList; 0184 if (d->m_orderConfig.hasKey(QString::number(parentCol.id()))) { 0185 existingList = d->m_orderConfig.readEntry(configKey(parentCol), QStringList()); 0186 } else { 0187 const int rowCount = this->rowCount(parent); 0188 existingList.reserve(rowCount); 0189 for (int row = 0; row < rowCount; ++row) { 0190 static const int column = 0; 0191 const QModelIndex idx = this->index(row, column, parent); 0192 existingList.append(configString(idx)); 0193 } 0194 } 0195 const int numberOfDroppedElement(droppedList.size()); 0196 for (int i = 0; i < numberOfDroppedElement; ++i) { 0197 const QString &droppedItem = droppedList.at(i); 0198 const int existingIndex = existingList.indexOf(droppedItem); 0199 existingList.removeAt(existingIndex); 0200 existingList.insert(row + i - (existingIndex > row ? 0 : 1), droppedList.at(i)); 0201 } 0202 0203 d->m_orderConfig.writeEntry(configKey(parentCol), existingList); 0204 0205 if (containsMove) { 0206 bool result = QSortFilterProxyModel::dropMimeData(data, action, row, column, parent); 0207 invalidate(); 0208 return result; 0209 } 0210 invalidate(); 0211 return true; 0212 } 0213 0214 QModelIndexList EntityOrderProxyModel::match(const QModelIndex &start, int role, const QVariant &value, int hits, Qt::MatchFlags flags) const 0215 { 0216 if (role < Qt::UserRole) { 0217 return QSortFilterProxyModel::match(start, role, value, hits, flags); 0218 } 0219 0220 QModelIndexList list; 0221 QModelIndex proxyIndex; 0222 const auto matches = sourceModel()->match(mapToSource(start), role, value, hits, flags); 0223 for (const auto &idx : matches) { 0224 proxyIndex = mapFromSource(idx); 0225 if (proxyIndex.isValid()) { 0226 list.push_back(proxyIndex); 0227 } 0228 } 0229 0230 return list; 0231 } 0232 0233 void EntityOrderProxyModelPrivate::saveOrder(const QModelIndex &parent) 0234 { 0235 Q_Q(const EntityOrderProxyModel); 0236 int rowCount = q->rowCount(parent); 0237 0238 if (rowCount == 0) { 0239 return; 0240 } 0241 0242 static const int column = 0; 0243 QModelIndex childIndex = q->index(0, column, parent); 0244 0245 const QString parentKey = q->parentConfigString(childIndex); 0246 0247 if (parentKey.isEmpty()) { 0248 return; 0249 } 0250 0251 QStringList list; 0252 0253 list << q->configString(childIndex); 0254 saveOrder(childIndex); 0255 list.reserve(list.count() + rowCount); 0256 for (int row = 1; row < rowCount; ++row) { 0257 childIndex = q->index(row, column, parent); 0258 list << q->configString(childIndex); 0259 saveOrder(childIndex); 0260 } 0261 0262 m_orderConfig.writeEntry(parentKey, list); 0263 } 0264 0265 QString EntityOrderProxyModel::parentConfigString(const QModelIndex &index) const 0266 { 0267 const Collection col = parentCollection(index); 0268 0269 Q_ASSERT(col.isValid()); 0270 if (!col.isValid()) { 0271 return QString(); 0272 } 0273 0274 return QString::number(col.id()); 0275 } 0276 0277 QString EntityOrderProxyModel::configString(const QModelIndex &index) const 0278 { 0279 Item::Id iId = index.data(EntityTreeModel::ItemIdRole).toLongLong(); 0280 if (iId != -1) { 0281 return QLatin1Char('i') + QString::number(iId); 0282 } 0283 Collection::Id cId = index.data(EntityTreeModel::CollectionIdRole).toLongLong(); 0284 if (cId != -1) { 0285 return QLatin1Char('c') + QString::number(cId); 0286 } 0287 Q_ASSERT(!"Invalid entity"); 0288 return QString(); 0289 } 0290 0291 void EntityOrderProxyModel::saveOrder() 0292 { 0293 Q_D(EntityOrderProxyModel); 0294 d->saveOrder(QModelIndex()); 0295 d->m_orderConfig.sync(); 0296 } 0297 0298 void EntityOrderProxyModel::clearOrder(const QModelIndex &parent) 0299 { 0300 Q_D(EntityOrderProxyModel); 0301 0302 const QString parentKey = parentConfigString(index(0, 0, parent)); 0303 0304 if (parentKey.isEmpty()) { 0305 return; 0306 } 0307 0308 d->m_orderConfig.deleteEntry(parentKey); 0309 invalidate(); 0310 } 0311 0312 void EntityOrderProxyModel::clearTreeOrder() 0313 { 0314 Q_D(EntityOrderProxyModel); 0315 d->m_orderConfig.deleteGroup(); 0316 invalidate(); 0317 } 0318 0319 #include "moc_entityorderproxymodel.cpp"