void KFileItemModel::setNameFilter(const QString& nameFilter)
{
if (m_nameFilter != nameFilter) {
- // TODO #1: Assure that expanded items only can get hidden
- // if no child item is visible
-
- // TODO #2: If the user entered a '*' use a regular expression
+ dispatchPendingItemsToInsert();
m_nameFilter = nameFilter;
- const QString filter = nameFilter.toLower();
-
// Check which shown items from m_itemData must get
// hidden and hence moved to m_filteredItems.
KFileItemList newFilteredItems;
foreach (ItemData* itemData, m_itemData) {
- if (!matchesNameFilter(itemData->item, filter)) {
- m_filteredItems.append(itemData->item);
+ if (!matchesNameFilter(itemData->item)) {
newFilteredItems.append(itemData->item);
+ m_filteredItems.insert(itemData->item);
}
}
- if (!newFilteredItems.isEmpty()) {
- slotItemsDeleted(newFilteredItems);
- }
+ removeItems(newFilteredItems);
// Check which hidden items from m_filteredItems should
// get visible again and hence removed from m_filteredItems.
KFileItemList newVisibleItems;
- for (int i = m_filteredItems.count() - 1; i >= 0; --i) {
- const KFileItem item = m_filteredItems.at(i);
- if (matchesNameFilter(item, filter)) {
+ QMutableSetIterator<KFileItem> it(m_filteredItems);
+ while (it.hasNext()) {
+ const KFileItem item = it.next();
+ if (matchesNameFilter(item)) {
newVisibleItems.append(item);
- m_filteredItems.removeAt(i);
+ m_filteredItems.remove(item);
}
}
- if (!newVisibleItems.isEmpty()) {
- slotNewItems(newVisibleItems);
- dispatchPendingItemsToInsert();
- }
+ insertItems(newVisibleItems);
}
}
void KFileItemModel::slotNewItems(const KFileItemList& items)
{
- m_pendingItemsToInsert.append(items);
+ if (m_nameFilter.isEmpty()) {
+ m_pendingItemsToInsert.append(items);
+ } else {
+ // The name-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 (matchesNameFilter(item)) {
+ filteredItems.append(item);
+ } else {
+ m_filteredItems.insert(item);
+ }
+ }
+
+ m_pendingItemsToInsert.append(filteredItems);
+ }
if (useMaximumUpdateInterval() && !m_maximumUpdateIntervalTimer->isActive()) {
// Assure that items get dispatched if no completed() or canceled() signal is
void KFileItemModel::slotItemsDeleted(const KFileItemList& items)
{
- if (!m_pendingItemsToInsert.isEmpty()) {
- insertItems(m_pendingItemsToInsert);
- m_pendingItemsToInsert.clear();
+ dispatchPendingItemsToInsert();
+
+ if (!m_filteredItems.isEmpty()) {
+ foreach (const KFileItem& item, items) {
+ m_filteredItems.remove(item);
+ }
}
+
removeItems(items);
}
kDebug() << "Clearing all items";
#endif
+ m_filteredItems.clear();
m_groups.clear();
m_minimumUpdateIntervalTimer->stop();
// Delete the items
for (int i = indexesToRemove.count() - 1; i >= 0; --i) {
const int indexToRemove = indexesToRemove.at(i);
- delete m_itemData.at(indexToRemove);
+ ItemData* data = m_itemData.at(indexToRemove);
+
+ m_items.remove(data->item.url());
+
+ delete data;
m_itemData.removeAt(indexToRemove);
}
return groups;
}
-bool KFileItemModel::matchesNameFilter(const KFileItem& item, const QString& nameFilter)
+bool KFileItemModel::matchesNameFilter(const KFileItem& item) const
{
+ // TODO #1: A performance improvement would be possible by caching m_nameFilter.toLower().
+ // Before adding yet-another-member it should be checked whether it brings a noticable
+ // improvement at all.
+
+ // TODO #2: If the user entered a '*' use a regular expression
const QString itemText = item.text().toLower();
- return itemText.contains(nameFilter);
+ return itemText.contains(m_nameFilter.toLower());
}
#include "kfileitemmodel.moc"
bool isChildItem(int index) const;
/**
- * @return True if the given item matches with the name filter.
+ * @return True if the given item matches with the current set name filter.
*/
- static bool matchesNameFilter(const KFileItem& item, const QString& nameFilter);
+ bool matchesNameFilter(const KFileItem& item) const;
private:
QWeakPointer<KDirLister> m_dirLister;
QHash<KUrl, int> m_items; // Allows O(1) access for KFileItemModel::index(const KFileItem& item)
QString m_nameFilter;
- KFileItemList m_filteredItems; // Items that got hidden by KFileItemModel::setNameFilter()
+ QSet<KFileItem> m_filteredItems; // Items that got hidden by KFileItemModel::setNameFilter()
bool m_requestRole[RolesCount];
if (currentItem < itemRange.index) {
break;
}
+
if (currentItem >= itemRange.index + itemRange.count) {
currentItem -= itemRange.count;
- } else if (currentItem >= m_model->count()) {
+ }
+
+ if (currentItem >= m_model->count()) {
currentItem = m_model->count() - 1;
}
}
// Calling setCurrentItem would trigger the selectionChanged signal, but we want to
// emit it only once in this function -> change the current item manually and emit currentChanged
m_currentItem = currentItem;
+ Q_ASSERT(m_currentItem < m_model->count());
emit currentChanged(m_currentItem, previousCurrent);
}
void KFileItemModelTest::testRemoveItems()
{
m_testDir->createFile("a.txt");
+ m_testDir->createFile("b.txt");
m_dirLister->openUrl(m_testDir->url());
QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsInserted(KItemRangeList)), DefaultTimeout));
- QCOMPARE(m_model->count(), 1);
+ QCOMPARE(m_model->count(), 2);
+ QVERIFY(isModelConsistent());
m_testDir->removeFile("a.txt");
m_dirLister->updateDirectory(m_testDir->url());
QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsRemoved(KItemRangeList)), DefaultTimeout));
- QCOMPARE(m_model->count(), 0);
+ QCOMPARE(m_model->count(), 1);
+ QVERIFY(isModelConsistent());
}
void KFileItemModelTest::testSetData()
bool KFileItemModelTest::isModelConsistent() const
{
+ if (m_model->m_items.count() != m_model->m_itemData.count()) {
+ return false;
+ }
+
for (int i = 0; i < m_model->count(); ++i) {
const KFileItem item = m_model->fileItem(i);
if (item.isNull()) {
{
public:
DummyModel();
+ void setCount(int count);
virtual int count() const;
virtual QHash<QByteArray, QVariant> data(int index) const;
+
+private:
+ int m_count;
};
DummyModel::DummyModel() :
- KItemModelBase()
+ KItemModelBase(),
+ m_count(100)
+{
+}
+
+void DummyModel::setCount(int count)
{
+ m_count = count;
}
int DummyModel::count() const
{
- return 100;
+ return m_count;
}
QHash<QByteArray, QVariant> DummyModel::data(int index) const
}
-
class KItemListSelectionManagerTest : public QObject
{
Q_OBJECT
void testAnchoredSelection();
void testChangeSelection_data();
void testChangeSelection();
+ void testDeleteCurrentItem_data();
+ void testDeleteCurrentItem();
private:
void verifySelectionChange(QSignalSpy& spy, const QSet<int>& currentSelection, const QSet<int>& previousSelection) const;
KItemListSelectionManager* m_selectionManager;
+ DummyModel* m_model;
};
void KItemListSelectionManagerTest::init()
{
+ m_model = new DummyModel();
m_selectionManager = new KItemListSelectionManager();
- m_selectionManager->setModel(new DummyModel());
+ m_selectionManager->setModel(m_model);
}
void KItemListSelectionManagerTest::cleanup()
{
- delete m_selectionManager->model();
delete m_selectionManager;
m_selectionManager = 0;
+
+ delete m_model;
+ m_model = 0;
}
void KItemListSelectionManagerTest::testConstructor()
verifySelectionChange(spySelectionChanged, QSet<int>(), finalSelection);
}
+void KItemListSelectionManagerTest::testDeleteCurrentItem_data()
+{
+ QTest::addColumn<int>("oldCurrentItemIndex");
+ QTest::addColumn<int>("removeIndex");
+ QTest::addColumn<int>("removeCount");
+ QTest::addColumn<int>("newCurrentItemIndex");
+
+ QTest::newRow("Remove before") << 50 << 0 << 10 << 40;
+ QTest::newRow("Remove after") << 50 << 51 << 10 << 50;
+ QTest::newRow("Remove exactly current item") << 50 << 50 << 1 << 50;
+ QTest::newRow("Remove around current item") << 50 << 45 << 10 << 50;
+ QTest::newRow("Remove all except one item") << 50 << 1 << 99 << 0;
+}
+
+void KItemListSelectionManagerTest::testDeleteCurrentItem()
+{
+ QFETCH(int, oldCurrentItemIndex);
+ QFETCH(int, removeIndex);
+ QFETCH(int, removeCount);
+ QFETCH(int, newCurrentItemIndex);
+
+ m_selectionManager->setCurrentItem(oldCurrentItemIndex);
+
+ const int newCount = m_model->count() - removeCount;
+ m_model->setCount(newCount);
+ m_selectionManager->itemsRemoved(KItemRangeList() << KItemRange(removeIndex, removeCount));
+
+ QCOMPARE(m_selectionManager->currentItem(), newCurrentItemIndex);
+}
+
void KItemListSelectionManagerTest::verifySelectionChange(QSignalSpy& spy,
const QSet<int>& currentSelection,
const QSet<int>& previousSelection) const
const int currentIndex = m_container->controller()->selectionManager()->currentItem();
if (currentIndex != -1) {
KFileItem item = fileItemModel()->fileItem(currentIndex);
+ Q_ASSERT(!item.isNull()); // If the current index is valid a item must exist
KUrl currentItemUrl = item.url();
stream << currentItemUrl;
} else {