X-Git-Url: https://cloud.milkyroute.net/gitweb/dolphin.git/blobdiff_plain/a3c41997db62e9af2a89b23a61bc7dda75aa1e58..3c83d8f14defbec5f09aeeb78de3885535d2dc71:/src/kitemviews/kfileitemmodel.cpp diff --git a/src/kitemviews/kfileitemmodel.cpp b/src/kitemviews/kfileitemmodel.cpp index 748a9bae1..e0ae03302 100644 --- a/src/kitemviews/kfileitemmodel.cpp +++ b/src/kitemviews/kfileitemmodel.cpp @@ -21,6 +21,7 @@ #include #include +#include #include #include #include @@ -33,7 +34,7 @@ KFileItemModel::KFileItemModel(KDirLister* dirLister, QObject* parent) : KItemModelBase("name", parent), m_dirLister(dirLister), - m_naturalSorting(true), + m_naturalSorting(KGlobalSettings::naturalSorting()), m_sortFoldersFirst(true), m_sortRole(NameRole), m_roles(), @@ -63,7 +64,7 @@ KFileItemModel::KFileItemModel(KDirLister* dirLister, QObject* parent) : Q_ASSERT(dirLister); connect(dirLister, SIGNAL(canceled()), this, SLOT(slotCanceled())); - connect(dirLister, SIGNAL(completed()), this, SLOT(slotCompleted())); + connect(dirLister, SIGNAL(completed(KUrl)), 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 >))); @@ -95,6 +96,8 @@ KFileItemModel::KFileItemModel(KDirLister* dirLister, QObject* parent) : connect(m_resortAllItemsTimer, SIGNAL(timeout()), this, SLOT(resortAllItems())); Q_ASSERT(m_minimumUpdateIntervalTimer->interval() <= m_maximumUpdateIntervalTimer->interval()); + + connect(KGlobalSettings::self(), SIGNAL(naturalSortingChanged()), this, SLOT(slotNaturalSortingChanged())); } KFileItemModel::~KFileItemModel() @@ -183,6 +186,20 @@ bool KFileItemModel::showHiddenFiles() const return dirLister ? dirLister->showingDotFiles() : false; } +void KFileItemModel::setShowFoldersOnly(bool enabled) +{ + KDirLister* dirLister = m_dirLister.data(); + if (dirLister) { + dirLister->setDirOnlyMode(enabled); + } +} + +bool KFileItemModel::showFoldersOnly() const +{ + KDirLister* dirLister = m_dirLister.data(); + return dirLister ? dirLister->dirOnlyMode() : false; +} + QMimeData* KFileItemModel::createMimeData(const QSet& indexes) const { QMimeData* data = new QMimeData(); @@ -240,7 +257,7 @@ int KFileItemModel::indexForKeyboardSearch(const QString& text, int startFromInd bool KFileItemModel::supportsDropping(int index) const { const KFileItem item = fileItem(index); - return item.isNull() ? false : item.isDir(); + return item.isNull() ? false : item.isDir() || item.isDesktopFile(); } QString KFileItemModel::roleDescription(const QByteArray& role) const @@ -358,8 +375,8 @@ void KFileItemModel::setRoles(const QSet& roles) m_roles = roles; if (count() > 0) { - const bool supportedExpanding = m_requestRole[IsExpandedRole] && m_requestRole[ExpansionLevelRole]; - const bool willSupportExpanding = roles.contains("isExpanded") && roles.contains("expansionLevel"); + const bool supportedExpanding = m_requestRole[ExpansionLevelRole]; + const bool willSupportExpanding = roles.contains("expansionLevel"); if (supportedExpanding && !willSupportExpanding) { // No expanding is supported anymore. Take care to delete all items that have an expansion level // that is not 0 (and hence are part of an expanded item). @@ -367,6 +384,7 @@ void KFileItemModel::setRoles(const QSet& roles) } } + m_groups.clear(); resetRoles(); QSetIterator it(roles); @@ -394,7 +412,7 @@ QSet KFileItemModel::roles() const bool KFileItemModel::setExpanded(int index, bool expanded) { - if (isExpanded(index) == expanded || index < 0 || index >= count()) { + if (!isExpandable(index) || isExpanded(index) == expanded) { return false; } @@ -445,7 +463,7 @@ bool KFileItemModel::isExpanded(int index) const bool KFileItemModel::isExpandable(int index) const { if (index >= 0 && index < count()) { - return m_itemData.at(index)->item.isDir(); + return m_itemData.at(index)->values.value("isExpandable").toBool(); } return false; } @@ -460,30 +478,24 @@ void KFileItemModel::restoreExpandedUrls(const QSet& urls) m_urlsToExpand = urls; } -void KFileItemModel::setExpanded(const QSet& urls) +void KFileItemModel::expandParentItems(const KUrl& url) { - const KDirLister* dirLister = m_dirLister.data(); if (!dirLister) { return; } - const int pos = dirLister->url().url().length(); + const int pos = dirLister->url().path().length(); - // Assure that each sub-path of the URLs that should be - // expanded is added to m_urlsToExpand too. KDirLister + // Assure that each sub-path of the URL that should be + // expanded is added to m_urlsToExpand. KDirLister // does not care whether the parent-URL has already been // expanded. - QSetIterator it1(urls); - while (it1.hasNext()) { - const KUrl& url = it1.next(); - - KUrl urlToExpand = dirLister->url(); - const QStringList subDirs = url.url().mid(pos).split(QDir::separator()); - for (int i = 0; i < subDirs.count(); ++i) { - urlToExpand.addPath(subDirs.at(i)); - m_urlsToExpand.insert(urlToExpand); - } + KUrl urlToExpand = dirLister->url(); + const QStringList subDirs = url.path().mid(pos).split(QDir::separator()); + for (int i = 0; i < subDirs.count() - 1; ++i) { + urlToExpand.addPath(subDirs.at(i)); + m_urlsToExpand.insert(urlToExpand); } // KDirLister::open() must called at least once to trigger an initial @@ -607,20 +619,19 @@ void KFileItemModel::resortAllItems() } // Determine the indexes that have been moved - bool emitItemsMoved = false; QList movedToIndexes; movedToIndexes.reserve(itemCount); for (int i = 0; i < itemCount; i++) { const int newIndex = m_items.value(oldUrls.at(i).url()); movedToIndexes.append(newIndex); - if (!emitItemsMoved && newIndex != i) { - emitItemsMoved = true; - } } - if (emitItemsMoved) { - emit itemsMoved(KItemRange(0, itemCount), movedToIndexes); - } + // Don't check whether items have really been moved and always emit a + // itemsMoved() signal after resorting: In case of grouped items + // the groups might change even if the items themselves don't change their + // position. Let the receiver of the signal decide whether a check for moved + // items makes sense. + emit itemsMoved(KItemRange(0, itemCount), movedToIndexes); #ifdef KFILEITEMMODEL_DEBUG kDebug() << "[TIME] Resorting of" << itemCount << "items:" << timer.elapsed(); @@ -674,19 +685,35 @@ void KFileItemModel::slotCanceled() void KFileItemModel::slotNewItems(const KFileItemList& items) { + Q_ASSERT(!items.isEmpty()); + if (m_requestRole[ExpansionLevelRole] && m_rootExpansionLevel >= 0) { - // If the expanding of items is enabled in the model, it might be - // possible that the call dirLister->openUrl(url, KDirLister::Keep) in - // KFileItemModel::setExpanded() results in emitting of the same items - // twice due to the Keep-parameter. This case happens if an item gets - // expanded, collapsed and expanded again before the items could be loaded - // for the first expansion. - foreach (const KFileItem& item, items) { - const int index = m_items.value(item.url(), -1); - if (index >= 0) { - // The items are already part of the model. - return; - } + // To be able to compare whether the new items may be inserted as children + // of a parent item the pending items must be added to the model first. + dispatchPendingItemsToInsert(); + + KFileItem item = items.first(); + + // If the expanding of items is enabled, the call + // dirLister->openUrl(url, KDirLister::Keep) in KFileItemModel::setExpanded() + // might result in emitting the same items twice due to the Keep-parameter. + // This case happens if an item gets expanded, collapsed and expanded again + // before the items could be loaded for the first expansion. + const int index = m_items.value(item.url(), -1); + if (index >= 0) { + // The items are already part of the model. + return; + } + + // KDirLister keeps the children of items that got expanded once even if + // they got collapsed again with KFileItemModel::setExpanded(false). So it must be + // checked whether the parent for new items is still expanded. + KUrl parentUrl = item.url().upUrl(); + parentUrl.adjustPath(KUrl::RemoveTrailingSlash); + const int parentIndex = m_items.value(parentUrl, -1); + if (parentIndex >= 0 && !m_itemData[parentIndex]->values.value("isExpanded").toBool()) { + // The parent is not expanded. + return; } } @@ -719,13 +746,21 @@ void KFileItemModel::slotItemsDeleted(const KFileItemList& items) { dispatchPendingItemsToInsert(); - if (!m_filteredItems.isEmpty()) { + KFileItemList itemsToRemove = items; + if (m_requestRole[ExpansionLevelRole] && m_rootExpansionLevel >= 0) { + // Assure that removing a parent item also results in removing all children foreach (const KFileItem& item, items) { + itemsToRemove.append(childItems(item)); + } + } + + if (!m_filteredItems.isEmpty()) { + foreach (const KFileItem& item, itemsToRemove) { m_filteredItems.remove(item); } } - removeItems(items); + removeItems(itemsToRemove); } void KFileItemModel::slotRefreshItems(const QList >& items) @@ -825,6 +860,12 @@ void KFileItemModel::slotClear(const KUrl& url) Q_UNUSED(url); } +void KFileItemModel::slotNaturalSortingChanged() +{ + m_naturalSorting = KGlobalSettings::naturalSorting(); + resortAllItems(); +} + void KFileItemModel::dispatchPendingItemsToInsert() { if (!m_pendingItemsToInsert.isEmpty()) { @@ -1073,11 +1114,36 @@ KFileItemModel::Role KFileItemModel::roleIndex(const QByteArray& role) const rolesHash.insert("rating", RatingRole); rolesHash.insert("isDir", IsDirRole); rolesHash.insert("isExpanded", IsExpandedRole); + rolesHash.insert("isExpandable", IsExpandableRole); rolesHash.insert("expansionLevel", ExpansionLevelRole); } return rolesHash.value(role, NoRole); } +QByteArray KFileItemModel::roleByteArray(Role role) const +{ + static const char* const roles[RolesCount] = { + 0, // NoRole + "name", + "size", + "date", + "permissions", + "owner", + "group", + "type", + "destination", + "path", + "comment", + "tags", + "rating", + "isDir", + "isExpanded", + "isExpandable", + "expansionLevel" + }; + return roles[role]; +} + QHash KFileItemModel::retrieveData(const KFileItem& item) const { // It is important to insert only roles that are fast to retrieve. E.g. @@ -1133,34 +1199,39 @@ QHash KFileItemModel::retrieveData(const KFileItem& item) } if (m_requestRole[PathRole]) { + QString path; if (item.url().protocol() == QLatin1String("trash")) { - const KIO::UDSEntry udsEntry = item.entry(); - data.insert("path", udsEntry.stringValue(KIO::UDSEntry::UDS_EXTRA)); + path = item.entry().stringValue(KIO::UDSEntry::UDS_EXTRA); } else { - data.insert("path", item.localPath()); + path = item.localPath(); } + + const int index = path.lastIndexOf(item.text()); + path = path.mid(0, index - 1); + data.insert("path", path); } if (m_requestRole[IsExpandedRole]) { data.insert("isExpanded", false); } + if (m_requestRole[IsExpandableRole]) { + data.insert("isExpandable", item.isDir() && item.url() == item.targetUrl()); + } + if (m_requestRole[ExpansionLevelRole]) { if (m_rootExpansionLevel == UninitializedRootExpansionLevel && m_dirLister.data()) { const KUrl rootUrl = m_dirLister.data()->url(); const QString protocol = rootUrl.protocol(); const bool forceRootExpansionLevel = (protocol == QLatin1String("trash") || protocol == QLatin1String("nepomuk") || + protocol == QLatin1String("remote") || protocol.contains(QLatin1String("search"))); if (forceRootExpansionLevel) { m_rootExpansionLevel = ForceRootExpansionLevel; } else { - const QString rootDir = rootUrl.directory(KUrl::AppendTrailingSlash); + const QString rootDir = rootUrl.path(KUrl::AddTrailingSlash); m_rootExpansionLevel = rootDir.count('/'); - if (m_rootExpansionLevel == 1) { - // Special case: The root is already reached and no parent is available - --m_rootExpansionLevel; - } } } @@ -1168,7 +1239,7 @@ QHash KFileItemModel::retrieveData(const KFileItem& item) data.insert("expansionLevel", -1); } else { const QString dir = item.url().directory(KUrl::AppendTrailingSlash); - const int level = dir.count('/') - m_rootExpansionLevel - 1; + const int level = dir.count('/') - m_rootExpansionLevel; data.insert("expansionLevel", level); } } @@ -1222,62 +1293,69 @@ int KFileItemModel::sortRoleCompare(const ItemData* a, const ItemData* b) const case NameRole: // The name role is handled as default fallback after the switch break; - - case DateRole: { - const KDateTime dateTimeA = itemA.time(KFileItem::ModificationTime); - const KDateTime dateTimeB = itemB.time(KFileItem::ModificationTime); - if (dateTimeA < dateTimeB) { - result = -1; - } else if (dateTimeA > dateTimeB) { - result = +1; - } - break; - } - + case SizeRole: { if (itemA.isDir()) { - Q_ASSERT(itemB.isDir()); // see "if (m_sortFoldersFirst || m_sortRole == SizeRole)" above + // See "if (m_sortFoldersFirst || m_sortRole == SizeRole)" in KFileItemModel::lessThan(): + Q_ASSERT(itemB.isDir()); const QVariant valueA = a->values.value("size"); const QVariant valueB = b->values.value("size"); - - if (valueA.isNull()) { + if (valueA.isNull() && valueB.isNull()) { + result = 0; + } else if (valueA.isNull()) { result = -1; } else if (valueB.isNull()) { result = +1; } else { - result = valueA.value() - valueB.value(); + result = valueA.toInt() - valueB.toInt(); } } else { - Q_ASSERT(!itemB.isDir()); // see "if (m_sortFoldersFirst || m_sortRole == SizeRole)" above - result = itemA.size() - itemB.size(); + // See "if (m_sortFoldersFirst || m_sortRole == SizeRole)" in KFileItemModel::lessThan(): + Q_ASSERT(!itemB.isDir()); + const KIO::filesize_t sizeA = itemA.size(); + const KIO::filesize_t sizeB = itemB.size(); + if (sizeA > sizeB) { + result = +1; + } else if (sizeA < sizeB) { + result = -1; + } else { + result = 0; + } } break; } - case TypeRole: { - result = QString::compare(a->values.value("type").toString(), - b->values.value("type").toString()); + case DateRole: { + const KDateTime dateTimeA = itemA.time(KFileItem::ModificationTime); + const KDateTime dateTimeB = itemB.time(KFileItem::ModificationTime); + if (dateTimeA < dateTimeB) { + result = -1; + } else if (dateTimeA > dateTimeB) { + result = +1; + } break; } - - case CommentRole: { - result = QString::compare(a->values.value("comment").toString(), - b->values.value("comment").toString()); + + case RatingRole: { + result = a->values.value("rating").toInt() - b->values.value("rating").toInt(); break; } - + + case PermissionsRole: + case OwnerRole: + case GroupRole: + case TypeRole: + case DestinationRole: + case PathRole: + case CommentRole: case TagsRole: { - result = QString::compare(a->values.value("tags").toString(), - b->values.value("tags").toString()); + const QByteArray role = roleByteArray(m_sortRole); + result = QString::compare(a->values.value(role).toString(), + b->values.value(role).toString()); break; } - - case RatingRole: { - result = a->values.value("rating").toInt() - b->values.value("rating").toInt(); - break; - } - + default: break; } @@ -1498,16 +1576,16 @@ int KFileItemModel::expansionLevelsCompare(const ItemData* a, const ItemData* b) // Compare the items of the parents that represent the first // different path after the common path. - const KUrl parentUrlA(pathA.left(index) + subPathA); - const KUrl parentUrlB(pathB.left(index) + subPathB); + const QString parentPathA = pathA.left(index) + subPathA; + const QString parentPathB = pathB.left(index) + subPathB; const ItemData* parentA = a; - while (parentA && parentA->item.url() != parentUrlA) { + while (parentA && parentA->item.url().path() != parentPathA) { parentA = parentA->parent; } const ItemData* parentB = b; - while (parentB && parentB->item.url() != parentUrlB) { + while (parentB && parentB->item.url().path() != parentPathB) { parentB = parentB->parent; } @@ -1812,12 +1890,12 @@ QList > KFileItemModel::ratingRoleGroups() const const int maxIndex = count() - 1; QList > groups; - int groupValue; + int groupValue = -1; for (int i = 0; i <= maxIndex; ++i) { if (isChildItem(i)) { continue; } - const int newGroupValue = m_itemData.at(i)->values.value("rating").toInt(); + const int newGroupValue = m_itemData.at(i)->values.value("rating", 0).toInt(); if (newGroupValue != groupValue) { groupValue = newGroupValue; groups.append(QPair(i, newGroupValue)); @@ -1834,19 +1912,38 @@ QList > KFileItemModel::genericStringRoleGroups(const QByte const int maxIndex = count() - 1; QList > groups; + bool isFirstGroupValue = true; QString groupValue; for (int i = 0; i <= maxIndex; ++i) { if (isChildItem(i)) { continue; } const QString newGroupValue = m_itemData.at(i)->values.value(role).toString(); - if (newGroupValue != groupValue) { + if (newGroupValue != groupValue || isFirstGroupValue) { groupValue = newGroupValue; groups.append(QPair(i, newGroupValue)); + isFirstGroupValue = false; } } return groups; } +KFileItemList KFileItemModel::childItems(const KFileItem& item) const +{ + KFileItemList items; + + int index = m_items.value(item.url(), -1); + if (index >= 0) { + const int parentLevel = m_itemData.at(index)->values.value("expansionLevel").toInt(); + ++index; + while (index < m_itemData.count() && m_itemData.at(index)->values.value("expansionLevel").toInt() > parentLevel) { + items.append(m_itemData.at(index)->item); + ++index; + } + } + + return items; +} + #include "kfileitemmodel.moc"