X-Git-Url: https://cloud.milkyroute.net/gitweb/dolphin.git/blobdiff_plain/7a91492cff931c0c4e0d38dd0aee77d9dcb29373..283f97ac27c3cfe5c72682b0843503e31643a612:/src/kitemviews/kfileitemmodel.cpp diff --git a/src/kitemviews/kfileitemmodel.cpp b/src/kitemviews/kfileitemmodel.cpp index 401ba218d..3a49135f9 100644 --- a/src/kitemviews/kfileitemmodel.cpp +++ b/src/kitemviews/kfileitemmodel.cpp @@ -31,11 +31,10 @@ #define KFILEITEMMODEL_DEBUG KFileItemModel::KFileItemModel(KDirLister* dirLister, QObject* parent) : - KItemModelBase(QByteArray(), "name", parent), + KItemModelBase("name", parent), m_dirLister(dirLister), m_naturalSorting(true), m_sortFoldersFirst(true), - m_groupRole(NoRole), m_sortRole(NameRole), m_caseSensitivity(Qt::CaseInsensitive), m_sortedItems(), @@ -45,7 +44,10 @@ KFileItemModel::KFileItemModel(KDirLister* dirLister, QObject* parent) : m_minimumUpdateIntervalTimer(0), m_maximumUpdateIntervalTimer(0), m_pendingItemsToInsert(), - m_rootExpansionLevel(-1) + m_pendingEmitLoadingCompleted(false), + m_rootExpansionLevel(-1), + m_expandedUrls(), + m_restoredExpandedUrls() { resetRoles(); m_requestRole[NameRole] = true; @@ -57,6 +59,7 @@ KFileItemModel::KFileItemModel(KDirLister* dirLister, QObject* parent) : connect(dirLister, SIGNAL(completed()), this, SLOT(slotCompleted())); connect(dirLister, SIGNAL(newItems(KFileItemList)), this, SLOT(slotNewItems(KFileItemList))); connect(dirLister, SIGNAL(itemsDeleted(KFileItemList)), this, SLOT(slotItemsDeleted(KFileItemList))); + connect(dirLister, SIGNAL(refreshItems(QList >)), this, SLOT(slotRefreshItems(QList >))); connect(dirLister, SIGNAL(clear()), this, SLOT(slotClear())); connect(dirLister, SIGNAL(clear(KUrl)), this, SLOT(slotClear(KUrl))); @@ -123,14 +126,17 @@ bool KFileItemModel::setData(int index, const QHash& value return false; } -bool KFileItemModel::supportsGrouping() const +void KFileItemModel::setSortFoldersFirst(bool foldersFirst) { - return true; + if (foldersFirst != m_sortFoldersFirst) { + m_sortFoldersFirst = foldersFirst; + resortAllItems(); + } } -bool KFileItemModel::supportsSorting() const +bool KFileItemModel::sortFoldersFirst() const { - return true; + return m_sortFoldersFirst; } QMimeData* KFileItemModel::createMimeData(const QSet& indexes) const @@ -174,15 +180,13 @@ QMimeData* KFileItemModel::createMimeData(const QSet& indexes) const int KFileItemModel::indexForKeyboardSearch(const QString& text, int startFromIndex) const { startFromIndex = qMax(0, startFromIndex); - for (int i = startFromIndex; i < count(); i++) { + for (int i = startFromIndex; i < count(); ++i) { if (data(i)["name"].toString().startsWith(text, Qt::CaseInsensitive)) { - kDebug() << data(i)["name"].toString(); return i; } } - for (int i = 0; i < startFromIndex; i++) { + for (int i = 0; i < startFromIndex; ++i) { if (data(i)["name"].toString().startsWith(text, Qt::CaseInsensitive)) { - kDebug() << data(i)["name"].toString(); return i; } } @@ -195,6 +199,44 @@ bool KFileItemModel::supportsDropping(int index) const return item.isNull() ? false : item.isDir(); } +QString KFileItemModel::roleDescription(const QByteArray& role) const +{ + QString descr; + + switch (roleIndex(role)) { + case NameRole: descr = i18nc("@item:intable", "Name"); break; + case SizeRole: descr = i18nc("@item:intable", "Size"); break; + case DateRole: descr = i18nc("@item:intable", "Date"); break; + case PermissionsRole: descr = i18nc("@item:intable", "Permissions"); break; + case OwnerRole: descr = i18nc("@item:intable", "Owner"); break; + case GroupRole: descr = i18nc("@item:intable", "Group"); break; + case TypeRole: descr = i18nc("@item:intable", "Type"); break; + case DestinationRole: descr = i18nc("@item:intable", "Destination"); break; + case PathRole: descr = i18nc("@item:intable", "Path"); break; + case NoRole: break; + case IsDirRole: break; + case IsExpandedRole: break; + case ExpansionLevelRole: break; + default: Q_ASSERT(false); break; + } + + return descr; +} + +QList > KFileItemModel::groups() const +{ + // TODO: + QPair group1(0, "Group 1"); + QPair group2(5, "Group 2"); + QPair group3(10, "Group 3"); + + QList > groups; + groups.append(group1); + groups.append(group2); + groups.append(group3); + return groups; +} + KFileItem KFileItemModel::fileItem(int index) const { if (index >= 0 && index < count()) { @@ -204,13 +246,38 @@ KFileItem KFileItemModel::fileItem(int index) const return KFileItem(); } +KFileItem KFileItemModel::fileItem(const KUrl& url) const +{ + const int index = m_items.value(url, -1); + if (index >= 0) { + return m_sortedItems.at(index); + } + return KFileItem(); +} + int KFileItemModel::index(const KFileItem& item) const { if (item.isNull()) { return -1; } - return m_items.value(item, -1); + return m_items.value(item.url(), -1); +} + +int KFileItemModel::index(const KUrl& url) const +{ + KUrl urlToFind = url; + urlToFind.adjustPath(KUrl::RemoveTrailingSlash); + return m_items.value(urlToFind, -1); +} + +KFileItem KFileItemModel::rootItem() const +{ + const KDirLister* dirLister = m_dirLister.data(); + if (dirLister) { + return dirLister->rootItem(); + } + return KFileItem(); } void KFileItemModel::clear() @@ -287,14 +354,18 @@ bool KFileItemModel::setExpanded(int index, bool expanded) return false; } + const KUrl url = m_sortedItems.at(index).url(); if (expanded) { - const KUrl url = m_sortedItems.at(index).url(); + m_expandedUrls.insert(url); + KDirLister* dirLister = m_dirLister.data(); if (dirLister) { dirLister->openUrl(url, KDirLister::Keep); return true; } } else { + m_expandedUrls.remove(url); + KFileItemList itemsToRemove; const int expansionLevel = data(index)["expansionLevel"].toInt(); ++index; @@ -325,50 +396,69 @@ bool KFileItemModel::isExpandable(int index) const return false; } -void KFileItemModel::onGroupRoleChanged(const QByteArray& current, const QByteArray& previous) +QSet KFileItemModel::expandedUrls() const { - Q_UNUSED(previous); - m_groupRole = roleIndex(current); + return m_expandedUrls; +} + +void KFileItemModel::restoreExpandedUrls(const QSet& urls) +{ + m_restoredExpandedUrls = urls; +} + +void KFileItemModel::onGroupedSortingChanged(bool current) +{ + Q_UNUSED(current); } void KFileItemModel::onSortRoleChanged(const QByteArray& current, const QByteArray& previous) { Q_UNUSED(previous); - const int itemCount = count(); - if (itemCount <= 0) { - return; - } - m_sortRole = roleIndex(current); + resortAllItems(); +} - KFileItemList sortedItems = m_sortedItems; - m_sortedItems.clear(); - m_items.clear(); - m_data.clear(); - emit itemsRemoved(KItemRangeList() << KItemRange(0, itemCount)); - - sort(sortedItems.begin(), sortedItems.end()); - int index = 0; - foreach (const KFileItem& item, sortedItems) { - m_sortedItems.append(item); - m_items.insert(item, index); - m_data.append(retrieveData(item)); - - ++index; - } - - emit itemsInserted(KItemRangeList() << KItemRange(0, itemCount)); +void KFileItemModel::onSortOrderChanged(Qt::SortOrder current, Qt::SortOrder previous) +{ + Q_UNUSED(current); + Q_UNUSED(previous); + resortAllItems(); } void KFileItemModel::slotCompleted() { - if (m_minimumUpdateIntervalTimer->isActive()) { + if (m_restoredExpandedUrls.isEmpty() && m_minimumUpdateIntervalTimer->isActive()) { // dispatchPendingItems() will be called when the timer // has been expired. + m_pendingEmitLoadingCompleted = true; return; } + m_pendingEmitLoadingCompleted = false; dispatchPendingItemsToInsert(); + + if (!m_restoredExpandedUrls.isEmpty()) { + // Try to find a URL that can be expanded. + // Note that the parent folder must be expanded before any of its subfolders become visible. + // Therefore, some URLs in m_restoredExpandedUrls might not be visible yet + // -> we expand the first visible URL we find in m_restoredExpandedUrls. + foreach(const KUrl& url, m_restoredExpandedUrls) { + const int index = m_items.value(url, -1); + if (index >= 0) { + // We have found an expandable URL. Expand it and return - when + // the dir lister has finished, this slot will be called again. + m_restoredExpandedUrls.remove(url); + setExpanded(index, true); + return; + } + } + + // None of the URLs in m_restoredExpandedUrls could be found in the model. This can happen + // if these URLs have been deleted in the meantime. + m_restoredExpandedUrls.clear(); + } + + emit loadingCompleted(); m_minimumUpdateIntervalTimer->start(); } @@ -399,6 +489,61 @@ void KFileItemModel::slotItemsDeleted(const KFileItemList& items) removeItems(items); } +void KFileItemModel::slotRefreshItems(const QList >& items) +{ + Q_ASSERT(!items.isEmpty()); +#ifdef KFILEITEMMODEL_DEBUG + kDebug() << "Refreshing" << items.count() << "items"; +#endif + + // Get the indexes of all items that have been refreshed + QList indexes; + indexes.reserve(items.count()); + + QListIterator > it(items); + while (it.hasNext()) { + const QPair& itemPair = it.next(); + const int index = m_items.value(itemPair.second.url(), -1); + if (index >= 0) { + indexes.append(index); + } + } + + // If the changed items have been created recently, they might not be in m_items yet. + // In that case, the list 'indexes' might be empty. + if (indexes.isEmpty()) { + return; + } + + // Extract the item-ranges out of the changed indexes + qSort(indexes); + + KItemRangeList itemRangeList; + int rangeIndex = 0; + int rangeCount = 1; + int previousIndex = indexes.at(0); + + const int maxIndex = indexes.count() - 1; + for (int i = 1; i <= maxIndex; ++i) { + const int currentIndex = indexes.at(i); + if (currentIndex == previousIndex + 1) { + ++rangeCount; + } else { + itemRangeList.append(KItemRange(rangeIndex, rangeCount)); + + rangeIndex = currentIndex; + rangeCount = 1; + } + previousIndex = currentIndex; + } + + if (rangeCount > 0) { + itemRangeList.append(KItemRange(rangeIndex, rangeCount)); + } + + emit itemsChanged(itemRangeList, QSet()); +} + void KFileItemModel::slotClear() { #ifdef KFILEITEMMODEL_DEBUG @@ -418,6 +563,8 @@ void KFileItemModel::slotClear() m_data.clear(); emit itemsRemoved(KItemRangeList() << KItemRange(0, removedCount)); } + + m_expandedUrls.clear(); } void KFileItemModel::slotClear(const KUrl& url) @@ -431,6 +578,10 @@ void KFileItemModel::dispatchPendingItemsToInsert() insertItems(m_pendingItemsToInsert); m_pendingItemsToInsert.clear(); } + + if (m_pendingEmitLoadingCompleted) { + emit loadingCompleted(); + } } void KFileItemModel::insertItems(const KFileItemList& items) @@ -495,7 +646,7 @@ void KFileItemModel::insertItems(const KFileItemList& items) // The indexes of all m_items must be adjusted, not only the index // of the new items for (int i = 0; i < m_sortedItems.count(); ++i) { - m_items.insert(m_sortedItems.at(i), i); + m_items.insert(m_sortedItems.at(i).url(), i); } itemRanges << KItemRange(insertedAtIndex, insertedCount); @@ -557,7 +708,7 @@ void KFileItemModel::removeItems(const KFileItemList& items) // Delete the items for (int i = indexesToRemove.count() - 1; i >= 0; --i) { const int indexToRemove = indexesToRemove.at(i); - m_items.remove(m_sortedItems.at(indexToRemove)); + m_items.remove(m_sortedItems.at(indexToRemove).url()); m_sortedItems.removeAt(indexToRemove); m_data.removeAt(indexToRemove); } @@ -565,7 +716,7 @@ void KFileItemModel::removeItems(const KFileItemList& items) // The indexes of all m_items must be adjusted, not only the index // of the removed items for (int i = 0; i < m_sortedItems.count(); ++i) { - m_items.insert(m_sortedItems.at(i), i); + m_items.insert(m_sortedItems.at(i).url(), i); } if (count() <= 0) { @@ -576,9 +727,49 @@ void KFileItemModel::removeItems(const KFileItemList& items) emit itemsRemoved(itemRanges); } -void KFileItemModel::removeExpandedItems() +void KFileItemModel::resortAllItems() { + const int itemCount = count(); + if (itemCount <= 0) { + return; + } + + const KFileItemList oldSortedItems = m_sortedItems; + const QHash oldItems = m_items; + const QList > oldData = m_data; + + m_items.clear(); + m_data.clear(); + sort(m_sortedItems.begin(), m_sortedItems.end()); + int index = 0; + foreach (const KFileItem& item, m_sortedItems) { + m_items.insert(item.url(), index); + + const int oldItemIndex = oldItems.value(item.url()); + m_data.append(oldData.at(oldItemIndex)); + + ++index; + } + + bool emitItemsMoved = false; + QList movedToIndexes; + movedToIndexes.reserve(m_sortedItems.count()); + for (int i = 0; i < itemCount; i++) { + const int newIndex = m_items.value(oldSortedItems.at(i).url()); + movedToIndexes.append(newIndex); + if (!emitItemsMoved && newIndex != i) { + emitItemsMoved = true; + } + } + + if (emitItemsMoved) { + emit itemsMoved(KItemRange(0, itemCount), movedToIndexes); + } +} + +void KFileItemModel::removeExpandedItems() +{ KFileItemList expandedItems; const int maxIndex = m_data.count() - 1; @@ -595,6 +786,7 @@ void KFileItemModel::removeExpandedItems() removeItems(expandedItems); m_rootExpansionLevel = -1; + m_expandedUrls.clear(); } void KFileItemModel::resetRoles() @@ -717,11 +909,11 @@ bool KFileItemModel::lessThan(const KFileItem& a, const KFileItem& b) const result = expansionLevelsCompare(a, b); if (result != 0) { // The items have parents with different expansion levels - return result < 0; + return (sortOrder() == Qt::AscendingOrder) ? result < 0 : result > 0; } } - if (m_sortFoldersFirst) { + if (m_sortFoldersFirst || m_sortRole == SizeRole) { const bool isDirA = a.isDir(); const bool isDirB = b.isDir(); if (isDirA && !isDirB) { @@ -753,6 +945,20 @@ bool KFileItemModel::lessThan(const KFileItem& a, const KFileItem& b) const break; } + case SizeRole: { + // TODO: Implement sorting folders by the number of items inside. + // This is more tricky to get right because this number is retrieved + // asynchronously by KFileItemModelRolesUpdater. + const KIO::filesize_t sizeA = a.size(); + const KIO::filesize_t sizeB = b.size(); + if (sizeA < sizeB) { + result = -1; + } else if (sizeA > sizeB) { + result = +1; + } + break; + } + default: break; } @@ -764,7 +970,7 @@ bool KFileItemModel::lessThan(const KFileItem& a, const KFileItem& b) const result = QString::compare(a.url().url(), b.url().url(), Qt::CaseSensitive); } - return result < 0; + return (sortOrder() == Qt::AscendingOrder) ? result < 0 : result > 0; } void KFileItemModel::sort(const KFileItemList::iterator& startIterator, const KFileItemList::iterator& endIterator)