X-Git-Url: https://cloud.milkyroute.net/gitweb/dolphin.git/blobdiff_plain/305ecd86f9cdfd7d4e3e96fb5b7f3daab72c6ba5..13dfc7fa2b883eaa88d6b4a03f17690d941008f8:/src/kitemviews/kfileitemmodel.cpp diff --git a/src/kitemviews/kfileitemmodel.cpp b/src/kitemviews/kfileitemmodel.cpp index 7644ae5ef..f8302cfab 100644 --- a/src/kitemviews/kfileitemmodel.cpp +++ b/src/kitemviews/kfileitemmodel.cpp @@ -19,25 +19,28 @@ #include "kfileitemmodel.h" -#include #include -#include "kfileitemmodelsortalgorithm_p.h" #include #include #include #include +#include "private/kfileitemmodelsortalgorithm.h" +#include "private/kfileitemmodeldirlister.h" + +#include #include #include // #define KFILEITEMMODEL_DEBUG -KFileItemModel::KFileItemModel(KDirLister* dirLister, QObject* parent) : - KItemModelBase("name", parent), - m_dirLister(dirLister), +KFileItemModel::KFileItemModel(QObject* parent) : + KItemModelBase("text", parent), + m_dirLister(0), m_naturalSorting(KGlobalSettings::naturalSorting()), - m_sortFoldersFirst(true), + m_sortDirsFirst(true), m_sortRole(NameRole), + m_sortingProgressPercent(-1), m_roles(), m_caseSensitivity(Qt::CaseInsensitive), m_itemData(), @@ -50,25 +53,35 @@ KFileItemModel::KFileItemModel(KDirLister* dirLister, QObject* parent) : m_pendingItemsToInsert(), m_groups(), m_expandedParentsCountRoot(UninitializedExpandedParentsCountRoot), - m_expandedUrls(), + m_expandedDirs(), m_urlsToExpand() { + m_dirLister = new KFileItemModelDirLister(this); + m_dirLister->setAutoUpdate(true); + m_dirLister->setDelayedMimeTypes(true); + m_dirLister->setMainWindow(qApp->activeWindow()); + + connect(m_dirLister, SIGNAL(started(KUrl)), this, SIGNAL(directoryLoadingStarted())); + connect(m_dirLister, SIGNAL(canceled()), this, SLOT(slotCanceled())); + connect(m_dirLister, SIGNAL(completed(KUrl)), this, SLOT(slotCompleted())); + connect(m_dirLister, SIGNAL(newItems(KFileItemList)), this, SLOT(slotNewItems(KFileItemList))); + connect(m_dirLister, SIGNAL(itemsDeleted(KFileItemList)), this, SLOT(slotItemsDeleted(KFileItemList))); + connect(m_dirLister, SIGNAL(refreshItems(QList >)), this, SLOT(slotRefreshItems(QList >))); + connect(m_dirLister, SIGNAL(clear()), this, SLOT(slotClear())); + connect(m_dirLister, SIGNAL(clear(KUrl)), this, SLOT(slotClear(KUrl))); + connect(m_dirLister, SIGNAL(infoMessage(QString)), this, SIGNAL(infoMessage(QString))); + connect(m_dirLister, SIGNAL(errorMessage(QString)), this, SIGNAL(errorMessage(QString))); + connect(m_dirLister, SIGNAL(redirection(KUrl,KUrl)), this, SIGNAL(directoryRedirection(KUrl,KUrl))); + connect(m_dirLister, SIGNAL(urlIsFileError(KUrl)), this, SIGNAL(urlIsFileError(KUrl))); + // Apply default roles that should be determined resetRoles(); m_requestRole[NameRole] = true; m_requestRole[IsDirRole] = true; - m_roles.insert("name"); + m_requestRole[IsLinkRole] = true; + m_roles.insert("text"); m_roles.insert("isDir"); - - Q_ASSERT(dirLister); - - connect(dirLister, SIGNAL(canceled()), this, SLOT(slotCanceled())); - 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 >))); - connect(dirLister, SIGNAL(clear()), this, SLOT(slotClear())); - connect(dirLister, SIGNAL(clear(KUrl)), this, SLOT(slotClear(KUrl))); + m_roles.insert("isLink"); // For slow KIO-slaves like used for searching it makes sense to show results periodically even // before the completed() or canceled() signal has been emitted. @@ -95,6 +108,26 @@ KFileItemModel::~KFileItemModel() m_itemData.clear(); } +void KFileItemModel::loadDirectory(const KUrl& url) +{ + m_dirLister->openUrl(url); +} + +void KFileItemModel::refreshDirectory(const KUrl& url) +{ + m_dirLister->openUrl(url, KDirLister::Reload); +} + +KUrl KFileItemModel::directory() const +{ + return m_dirLister->url(); +} + +void KFileItemModel::cancelDirectoryLoading() +{ + m_dirLister->stop(); +} + int KFileItemModel::count() const { return m_itemData.count(); @@ -135,6 +168,12 @@ bool KFileItemModel::setData(int index, const QHash& value } m_itemData[index]->values = currentValues; + if (changedRoles.contains("text")) { + KUrl url = m_itemData[index]->item.url(); + url.setFileName(currentValues["text"].toString()); + m_itemData[index]->item.setUrl(url); + } + emit itemsChanged(KItemRangeList() << KItemRange(index, 1), changedRoles); if (changedRoles.contains(sortRole())) { @@ -144,49 +183,41 @@ bool KFileItemModel::setData(int index, const QHash& value return true; } -void KFileItemModel::setSortFoldersFirst(bool foldersFirst) +void KFileItemModel::setSortDirectoriesFirst(bool dirsFirst) { - if (foldersFirst != m_sortFoldersFirst) { - m_sortFoldersFirst = foldersFirst; + if (dirsFirst != m_sortDirsFirst) { + m_sortDirsFirst = dirsFirst; resortAllItems(); } } -bool KFileItemModel::sortFoldersFirst() const +bool KFileItemModel::sortDirectoriesFirst() const { - return m_sortFoldersFirst; + return m_sortDirsFirst; } void KFileItemModel::setShowHiddenFiles(bool show) { - KDirLister* dirLister = m_dirLister.data(); - if (dirLister) { - dirLister->setShowingDotFiles(show); - dirLister->emitChanges(); - if (show) { - slotCompleted(); - } + m_dirLister->setShowingDotFiles(show); + m_dirLister->emitChanges(); + if (show) { + slotCompleted(); } } bool KFileItemModel::showHiddenFiles() const { - const KDirLister* dirLister = m_dirLister.data(); - return dirLister ? dirLister->showingDotFiles() : false; + return m_dirLister->showingDotFiles(); } -void KFileItemModel::setShowFoldersOnly(bool enabled) +void KFileItemModel::setShowDirectoriesOnly(bool enabled) { - KDirLister* dirLister = m_dirLister.data(); - if (dirLister) { - dirLister->setDirOnlyMode(enabled); - } + m_dirLister->setDirOnlyMode(enabled); } -bool KFileItemModel::showFoldersOnly() const +bool KFileItemModel::showDirectoriesOnly() const { - KDirLister* dirLister = m_dirLister.data(); - return dirLister ? dirLister->dirOnlyMode() : false; + return m_dirLister->dirOnlyMode(); } QMimeData* KFileItemModel::createMimeData(const QSet& indexes) const @@ -231,12 +262,12 @@ int KFileItemModel::indexForKeyboardSearch(const QString& text, int startFromInd { startFromIndex = qMax(0, startFromIndex); for (int i = startFromIndex; i < count(); ++i) { - if (data(i)["name"].toString().startsWith(text, Qt::CaseInsensitive)) { + if (data(i)["text"].toString().startsWith(text, Qt::CaseInsensitive)) { return i; } } for (int i = 0; i < startFromIndex; ++i) { - if (data(i)["name"].toString().startsWith(text, Qt::CaseInsensitive)) { + if (data(i)["text"].toString().startsWith(text, Qt::CaseInsensitive)) { return i; } } @@ -323,11 +354,7 @@ int KFileItemModel::index(const KUrl& url) const KFileItem KFileItemModel::rootItem() const { - const KDirLister* dirLister = m_dirLister.data(); - if (dirLister) { - return dirLister->rootItem(); - } - return KFileItem(); + return m_dirLister->rootItem(); } void KFileItemModel::clear() @@ -390,21 +417,14 @@ bool KFileItemModel::setExpanded(int index, bool expanded) return false; } - KDirLister* dirLister = m_dirLister.data(); const KUrl url = m_itemData.at(index)->item.url(); if (expanded) { - m_expandedUrls.insert(url); - - if (dirLister) { - dirLister->openUrl(url, KDirLister::Keep); - return true; - } + m_expandedDirs.insert(url); + m_dirLister->openUrl(url, KDirLister::Keep); } else { - m_expandedUrls.remove(url); + m_expandedDirs.remove(url); + m_dirLister->stop(url); - if (dirLister) { - dirLister->stop(url); - } KFileItemList itemsToRemove; const int expandedParentsCount = data(index)["expandedParentsCount"].toInt(); @@ -414,10 +434,9 @@ bool KFileItemModel::setExpanded(int index, bool expanded) ++index; } removeItems(itemsToRemove); - return true; } - return false; + return true; } bool KFileItemModel::isExpanded(int index) const @@ -447,30 +466,25 @@ int KFileItemModel::expandedParentsCount(int index) const return 0; } -QSet KFileItemModel::expandedUrls() const +QSet KFileItemModel::expandedDirectories() const { - return m_expandedUrls; + return m_expandedDirs; } -void KFileItemModel::restoreExpandedUrls(const QSet& urls) +void KFileItemModel::restoreExpandedDirectories(const QSet& urls) { m_urlsToExpand = urls; } -void KFileItemModel::expandParentItems(const KUrl& url) +void KFileItemModel::expandParentDirectories(const KUrl& url) { - const KDirLister* dirLister = m_dirLister.data(); - if (!dirLister) { - return; - } - - const int pos = dirLister->url().path().length(); + const int pos = m_dirLister->url().path().length(); // 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. - KUrl urlToExpand = dirLister->url(); + KUrl urlToExpand = m_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)); @@ -523,7 +537,7 @@ void KFileItemModel::setNameFilter(const QString& nameFilter) const KFileItem item = it.next(); if (m_filter.matches(item)) { newVisibleItems.append(item); - m_filteredItems.remove(item); + it.remove(); } } @@ -648,7 +662,7 @@ void KFileItemModel::slotCompleted() // 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_urlsToExpand) { + foreach (const KUrl& url, m_urlsToExpand) { const int index = m_items.value(url, -1); if (index >= 0) { m_urlsToExpand.remove(url); @@ -665,7 +679,7 @@ void KFileItemModel::slotCompleted() m_urlsToExpand.clear(); } - emit loadingCompleted(); + emit directoryLoadingCompleted(); } void KFileItemModel::slotCanceled() @@ -850,7 +864,7 @@ void KFileItemModel::slotClear() emit itemsRemoved(KItemRangeList() << KItemRange(0, removedCount)); } - m_expandedUrls.clear(); + m_expandedDirs.clear(); } void KFileItemModel::slotClear(const KUrl& url) @@ -878,6 +892,13 @@ void KFileItemModel::insertItems(const KFileItemList& items) return; } + if (m_sortRole == TypeRole) { + // Try to resolve the MIME-types synchronously to prevent a reordering of + // the items when sorting by type (per default MIME-types are resolved + // asynchronously by KFileItemModelRolesUpdater). + determineMimeTypes(items, 200); + } + #ifdef KFILEITEMMODEL_DEBUG QElapsedTimer timer; timer.start(); @@ -918,7 +939,7 @@ void KFileItemModel::insertItems(const KFileItemList& items) insertedCount = 0; } - // Insert item at the position targetIndex by transfering + // Insert item at the position targetIndex by transferring // the ownership of the item-data from sortedItems to m_itemData. // m_items will be inserted after the loop (see comment below) m_itemData.insert(targetIndex, sortedItems.at(sourceIndex)); @@ -1079,7 +1100,7 @@ void KFileItemModel::removeExpandedItems() removeItems(expandedItems); m_expandedParentsCountRoot = UninitializedExpandedParentsCountRoot; - m_expandedUrls.clear(); + m_expandedDirs.clear(); } void KFileItemModel::resetRoles() @@ -1104,6 +1125,7 @@ KFileItemModel::RoleType KFileItemModel::typeForRole(const QByteArray& role) con // Insert internal roles (take care to synchronize the implementation // with KFileItemModel::roleForType() in case if a change is done). roles.insert("isDir", IsDirRole); + roles.insert("isLink", IsLinkRole); roles.insert("isExpanded", IsExpandedRole); roles.insert("isExpandable", IsExpandableRole); roles.insert("expandedParentsCount", ExpandedParentsCountRole); @@ -1129,6 +1151,7 @@ QByteArray KFileItemModel::roleForType(RoleType roleType) const // Insert internal roles (take care to synchronize the implementation // with KFileItemModel::typeForRole() in case if a change is done). roles.insert(IsDirRole, "isDir"); + roles.insert(IsLinkRole, "isLink"); roles.insert(IsExpandedRole, "isExpanded"); roles.insert(IsExpandableRole, "isExpandable"); roles.insert(ExpandedParentsCountRole, "expandedParentsCount"); @@ -1152,8 +1175,13 @@ QHash KFileItemModel::retrieveData(const KFileItem& item) data.insert("isDir", isDir); } + if (m_requestRole[IsLinkRole]) { + const bool isLink = item.isLink(); + data.insert("isLink", isLink); + } + if (m_requestRole[NameRole]) { - data.insert("name", item.text()); + data.insert("text", item.text()); } if (m_requestRole[SizeRole]) { @@ -1197,7 +1225,17 @@ QHash KFileItemModel::retrieveData(const KFileItem& item) if (item.url().protocol() == QLatin1String("trash")) { path = item.entry().stringValue(KIO::UDSEntry::UDS_EXTRA); } else { + // For performance reasons cache the home-path in a static QString + // (see QDir::homePath() for more details) + static QString homePath; + if (homePath.isEmpty()) { + homePath = QDir::homePath(); + } + path = item.localPath(); + if (path.startsWith(homePath)) { + path.replace(0, homePath.length(), QLatin1Char('~')); + } } const int index = path.lastIndexOf(item.text()); @@ -1214,8 +1252,8 @@ QHash KFileItemModel::retrieveData(const KFileItem& item) } if (m_requestRole[ExpandedParentsCountRole]) { - if (m_expandedParentsCountRoot == UninitializedExpandedParentsCountRoot && m_dirLister.data()) { - const KUrl rootUrl = m_dirLister.data()->url(); + if (m_expandedParentsCountRoot == UninitializedExpandedParentsCountRoot) { + const KUrl rootUrl = m_dirLister->url(); const QString protocol = rootUrl.protocol(); const bool forceExpandedParentsCountRoot = (protocol == QLatin1String("trash") || protocol == QLatin1String("nepomuk") || @@ -1261,7 +1299,7 @@ bool KFileItemModel::lessThan(const ItemData* a, const ItemData* b) const } } - if (m_sortFoldersFirst || m_sortRole == SizeRole) { + if (m_sortDirsFirst || m_sortRole == SizeRole) { const bool isDirA = a->item.isDir(); const bool isDirB = b->item.isDir(); if (isDirA && !isDirB) { @@ -1345,22 +1383,13 @@ int KFileItemModel::sortRoleCompare(const ItemData* a, const ItemData* b) const break; } - case PermissionsRole: - case OwnerRole: - case GroupRole: - case TypeRole: - case DestinationRole: - case PathRole: - case CommentRole: - case TagsRole: { + default: { const QByteArray role = roleForType(m_sortRole); result = QString::compare(a->values.value(role).toString(), b->values.value(role).toString()); break; } - default: - break; } if (result != 0) { @@ -1449,7 +1478,7 @@ int KFileItemModel::expandedParentsCountCompare(const ItemData* a, const ItemDat bool isDirB = true; const QString subPathB = subPath(b->item, pathB, index, &isDirB); - if (m_sortFoldersFirst || m_sortRole == SizeRole) { + if (m_sortDirsFirst || m_sortRole == SizeRole) { if (isDirA && !isDirB) { return (sortOrder() == Qt::AscendingOrder) ? -1 : +1; } else if (!isDirA && isDirB) { @@ -1493,8 +1522,7 @@ QString KFileItemModel::subPath(const KFileItem& item, bool KFileItemModel::useMaximumUpdateInterval() const { - const KDirLister* dirLister = m_dirLister.data(); - return dirLister && !dirLister->url().isLocalFile(); + return !m_dirLister->url().isLocalFile(); } QList > KFileItemModel::nameRoleGroups() const @@ -1512,7 +1540,7 @@ QList > KFileItemModel::nameRoleGroups() const continue; } - const QString name = m_itemData.at(i)->values.value("name").toString(); + const QString name = m_itemData.at(i)->values.value("text").toString(); // Use the first character of the name as group indication QChar newFirstChar = name.at(0).toUpper(); @@ -1829,12 +1857,40 @@ KFileItemList KFileItemModel::childItems(const KFileItem& item) const return items; } +void KFileItemModel::emitSortProgress(int resolvedCount) +{ + // Be tolerant against a resolvedCount with a wrong range. + // Although there should not be a case where KFileItemModelRolesUpdater + // (= caller) provides a wrong range, it is important to emit + // a useful progress information even if there is an unexpected + // implementation issue. + + const int itemCount = count(); + if (resolvedCount >= itemCount) { + m_sortingProgressPercent = -1; + if (m_resortAllItemsTimer->isActive()) { + m_resortAllItemsTimer->stop(); + resortAllItems(); + } + + emit directorySortingProgress(100); + } else if (itemCount > 0) { + resolvedCount = qBound(0, resolvedCount, itemCount); + + const int progress = resolvedCount * 100 / itemCount; + if (m_sortingProgressPercent != progress) { + m_sortingProgressPercent = progress; + emit directorySortingProgress(progress); + } + } +} + const KFileItemModel::RoleInfoMap* KFileItemModel::rolesInfoMap(int& count) { static const RoleInfoMap rolesInfoMap[] = { // | role | roleType | role translation | group translation | requires Nepomuk | requires indexer { 0, NoRole, 0, 0, 0, 0, false, false }, - { "name", NameRole, I18N_NOOP2_NOSTRIP("@label", "Name"), 0, 0, false, false }, + { "text", NameRole, I18N_NOOP2_NOSTRIP("@label", "Name"), 0, 0, false, false }, { "size", SizeRole, I18N_NOOP2_NOSTRIP("@label", "Size"), 0, 0, false, false }, { "date", DateRole, I18N_NOOP2_NOSTRIP("@label", "Date"), 0, 0, false, false }, { "type", TypeRole, I18N_NOOP2_NOSTRIP("@label", "Type"), 0, 0, false, false }, @@ -1845,10 +1901,10 @@ const KFileItemModel::RoleInfoMap* KFileItemModel::rolesInfoMap(int& count) { "lineCount", LineCountRole, I18N_NOOP2_NOSTRIP("@label", "Line Count"), I18N_NOOP2_NOSTRIP("@label", "Document"), true, true }, { "imageSize", ImageSizeRole, I18N_NOOP2_NOSTRIP("@label", "Image Size"), I18N_NOOP2_NOSTRIP("@label", "Image"), true, true }, { "orientation", OrientationRole, I18N_NOOP2_NOSTRIP("@label", "Orientation"), I18N_NOOP2_NOSTRIP("@label", "Image"), true, true }, - { "artist", ArtistRole, I18N_NOOP2_NOSTRIP("@label", "Artist"), I18N_NOOP2_NOSTRIP("@label", "Music"), true, true }, - { "album", AlbumRole, I18N_NOOP2_NOSTRIP("@label", "Album"), I18N_NOOP2_NOSTRIP("@label", "Music"), true, true }, - { "duration", DurationRole, I18N_NOOP2_NOSTRIP("@label", "Duration"), I18N_NOOP2_NOSTRIP("@label", "Music"), true, true }, - { "track", TrackRole, I18N_NOOP2_NOSTRIP("@label", "Track"), I18N_NOOP2_NOSTRIP("@label", "Music"), true, true }, + { "artist", ArtistRole, I18N_NOOP2_NOSTRIP("@label", "Artist"), I18N_NOOP2_NOSTRIP("@label", "Audio"), true, true }, + { "album", AlbumRole, I18N_NOOP2_NOSTRIP("@label", "Album"), I18N_NOOP2_NOSTRIP("@label", "Audio"), true, true }, + { "duration", DurationRole, I18N_NOOP2_NOSTRIP("@label", "Duration"), I18N_NOOP2_NOSTRIP("@label", "Audio"), true, true }, + { "track", TrackRole, I18N_NOOP2_NOSTRIP("@label", "Track"), I18N_NOOP2_NOSTRIP("@label", "Audio"), true, true }, { "path", PathRole, I18N_NOOP2_NOSTRIP("@label", "Path"), I18N_NOOP2_NOSTRIP("@label", "Other"), false, false }, { "destination", DestinationRole, I18N_NOOP2_NOSTRIP("@label", "Link Destination"), I18N_NOOP2_NOSTRIP("@label", "Other"), false, false }, { "copiedFrom", CopiedFromRole, I18N_NOOP2_NOSTRIP("@label", "Copied From"), I18N_NOOP2_NOSTRIP("@label", "Other"), true, false }, @@ -1861,4 +1917,18 @@ const KFileItemModel::RoleInfoMap* KFileItemModel::rolesInfoMap(int& count) return rolesInfoMap; } +void KFileItemModel::determineMimeTypes(const KFileItemList& items, int timeout) +{ + QElapsedTimer timer; + timer.start(); + foreach (KFileItem item, items) { // krazy:exclude=foreach + item.determineMimeType(); + if (timer.elapsed() > timeout) { + // Don't block the user interface, let the remaining items + // be resolved asynchronously. + return; + } + } +} + #include "kfileitemmodel.moc"