┌   ┐
54
└   ┘

summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/CMakeLists.txt24
-rw-r--r--src/dolphinmainwindow.cpp3
-rw-r--r--src/dolphinviewcontainer.cpp14
-rw-r--r--src/dolphinviewcontainer.h2
-rw-r--r--src/kitemviews/kfileitemmodel.cpp328
-rw-r--r--src/kitemviews/kfileitemmodel.h30
-rw-r--r--src/kitemviews/private/kfileitemmodelsortalgorithm.cpp190
-rw-r--r--src/kitemviews/private/kfileitemmodelsortalgorithm.h180
-rw-r--r--src/main.cpp3
-rw-r--r--src/panels/folders/folderspanel.cpp3
-rw-r--r--src/panels/places/placespanel.cpp15
-rw-r--r--src/tests/CMakeLists.txt10
-rw-r--r--src/tests/kfileitemmodelbenchmark.cpp328
-rw-r--r--src/tests/kfileitemmodeltest.cpp93
-rw-r--r--src/views/dolphinremoteencoding.cpp5
-rw-r--r--src/views/dolphinview.cpp32
-rw-r--r--src/views/dolphinview.h13
-rw-r--r--src/views/draganddrophelper.cpp16
-rw-r--r--src/views/draganddrophelper.h11
-rw-r--r--src/views/versioncontrol/updateitemstatesthread.cpp26
-rw-r--r--src/views/versioncontrol/updateitemstatesthread.h9
-rw-r--r--src/views/versioncontrol/versioncontrolobserver.cpp34
22 files changed, 828 insertions, 541 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 41efa3589..ffb232ce2 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -1,8 +1,17 @@
-macro_optional_find_package(Soprano)
macro_optional_find_package(NepomukCore)
+set_package_properties(NepomukCore PROPERTIES DESCRIPTION "Nepomuk Core libraries"
+ URL "http://www.kde.org"
+ TYPE OPTIONAL
+ PURPOSE "For adding desktop-wide tagging support to dolphin"
+ )
+
macro_optional_find_package(NepomukWidgets)
-macro_log_feature(NepomukCore_FOUND "Nepomuk Core" "Nepomuk Core functionality" "http://www.kde.org" FALSE "" "For fetching additional file metadata in dolphin")
-macro_log_feature(NepomukWidgets_FOUND "Nepomuk Widgets" "Nepomuk Widgets" "http://www.kde.org" FALSE "" "For adding desktop-wide tagging support to dolphin")
+set_package_properties(NepomukWidgets PROPERTIES DESCRIPTION "Nepomuk Widgets"
+ URL "http://www.kde.org"
+ TYPE OPTIONAL
+ PURPOSE "For adding desktop-wide tagging support to dolphin"
+ )
+
if(NepomukCore_FOUND AND NepomukWidgets_FOUND)
set(HAVE_NEPOMUK TRUE)
endif()
@@ -15,7 +24,13 @@ configure_file(config-X11.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-X11.h )
include_directories( ${KACTIVITIES_INCLUDE_DIRS} )
if(HAVE_NEPOMUK)
- # Yes, Soprano includes is what we need here
+ find_package(Soprano 2.7.56)
+ set_package_properties(Soprano PROPERTIES DESCRIPTION "Qt-based RDF storage and parsing solution"
+ URL "http://soprano.sourceforge.net"
+ TYPE REQUIRED
+ PURPOSE "Required for everything (storage and general data management)"
+ )
+
include_directories( ${SOPRANO_INCLUDE_DIR} ${NEPOMUK_CORE_INCLUDE_DIR} ${NEPOMUK_WIDGETS_INCLUDE_DIR} )
endif()
@@ -45,7 +60,6 @@ set(dolphinprivate_LIB_SRCS
kitemviews/kstandarditemmodel.cpp
kitemviews/private/kfileitemclipboard.cpp
kitemviews/private/kfileitemmodeldirlister.cpp
- kitemviews/private/kfileitemmodelsortalgorithm.cpp
kitemviews/private/kfileitemmodelfilter.cpp
kitemviews/private/kitemlistheaderwidget.cpp
kitemviews/private/kitemlistkeyboardsearchmanager.cpp
diff --git a/src/dolphinmainwindow.cpp b/src/dolphinmainwindow.cpp
index 9454c8c42..8ed31dea4 100644
--- a/src/dolphinmainwindow.cpp
+++ b/src/dolphinmainwindow.cpp
@@ -1276,7 +1276,8 @@ void DolphinMainWindow::tabDropEvent(int tab, QDropEvent* event)
const ViewTab& viewTab = m_viewTab[tab];
const DolphinView* view = viewTab.isPrimaryViewActive ? viewTab.primaryView->view()
: viewTab.secondaryView->view();
- const QString error = DragAndDropHelper::dropUrls(view->rootItem(), view->url(), event);
+ QString error;
+ DragAndDropHelper::dropUrls(view->rootItem(), view->url(), event, error);
if (!error.isEmpty()) {
activeViewContainer()->showMessage(error, DolphinViewContainer::Error);
}
diff --git a/src/dolphinviewcontainer.cpp b/src/dolphinviewcontainer.cpp
index 8800a1732..b2c8605d8 100644
--- a/src/dolphinviewcontainer.cpp
+++ b/src/dolphinviewcontainer.cpp
@@ -128,6 +128,8 @@ DolphinViewContainer::DolphinViewContainer(const KUrl& url, QWidget* parent) :
this, SLOT(slotUrlNavigatorLocationChanged(KUrl)));
connect(m_urlNavigator, SIGNAL(historyChanged()),
this, SLOT(slotHistoryChanged()));
+ connect(m_urlNavigator, SIGNAL(returnPressed()),
+ this, SLOT(slotReturnPressed()));
// Initialize status bar
m_statusBar = new DolphinStatusBar(this);
@@ -574,6 +576,8 @@ void DolphinViewContainer::slotUrlNavigatorLocationAboutToBeChanged(const KUrl&
void DolphinViewContainer::slotUrlNavigatorLocationChanged(const KUrl& url)
{
+ slotReturnPressed();
+
if (KProtocolManager::supportsListing(url)) {
setSearchModeEnabled(isSearchUrl(url));
m_view->setUrl(url);
@@ -616,7 +620,8 @@ void DolphinViewContainer::slotUrlNavigatorLocationChanged(const KUrl& url)
void DolphinViewContainer::dropUrls(const KUrl& destination, QDropEvent* event)
{
- const QString error = DragAndDropHelper::dropUrls(KFileItem(), destination, event);
+ QString error;
+ DragAndDropHelper::dropUrls(KFileItem(), destination, event, error);
if (!error.isEmpty()) {
showMessage(error, Error);
}
@@ -657,6 +662,13 @@ void DolphinViewContainer::slotHistoryChanged()
}
}
+void DolphinViewContainer::slotReturnPressed()
+{
+ if (!GeneralSettings::editableUrl()) {
+ m_urlNavigator->setUrlEditable(false);
+ }
+}
+
void DolphinViewContainer::startSearching()
{
const KUrl url = m_searchBox->urlForSearching();
diff --git a/src/dolphinviewcontainer.h b/src/dolphinviewcontainer.h
index e2d1b1875..bc58531a2 100644
--- a/src/dolphinviewcontainer.h
+++ b/src/dolphinviewcontainer.h
@@ -282,6 +282,8 @@ private slots:
void slotHistoryChanged();
+ void slotReturnPressed();
+
/**
* Gets the search URL from the searchbox and starts searching.
*/
diff --git a/src/kitemviews/kfileitemmodel.cpp b/src/kitemviews/kfileitemmodel.cpp
index 6c015db37..60fc27546 100644
--- a/src/kitemviews/kfileitemmodel.cpp
+++ b/src/kitemviews/kfileitemmodel.cpp
@@ -1,5 +1,6 @@
/***************************************************************************
* Copyright (C) 2011 by Peter Penz <[email protected]> *
+ * Copyright (C) 2013 by Frank Reininghaus <[email protected]> *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
@@ -58,7 +59,6 @@ KFileItemModel::KFileItemModel(QObject* parent) :
m_urlsToExpand()
{
m_dirLister = new KFileItemModelDirLister(this);
- m_dirLister->setAutoUpdate(true);
m_dirLister->setDelayedMimeTypes(true);
const QWidget* parentWidget = qobject_cast<QWidget*>(parent);
@@ -658,7 +658,7 @@ void KFileItemModel::resortAllItems()
m_items.clear();
// Resort the items
- KFileItemModelSortAlgorithm::sort(this, m_itemData.begin(), m_itemData.end());
+ sort(m_itemData.begin(), m_itemData.end());
for (int i = 0; i < itemCount; ++i) {
m_items.insert(m_itemData.at(i)->item.url(), i);
}
@@ -941,7 +941,7 @@ void KFileItemModel::insertItems(const KFileItemList& items)
m_groups.clear();
QList<ItemData*> sortedItems = createItemDataList(items);
- KFileItemModelSortAlgorithm::sort(this, sortedItems.begin(), sortedItems.end());
+ sort(sortedItems.begin(), sortedItems.end());
#ifdef KFILEITEMMODEL_DEBUG
kDebug() << "[TIME] Sorting:" << timer.elapsed();
@@ -988,6 +988,7 @@ void KFileItemModel::insertItems(const KFileItemList& items)
// 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) {
m_items.insert(m_itemData.at(i)->item.url(), i);
}
@@ -1000,80 +1001,96 @@ void KFileItemModel::insertItems(const KFileItemList& items)
#endif
}
-void KFileItemModel::removeItems(const KFileItemList& items)
+static KItemRangeList sortedIndexesToKItemRangeList(const QList<int>& sortedNumbers)
{
- if (items.isEmpty()) {
- return;
+ if (sortedNumbers.empty()) {
+ return KItemRangeList();
}
+ KItemRangeList result;
+
+ QList<int>::const_iterator it = sortedNumbers.begin();
+ int index = *it;
+ int count = 1;
+
+ ++it;
+
+ QList<int>::const_iterator end = sortedNumbers.end();
+ while (it != end) {
+ if (*it == index + count) {
+ ++count;
+ } else {
+ result << KItemRange(index, count);
+ index = *it;
+ count = 1;
+ }
+ ++it;
+ }
+
+ result << KItemRange(index, count);
+ return result;
+}
+
+void KFileItemModel::removeItems(const KFileItemList& items)
+{
#ifdef KFILEITEMMODEL_DEBUG
kDebug() << "Removing " << items.count() << "items";
#endif
m_groups.clear();
- QList<ItemData*> sortedItems;
- sortedItems.reserve(items.count());
+ // Step 1: Determine the indexes of the removed items, remove them from
+ // the hash m_items, and free the ItemData.
+ QList<int> indexesToRemove;
+ indexesToRemove.reserve(items.count());
foreach (const KFileItem& item, items) {
- const int index = m_items.value(item.url(), -1);
+ const KUrl url = item.url();
+ const int index = m_items.value(url, -1);
if (index >= 0) {
- sortedItems.append(m_itemData.at(index));
+ indexesToRemove.append(index);
+
+ // Prevent repeated expensive rehashing by using QHash::erase(),
+ // rather than QHash::remove().
+ QHash<KUrl, int>::iterator it = m_items.find(url);
+ m_items.erase(it);
+
+ ItemData* data = m_itemData.at(index);
+ delete data;
+ m_itemData[index] = 0;
}
}
- KFileItemModelSortAlgorithm::sort(this, sortedItems.begin(), sortedItems.end());
- QList<int> indexesToRemove;
- indexesToRemove.reserve(items.count());
+ if (indexesToRemove.isEmpty()) {
+ return;
+ }
- // Calculate the item ranges that will get deleted
- KItemRangeList itemRanges;
- int removedAtIndex = -1;
- int removedCount = 0;
- int targetIndex = 0;
- foreach (const ItemData* itemData, sortedItems) {
- const KFileItem& itemToRemove = itemData->item;
+ std::sort(indexesToRemove.begin(), indexesToRemove.end());
- const int previousTargetIndex = targetIndex;
- while (targetIndex < m_itemData.count()) {
- if (m_itemData.at(targetIndex)->item.url() == itemToRemove.url()) {
- break;
- }
- ++targetIndex;
- }
- if (targetIndex >= m_itemData.count()) {
- kWarning() << "Item that should be deleted has not been found!";
- return;
- }
+ // Step 2: Remove the ItemData pointers from the list m_itemData.
+ const KItemRangeList itemRanges = sortedIndexesToKItemRangeList(indexesToRemove);
+ int target = itemRanges.at(0).index;
+ int source = itemRanges.at(0).index + itemRanges.at(0).count;
+ int nextRange = 1;
- if (targetIndex - previousTargetIndex > 0 && removedAtIndex >= 0) {
- itemRanges << KItemRange(removedAtIndex, removedCount);
- removedAtIndex = targetIndex;
- removedCount = 0;
- }
+ const int oldItemDataCount = m_itemData.count();
+ while (source < oldItemDataCount) {
+ m_itemData[target] = m_itemData[source];
+ ++target;
+ ++source;
- indexesToRemove.append(targetIndex);
- if (removedAtIndex < 0) {
- removedAtIndex = targetIndex;
+ if (nextRange < itemRanges.count() && source == itemRanges.at(nextRange).index) {
+ // Skip the items in the next removed range.
+ source += itemRanges.at(nextRange).count;
+ ++nextRange;
}
- ++removedCount;
- ++targetIndex;
}
- // Delete the items
- for (int i = indexesToRemove.count() - 1; i >= 0; --i) {
- const int indexToRemove = indexesToRemove.at(i);
- ItemData* data = m_itemData.at(indexToRemove);
-
- m_items.remove(data->item.url());
+ m_itemData.erase(m_itemData.end() - indexesToRemove.count(), m_itemData.end());
- delete data;
- m_itemData.removeAt(indexToRemove);
- }
-
- // The indexes of all m_items must be adjusted, not only the index
- // of the removed items
- const int itemDataCount = m_itemData.count();
- for (int i = 0; i < itemDataCount; ++i) {
+ // Step 3: Adjust indexes in the hash m_items. Note that all indexes
+ // might have been changed by the removal of the items.
+ const int newItemDataCount = m_itemData.count();
+ for (int i = 0; i < newItemDataCount; ++i) {
m_items.insert(m_itemData.at(i)->item.url(), i);
}
@@ -1081,7 +1098,6 @@ void KFileItemModel::removeItems(const KFileItemList& items)
m_expandedParentsCountRoot = UninitializedExpandedParentsCountRoot;
}
- itemRanges << KItemRange(removedAtIndex, removedCount);
emit itemsRemoved(itemRanges);
}
@@ -1323,11 +1339,34 @@ bool KFileItemModel::lessThan(const ItemData* a, const ItemData* b) const
{
int result = 0;
- if (m_expandedParentsCountRoot >= 0) {
- result = expandedParentsCountCompare(a, b);
- if (result != 0) {
- // The items have parents with different expansion levels
- return (sortOrder() == Qt::AscendingOrder) ? result < 0 : result > 0;
+ if (m_expandedParentsCountRoot >= 0 && a->parent != b->parent) {
+ const int expansionLevelA = a->values.value("expandedParentsCount").toInt();
+ const int expansionLevelB = b->values.value("expandedParentsCount").toInt();
+
+ // If b has a higher expansion level than a, check if a is a parent
+ // of b, and make sure that both expansion levels are equal otherwise.
+ for (int i = expansionLevelB; i > expansionLevelA; --i) {
+ if (b->parent == a) {
+ return true;
+ }
+ b = b->parent;
+ }
+
+ // If a has a higher expansion level than a, check if b is a parent
+ // of a, and make sure that both expansion levels are equal otherwise.
+ for (int i = expansionLevelA; i > expansionLevelB; --i) {
+ if (a->parent == b) {
+ return false;
+ }
+ a = a->parent;
+ }
+
+ Q_ASSERT(a->values.value("expandedParentsCount").toInt() == b->values.value("expandedParentsCount").toInt());
+
+ // Compare the last parents of a and b which are different.
+ while (a->parent != b->parent) {
+ a = a->parent;
+ b = b->parent;
}
}
@@ -1346,6 +1385,44 @@ bool KFileItemModel::lessThan(const ItemData* a, const ItemData* b) const
return (sortOrder() == Qt::AscendingOrder) ? result < 0 : result > 0;
}
+/**
+ * Helper class for KFileItemModel::sort().
+ */
+class KFileItemModelLessThan
+{
+public:
+ KFileItemModelLessThan(const KFileItemModel* model) :
+ m_model(model)
+ {
+ }
+
+ bool operator()(const KFileItemModel::ItemData* a, const KFileItemModel::ItemData* b) const
+ {
+ return m_model->lessThan(a, b);
+ }
+
+private:
+ const KFileItemModel* m_model;
+};
+
+void KFileItemModel::sort(QList<KFileItemModel::ItemData*>::iterator begin,
+ QList<KFileItemModel::ItemData*>::iterator end) const
+{
+ KFileItemModelLessThan lessThan(this);
+
+ if (m_sortRole == NameRole) {
+ // Sorting by name can be expensive, in particular if natural sorting is
+ // enabled. Use all CPU cores to speed up the sorting process.
+ static const int numberOfThreads = QThread::idealThreadCount();
+ parallelMergeSort(begin, end, lessThan, numberOfThreads);
+ } else {
+ // Sorting by other roles is quite fast. Use only one thread to prevent
+ // problems caused by non-reentrant comparison functions, see
+ // https://bugs.kde.org/show_bug.cgi?id=312679
+ mergeSort(begin, end, lessThan);
+ }
+}
+
int KFileItemModel::sortRoleCompare(const ItemData* a, const ItemData* b) const
{
const KFileItem& itemA = a->item;
@@ -1470,88 +1547,6 @@ int KFileItemModel::stringCompare(const QString& a, const QString& b) const
: QString::compare(a, b, Qt::CaseSensitive);
}
-int KFileItemModel::expandedParentsCountCompare(const ItemData* a, const ItemData* b) const
-{
- const KUrl urlA = a->item.url();
- const KUrl urlB = b->item.url();
- if (urlA.directory() == urlB.directory()) {
- // Both items have the same directory as parent
- return 0;
- }
-
- // Check whether one item is the parent of the other item
- if (urlA.isParentOf(urlB)) {
- return (sortOrder() == Qt::AscendingOrder) ? -1 : +1;
- } else if (urlB.isParentOf(urlA)) {
- return (sortOrder() == Qt::AscendingOrder) ? +1 : -1;
- }
-
- // Determine the maximum common path of both items and
- // remember the index in 'index'
- const QString pathA = urlA.path();
- const QString pathB = urlB.path();
-
- const int maxIndex = qMin(pathA.length(), pathB.length()) - 1;
- int index = 0;
- while (index <= maxIndex && pathA.at(index) == pathB.at(index)) {
- ++index;
- }
- if (index > maxIndex) {
- index = maxIndex;
- }
- while (index > 0 && (pathA.at(index) != QLatin1Char('/') || pathB.at(index) != QLatin1Char('/'))) {
- --index;
- }
-
- // Determine the first sub-path after the common path and
- // check whether it represents a directory or already a file
- bool isDirA = true;
- const QString subPathA = subPath(a->item, pathA, index, &isDirA);
- bool isDirB = true;
- const QString subPathB = subPath(b->item, pathB, index, &isDirB);
-
- if (m_sortDirsFirst || m_sortRole == SizeRole) {
- if (isDirA && !isDirB) {
- return (sortOrder() == Qt::AscendingOrder) ? -1 : +1;
- } else if (!isDirA && isDirB) {
- return (sortOrder() == Qt::AscendingOrder) ? +1 : -1;
- }
- }
-
- // Compare the items of the parents that represent the first
- // different path after the common path.
- const QString parentPathA = pathA.left(index) + subPathA;
- const QString parentPathB = pathB.left(index) + subPathB;
-
- const ItemData* parentA = a;
- while (parentA && parentA->item.url().path() != parentPathA) {
- parentA = parentA->parent;
- }
-
- const ItemData* parentB = b;
- while (parentB && parentB->item.url().path() != parentPathB) {
- parentB = parentB->parent;
- }
-
- if (parentA && parentB) {
- return sortRoleCompare(parentA, parentB);
- }
-
- kWarning() << "Child items without parent detected:" << a->item.url() << b->item.url();
- return QString::compare(urlA.url(), urlB.url(), Qt::CaseSensitive);
-}
-
-QString KFileItemModel::subPath(const KFileItem& item,
- const QString& itemPath,
- int start,
- bool* isDir) const
-{
- Q_ASSERT(isDir);
- const int pathIndex = itemPath.indexOf('/', start + 1);
- *isDir = (pathIndex > 0) || item.isDir();
- return itemPath.mid(start, pathIndex - start);
-}
-
bool KFileItemModel::useMaximumUpdateInterval() const
{
return !m_dirLister->url().isLocalFile();
@@ -1963,4 +1958,51 @@ void KFileItemModel::determineMimeTypes(const KFileItemList& items, int timeout)
}
}
+bool KFileItemModel::isConsistent() const
+{
+ if (m_items.count() != m_itemData.count()) {
+ return false;
+ }
+
+ for (int i = 0; i < count(); ++i) {
+ // Check if m_items and m_itemData are consistent.
+ const KFileItem item = fileItem(i);
+ if (item.isNull()) {
+ qWarning() << "Item" << i << "is null";
+ return false;
+ }
+
+ const int itemIndex = index(item);
+ if (itemIndex != i) {
+ qWarning() << "Item" << i << "has a wrong index:" << itemIndex;
+ return false;
+ }
+
+ // Check if the items are sorted correctly.
+ if (i > 0 && !lessThan(m_itemData.at(i - 1), m_itemData.at(i))) {
+ qWarning() << "The order of items" << i - 1 << "and" << i << "is wrong:"
+ << fileItem(i - 1) << fileItem(i);
+ return false;
+ }
+
+ // Check if all parent-child relationships are consistent.
+ const ItemData* data = m_itemData.at(i);
+ const ItemData* parent = data->parent;
+ if (parent) {
+ if (data->values.value("expandedParentsCount").toInt() != parent->values.value("expandedParentsCount").toInt() + 1) {
+ qWarning() << "expandedParentsCount is inconsistent for parent" << parent->item << "and child" << data->item;
+ return false;
+ }
+
+ const int parentIndex = index(parent->item);
+ if (parentIndex >= i) {
+ qWarning() << "Index" << parentIndex << "of parent" << parent->item << "is not smaller than index" << i << "of child" << data->item;
+ return false;
+ }
+ }
+ }
+
+ return true;
+}
+
#include "kfileitemmodel.moc"
diff --git a/src/kitemviews/kfileitemmodel.h b/src/kitemviews/kfileitemmodel.h
index ef9dc98b9..a05d1f9d8 100644
--- a/src/kitemviews/kfileitemmodel.h
+++ b/src/kitemviews/kfileitemmodel.h
@@ -342,6 +342,12 @@ private:
bool lessThan(const ItemData* a, const ItemData* b) const;
/**
+ * Sorts the items between \a begin and \a end using the comparison
+ * function lessThan().
+ */
+ void sort(QList<ItemData*>::iterator begin, QList<ItemData*>::iterator end) const;
+
+ /**
* Helper method for lessThan() and expandedParentsCountCompare(): Compares
* the passed item-data using m_sortRole as criteria. Both items must
* have the same parent item, otherwise the comparison will be wrong.
@@ -350,22 +356,6 @@ private:
int stringCompare(const QString& a, const QString& b) const;
- /**
- * Compares the expansion level of both items. The "expansion level" is defined
- * by the number of parent directories. However simply comparing just the numbers
- * is not sufficient, it is also important to check the hierarchy for having
- * a correct order like shown in a tree.
- */
- int expandedParentsCountCompare(const ItemData* a, const ItemData* b) const;
-
- /**
- * Helper method for expandedParentsCountCompare().
- */
- QString subPath(const KFileItem& item,
- const QString& itemPath,
- int start,
- bool* isDir) const;
-
bool useMaximumUpdateInterval() const;
QList<QPair<int, QVariant> > nameRoleGroups() const;
@@ -428,6 +418,11 @@ private:
*/
static void determineMimeTypes(const KFileItemList& items, int timeout);
+ /**
+ * Checks if the model's internal data structures are consistent.
+ */
+ bool isConsistent() const;
+
private:
KFileItemModelDirLister* m_dirLister;
@@ -476,9 +471,10 @@ private:
// and done step after step in slotCompleted().
QSet<KUrl> m_urlsToExpand;
- friend class KFileItemModelSortAlgorithm; // Accesses lessThan() method
+ friend class KFileItemModelLessThan; // Accesses lessThan() method
friend class KFileItemModelRolesUpdater; // Accesses emitSortProgress() method
friend class KFileItemModelTest; // For unit testing
+ friend class KFileItemModelBenchmark; // For unit testing
friend class KFileItemListViewTest; // For unit testing
friend class DolphinPart; // Accesses m_dirLister
};
diff --git a/src/kitemviews/private/kfileitemmodelsortalgorithm.cpp b/src/kitemviews/private/kfileitemmodelsortalgorithm.cpp
deleted file mode 100644
index ab650efea..000000000
--- a/src/kitemviews/private/kfileitemmodelsortalgorithm.cpp
+++ /dev/null
@@ -1,190 +0,0 @@
-/***************************************************************************
- * Copyright (C) 2012 by Peter Penz <[email protected]> *
- * *
- * This program is free software; you can redistribute it and/or modify *
- * it under the terms of the GNU General Public License as published by *
- * the Free Software Foundation; either version 2 of the License, or *
- * (at your option) any later version. *
- * *
- * This program is distributed in the hope that it will be useful, *
- * but WITHOUT ANY WARRANTY; without even the implied warranty of *
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
- * GNU General Public License for more details. *
- * *
- * You should have received a copy of the GNU General Public License *
- * along with this program; if not, write to the *
- * Free Software Foundation, Inc., *
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA *
- ***************************************************************************/
-
-#include "kfileitemmodelsortalgorithm.h"
-
-#include <QThread>
-#include <QtCore>
-
-void KFileItemModelSortAlgorithm::sort(KFileItemModel* model,
- QList<KFileItemModel::ItemData*>::iterator begin,
- QList<KFileItemModel::ItemData*>::iterator end)
-{
- if (model->sortRole() == model->roleForType(KFileItemModel::NameRole)) {
- // Sorting by name can be expensive, in particular if natural sorting is
- // enabled. Use all CPU cores to speed up the sorting process.
- static const int numberOfThreads = QThread::idealThreadCount();
- parallelSort(model, begin, end, numberOfThreads);
- } else {
- // Sorting by other roles is quite fast. Use only one thread to prevent
- // problems caused by non-reentrant comparison functions, see
- // https://bugs.kde.org/show_bug.cgi?id=312679
- sequentialSort(model, begin, end);
- }
-}
-
-void KFileItemModelSortAlgorithm::sequentialSort(KFileItemModel* model,
- QList< KFileItemModel::ItemData* >::iterator begin,
- QList< KFileItemModel::ItemData* >::iterator end)
-{
- // The implementation is based on qStableSortHelper() from qalgorithms.h
- // Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
-
- const int span = end - begin;
- if (span < 2) {
- return;
- }
-
- const QList<KFileItemModel::ItemData*>::iterator middle = begin + span / 2;
- sequentialSort(model, begin, middle);
- sequentialSort(model, middle, end);
- merge(model, begin, middle, end);
-}
-
-void KFileItemModelSortAlgorithm::parallelSort(KFileItemModel* model,
- QList< KFileItemModel::ItemData* >::iterator begin,
- QList< KFileItemModel::ItemData* >::iterator end,
- const int numberOfThreads)
-{
- const int span = end - begin;
-
- if (numberOfThreads > 1 && span > 100) {
- const int newNumberOfThreads = numberOfThreads / 2;
- const QList<KFileItemModel::ItemData*>::iterator middle = begin + span / 2;
-
- QFuture<void> future = QtConcurrent::run(parallelSort, model, begin, middle, newNumberOfThreads);
- parallelSort(model, middle, end, newNumberOfThreads);
-
- future.waitForFinished();
-
- merge(model, begin, middle, end);
- } else {
- sequentialSort(model, begin, end);
- }
-}
-
-void KFileItemModelSortAlgorithm::merge(KFileItemModel* model,
- QList<KFileItemModel::ItemData*>::iterator begin,
- QList<KFileItemModel::ItemData*>::iterator pivot,
- QList<KFileItemModel::ItemData*>::iterator end)
-{
- // The implementation is based on qMerge() from qalgorithms.h
- // Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
-
- const int len1 = pivot - begin;
- const int len2 = end - pivot;
-
- if (len1 == 0 || len2 == 0) {
- return;
- }
-
- if (len1 + len2 == 2) {
- if (model->lessThan(*(begin + 1), *(begin))) {
- qSwap(*begin, *(begin + 1));
- }
- return;
- }
-
- QList<KFileItemModel::ItemData*>::iterator firstCut;
- QList<KFileItemModel::ItemData*>::iterator secondCut;
- int len2Half;
- if (len1 > len2) {
- const int len1Half = len1 / 2;
- firstCut = begin + len1Half;
- secondCut = lowerBound(model, pivot, end, *firstCut);
- len2Half = secondCut - pivot;
- } else {
- len2Half = len2 / 2;
- secondCut = pivot + len2Half;
- firstCut = upperBound(model, begin, pivot, *secondCut);
- }
-
- reverse(firstCut, pivot);
- reverse(pivot, secondCut);
- reverse(firstCut, secondCut);
-
- const QList<KFileItemModel::ItemData*>::iterator newPivot = firstCut + len2Half;
- merge(model, begin, firstCut, newPivot);
- merge(model, newPivot, secondCut, end);
-}
-
-
-QList<KFileItemModel::ItemData*>::iterator
-KFileItemModelSortAlgorithm::lowerBound(KFileItemModel* model,
- QList<KFileItemModel::ItemData*>::iterator begin,
- QList<KFileItemModel::ItemData*>::iterator end,
- const KFileItemModel::ItemData* value)
-{
- // The implementation is based on qLowerBound() from qalgorithms.h
- // Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
-
- QList<KFileItemModel::ItemData*>::iterator middle;
- int n = int(end - begin);
- int half;
-
- while (n > 0) {
- half = n >> 1;
- middle = begin + half;
- if (model->lessThan(*middle, value)) {
- begin = middle + 1;
- n -= half + 1;
- } else {
- n = half;
- }
- }
- return begin;
-}
-
-QList<KFileItemModel::ItemData*>::iterator
-KFileItemModelSortAlgorithm::upperBound(KFileItemModel* model,
- QList<KFileItemModel::ItemData*>::iterator begin,
- QList<KFileItemModel::ItemData*>::iterator end,
- const KFileItemModel::ItemData* value)
-{
- // The implementation is based on qUpperBound() from qalgorithms.h
- // Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
-
- QList<KFileItemModel::ItemData*>::iterator middle;
- int n = end - begin;
- int half;
-
- while (n > 0) {
- half = n >> 1;
- middle = begin + half;
- if (model->lessThan(value, *middle)) {
- n = half;
- } else {
- begin = middle + 1;
- n -= half + 1;
- }
- }
- return begin;
-}
-
-void KFileItemModelSortAlgorithm::reverse(QList<KFileItemModel::ItemData*>::iterator begin,
- QList<KFileItemModel::ItemData*>::iterator end)
-{
- // The implementation is based on qReverse() from qalgorithms.h
- // Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
-
- --end;
- while (begin < end) {
- qSwap(*begin++, *end--);
- }
-}
diff --git a/src/kitemviews/private/kfileitemmodelsortalgorithm.h b/src/kitemviews/private/kfileitemmodelsortalgorithm.h
index 07e5d4a81..1d5689432 100644
--- a/src/kitemviews/private/kfileitemmodelsortalgorithm.h
+++ b/src/kitemviews/private/kfileitemmodelsortalgorithm.h
@@ -1,79 +1,143 @@
-/***************************************************************************
- * Copyright (C) 2012 by Peter Penz <[email protected]> *
- * *
- * This program is free software; you can redistribute it and/or modify *
- * it under the terms of the GNU General Public License as published by *
- * the Free Software Foundation; either version 2 of the License, or *
- * (at your option) any later version. *
- * *
- * This program is distributed in the hope that it will be useful, *
- * but WITHOUT ANY WARRANTY; without even the implied warranty of *
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
- * GNU General Public License for more details. *
- * *
- * You should have received a copy of the GNU General Public License *
- * along with this program; if not, write to the *
- * Free Software Foundation, Inc., *
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA *
- ***************************************************************************/
+/*****************************************************************************
+ * Copyright (C) 2012 by Peter Penz <[email protected]> *
+ * Copyright (C) 2012 by Emmanuel Pescosta <[email protected]> *
+ * Copyright (C) 2013 by Frank Reininghaus <[email protected]> *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA *
+ *****************************************************************************/
#ifndef KFILEITEMMODELSORTALGORITHM_H
#define KFILEITEMMODELSORTALGORITHM_H
-#include <libdolphin_export.h>
+#include <QtCore>
-#include <kitemviews/kfileitemmodel.h>
+#include <algorithm>
/**
- * @brief Sort algorithm for sorting items of KFileItemModel.
- *
- * Sorts the items by using KFileItemModel::lessThan() as comparison criteria.
- * The merge sort algorithm is used to assure a worst-case
- * of O(n * log(n)) and to keep the number of comparisons low.
+ * Sorts the items using the merge sort algorithm is used to assure a
+ * worst-case of O(n * log(n)) and to keep the number of comparisons low.
*
* The implementation is based on qStableSortHelper() from qalgorithms.h
* Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
- * The sorting implementations of qAlgorithms could not be used as they
- * don't support having a member-function as comparison criteria.
*/
-class LIBDOLPHINPRIVATE_EXPORT KFileItemModelSortAlgorithm
+
+template <typename RandomAccessIterator, typename LessThan>
+static void mergeSort(RandomAccessIterator begin,
+ RandomAccessIterator end,
+ LessThan lessThan)
{
-public:
- static void sort(KFileItemModel* model,
- QList<KFileItemModel::ItemData*>::iterator begin,
- QList<KFileItemModel::ItemData*>::iterator end);
+ // The implementation is based on qStableSortHelper() from qalgorithms.h
+ // Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
-private:
- static void sequentialSort(KFileItemModel* model,
- QList<KFileItemModel::ItemData*>::iterator begin,
- QList<KFileItemModel::ItemData*>::iterator end);
+ const int span = end - begin;
+ if (span < 2) {
+ return;
+ }
- static void parallelSort(KFileItemModel* model,
- QList<KFileItemModel::ItemData*>::iterator begin,
- QList<KFileItemModel::ItemData*>::iterator end,
- const int numberOfThreads);
+ const RandomAccessIterator middle = begin + span / 2;
+ mergeSort(begin, middle, lessThan);
+ mergeSort(middle, end, lessThan);
+ merge(begin, middle, end, lessThan);
+}
- static void merge(KFileItemModel* model,
- QList<KFileItemModel::ItemData*>::iterator begin,
- QList<KFileItemModel::ItemData*>::iterator pivot,
- QList<KFileItemModel::ItemData*>::iterator end);
+/**
+ * Uses up to \a numberOfThreads threads to sort the items between
+ * \a begin and \a end. Only item ranges longer than
+ * \a parallelMergeSortingThreshold are split to be sorted by two different
+ * threads.
+ *
+ * The comparison function \a lessThan must be reentrant.
+ */
- static QList<KFileItemModel::ItemData*>::iterator
- lowerBound(KFileItemModel* model,
- QList<KFileItemModel::ItemData*>::iterator begin,
- QList<KFileItemModel::ItemData*>::iterator end,
- const KFileItemModel::ItemData* value);
+template <typename RandomAccessIterator, typename LessThan>
+static void parallelMergeSort(RandomAccessIterator begin,
+ RandomAccessIterator end,
+ LessThan lessThan,
+ int numberOfThreads,
+ int parallelMergeSortingThreshold = 100)
+{
+ const int span = end - begin;
- static QList<KFileItemModel::ItemData*>::iterator
- upperBound(KFileItemModel* model,
- QList<KFileItemModel::ItemData*>::iterator begin,
- QList<KFileItemModel::ItemData*>::iterator end,
- const KFileItemModel::ItemData* value);
+ if (numberOfThreads > 1 && span > parallelMergeSortingThreshold) {
+ const int newNumberOfThreads = numberOfThreads / 2;
+ const RandomAccessIterator middle = begin + span / 2;
- static void reverse(QList<KFileItemModel::ItemData*>::iterator begin,
- QList<KFileItemModel::ItemData*>::iterator end);
-};
+ QFuture<void> future = QtConcurrent::run(parallelMergeSort<RandomAccessIterator, LessThan>, begin, middle, lessThan, newNumberOfThreads, parallelMergeSortingThreshold);
+ parallelMergeSort(middle, end, lessThan, newNumberOfThreads, parallelMergeSortingThreshold);
-#endif
+ future.waitForFinished();
+
+ merge(begin, middle, end, lessThan);
+ } else {
+ mergeSort(begin, end, lessThan);
+ }
+}
+
+/**
+ * Merges the sorted item ranges between \a begin and \a pivot and
+ * between \a pivot and \a end into a single sorted range between
+ * \a begin and \a end.
+ *
+ * The implementation is based on qMerge() from qalgorithms.h
+ * Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+ */
+
+template <typename RandomAccessIterator, typename LessThan>
+static void merge(RandomAccessIterator begin,
+ RandomAccessIterator pivot,
+ RandomAccessIterator end,
+ LessThan lessThan)
+{
+ // The implementation is based on qMerge() from qalgorithms.h
+ // Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+
+ const int len1 = pivot - begin;
+ const int len2 = end - pivot;
+
+ if (len1 == 0 || len2 == 0) {
+ return;
+ }
+ if (len1 + len2 == 2) {
+ if (lessThan(*(begin + 1), *(begin))) {
+ qSwap(*begin, *(begin + 1));
+ }
+ return;
+ }
+
+ RandomAccessIterator firstCut;
+ RandomAccessIterator secondCut;
+ int len2Half;
+ if (len1 > len2) {
+ const int len1Half = len1 / 2;
+ firstCut = begin + len1Half;
+ secondCut = std::lower_bound(pivot, end, *firstCut, lessThan);
+ len2Half = secondCut - pivot;
+ } else {
+ len2Half = len2 / 2;
+ secondCut = pivot + len2Half;
+ firstCut = std::upper_bound(begin, pivot, *secondCut, lessThan);
+ }
+
+ std::rotate(firstCut, pivot, secondCut);
+
+ RandomAccessIterator newPivot = firstCut + len2Half;
+ merge(begin, firstCut, newPivot, lessThan);
+ merge(newPivot, secondCut, end, lessThan);
+}
+
+#endif
diff --git a/src/main.cpp b/src/main.cpp
index 5addff194..59575fdaf 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -50,6 +50,9 @@ KDE_EXPORT int kdemain(int argc, char **argv)
about.addAuthor(ki18nc("@info:credit", "David Faure"),
ki18nc("@info:credit", "Developer"),
+ about.addAuthor(ki18nc("@info:credit", "Emmanuel Pescosta"),
+ ki18nc("@info:credit", "Developer"),
about.addAuthor(ki18nc("@info:credit", "Aaron J. Seigo"),
ki18nc("@info:credit", "Developer"),
diff --git a/src/panels/folders/folderspanel.cpp b/src/panels/folders/folderspanel.cpp
index 98c06fb35..46c1b3450 100644
--- a/src/panels/folders/folderspanel.cpp
+++ b/src/panels/folders/folderspanel.cpp
@@ -236,7 +236,8 @@ void FoldersPanel::slotItemDropEvent(int index, QGraphicsSceneDragDropEvent* eve
event->buttons(),
event->modifiers());
- const QString error = DragAndDropHelper::dropUrls(destItem, destItem.url(), &dropEvent);
+ QString error;
+ DragAndDropHelper::dropUrls(destItem, destItem.url(), &dropEvent, error);
if (!error.isEmpty()) {
emit errorMessage(error);
}
diff --git a/src/panels/places/placespanel.cpp b/src/panels/places/placespanel.cpp
index 919f2ed45..e23732c97 100644
--- a/src/panels/places/placespanel.cpp
+++ b/src/panels/places/placespanel.cpp
@@ -352,7 +352,11 @@ void PlacesPanel::slotItemDropEvent(int index, QGraphicsSceneDragDropEvent* even
event->buttons(),
event->modifiers());
- DragAndDropHelper::dropUrls(KFileItem(), destUrl, &dropEvent);
+ QString error;
+ DragAndDropHelper::dropUrls(KFileItem(), destUrl, &dropEvent, error);
+ if (!error.isEmpty()) {
+ emit errorMessage(error);
+ }
}
void PlacesPanel::slotItemDropEventStorageSetupDone(int index, bool success)
@@ -364,7 +368,11 @@ void PlacesPanel::slotItemDropEventStorageSetupDone(int index, bool success)
if (success) {
KUrl destUrl = m_model->placesItem(index)->url();
- DragAndDropHelper::dropUrls(KFileItem(), destUrl, m_itemDropEvent);
+ QString error;
+ DragAndDropHelper::dropUrls(KFileItem(), destUrl, m_itemDropEvent, error);
+ if (!error.isEmpty()) {
+ emit errorMessage(error);
+ }
}
delete m_itemDropEventMimeData;
@@ -384,7 +392,8 @@ void PlacesPanel::slotAboveItemDropEvent(int index, QGraphicsSceneDragDropEvent*
void PlacesPanel::slotUrlsDropped(const KUrl& dest, QDropEvent* event, QWidget* parent)
{
Q_UNUSED(parent);
- const QString error = DragAndDropHelper::dropUrls(KFileItem(), dest, event);
+ QString error;
+ DragAndDropHelper::dropUrls(KFileItem(), dest, event, error);
if (!error.isEmpty()) {
emit errorMessage(error);
}
diff --git a/src/tests/CMakeLists.txt b/src/tests/CMakeLists.txt
index 6575eecde..543261eac 100644
--- a/src/tests/CMakeLists.txt
+++ b/src/tests/CMakeLists.txt
@@ -48,6 +48,16 @@ set(kfileitemmodeltest_SRCS
kde4_add_unit_test(kfileitemmodeltest TEST ${kfileitemmodeltest_SRCS})
target_link_libraries(kfileitemmodeltest dolphinprivate ${KDE4_KIO_LIBS} ${QT_QTTEST_LIBRARY})
+# KFileItemModelBenchmark
+set(kfileitemmodelbenchmark_SRCS
+ kfileitemmodelbenchmark.cpp
+ testdir.cpp
+ ../kitemviews/kfileitemmodel.cpp
+ ../kitemviews/kitemmodelbase.cpp
+)
+kde4_add_executable(kfileitemmodelbenchmark TEST ${kfileitemmodelbenchmark_SRCS})
+target_link_libraries(kfileitemmodelbenchmark dolphinprivate ${KDE4_KIO_LIBS} ${QT_QTTEST_LIBRARY})
+
# KItemListKeyboardSearchManagerTest
set(kitemlistkeyboardsearchmanagertest_SRCS
kitemlistkeyboardsearchmanagertest.cpp
diff --git a/src/tests/kfileitemmodelbenchmark.cpp b/src/tests/kfileitemmodelbenchmark.cpp
new file mode 100644
index 000000000..b1e777c06
--- /dev/null
+++ b/src/tests/kfileitemmodelbenchmark.cpp
@@ -0,0 +1,328 @@
+/***************************************************************************
+ * Copyright (C) 2011 by Peter Penz <[email protected]> *
+ * Copyright (C) 2013 by Frank Reininghaus <[email protected]> *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA *
+ ***************************************************************************/
+
+#include <qtest_kde.h>
+
+#include "kitemviews/kfileitemmodel.h"
+#include "kitemviews/private/kfileitemmodelsortalgorithm.h"
+
+#include "testdir.h"
+
+#include <KRandomSequence>
+
+void myMessageOutput(QtMsgType type, const char* msg)
+{
+ switch (type) {
+ case QtDebugMsg:
+ break;
+ case QtWarningMsg:
+ break;
+ case QtCriticalMsg:
+ fprintf(stderr, "Critical: %s\n", msg);
+ break;
+ case QtFatalMsg:
+ fprintf(stderr, "Fatal: %s\n", msg);
+ abort();
+ default:
+ break;
+ }
+}
+
+namespace {
+ const int DefaultTimeout = 5000;
+};
+
+Q_DECLARE_METATYPE(KFileItemList)
+Q_DECLARE_METATYPE(KItemRangeList)
+
+class KFileItemModelBenchmark : public QObject
+{
+ Q_OBJECT
+
+public:
+ KFileItemModelBenchmark();
+
+private slots:
+ void insertAndRemoveManyItems_data();
+ void insertAndRemoveManyItems();
+ void insertManyChildItems();
+
+private:
+ static KFileItemList createFileItemList(const QStringList& fileNames, const QString& urlPrefix = QLatin1String("file:///"));
+};
+
+KFileItemModelBenchmark::KFileItemModelBenchmark()
+{
+}
+
+void KFileItemModelBenchmark::insertAndRemoveManyItems_data()
+{
+ QTest::addColumn<KFileItemList>("initialItems");
+ QTest::addColumn<KFileItemList>("newItems");
+ QTest::addColumn<KFileItemList>("removedItems");
+ QTest::addColumn<KFileItemList>("expectedFinalItems");
+ QTest::addColumn<KItemRangeList>("expectedItemsInserted");
+ QTest::addColumn<KItemRangeList>("expectedItemsRemoved");
+
+ QList<int> sizes;
+ sizes << 1000 << 4000 << 16000 << 64000 << 256000;
+ //sizes << 50000 << 100000 << 150000 << 200000 << 250000;
+
+ foreach (int n, sizes) {
+ QStringList allStrings;
+ for (int i = 0; i < n; ++i) {
+ allStrings << QString::number(i);
+ }
+
+ // We want to keep the sorting overhead in the benchmark low.
+ // Therefore, we do not use natural sorting. However, this
+ // means that our list is currently not sorted.
+ allStrings.sort();
+
+ KFileItemList all = createFileItemList(allStrings);
+
+ KFileItemList firstHalf, secondHalf, even, odd;
+ for (int i = 0; i < n; ++i) {
+ if (i < n / 2) {
+ firstHalf << all.at(i);
+ } else {
+ secondHalf << all.at(i);
+ }
+
+ if (i % 2 == 0) {
+ even << all.at(i);
+ } else {
+ odd << all.at(i);
+ }
+ }
+
+ KItemRangeList itemRangeListFirstHalf;
+ itemRangeListFirstHalf << KItemRange(0, firstHalf.count());
+
+ KItemRangeList itemRangeListSecondHalf;
+ itemRangeListSecondHalf << KItemRange(firstHalf.count(), secondHalf.count());
+
+ KItemRangeList itemRangeListOddInserted, itemRangeListOddRemoved;
+ for (int i = 0; i < odd.count(); ++i) {
+ // Note that the index in the KItemRange is the index of
+ // the model *before* the items have been inserted.
+ itemRangeListOddInserted << KItemRange(i + 1, 1);
+ itemRangeListOddRemoved << KItemRange(2 * i + 1, 1);
+ }
+
+ const int bufferSize = 128;
+ char buffer[bufferSize];
+
+ snprintf(buffer, bufferSize, "all--n=%i", n);
+ QTest::newRow(buffer) << all << KFileItemList() << KFileItemList() << all << KItemRangeList() << KItemRangeList();
+
+ snprintf(buffer, bufferSize, "1st half + 2nd half--n=%i", n);
+ QTest::newRow(buffer) << firstHalf << secondHalf << KFileItemList() << all << itemRangeListSecondHalf << KItemRangeList();
+
+ snprintf(buffer, bufferSize, "2nd half + 1st half--n=%i", n);
+ QTest::newRow(buffer) << secondHalf << firstHalf << KFileItemList() << all << itemRangeListFirstHalf << KItemRangeList();
+
+ snprintf(buffer, bufferSize, "even + odd--n=%i", n);
+ QTest::newRow(buffer) << even << odd << KFileItemList() << all << itemRangeListOddInserted << KItemRangeList();
+
+ snprintf(buffer, bufferSize, "all - 2nd half--n=%i", n);
+ QTest::newRow(buffer) << all << KFileItemList() << secondHalf << firstHalf << KItemRangeList() << itemRangeListSecondHalf;
+
+ snprintf(buffer, bufferSize, "all - 1st half--n=%i", n);
+ QTest::newRow(buffer) << all << KFileItemList() << firstHalf << secondHalf << KItemRangeList() << itemRangeListFirstHalf;
+
+ snprintf(buffer, bufferSize, "all - odd--n=%i", n);
+ QTest::newRow(buffer) << all << KFileItemList() << odd << even << KItemRangeList() << itemRangeListOddRemoved;
+ }
+}
+
+void KFileItemModelBenchmark::insertAndRemoveManyItems()
+{
+ QFETCH(KFileItemList, initialItems);
+ QFETCH(KFileItemList, newItems);
+ QFETCH(KFileItemList, removedItems);
+ QFETCH(KFileItemList, expectedFinalItems);
+ QFETCH(KItemRangeList, expectedItemsInserted);
+ QFETCH(KItemRangeList, expectedItemsRemoved);
+
+ KFileItemModel model;
+
+ // Avoid overhead caused by natural sorting
+ // and determining the isDir/isLink roles.
+ model.m_naturalSorting = false;
+ model.setRoles(QSet<QByteArray>() << "text");
+
+ QSignalSpy spyItemsInserted(&model, SIGNAL(itemsInserted(KItemRangeList)));
+ QSignalSpy spyItemsRemoved(&model, SIGNAL(itemsRemoved(KItemRangeList)));
+
+ QBENCHMARK {
+ model.slotClear();
+ model.slotNewItems(initialItems);
+ model.slotCompleted();
+ QCOMPARE(model.count(), initialItems.count());
+
+ if (!newItems.isEmpty()) {
+ model.slotNewItems(newItems);
+ model.slotCompleted();
+ }
+ QCOMPARE(model.count(), initialItems.count() + newItems.count());
+
+ if (!removedItems.isEmpty()) {
+ model.removeItems(removedItems);
+ }
+ QCOMPARE(model.count(), initialItems.count() + newItems.count() - removedItems.count());
+ }
+
+ QVERIFY(model.isConsistent());
+
+ for (int i = 0; i < model.count(); ++i) {
+ QCOMPARE(model.fileItem(i), expectedFinalItems.at(i));
+ }
+
+ if (!expectedItemsInserted.empty()) {
+ QVERIFY(!spyItemsInserted.empty());
+ const KItemRangeList actualItemsInserted = spyItemsInserted.last().first().value<KItemRangeList>();
+ QCOMPARE(actualItemsInserted, expectedItemsInserted);
+ }
+
+ if (!expectedItemsRemoved.empty()) {
+ QVERIFY(!spyItemsRemoved.empty());
+ const KItemRangeList actualItemsRemoved = spyItemsRemoved.last().first().value<KItemRangeList>();
+ QCOMPARE(actualItemsRemoved, expectedItemsRemoved);
+ }
+}
+
+void KFileItemModelBenchmark::insertManyChildItems()
+{
+ qInstallMsgHandler(myMessageOutput);
+
+ KFileItemModel model;
+
+ // Avoid overhead caused by natural sorting.
+ model.m_naturalSorting = false;
+
+ QSet<QByteArray> modelRoles = model.roles();
+ modelRoles << "isExpanded" << "isExpandable" << "expandedParentsCount";
+ model.setRoles(modelRoles);
+ model.setSortDirectoriesFirst(false);
+
+ // Create a test folder with a 3-level tree structure of folders.
+ TestDir testFolder;
+ int numberOfFolders = 0;
+
+ QStringList subFolderNames;
+ subFolderNames << "a/" << "b/" << "c/" << "d/";
+
+ foreach (const QString& s1, subFolderNames) {
+ ++numberOfFolders;
+ foreach (const QString& s2, subFolderNames) {
+ ++numberOfFolders;
+ foreach (const QString& s3, subFolderNames) {
+ testFolder.createDir("level-1-" + s1 + "level-2-" + s2 + "level-3-" + s3);
+ ++numberOfFolders;
+ }
+ }
+ }
+
+ // Open the folder in the model and expand all subfolders.
+ model.loadDirectory(testFolder.url());
+ QVERIFY(QTest::kWaitForSignal(&model, SIGNAL(itemsInserted(KItemRangeList)), DefaultTimeout));
+
+ int index = 0;
+ while (index < model.count()) {
+ if (model.isExpandable(index)) {
+ model.setExpanded(index, true);
+
+ if (!model.data(index).value("text").toString().startsWith("level-3")) {
+ // New subfolders will appear unless we are on the final level already.
+ QVERIFY(QTest::kWaitForSignal(&model, SIGNAL(itemsInserted(KItemRangeList)), DefaultTimeout));
+ }
+
+ QVERIFY(model.isExpanded(index));
+ }
+ ++index;
+ }
+
+ QCOMPARE(model.count(), numberOfFolders);
+
+ // Create a list of many file items, which will be added to each of the
+ // "level 1", "level 2", and "level 3" folders.
+ const int filesPerDirectory = 500;
+ QStringList allStrings;
+ for (int i = 0; i < filesPerDirectory; ++i) {
+ allStrings << QString::number(i);
+ }
+ allStrings.sort();
+
+ KFileItemList newItems;
+
+ // Also keep track of all expected items, including the existing
+ // folders, to verify the final state of the model.
+ KFileItemList allExpectedItems;
+
+ for (int i = 0; i < model.count(); ++i) {
+ const KFileItem folderItem = model.fileItem(i);
+ allExpectedItems << folderItem;
+
+ const KUrl folderUrl = folderItem.url();
+ KFileItemList itemsInFolder = createFileItemList(allStrings, folderUrl.url(KUrl::AddTrailingSlash));
+
+ newItems.append(itemsInFolder);
+ allExpectedItems.append(itemsInFolder);
+ }
+
+ // Bring the items into random order.
+ KRandomSequence randomSequence(0);
+ randomSequence.randomize(newItems);
+
+ // Measure how long it takes to insert and then remove all files.
+ QBENCHMARK {
+ model.slotNewItems(newItems);
+ model.slotCompleted();
+
+ QCOMPARE(model.count(), allExpectedItems.count());
+ QVERIFY(model.isConsistent());
+ for (int i = 0; i < model.count(); ++i) {
+ QCOMPARE(model.fileItem(i), allExpectedItems.at(i));
+ }
+
+ model.slotItemsDeleted(newItems);
+ QCOMPARE(model.count(), numberOfFolders);
+ QVERIFY(model.isConsistent());
+ }
+}
+
+KFileItemList KFileItemModelBenchmark::createFileItemList(const QStringList& fileNames, const QString& prefix)
+{
+ // Suppress 'file does not exist anymore' messages from KFileItemPrivate::init().
+ qInstallMsgHandler(myMessageOutput);
+
+ KFileItemList result;
+ foreach (const QString& name, fileNames) {
+ const KUrl url(prefix + name);
+ const KFileItem item(url, QString(), KFileItem::Unknown);
+ result << item;
+ }
+ return result;
+}
+
+QTEST_KDEMAIN(KFileItemModelBenchmark, NoGUI)
+
+#include "kfileitemmodelbenchmark.moc"
diff --git a/src/tests/kfileitemmodeltest.cpp b/src/tests/kfileitemmodeltest.cpp
index 719459c6f..c9f8a3468 100644
--- a/src/tests/kfileitemmodeltest.cpp
+++ b/src/tests/kfileitemmodeltest.cpp
@@ -21,6 +21,8 @@
#include <qtest_kde.h>
#include <KDirLister>
+#include <kio/job.h>
+
#include "kitemviews/kfileitemmodel.h"
#include "kitemviews/private/kfileitemmodeldirlister.h"
#include "testdir.h"
@@ -71,6 +73,7 @@ private slots:
void testItemRangeConsistencyWhenInsertingItems();
void testExpandItems();
void testExpandParentItems();
+ void testMakeExpandedItemHidden();
void testSorting();
void testIndexForKeyboardSearch();
void testNameFilter();
@@ -78,7 +81,6 @@ private slots:
void testRemoveHiddenItems();
private:
- bool isModelConsistent() const;
QStringList itemsInModel() const;
private:
@@ -153,7 +155,7 @@ void KFileItemModelTest::testNewItems()
QCOMPARE(m_model->count(), 3);
- QVERIFY(isModelConsistent());
+ QVERIFY(m_model->isConsistent());
}
void KFileItemModelTest::testRemoveItems()
@@ -163,13 +165,13 @@ void KFileItemModelTest::testRemoveItems()
m_model->loadDirectory(m_testDir->url());
QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsInserted(KItemRangeList)), DefaultTimeout));
QCOMPARE(m_model->count(), 2);
- QVERIFY(isModelConsistent());
+ QVERIFY(m_model->isConsistent());
m_testDir->removeFile("a.txt");
m_model->m_dirLister->updateDirectory(m_testDir->url());
QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsRemoved(KItemRangeList)), DefaultTimeout));
QCOMPARE(m_model->count(), 1);
- QVERIFY(isModelConsistent());
+ QVERIFY(m_model->isConsistent());
}
void KFileItemModelTest::testDirLoadingCompleted()
@@ -212,7 +214,7 @@ void KFileItemModelTest::testDirLoadingCompleted()
QCOMPARE(itemsRemovedSpy.count(), 2);
QCOMPARE(m_model->count(), 4);
- QVERIFY(isModelConsistent());
+ QVERIFY(m_model->isConsistent());
}
void KFileItemModelTest::testSetData()
@@ -233,7 +235,7 @@ void KFileItemModelTest::testSetData()
values = m_model->data(0);
QCOMPARE(values.value("customRole1").toString(), QString("Test1"));
QCOMPARE(values.value("customRole2").toString(), QString("Test2"));
- QVERIFY(isModelConsistent());
+ QVERIFY(m_model->isConsistent());
}
void KFileItemModelTest::testSetDataWithModifiedSortRole_data()
@@ -314,7 +316,7 @@ void KFileItemModelTest::testSetDataWithModifiedSortRole()
QCOMPARE(m_model->data(0).value("rating").toInt(), ratingIndex0);
QCOMPARE(m_model->data(1).value("rating").toInt(), ratingIndex1);
QCOMPARE(m_model->data(2).value("rating").toInt(), ratingIndex2);
- QVERIFY(isModelConsistent());
+ QVERIFY(m_model->isConsistent());
}
void KFileItemModelTest::testModelConsistencyWhenInsertingItems()
@@ -353,7 +355,7 @@ void KFileItemModelTest::testModelConsistencyWhenInsertingItems()
QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsInserted(KItemRangeList)), DefaultTimeout));
}
- QVERIFY(isModelConsistent());
+ QVERIFY(m_model->isConsistent());
}
QCOMPARE(m_model->count(), 201);
@@ -487,6 +489,7 @@ void KFileItemModelTest::testExpandItems()
QCOMPARE(spyRemoved.count(), 1);
itemRangeList = spyRemoved.takeFirst().at(0).value<KItemRangeList>();
QCOMPARE(itemRangeList, KItemRangeList() << KItemRange(1, 4)); // 4 items removed
+ QVERIFY(m_model->isConsistent());
// Clear the model, reload the folder and try to restore the expanded folders.
m_model->clear();
@@ -503,6 +506,7 @@ void KFileItemModelTest::testExpandItems()
QVERIFY(m_model->isExpanded(3));
QVERIFY(!m_model->isExpanded(4));
QCOMPARE(m_model->expandedDirectories(), allFolders);
+ QVERIFY(m_model->isConsistent());
// Move to a sub folder, then call restoreExpandedFolders() *before* going back.
// This is how DolphinView restores the expanded folders when navigating in history.
@@ -565,6 +569,56 @@ void KFileItemModelTest::testExpandParentItems()
QVERIFY(m_model->isExpanded(2));
QVERIFY(m_model->isExpanded(3));
QVERIFY(!m_model->isExpanded(4));
+ QVERIFY(m_model->isConsistent());
+}
+
+/**
+ * Renaming an expanded folder by prepending its name with a dot makes it
+ * hidden. Verify that this does not cause an inconsistent model state and
+ * a crash later on, see https://bugs.kde.org/show_bug.cgi?id=311947
+ */
+void KFileItemModelTest::testMakeExpandedItemHidden()
+{
+ QSet<QByteArray> modelRoles = m_model->roles();
+ modelRoles << "isExpanded" << "isExpandable" << "expandedParentsCount";
+ m_model->setRoles(modelRoles);
+
+ QStringList files;
+ m_testDir->createFile("1a/2a/3a");
+ m_testDir->createFile("1a/2a/3b");
+ m_testDir->createFile("1a/2b");
+ m_testDir->createFile("1b");
+
+ m_model->loadDirectory(m_testDir->url());
+ QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsInserted(KItemRangeList)), DefaultTimeout));
+
+ // So far, the model contains only "1a/" and "1b".
+ QCOMPARE(m_model->count(), 2);
+ m_model->setExpanded(0, true);
+ QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsInserted(KItemRangeList)), DefaultTimeout));
+
+ // Now "1a/2a" and "1a/2b" have appeared.
+ QCOMPARE(m_model->count(), 4);
+ m_model->setExpanded(1, true);
+ QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsInserted(KItemRangeList)), DefaultTimeout));
+ QCOMPARE(m_model->count(), 6);
+
+ // Rename "1a/2" and make it hidden.
+ const QString oldPath = m_model->fileItem(0).url().path() + "/2a";
+ const QString newPath = m_model->fileItem(0).url().path() + "/.2a";
+
+ KIO::SimpleJob* job = KIO::rename(oldPath, newPath, KIO::HideProgressInfo);
+ bool ok = job->exec();
+ QVERIFY(ok);
+ QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsRemoved(KItemRangeList)), DefaultTimeout));
+
+ // "1a/2" and its subfolders have disappeared now.
+ QVERIFY(m_model->isConsistent());
+ QCOMPARE(m_model->count(), 3);
+
+ m_model->setExpanded(0, false);
+ QCOMPARE(m_model->count(), 2);
+
}
void KFileItemModelTest::testSorting()
@@ -849,29 +903,6 @@ void KFileItemModelTest::testRemoveHiddenItems()
m_model->setShowHiddenFiles(false);
}
-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()) {
- qWarning() << "Item" << i << "is null";
- return false;
- }
-
- const int itemIndex = m_model->index(item);
- if (itemIndex != i) {
- qWarning() << "Item" << i << "has a wrong index:" << itemIndex;
- return false;
- }
- }
-
- return true;
-}
-
QStringList KFileItemModelTest::itemsInModel() const
{
QStringList items;
diff --git a/src/views/dolphinremoteencoding.cpp b/src/views/dolphinremoteencoding.cpp
index 375b3fd46..04b350eda 100644
--- a/src/views/dolphinremoteencoding.cpp
+++ b/src/views/dolphinremoteencoding.cpp
@@ -38,7 +38,6 @@
#include <KMenu>
#include <KProtocolInfo>
#include <KProtocolManager>
-#include <KIO/SlaveConfig>
#include <KIO/Scheduler>
#include <KConfigGroup>
@@ -132,9 +131,7 @@ void DolphinRemoteEncoding::updateMenu()
m_menu->menu()->actions().at(i)->setChecked(false);
}
- QString charset = KGlobal::charsets()->descriptionForEncoding(KIO::SlaveConfig::self()->configData(m_currentURL.protocol(),
- m_currentURL.host(), DATA_KEY));
-
+ const QString charset = KGlobal::charsets()->descriptionForEncoding(KProtocolManager::charsetFor(m_currentURL));
if (!charset.isEmpty()) {
int id = 0;
bool isFound = false;
diff --git a/src/views/dolphinview.cpp b/src/views/dolphinview.cpp
index 502ffd428..d1e154f68 100644
--- a/src/views/dolphinview.cpp
+++ b/src/views/dolphinview.cpp
@@ -1023,14 +1023,16 @@ void DolphinView::slotItemDropEvent(int index, QGraphicsSceneDragDropEvent* even
event->buttons(),
event->modifiers());
- const QString error = DragAndDropHelper::dropUrls(destItem, destUrl, &dropEvent);
+ QString error;
+ KonqOperations* op = DragAndDropHelper::dropUrls(destItem, destUrl, &dropEvent, error);
if (!error.isEmpty()) {
emit infoMessage(error);
}
- if (destUrl == url()) {
+ if (op && destUrl == url()) {
// Mark the dropped urls as selected.
- markPastedUrlsAsSelected(event->mimeData());
+ m_clearSelectionBeforeSelectingNewItems = true;
+ connect(op, SIGNAL(urlPasted(KUrl)), this, SLOT(slotUrlPasted(KUrl)));
}
}
@@ -1066,6 +1068,11 @@ void DolphinView::slotMouseButtonPressed(int itemIndex, Qt::MouseButtons buttons
}
}
+void DolphinView::slotAboutToCreate(const KUrl::List& urls)
+{
+ m_selectedUrls << urls;
+}
+
void DolphinView::slotSelectionChanged(const QSet<int>& current, const QSet<int>& previous)
{
const int currentCount = current.count();
@@ -1523,8 +1530,11 @@ void DolphinView::applyModeToView()
void DolphinView::pasteToUrl(const KUrl& url)
{
- markPastedUrlsAsSelected(QApplication::clipboard()->mimeData());
- KonqOperations::doPaste(this, url);
+ KonqOperations* op = KonqOperations::doPasteV2(this, url);
+ if (op) {
+ m_clearSelectionBeforeSelectingNewItems = true;
+ connect(op, SIGNAL(aboutToCreate(KUrl::List)), this, SLOT(slotAboutToCreate(KUrl::List)));
+ }
}
KUrl::List DolphinView::simplifiedSelectedUrls() const
@@ -1552,18 +1562,6 @@ QMimeData* DolphinView::selectionMimeData() const
return m_model->createMimeData(selectedIndexes);
}
-void DolphinView::markPastedUrlsAsSelected(const QMimeData* mimeData)
-{
- const KUrl::List sourceUrls = KUrl::List::fromMimeData(mimeData);
- KUrl::List destUrls;
- foreach (const KUrl& source, sourceUrls) {
- KUrl destination(url().url() + '/' + source.fileName());
- destUrls << destination;
- }
- markUrlsAsSelected(destUrls);
- m_clearSelectionBeforeSelectingNewItems = true;
-}
-
void DolphinView::updateWritableState()
{
const bool wasFolderWritable = m_isFolderWritable;
diff --git a/src/views/dolphinview.h b/src/views/dolphinview.h
index a2fe9f62a..13cc66545 100644
--- a/src/views/dolphinview.h
+++ b/src/views/dolphinview.h
@@ -566,6 +566,11 @@ private slots:
void slotModelChanged(KItemModelBase* current, KItemModelBase* previous);
void slotMouseButtonPressed(int itemIndex, Qt::MouseButtons buttons);
+ /*
+ * Is called when new items get pasted or dropped.
+ */
+ void slotAboutToCreate(const KUrl::List& urls);
+
/**
* Emits the signal \a selectionChanged() with a small delay. This is
* because getting all file items for the selection can be an expensive
@@ -722,14 +727,6 @@ private:
QMimeData* selectionMimeData() const;
/**
- * Is invoked after a paste operation or a drag & drop
- * operation and URLs from \a mimeData as selected.
- * This allows to select all newly pasted
- * items in restoreViewState().
- */
- void markPastedUrlsAsSelected(const QMimeData* mimeData);
-
- /**
* Updates m_isFolderWritable dependent on whether the folder represented by
* the current URL is writable. If the state has changed, the signal
* writeableStateChanged() will be emitted.
diff --git a/src/views/draganddrophelper.cpp b/src/views/draganddrophelper.cpp
index f81d4d0bf..f8ae0ad03 100644
--- a/src/views/draganddrophelper.cpp
+++ b/src/views/draganddrophelper.cpp
@@ -28,10 +28,13 @@
#include <QtDBus>
#include <QDropEvent>
-QString DragAndDropHelper::dropUrls(const KFileItem& destItem, const KUrl& destUrl, QDropEvent* event)
+KonqOperations* DragAndDropHelper::dropUrls(const KFileItem& destItem, const KUrl& destUrl, QDropEvent* event, QString& error)
{
+ error.clear();
+
if (!destItem.isNull() && !destItem.isWritable()) {
- return i18nc("@info:status", "Access denied. Could not write to <filename>%1</filename>", destUrl.pathOrUrl());
+ error = i18nc("@info:status", "Access denied. Could not write to <filename>%1</filename>", destUrl.pathOrUrl());
+ return 0;
}
const QMimeData* mimeData = event->mimeData();
@@ -49,15 +52,16 @@ QString DragAndDropHelper::dropUrls(const KFileItem& destItem, const KUrl& destU
const KUrl::List urls = KUrl::List::fromMimeData(event->mimeData());
foreach (const KUrl& url, urls) {
if (url == destUrl) {
- return i18nc("@info:status", "A folder cannot be dropped into itself");
+ error = i18nc("@info:status", "A folder cannot be dropped into itself");
+ return 0;
}
}
- KonqOperations::doDrop(destItem, destUrl, event, QApplication::activeWindow());
+ return KonqOperations::doDrop(destItem, destUrl, event, QApplication::activeWindow(), QList<QAction*>());
} else {
- KonqOperations::doDrop(KFileItem(), destUrl, event, QApplication::activeWindow());
+ return KonqOperations::doDrop(KFileItem(), destUrl, event, QApplication::activeWindow(), QList<QAction*>());
}
- return QString();
+ return 0;
}
diff --git a/src/views/draganddrophelper.h b/src/views/draganddrophelper.h
index ac16f7cf2..eda5fc5c2 100644
--- a/src/views/draganddrophelper.h
+++ b/src/views/draganddrophelper.h
@@ -29,6 +29,7 @@ class KFileItem;
class KUrl;
class QDropEvent;
class QWidget;
+class KonqOperations;
class LIBDOLPHINPRIVATE_EXPORT DragAndDropHelper
{
@@ -46,13 +47,15 @@ public:
* @param destUrl URL of the item destination. Is used only if destItem::isNull()
* is true.
* @param event Drop event.
- * @return Error message intended to be shown for users if dropping is not
+ * @param error Error message intended to be shown for users if dropping is not
* possible. If an empty string is returned, the dropping has been
* successful.
+ * @return KonqOperations pointer
*/
- static QString dropUrls(const KFileItem& destItem,
- const KUrl& destUrl,
- QDropEvent* event);
+ static KonqOperations* dropUrls(const KFileItem& destItem,
+ const KUrl& destUrl,
+ QDropEvent* event,
+ QString& error);
};
#endif
diff --git a/src/views/versioncontrol/updateitemstatesthread.cpp b/src/views/versioncontrol/updateitemstatesthread.cpp
index e07d72c76..fa005f8f1 100644
--- a/src/views/versioncontrol/updateitemstatesthread.cpp
+++ b/src/views/versioncontrol/updateitemstatesthread.cpp
@@ -23,13 +23,13 @@
#include <QMutexLocker>
-UpdateItemStatesThread::UpdateItemStatesThread() :
+UpdateItemStatesThread::UpdateItemStatesThread(KVersionControlPlugin* plugin,
+ const QList<VersionControlObserver::ItemState>& itemStates) :
QThread(),
m_globalPluginMutex(0),
- m_plugin(0),
- m_itemMutex(),
+ m_plugin(plugin),
m_retrievedItems(false),
- m_itemStates()
+ m_itemStates(itemStates)
{
// Several threads may share one instance of a plugin. A global
// mutex is required to serialize the retrieval of version control
@@ -42,32 +42,16 @@ UpdateItemStatesThread::~UpdateItemStatesThread()
{
}
-void UpdateItemStatesThread::setData(KVersionControlPlugin* plugin,
- const QList<VersionControlObserver::ItemState>& itemStates)
-{
- // The locks are taken in the same order as in run()
- // to avoid potential deadlock.
- QMutexLocker pluginLocker(m_globalPluginMutex);
- QMutexLocker itemLocker(&m_itemMutex);
-
- m_itemStates = itemStates;
- m_plugin = plugin;
-}
-
void UpdateItemStatesThread::run()
{
Q_ASSERT(!m_itemStates.isEmpty());
Q_ASSERT(m_plugin);
- QMutexLocker itemLocker(&m_itemMutex);
-
const QString directory = m_itemStates.first().item.url().directory(KUrl::AppendTrailingSlash);
m_retrievedItems = false;
- itemLocker.unlock();
QMutexLocker pluginLocker(m_globalPluginMutex);
if (m_plugin->beginRetrieval(directory)) {
- itemLocker.relock();
const int count = m_itemStates.count();
KVersionControlPlugin2* pluginV2 = qobject_cast<KVersionControlPlugin2*>(m_plugin);
@@ -99,13 +83,11 @@ void UpdateItemStatesThread::unlockPlugin()
QList<VersionControlObserver::ItemState> UpdateItemStatesThread::itemStates() const
{
- QMutexLocker locker(&m_itemMutex);
return m_itemStates;
}
bool UpdateItemStatesThread::retrievedItems() const
{
- QMutexLocker locker(&m_itemMutex);
return m_retrievedItems;
}
diff --git a/src/views/versioncontrol/updateitemstatesthread.h b/src/views/versioncontrol/updateitemstatesthread.h
index f0f91d7d2..a28169755 100644
--- a/src/views/versioncontrol/updateitemstatesthread.h
+++ b/src/views/versioncontrol/updateitemstatesthread.h
@@ -38,9 +38,6 @@ class LIBDOLPHINPRIVATE_EXPORT UpdateItemStatesThread : public QThread
Q_OBJECT
public:
- UpdateItemStatesThread();
- virtual ~UpdateItemStatesThread();
-
/**
* @param plugin Version control plugin that is used to update the
* state of the items. Whenever the plugin is accessed
@@ -49,8 +46,9 @@ public:
* UpdateItemStatesThread::unlockPlugin() must be used.
* @param itemStates List of items, where the states get updated.
*/
- void setData(KVersionControlPlugin* plugin,
- const QList<VersionControlObserver::ItemState>& itemStates);
+ UpdateItemStatesThread(KVersionControlPlugin* plugin,
+ const QList<VersionControlObserver::ItemState>& itemStates);
+ virtual ~UpdateItemStatesThread();
/**
* Whenever the plugin is accessed by the thread creator, lockPlugin() must
@@ -76,7 +74,6 @@ private:
QMutex* m_globalPluginMutex; // Protects the m_plugin globally
KVersionControlPlugin* m_plugin;
- mutable QMutex m_itemMutex; // Protects m_retrievedItems and m_itemStates
bool m_retrievedItems;
QList<VersionControlObserver::ItemState> m_itemStates;
};
diff --git a/src/views/versioncontrol/versioncontrolobserver.cpp b/src/views/versioncontrol/versioncontrolobserver.cpp
index 64bc26867..402a2de54 100644
--- a/src/views/versioncontrol/versioncontrolobserver.cpp
+++ b/src/views/versioncontrol/versioncontrolobserver.cpp
@@ -108,12 +108,7 @@ QList<QAction*> VersionControlObserver::actions(const KFileItemList& items) cons
if (pluginV2) {
// Use version 2 of the KVersionControlPlugin which allows providing actions
// also for non-versioned directories.
- if (m_updateItemStatesThread && m_updateItemStatesThread->lockPlugin()) {
- actions = pluginV2->actions(items);
- m_updateItemStatesThread->unlockPlugin();
- } else {
- actions = pluginV2->actions(items);
- }
+ actions = pluginV2->actions(items);
} else if (isVersioned()) {
// Support deprecated interfaces from KVersionControlPlugin version 1.
// Context menu actions where only available for versioned directories.
@@ -125,14 +120,8 @@ QList<QAction*> VersionControlObserver::actions(const KFileItemList& items) cons
}
}
- if (m_updateItemStatesThread && m_updateItemStatesThread->lockPlugin()) {
- actions = directory.isEmpty() ? m_plugin->contextMenuActions(items)
- : m_plugin->contextMenuActions(directory);
- m_updateItemStatesThread->unlockPlugin();
- } else {
- actions = directory.isEmpty() ? m_plugin->contextMenuActions(items)
- : m_plugin->contextMenuActions(directory);
- }
+ actions = directory.isEmpty() ? m_plugin->contextMenuActions(items)
+ : m_plugin->contextMenuActions(directory);
}
return actions;
@@ -238,20 +227,12 @@ void VersionControlObserver::slotThreadFinished()
void VersionControlObserver::updateItemStates()
{
Q_ASSERT(m_plugin);
- if (!m_updateItemStatesThread) {
- m_updateItemStatesThread = new UpdateItemStatesThread();
- connect(m_updateItemStatesThread, SIGNAL(finished()),
- this, SLOT(slotThreadFinished()));
- connect(m_updateItemStatesThread, SIGNAL(finished()),
- m_updateItemStatesThread, SLOT(deleteLater()));
- }
- else {
+ if (m_updateItemStatesThread) {
// An update is currently ongoing. Wait until the thread has finished
// the update (see slotThreadFinished()).
m_pendingItemStatesUpdate = true;
return;
}
-
QList<ItemState> itemStates;
const int itemCount = m_model->count();
itemStates.reserve(itemCount);
@@ -269,7 +250,12 @@ void VersionControlObserver::updateItemStates()
if (!m_silentUpdate) {
emit infoMessage(i18nc("@info:status", "Updating version information..."));
}
- m_updateItemStatesThread->setData(m_plugin, itemStates);
+ m_updateItemStatesThread = new UpdateItemStatesThread(m_plugin, itemStates);
+ connect(m_updateItemStatesThread, SIGNAL(finished()),
+ this, SLOT(slotThreadFinished()));
+ connect(m_updateItemStatesThread, SIGNAL(finished()),
+ m_updateItemStatesThread, SLOT(deleteLater()));
+
m_updateItemStatesThread->start(); // slotThreadFinished() is called when finished
}
}