m_resortAllItemsTimer(0),
m_pendingItemsToInsert(),
m_groups(),
- m_expandedParentsCountRoot(UninitializedExpandedParentsCountRoot),
m_expandedDirs(),
m_urlsToExpand()
{
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(itemsAdded(KUrl,KFileItemList)), this, SLOT(slotItemsAdded(KUrl,KFileItemList)));
connect(m_dirLister, SIGNAL(itemsDeleted(KFileItemList)), this, SLOT(slotItemsDeleted(KFileItemList)));
connect(m_dirLister, SIGNAL(refreshItems(QList<QPair<KFileItem,KFileItem> >)), this, SLOT(slotRefreshItems(QList<QPair<KFileItem,KFileItem> >)));
connect(m_dirLister, SIGNAL(clear()), this, SLOT(slotClear()));
KFileItemModel::~KFileItemModel()
{
qDeleteAll(m_itemData);
- m_itemData.clear();
+ qDeleteAll(m_filteredItems.values());
}
void KFileItemModel::loadDirectory(const KUrl& url)
// Update m_data with the changed requested roles
const int maxIndex = count() - 1;
for (int i = 0; i <= maxIndex; ++i) {
- m_itemData[i]->values = retrieveData(m_itemData.at(i)->item);
+ m_itemData[i]->values = retrieveData(m_itemData.at(i)->item, m_itemData.at(i)->parent);
}
kWarning() << "TODO: Emitting itemsChanged() with no information what has changed!";
itemsToRemove.append(m_itemData.at(index)->item);
++index;
}
- removeItems(itemsToRemove);
+ removeItems(itemsToRemove, DeleteItemData);
}
return true;
// Only filter non-expanded items as child items may never
// exist without a parent item
if (!itemData->values.value("isExpanded").toBool()) {
- if (!m_filter.matches(itemData->item)) {
- newFilteredItems.append(itemData->item);
- m_filteredItems.insert(itemData->item);
+ const KFileItem item = itemData->item;
+ if (!m_filter.matches(item)) {
+ newFilteredItems.append(item);
+ m_filteredItems.insert(item, itemData);
}
}
}
- removeItems(newFilteredItems);
+ removeItems(newFilteredItems, KeepItemData);
// Check which hidden items from m_filteredItems should
// get visible again and hence removed from m_filteredItems.
- KFileItemList newVisibleItems;
+ QList<ItemData*> newVisibleItems;
- QMutableSetIterator<KFileItem> it(m_filteredItems);
- while (it.hasNext()) {
- const KFileItem item = it.next();
- if (m_filter.matches(item)) {
- newVisibleItems.append(item);
- it.remove();
+ QHash<KFileItem, ItemData*>::iterator it = m_filteredItems.begin();
+ while (it != m_filteredItems.end()) {
+ if (m_filter.matches(it.key())) {
+ newVisibleItems.append(it.value());
+ it = m_filteredItems.erase(it);
+ } else {
+ ++it;
}
}
emit directoryLoadingCanceled();
}
-void KFileItemModel::slotNewItems(const KFileItemList& items)
+void KFileItemModel::slotItemsAdded(const KUrl& directoryUrl, const KFileItemList& items)
{
Q_ASSERT(!items.isEmpty());
- if (m_requestRole[ExpandedParentsCountRole] && m_expandedParentsCountRoot >= 0) {
+ KUrl parentUrl = directoryUrl;
+ parentUrl.adjustPath(KUrl::RemoveTrailingSlash);
+
+ if (m_requestRole[ExpandedParentsCountRole]) {
// 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();
// 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.
}
}
+ QList<ItemData*> itemDataList = createItemDataList(parentUrl, items);
+
if (!m_filter.hasSetFilters()) {
- m_pendingItemsToInsert.append(items);
+ m_pendingItemsToInsert.append(itemDataList);
} else {
// The name or type filter is active. Hide filtered items
// before inserting them into the model and remember
// the filtered items in m_filteredItems.
- KFileItemList filteredItems;
- foreach (const KFileItem& item, items) {
- if (m_filter.matches(item)) {
- filteredItems.append(item);
+ foreach (ItemData* itemData, itemDataList) {
+ if (m_filter.matches(itemData->item)) {
+ m_pendingItemsToInsert.append(itemData);
} else {
- m_filteredItems.insert(item);
+ m_filteredItems.insert(itemData->item, itemData);
}
}
-
- m_pendingItemsToInsert.append(filteredItems);
}
if (useMaximumUpdateInterval() && !m_maximumUpdateIntervalTimer->isActive()) {
dispatchPendingItemsToInsert();
KFileItemList itemsToRemove = items;
- if (m_requestRole[ExpandedParentsCountRole] && m_expandedParentsCountRoot >= 0) {
+ if (m_requestRole[ExpandedParentsCountRole]) {
// 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);
+ QHash<KFileItem, ItemData*>::iterator it = m_filteredItems.find(item);
+ if (it != m_filteredItems.end()) {
+ delete it.value();
+ m_filteredItems.erase(it);
+ }
+ }
+
+ if (m_requestRole[ExpandedParentsCountRole]) {
+ // Remove all filtered children of deleted items. First, we put the
+ // deleted URLs into a set to provide fast lookup while iterating
+ // over m_filteredItems and prevent quadratic complexity if there
+ // are N removed items and N filtered items.
+ //
+ // TODO: This does currently *not* work if the parent-child
+ // relationships can not be determined just by using KUrl::upUrl().
+ // This is the case, e.g., when browsing smb:/.
+ QSet<KUrl> urlsToRemove;
+ urlsToRemove.reserve(itemsToRemove.count());
+ foreach (const KFileItem& item, itemsToRemove) {
+ KUrl url = item.url();
+ url.adjustPath(KUrl::RemoveTrailingSlash);
+ urlsToRemove.insert(url);
+ }
+
+ QHash<KFileItem, ItemData*>::iterator it = m_filteredItems.begin();
+ while (it != m_filteredItems.end()) {
+ const KUrl url = it.key().url();
+ KUrl parentUrl = url.upUrl();
+ parentUrl.adjustPath(KUrl::RemoveTrailingSlash);
+
+ if (urlsToRemove.contains(parentUrl)) {
+ delete it.value();
+ it = m_filteredItems.erase(it);
+ } else {
+ ++it;
+ }
+ }
}
}
- removeItems(itemsToRemove);
+ removeItems(itemsToRemove, DeleteItemData);
}
void KFileItemModel::slotRefreshItems(const QList<QPair<KFileItem, KFileItem> >& items)
// Keep old values as long as possible if they could not retrieved synchronously yet.
// The update of the values will be done asynchronously by KFileItemModelRolesUpdater.
- QHashIterator<QByteArray, QVariant> it(retrieveData(newItem));
+ QHashIterator<QByteArray, QVariant> it(retrieveData(newItem, m_itemData.at(index)->parent));
while (it.hasNext()) {
it.next();
m_itemData[index]->values.insert(it.key(), it.value());
kDebug() << "Clearing all items";
#endif
+ qDeleteAll(m_filteredItems.values());
m_filteredItems.clear();
m_groups.clear();
m_resortAllItemsTimer->stop();
m_pendingItemsToInsert.clear();
- m_expandedParentsCountRoot = UninitializedExpandedParentsCountRoot;
-
const int removedCount = m_itemData.count();
if (removedCount > 0) {
qDeleteAll(m_itemData);
}
}
-void KFileItemModel::insertItems(const KFileItemList& items)
+void KFileItemModel::insertItems(QList<ItemData*>& items)
{
if (items.isEmpty()) {
return;
m_groups.clear();
- QList<ItemData*> sortedItems = createItemDataList(items);
- sort(sortedItems.begin(), sortedItems.end());
+ sort(items.begin(), items.end());
#ifdef KFILEITEMMODEL_DEBUG
kDebug() << "[TIME] Sorting:" << timer.elapsed();
int insertedAtIndex = -1; // Index for the current item-range
int insertedCount = 0; // Count for the current item-range
int previouslyInsertedCount = 0; // Sum of previously inserted items for all ranges
- while (sourceIndex < sortedItems.count()) {
+ while (sourceIndex < items.count()) {
// Find target index from m_items to insert the current item
// in a sorted order
const int previousTargetIndex = targetIndex;
while (targetIndex < m_itemData.count()) {
- if (!lessThan(m_itemData.at(targetIndex), sortedItems.at(sourceIndex))) {
+ if (!lessThan(m_itemData.at(targetIndex), items.at(sourceIndex))) {
break;
}
++targetIndex;
}
// Insert item at the position targetIndex by transferring
- // the ownership of the item-data from sortedItems to m_itemData.
+ // the ownership of the item-data from 'items' to m_itemData.
// m_items will be inserted after the loop (see comment below)
- m_itemData.insert(targetIndex, sortedItems.at(sourceIndex));
+ m_itemData.insert(targetIndex, items.at(sourceIndex));
++insertedCount;
if (insertedAtIndex < 0) {
return result;
}
-void KFileItemModel::removeItems(const KFileItemList& items)
+void KFileItemModel::removeItems(const KFileItemList& items, RemoveItemsBehavior behavior)
{
#ifdef KFILEITEMMODEL_DEBUG
kDebug() << "Removing " << items.count() << "items";
QHash<KUrl, int>::iterator it = m_items.find(url);
m_items.erase(it);
- ItemData* data = m_itemData.at(index);
- delete data;
+ if (behavior == DeleteItemData) {
+ delete m_itemData.at(index);
+ }
+
m_itemData[index] = 0;
}
}
m_items.insert(m_itemData.at(i)->item.url(), i);
}
- if (count() <= 0) {
- m_expandedParentsCountRoot = UninitializedExpandedParentsCountRoot;
- }
-
emit itemsRemoved(itemRanges);
}
-QList<KFileItemModel::ItemData*> KFileItemModel::createItemDataList(const KFileItemList& items) const
+QList<KFileItemModel::ItemData*> KFileItemModel::createItemDataList(const KUrl& parentUrl, const KFileItemList& items) const
{
+ const int parentIndex = m_items.value(parentUrl, -1);
+ ItemData* parentItem = parentIndex < 0 ? 0 : m_itemData.at(parentIndex);
+
QList<ItemData*> itemDataList;
itemDataList.reserve(items.count());
foreach (const KFileItem& item, items) {
ItemData* itemData = new ItemData();
itemData->item = item;
- itemData->values = retrieveData(item);
- itemData->parent = 0;
-
- const bool determineParent = m_requestRole[ExpandedParentsCountRole]
- && itemData->values["expandedParentsCount"].toInt() > 0;
- if (determineParent) {
- KUrl parentUrl = item.url().upUrl();
- parentUrl.adjustPath(KUrl::RemoveTrailingSlash);
- const int parentIndex = m_items.value(parentUrl, -1);
- if (parentIndex >= 0) {
- itemData->parent = m_itemData.at(parentIndex);
- } else {
- kWarning() << "Parent item not found for" << item.url();
- }
- }
-
+ itemData->values = retrieveData(item, parentItem);
+ itemData->parent = parentItem;
itemDataList.append(itemData);
}
// The m_expandedParentsCountRoot may not get reset before all items with
// a bigger count have been removed.
- removeItems(expandedItems);
+ removeItems(expandedItems, DeleteItemData);
- m_expandedParentsCountRoot = UninitializedExpandedParentsCountRoot;
m_expandedDirs.clear();
}
return roles.value(roleType);
}
-QHash<QByteArray, QVariant> KFileItemModel::retrieveData(const KFileItem& item) const
+QHash<QByteArray, QVariant> KFileItemModel::retrieveData(const KFileItem& item, const ItemData* parent) const
{
// It is important to insert only roles that are fast to retrieve. E.g.
// KFileItem::iconName() can be very expensive if the MIME-type is unknown
}
if (m_requestRole[ExpandedParentsCountRole]) {
- if (m_expandedParentsCountRoot == UninitializedExpandedParentsCountRoot) {
- const KUrl rootUrl = m_dirLister->url();
- const QString protocol = rootUrl.protocol();
- const bool forceExpandedParentsCountRoot = (protocol == QLatin1String("trash") ||
- protocol == QLatin1String("nepomuk") ||
- protocol == QLatin1String("remote") ||
- protocol.contains(QLatin1String("search")));
- if (forceExpandedParentsCountRoot) {
- m_expandedParentsCountRoot = ForceExpandedParentsCountRoot;
- } else {
- const QString rootDir = rootUrl.path(KUrl::AddTrailingSlash);
- m_expandedParentsCountRoot = rootDir.count('/');
- }
+ int level = 0;
+ if (parent) {
+ level = parent->values["expandedParentsCount"].toInt() + 1;
}
- if (m_expandedParentsCountRoot == ForceExpandedParentsCountRoot) {
- data.insert("expandedParentsCount", -1);
- } else {
- const QString dir = item.url().directory(KUrl::AppendTrailingSlash);
- const int level = dir.count('/') - m_expandedParentsCountRoot;
- data.insert("expandedParentsCount", level);
- }
+ data.insert("expandedParentsCount", level);
}
if (item.isMimeTypeKnown()) {
{
int result = 0;
- if (m_expandedParentsCountRoot >= 0 && a->parent != b->parent) {
+ if (a->parent != b->parent) {
const int expansionLevelA = a->values.value("expandedParentsCount").toInt();
const int expansionLevelB = b->values.value("expandedParentsCount").toInt();
const QDate currentDate = KDateTime::currentLocalDateTime().date();
- int yearForCurrentWeek = 0;
- int currentWeek = currentDate.weekNumber(&yearForCurrentWeek);
- if (yearForCurrentWeek == currentDate.year() + 1) {
- currentWeek = 53;
- }
-
QDate previousModifiedDate;
QString groupValue;
for (int i = 0; i <= maxIndex; ++i) {
const int daysDistance = modifiedDate.daysTo(currentDate);
- int yearForModifiedWeek = 0;
- int modifiedWeek = modifiedDate.weekNumber(&yearForModifiedWeek);
- if (yearForModifiedWeek == modifiedDate.year() + 1) {
- modifiedWeek = 53;
- }
-
QString newGroupValue;
if (currentDate.year() == modifiedDate.year() && currentDate.month() == modifiedDate.month()) {
- if (modifiedWeek > currentWeek) {
- // Usecase: modified date = 2010-01-01, current date = 2010-01-22
- // modified week = 53, current week = 3
- modifiedWeek = 0;
- }
- switch (currentWeek - modifiedWeek) {
+ switch (daysDistance / 7) {
case 0:
switch (daysDistance) {
case 0: newGroupValue = i18nc("@title:group Date", "Today"); break;
}
break;
case 1:
- newGroupValue = i18nc("@title:group Date", "Last Week");
+ newGroupValue = i18nc("@title:group Date", "One Week Ago");
break;
case 2:
newGroupValue = i18nc("@title:group Date", "Two Weeks Ago");
} else if (daysDistance <= 7) {
newGroupValue = modifiedTime.toString(i18nc("@title:group The week day name: %A, %B is full month name in current locale, and %Y is full year number", "%A (%B, %Y)"));
} else if (daysDistance <= 7 * 2) {
- newGroupValue = modifiedTime.toString(i18nc("@title:group Date: %B is full month name in current locale, and %Y is full year number", "Last Week (%B, %Y)"));
+ newGroupValue = modifiedTime.toString(i18nc("@title:group Date: %B is full month name in current locale, and %Y is full year number", "One Week Ago (%B, %Y)"));
} else if (daysDistance <= 7 * 3) {
newGroupValue = modifiedTime.toString(i18nc("@title:group Date: %B is full month name in current locale, and %Y is full year number", "Two Weeks Ago (%B, %Y)"));
} else if (daysDistance <= 7 * 4) {
return rolesInfoMap;
}
-void KFileItemModel::determineMimeTypes(const KFileItemList& items, int timeout)
+void KFileItemModel::determineMimeTypes(const QList<ItemData*>& items, int timeout)
{
QElapsedTimer timer;
timer.start();
- foreach (KFileItem item, items) { // krazy:exclude=foreach
- item.determineMimeType();
+ foreach (const ItemData* itemData, items) { // krazy:exclude=foreach
+ itemData->item.determineMimeType();
if (timer.elapsed() > timeout) {
// Don't block the user interface, let the remaining items
// be resolved asynchronously.