changes! :)
* Fixed the "Network browser" and "timeline" issues, by using the
KDirLister's itemsAdded(KUrl,KFileItemList) signal -> Use the
given Url to define the parent-child relationship.
* Changed the name of the slot "slotNewItems" to "slotItemsAdded"
for consistency with the signal.
* Use a QHash<KFileItem, ItemData*> instead of a QSet<KFileItem> to
store the filtered data (needed to keep the O(1) lookup for filtered
KFileItems in slotItemsDeleted + needed to fix bug 311912 "After
erasing a filter, some thumbnails randomly disappear")
* Made the determination of the "expandedParentsCount" slightly
simpler - just adding 1 to the parent's level (Also needed to fix the
"Network browser" and "timeline" issues)
FIXED-IN: 4.11.0
REVIEW: 109180
BUG: 304565
BUG: 311912
BUG: 312890
BUG: 315593
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(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()));
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);
KFileItemModel::~KFileItemModel()
{
qDeleteAll(m_itemData);
+ qDeleteAll(m_filteredItems.values());
}
void KFileItemModel::loadDirectory(const KUrl& url)
}
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) {
// 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!";
}
kWarning() << "TODO: Emitting itemsChanged() with no information what has changed!";
itemsToRemove.append(m_itemData.at(index)->item);
++index;
}
itemsToRemove.append(m_itemData.at(index)->item);
++index;
}
- removeItems(itemsToRemove);
+ removeItems(itemsToRemove, DeleteItemData);
// Only filter non-expanded items as child items may never
// exist without a parent item
if (!itemData->values.value("isExpanded").toBool()) {
// 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.
// 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();
}
emit directoryLoadingCanceled();
}
-void KFileItemModel::slotNewItems(const KFileItemList& items)
+void KFileItemModel::slotItemsAdded(const KUrl& directoryUrl, const KFileItemList& items)
{
Q_ASSERT(!items.isEmpty());
{
Q_ASSERT(!items.isEmpty());
+ KUrl parentUrl = directoryUrl;
+ parentUrl.adjustPath(KUrl::RemoveTrailingSlash);
+
if (m_requestRole[ExpandedParentsCountRole] && m_expandedParentsCountRoot >= 0) {
// 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.
if (m_requestRole[ExpandedParentsCountRole] && m_expandedParentsCountRoot >= 0) {
// 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.
// 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.
// 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.
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()) {
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.
} 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);
- m_filteredItems.insert(item);
+ m_filteredItems.insert(itemData->item, itemData);
-
- m_pendingItemsToInsert.append(filteredItems);
}
if (useMaximumUpdateInterval() && !m_maximumUpdateIntervalTimer->isActive()) {
}
if (useMaximumUpdateInterval() && !m_maximumUpdateIntervalTimer->isActive()) {
if (!m_filteredItems.isEmpty()) {
foreach (const KFileItem& item, itemsToRemove) {
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] && m_expandedParentsCountRoot >= 0) {
}
if (m_requestRole[ExpandedParentsCountRole] && m_expandedParentsCountRoot >= 0) {
// 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.
// 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) {
QSet<KUrl> urlsToRemove;
urlsToRemove.reserve(itemsToRemove.count());
foreach (const KFileItem& item, itemsToRemove) {
urlsToRemove.insert(url);
}
urlsToRemove.insert(url);
}
- QSet<KFileItem>::iterator it = m_filteredItems.begin();
+ QHash<KFileItem, ItemData*>::iterator it = m_filteredItems.begin();
while (it != m_filteredItems.end()) {
while (it != m_filteredItems.end()) {
- const KUrl url = it->url();
+ const KUrl url = it.key().url();
KUrl parentUrl = url.upUrl();
parentUrl.adjustPath(KUrl::RemoveTrailingSlash);
if (urlsToRemove.contains(parentUrl)) {
KUrl parentUrl = url.upUrl();
parentUrl.adjustPath(KUrl::RemoveTrailingSlash);
if (urlsToRemove.contains(parentUrl)) {
it = m_filteredItems.erase(it);
} else {
++it;
it = m_filteredItems.erase(it);
} else {
++it;
- removeItems(itemsToRemove);
+ removeItems(itemsToRemove, DeleteItemData);
}
void KFileItemModel::slotRefreshItems(const QList<QPair<KFileItem, KFileItem> >& items)
}
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.
// 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());
while (it.hasNext()) {
it.next();
m_itemData[index]->values.insert(it.key(), it.value());
kDebug() << "Clearing all items";
#endif
kDebug() << "Clearing all items";
#endif
+ qDeleteAll(m_filteredItems.values());
m_filteredItems.clear();
m_groups.clear();
m_filteredItems.clear();
m_groups.clear();
-void KFileItemModel::insertItems(const KFileItemList& items)
+void KFileItemModel::insertItems(QList<ItemData*>& items)
{
if (items.isEmpty()) {
return;
{
if (items.isEmpty()) {
return;
- QList<ItemData*> sortedItems = createItemDataList(items);
- sort(sortedItems.begin(), sortedItems.end());
+ sort(items.begin(), items.end());
#ifdef KFILEITEMMODEL_DEBUG
kDebug() << "[TIME] Sorting:" << timer.elapsed();
#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
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()) {
// 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))) {
}
// Insert item at the position targetIndex by transferring
}
// 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_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) {
++insertedCount;
if (insertedAtIndex < 0) {
-void KFileItemModel::removeItems(const KFileItemList& items)
+void KFileItemModel::removeItems(const KFileItemList& items, RemoveItemsBehavior behavior)
{
#ifdef KFILEITEMMODEL_DEBUG
kDebug() << "Removing " << items.count() << "items";
{
#ifdef KFILEITEMMODEL_DEBUG
kDebug() << "Removing " << items.count() << "items";
QHash<KUrl, int>::iterator it = m_items.find(url);
m_items.erase(it);
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_itemData[index] = 0;
}
}
emit itemsRemoved(itemRanges);
}
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;
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);
}
itemDataList.append(itemData);
}
// The m_expandedParentsCountRoot may not get reset before all items with
// a bigger count have been removed.
// 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();
m_expandedParentsCountRoot = UninitializedExpandedParentsCountRoot;
m_expandedDirs.clear();
return roles.value(roleType);
}
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
{
// 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
data.insert("expandedParentsCount", -1);
} else {
const QString dir = item.url().directory(KUrl::AppendTrailingSlash);
data.insert("expandedParentsCount", -1);
} else {
const QString dir = item.url().directory(KUrl::AppendTrailingSlash);
- const int level = dir.count('/') - m_expandedParentsCountRoot;
+ int level = 0;
+ if (parent) {
+ level = parent->values["expandedParentsCount"].toInt() + 1;
+ }
data.insert("expandedParentsCount", level);
}
}
data.insert("expandedParentsCount", level);
}
}
-void KFileItemModel::determineMimeTypes(const KFileItemList& items, int timeout)
+void KFileItemModel::determineMimeTypes(const QList<ItemData*>& items, int timeout)
{
QElapsedTimer timer;
timer.start();
{
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.
if (timer.elapsed() > timeout) {
// Don't block the user interface, let the remaining items
// be resolved asynchronously.
void slotCompleted();
void slotCanceled();
void slotCompleted();
void slotCanceled();
- void slotNewItems(const KFileItemList& items);
+ void slotItemsAdded(const KUrl& directoryUrl, const KFileItemList& items);
void slotItemsDeleted(const KFileItemList& items);
void slotRefreshItems(const QList<QPair<KFileItem, KFileItem> >& items);
void slotClear();
void slotItemsDeleted(const KFileItemList& items);
void slotRefreshItems(const QList<QPair<KFileItem, KFileItem> >& items);
void slotClear();
- void insertItems(const KFileItemList& items);
- void removeItems(const KFileItemList& items);
+ enum RemoveItemsBehavior {
+ KeepItemData,
+ DeleteItemData
+ };
+
+ void insertItems(QList<ItemData*>& items);
+ void removeItems(const KFileItemList& items, RemoveItemsBehavior behavior);
/**
* Helper method for insertItems() and removeItems(): Creates
/**
* Helper method for insertItems() and removeItems(): Creates
* Note that the ItemData instances are created dynamically and
* must be deleted by the caller.
*/
* Note that the ItemData instances are created dynamically and
* must be deleted by the caller.
*/
- QList<ItemData*> createItemDataList(const KFileItemList& items) const;
+ QList<ItemData*> createItemDataList(const KUrl& parentUrl, const KFileItemList& items) const;
void removeExpandedItems();
void removeExpandedItems();
*/
QByteArray roleForType(RoleType roleType) const;
*/
QByteArray roleForType(RoleType roleType) const;
- QHash<QByteArray, QVariant> retrieveData(const KFileItem& item) const;
+ QHash<QByteArray, QVariant> retrieveData(const KFileItem& item, const ItemData* parent) const;
/**
* @return True if the item-data \a a should be ordered before the item-data
/**
* @return True if the item-data \a a should be ordered before the item-data
* Determines the MIME-types of all items that can be done within
* the given timeout.
*/
* Determines the MIME-types of all items that can be done within
* the given timeout.
*/
- static void determineMimeTypes(const KFileItemList& items, int timeout);
+ static void determineMimeTypes(const QList<ItemData*>& items, int timeout);
/**
* Checks if the model's internal data structures are consistent.
/**
* Checks if the model's internal data structures are consistent.
QHash<KUrl, int> m_items; // Allows O(1) access for KFileItemModel::index(const KFileItem& item)
KFileItemModelFilter m_filter;
QHash<KUrl, int> m_items; // Allows O(1) access for KFileItemModel::index(const KFileItem& item)
KFileItemModelFilter m_filter;
- QSet<KFileItem> m_filteredItems; // Items that got hidden by KFileItemModel::setNameFilter()
+ QHash<KFileItem, ItemData*> m_filteredItems; // Items that got hidden by KFileItemModel::setNameFilter()
bool m_requestRole[RolesCount];
QTimer* m_maximumUpdateIntervalTimer;
QTimer* m_resortAllItemsTimer;
bool m_requestRole[RolesCount];
QTimer* m_maximumUpdateIntervalTimer;
QTimer* m_resortAllItemsTimer;
- KFileItemList m_pendingItemsToInsert;
+ QList<ItemData*> m_pendingItemsToInsert;
// Cache for KFileItemModel::groups()
mutable QList<QPair<int, QVariant> > m_groups;
// Cache for KFileItemModel::groups()
mutable QList<QPair<int, QVariant> > m_groups;
QBENCHMARK {
model.slotClear();
QBENCHMARK {
model.slotClear();
- model.slotNewItems(initialItems);
+ model.slotItemsAdded(model.directory(), initialItems);
model.slotCompleted();
QCOMPARE(model.count(), initialItems.count());
if (!newItems.isEmpty()) {
model.slotCompleted();
QCOMPARE(model.count(), initialItems.count());
if (!newItems.isEmpty()) {
- model.slotNewItems(newItems);
+ model.slotItemsAdded(model.directory(), newItems);
model.slotCompleted();
}
QCOMPARE(model.count(), initialItems.count() + newItems.count());
if (!removedItems.isEmpty()) {
model.slotCompleted();
}
QCOMPARE(model.count(), initialItems.count() + newItems.count());
if (!removedItems.isEmpty()) {
- model.removeItems(removedItems);
+ model.removeItems(removedItems, KFileItemModel::DeleteItemData);
}
QCOMPARE(model.count(), initialItems.count() + newItems.count() - removedItems.count());
}
}
QCOMPARE(model.count(), initialItems.count() + newItems.count() - removedItems.count());
}
void KFileItemModelBenchmark::insertManyChildItems()
{
void KFileItemModelBenchmark::insertManyChildItems()
{
+ // TODO: this function needs to be adjusted to the changes in KFileItemModel
+ // (replacement of slotNewItems(KFileItemList) by slotItemsAdded(KUrl,KFileItemList))
+ // Currently, this function tries to insert child items of multiple
+ // directories by invoking the slot only once.
+#if 0
qInstallMsgHandler(myMessageOutput);
KFileItemModel model;
qInstallMsgHandler(myMessageOutput);
KFileItemModel model;
QCOMPARE(model.count(), numberOfFolders);
QVERIFY(model.isConsistent());
}
QCOMPARE(model.count(), numberOfFolders);
QVERIFY(model.isConsistent());
}
}
KFileItemList KFileItemModelBenchmark::createFileItemList(const QStringList& fileNames, const QString& prefix)
}
KFileItemList KFileItemModelBenchmark::createFileItemList(const QStringList& fileNames, const QString& prefix)
KFileItemList items;
items << KFileItem(emptyUrl, QString(), KFileItem::Unknown) << KFileItem(url, QString(), KFileItem::Unknown);
KFileItemList items;
items << KFileItem(emptyUrl, QString(), KFileItem::Unknown) << KFileItem(url, QString(), KFileItem::Unknown);
- m_model->slotNewItems(items);
+ m_model->slotItemsAdded(emptyUrl, items);
m_model->slotCompleted();
}
m_model->slotCompleted();
}