QHashIterator<QByteArray, QVariant> it(values);
while (it.hasNext()) {
it.next();
- const QByteArray role = it.key();
+ const QByteArray role = sharedValue(it.key());
const QVariant value = it.value();
if (currentValues[role] != value) {
}
QHash<QByteArray, QVariant> values;
- values.insert("isExpanded", expanded);
+ values.insert(sharedValue("isExpanded"), expanded);
if (!setData(index, values)) {
return false;
}
Q_UNUSED(previous);
m_sortRole = typeForRole(current);
-#ifdef KFILEITEMMODEL_DEBUG
if (!m_requestRole[m_sortRole]) {
- kWarning() << "The sort-role has been changed to a role that has not been received yet";
+ QSet<QByteArray> newRoles = m_roles;
+ newRoles << current;
+ setRoles(newRoles);
}
-#endif
resortAllItems();
}
}
}
-void KFileItemModel::insertItems(QList<ItemData*>& items)
+void KFileItemModel::insertItems(QList<ItemData*>& newItems)
{
- if (items.isEmpty()) {
+ if (newItems.isEmpty()) {
return;
}
QElapsedTimer timer;
timer.start();
kDebug() << "===========================================================";
- kDebug() << "Inserting" << items.count() << "items";
+ kDebug() << "Inserting" << newItems.count() << "items";
#endif
m_groups.clear();
- sort(items.begin(), items.end());
+ sort(newItems.begin(), newItems.end());
#ifdef KFILEITEMMODEL_DEBUG
kDebug() << "[TIME] Sorting:" << timer.elapsed();
#endif
KItemRangeList itemRanges;
- int targetIndex = 0;
- int sourceIndex = 0;
- 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 < 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), items.at(sourceIndex))) {
- break;
+ const int existingItemCount = m_itemData.count();
+ const int newItemCount = newItems.count();
+ const int totalItemCount = existingItemCount + newItemCount;
+
+ if (existingItemCount == 0) {
+ // Optimization for the common special case that there are no
+ // items in the model yet. Happens, e.g., when entering a folder.
+ m_itemData = newItems;
+ itemRanges << KItemRange(0, newItemCount);
+ } else {
+ m_itemData.reserve(totalItemCount);
+ for (int i = existingItemCount; i < totalItemCount; ++i) {
+ m_itemData.append(0);
+ }
+
+ // We build the new list m_items in reverse order to minimize
+ // the number of moves and guarantee O(N) complexity.
+ int targetIndex = totalItemCount - 1;
+ int sourceIndexExistingItems = existingItemCount - 1;
+ int sourceIndexNewItems = newItemCount - 1;
+
+ int rangeCount = 0;
+
+ while (sourceIndexNewItems >= 0) {
+ ItemData* newItem = newItems.at(sourceIndexNewItems);
+ if (sourceIndexExistingItems >= 0 && lessThan(newItem, m_itemData.at(sourceIndexExistingItems))) {
+ // Move an existing item to its new position. If any new items
+ // are behind it, push the item range to itemRanges.
+ if (rangeCount > 0) {
+ itemRanges << KItemRange(sourceIndexExistingItems + 1, rangeCount);
+ rangeCount = 0;
+ }
+
+ m_itemData[targetIndex] = m_itemData.at(sourceIndexExistingItems);
+ --sourceIndexExistingItems;
+ } else {
+ // Insert a new item into the list.
+ ++rangeCount;
+ m_itemData[targetIndex] = newItem;
+ --sourceIndexNewItems;
}
- ++targetIndex;
+ --targetIndex;
}
- if (targetIndex - previousTargetIndex > 0 && insertedAtIndex >= 0) {
- itemRanges << KItemRange(insertedAtIndex, insertedCount);
- previouslyInsertedCount += insertedCount;
- insertedAtIndex = targetIndex - previouslyInsertedCount;
- insertedCount = 0;
+ // Push the final item range to itemRanges.
+ if (rangeCount > 0) {
+ itemRanges << KItemRange(sourceIndexExistingItems + 1, rangeCount);
}
- // Insert item at the position targetIndex by transferring
- // 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, items.at(sourceIndex));
- ++insertedCount;
-
- if (insertedAtIndex < 0) {
- insertedAtIndex = targetIndex;
- Q_ASSERT(previouslyInsertedCount == 0);
- }
- ++targetIndex;
- ++sourceIndex;
+ // Note that itemRanges is still sorted in reverse order.
+ std::reverse(itemRanges.begin(), itemRanges.end());
}
- // The indexes of all m_items must be adjusted, not only the index
- // of the new items
- const int itemDataCount = m_itemData.count();
- m_items.reserve(itemDataCount);
- for (int i = 0; i < itemDataCount; ++i) {
+ // The indexes starting from the first inserted item must be adjusted.
+ m_items.reserve(totalItemCount);
+ for (int i = itemRanges.front().index; i < totalItemCount; ++i) {
m_items.insert(m_itemData.at(i)->item.url(), i);
}
- itemRanges << KItemRange(insertedAtIndex, insertedCount);
emit itemsInserted(itemRanges);
#ifdef KFILEITEMMODEL_DEBUG
- kDebug() << "[TIME] Inserting of" << items.count() << "items:" << timer.elapsed();
+ kDebug() << "[TIME] Inserting of" << newItems.count() << "items:" << timer.elapsed();
#endif
}
m_itemData.erase(m_itemData.end() - indexesToRemove.count(), m_itemData.end());
- // Step 3: Adjust indexes in the hash m_items. Note that all indexes
- // might have been changed by the removal of the items.
+ // Step 3: Adjust indexes in the hash m_items, starting from the
+ // index of the first removed item.
const int newItemDataCount = m_itemData.count();
- for (int i = 0; i < newItemDataCount; ++i) {
+ for (int i = itemRanges.front().index; i < newItemDataCount; ++i) {
m_items.insert(m_itemData.at(i)->item.url(), i);
}
// KFileItem::iconName() can be very expensive if the MIME-type is unknown
// and hence will be retrieved asynchronously by KFileItemModelRolesUpdater.
QHash<QByteArray, QVariant> data;
- data.insert("url", item.url());
+ data.insert(sharedValue("url"), item.url());
const bool isDir = item.isDir();
if (m_requestRole[IsDirRole]) {
- data.insert("isDir", isDir);
+ data.insert(sharedValue("isDir"), isDir);
}
if (m_requestRole[IsLinkRole]) {
const bool isLink = item.isLink();
- data.insert("isLink", isLink);
+ data.insert(sharedValue("isLink"), isLink);
}
if (m_requestRole[NameRole]) {
- data.insert("text", item.text());
+ data.insert(sharedValue("text"), item.text());
}
if (m_requestRole[SizeRole]) {
if (isDir) {
- data.insert("size", QVariant());
+ data.insert(sharedValue("size"), QVariant());
} else {
- data.insert("size", item.size());
+ data.insert(sharedValue("size"), item.size());
}
}
// having several thousands of items. Instead the formatting of the
// date-time will be done on-demand by the view when the date will be shown.
const KDateTime dateTime = item.time(KFileItem::ModificationTime);
- data.insert("date", dateTime.dateTime());
+ data.insert(sharedValue("date"), dateTime.dateTime());
}
if (m_requestRole[PermissionsRole]) {
- data.insert("permissions", item.permissionsString());
+ data.insert(sharedValue("permissions"), item.permissionsString());
}
if (m_requestRole[OwnerRole]) {
- data.insert("owner", item.user());
+ data.insert(sharedValue("owner"), item.user());
}
if (m_requestRole[GroupRole]) {
- data.insert("group", item.group());
+ data.insert(sharedValue("group"), item.group());
}
if (m_requestRole[DestinationRole]) {
if (destination.isEmpty()) {
destination = QLatin1String("-");
}
- data.insert("destination", destination);
+ data.insert(sharedValue("destination"), destination);
}
if (m_requestRole[PathRole]) {
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);
+ data.insert(sharedValue("path"), path);
}
if (m_requestRole[IsExpandableRole]) {
- data.insert("isExpandable", item.isDir() && item.url() == item.targetUrl());
+ data.insert(sharedValue("isExpandable"), item.isDir() && item.url() == item.targetUrl());
}
if (m_requestRole[ExpandedParentsCountRole]) {
level = parent->values["expandedParentsCount"].toInt() + 1;
}
- data.insert("expandedParentsCount", level);
+ data.insert(sharedValue("expandedParentsCount"), level);
}
if (item.isMimeTypeKnown()) {
- data.insert("iconName", item.iconName());
+ data.insert(sharedValue("iconName"), item.iconName());
if (m_requestRole[TypeRole]) {
- data.insert("type", item.mimeComment());
+ data.insert(sharedValue("type"), item.mimeComment());
}
}
}
}
+QByteArray KFileItemModel::sharedValue(const QByteArray& value)
+{
+ static QSet<QByteArray> pool;
+ const QSet<QByteArray>::const_iterator it = pool.constFind(value);
+
+ if (it != pool.constEnd()) {
+ return *it;
+ } else {
+ pool.insert(value);
+ return value;
+ }
+}
+
bool KFileItemModel::isConsistent() const
{
if (m_items.count() != m_itemData.count()) {