]> cloud.milkyroute.net Git - dolphin.git/commitdiff
Merge remote-tracking branch 'origin/KDE/4.11' into KDE/4.12
authorFrank Reininghaus <frank78ac@googlemail.com>
Thu, 14 Nov 2013 08:14:51 +0000 (09:14 +0100)
committerFrank Reininghaus <frank78ac@googlemail.com>
Thu, 14 Nov 2013 08:14:51 +0000 (09:14 +0100)
64 files changed:
src/CMakeLists.txt
src/dolphincontextmenu.cpp
src/dolphindockwidget.cpp
src/dolphinmainwindow.cpp
src/dolphinnewfilemenu.cpp
src/dolphinnewfilemenu.h
src/dolphinpart.cpp
src/dolphinpart.desktop
src/dolphinpart.h
src/dolphinpart_ext.cpp
src/dolphinpart_ext.h
src/dolphinui.rc
src/dolphinviewcontainer.cpp
src/kitemviews/kfileitemlistview.cpp
src/kitemviews/kfileitemlistview.h
src/kitemviews/kfileitemlistwidget.cpp
src/kitemviews/kfileitemlistwidget.h
src/kitemviews/kfileitemmodel.cpp
src/kitemviews/kfileitemmodel.h
src/kitemviews/kfileitemmodelrolesupdater.cpp
src/kitemviews/kfileitemmodelrolesupdater.h
src/kitemviews/kitemlistcontroller.cpp
src/kitemviews/kitemlistcontroller.h
src/kitemviews/kitemlistselectionmanager.cpp
src/kitemviews/kitemlistselectionmanager.h
src/kitemviews/kitemlistview.cpp
src/kitemviews/kitemlistview.h
src/kitemviews/kitemlistviewaccessible.cpp
src/kitemviews/kitemmodelbase.cpp
src/kitemviews/kitemmodelbase.h
src/kitemviews/kitemrange.h [new file with mode: 0644]
src/kitemviews/kitemset.cpp [new file with mode: 0644]
src/kitemviews/kitemset.h [new file with mode: 0644]
src/kitemviews/kstandarditemlistview.cpp
src/kitemviews/kstandarditemlistview.h
src/kitemviews/kstandarditemlistwidget.cpp
src/kitemviews/kstandarditemlistwidget.h
src/kitemviews/kstandarditemmodel.cpp
src/kitemviews/kstandarditemmodel.h
src/kitemviews/private/kdirectorycontentscounter.cpp [new file with mode: 0644]
src/kitemviews/private/kdirectorycontentscounter.h [new file with mode: 0644]
src/kitemviews/private/kdirectorycontentscounterworker.cpp [new file with mode: 0644]
src/kitemviews/private/kdirectorycontentscounterworker.h [new file with mode: 0644]
src/kitemviews/private/kitemlistsizehintresolver.cpp
src/kitemviews/private/kitemlistviewlayouter.cpp
src/kitemviews/private/kitemlistviewlayouter.h
src/panels/places/placesitemmodel.cpp
src/panels/places/placesitemmodel.h
src/settings/kcm/kcmdolphingeneral.desktop
src/settings/kcm/kcmdolphinnavigation.desktop
src/settings/kcm/kcmdolphinservices.desktop
src/settings/kcm/kcmdolphinviewmodes.desktop
src/tests/CMakeLists.txt
src/tests/kfileitemmodelbenchmark.cpp
src/tests/kfileitemmodeltest.cpp
src/tests/kitemlistcontrollertest.cpp
src/tests/kitemlistselectionmanagertest.cpp
src/tests/kitemsettest.cpp [new file with mode: 0644]
src/views/dolphinitemlistview.cpp
src/views/dolphinitemlistview.h
src/views/dolphinnewfilemenuobserver.cpp
src/views/dolphinnewfilemenuobserver.h
src/views/dolphinview.cpp
src/views/dolphinview.h

index 6856991d5ef119d994414edbcfc8cba5c3046114..7710ddf928854b831ebdf1c5f47530a056bc68c6 100644 (file)
@@ -53,11 +53,14 @@ set(dolphinprivate_LIB_SRCS
     kitemviews/kitemlistviewaccessible.cpp
     kitemviews/kitemlistwidget.cpp
     kitemviews/kitemmodelbase.cpp
+    kitemviews/kitemset.cpp
     kitemviews/kstandarditem.cpp
     kitemviews/kstandarditemlistgroupheader.cpp
     kitemviews/kstandarditemlistwidget.cpp
     kitemviews/kstandarditemlistview.cpp
     kitemviews/kstandarditemmodel.cpp
+    kitemviews/private/kdirectorycontentscounter.cpp
+    kitemviews/private/kdirectorycontentscounterworker.cpp
     kitemviews/private/kfileitemclipboard.cpp
     kitemviews/private/kfileitemmodeldirlister.cpp
     kitemviews/private/kfileitemmodelfilter.cpp
@@ -92,6 +95,7 @@ set(dolphinprivate_LIB_SRCS
     views/viewproperties.cpp
     views/zoomlevelinfo.cpp
     dolphinremoveaction.cpp
+    dolphinnewfilemenu.cpp
 )
 
 if(HAVE_NEPOMUK)
index f4b46988619e7bde82c4b6db588b2ce5f60d3386..7d11c3bcdb48d724c68d0c045169119b3fd867aa 100644 (file)
@@ -196,7 +196,7 @@ void DolphinContextMenu::openItemContextMenu()
     if (m_selectedItems.count() == 1) {
         if (m_fileInfo.isDir()) {
             // setup 'Create New' menu
-            DolphinNewFileMenu* newFileMenu = new DolphinNewFileMenu(m_mainWindow);
+            DolphinNewFileMenu* newFileMenu = new DolphinNewFileMenu(m_mainWindow->actionCollection(), this);
             const DolphinView* view = m_mainWindow->activeViewContainer()->view();
             newFileMenu->setViewShowsHiddenFiles(view->hiddenFilesShown());
             newFileMenu->checkUpToDate();
index 0d8aea7bdf24ad7c398554042898106188a109d7..6495c8da963bef6431061bb34e0ab9e2f4bec3fe 100644 (file)
 
 #include <QStyle>
 
+namespace {
+    // Disable the 'Floatable' feature, i.e., the possibility to drag the
+    // dock widget out of the main window. This works around problems like
+    // https://bugs.kde.org/show_bug.cgi?id=288629
+    // https://bugs.kde.org/show_bug.cgi?id=322299
+    const QDockWidget::DockWidgetFeatures DefaultDockWidgetFeatures = QDockWidget::DockWidgetMovable | QDockWidget::DockWidgetClosable;
+}
+
  // Empty titlebar for the dock widgets when "Lock Layout" has been activated.
 class DolphinDockTitleBar : public QWidget
 {
@@ -45,6 +53,7 @@ DolphinDockWidget::DolphinDockWidget(const QString& title, QWidget* parent, Qt::
     m_locked(false),
     m_dockTitleBar(0)
 {
+    setFeatures(DefaultDockWidgetFeatures);
 }
 
 DolphinDockWidget::DolphinDockWidget(QWidget* parent, Qt::WindowFlags flags) :
@@ -52,6 +61,7 @@ DolphinDockWidget::DolphinDockWidget(QWidget* parent, Qt::WindowFlags flags) :
     m_locked(false),
     m_dockTitleBar(0)
 {
+    setFeatures(DefaultDockWidgetFeatures);
 }
 
 DolphinDockWidget::~DolphinDockWidget()
@@ -71,9 +81,7 @@ void DolphinDockWidget::setLocked(bool lock)
             setFeatures(QDockWidget::NoDockWidgetFeatures);
         } else {
             setTitleBarWidget(0);
-            setFeatures(QDockWidget::DockWidgetMovable |
-                        QDockWidget::DockWidgetFloatable |
-                        QDockWidget::DockWidgetClosable);
+            setFeatures(DefaultDockWidgetFeatures);
         }
     }
 }
index b477600f04b9e753ba1d5536d189b2e1571366ec..a4dbb6f341c02404baf3dc5d41e037689cae8446 100644 (file)
@@ -35,6 +35,7 @@
 #include "views/dolphinremoteencoding.h"
 #include "views/draganddrophelper.h"
 #include "views/viewproperties.h"
+#include "views/dolphinnewfilemenuobserver.h"
 
 #ifndef Q_OS_WIN
 #include "panels/terminal/terminalpanel.h"
@@ -126,6 +127,9 @@ DolphinMainWindow::DolphinMainWindow() :
     ViewTab& viewTab = m_viewTab[m_tabIndex];
     viewTab.wasActive = true; // The first opened tab is automatically active
 
+    connect(&DolphinNewFileMenuObserver::instance(), SIGNAL(errorMessage(QString)),
+            this, SLOT(showErrorMessage(QString)));
+
     KIO::FileUndoManager* undoManager = KIO::FileUndoManager::self();
     undoManager->setUiInterface(new UndoUiInterface());
 
@@ -254,12 +258,12 @@ void DolphinMainWindow::openDirectories(const QList<KUrl>& dirs)
 
     // Open each directory inside a new tab. If the "split view" option has been enabled,
     // always show two directories within one tab.
-    QList<KUrl>::const_iterator it = dirs.begin();
-    while (it != dirs.end()) {
+    QList<KUrl>::const_iterator it = dirs.constBegin();
+    while (it != dirs.constEnd()) {
         openNewTab(*it);
         ++it;
 
-        if (hasSplitView && (it != dirs.end())) {
+        if (hasSplitView && (it != dirs.constEnd())) {
             const int tabIndex = m_viewTab.count() - 1;
             m_viewTab[tabIndex].secondaryView->setUrl(*it);
             ++it;
@@ -1468,7 +1472,7 @@ DolphinViewContainer* DolphinMainWindow::createViewContainer(const KUrl& url, QW
 void DolphinMainWindow::setupActions()
 {
     // setup 'File' menu
-    m_newFileMenu = new DolphinNewFileMenu(this);
+    m_newFileMenu = new DolphinNewFileMenu(actionCollection(), this);
     KMenu* menu = m_newFileMenu->menu();
     menu->setTitle(i18nc("@title:menu Create new folder, file, link, etc.", "Create New"));
     menu->setIcon(KIcon("document-new"));
index 9d9baabe2b6911fee5c0c3debba615e6fb242f1c..da57ca946f34a2c6eb4d235359c87cb3afb5f7a8 100644 (file)
 
 #include "dolphinnewfilemenu.h"
 
-#include "dolphinmainwindow.h"
-#include "dolphinviewcontainer.h"
 #include "views/dolphinnewfilemenuobserver.h"
-#include "views/dolphinview.h"
 
 #include <KActionCollection>
 #include <KIO/Job>
 
-DolphinNewFileMenu::DolphinNewFileMenu(DolphinMainWindow* parent) :
-    KNewFileMenu(parent->actionCollection(), "create_new", parent),
-    m_mainWin(parent)
+DolphinNewFileMenu::DolphinNewFileMenu(KActionCollection* collection, QObject* parent) :
+    KNewFileMenu(collection, "new_menu", parent)
 {
     DolphinNewFileMenuObserver::instance().attach(this);
 }
@@ -43,8 +39,7 @@ DolphinNewFileMenu::~DolphinNewFileMenu()
 void DolphinNewFileMenu::slotResult(KJob* job)
 {
     if (job->error()) {
-        DolphinViewContainer* container = m_mainWin->activeViewContainer();
-        container->showMessage(job->errorString(), DolphinViewContainer::Error);
+        emit errorMessage(job->errorString());
     } else {
         KNewFileMenu::slotResult(job);
     }
index 0d336080baac061724739a625fd346d45b6fd228..e211dfd881319a7495be258485ad1d16f410c94e 100644 (file)
@@ -23,7 +23,8 @@
 
 #include <KNewFileMenu>
 
-class DolphinMainWindow;
+#include "libdolphin_export.h"
+
 class KJob;
 
 /**
@@ -34,20 +35,20 @@ class KJob;
  * All errors are shown in the status bar of Dolphin
  * instead as modal error dialog with an OK button.
  */
-class DolphinNewFileMenu : public KNewFileMenu
+class LIBDOLPHINPRIVATE_EXPORT DolphinNewFileMenu : public KNewFileMenu
 {
     Q_OBJECT
 
 public:
-    DolphinNewFileMenu(DolphinMainWindow* parent);
+    DolphinNewFileMenu(KActionCollection* collection, QObject* parent);
     virtual ~DolphinNewFileMenu();
 
+signals:
+    void errorMessage(const QString& error);
+
 protected slots:
     /** @see KNewFileMenu::slotResult() */
     virtual void slotResult(KJob* job);
-
-private:
-    DolphinMainWindow* m_mainWin;
 };
 
 #endif
index 81fbacb77519e32bd57a66918ef818060d8c997e..90817319333dea9ed3bddf25e2b40ad97e926562 100644 (file)
@@ -37,7 +37,6 @@
 #include <KIO/NetAccess>
 #include <KToolInvocation>
 #include <kauthorized.h>
-#include <KNewFileMenu>
 #include <KMenu>
 #include <KInputDialog>
 #include <KProtocolInfo>
@@ -47,6 +46,7 @@
 #include "dolphinpart_ext.h"
 #endif
 
+#include "dolphinnewfilemenu.h"
 #include "views/dolphinview.h"
 #include "views/dolphinviewactionhandler.h"
 #include "views/dolphinnewfilemenuobserver.h"
@@ -79,6 +79,9 @@ DolphinPart::DolphinPart(QWidget* parentWidget, QObject* parent, const QVariantL
     m_view->setTabsForFilesEnabled(true);
     setWidget(m_view);
 
+    connect(&DolphinNewFileMenuObserver::instance(), SIGNAL(errorMessage(QString)),
+            this, SLOT(slotErrorMessage(QString)));
+
     connect(m_view, SIGNAL(directoryLoadingCompleted()), this, SIGNAL(completed()));
     connect(m_view, SIGNAL(directoryLoadingProgress(int)), this, SLOT(updateProgress(int)));
     connect(m_view, SIGNAL(errorMessage(QString)), this, SLOT(slotErrorMessage(QString)));
@@ -160,16 +163,14 @@ DolphinPart::DolphinPart(QWidget* parentWidget, QObject* parent, const QVariantL
 
 DolphinPart::~DolphinPart()
 {
-    DolphinNewFileMenuObserver::instance().detach(m_newFileMenu);
 }
 
 void DolphinPart::createActions()
 {
     // Edit menu
 
-    m_newFileMenu = new KNewFileMenu(actionCollection(), "new_menu", this);
+    m_newFileMenu = new DolphinNewFileMenu(actionCollection(), this);
     m_newFileMenu->setParentWidget(widget());
-    DolphinNewFileMenuObserver::instance().attach(m_newFileMenu);
     connect(m_newFileMenu->menu(), SIGNAL(aboutToShow()),
             this, SLOT(updateNewMenu()));
 
@@ -572,7 +573,8 @@ void DolphinPart::updateNewMenu()
 
 void DolphinPart::updateStatusBar()
 {
-    emit ReadOnlyPart::setStatusBarText(m_view->statusBarText());
+    const QString escapedText = Qt::convertFromPlainText(m_view->statusBarText());
+    emit ReadOnlyPart::setStatusBarText(QString("<qt>%1</qt>").arg(escapedText));
 }
 
 void DolphinPart::updateProgress(int percent)
@@ -614,89 +616,4 @@ bool DolphinPart::eventFilter(QObject* obj, QEvent* event)
     return KParts::ReadOnlyPart::eventFilter(obj, event);
 }
 
-////
-
-void DolphinPartBrowserExtension::restoreState(QDataStream &stream)
-{
-    KParts::BrowserExtension::restoreState(stream);
-    m_part->view()->restoreState(stream);
-}
-
-void DolphinPartBrowserExtension::saveState(QDataStream &stream)
-{
-    KParts::BrowserExtension::saveState(stream);
-    m_part->view()->saveState(stream);
-}
-
-void DolphinPartBrowserExtension::cut()
-{
-    m_part->view()->cutSelectedItems();
-}
-
-void DolphinPartBrowserExtension::copy()
-{
-    m_part->view()->copySelectedItems();
-}
-
-void DolphinPartBrowserExtension::paste()
-{
-    m_part->view()->paste();
-}
-
-void DolphinPartBrowserExtension::pasteTo(const KUrl&)
-{
-    m_part->view()->pasteIntoFolder();
-}
-
-void DolphinPartBrowserExtension::reparseConfiguration()
-{
-    m_part->view()->readSettings();
-}
-
-////
-
-DolphinPartFileInfoExtension::DolphinPartFileInfoExtension(DolphinPart* part)
-                             : KParts::FileInfoExtension(part)
-{
-}
-
-DolphinPart* DolphinPartFileInfoExtension::part() const
-{
-    return static_cast<DolphinPart*>(parent());
-}
-
-bool DolphinPartFileInfoExtension::hasSelection() const
-{
-    return part()->view()->selectedItemsCount() > 0;
-}
-
-KParts::FileInfoExtension::QueryModes DolphinPartFileInfoExtension::supportedQueryModes() const
-{
-    return (KParts::FileInfoExtension::AllItems | KParts::FileInfoExtension::SelectedItems);
-}
-
-KFileItemList DolphinPartFileInfoExtension::queryFor(KParts::FileInfoExtension::QueryMode mode) const
-{
-    KFileItemList list;
-
-    if (mode == KParts::FileInfoExtension::None)
-      return list;
-
-    if (!(supportedQueryModes() & mode))
-      return list;
-
-    switch (mode) {
-      case KParts::FileInfoExtension::SelectedItems:
-          if (hasSelection())
-              return part()->view()->selectedItems();
-          break;
-      case KParts::FileInfoExtension::AllItems:
-          return part()->view()->items();
-      default:
-          break;
-    }
-
-    return list;
-}
-
 #include "dolphinpart.moc"
index a553034a2cb74c83a91a4c479ac73d7fe2dde623..ac6607cc07fdd9fb318aa6279c61fa0f088b17f2 100644 (file)
@@ -196,6 +196,7 @@ Exec=dolphin
 [Desktop Action compact]
 Name=Compact
 Name[ar]=مضغوط
+Name[bg]=Компактно
 Name[bs]=Sabij
 Name[ca]=Compacte
 Name[ca@valencia]=Compacte
@@ -323,7 +324,7 @@ Name[te]=వివరాలు
 Name[tg]=Тафсилотҳо
 Name[th]=รายละเอียด
 Name[tr]=Ayrıntılar
-Name[ug]=تÛ\95پسÙ\89Ù\84اتÙ\89
+Name[ug]=تÛ\95پسÙ\89Ù\84اتÙ\84ار
 Name[uk]=Подробиці
 Name[uz]=Tafsilotlar
 Name[uz@cyrillic]=Тафсилотлар
index 172bfafc6d14b0dac5c7a5bc3b327902d9b43bbd..7146b46af375aadefbb4300f4cda950159e9e4c5 100644 (file)
 #define DOLPHINPART_H
 
 #include <kparts/part.h>
-#include <kparts/browserextension.h>
-#include <kparts/fileinfoextension.h>
 
 #include <QItemSelectionModel>
 
-class KNewFileMenu;
+class DolphinNewFileMenu;
 class DolphinViewActionHandler;
 class QActionGroup;
 class KAction;
@@ -244,7 +242,7 @@ private:
     DolphinViewActionHandler* m_actionHandler;
     DolphinRemoteEncoding* m_remoteEncoding;
     DolphinPartBrowserExtension* m_extension;
-    KNewFileMenu* m_newFileMenu;
+    DolphinNewFileMenu* m_newFileMenu;
     KAction* m_findFileAction;
     KAction* m_openTerminalAction;
     QString m_nameFilter;
@@ -252,41 +250,4 @@ private:
     Q_DISABLE_COPY(DolphinPart)
 };
 
-class DolphinPartBrowserExtension : public KParts::BrowserExtension
-{
-    Q_OBJECT
-public:
-    DolphinPartBrowserExtension( DolphinPart* part )
-        : KParts::BrowserExtension( part ), m_part(part) {}
-
-    virtual void restoreState(QDataStream &stream);
-    virtual void saveState(QDataStream &stream);
-
-public Q_SLOTS:
-    void cut();
-    void copy();
-    void paste();
-    void pasteTo(const KUrl&);
-    void reparseConfiguration();
-
-private:
-    DolphinPart* m_part;
-};
-
-
-class DolphinPartFileInfoExtension : public KParts::FileInfoExtension
-{
-    Q_OBJECT
-
-public:
-    DolphinPartFileInfoExtension(DolphinPart* part);
-
-    virtual QueryModes supportedQueryModes() const;
-    virtual bool hasSelection() const;
-
-    virtual KFileItemList queryFor(QueryMode mode) const;
-protected:
-    DolphinPart* part() const;
-};
-
 #endif /* DOLPHINPART_H */
index e98c0648ed612d6b0f9107d15a730dc73dffd8e8..fb7a4d2eaf6f0ca50406793449d076ea1833d4dc 100644 (file)
 
 #include <KFileItemList>
 
+
+DolphinPartBrowserExtension::DolphinPartBrowserExtension(DolphinPart* part)
+    :KParts::BrowserExtension( part )
+    ,m_part(part)
+{
+
+}
+
+void DolphinPartBrowserExtension::restoreState(QDataStream &stream)
+{
+    KParts::BrowserExtension::restoreState(stream);
+    m_part->view()->restoreState(stream);
+}
+
+void DolphinPartBrowserExtension::saveState(QDataStream &stream)
+{
+    KParts::BrowserExtension::saveState(stream);
+    m_part->view()->saveState(stream);
+}
+
+void DolphinPartBrowserExtension::cut()
+{
+    m_part->view()->cutSelectedItems();
+}
+
+void DolphinPartBrowserExtension::copy()
+{
+    m_part->view()->copySelectedItems();
+}
+
+void DolphinPartBrowserExtension::paste()
+{
+    m_part->view()->paste();
+}
+
+void DolphinPartBrowserExtension::pasteTo(const KUrl&)
+{
+    m_part->view()->pasteIntoFolder();
+}
+
+void DolphinPartBrowserExtension::reparseConfiguration()
+{
+    m_part->view()->readSettings();
+}
+
+
+DolphinPartFileInfoExtension::DolphinPartFileInfoExtension(DolphinPart* part)
+    :KParts::FileInfoExtension(part)
+    ,m_part(part)
+{
+}
+
+bool DolphinPartFileInfoExtension::hasSelection() const
+{
+    return m_part->view()->selectedItemsCount() > 0;
+}
+
+KParts::FileInfoExtension::QueryModes DolphinPartFileInfoExtension::supportedQueryModes() const
+{
+    return (KParts::FileInfoExtension::AllItems | KParts::FileInfoExtension::SelectedItems);
+}
+
+KFileItemList DolphinPartFileInfoExtension::queryFor(KParts::FileInfoExtension::QueryMode mode) const
+{
+    KFileItemList list;
+
+    if (mode == KParts::FileInfoExtension::None)
+      return list;
+
+    if (!(supportedQueryModes() & mode))
+      return list;
+
+    switch (mode) {
+      case KParts::FileInfoExtension::SelectedItems:
+          if (hasSelection())
+              return m_part->view()->selectedItems();
+          break;
+      case KParts::FileInfoExtension::AllItems:
+          return m_part->view()->items();
+      default:
+          break;
+    }
+
+    return list;
+}
+
 DolphinPartListingFilterExtension::DolphinPartListingFilterExtension(DolphinPart* part)
     : KParts::ListingFilterExtension(part)
     , m_part(part)
index 423e79efedbd11568194887e4017c32e6ff95398..c05962cd97bca5273b529b5501f34bd2eab9b85e 100644 (file)
 #ifndef DOLPHINPART_EXT_H
 #define DOLPHINPART_EXT_H
 
-
+#include <kparts/browserextension.h>
+#include <kparts/fileinfoextension.h>
 #include <kparts/listingextension.h>
 
 class DolphinPart;
 
+class DolphinPartBrowserExtension : public KParts::BrowserExtension
+{
+    Q_OBJECT
+public:
+    DolphinPartBrowserExtension( DolphinPart* part );
+    virtual void restoreState(QDataStream &stream);
+    virtual void saveState(QDataStream &stream);
+
+public Q_SLOTS:
+    void cut();
+    void copy();
+    void paste();
+    void pasteTo(const KUrl&);
+    void reparseConfiguration();
+
+private:
+    DolphinPart* m_part;
+};
+
+class DolphinPartFileInfoExtension : public KParts::FileInfoExtension
+{
+    Q_OBJECT
+
+public:
+    DolphinPartFileInfoExtension(DolphinPart* part);
+
+    virtual QueryModes supportedQueryModes() const;
+    virtual bool hasSelection() const;
+
+    virtual KFileItemList queryFor(QueryMode mode) const;
+
+private:
+    DolphinPart* m_part;
+};
+
 class DolphinPartListingFilterExtension : public KParts::ListingFilterExtension
 {
     Q_OBJECT
index 68e03752f5708e199ac24692d3d4001af015eeaf..52826bb43a1a7556ddca433262caaab8b0235924 100644 (file)
@@ -2,7 +2,7 @@
 <kpartgui name="dolphin" version="14">
     <MenuBar>
         <Menu name="file">
-            <Action name="create_new" />
+            <Action name="new_menu" />
             <Action name="new_window" />
             <Action name="new_tab" />
             <Action name="close_tab" />
index e7c571294be20f9ae13681be58a1de1f36762e62..cc26198a0f4f333bbae4078c641fcbe5fcb6feea 100644 (file)
@@ -508,8 +508,7 @@ void DolphinViewContainer::showItemInfo(const KFileItem& item)
     if (item.isNull()) {
         m_statusBar->resetToDefaultText();
     } else {
-        const QString text = item.isDir() ? item.text() : item.getStatusBarInfo();
-        m_statusBar->setText(text);
+        m_statusBar->setText(item.getStatusBarInfo());
     }
 }
 
index 1ffd5019b48dd9ac4719d3bbc12a2460279164bb..fd01f2c4c23565827f7ed6b1e4aaf0e3b99f141e 100644 (file)
@@ -119,7 +119,7 @@ QStringList KFileItemListView::enabledPlugins() const
     return m_modelRolesUpdater ? m_modelRolesUpdater->enabledPlugins() : QStringList();
 }
 
-QPixmap KFileItemListView::createDragPixmap(const QSet<int>& indexes) const
+QPixmap KFileItemListView::createDragPixmap(const KItemSet& indexes) const
 {
     if (!model()) {
         return QPixmap();
@@ -165,10 +165,8 @@ QPixmap KFileItemListView::createDragPixmap(const QSet<int>& indexes) const
     QPainter painter(&dragPixmap);
     int x = 0;
     int y = 0;
-    QSetIterator<int> it(indexes);
-    while (it.hasNext()) {
-        const int index = it.next();
 
+    foreach (int index, indexes) {
         QPixmap pixmap = model()->data(index).value("iconPixmap").value<QPixmap>();
         if (pixmap.isNull()) {
             KIcon icon(model()->data(index).value("iconName").toString());
@@ -221,12 +219,6 @@ void KFileItemListView::onPreviewsShownChanged(bool shown)
 
 void KFileItemListView::onItemLayoutChanged(ItemLayout current, ItemLayout previous)
 {
-    if (previous == DetailsLayout || current == DetailsLayout) {
-        // The details-layout requires some invisible roles that
-        // must be added to the model if the new layout is "details".
-        // If the old layout was "details" the roles will get removed.
-        applyRolesToModel();
-    }
     KStandardItemListView::onItemLayoutChanged(current, previous);
     triggerVisibleIndexRangeUpdate();
 }
index bdc63b01bdb21bd8d6c8105f76ff9c3f81743e2a..2fd21edbfdda12e0c5e7b045f3774eb510191731 100644 (file)
@@ -73,7 +73,7 @@ public:
     QStringList enabledPlugins() const;
 
     /** @reimp */
-    virtual QPixmap createDragPixmap(const QSet<int>& indexes) const;
+    virtual QPixmap createDragPixmap(const KItemSet& indexes) const;
 
 protected:
     virtual KItemListWidgetCreatorBase* defaultWidgetCreator() const;
index 3a7724134373a9e21c935d996515cd6b3aea8a33..688a4da083d0c5b15022f2c246d4e9cac5a6a542 100644 (file)
@@ -18,6 +18,8 @@
  ***************************************************************************/
 
 #include "kfileitemlistwidget.h"
+#include "kfileitemmodel.h"
+#include "kitemlistview.h"
 
 #include <kmimetype.h>
 #include <KDebug>
@@ -35,6 +37,15 @@ KFileItemListWidgetInformant::~KFileItemListWidgetInformant()
 {
 }
 
+QString KFileItemListWidgetInformant::itemText(int index, const KItemListView* view) const
+{
+    Q_ASSERT(qobject_cast<KFileItemModel*>(view->model()));
+    KFileItemModel* fileItemModel = static_cast<KFileItemModel*>(view->model());
+
+    const KFileItem item = fileItemModel->fileItem(index);
+    return item.text();
+}
+
 QString KFileItemListWidgetInformant::roleText(const QByteArray& role,
                                                const QHash<QByteArray, QVariant>& values) const
 {
index 24c6778281773823122836a16830a1153bc2f7af..1d7bc7f012d7be878a9b2ff8e3e98845c9e9b272 100644 (file)
@@ -31,6 +31,7 @@ public:
     virtual ~KFileItemListWidgetInformant();
 
 protected:
+    virtual QString itemText(int index, const KItemListView* view) const;
     virtual QString roleText(const QByteArray& role, const QHash<QByteArray, QVariant>& values) const;
 };
 
index 7b7c39ad74b643ae6c7ecc9867cdd8ffef46d05d..4c8577543fdd3bb897a7d2e1c8034409617d0bcd 100644 (file)
@@ -152,7 +152,12 @@ int KFileItemModel::count() const
 QHash<QByteArray, QVariant> KFileItemModel::data(int index) const
 {
     if (index >= 0 && index < count()) {
-        return m_itemData.at(index)->values;
+        ItemData* data = m_itemData.at(index);
+        if (data->values.isEmpty()) {
+            data->values = retrieveData(data->item, data->parent);
+        }
+
+        return data->values;
     }
     return QHash<QByteArray, QVariant>();
 }
@@ -163,7 +168,7 @@ bool KFileItemModel::setData(int index, const QHash<QByteArray, QVariant>& value
         return false;
     }
 
-    QHash<QByteArray, QVariant> currentValues = m_itemData.at(index)->values;
+    QHash<QByteArray, QVariant> currentValues = data(index);
 
     // Determine which roles have been changed
     QSet<QByteArray> changedRoles;
@@ -232,7 +237,7 @@ bool KFileItemModel::showDirectoriesOnly() const
     return m_dirLister->dirOnlyMode();
 }
 
-QMimeData* KFileItemModel::createMimeData(const QSet<int>& indexes) const
+QMimeData* KFileItemModel::createMimeData(const KItemSet& indexes) const
 {
     QMimeData* data = new QMimeData();
 
@@ -243,9 +248,7 @@ QMimeData* KFileItemModel::createMimeData(const QSet<int>& indexes) const
     KUrl::List mostLocalUrls;
     bool canUseMostLocalUrls = true;
 
-    QSetIterator<int> it(indexes);
-    while (it.hasNext()) {
-        const int index = it.next();
+    foreach (int index, indexes) {
         const KFileItem item = fileItem(index);
         if (!item.isNull()) {
             urls << item.targetUrl();
@@ -274,12 +277,12 @@ int KFileItemModel::indexForKeyboardSearch(const QString& text, int startFromInd
 {
     startFromIndex = qMax(0, startFromIndex);
     for (int i = startFromIndex; i < count(); ++i) {
-        if (data(i)["text"].toString().startsWith(text, Qt::CaseInsensitive)) {
+        if (fileItem(i).text().startsWith(text, Qt::CaseInsensitive)) {
             return i;
         }
     }
     for (int i = 0; i < startFromIndex; ++i) {
-        if (data(i)["text"].toString().startsWith(text, Qt::CaseInsensitive)) {
+        if (fileItem(i).text().startsWith(text, Qt::CaseInsensitive)) {
             return i;
         }
     }
@@ -435,15 +438,37 @@ bool KFileItemModel::setExpanded(int index, bool expanded)
     if (expanded) {
         m_expandedDirs.insert(targetUrl, url);
         m_dirLister->openUrl(url, KDirLister::Keep);
+
+        const KUrl::List previouslyExpandedChildren = m_itemData.at(index)->values.value("previouslyExpandedChildren").value<KUrl::List>();
+        foreach (const KUrl& url, previouslyExpandedChildren) {
+            m_urlsToExpand.insert(url);
+        }
     } else {
         m_expandedDirs.remove(targetUrl);
         m_dirLister->stop(url);
 
-        removeFilteredChildren(KFileItemList() << item);
+        const int parentLevel = expandedParentsCount(index);
+        const int itemCount = m_itemData.count();
+        const int firstChildIndex = index + 1;
+
+        KUrl::List expandedChildren;
+
+        int childIndex = firstChildIndex;
+        while (childIndex < itemCount && expandedParentsCount(childIndex) > parentLevel) {
+            ItemData* itemData = m_itemData.at(childIndex);
+            if (itemData->values.value("isExpanded").toBool()) {
+                const KUrl targetUrl = itemData->item.targetUrl();
+                m_expandedDirs.remove(targetUrl);
+                expandedChildren.append(targetUrl);
+            }
+            ++childIndex;
+        }
+        const int childrenCount = childIndex - firstChildIndex;
 
-        const KFileItemList itemsToRemove = childItems(item);
-        removeFilteredChildren(itemsToRemove);
-        removeItems(itemsToRemove, DeleteItemData);
+        removeFilteredChildren(KItemRangeList() << KItemRange(index, 1 + childrenCount));
+        removeItems(KItemRangeList() << KItemRange(firstChildIndex, childrenCount), DeleteItemData);
+
+        m_itemData.at(index)->values.insert("previouslyExpandedChildren", expandedChildren);
     }
 
     return true;
@@ -460,7 +485,9 @@ bool KFileItemModel::isExpanded(int index) const
 bool KFileItemModel::isExpandable(int index) const
 {
     if (index >= 0 && index < count()) {
-        return m_itemData.at(index)->values.value("isExpandable").toBool();
+        // Call data (instead of accessing m_itemData directly)
+        // to ensure that the value is initialized.
+        return data(index).value("isExpandable").toBool();
     }
     return false;
 }
@@ -468,10 +495,7 @@ bool KFileItemModel::isExpandable(int index) const
 int KFileItemModel::expandedParentsCount(int index) const
 {
     if (index >= 0 && index < count()) {
-        const int parentsCount = m_itemData.at(index)->values.value("expandedParentsCount").toInt();
-        if (parentsCount > 0) {
-            return parentsCount;
-        }
+        return expandedParentsCount(m_itemData.at(index));
     }
     return 0;
 }
@@ -547,21 +571,25 @@ void KFileItemModel::applyFilters()
 {
     // Check which shown items from m_itemData must get
     // hidden and hence moved to m_filteredItems.
-    KFileItemList newFilteredItems;
+    QVector<int> newFilteredIndexes;
+
+    const int itemCount = m_itemData.count();
+    for (int index = 0; index < itemCount; ++index) {
+        ItemData* itemData = m_itemData.at(index);
 
-    foreach (ItemData* itemData, m_itemData) {
         // Only filter non-expanded items as child items may never
         // exist without a parent item
         if (!itemData->values.value("isExpanded").toBool()) {
             const KFileItem item = itemData->item;
             if (!m_filter.matches(item)) {
-                newFilteredItems.append(item);
+                newFilteredIndexes.append(index);
                 m_filteredItems.insert(item, itemData);
             }
         }
     }
 
-    removeItems(newFilteredItems, KeepItemData);
+    const KItemRangeList removedRanges = KItemRangeList::fromSortedContainer(newFilteredIndexes);
+    removeItems(removedRanges, KeepItemData);
 
     // Check which hidden items from m_filteredItems should
     // get visible again and hence removed from m_filteredItems.
@@ -580,22 +608,24 @@ void KFileItemModel::applyFilters()
     insertItems(newVisibleItems);
 }
 
-void KFileItemModel::removeFilteredChildren(const KFileItemList& parentsList)
+void KFileItemModel::removeFilteredChildren(const KItemRangeList& itemRanges)
 {
-    if (m_filteredItems.isEmpty()) {
+    if (m_filteredItems.isEmpty() || !m_requestRole[ExpandedParentsCountRole]) {
+        // There are either no filtered items, or it is not possible to expand
+        // folders -> there cannot be any filtered children.
         return;
     }
 
-    // First, we put the parent items into a set to provide fast lookup
-    // while iterating over m_filteredItems and prevent quadratic
-    // complexity if there are N parents and N filtered items.
-    const QSet<KFileItem> parents = parentsList.toSet();
+    QSet<ItemData*> parents;
+    foreach (const KItemRange& range, itemRanges) {
+        for (int index = range.index; index < range.index + range.count; ++index) {
+            parents.insert(m_itemData.at(index));
+        }
+    }
 
     QHash<KFileItem, ItemData*>::iterator it = m_filteredItems.begin();
     while (it != m_filteredItems.end()) {
-        const ItemData* parent = it.value()->parent;
-
-        if (parent && parents.contains(parent->item)) {
+        if (parents.contains(it.value()->parent)) {
             delete it.value();
             it = m_filteredItems.erase(it);
         } else {
@@ -685,7 +715,6 @@ void KFileItemModel::resortAllItems()
         oldUrls.append(itemData->item.url());
     }
 
-    m_groups.clear();
     m_items.clear();
 
     // Resort the items
@@ -694,20 +723,45 @@ void KFileItemModel::resortAllItems()
         m_items.insert(m_itemData.at(i)->item.url(), i);
     }
 
-    // Determine the indexes that have been moved
-    QList<int> movedToIndexes;
-    movedToIndexes.reserve(itemCount);
-    for (int i = 0; i < itemCount; i++) {
-        const int newIndex = m_items.value(oldUrls.at(i));
-        movedToIndexes.append(newIndex);
+    // Determine the first index that has been moved.
+    int firstMovedIndex = 0;
+    while (firstMovedIndex < itemCount
+           && firstMovedIndex == m_items.value(oldUrls.at(firstMovedIndex))) {
+        ++firstMovedIndex;
     }
 
-    // Don't check whether items have really been moved and always emit a
-    // itemsMoved() signal after resorting: In case of grouped items
-    // the groups might change even if the items themselves don't change their
-    // position. Let the receiver of the signal decide whether a check for moved
-    // items makes sense.
-    emit itemsMoved(KItemRange(0, itemCount), movedToIndexes);
+    const bool itemsHaveMoved = firstMovedIndex < itemCount;
+    if (itemsHaveMoved) {
+        m_groups.clear();
+
+        int lastMovedIndex = itemCount - 1;
+        while (lastMovedIndex > firstMovedIndex
+               && lastMovedIndex == m_items.value(oldUrls.at(lastMovedIndex))) {
+            --lastMovedIndex;
+        }
+
+        Q_ASSERT(firstMovedIndex <= lastMovedIndex);
+
+        // Create a list movedToIndexes, which has the property that
+        // movedToIndexes[i] is the new index of the item with the old index
+        // firstMovedIndex + i.
+        const int movedItemsCount = lastMovedIndex - firstMovedIndex + 1;
+        QList<int> movedToIndexes;
+        movedToIndexes.reserve(movedItemsCount);
+        for (int i = firstMovedIndex; i <= lastMovedIndex; ++i) {
+            const int newIndex = m_items.value(oldUrls.at(i));
+            movedToIndexes.append(newIndex);
+        }
+
+        emit itemsMoved(KItemRange(firstMovedIndex, movedItemsCount), movedToIndexes);
+    } else if (groupedSorting()) {
+        // The groups might have changed even if the order of the items has not.
+        const QList<QPair<int, QVariant> > oldGroups = m_groups;
+        m_groups.clear();
+        if (groups() != oldGroups) {
+            emit groupsChanged();
+        }
+    }
 
 #ifdef KFILEITEMMODEL_DEBUG
     kDebug() << "[TIME] Resorting of" << itemCount << "items:" << timer.elapsed();
@@ -821,29 +875,49 @@ void KFileItemModel::slotItemsDeleted(const KFileItemList& items)
 {
     dispatchPendingItemsToInsert();
 
-    KFileItemList itemsToRemove = items;
-    if (m_requestRole[ExpandedParentsCountRole]) {
-        // Assure that removing a parent item also results in removing all children
-        foreach (const KFileItem& item, items) {
-            itemsToRemove.append(childItems(item));
-        }
-    }
+    QVector<int> indexesToRemove;
+    indexesToRemove.reserve(items.count());
 
-    if (!m_filteredItems.isEmpty()) {
-        foreach (const KFileItem& item, itemsToRemove) {
+    foreach (const KFileItem& item, items) {
+        const KUrl url = item.url();
+        const int index = m_items.value(url, -1);
+        if (index >= 0) {
+            indexesToRemove.append(index);
+        } else {
+            // Probably the item has been filtered.
             QHash<KFileItem, ItemData*>::iterator it = m_filteredItems.find(item);
             if (it != m_filteredItems.end()) {
                 delete it.value();
                 m_filteredItems.erase(it);
             }
         }
+    }
+
+    std::sort(indexesToRemove.begin(), indexesToRemove.end());
 
-        if (m_requestRole[ExpandedParentsCountRole]) {
-            removeFilteredChildren(itemsToRemove);
+    if (m_requestRole[ExpandedParentsCountRole] && !m_expandedDirs.isEmpty()) {
+        // Assure that removing a parent item also results in removing all children
+        QVector<int> indexesToRemoveWithChildren;
+        indexesToRemoveWithChildren.reserve(m_items.count());
+
+        const int itemCount = m_itemData.count();
+        foreach (int index, indexesToRemove) {
+            indexesToRemoveWithChildren.append(index);
+
+            const int parentLevel = expandedParentsCount(index);
+            int childIndex = index + 1;
+            while (childIndex < itemCount && expandedParentsCount(childIndex) > parentLevel) {
+                indexesToRemoveWithChildren.append(childIndex);
+                ++childIndex;
+            }
         }
+
+        indexesToRemove = indexesToRemoveWithChildren;
     }
 
-    removeItems(itemsToRemove, DeleteItemData);
+    const KItemRangeList itemRanges = KItemRangeList::fromSortedContainer(indexesToRemove);
+    removeFilteredChildren(itemRanges);
+    removeItems(itemRanges, DeleteItemData);
 }
 
 void KFileItemModel::slotRefreshItems(const QList<QPair<KFileItem, KFileItem> >& items)
@@ -895,30 +969,7 @@ void KFileItemModel::slotRefreshItems(const QList<QPair<KFileItem, KFileItem> >&
 
     // Extract the item-ranges out of the changed indexes
     qSort(indexes);
-
-    KItemRangeList itemRangeList;
-    int previousIndex = indexes.at(0);
-    int rangeIndex = previousIndex;
-    int rangeCount = 1;
-
-    const int maxIndex = indexes.count() - 1;
-    for (int i = 1; i <= maxIndex; ++i) {
-        const int currentIndex = indexes.at(i);
-        if (currentIndex == previousIndex + 1) {
-            ++rangeCount;
-        } else {
-            itemRangeList.append(KItemRange(rangeIndex, rangeCount));
-
-            rangeIndex = currentIndex;
-            rangeCount = 1;
-        }
-        previousIndex = currentIndex;
-    }
-
-    if (rangeCount > 0) {
-        itemRangeList.append(KItemRange(rangeIndex, rangeCount));
-    }
-
+    const KItemRangeList itemRangeList = KItemRangeList::fromSortedContainer(indexes);
     emitItemsChangedAndTriggerResorting(itemRangeList, changedRoles);
 }
 
@@ -983,6 +1034,14 @@ void KFileItemModel::insertItems(QList<ItemData*>& newItems)
 
     m_groups.clear();
 
+    if (m_sortRole == NameRole && m_naturalSorting) {
+        // Natural sorting of items can be very slow. However, it becomes much
+        // faster if the input sequence is already mostly sorted. Therefore, we
+        // first sort 'newItems' according to the QStrings returned by
+        // KFileItem::text() using QString::operator<(), which is quite fast.
+        parallelMergeSort(newItems.begin(), newItems.end(), nameLessThan, QThread::idealThreadCount());
+    }
+
     sort(newItems.begin(), newItems.end());
 
 #ifdef KFILEITEMMODEL_DEBUG
@@ -1056,53 +1115,21 @@ void KFileItemModel::insertItems(QList<ItemData*>& newItems)
 #endif
 }
 
-static KItemRangeList sortedIndexesToKItemRangeList(const QList<int>& sortedNumbers)
+void KFileItemModel::removeItems(const KItemRangeList& itemRanges, RemoveItemsBehavior behavior)
 {
-    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;
+    if (itemRanges.isEmpty()) {
+        return;
     }
 
-    result << KItemRange(index, count);
-    return result;
-}
-
-void KFileItemModel::removeItems(const KFileItemList& items, RemoveItemsBehavior behavior)
-{
-#ifdef KFILEITEMMODEL_DEBUG
-    kDebug() << "Removing " << items.count() << "items";
-#endif
-
     m_groups.clear();
 
-    // 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 KUrl url = item.url();
-        const int index = m_items.value(url, -1);
-        if (index >= 0) {
-            indexesToRemove.append(index);
+    // Step 1: Remove the items from the hash m_items, and free the ItemData.
+    int removedItemsCount = 0;
+    foreach (const KItemRange& range, itemRanges) {
+        removedItemsCount += range.count;
+
+        for (int index = range.index; index < range.index + range.count; ++index) {
+            const KUrl url = m_itemData.at(index)->item.url();
 
             // Prevent repeated expensive rehashing by using QHash::erase(),
             // rather than QHash::remove().
@@ -1117,14 +1144,7 @@ void KFileItemModel::removeItems(const KFileItemList& items, RemoveItemsBehavior
         }
     }
 
-    if (indexesToRemove.isEmpty()) {
-        return;
-    }
-
-    std::sort(indexesToRemove.begin(), indexesToRemove.end());
-
     // 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;
@@ -1142,7 +1162,7 @@ void KFileItemModel::removeItems(const KFileItemList& items, RemoveItemsBehavior
         }
     }
 
-    m_itemData.erase(m_itemData.end() - indexesToRemove.count(), m_itemData.end());
+    m_itemData.erase(m_itemData.end() - removedItemsCount, m_itemData.end());
 
     // Step 3: Adjust indexes in the hash m_items, starting from the
     //         index of the first removed item.
@@ -1172,30 +1192,75 @@ QList<KFileItemModel::ItemData*> KFileItemModel::createItemDataList(const KUrl&
     foreach (const KFileItem& item, items) {
         ItemData* itemData = new ItemData();
         itemData->item = item;
-        itemData->values = retrieveData(item, parentItem);
         itemData->parent = parentItem;
         itemDataList.append(itemData);
     }
 
+    switch (m_sortRole) {
+    case PermissionsRole:
+    case OwnerRole:
+    case GroupRole:
+    case DestinationRole:
+    case PathRole:
+        // These roles can be determined with retrieveData, and they have to be stored
+        // in the QHash "values" for the sorting.
+        foreach (ItemData* itemData, itemDataList) {
+            itemData->values = retrieveData(itemData->item, parentItem);
+        }
+        break;
+
+    case TypeRole:
+        // At least store the data including the file type for items with known MIME type.
+        foreach (ItemData* itemData, itemDataList) {
+            const KFileItem item = itemData->item;
+            if (item.isDir() || item.isMimeTypeKnown()) {
+                itemData->values = retrieveData(itemData->item, parentItem);
+            }
+        }
+        break;
+
+    default:
+        // The other roles are either resolved by KFileItemModelRolesUpdater
+        // (this includes the SizeRole for directories), or they do not need
+        // to be stored in the QHash "values" for sorting because the data can
+        // be retrieved directly from the KFileItem (NameRole, SiezRole for files,
+        // DateRole).
+        break;
+    }
+
     return itemDataList;
 }
 
+int KFileItemModel::expandedParentsCount(const ItemData* data)
+{
+    // The hash 'values' is only guaranteed to contain the key "expandedParentsCount"
+    // if the corresponding item is expanded, and it is not a top-level item.
+    const ItemData* parent = data->parent;
+    if (parent) {
+        if (parent->parent) {
+            Q_ASSERT(parent->values.contains("expandedParentsCount"));
+            return parent->values.value("expandedParentsCount").toInt() + 1;
+        } else {
+            return 1;
+        }
+    } else {
+        return 0;
+    }
+}
+
 void KFileItemModel::removeExpandedItems()
 {
-    KFileItemList expandedItems;
+    QVector<int> indexesToRemove;
 
     const int maxIndex = m_itemData.count() - 1;
     for (int i = 0; i <= maxIndex; ++i) {
         const ItemData* itemData = m_itemData.at(i);
-        if (itemData->values.value("expandedParentsCount").toInt() > 0) {
-            expandedItems.append(itemData->item);
+        if (itemData->parent) {
+            indexesToRemove.append(i);
         }
     }
 
-    // The m_expandedParentsCountRoot may not get reset before all items with
-    // a bigger count have been removed.
-    removeItems(expandedItems, DeleteItemData);
-
+    removeItems(KItemRangeList::fromSortedContainer(indexesToRemove), DeleteItemData);
     m_expandedDirs.clear();
 
     // Also remove all filtered items which have a parent.
@@ -1405,7 +1470,7 @@ QHash<QByteArray, QVariant> KFileItemModel::retrieveData(const KFileItem& item,
 
     if (m_requestRole[ExpandedParentsCountRole]) {
         if (parent) {
-            const int level = parent->values["expandedParentsCount"].toInt() + 1;
+            const int level = expandedParentsCount(parent) + 1;
             data.insert(sharedValue("expandedParentsCount"), level);
         }
     }
@@ -1429,8 +1494,8 @@ bool KFileItemModel::lessThan(const ItemData* a, const ItemData* b) const
     int result = 0;
 
     if (a->parent != b->parent) {
-        const int expansionLevelA = a->values.value("expandedParentsCount").toInt();
-        const int expansionLevelB = b->values.value("expandedParentsCount").toInt();
+        const int expansionLevelA = expandedParentsCount(a);
+        const int expansionLevelB = expandedParentsCount(b);
 
         // 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.
@@ -1450,7 +1515,7 @@ bool KFileItemModel::lessThan(const ItemData* a, const ItemData* b) const
             a = a->parent;
         }
 
-        Q_ASSERT(a->values.value("expandedParentsCount").toInt() == b->values.value("expandedParentsCount").toInt());
+        Q_ASSERT(expandedParentsCount(a) == expandedParentsCount(b));
 
         // Compare the last parents of a and b which are different.
         while (a->parent != b->parent) {
@@ -1660,7 +1725,7 @@ QList<QPair<int, QVariant> > KFileItemModel::nameRoleGroups() const
             continue;
         }
 
-        const QString name = m_itemData.at(i)->values.value("text").toString();
+        const QString name = m_itemData.at(i)->item.text();
 
         // Use the first character of the name as group indication
         QChar newFirstChar = name.at(0).toUpper();
@@ -1943,23 +2008,6 @@ QList<QPair<int, QVariant> > KFileItemModel::genericStringRoleGroups(const QByte
     return groups;
 }
 
-KFileItemList KFileItemModel::childItems(const KFileItem& item) const
-{
-    KFileItemList items;
-
-    int index = m_items.value(item.url(), -1);
-    if (index >= 0) {
-        const int parentLevel = m_itemData.at(index)->values.value("expandedParentsCount").toInt();
-        ++index;
-        while (index < m_itemData.count() && m_itemData.at(index)->values.value("expandedParentsCount").toInt() > parentLevel) {
-            items.append(m_itemData.at(index)->item);
-            ++index;
-        }
-    }
-
-    return items;
-}
-
 void KFileItemModel::emitSortProgress(int resolvedCount)
 {
     // Be tolerant against a resolvedCount with a wrong range.
@@ -2086,7 +2134,7 @@ bool KFileItemModel::isConsistent() const
         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) {
+            if (expandedParentsCount(data) != expandedParentsCount(parent) + 1) {
                 qWarning() << "expandedParentsCount is inconsistent for parent" << parent->item << "and child" << data->item;
                 return false;
             }
index c87ee9736d3812b5a5296b097efeec3da02df106..c57329fc85051b153f0430e327bc2cdc1dccfded 100644 (file)
@@ -27,6 +27,7 @@
 #include <kitemviews/private/kfileitemmodelfilter.h>
 
 #include <QHash>
+#include <QSet>
 
 class KFileItemModelDirLister;
 class QTimer;
@@ -99,7 +100,7 @@ public:
     bool showDirectoriesOnly() const;
 
     /** @reimp */
-    virtual QMimeData* createMimeData(const QSet<int>& indexes) const;
+    virtual QMimeData* createMimeData(const KItemSet& indexes) const;
 
     /** @reimp */
     virtual int indexForKeyboardSearch(const QString& text, int startFromIndex = 0) const;
@@ -309,7 +310,7 @@ private:
     };
 
     void insertItems(QList<ItemData*>& items);
-    void removeItems(const KFileItemList& items, RemoveItemsBehavior behavior);
+    void removeItems(const KItemRangeList& itemRanges, RemoveItemsBehavior behavior);
 
     /**
      * Helper method for insertItems() and removeItems(): Creates
@@ -319,6 +320,8 @@ private:
      */
     QList<ItemData*> createItemDataList(const KUrl& parentUrl, const KFileItemList& items) const;
 
+    static int expandedParentsCount(const ItemData* data);
+
     void removeExpandedItems();
 
     /**
@@ -347,6 +350,12 @@ private:
 
     QHash<QByteArray, QVariant> retrieveData(const KFileItem& item, const ItemData* parent) const;
 
+    /**
+     * @return True if \a a has a KFileItem whose text is 'less than' the one
+     *         of \a b according to QString::operator<(const QString&).
+     */
+    static bool nameLessThan(const ItemData* a, const ItemData* b);
+
     /**
      * @return True if the item-data \a a should be ordered before the item-data
      *         \b. The item-data may have different parent-items.
@@ -387,11 +396,6 @@ private:
      */
     bool isChildItem(int index) const;
 
-    /**
-     * @return Recursive list of child items that have \a item as upper most parent.
-     */
-    KFileItemList childItems(const KFileItem& item) const;
-
     /**
      * Is invoked by KFileItemModelRolesUpdater and results in emitting the
      * sortProgress signal with a percent-value of the progress.
@@ -407,7 +411,7 @@ private:
      * Removes filtered items whose expanded parents have been deleted
      * or collapsed via setExpanded(parentIndex, false).
      */
-    void removeFilteredChildren(const KFileItemList& parentsList);
+    void removeFilteredChildren(const KItemRangeList& parents);
 
     /**
      * Maps the QByteArray-roles to RoleTypes and provides translation- and
@@ -488,9 +492,19 @@ private:
     friend class DolphinPart;                  // Accesses m_dirLister
 };
 
+inline bool KFileItemModel::nameLessThan(const ItemData* a, const ItemData* b)
+{
+    return a->item.text() < b->item.text();
+}
+
+
 inline bool KFileItemModel::isChildItem(int index) const
 {
-    return m_requestRole[ExpandedParentsCountRole] && m_itemData.at(index)->values.value("expandedParentsCount").toInt() > 0;
+    if (m_itemData.at(index)->parent) {
+        return true;
+    } else {
+        return false;
+    }
 }
 
 #endif
index e9b69566d5b542ddb5234961b32bbd1652b85b61..d6445c981f72dcf5a0aa45851d169b056feff14b 100644 (file)
 #include <KConfig>
 #include <KConfigGroup>
 #include <KDebug>
-#include <KDirWatch>
 #include <KFileItem>
 #include <KGlobal>
 #include <KIO/JobUiDelegate>
 #include <KIO/PreviewJob>
 
 #include "private/kpixmapmodifier.h"
+#include "private/kdirectorycontentscounter.h"
 
 #include <QApplication>
 #include <QPainter>
     #include <Nepomuk2/ResourceManager>
 #endif
 
-// Required includes for subItemsCount():
-#ifdef Q_WS_WIN
-    #include <QDir>
-#else
-    #include <dirent.h>
-    #include <QFile>
-#endif
-
 // #define KFILEITEMMODELROLESUPDATER_DEBUG
 
 namespace {
@@ -95,8 +87,7 @@ KFileItemModelRolesUpdater::KFileItemModelRolesUpdater(KFileItemModel* model, QO
     m_recentlyChangedItemsTimer(0),
     m_recentlyChangedItems(),
     m_changedItems(),
-    m_dirWatcher(0),
-    m_watchedDirs()
+    m_directoryContentsCounter(0)
   #ifdef HAVE_NEPOMUK
   , m_nepomukResourceWatcher(0),
     m_nepomukUriItems()
@@ -135,10 +126,9 @@ KFileItemModelRolesUpdater::KFileItemModelRolesUpdater(KFileItemModel* model, QO
     m_resolvableRoles += KNepomukRolesProvider::instance().roles();
 #endif
 
-    // When folders are expandable or the item-count is shown for folders, it is necessary
-    // to watch the number of items of the sub-folder to be able to react on changes.
-    m_dirWatcher = new KDirWatch(this);
-    connect(m_dirWatcher, SIGNAL(dirty(QString)), this, SLOT(slotDirWatchDirty(QString)));
+    m_directoryContentsCounter = new KDirectoryContentsCounter(m_model, this);
+    connect(m_directoryContentsCounter, SIGNAL(result(QString,int)),
+            this,                       SLOT(slotDirectoryContentsCountReceived(QString,int)));
 }
 
 KFileItemModelRolesUpdater::~KFileItemModelRolesUpdater()
@@ -367,25 +357,6 @@ void KFileItemModelRolesUpdater::slotItemsRemoved(const KItemRangeList& itemRang
 
     const bool allItemsRemoved = (m_model->count() == 0);
 
-    if (!m_watchedDirs.isEmpty()) {
-        // Don't let KDirWatch watch for removed items
-        if (allItemsRemoved) {
-            foreach (const QString& path, m_watchedDirs) {
-                m_dirWatcher->removeDir(path);
-            }
-            m_watchedDirs.clear();
-        } else {
-            QMutableSetIterator<QString> it(m_watchedDirs);
-            while (it.hasNext()) {
-                const QString& path = it.next();
-                if (m_model->index(KUrl(path)) < 0) {
-                    m_dirWatcher->removeDir(path);
-                    it.remove();
-                }
-            }
-        }
-    }
-
 #ifdef HAVE_NEPOMUK
     if (m_nepomukResourceWatcher) {
         // Don't let the ResourceWatcher watch for removed items
@@ -783,7 +754,7 @@ void KFileItemModelRolesUpdater::applyChangedNepomukRoles(const Nepomuk2::Resour
 #endif
 }
 
-void KFileItemModelRolesUpdater::slotDirWatchDirty(const QString& path)
+void KFileItemModelRolesUpdater::slotDirectoryContentsCountReceived(const QString& path, int count)
 {
     const bool getSizeRole = m_roles.contains("size");
     const bool getIsExpandableRole = m_roles.contains("isExpandable");
@@ -791,16 +762,9 @@ void KFileItemModelRolesUpdater::slotDirWatchDirty(const QString& path)
     if (getSizeRole || getIsExpandableRole) {
         const int index = m_model->index(KUrl(path));
         if (index >= 0) {
-            if (!m_model->fileItem(index).isDir()) {
-                // If INotify is used, KDirWatch issues the dirty() signal
-                // also for changed files inside the directory, even if we
-                // don't enable this behavior explicitly (see bug 309740).
-                return;
-            }
 
             QHash<QByteArray, QVariant> data;
 
-            const int count = subItemsCount(path);
             if (getSizeRole) {
                 data.insert("size", count);
             }
@@ -808,9 +772,11 @@ void KFileItemModelRolesUpdater::slotDirWatchDirty(const QString& path)
                 data.insert("isExpandable", count > 0);
             }
 
-            // Note that we do not block the itemsChanged signal here.
-            // This ensures that a new preview will be generated.
+            disconnect(m_model, SIGNAL(itemsChanged(KItemRangeList,QSet<QByteArray>)),
+                       this,    SLOT(slotItemsChanged(KItemRangeList,QSet<QByteArray>)));
             m_model->setData(index, data);
+            connect(m_model, SIGNAL(itemsChanged(KItemRangeList,QSet<QByteArray>)),
+                    this,    SLOT(slotItemsChanged(KItemRangeList,QSet<QByteArray>)));
         }
     }
 }
@@ -1037,7 +1003,7 @@ void KFileItemModelRolesUpdater::applySortRole(int index)
         data.insert("type", item.mimeComment());
     } else if (m_model->sortRole() == "size" && item.isLocalFile() && item.isDir()) {
         const QString path = item.localPath();
-        data.insert("size", subItemsCount(path));
+        data.insert("size", m_directoryContentsCounter->countDirectoryContentsSynchronously(path));
     } else {
         // Probably the sort role is a Nepomuk role - just determine all roles.
         data = rolesData(item);
@@ -1105,7 +1071,7 @@ bool KFileItemModelRolesUpdater::applyResolvedRoles(const KFileItem& item, Resol
     return false;
 }
 
-QHash<QByteArray, QVariant> KFileItemModelRolesUpdater::rolesData(const KFileItem& item) const
+QHash<QByteArray, QVariant> KFileItemModelRolesUpdater::rolesData(const KFileItem& item)
 {
     QHash<QByteArray, QVariant> data;
 
@@ -1114,19 +1080,10 @@ QHash<QByteArray, QVariant> KFileItemModelRolesUpdater::rolesData(const KFileIte
 
     if ((getSizeRole || getIsExpandableRole) && item.isDir()) {
         if (item.isLocalFile()) {
+            // Tell m_directoryContentsCounter that we want to count the items
+            // inside the directory. The result will be received in slotDirectoryContentsCountReceived.
             const QString path = item.localPath();
-            const int count = subItemsCount(path);
-            if (getSizeRole) {
-                data.insert("size", count);
-            }
-            if (getIsExpandableRole) {
-                data.insert("isExpandable", count > 0);
-            }
-
-            if (!m_dirWatcher->contains(path)) {
-                m_dirWatcher->addDir(path);
-                m_watchedDirs.insert(path);
-            }
+            m_directoryContentsCounter->addDirectory(path);
         } else if (getSizeRole) {
             data.insert("size", -1); // -1 indicates an unknown number of items
         }
@@ -1170,61 +1127,6 @@ QHash<QByteArray, QVariant> KFileItemModelRolesUpdater::rolesData(const KFileIte
     return data;
 }
 
-int KFileItemModelRolesUpdater::subItemsCount(const QString& path) const
-{
-    const bool countHiddenFiles = m_model->showHiddenFiles();
-    const bool showFoldersOnly  = m_model->showDirectoriesOnly();
-
-#ifdef Q_WS_WIN
-    QDir dir(path);
-    QDir::Filters filters = QDir::NoDotAndDotDot | QDir::System;
-    if (countHiddenFiles) {
-        filters |= QDir::Hidden;
-    }
-    if (showFoldersOnly) {
-        filters |= QDir::Dirs;
-    } else {
-        filters |= QDir::AllEntries;
-    }
-    return dir.entryList(filters).count();
-#else
-    // Taken from kdelibs/kio/kio/kdirmodel.cpp
-    // Copyright (C) 2006 David Faure <faure@kde.org>
-
-    int count = -1;
-    DIR* dir = ::opendir(QFile::encodeName(path));
-    if (dir) {  // krazy:exclude=syscalls
-        count = 0;
-        struct dirent *dirEntry = 0;
-        while ((dirEntry = ::readdir(dir))) {
-            if (dirEntry->d_name[0] == '.') {
-                if (dirEntry->d_name[1] == '\0' || !countHiddenFiles) {
-                    // Skip "." or hidden files
-                    continue;
-                }
-                if (dirEntry->d_name[1] == '.' && dirEntry->d_name[2] == '\0') {
-                    // Skip ".."
-                    continue;
-                }
-            }
-
-            // If only directories are counted, consider an unknown file type and links also
-            // as directory instead of trying to do an expensive stat()
-            // (see bugs 292642 and 299997).
-            const bool countEntry = !showFoldersOnly ||
-                                    dirEntry->d_type == DT_DIR ||
-                                    dirEntry->d_type == DT_LNK ||
-                                    dirEntry->d_type == DT_UNKNOWN;
-            if (countEntry) {
-                ++count;
-            }
-        }
-        ::closedir(dir);
-    }
-    return count;
-#endif
-}
-
 void KFileItemModelRolesUpdater::updateAllPreviews()
 {
     if (m_state == Paused) {
index 409f098e8cd60df6e18bf241e230c25c15ba69a0..fced44a85da6be095899c01c7db50253a5b0e082 100644 (file)
@@ -32,7 +32,7 @@
 #include <QSize>
 #include <QStringList>
 
-class KDirWatch;
+class KDirectoryContentsCounter;
 class KFileItemModel;
 class KJob;
 class QPixmap;
@@ -218,12 +218,7 @@ private slots:
 
     void applyChangedNepomukRoles(const Nepomuk2::Resource& resource, const Nepomuk2::Types::Property& property);
 
-    /**
-     * Is invoked if a directory watched by KDirWatch got dirty. Updates
-     * the "isExpandable"- and "size"-roles of the item that matches to
-     * the given path.
-     */
-    void slotDirWatchDirty(const QString& path);
+    void slotDirectoryContentsCountReceived(const QString& path, int count);
 
 private:
     /**
@@ -267,7 +262,7 @@ private:
         ResolveAll
     };
     bool applyResolvedRoles(const KFileItem& item, ResolveHint hint);
-    QHash<QByteArray, QVariant> rolesData(const KFileItem& item) const;
+    QHash<QByteArray, QVariant> rolesData(const KFileItem& item);
 
     /**
      * @return The number of items of the path \a path.
@@ -349,9 +344,8 @@ private:
     // Items which have not been changed repeatedly recently.
     QSet<KFileItem> m_changedItems;
 
-    KDirWatch* m_dirWatcher;
-    mutable QSet<QString> m_watchedDirs; // Required as sadly KDirWatch does not offer a getter method
-                                         // to get all watched directories.
+    KDirectoryContentsCounter* m_directoryContentsCounter;
+
 #ifdef HAVE_NEPOMUK
     Nepomuk2::ResourceWatcher* m_nepomukResourceWatcher;
     mutable QHash<QUrl, KUrl> m_nepomukUriItems;
index 158c12de1d38deb475efc9c295e730f6f18bf611..7344b9960f07ec72eb7f47d6a76caa66b063d75c 100644 (file)
@@ -364,11 +364,11 @@ bool KItemListController::keyPressEvent(QKeyEvent* event)
 
     case Qt::Key_Enter:
     case Qt::Key_Return: {
-        const QSet<int> selectedItems = m_selectionManager->selectedItems();
+        const KItemSet selectedItems = m_selectionManager->selectedItems();
         if (selectedItems.count() >= 2) {
             emit itemsActivated(selectedItems);
         } else if (selectedItems.count() == 1) {
-            emit itemActivated(selectedItems.toList().first());
+            emit itemActivated(selectedItems.first());
         } else {
             emit itemActivated(index);
         }
@@ -378,14 +378,14 @@ bool KItemListController::keyPressEvent(QKeyEvent* event)
     case Qt::Key_Menu: {
         // Emit the signal itemContextMenuRequested() in case if at least one
         // item is selected. Otherwise the signal viewContextMenuRequested() will be emitted.
-        const QSet<int> selectedItems = m_selectionManager->selectedItems();
+        const KItemSet selectedItems = m_selectionManager->selectedItems();
         int index = -1;
         if (selectedItems.count() >= 2) {
             const int currentItemIndex = m_selectionManager->currentItem();
             index = selectedItems.contains(currentItemIndex)
-                    ? currentItemIndex : selectedItems.toList().first();
+                    ? currentItemIndex : selectedItems.first();
         } else if (selectedItems.count() == 1) {
-            index = selectedItems.toList().first();
+            index = selectedItems.first();
         }
 
         if (index >= 0) {
@@ -403,6 +403,7 @@ bool KItemListController::keyPressEvent(QKeyEvent* event)
             m_selectionManager->clearSelection();
         }
         m_keyboardManager->cancelSearch();
+        emit escapePressed();
         break;
 
     case Qt::Key_Space:
@@ -1086,7 +1087,7 @@ void KItemListController::slotRubberBandChanged()
         }
     }
 
-    QSet<int> selectedItems;
+    KItemSet selectedItems;
 
     // Select all visible items that intersect with the rubberband
     foreach (const KItemListWidget* widget, m_view->visibleItemListWidgets()) {
@@ -1134,7 +1135,7 @@ void KItemListController::slotRubberBandChanged()
         // Therefore, the new selection contains:
         // 1. All previously selected items which are not inside the rubberband, and
         // 2. all items inside the rubberband which have not been selected previously.
-        m_selectionManager->setSelectedItems((m_oldSelection - selectedItems) + (selectedItems - m_oldSelection));
+        m_selectionManager->setSelectedItems(m_oldSelection ^ selectedItems);
     }
     else {
         m_selectionManager->setSelectedItems(selectedItems + m_oldSelection);
@@ -1147,7 +1148,7 @@ void KItemListController::startDragging()
         return;
     }
 
-    const QSet<int> selectedItems = m_selectionManager->selectedItems();
+    const KItemSet selectedItems = m_selectionManager->selectedItems();
     if (selectedItems.isEmpty()) {
         return;
     }
index 4d5fee3450fa8e391dc881f1878e057e863290b0..e9b70cddaf7dfc46659b5b37434b68a1f02394bf 100644 (file)
 
 #include <libdolphin_export.h>
 
+#include "kitemset.h"
+
 #include <QObject>
 #include <QPixmap>
 #include <QPointF>
-#include <QSet>
 
 class KItemModelBase;
 class KItemListKeyboardSearchManager;
@@ -129,7 +130,7 @@ public:
 
     /**
      * If set to true, the signals itemActivated() and itemsActivated() are emitted
-     * after a single-click of the left mouse button. If set to false (the default), 
+     * after a single-click of the left mouse button. If set to false (the default),
      * the setting from KGlobalSettings::singleClick() is used.
      */
     void setSingleClickActivationEnforced(bool singleClick);
@@ -165,7 +166,7 @@ signals:
      * Is emitted if more than one item has been activated by pressing Return/Enter
      * when having a selection.
      */
-    void itemsActivated(const QSet<int>& indexes);
+    void itemsActivated(const KItemSet& indexes);
 
     void itemMiddleClicked(int index);
 
@@ -230,6 +231,11 @@ signals:
      */
     void aboveItemDropEvent(int index, QGraphicsSceneDragDropEvent* event);
 
+    /**
+     * Is emitted if the Escape key is pressed.
+     */
+    void escapePressed();
+
     void modelChanged(KItemModelBase* current, KItemModelBase* previous);
     void viewChanged(KItemListView* current, KItemListView* previous);
 
@@ -321,7 +327,7 @@ private:
      * the current selection it is remembered in m_oldSelection before the
      * rubberband gets activated.
      */
-    QSet<int> m_oldSelection;
+    KItemSet m_oldSelection;
 
     /**
      * Assuming a view is given with a vertical scroll-orientation, grouped items and
index 833f7aad07f6d0e985f6fe6dfa59f5a5c8c0daf9..ebff1a30ed4604db4759bd40e6d06cacabd450ba 100644 (file)
@@ -43,7 +43,7 @@ KItemListSelectionManager::~KItemListSelectionManager()
 void KItemListSelectionManager::setCurrentItem(int current)
 {
     const int previous = m_currentItem;
-    const QSet<int> previousSelection = selectedItems();
+    const KItemSet previousSelection = selectedItems();
 
     if (m_model && current >= 0 && current < m_model->count()) {
         m_currentItem = current;
@@ -55,7 +55,7 @@ void KItemListSelectionManager::setCurrentItem(int current)
         emit currentChanged(m_currentItem, previous);
 
         if (m_isAnchoredSelectionActive) {
-            const QSet<int> selection = selectedItems();
+            const KItemSet selection = selectedItems();
             if (selection != previousSelection) {
                 emit selectionChanged(selection, previousSelection);
             }
@@ -68,18 +68,18 @@ int KItemListSelectionManager::currentItem() const
     return m_currentItem;
 }
 
-void KItemListSelectionManager::setSelectedItems(const QSet<int>& items)
+void KItemListSelectionManager::setSelectedItems(const KItemSet& items)
 {
     if (m_selectedItems != items) {
-        const QSet<int> previous = m_selectedItems;
+        const KItemSet previous = m_selectedItems;
         m_selectedItems = items;
         emit selectionChanged(m_selectedItems, previous);
     }
 }
 
-QSet<int> KItemListSelectionManager::selectedItems() const
+KItemSet KItemListSelectionManager::selectedItems() const
 {
-    QSet<int> selectedItems = m_selectedItems;
+    KItemSet selectedItems = m_selectedItems;
 
     if (m_isAnchoredSelectionActive && m_anchorItem != m_currentItem) {
         Q_ASSERT(m_anchorItem >= 0);
@@ -127,7 +127,7 @@ void KItemListSelectionManager::setSelected(int index, int count, SelectionMode
     }
 
     endAnchoredSelection();
-    const QSet<int> previous = selectedItems();
+    const KItemSet previous = selectedItems();
 
     count = qMin(count, m_model->count() - index);
 
@@ -160,7 +160,7 @@ void KItemListSelectionManager::setSelected(int index, int count, SelectionMode
         break;
     }
 
-    const QSet<int> selection = selectedItems();
+    const KItemSet selection = selectedItems();
     if (selection != previous) {
         emit selectionChanged(selection, previous);
     }
@@ -168,11 +168,11 @@ void KItemListSelectionManager::setSelected(int index, int count, SelectionMode
 
 void KItemListSelectionManager::clearSelection()
 {
-    const QSet<int> previous = selectedItems();
+    const KItemSet previous = selectedItems();
     if (!previous.isEmpty()) {
         m_selectedItems.clear();
         m_isAnchoredSelectionActive = false;
-        emit selectionChanged(QSet<int>(), previous);
+        emit selectionChanged(KItemSet(), previous);
     }
 }
 
@@ -221,7 +221,7 @@ void KItemListSelectionManager::setModel(KItemModelBase* model)
 void KItemListSelectionManager::itemsInserted(const KItemRangeList& itemRanges)
 {
     // Store the current selection (needed in the selectionChanged() signal)
-    const QSet<int> previousSelection = selectedItems();
+    const KItemSet previousSelection = selectedItems();
 
     // Update the current item
     if (m_currentItem < 0) {
@@ -257,12 +257,10 @@ void KItemListSelectionManager::itemsInserted(const KItemRangeList& itemRanges)
 
     // Update the selections
     if (!m_selectedItems.isEmpty()) {
-        const QSet<int> previous = m_selectedItems;
+        const KItemSet previous = m_selectedItems;
         m_selectedItems.clear();
-        m_selectedItems.reserve(previous.count());
-        QSetIterator<int> it(previous);
-        while (it.hasNext()) {
-            const int index = it.next();
+
+        foreach (int index, previous) {
             int inc = 0;
             foreach (const KItemRange& itemRange, itemRanges) {
                 if (index < itemRange.index) {
@@ -274,7 +272,7 @@ void KItemListSelectionManager::itemsInserted(const KItemRangeList& itemRanges)
         }
     }
 
-    const QSet<int> selection = selectedItems();
+    const KItemSet selection = selectedItems();
     if (selection != previousSelection) {
         emit selectionChanged(selection, previousSelection);
     }
@@ -283,7 +281,7 @@ void KItemListSelectionManager::itemsInserted(const KItemRangeList& itemRanges)
 void KItemListSelectionManager::itemsRemoved(const KItemRangeList& itemRanges)
 {
     // Store the current selection (needed in the selectionChanged() signal)
-    const QSet<int> previousSelection = selectedItems();
+    const KItemSet previousSelection = selectedItems();
     const int previousCurrent = m_currentItem;
 
     // Update the current item
@@ -308,19 +306,18 @@ void KItemListSelectionManager::itemsRemoved(const KItemRangeList& itemRanges)
 
     // Update the selections and the anchor item
     if (!m_selectedItems.isEmpty()) {
-        const QSet<int> previous = m_selectedItems;
+        const KItemSet previous = m_selectedItems;
         m_selectedItems.clear();
-        m_selectedItems.reserve(previous.count());
-        QSetIterator<int> it(previous);
-        while (it.hasNext()) {
-            const int index = indexAfterRangesRemoving(it.next(), itemRanges, DiscardRemovedIndex);
+
+        foreach (int oldIndex, previous) {
+            const int index = indexAfterRangesRemoving(oldIndex, itemRanges, DiscardRemovedIndex);
             if (index >= 0)  {
                 m_selectedItems.insert(index);
             }
         }
     }
 
-    const QSet<int> selection = selectedItems();
+    const KItemSet selection = selectedItems();
     if (selection != previousSelection) {
         emit selectionChanged(selection, previousSelection);
     }
@@ -332,7 +329,7 @@ void KItemListSelectionManager::itemsRemoved(const KItemRangeList& itemRanges)
 void KItemListSelectionManager::itemsMoved(const KItemRange& itemRange, const QList<int>& movedToIndexes)
 {
     // Store the current selection (needed in the selectionChanged() signal)
-    const QSet<int> previousSelection = selectedItems();
+    const KItemSet previousSelection = selectedItems();
 
     // Update the current item
     if (m_currentItem >= itemRange.index && m_currentItem < itemRange.index + itemRange.count) {
@@ -352,12 +349,10 @@ void KItemListSelectionManager::itemsMoved(const KItemRange& itemRange, const QL
 
     // Update the selections
     if (!m_selectedItems.isEmpty()) {
-        const QSet<int> previous = m_selectedItems;
+        const KItemSet previous = m_selectedItems;
         m_selectedItems.clear();
-        m_selectedItems.reserve(previous.count());
-        QSetIterator<int> it(previous);
-        while (it.hasNext()) {
-            const int index = it.next();
+
+        foreach (int index, previous) {
             if (index >= itemRange.index && index < itemRange.index + itemRange.count) {
                 m_selectedItems.insert(movedToIndexes.at(index - itemRange.index));
             }
@@ -367,7 +362,7 @@ void KItemListSelectionManager::itemsMoved(const KItemRange& itemRange, const QL
         }
     }
 
-    const QSet<int> selection = selectedItems();
+    const KItemSet selection = selectedItems();
     if (selection != previousSelection) {
         emit selectionChanged(selection, previousSelection);
     }
index c89b8a4b85916655e48a7853eb889849ec2c8e16..c4decd39eb3d07e49058d73b6b30259c843a77e2 100644 (file)
@@ -26,9 +26,9 @@
 #include <libdolphin_export.h>
 
 #include <kitemviews/kitemmodelbase.h>
+#include <kitemviews/kitemset.h>
 
 #include <QObject>
-#include <QSet>
 
 class KItemModelBase;
 
@@ -57,8 +57,8 @@ public:
     void setCurrentItem(int current);
     int currentItem() const;
 
-    void setSelectedItems(const QSet<int>& items);
-    QSet<int> selectedItems() const;
+    void setSelectedItems(const KItemSet& items);
+    KItemSet selectedItems() const;
     bool isSelected(int index) const;
     bool hasSelection() const;
 
@@ -73,7 +73,7 @@ public:
 
 signals:
     void currentChanged(int current, int previous);
-    void selectionChanged(const QSet<int>& current, const QSet<int>& previous);
+    void selectionChanged(const KItemSet& current, const KItemSet& previous);
 
 private:
     void setModel(KItemModelBase* model);
@@ -91,7 +91,7 @@ private:
 private:
     int m_currentItem;
     int m_anchorItem;
-    QSet<int> m_selectedItems;
+    KItemSet m_selectedItems;
     bool m_isAnchoredSelectionActive;
 
     KItemModelBase* m_model;
index a667150904147ecd72568abdf95e9eb0ce332739..7f497210cdd5eb5abc8603319c950f3f374ad9ef 100644 (file)
@@ -609,12 +609,12 @@ KItemListHeader* KItemListView::header() const
     return m_header;
 }
 
-QPixmap KItemListView::createDragPixmap(const QSet<int>& indexes) const
+QPixmap KItemListView::createDragPixmap(const KItemSet& indexes) const
 {
     QPixmap pixmap;
 
     if (indexes.count() == 1) {
-        KItemListWidget* item = m_visibleItems.value(indexes.toList().first());
+        KItemListWidget* item = m_visibleItems.value(indexes.first());
         QGraphicsView* graphicsView = scene()->views()[0];
         if (item && graphicsView) {
             pixmap = item->createDragPixmap(0, graphicsView);
@@ -1233,6 +1233,13 @@ void KItemListView::slotItemsChanged(const KItemRangeList& itemRanges,
     QAccessible::updateAccessibility(this, 0, QAccessible::TableModelChanged);
 }
 
+void KItemListView::slotGroupsChanged()
+{
+    updateVisibleGroupHeaders();
+    doLayout(NoAnimation);
+    updateSiblingsInformation();
+}
+
 void KItemListView::slotGroupedSortingChanged(bool current)
 {
     m_grouped = current;
@@ -1298,7 +1305,7 @@ void KItemListView::slotCurrentChanged(int current, int previous)
     QAccessible::updateAccessibility(this, current+1, QAccessible::Focus);
 }
 
-void KItemListView::slotSelectionChanged(const QSet<int>& current, const QSet<int>& previous)
+void KItemListView::slotSelectionChanged(const KItemSet& current, const KItemSet& previous)
 {
     Q_UNUSED(previous);
 
@@ -1495,7 +1502,7 @@ void KItemListView::setController(KItemListController* controller)
         if (previous) {
             KItemListSelectionManager* selectionManager = previous->selectionManager();
             disconnect(selectionManager, SIGNAL(currentChanged(int,int)), this, SLOT(slotCurrentChanged(int,int)));
-            disconnect(selectionManager, SIGNAL(selectionChanged(QSet<int>,QSet<int>)), this, SLOT(slotSelectionChanged(QSet<int>,QSet<int>)));
+            disconnect(selectionManager, SIGNAL(selectionChanged(KItemSet,KItemSet)), this, SLOT(slotSelectionChanged(KItemSet,KItemSet)));
         }
 
         m_controller = controller;
@@ -1503,7 +1510,7 @@ void KItemListView::setController(KItemListController* controller)
         if (controller) {
             KItemListSelectionManager* selectionManager = controller->selectionManager();
             connect(selectionManager, SIGNAL(currentChanged(int,int)), this, SLOT(slotCurrentChanged(int,int)));
-            connect(selectionManager, SIGNAL(selectionChanged(QSet<int>,QSet<int>)), this, SLOT(slotSelectionChanged(QSet<int>,QSet<int>)));
+            connect(selectionManager, SIGNAL(selectionChanged(KItemSet,KItemSet)), this, SLOT(slotSelectionChanged(KItemSet,KItemSet)));
         }
 
         onControllerChanged(controller, previous);
@@ -1527,6 +1534,8 @@ void KItemListView::setModel(KItemModelBase* model)
                    this,    SLOT(slotItemsRemoved(KItemRangeList)));
         disconnect(m_model, SIGNAL(itemsMoved(KItemRange,QList<int>)),
                    this,    SLOT(slotItemsMoved(KItemRange,QList<int>)));
+        disconnect(m_model, SIGNAL(groupsChanged()),
+                   this,    SLOT(slotGroupsChanged()));
         disconnect(m_model, SIGNAL(groupedSortingChanged(bool)),
                    this,    SLOT(slotGroupedSortingChanged(bool)));
         disconnect(m_model, SIGNAL(sortOrderChanged(Qt::SortOrder,Qt::SortOrder)),
@@ -1550,6 +1559,8 @@ void KItemListView::setModel(KItemModelBase* model)
                 this,    SLOT(slotItemsRemoved(KItemRangeList)));
         connect(m_model, SIGNAL(itemsMoved(KItemRange,QList<int>)),
                 this,    SLOT(slotItemsMoved(KItemRange,QList<int>)));
+        connect(m_model, SIGNAL(groupsChanged()),
+                this,    SLOT(slotGroupsChanged()));
         connect(m_model, SIGNAL(groupedSortingChanged(bool)),
                 this,    SLOT(slotGroupedSortingChanged(bool)));
         connect(m_model, SIGNAL(sortOrderChanged(Qt::SortOrder,Qt::SortOrder)),
index 6467b8c915cc252c56bfafbdd46b3534d91d33d7..dbe9230867610975e17e0b9607cd3f9e27dc03ae 100644 (file)
@@ -271,7 +271,7 @@ public:
      * @return Pixmap that is used for a drag operation based on the
      *         items given by \a indexes.
      */
-    virtual QPixmap createDragPixmap(const QSet<int>& indexes) const;
+    virtual QPixmap createDragPixmap(const KItemSet& indexes) const;
 
     /**
      * Lets the user edit the role \a role for item with the index \a index.
@@ -394,12 +394,13 @@ protected slots:
     virtual void slotItemsMoved(const KItemRange& itemRange, const QList<int>& movedToIndexes);
     virtual void slotItemsChanged(const KItemRangeList& itemRanges,
                                   const QSet<QByteArray>& roles);
+    virtual void slotGroupsChanged();
 
     virtual void slotGroupedSortingChanged(bool current);
     virtual void slotSortOrderChanged(Qt::SortOrder current, Qt::SortOrder previous);
     virtual void slotSortRoleChanged(const QByteArray& current, const QByteArray& previous);
     virtual void slotCurrentChanged(int current, int previous);
-    virtual void slotSelectionChanged(const QSet<int>& current, const QSet<int>& previous);
+    virtual void slotSelectionChanged(const KItemSet& current, const KItemSet& previous);
 
 private slots:
     void slotAnimationFinished(QGraphicsWidget* widget,
index 565c2151ec17c59fb50f4db0fb17763de524486c..d9ddd58f84045b9831b56ee74a601013250220b4 100644 (file)
@@ -109,7 +109,7 @@ int KItemListViewAccessible::rowCount() const
 
 int KItemListViewAccessible::selectedCellCount() const
 {
-    return view()->controller()->selectionManager()->selectedItems().size();
+    return view()->controller()->selectionManager()->selectedItems().count();
 }
 
 int KItemListViewAccessible::selectedColumnCount() const
index c13c9f88c815430a9802f349f40491b2a261dbfb..4312640e41f423dac936958f44ebc20d3caaa3f8 100644 (file)
 
 #include "kitemmodelbase.h"
 
-KItemRange::KItemRange(int index, int count) :
-    index(index),
-    count(count)
-{
-}
-
-bool KItemRange::operator == (const KItemRange& other) const
-{
-    return index == other.index && count == other.count;
-}
-
 KItemModelBase::KItemModelBase(QObject* parent) :
     QObject(parent),
     m_groupedSorting(false),
@@ -134,7 +123,7 @@ int KItemModelBase::expandedParentsCount(int index) const
     return 0;
 }
 
-QMimeData* KItemModelBase::createMimeData(const QSet<int>& indexes) const
+QMimeData* KItemModelBase::createMimeData(const KItemSet& indexes) const
 {
     Q_UNUSED(indexes);
     return 0;
index 70f688390951986171a6f9fab75f4e28301f0263..283cfa5523b363fbe72996b27c40b8c7f4ec1538 100644 (file)
 
 #include <libdolphin_export.h>
 
+#include <kitemviews/kitemrange.h>
+#include <kitemviews/kitemset.h>
+
 #include <QHash>
 #include <QObject>
-#include <QSet>
 #include <QVariant>
 
 class QMimeData;
 
-struct KItemRange
-{
-    KItemRange(int index = 0, int count = 0);
-    int index;
-    int count;
-
-    bool operator == (const KItemRange& other) const;
-};
-typedef QList<KItemRange> KItemRangeList;
-
 /**
  * @brief Base class for model implementations used by KItemListView and KItemListController.
  *
@@ -157,7 +149,7 @@ public:
      *         caller of this method. The method must be implemented if dragging of
      *         items should be possible.
      */
-    virtual QMimeData* createMimeData(const QSet<int>& indexes) const;
+    virtual QMimeData* createMimeData(const KItemSet& indexes) const;
 
     /**
      * @return Reimplement this to return the index for the first item
@@ -218,11 +210,20 @@ signals:
      * with the items 5 and 6 then the parameters look like this:
      * - itemRange: has the index 0 and a count of 7.
      * - movedToIndexes: Contains the seven values 5, 6, 2, 3, 4, 0, 1
+     *
+     * This signal implies that the groups might have changed. Therefore,
+     * gropusChanged() is not emitted if this signal is emitted.
      */
     void itemsMoved(const KItemRange& itemRange, const QList<int>& movedToIndexes);
 
     void itemsChanged(const KItemRangeList& itemRanges, const QSet<QByteArray>& roles);
 
+    /**
+     * Is emitted if the groups have changed, even though the order of the
+     * items has not been modified.
+     */
+    void groupsChanged();
+
     void groupedSortingChanged(bool current);
     void sortRoleChanged(const QByteArray& current, const QByteArray& previous);
     void sortOrderChanged(Qt::SortOrder current, Qt::SortOrder previous);
diff --git a/src/kitemviews/kitemrange.h b/src/kitemviews/kitemrange.h
new file mode 100644 (file)
index 0000000..70927b9
--- /dev/null
@@ -0,0 +1,98 @@
+/***************************************************************************
+ *   Copyright (C) 2011 by Peter Penz <peter.penz19@gmail.com>             *
+ *   Copyright (C) 2013 by Frank Reininghaus <frank78ac@googlemail.com>    *
+ *                                                                         *
+ *   Based on the Itemviews NG project from Trolltech Labs:                *
+ *   http://qt.gitorious.org/qt-labs/itemviews-ng                          *
+ *                                                                         *
+ *   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 KITEMRANGE_H
+#define KITEMRANGE_H
+
+#include <QList>
+
+struct KItemRange
+{
+    KItemRange(int index = 0, int count = 0);
+    int index;
+    int count;
+
+    bool operator == (const KItemRange& other) const;
+};
+
+inline KItemRange::KItemRange(int index, int count) :
+    index(index),
+    count(count)
+{
+}
+
+inline bool KItemRange::operator == (const KItemRange& other) const
+{
+    return index == other.index && count == other.count;
+}
+
+
+class KItemRangeList : public QList<KItemRange>
+{
+public:
+    KItemRangeList() : QList<KItemRange>() {}
+    KItemRangeList(const QList<KItemRange>& list) : QList<KItemRange>(list) {}
+
+    template<class Container>
+    static KItemRangeList fromSortedContainer(const Container& container);
+
+    KItemRangeList& operator<<(const KItemRange& range)
+    {
+        append(range);
+        return *this;
+    }
+};
+
+template<class Container>
+KItemRangeList KItemRangeList::fromSortedContainer(const Container& container)
+{
+    typename Container::const_iterator it = container.constBegin();
+    const typename Container::const_iterator end = container.constEnd();
+
+    if (it == end) {
+        return KItemRangeList();
+    }
+
+    KItemRangeList result;
+
+    int index = *it;
+    int count = 1;
+
+    ++it;
+
+    while (it != end) {
+        if (*it == index + count) {
+            ++count;
+        } else {
+            result << KItemRange(index, count);
+            index = *it;
+            count = 1;
+        }
+        ++it;
+    }
+
+    result << KItemRange(index, count);
+    return result;
+}
+
+#endif
diff --git a/src/kitemviews/kitemset.cpp b/src/kitemviews/kitemset.cpp
new file mode 100644 (file)
index 0000000..f855368
--- /dev/null
@@ -0,0 +1,348 @@
+/***************************************************************************
+ *   Copyright (C) 2013 by Frank Reininghaus <frank78ac@googlemail.com>    *
+ *                                                                         *
+ *   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 "kitemset.h"
+
+#include <QVector>
+
+#include <algorithm>
+
+KItemSet::iterator KItemSet::insert(int i)
+{
+    if (m_itemRanges.empty()) {
+        m_itemRanges.push_back(KItemRange(i, 1));
+        return iterator(m_itemRanges.begin(), 0);
+    }
+
+    KItemRangeList::iterator rangeBegin = m_itemRanges.begin();
+    if (i < rangeBegin->index) {
+        // The inserted index is smaller than all existing items.
+        if (i == rangeBegin->index - 1) {
+            // Move the beginning of the first range one item to the front.
+            --rangeBegin->index;
+            ++rangeBegin->count;
+        } else {
+            // Insert a new range at the beginning.
+            rangeBegin = m_itemRanges.insert(rangeBegin, KItemRange(i, 1));
+        }
+
+        return iterator(rangeBegin, 0);
+    }
+
+    KItemRangeList::iterator rangeEnd = m_itemRanges.end();
+    KItemRangeList::iterator lastRange = rangeEnd - 1;
+    if (i >= lastRange->index) {
+        // i either belongs to the last range, or it is larger than all existing items.
+        const int lastItemPlus1 = lastRange->index + lastRange->count;
+        if (i == lastItemPlus1) {
+            // Move the end of the last range one item to the back.
+            ++lastRange->count;
+        } else if (i > lastItemPlus1) {
+            // Append a new range.
+            lastRange = m_itemRanges.insert(rangeEnd, KItemRange(i, 1));
+        }
+
+        return iterator(lastRange, i - lastRange->index);
+    }
+
+    // We know that i is between the smallest existing item and the first item
+    // of the last range. Find the lowest range whose 'index' is smaller than i.
+    KItemRangeList::iterator low = rangeBegin;
+    KItemRangeList::iterator high = lastRange;
+
+    while (low + 1 != high) {
+        const int span = high - low;
+        Q_ASSERT(span >= 2);
+
+        KItemRangeList::iterator mid = low + span / 2;
+        if (mid->index > i) {
+            high = mid;
+        } else {
+            low = mid;
+        }
+    }
+
+    Q_ASSERT(low->index <= i && high->index > i);
+
+    if (i == low->index + low->count) {
+        // i is just one item behind the range low.
+        if (i == high->index - 1) {
+            // i closes the gap between low and high. Merge the two ranges.
+            const int newRangeCount = low->count + 1 + high->count;
+            KItemRangeList::iterator behindNewRange = m_itemRanges.erase(high);
+            KItemRangeList::iterator newRange = behindNewRange - 1;
+            newRange->count = newRangeCount;
+            return iterator(newRange, i - newRange->index);
+        } else {
+            // Extend low by one item.
+            ++low->count;
+            return iterator(low, low->count - 1);
+        }
+    } else if (i > low->index + low->count) {
+        if (i == high->index - 1) {
+            // Extend high by one item to the front.
+            --high->index;
+            ++high->count;
+            return iterator(high, 0);
+        } else {
+            // Insert a new range between low and high.
+            KItemRangeList::iterator newRange = m_itemRanges.insert(high, KItemRange(i, 1));
+            return iterator(newRange, 0);
+        }
+    } else {
+        // The range low already contains i.
+        return iterator(low, i - low->index);
+    }
+}
+
+KItemSet::iterator KItemSet::erase(iterator it)
+{
+    KItemRangeList::iterator rangeIt = it.m_rangeIt;
+
+    if (it.m_offset == 0) {
+        // Removed index is at the beginning of a range.
+        if (rangeIt->count > 1) {
+            ++rangeIt->index;
+            --rangeIt->count;
+        } else {
+            // The range only contains the removed index.
+            rangeIt = m_itemRanges.erase(rangeIt);
+        }
+        return iterator(rangeIt, 0);
+    } else if (it.m_offset == rangeIt->count - 1) {
+        // Removed index is at the end of a range.
+        --rangeIt->count;
+        ++rangeIt;
+        return iterator(rangeIt, 0);
+    } else {
+        // The removed index is in the middle of a range.
+        const int newRangeIndex = *it + 1;
+        const int newRangeCount = rangeIt->count - it.m_offset - 1;
+        const KItemRange newRange(newRangeIndex, newRangeCount);
+
+        rangeIt->count = it.m_offset;
+        ++rangeIt;
+        rangeIt = m_itemRanges.insert(rangeIt, newRange);
+
+        return iterator(rangeIt, 0);
+    }
+}
+
+KItemSet KItemSet::operator+(const KItemSet& other) const
+{
+    KItemSet sum;
+
+    KItemRangeList::const_iterator it1 = m_itemRanges.constBegin();
+    KItemRangeList::const_iterator it2 = other.m_itemRanges.constBegin();
+
+    const KItemRangeList::const_iterator end1 = m_itemRanges.constEnd();
+    const KItemRangeList::const_iterator end2 = other.m_itemRanges.constEnd();
+
+    while (it1 != end1 || it2 != end2) {
+        if (it1 == end1) {
+            // We are past the end of 'this' already. Append all remaining
+            // item ranges from 'other'.
+            while (it2 != end2) {
+                sum.m_itemRanges.append(*it2);
+                ++it2;
+            }
+        } else if (it2 == end2) {
+            // We are past the end of 'other' already. Append all remaining
+            // item ranges from 'this'.
+            while (it1 != end1) {
+                sum.m_itemRanges.append(*it1);
+                ++it1;
+            }
+        } else {
+            // Find the beginning of the next range.
+            int index = qMin(it1->index, it2->index);
+            int count = 0;
+
+            do {
+                if (it1 != end1 && it1->index <= index + count) {
+                    // The next range from 'this' overlaps with the current range in the sum.
+                    count = qMax(count, it1->index + it1->count - index);
+                    ++it1;
+                }
+
+                if (it2 != end2 && it2->index <= index + count) {
+                    // The next range from 'other' overlaps with the current range in the sum.
+                    count = qMax(count, it2->index + it2->count - index);
+                    ++it2;
+                }
+            } while ((it1 != end1 && it1->index <= index + count)
+                    || (it2 != end2 && it2->index <= index + count));
+
+            sum.m_itemRanges.append(KItemRange(index, count));
+        }
+    }
+
+    return sum;
+}
+
+KItemSet KItemSet::operator^(const KItemSet& other) const
+{
+    // We are looking for all ints which are either in *this or in other,
+    // but not in both.
+    KItemSet result;
+
+    // When we go through all integers from INT_MIN to INT_MAX and start
+    // in the state "do not add to result", every beginning/end of a range
+    // of *this and other toggles the "add/do not add to result" state.
+    // Therefore, we just have to put ints where any range starts/ends to
+    // a sorted array, and then we can calculate the result quite easily.
+    QVector<int> rangeBoundaries;
+    rangeBoundaries.resize(2 * (m_itemRanges.count() + other.m_itemRanges.count()));
+    const QVector<int>::iterator begin = rangeBoundaries.begin();
+    const QVector<int>::iterator end = rangeBoundaries.end();
+    QVector<int>::iterator it = begin;
+
+    foreach (const KItemRange& range, m_itemRanges) {
+        *it++ = range.index;
+        *it++ = range.index + range.count;
+    }
+
+    const QVector<int>::iterator middle = it;
+
+    foreach (const KItemRange& range, other.m_itemRanges) {
+        *it++ = range.index;
+        *it++ = range.index + range.count;
+    }
+    Q_ASSERT(it == end);
+
+    std::inplace_merge(begin, middle, end);
+
+    it = begin;
+    while (it != end) {
+        const int rangeBegin = *it;
+        ++it;
+
+        if (*it == rangeBegin) {
+            // It seems that ranges from both *this and other start at
+            // rangeBegin. Do not start a new range, but read the next int.
+            //
+            // Example: Consider the symmetric difference of the sets
+            // {1, 2, 3, 4} and {1, 2}. The sorted list of range boundaries is
+            // 1 1 3 5. Discarding the duplicate 1 yields the result
+            // rangeBegin = 3, rangeEnd = 5, which corresponds to the set {3, 4}.
+            ++it;
+        } else {
+            // The end of the current range is the next *single* int that we
+            // find. If an int appears twice in rangeBoundaries, the range does
+            // not end.
+            //
+            // Example: Consider the symmetric difference of the sets
+            // {1, 2, 3, 4, 8, 9, 10} and {5, 6, 7}. The sorted list of range
+            // boundaries is 1 5 5 8 8 11, and discarding all duplicates yields
+            // the result rangeBegin = 1, rangeEnd = 11, which corresponds to
+            // the set {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}.
+            bool foundEndOfRange = false;
+            int rangeEnd;
+            do {
+                rangeEnd = *it;
+                ++it;
+
+                if (it == end || *it != rangeEnd) {
+                    foundEndOfRange = true;
+                } else {
+                    ++it;
+                }
+            } while (!foundEndOfRange);
+
+            result.m_itemRanges.append(KItemRange(rangeBegin, rangeEnd - rangeBegin));
+        }
+    }
+
+    return result;
+}
+
+bool KItemSet::isValid() const
+{
+    const KItemRangeList::const_iterator begin = m_itemRanges.constBegin();
+    const KItemRangeList::const_iterator end = m_itemRanges.constEnd();
+
+    for (KItemRangeList::const_iterator it = begin; it != end; ++it) {
+        if (it->count <= 0) {
+            return false;
+        }
+
+        if (it != begin) {
+            const KItemRangeList::const_iterator previous = it - 1;
+            if (previous->index + previous->count >= it->index) {
+                return false;
+            }
+        }
+    }
+
+    return true;
+}
+
+KItemRangeList::iterator KItemSet::rangeForItem(int i)
+{
+    const KItemRangeList::iterator end = m_itemRanges.end();
+    KItemRangeList::iterator low = m_itemRanges.begin();
+    KItemRangeList::iterator high = end;
+
+    if (low == end || low->index > i) {
+        return end;
+    }
+
+    while (low != high && low + 1 != high) {
+        KItemRangeList::iterator mid = low + (high - low) / 2;
+        if (mid->index > i) {
+            high = mid;
+        } else {
+            low = mid;
+        }
+    }
+
+    Q_ASSERT(low->index <= i);
+    if (low->index + low->count > i) {
+        return low;
+    }
+
+    return end;
+}
+
+KItemRangeList::const_iterator KItemSet::constRangeForItem(int i) const
+{
+    const KItemRangeList::const_iterator end = m_itemRanges.constEnd();
+    KItemRangeList::const_iterator low = m_itemRanges.constBegin();
+    KItemRangeList::const_iterator high = end;
+
+    if (low == end || low->index > i) {
+        return end;
+    }
+
+    while (low != high && low + 1 != high) {
+        KItemRangeList::const_iterator mid = low + (high - low) / 2;
+        if (mid->index > i) {
+            high = mid;
+        } else {
+            low = mid;
+        }
+    }
+
+    Q_ASSERT(low->index <= i);
+    if (low->index + low->count > i) {
+        return low;
+    }
+
+    return end;
+}
diff --git a/src/kitemviews/kitemset.h b/src/kitemviews/kitemset.h
new file mode 100644 (file)
index 0000000..385010f
--- /dev/null
@@ -0,0 +1,413 @@
+/***************************************************************************
+ *   Copyright (C) 2013 by Frank Reininghaus <frank78ac@googlemail.com>    *
+ *                                                                         *
+ *   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 KITEMSET_H
+#define KITEMSET_H
+
+#include <kitemviews/kitemrange.h>
+
+/**
+ * @brief Stores a set of integer numbers in a space-efficient way.
+ *
+ * This class is similar to QSet<int>, but it has the following advantages:
+ *
+ * 1. It uses less memory than a QSet<int> if many consecutive numbers are
+ *    stored. This is achieved by not storing each number separately, but
+ *    "ranges" of numbers.
+ *
+ *    Example: The set {1, 2, 3, 4, 5} is represented by a single range which
+ *    starts at 1 and has the length 5.
+ *
+ * 2. When iterating through a KItemSet using KItemSet::iterator or
+ *    KItemSet::const_iterator, the numbers are traversed in ascending order.
+ *
+ * The complexity of most operations depends on the number of ranges.
+ */
+
+class KItemSet
+{
+public:
+    KItemSet();
+    KItemSet(const KItemSet& other);
+
+    /**
+     * Returns the number of items in the set.
+     * Complexity: O(log(number of ranges)).
+     */
+    int count() const;
+
+    bool isEmpty() const;
+    void clear();
+
+    bool operator==(const KItemSet& other) const;
+    bool operator!=(const KItemSet& other) const;
+
+    class iterator
+    {
+        iterator(const KItemRangeList::iterator& rangeIt, int offset) :
+            m_rangeIt(rangeIt),
+            m_offset(offset)
+        {
+        }
+
+    public:
+        iterator(const iterator& other) :
+            m_rangeIt(other.m_rangeIt),
+            m_offset(other.m_offset)
+        {
+        }
+
+        iterator& operator=(const iterator& other)
+        {
+            m_rangeIt = other.m_rangeIt;
+            m_offset = other.m_offset;
+            return *this;
+        }
+
+        int operator*() const
+        {
+            return m_rangeIt->index + m_offset;
+        }
+
+        inline bool operator==(const iterator& other) const
+        {
+            return m_rangeIt == other.m_rangeIt && m_offset == other.m_offset;
+        }
+
+        inline bool operator!=(const iterator& other) const
+        {
+            return !(*this == other);
+        }
+
+        inline iterator& operator++()
+        {
+            ++m_offset;
+
+            if (m_offset == m_rangeIt->count) {
+                ++m_rangeIt;
+                m_offset = 0;
+            }
+
+            return *this;
+        }
+
+        inline iterator operator++(int)
+        {
+            iterator r = *this;
+            ++(*this);
+            return r;
+        }
+
+        inline iterator& operator--()
+        {
+            if (m_offset == 0) {
+                --m_rangeIt;
+                m_offset = m_rangeIt->count - 1;
+            } else {
+                --m_offset;
+            }
+
+            return *this;
+        }
+
+        inline iterator operator--(int)
+        {
+            iterator r = *this;
+            --(*this);
+            return r;
+        }
+
+    private:
+        KItemRangeList::iterator m_rangeIt;
+        int m_offset;
+
+        friend class const_iterator;
+        friend class KItemSet;
+    };
+
+
+    class const_iterator
+    {
+        const_iterator(KItemRangeList::const_iterator rangeIt, int offset) :
+            m_rangeIt(rangeIt),
+            m_offset(offset)
+        {
+        }
+
+    public:
+        const_iterator(const const_iterator& other) :
+            m_rangeIt(other.m_rangeIt),
+            m_offset(other.m_offset)
+        {
+        }
+
+        const_iterator(const iterator& other) :
+            m_rangeIt(other.m_rangeIt),
+            m_offset(other.m_offset)
+        {
+        }
+
+        const_iterator& operator=(const const_iterator& other)
+        {
+            m_rangeIt = other.m_rangeIt;
+            m_offset = other.m_offset;
+            return *this;
+        }
+
+        int operator*() const
+        {
+            return m_rangeIt->index + m_offset;
+        }
+
+        inline bool operator==(const const_iterator& other) const
+        {
+            return m_rangeIt == other.m_rangeIt && m_offset == other.m_offset;
+        }
+
+        inline bool operator!=(const const_iterator& other) const
+        {
+            return !(*this == other);
+        }
+
+        inline const_iterator& operator++()
+        {
+            ++m_offset;
+
+            if (m_offset == m_rangeIt->count) {
+                ++m_rangeIt;
+                m_offset = 0;
+            }
+
+            return *this;
+        }
+
+        inline const_iterator operator++(int)
+        {
+            const_iterator r = *this;
+            ++(*this);
+            return r;
+        }
+
+        inline const_iterator& operator--()
+        {
+            if (m_offset == 0) {
+                --m_rangeIt;
+                m_offset = m_rangeIt->count - 1;
+            } else {
+                --m_offset;
+            }
+
+            return *this;
+        }
+
+        inline const_iterator operator--(int)
+        {
+            const_iterator r = *this;
+            --(*this);
+            return r;
+        }
+
+    private:
+        KItemRangeList::const_iterator m_rangeIt;
+        int m_offset;
+
+        friend class KItemSet;
+    };
+
+    iterator begin();
+    const_iterator begin() const;
+    const_iterator constBegin() const;
+    iterator end();
+    const_iterator end() const;
+    const_iterator constEnd() const;
+
+    int first() const;
+    int last() const;
+
+    bool contains(int i) const;
+    iterator insert(int i);
+    iterator find(int i);
+    const_iterator constFind(int i) const;
+    bool remove(int i);
+    iterator erase(iterator it);
+
+    /**
+     * Returns a new set which contains all items that are contained in this
+     * KItemSet, in \a other, or in both.
+     */
+    KItemSet operator+(const KItemSet& other) const;
+
+    /**
+     * Returns a new set which contains all items that are contained either in
+     * this KItemSet, or in \a other, but not in both (the symmetric difference
+     * of both KItemSets).
+     */
+    KItemSet operator^(const KItemSet& other) const;
+
+    KItemSet& operator<<(int i);
+
+private:
+    /**
+     * Returns true if the KItemSet is valid, and false otherwise.
+     * A valid KItemSet must store the item ranges in ascending order, and
+     * the ranges must not overlap.
+     */
+    bool isValid() const;
+
+    /**
+     * This function returns an iterator that points to the KItemRange which
+     * contains i, or m_itemRanges.end() if no such range exists.
+     */
+    KItemRangeList::iterator rangeForItem(int i);
+
+    /**
+     * This function returns an iterator that points to the KItemRange which
+     * contains i, or m_itemRanges.constEnd() if no such range exists.
+     */
+    KItemRangeList::const_iterator constRangeForItem(int i) const;
+
+    KItemRangeList m_itemRanges;
+
+    friend class KItemSetTest;
+};
+
+inline KItemSet::KItemSet() :
+    m_itemRanges()
+{
+}
+
+inline KItemSet::KItemSet(const KItemSet& other) :
+    m_itemRanges(other.m_itemRanges)
+{
+}
+
+inline int KItemSet::count() const
+{
+    int result = 0;
+    foreach (const KItemRange& range, m_itemRanges) {
+        result += range.count;
+    }
+    return result;
+}
+
+inline bool KItemSet::isEmpty() const
+{
+    return m_itemRanges.isEmpty();
+}
+
+inline void KItemSet::clear()
+{
+    m_itemRanges.clear();
+}
+
+inline bool KItemSet::operator==(const KItemSet& other) const
+{
+    return m_itemRanges == other.m_itemRanges;
+}
+
+inline bool KItemSet::operator!=(const KItemSet& other) const
+{
+    return m_itemRanges != other.m_itemRanges;
+}
+
+inline bool KItemSet::contains(int i) const
+{
+    const KItemRangeList::const_iterator it = constRangeForItem(i);
+    return it != m_itemRanges.end();
+}
+
+inline KItemSet::iterator KItemSet::find(int i)
+{
+    const KItemRangeList::iterator it = rangeForItem(i);
+    if (it != m_itemRanges.end()) {
+        return iterator(it, i - it->index);
+    } else {
+        return end();
+    }
+}
+
+inline KItemSet::const_iterator KItemSet::constFind(int i) const
+{
+    const KItemRangeList::const_iterator it = constRangeForItem(i);
+    if (it != m_itemRanges.constEnd()) {
+        return const_iterator(it, i - it->index);
+    } else {
+        return constEnd();
+    }
+}
+
+inline bool KItemSet::remove(int i)
+{
+    iterator it = find(i);
+    if (it != end()) {
+        erase(it);
+        return true;
+    } else {
+        return false;
+    }
+}
+
+inline KItemSet::iterator KItemSet::begin()
+{
+    return iterator(m_itemRanges.begin(), 0);
+}
+
+inline KItemSet::const_iterator KItemSet::begin() const
+{
+    return const_iterator(m_itemRanges.begin(), 0);
+}
+
+inline KItemSet::const_iterator KItemSet::constBegin() const
+{
+    return const_iterator(m_itemRanges.constBegin(), 0);
+}
+
+inline KItemSet::iterator KItemSet::end()
+{
+    return iterator(m_itemRanges.end(), 0);
+}
+
+inline KItemSet::const_iterator KItemSet::end() const
+{
+    return const_iterator(m_itemRanges.end(), 0);
+}
+
+inline KItemSet::const_iterator KItemSet::constEnd() const
+{
+    return const_iterator(m_itemRanges.constEnd(), 0);
+}
+
+inline int KItemSet::first() const
+{
+    return m_itemRanges.first().index;
+}
+
+inline int KItemSet::last() const
+{
+    const KItemRange& lastRange = m_itemRanges.last();
+    return lastRange.index + lastRange.count - 1;
+}
+
+inline KItemSet& KItemSet::operator<<(int i)
+{
+    insert(i);
+    return *this;
+}
+
+#endif
index bd4f6081fc1cb9dd8be49a0e31baab3e2177f45c..135cd0b7df0102e698cbde8155fb4a19d4206861 100644 (file)
@@ -48,23 +48,8 @@ void KStandardItemListView::setItemLayout(ItemLayout layout)
     const ItemLayout previous = m_itemLayout;
     m_itemLayout = layout;
 
-    switch (layout) {
-    case IconsLayout:
-        setScrollOrientation(Qt::Vertical);
-        setSupportsItemExpanding(false);
-        break;
-    case DetailsLayout:
-        setScrollOrientation(Qt::Vertical);
-        setSupportsItemExpanding(true);
-        break;
-    case CompactLayout:
-        setScrollOrientation(Qt::Horizontal);
-        setSupportsItemExpanding(false);
-        break;
-    default:
-        Q_ASSERT(false);
-        break;
-    }
+    setSupportsItemExpanding(itemLayoutSupportsItemExpanding(layout));
+    setScrollOrientation(layout == CompactLayout ? Qt::Horizontal : Qt::Vertical);
 
     onItemLayoutChanged(layout, previous);
 
@@ -117,6 +102,11 @@ bool KStandardItemListView::itemSizeHintUpdateRequired(const QSet<QByteArray>& c
     return false;
 }
 
+bool KStandardItemListView::itemLayoutSupportsItemExpanding(ItemLayout layout) const
+{
+    return layout == DetailsLayout;
+}
+
 void KStandardItemListView::onItemLayoutChanged(ItemLayout current, ItemLayout previous)
 {
     Q_UNUSED(current);
index fd4fa861c63d5cb3130f2de01f3bbb5aee98c513..f5b0bfd8cf3f0884b42faa488cf1d926a35f66d3 100644 (file)
@@ -63,6 +63,7 @@ protected:
     virtual KItemListGroupHeaderCreatorBase* defaultGroupHeaderCreator() const;
     virtual void initializeItemListWidget(KItemListWidget* item);
     virtual bool itemSizeHintUpdateRequired(const QSet<QByteArray>& changedRoles) const;
+    virtual bool itemLayoutSupportsItemExpanding(ItemLayout layout) const;
     virtual void onItemLayoutChanged(ItemLayout current, ItemLayout previous);
     virtual void onScrollOrientationChanged(Qt::Orientation current, Qt::Orientation previous);
     virtual void onSupportsItemExpandingChanged(bool supportsExpanding);
index bc0503663bceb1dfdb1065731bbaf6110838c084..302150fec1267d93a094a3c5d2b0e3400acc41ca 100644 (file)
@@ -57,13 +57,12 @@ KStandardItemListWidgetInformant::~KStandardItemListWidgetInformant()
 
 QSizeF KStandardItemListWidgetInformant::itemSizeHint(int index, const KItemListView* view) const
 {
-    const QHash<QByteArray, QVariant> values = view->model()->data(index);
     const KItemListStyleOption& option = view->styleOption();
     const int additionalRolesCount = qMax(view->visibleRoles().count() - 1, 0);
 
     switch (static_cast<const KStandardItemListView*>(view)->itemLayout()) {
     case KStandardItemListWidget::IconsLayout: {
-        const QString text = KStringHandler::preProcessWrap(values["text"].toString());
+        const QString text = KStringHandler::preProcessWrap(itemText(index, view));
 
         const qreal itemWidth = view->itemSize().width();
         const qreal maxWidth = itemWidth - 2 * option.padding;
@@ -100,6 +99,7 @@ QSizeF KStandardItemListWidgetInformant::itemSizeHint(int index, const KItemList
         // to show all roles without horizontal clipping.
         qreal maximumRequiredWidth = 0.0;
 
+        const QHash<QByteArray, QVariant> values = view->model()->data(index);
         foreach (const QByteArray& role, view->visibleRoles()) {
             const QString text = roleText(role, values);
             const qreal requiredWidth = option.fontMetrics.width(text);
@@ -159,6 +159,11 @@ qreal KStandardItemListWidgetInformant::preferredRoleColumnWidth(const QByteArra
     return width;
 }
 
+QString KStandardItemListWidgetInformant::itemText(int index, const KItemListView* view) const
+{
+    return view->model()->data(index).value("text").toString();
+}
+
 QString KStandardItemListWidgetInformant::roleText(const QByteArray& role,
                                                    const QHash<QByteArray, QVariant>& values) const
 {
@@ -797,11 +802,12 @@ void KStandardItemListWidget::updateExpansionArea()
         const QHash<QByteArray, QVariant> values = data();
         const int expandedParentsCount = values.value("expandedParentsCount", 0).toInt();
         if (expandedParentsCount >= 0) {
+            const KItemListStyleOption& option = styleOption();
             const qreal widgetHeight = size().height();
-            const qreal inc = (widgetHeight - KIconLoader::SizeSmall) / 2;
+            const qreal inc = (widgetHeight - option.iconSize) / 2;
             const qreal x = expandedParentsCount * widgetHeight + inc;
             const qreal y = inc;
-            m_expansionArea = QRectF(x, y, KIconLoader::SizeSmall, KIconLoader::SizeSmall);
+            m_expansionArea = QRectF(x, y, option.iconSize, option.iconSize);
             return;
         }
     }
index 4bf6116fd1121cd263c1cff74aed55ea0a75456b..7dd93b2b819babd5e48271ba47a1d89339b166bb 100644 (file)
@@ -44,6 +44,15 @@ public:
                                            int index,
                                            const KItemListView* view) const;
 protected:
+    /**
+     * @return The value of the "text" role. The default implementation returns
+     *         view->model()->data(index)["text"]. If a derived class can
+     *         prevent the (possibly expensive) construction of the
+     *         QHash<QByteArray, QVariant> returned by KItemModelBase::data(int),
+     *         it can reimplement this function.
+     */
+    virtual QString itemText(int index, const KItemListView* view) const;
+
     /**
      * @return String representation of the role \a role. The representation of
      *         a role might depend on other roles, so the values of all roles
index 959d62cb8ad55a643f9263c25ea1c97570489d41..e8c1b6204293d65f01b478599ade9d13847ea940 100644 (file)
@@ -175,7 +175,7 @@ bool KStandardItemModel::setData(int index, const QHash<QByteArray, QVariant>& v
     return true;
 }
 
-QMimeData* KStandardItemModel::createMimeData(const QSet<int>& indexes) const
+QMimeData* KStandardItemModel::createMimeData(const KItemSet& indexes) const
 {
     Q_UNUSED(indexes);
     return 0;
index 0debd6a6fccc3e5c6a78b62f374fce3012e3ee7b..721e155299862f91a2c3e9bbd4999d92611401b8 100644 (file)
@@ -72,7 +72,7 @@ public:
     virtual int count() const;
     virtual QHash<QByteArray, QVariant> data(int index) const;
     virtual bool setData(int index, const QHash<QByteArray, QVariant>& values);
-    virtual QMimeData* createMimeData(const QSet<int>& indexes) const;
+    virtual QMimeData* createMimeData(const KItemSet& indexes) const;
     virtual int indexForKeyboardSearch(const QString& text, int startFromIndex = 0) const;
     virtual bool supportsDropping(int index) const;
     virtual QString roleDescription(const QByteArray& role) const;
diff --git a/src/kitemviews/private/kdirectorycontentscounter.cpp b/src/kitemviews/private/kdirectorycontentscounter.cpp
new file mode 100644 (file)
index 0000000..fd8479f
--- /dev/null
@@ -0,0 +1,164 @@
+/***************************************************************************
+ *   Copyright (C) 2011 by Peter Penz <peter.penz19@gmail.com>             *
+ *   Copyright (C) 2013 by Frank Reininghaus <frank78ac@googlemail.com>    *
+ *                                                                         *
+ *   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 "kdirectorycontentscounter.h"
+
+#include "kdirectorycontentscounterworker.h"
+#include <kitemviews/kfileitemmodel.h>
+
+#include <KDirWatch>
+#include <QThread>
+
+KDirectoryContentsCounter::KDirectoryContentsCounter(KFileItemModel* model, QObject* parent) :
+    QObject(parent),
+    m_model(model),
+    m_queue(),
+    m_workerThread(0),
+    m_worker(0),
+    m_workerIsBusy(false),
+    m_dirWatcher(0),
+    m_watchedDirs()
+{
+    connect(m_model, SIGNAL(itemsRemoved(KItemRangeList)),
+            this,    SLOT(slotItemsRemoved()));
+
+    m_workerThread = new QThread(this);
+    m_worker = new KDirectoryContentsCounterWorker();
+    m_worker->moveToThread(m_workerThread);
+
+    connect(this,     SIGNAL(requestDirectoryContentsCount(QString,KDirectoryContentsCounterWorker::Options)),
+            m_worker, SLOT(countDirectoryContents(QString,KDirectoryContentsCounterWorker::Options)));
+    connect(m_worker, SIGNAL(result(QString,int)),
+            this,     SLOT(slotResult(QString,int)));
+
+    m_workerThread->start();
+
+    m_dirWatcher = new KDirWatch(this);
+    connect(m_dirWatcher, SIGNAL(dirty(QString)), this, SLOT(slotDirWatchDirty(QString)));
+}
+
+KDirectoryContentsCounter::~KDirectoryContentsCounter()
+{
+    m_workerThread->quit();
+    m_workerThread->wait();
+
+    delete m_worker;
+}
+
+void KDirectoryContentsCounter::addDirectory(const QString& path)
+{
+    startWorker(path);
+}
+
+int KDirectoryContentsCounter::countDirectoryContentsSynchronously(const QString& path)
+{
+    if (!m_dirWatcher->contains(path)) {
+        m_dirWatcher->addDir(path);
+        m_watchedDirs.insert(path);
+    }
+
+    KDirectoryContentsCounterWorker::Options options;
+
+    if (m_model->showHiddenFiles()) {
+        options |= KDirectoryContentsCounterWorker::CountHiddenFiles;
+    }
+
+    if (m_model->showDirectoriesOnly()) {
+        options |= KDirectoryContentsCounterWorker::CountDirectoriesOnly;
+    }
+
+    return KDirectoryContentsCounterWorker::subItemsCount(path, options);
+}
+
+void KDirectoryContentsCounter::slotResult(const QString& path, int count)
+{
+    m_workerIsBusy = false;
+
+    if (!m_dirWatcher->contains(path)) {
+        m_dirWatcher->addDir(path);
+        m_watchedDirs.insert(path);
+    }
+
+    if (!m_queue.isEmpty()) {
+        startWorker(m_queue.dequeue());
+    }
+
+    emit result(path, count);
+}
+
+void KDirectoryContentsCounter::slotDirWatchDirty(const QString& path)
+{
+    const int index = m_model->index(KUrl(path));
+    if (index >= 0) {
+        if (!m_model->fileItem(index).isDir()) {
+            // If INotify is used, KDirWatch issues the dirty() signal
+            // also for changed files inside the directory, even if we
+            // don't enable this behavior explicitly (see bug 309740).
+            return;
+        }
+
+        startWorker(path);
+    }
+}
+
+void KDirectoryContentsCounter::slotItemsRemoved()
+{
+    const bool allItemsRemoved = (m_model->count() == 0);
+
+    if (!m_watchedDirs.isEmpty()) {
+        // Don't let KDirWatch watch for removed items
+        if (allItemsRemoved) {
+            foreach (const QString& path, m_watchedDirs) {
+                m_dirWatcher->removeDir(path);
+            }
+            m_watchedDirs.clear();
+            m_queue.clear();
+        } else {
+            QMutableSetIterator<QString> it(m_watchedDirs);
+            while (it.hasNext()) {
+                const QString& path = it.next();
+                if (m_model->index(KUrl(path)) < 0) {
+                    m_dirWatcher->removeDir(path);
+                    it.remove();
+                }
+            }
+        }
+    }
+}
+
+void KDirectoryContentsCounter::startWorker(const QString& path)
+{
+    if (m_workerIsBusy) {
+        m_queue.enqueue(path);
+    } else {
+        KDirectoryContentsCounterWorker::Options options;
+
+        if (m_model->showHiddenFiles()) {
+            options |= KDirectoryContentsCounterWorker::CountHiddenFiles;
+        }
+
+        if (m_model->showDirectoriesOnly()) {
+            options |= KDirectoryContentsCounterWorker::CountDirectoriesOnly;
+        }
+
+        emit requestDirectoryContentsCount(path, options);
+        m_workerIsBusy = true;
+    }
+}
diff --git a/src/kitemviews/private/kdirectorycontentscounter.h b/src/kitemviews/private/kdirectorycontentscounter.h
new file mode 100644 (file)
index 0000000..425c363
--- /dev/null
@@ -0,0 +1,90 @@
+/***************************************************************************
+ *   Copyright (C) 2011 by Peter Penz <peter.penz19@gmail.com>             *
+ *   Copyright (C) 2013 by Frank Reininghaus <frank78ac@googlemail.com>    *
+ *                                                                         *
+ *   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 KDIRECTORYCONTENTSCOUNTER_H
+#define KDIRECTORYCONTENTSCOUNTER_H
+
+#include "kdirectorycontentscounterworker.h"
+
+#include <QSet>
+#include <QQueue>
+
+class KDirWatch;
+class KFileItemModel;
+class QString;
+
+class KDirectoryContentsCounter : public QObject
+{
+    Q_OBJECT
+
+public:
+    explicit KDirectoryContentsCounter(KFileItemModel* model, QObject* parent = 0);
+    ~KDirectoryContentsCounter();
+
+    /**
+     * Requests the number of items inside the directory \a path. The actual
+     * counting is done asynchronously, and the result is announced via the
+     * signal \a result.
+     *
+     * The directory \a path is watched for changes, and the signal is emitted
+     * again if a change occurs.
+     */
+    void addDirectory(const QString& path);
+
+    /**
+     * In contrast to \a addDirectory, this function counts the items inside
+     * the directory \a path synchronously and returns the result.
+     *
+     * The directory is watched for changes, and the signal \a result is
+     * emitted if a change occurs.
+     */
+    int countDirectoryContentsSynchronously(const QString& path);
+
+signals:
+    /**
+     * Signals that the directory \a path contains \a count items.
+     */
+    void result(const QString& path, int count);
+
+    void requestDirectoryContentsCount(const QString& path, KDirectoryContentsCounterWorker::Options options);
+
+private slots:
+    void slotResult(const QString& path, int count);
+    void slotDirWatchDirty(const QString& path);
+    void slotItemsRemoved();
+
+private:
+    void startWorker(const QString& path);
+
+private:
+    KFileItemModel* m_model;
+
+    QQueue<QString> m_queue;
+
+    QThread* m_workerThread;
+    KDirectoryContentsCounterWorker* m_worker;
+    bool m_workerIsBusy;
+
+    KDirWatch* m_dirWatcher;
+    QSet<QString> m_watchedDirs;    // Required as sadly KDirWatch does not offer a getter method
+                                    // to get all watched directories.
+};
+
+#endif
\ No newline at end of file
diff --git a/src/kitemviews/private/kdirectorycontentscounterworker.cpp b/src/kitemviews/private/kdirectorycontentscounterworker.cpp
new file mode 100644 (file)
index 0000000..e649c20
--- /dev/null
@@ -0,0 +1,95 @@
+/***************************************************************************
+ *   Copyright (C) 2011 by Peter Penz <peter.penz19@gmail.com>             *
+ *   Copyright (C) 2013 by Frank Reininghaus <frank78ac@googlemail.com>    *
+ *                                                                         *
+ *   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 "kdirectorycontentscounterworker.h"
+
+// Required includes for subItemsCount():
+#ifdef Q_WS_WIN
+    #include <QDir>
+#else
+    #include <dirent.h>
+    #include <QFile>
+#endif
+
+KDirectoryContentsCounterWorker::KDirectoryContentsCounterWorker(QObject* parent) :
+    QObject(parent)
+{
+    qRegisterMetaType<KDirectoryContentsCounterWorker::Options>();
+}
+
+int KDirectoryContentsCounterWorker::subItemsCount(const QString& path, Options options)
+{
+    const bool countHiddenFiles = options & CountHiddenFiles;
+    const bool countDirectoriesOnly = options & CountDirectoriesOnly;
+
+#ifdef Q_WS_WIN
+    QDir dir(path);
+    QDir::Filters filters = QDir::NoDotAndDotDot | QDir::System;
+    if (countHiddenFiles) {
+        filters |= QDir::Hidden;
+    }
+    if (countDirectoriesOnly) {
+        filters |= QDir::Dirs;
+    } else {
+        filters |= QDir::AllEntries;
+    }
+    return dir.entryList(filters).count();
+#else
+    // Taken from kdelibs/kio/kio/kdirmodel.cpp
+    // Copyright (C) 2006 David Faure <faure@kde.org>
+
+    int count = -1;
+    DIR* dir = ::opendir(QFile::encodeName(path));
+    if (dir) {  // krazy:exclude=syscalls
+        count = 0;
+        struct dirent *dirEntry = 0;
+        while ((dirEntry = ::readdir(dir))) {
+            if (dirEntry->d_name[0] == '.') {
+                if (dirEntry->d_name[1] == '\0' || !countHiddenFiles) {
+                    // Skip "." or hidden files
+                    continue;
+                }
+                if (dirEntry->d_name[1] == '.' && dirEntry->d_name[2] == '\0') {
+                    // Skip ".."
+                    continue;
+                }
+            }
+
+            // If only directories are counted, consider an unknown file type and links also
+            // as directory instead of trying to do an expensive stat()
+            // (see bugs 292642 and 299997).
+            const bool countEntry = !countDirectoriesOnly ||
+                                    dirEntry->d_type == DT_DIR ||
+                                    dirEntry->d_type == DT_LNK ||
+                                    dirEntry->d_type == DT_UNKNOWN;
+            if (countEntry) {
+                ++count;
+            }
+        }
+        ::closedir(dir);
+    }
+    return count;
+#endif
+}
+
+void KDirectoryContentsCounterWorker::countDirectoryContents(const QString& path, Options options)
+{
+    emit result(path, subItemsCount(path, options));
+}
diff --git a/src/kitemviews/private/kdirectorycontentscounterworker.h b/src/kitemviews/private/kdirectorycontentscounterworker.h
new file mode 100644 (file)
index 0000000..96831ef
--- /dev/null
@@ -0,0 +1,71 @@
+/***************************************************************************
+ *   Copyright (C) 2013 by Frank Reininghaus <frank78ac@googlemail.com>    *
+ *                                                                         *
+ *   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 KDIRECTORYCONTENTENTSCOUNTERWORKER_H
+#define KDIRECTORYCONTENTENTSCOUNTERWORKER_H
+
+#include <QFlags>
+#include <QMetaType>
+#include <QObject>
+
+class QString;
+
+class KDirectoryContentsCounterWorker : public QObject
+{
+    Q_OBJECT
+
+public:
+    enum Option {
+        NoOptions = 0x0,
+        CountHiddenFiles = 0x1,
+        CountDirectoriesOnly = 0x2
+    };
+    Q_DECLARE_FLAGS(Options, Option)
+
+    explicit KDirectoryContentsCounterWorker(QObject* parent = 0);
+
+    /**
+     * Counts the items inside the directory \a path using the options
+     * \a options.
+     *
+     * @return The number of items.
+     */
+    static int subItemsCount(const QString& path, Options options);
+
+signals:
+    /**
+     * Signals that the directory \a path contains \a count items.
+     */
+    void result(const QString& path, int count);
+
+public slots:
+    /**
+     * Requests the number of items inside the directory \a path using the
+     * options \a options. The result is announced via the signal \a result.
+     */
+    // Note that the full type name KDirectoryContentsCounterWorker::Options
+    // is needed here. Just using 'Options' is OK for the compiler, but
+    // confuses moc.
+    void countDirectoryContents(const QString& path, KDirectoryContentsCounterWorker::Options options);
+};
+
+Q_DECLARE_METATYPE(KDirectoryContentsCounterWorker::Options)
+Q_DECLARE_OPERATORS_FOR_FLAGS(KDirectoryContentsCounterWorker::Options)
+
+#endif
index e44630243b1c148325f26689a2a03d093563c68a..0e2286b453a9c539358528318288c9e695de1b6c 100644 (file)
@@ -120,7 +120,7 @@ void KItemListSizeHintResolver::itemsMoved(const KItemRange& range, const QList<
 
     const int movedRangeEnd = range.index + range.count;
     for (int i = range.index; i < movedRangeEnd; ++i) {
-        const int newIndex = movedToIndexes.at(i);
+        const int newIndex = movedToIndexes.at(i - range.index);
         newSizeHintCache[newIndex] = m_sizeHintCache.at(i);
     }
 
@@ -139,8 +139,5 @@ void KItemListSizeHintResolver::itemsChanged(int index, int count, const QSet<QB
 
 void KItemListSizeHintResolver::clearCache()
 {
-    const int count = m_sizeHintCache.count();
-    for (int i = 0; i < count; ++i) {
-        m_sizeHintCache[i] = QSizeF();
-    }
+    m_sizeHintCache.fill(QSizeF());
 }
index da569b3dc6cf0ee97b8b119efd9aaba31ea4eb03..f5f63d5ab363127d837c2a9705918eb73a09c32c 100644 (file)
@@ -239,20 +239,30 @@ QRectF KItemListViewLayouter::itemRect(int index) const
         return QRectF();
     }
 
+    QSizeF sizeHint;
+    if (m_sizeHintResolver) {
+        sizeHint = m_sizeHintResolver->sizeHint(index);
+    } else {
+        sizeHint = m_itemSize;
+    }
+
     if (m_scrollOrientation == Qt::Horizontal) {
         // Rotate the logical direction which is always vertical by 90°
         // to get the physical horizontal direction
-        const QRectF& b = m_itemInfos[index].rect;
-        QRectF bounds(b.y(), b.x(), b.height(), b.width());
-        QPointF pos = bounds.topLeft();
+        const QPointF logicalPos = m_itemInfos[index].pos;
+        QPointF pos(logicalPos.y(), logicalPos.x());
         pos.rx() -= m_scrollOffset;
-        bounds.moveTo(pos);
-        return bounds;
+        return QRectF(pos, sizeHint);
     }
 
-    QRectF bounds = m_itemInfos[index].rect;
-    bounds.moveTo(bounds.topLeft() - QPointF(m_itemOffset, m_scrollOffset));
-    return bounds;
+    if (sizeHint.width() <= 0) {
+        // In Details View, a size hint with negative width is used internally.
+        sizeHint.rwidth() = m_itemSize.width();
+    }
+
+    QPointF pos = m_itemInfos[index].pos;
+    pos -= QPointF(m_itemOffset, m_scrollOffset);
+    return QRectF(pos, sizeHint);
 }
 
 QRectF KItemListViewLayouter::groupHeaderRect(int index) const
@@ -278,23 +288,30 @@ QRectF KItemListViewLayouter::groupHeaderRect(int index) const
         // current column. As the scroll-direction is
         // Qt::Horizontal and m_itemRects is accessed directly,
         // the logical height represents the visual width.
-        qreal width = minimumGroupHeaderWidth();
-        const qreal y = m_itemInfos[index].rect.y();
+        qreal headerWidth = minimumGroupHeaderWidth();
+        const qreal y = m_itemInfos[index].pos.y();
         const int maxIndex = m_itemInfos.count() - 1;
         while (index <= maxIndex) {
-            QRectF bounds = m_itemInfos[index].rect;
-            if (bounds.y() != y) {
+            const QPointF pos = m_itemInfos[index].pos;
+            if (pos.y() != y) {
                 break;
             }
 
-            if (bounds.height() > width) {
-                width = bounds.height();
+            qreal itemWidth;
+            if (m_sizeHintResolver) {
+                itemWidth = m_sizeHintResolver->sizeHint(index).width();
+            } else {
+                itemWidth = m_itemSize.width();
+            }
+
+            if (itemWidth > headerWidth) {
+                headerWidth = itemWidth;
             }
 
             ++index;
         }
 
-        size = QSizeF(width, m_size.height());
+        size = QSizeF(headerWidth, m_size.height());
     }
     return QRectF(pos, size);
 }
@@ -375,12 +392,9 @@ void KItemListViewLayouter::doLayout()
         if (horizontalScrolling) {
             // Flip everything so that the layout logically can work like having
             // a vertical scrolling
-            itemSize.setWidth(m_itemSize.height());
-            itemSize.setHeight(m_itemSize.width());
-            itemMargin.setWidth(m_itemMargin.height());
-            itemMargin.setHeight(m_itemMargin.width());
-            size.setWidth(m_size.height());
-            size.setHeight(m_size.width());
+            itemSize.transpose();
+            itemMargin.transpose();
+            size.transpose();
 
             if (grouped) {
                 // In the horizontal scrolling case all groups are aligned
@@ -406,12 +420,7 @@ void KItemListViewLayouter::doLayout()
             }
         }
 
-        int rowCount = itemCount / m_columnCount;
-        if (itemCount % m_columnCount != 0) {
-            ++rowCount;
-        }
-
-        m_itemInfos.reserve(itemCount);
+        m_itemInfos.resize(itemCount);
 
         qreal y = m_headerHeight + itemMargin.height();
         int row = 0;
@@ -458,18 +467,10 @@ void KItemListViewLayouter::doLayout()
                     }
                 }
 
-                const QRectF bounds(x, y, itemSize.width(), requiredItemHeight);
-                if (index < m_itemInfos.count()) {
-                    m_itemInfos[index].rect = bounds;
-                    m_itemInfos[index].column = column;
-                    m_itemInfos[index].row = row;
-                } else {
-                    ItemInfo itemInfo;
-                    itemInfo.rect = bounds;
-                    itemInfo.column = column;
-                    itemInfo.row = row;
-                    m_itemInfos.append(itemInfo);
-                }
+                ItemInfo& itemInfo = m_itemInfos[index];
+                itemInfo.pos = QPointF(x, y);
+                itemInfo.column = column;
+                itemInfo.row = row;
 
                 if (grouped && horizontalScrolling) {
                     // When grouping is enabled in the horizontal mode, the header alignment
@@ -505,24 +506,9 @@ void KItemListViewLayouter::doLayout()
             y += maxItemHeight + itemMargin.height();
             ++row;
         }
-        if (m_itemInfos.count() > itemCount) {
-            m_itemInfos.erase(m_itemInfos.begin() + itemCount,
-                              m_itemInfos.end());
-        }
 
         if (itemCount > 0) {
-            // Calculate the maximum y-range of the last row for m_maximumScrollOffset
-            m_maximumScrollOffset = m_itemInfos.last().rect.bottom();
-            const qreal rowY = m_itemInfos.last().rect.y();
-
-            int index = m_itemInfos.count() - 2;
-            while (index >= 0 && m_itemInfos[index].rect.bottom() >= rowY) {
-                m_maximumScrollOffset = qMax(m_maximumScrollOffset, m_itemInfos[index].rect.bottom());
-                --index;
-            }
-
-            m_maximumScrollOffset += itemMargin.height();
-
+            m_maximumScrollOffset = y;
             m_maximumItemOffset = m_columnCount * m_columnWidth;
         } else {
             m_maximumScrollOffset = 0;
@@ -561,7 +547,7 @@ void KItemListViewLayouter::updateVisibleIndexes()
     int mid = 0;
     do {
         mid = (min + max) / 2;
-        if (m_itemInfos[mid].rect.top() < m_scrollOffset) {
+        if (m_itemInfos[mid].pos.y() < m_scrollOffset) {
             min = mid + 1;
         } else {
             max = mid - 1;
@@ -571,13 +557,13 @@ void KItemListViewLayouter::updateVisibleIndexes()
     if (mid > 0) {
         // Include the row before the first fully visible index, as it might
         // be partly visible
-        if (m_itemInfos[mid].rect.top() >= m_scrollOffset) {
+        if (m_itemInfos[mid].pos.y() >= m_scrollOffset) {
             --mid;
-            Q_ASSERT(m_itemInfos[mid].rect.top() < m_scrollOffset);
+            Q_ASSERT(m_itemInfos[mid].pos.y() < m_scrollOffset);
         }
 
-        const qreal rowTop = m_itemInfos[mid].rect.top();
-        while (mid > 0 && m_itemInfos[mid - 1].rect.top() == rowTop) {
+        const qreal rowTop = m_itemInfos[mid].pos.y();
+        while (mid > 0 && m_itemInfos[mid - 1].pos.y() == rowTop) {
             --mid;
         }
     }
@@ -594,14 +580,14 @@ void KItemListViewLayouter::updateVisibleIndexes()
     max = maxIndex;
     do {
         mid = (min + max) / 2;
-        if (m_itemInfos[mid].rect.y() <= bottom) {
+        if (m_itemInfos[mid].pos.y() <= bottom) {
             min = mid + 1;
         } else {
             max = mid - 1;
         }
     } while (min <= max);
 
-    while (mid > 0 && m_itemInfos[mid].rect.y() > bottom) {
+    while (mid > 0 && m_itemInfos[mid].pos.y() > bottom) {
         --mid;
     }
     m_lastVisibleIndex = mid;
index 306fcd3605b0b1990c4dbce4f83b1d0e16d28137..a3b0893a1e0debb0089645b8f07299edebaed1e4 100644 (file)
@@ -227,7 +227,7 @@ private:
     qreal m_groupHeaderMargin;
 
     struct ItemInfo {
-        QRectF rect;
+        QPointF pos;
         int column;
         int row;
     };
index eae2095c994ff37fe78fb8dc9a82a1be2cdda644..681479dfbad0ea0ae43883f252a764ff95d4511b 100644 (file)
@@ -377,7 +377,7 @@ void PlacesItemModel::requestStorageSetup(int index)
     }
 }
 
-QMimeData* PlacesItemModel::createMimeData(const QSet<int>& indexes) const
+QMimeData* PlacesItemModel::createMimeData(const KItemSet& indexes) const
 {
     KUrl::List urls;
     QByteArray itemData;
index 693836033265e2978f6981d9a10b05d37ef117ff..cd37e7353583d288fcf602935144bc48394442de 100644 (file)
@@ -118,7 +118,7 @@ public:
     void requestStorageSetup(int index);
 
     /** @reimp */
-    virtual QMimeData* createMimeData(const QSet<int>& indexes) const;
+    virtual QMimeData* createMimeData(const KItemSet& indexes) const;
 
     /** @reimp */
     virtual bool supportsDropping(int index) const;
index 5f3772d28132cf32ff5143a7cabd6982be05fc38..54bece16a3537cb32704414b02b2ae7fa01d888e 100644 (file)
@@ -297,6 +297,7 @@ Comment[zh_CN]=配置常规文件管理器设置
 Comment[zh_TW]=設定一般檔案管理員
 X-KDE-Keywords=file manager
 X-KDE-Keywords[ar]=مدير الملفات
+X-KDE-Keywords[bg]=преглед на файлове
 X-KDE-Keywords[bs]=upravitelj datoteka
 X-KDE-Keywords[ca]=gestor de fitxers
 X-KDE-Keywords[ca@valencia]=gestor de fitxers
index ab79449f5a9512a82c2b05c65605f566c184b51b..cecf495bd3f8e9152743afdd69efd06771edea6f 100644 (file)
@@ -297,6 +297,7 @@ Comment[zh_CN]=配置文件管理器导航
 Comment[zh_TW]=設定檔案管理員導覽
 X-KDE-Keywords=file manager
 X-KDE-Keywords[ar]=مدير الملفات
+X-KDE-Keywords[bg]=преглед на файлове
 X-KDE-Keywords[bs]=upravitelj datoteka
 X-KDE-Keywords[ca]=gestor de fitxers
 X-KDE-Keywords[ca@valencia]=gestor de fitxers
index 14436dd99fba0452b7b85efb3e3671ce4fec1fa1..de88fb58957cadc20e5103e03dc10576faa5bcfd 100644 (file)
@@ -246,6 +246,7 @@ Comment[zh_CN]=配置文件管理器服务
 Comment[zh_TW]=設定檔案管理員服務
 X-KDE-Keywords=file manager
 X-KDE-Keywords[ar]=مدير الملفات
+X-KDE-Keywords[bg]=преглед на файлове
 X-KDE-Keywords[bs]=upravitelj datoteka
 X-KDE-Keywords[ca]=gestor de fitxers
 X-KDE-Keywords[ca@valencia]=gestor de fitxers
index a05d944c2ef726f315e30d95ce186ed34f55ad90..205570cc05780b9cecc47850b753cf6fd471c126 100644 (file)
@@ -295,6 +295,7 @@ Comment[zh_CN]=配置文件管理器视图模式
 Comment[zh_TW]=設定檔案管理員檢視模式
 X-KDE-Keywords=file manager
 X-KDE-Keywords[ar]=مدير الملفات
+X-KDE-Keywords[bg]=преглед на файлове
 X-KDE-Keywords[bs]=upravitelj datoteka
 X-KDE-Keywords[ca]=gestor de fitxers
 X-KDE-Keywords[ca@valencia]=gestor de fitxers
index dd761fc90b0113f81359e30a2fd2531a146e314a..9b152ed07d4f3be36f80e59a38a3e93979e4e14a 100644 (file)
@@ -3,11 +3,21 @@ include_directories( ${CMAKE_CURRENT_SOURCE_DIR}/.. ${CMAKE_CURRENT_BUILD_DIR}/.
 
 # needed on windows to correctly use the files from dolphinprivate
 add_definitions(-DLIBDOLPHINPRIVATE_EXPORT=)
+
+# KItemSetTest
+set(kitemsettest_SRCS
+    kitemsettest.cpp
+    ../kitemviews/kitemset.cpp
+)
+kde4_add_unit_test(kitemsettest TEST ${kitemsettest_SRCS})
+target_link_libraries(kitemsettest dolphinprivate ${KDE4_KIO_LIBS} ${QT_QTTEST_LIBRARY})
+
 # KItemListSelectionManagerTest
 set(kitemlistselectionmanagertest_SRCS
     kitemlistselectionmanagertest.cpp
     ../kitemviews/kitemlistselectionmanager.cpp
     ../kitemviews/kitemmodelbase.cpp
+    ../kitemviews/kitemset.cpp
 )
 kde4_add_unit_test(kitemlistselectionmanagertest TEST ${kitemlistselectionmanagertest_SRCS})
 target_link_libraries(kitemlistselectionmanagertest dolphinprivate ${KDE4_KIO_LIBS} ${QT_QTTEST_LIBRARY})
@@ -24,6 +34,7 @@ set(kitemlistcontrollertest_SRCS
     ../kitemviews/kitemlistcontainer.cpp
     ../kitemviews/kitemlistwidget.cpp
     ../kitemviews/kitemlistviewaccessible.cpp
+    ../kitemviews/kitemset.cpp
     ../kitemviews/kstandarditemlistview.cpp
     ../kitemviews/kstandarditemlistwidget.cpp
 )
@@ -41,6 +52,7 @@ set(kfileitemlistviewtest_SRCS
     ../kitemviews/kitemlistviewaccessible.cpp
     ../kitemviews/kitemlistcontainer.cpp
     ../kitemviews/kitemlistwidget.cpp
+    ../kitemviews/kitemset.cpp
     ../kitemviews/kstandarditemlistview.cpp
     ../kitemviews/kstandarditemlistwidget.cpp
 )
index f72e43edeb982a42518604c9b7918758d21d697f..66918b6eec50bd021da324f00a18a6004f0fe1a6 100644 (file)
@@ -185,7 +185,7 @@ void KFileItemModelBenchmark::insertAndRemoveManyItems()
         QCOMPARE(model.count(), initialItems.count() + newItems.count());
 
         if (!removedItems.isEmpty()) {
-            model.removeItems(removedItems, KFileItemModel::DeleteItemData);
+            model.slotItemsDeleted(removedItems);
         }
         QCOMPARE(model.count(), initialItems.count() + newItems.count() - removedItems.count());
     }
index 5dd3417fc5c43225c58328e4b58ebbe987f40382..62ff4fae2d705bb9953a01e0a55c3e4cd1e32075 100644 (file)
@@ -49,6 +49,7 @@ namespace {
     const int DefaultTimeout = 5000;
 };
 
+Q_DECLARE_METATYPE(KItemRange)
 Q_DECLARE_METATYPE(KItemRangeList)
 Q_DECLARE_METATYPE(QList<int>)
 
@@ -88,6 +89,7 @@ private slots:
     void testGeneralParentChildRelationships();
     void testNameRoleGroups();
     void testNameRoleGroupsWithExpandedItems();
+    void testInconsistentModel();
 
 private:
     QStringList itemsInModel() const;
@@ -499,7 +501,8 @@ void KFileItemModelTest::testExpandItems()
     // KFileItemModel::expansionLevelsCompare(const KFileItem& a, const KFileItem& b)
     // yields the correct result for "a/a/1" and "a/a-1/", whis is non-trivial because they share the
     // first three characters.
-    QSet<QByteArray> modelRoles = m_model->roles();
+    QSet<QByteArray> originalModelRoles = m_model->roles();
+    QSet<QByteArray> modelRoles = originalModelRoles;
     modelRoles << "isExpanded" << "isExpandable" << "expandedParentsCount";
     m_model->setRoles(modelRoles);
 
@@ -606,6 +609,18 @@ void KFileItemModelTest::testExpandItems()
     QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(directoryLoadingCompleted()), DefaultTimeout));
     QCOMPARE(m_model->count(), 5);  // 5 items: "a/", "a/a/", "a/a/1", "a/a-1/", "a/a-1/1"
     QCOMPARE(m_model->expandedDirectories(), allFolders);
+
+    // Remove all expanded items by changing the roles
+    spyRemoved.clear();
+    m_model->setRoles(originalModelRoles);
+    QVERIFY(!m_model->isExpanded(0));
+    QCOMPARE(m_model->count(), 1);
+    QVERIFY(!m_model->expandedDirectories().contains(KUrl(m_testDir->name() + 'a')));
+
+    QCOMPARE(spyRemoved.count(), 1);
+    itemRangeList = spyRemoved.takeFirst().at(0).value<KItemRangeList>();
+    QCOMPARE(itemRangeList, KItemRangeList() << KItemRange(1, 4)); // 4 items removed
+    QVERIFY(m_model->isConsistent());
 }
 
 void KFileItemModelTest::testExpandParentItems()
@@ -658,6 +673,28 @@ void KFileItemModelTest::testExpandParentItems()
     QVERIFY(m_model->isExpanded(3));
     QVERIFY(!m_model->isExpanded(4));
     QVERIFY(m_model->isConsistent());
+
+    // Expand "a 1/b1/".
+    m_model->setExpanded(1, true);
+    QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(directoryLoadingCompleted()), DefaultTimeout));
+    QCOMPARE(m_model->count(), 6);
+    QVERIFY(m_model->isExpanded(0));
+    QVERIFY(m_model->isExpanded(1));
+    QVERIFY(!m_model->isExpanded(2));
+    QVERIFY(m_model->isExpanded(3));
+    QVERIFY(m_model->isExpanded(4));
+    QVERIFY(!m_model->isExpanded(5));
+    QVERIFY(m_model->isConsistent());
+
+    // Collapse "a 1/b1/" again, and verify that the previous state is restored.
+    m_model->setExpanded(1, false);
+    QCOMPARE(m_model->count(), 5);
+    QVERIFY(m_model->isExpanded(0));
+    QVERIFY(!m_model->isExpanded(1));
+    QVERIFY(m_model->isExpanded(2));
+    QVERIFY(m_model->isExpanded(3));
+    QVERIFY(!m_model->isExpanded(4));
+    QVERIFY(m_model->isConsistent());
 }
 
 /**
@@ -803,7 +840,8 @@ void KFileItemModelTest::testSorting()
     QCOMPARE(m_model->sortOrder(), Qt::AscendingOrder);
     QCOMPARE(itemsInModel(), QStringList() << "a" << "b" << "c" << "c-1" << "c-2" << "c-3" << "d" << "e");
     QCOMPARE(spyItemsMoved.count(), 1);
-    QCOMPARE(spyItemsMoved.takeFirst().at(1).value<QList<int> >(), QList<int>() << 2 << 4 << 5 << 3 << 0 << 1 << 6 << 7);
+    QCOMPARE(spyItemsMoved.first().at(0).value<KItemRange>(), KItemRange(0, 6));
+    QCOMPARE(spyItemsMoved.takeFirst().at(1).value<QList<int> >(), QList<int>() << 2 << 4 << 5 << 3 << 0 << 1);
 
     // Sort by Name, descending
     m_model->setSortDirectoriesFirst(true);
@@ -812,8 +850,10 @@ void KFileItemModelTest::testSorting()
     QCOMPARE(m_model->sortOrder(), Qt::DescendingOrder);
     QCOMPARE(itemsInModel(), QStringList() << "c" << "c-2" << "c-3" << "c-1" << "e" << "d" << "b" << "a");
     QCOMPARE(spyItemsMoved.count(), 2);
-    QCOMPARE(spyItemsMoved.takeFirst().at(1).value<QList<int> >(), QList<int>() << 4 << 5 << 0 << 3 << 1 << 2 << 6 << 7);
-    QCOMPARE(spyItemsMoved.takeFirst().at(1).value<QList<int> >(), QList<int>() << 0 << 1 << 2 << 3 << 7 << 6 << 5 << 4);
+    QCOMPARE(spyItemsMoved.first().at(0).value<KItemRange>(), KItemRange(0, 6));
+    QCOMPARE(spyItemsMoved.takeFirst().at(1).value<QList<int> >(), QList<int>() << 4 << 5 << 0 << 3 << 1 << 2);
+    QCOMPARE(spyItemsMoved.first().at(0).value<KItemRange>(), KItemRange(4, 4));
+    QCOMPARE(spyItemsMoved.takeFirst().at(1).value<QList<int> >(), QList<int>() << 7 << 6 << 5 << 4);
 
     // Sort by Date, descending
     m_model->setSortDirectoriesFirst(true);
@@ -822,7 +862,8 @@ void KFileItemModelTest::testSorting()
     QCOMPARE(m_model->sortOrder(), Qt::DescendingOrder);
     QCOMPARE(itemsInModel(), QStringList() << "c" << "c-2" << "c-3" << "c-1" << "b" << "d" << "a" << "e");
     QCOMPARE(spyItemsMoved.count(), 1);
-    QCOMPARE(spyItemsMoved.takeFirst().at(1).value<QList<int> >(), QList<int>() << 0 << 1 << 2 << 3 << 7 << 5 << 4 << 6);
+    QCOMPARE(spyItemsMoved.first().at(0).value<KItemRange>(), KItemRange(4, 4));
+    QCOMPARE(spyItemsMoved.takeFirst().at(1).value<QList<int> >(), QList<int>() << 7 << 5 << 4 << 6);
 
     // Sort by Date, ascending
     m_model->setSortOrder(Qt::AscendingOrder);
@@ -830,7 +871,8 @@ void KFileItemModelTest::testSorting()
     QCOMPARE(m_model->sortOrder(), Qt::AscendingOrder);
     QCOMPARE(itemsInModel(), QStringList() << "c" << "c-2" << "c-3" << "c-1" << "e" << "a" << "d" << "b");
     QCOMPARE(spyItemsMoved.count(), 1);
-    QCOMPARE(spyItemsMoved.takeFirst().at(1).value<QList<int> >(), QList<int>() << 0 << 1 << 2 << 3 << 7 << 6 << 5 << 4);
+    QCOMPARE(spyItemsMoved.first().at(0).value<KItemRange>(), KItemRange(4, 4));
+    QCOMPARE(spyItemsMoved.takeFirst().at(1).value<QList<int> >(), QList<int>() << 7 << 6 << 5 << 4);
 
     // Sort by Date, ascending, 'Sort Folders First' disabled
     m_model->setSortDirectoriesFirst(false);
@@ -839,7 +881,8 @@ void KFileItemModelTest::testSorting()
     QVERIFY(!m_model->sortDirectoriesFirst());
     QCOMPARE(itemsInModel(), QStringList() << "e" << "a" << "c" << "c-1" << "c-2" << "c-3" << "d" << "b");
     QCOMPARE(spyItemsMoved.count(), 1);
-    QCOMPARE(spyItemsMoved.takeFirst().at(1).value<QList<int> >(), QList<int>() << 2 << 4 << 5 << 3 << 0 << 1 << 6 << 7);
+    QCOMPARE(spyItemsMoved.first().at(0).value<KItemRange>(), KItemRange(0, 6));
+    QCOMPARE(spyItemsMoved.takeFirst().at(1).value<QList<int> >(), QList<int>() << 2 << 4 << 5 << 3 << 0 << 1);
 
     // Sort by Name, ascending, 'Sort Folders First' disabled
     m_model->setSortRole("text");
@@ -847,6 +890,7 @@ void KFileItemModelTest::testSorting()
     QVERIFY(!m_model->sortDirectoriesFirst());
     QCOMPARE(itemsInModel(), QStringList() << "a" << "b" << "c" << "c-1" << "c-2" << "c-3" << "d" << "e");
     QCOMPARE(spyItemsMoved.count(), 1);
+    QCOMPARE(spyItemsMoved.first().at(0).value<KItemRange>(), KItemRange(0, 8));
     QCOMPARE(spyItemsMoved.takeFirst().at(1).value<QList<int> >(), QList<int>() << 7 << 0 << 2 << 3 << 4 << 5 << 6 << 1);
 
     // Sort by Size, ascending, 'Sort Folders First' disabled
@@ -856,19 +900,15 @@ void KFileItemModelTest::testSorting()
     QVERIFY(!m_model->sortDirectoriesFirst());
     QCOMPARE(itemsInModel(), QStringList() << "c" << "c-2" << "c-3" << "c-1" << "a" << "b" << "e" << "d");
     QCOMPARE(spyItemsMoved.count(), 1);
+    QCOMPARE(spyItemsMoved.first().at(0).value<KItemRange>(), KItemRange(0, 8));
     QCOMPARE(spyItemsMoved.takeFirst().at(1).value<QList<int> >(), QList<int>() << 4 << 5 << 0 << 3 << 1 << 2 << 7 << 6);
 
-    QSKIP("2 tests of testSorting() are temporary deactivated as in KFileItemModel resortAllItems() "
-          "always emits a itemsMoved() signal. Before adjusting the tests think about probably introducing "
-          "another signal", SkipSingle);
-    // Internal note: Check comment in KFileItemModel::resortAllItems() for details.
-
     // In 'Sort by Size' mode, folders are always first -> changing 'Sort Folders First' does not resort the model
     m_model->setSortDirectoriesFirst(true);
     QCOMPARE(m_model->sortRole(), QByteArray("size"));
     QCOMPARE(m_model->sortOrder(), Qt::AscendingOrder);
     QVERIFY(m_model->sortDirectoriesFirst());
-    QCOMPARE(itemsInModel(), QStringList() << "c" << "a" << "b" << "e" << "d");
+    QCOMPARE(itemsInModel(), QStringList() << "c" << "c-2" << "c-3" << "c-1" << "a" << "b" << "e" << "d");
     QCOMPARE(spyItemsMoved.count(), 0);
 
     // Sort by Size, descending, 'Sort Folders First' enabled
@@ -876,9 +916,10 @@ void KFileItemModelTest::testSorting()
     QCOMPARE(m_model->sortRole(), QByteArray("size"));
     QCOMPARE(m_model->sortOrder(), Qt::DescendingOrder);
     QVERIFY(m_model->sortDirectoriesFirst());
-    QCOMPARE(itemsInModel(), QStringList() << "c" << "d" << "e" << "b" << "a");
+    QCOMPARE(itemsInModel(), QStringList() << "c" << "c-2" << "c-3" << "c-1" << "d" << "e" << "b" << "a");
     QCOMPARE(spyItemsMoved.count(), 1);
-    QCOMPARE(spyItemsMoved.takeFirst().at(1).value<QList<int> >(), QList<int>() << 0 << 4 << 3 << 2 << 1);
+    QCOMPARE(spyItemsMoved.first().at(0).value<KItemRange>(), KItemRange(4, 4));
+    QCOMPARE(spyItemsMoved.takeFirst().at(1).value<QList<int> >(), QList<int>() << 7 << 6 << 5 << 4);
 
     // TODO: Sort by other roles; show/hide hidden files
 }
@@ -1296,7 +1337,7 @@ void KFileItemModelTest::testNameRoleGroups()
     // Rename c.txt to d.txt.
     data.insert("text", "d.txt");
     m_model->setData(2, data);
-    QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsMoved(KItemRange,QList<int>)), DefaultTimeout));
+    QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(groupsChanged()), DefaultTimeout));
     QCOMPARE(itemsInModel(), QStringList() << "a.txt" << "b.txt" << "d.txt" << "e.txt");
 
     expectedGroups.clear();
@@ -1314,7 +1355,7 @@ void KFileItemModelTest::testNameRoleGroups()
     fileItemC.setUrl(urlC);
 
     m_model->slotRefreshItems(QList<QPair<KFileItem, KFileItem> >() << qMakePair(fileItemD, fileItemC));
-    QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsMoved(KItemRange,QList<int>)), DefaultTimeout));
+    QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(groupsChanged()), DefaultTimeout));
     QCOMPARE(itemsInModel(), QStringList() << "a.txt" << "b.txt" << "c.txt" << "e.txt");
 
     expectedGroups.clear();
@@ -1364,6 +1405,63 @@ void KFileItemModelTest::testNameRoleGroupsWithExpandedItems()
     QCOMPARE(m_model->groups(), expectedGroups);
 }
 
+void KFileItemModelTest::testInconsistentModel()
+{
+    QSet<QByteArray> modelRoles = m_model->roles();
+    modelRoles << "isExpanded" << "isExpandable" << "expandedParentsCount";
+    m_model->setRoles(modelRoles);
+
+    QStringList files;
+    files << "a/b/c1.txt" << "a/b/c2.txt";
+
+    m_testDir->createFiles(files);
+
+    m_model->loadDirectory(m_testDir->url());
+    QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsInserted(KItemRangeList)), DefaultTimeout));
+    QCOMPARE(itemsInModel(), QStringList() << "a");
+
+    // Expand "a/" and "a/b/".
+    m_model->setExpanded(0, true);
+    QVERIFY(m_model->isExpanded(0));
+    QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsInserted(KItemRangeList)), DefaultTimeout));
+    QCOMPARE(itemsInModel(), QStringList() << "a" << "b");
+
+    m_model->setExpanded(1, true);
+    QVERIFY(m_model->isExpanded(1));
+    QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsInserted(KItemRangeList)), DefaultTimeout));
+    QCOMPARE(itemsInModel(), QStringList() << "a" << "b" << "c1.txt" << "c2.txt");
+
+    // Add the files "c1.txt" and "c2.txt" to the model also as top-level items.
+    // Such a thing can in principle happen when performing a search, and there
+    // are files which
+    // (a) match the search string, and
+    // (b) are children of a folder that matches the search string and is expanded.
+    //
+    // Note that the first item in the list of added items must be new (i.e., not
+    // in the model yet). Otherwise, KFileItemModel::slotItemsAdded() will see that
+    // it receives items that are in the model already and ignore them.
+    KUrl url(m_model->directory().url() + "/a2");
+    KFileItem newItem(KFileItem::Unknown, KFileItem::Unknown, url);
+
+    KFileItemList items;
+    items << newItem << m_model->fileItem(2) << m_model->fileItem(3);
+    m_model->slotItemsAdded(m_model->directory(), items);
+    m_model->slotCompleted();
+    QCOMPARE(itemsInModel(), QStringList() << "a" << "b" << "c1.txt" << "c2.txt" << "a2" << "c1.txt" << "c2.txt");
+
+    m_model->setExpanded(0, false);
+
+    // Test that the right items have been removed, see
+    // https://bugs.kde.org/show_bug.cgi?id=324371
+    QCOMPARE(itemsInModel(), QStringList() << "a" << "a2" << "c1.txt" << "c2.txt");
+
+    // Test that resorting does not cause a crash, see
+    // https://bugs.kde.org/show_bug.cgi?id=325359
+    // The crash is not 100% reproducible, but Valgrind will report an invalid memory access.
+    m_model->resortAllItems();
+
+}
+
 QStringList KFileItemModelTest::itemsInModel() const
 {
     QStringList items;
index 60e93e539abc9f87926ba7c9c0dcbf36440a4569..7dd37bf0a3d60f78e5e56ec888d43ffe0db59073 100644 (file)
@@ -41,7 +41,7 @@ namespace {
 Q_DECLARE_METATYPE(KFileItemListView::ItemLayout);
 Q_DECLARE_METATYPE(Qt::Orientation);
 Q_DECLARE_METATYPE(KItemListController::SelectionBehavior);
-Q_DECLARE_METATYPE(QSet<int>);
+Q_DECLARE_METATYPE(KItemSet);
 
 class KItemListControllerTest : public QObject
 {
@@ -81,7 +81,7 @@ private:
  */
 void KItemListControllerTest::initTestCase()
 {
-    qRegisterMetaType<QSet<int> >("QSet<int>");
+    qRegisterMetaType<KItemSet>("KItemSet");
 
     m_testDir = new TestDir();
     m_model = new KFileItemModel();
@@ -159,14 +159,14 @@ struct KeyPress {
  */
 struct ViewState {
 
-    ViewState(int current, const QSet<int> selection, bool activated = false) :
+    ViewState(int current, const KItemSet selection, bool activated = false) :
         m_current(current),
         m_selection(selection),
         m_activated(activated)
     {}
 
     int m_current;
-    QSet<int> m_selection;
+    KItemSet m_selection;
     bool m_activated;
 };
 
@@ -262,34 +262,34 @@ void KItemListControllerTest::testKeyboardNavigation_data()
                     // First, key presses which should have the same effect
                     // for any layout and any number of columns.
                     testList
-                        << qMakePair(KeyPress(nextItemKey), ViewState(1, QSet<int>() << 1))
-                        << qMakePair(KeyPress(Qt::Key_Return), ViewState(1, QSet<int>() << 1, true))
-                        << qMakePair(KeyPress(Qt::Key_Enter), ViewState(1, QSet<int>() << 1, true))
-                        << qMakePair(KeyPress(nextItemKey), ViewState(2, QSet<int>() << 2))
-                        << qMakePair(KeyPress(nextItemKey, Qt::ShiftModifier), ViewState(3, QSet<int>() << 2 << 3))
-                        << qMakePair(KeyPress(Qt::Key_Return), ViewState(3, QSet<int>() << 2 << 3, true))
-                        << qMakePair(KeyPress(previousItemKey, Qt::ShiftModifier), ViewState(2, QSet<int>() << 2))
-                        << qMakePair(KeyPress(nextItemKey, Qt::ShiftModifier), ViewState(3, QSet<int>() << 2 << 3))
-                        << qMakePair(KeyPress(nextItemKey, Qt::ControlModifier), ViewState(4, QSet<int>() << 2 << 3))
-                        << qMakePair(KeyPress(Qt::Key_Return), ViewState(4, QSet<int>() << 2 << 3, true))
-                        << qMakePair(KeyPress(previousItemKey), ViewState(3, QSet<int>() << 3))
-                        << qMakePair(KeyPress(Qt::Key_Home, Qt::ShiftModifier), ViewState(0, QSet<int>() << 0 << 1 << 2 << 3))
-                        << qMakePair(KeyPress(nextItemKey, Qt::ControlModifier), ViewState(1, QSet<int>() << 0 << 1 << 2 << 3))
-                        << qMakePair(KeyPress(Qt::Key_Space, Qt::ControlModifier), ViewState(1, QSet<int>() << 0 << 2 << 3))
-                        << qMakePair(KeyPress(Qt::Key_Space, Qt::ControlModifier), ViewState(1, QSet<int>() << 0 << 1 << 2 << 3))
-                        << qMakePair(KeyPress(Qt::Key_End), ViewState(19, QSet<int>() << 19))
-                        << qMakePair(KeyPress(previousItemKey, Qt::ShiftModifier), ViewState(18, QSet<int>() << 18 << 19))
-                        << qMakePair(KeyPress(Qt::Key_Home), ViewState(0, QSet<int>() << 0))
-                        << qMakePair(KeyPress(Qt::Key_Space, Qt::ControlModifier), ViewState(0, QSet<int>()))
-                        << qMakePair(KeyPress(Qt::Key_Enter), ViewState(0, QSet<int>(), true))
-                        << qMakePair(KeyPress(Qt::Key_Space, Qt::ControlModifier), ViewState(0, QSet<int>() << 0))
-                        << qMakePair(KeyPress(Qt::Key_Space, Qt::ControlModifier), ViewState(0, QSet<int>()))
-                        << qMakePair(KeyPress(Qt::Key_Space), ViewState(0, QSet<int>() << 0))
-                        << qMakePair(KeyPress(Qt::Key_E), ViewState(13, QSet<int>() << 13))
-                        << qMakePair(KeyPress(Qt::Key_Space), ViewState(14, QSet<int>() << 14))
-                        << qMakePair(KeyPress(Qt::Key_3), ViewState(15, QSet<int>() << 15))
-                        << qMakePair(KeyPress(Qt::Key_Home), ViewState(0, QSet<int>() << 0))
-                        << qMakePair(KeyPress(Qt::Key_Escape), ViewState(0, QSet<int>()));
+                        << qMakePair(KeyPress(nextItemKey), ViewState(1, KItemSet() << 1))
+                        << qMakePair(KeyPress(Qt::Key_Return), ViewState(1, KItemSet() << 1, true))
+                        << qMakePair(KeyPress(Qt::Key_Enter), ViewState(1, KItemSet() << 1, true))
+                        << qMakePair(KeyPress(nextItemKey), ViewState(2, KItemSet() << 2))
+                        << qMakePair(KeyPress(nextItemKey, Qt::ShiftModifier), ViewState(3, KItemSet() << 2 << 3))
+                        << qMakePair(KeyPress(Qt::Key_Return), ViewState(3, KItemSet() << 2 << 3, true))
+                        << qMakePair(KeyPress(previousItemKey, Qt::ShiftModifier), ViewState(2, KItemSet() << 2))
+                        << qMakePair(KeyPress(nextItemKey, Qt::ShiftModifier), ViewState(3, KItemSet() << 2 << 3))
+                        << qMakePair(KeyPress(nextItemKey, Qt::ControlModifier), ViewState(4, KItemSet() << 2 << 3))
+                        << qMakePair(KeyPress(Qt::Key_Return), ViewState(4, KItemSet() << 2 << 3, true))
+                        << qMakePair(KeyPress(previousItemKey), ViewState(3, KItemSet() << 3))
+                        << qMakePair(KeyPress(Qt::Key_Home, Qt::ShiftModifier), ViewState(0, KItemSet() << 0 << 1 << 2 << 3))
+                        << qMakePair(KeyPress(nextItemKey, Qt::ControlModifier), ViewState(1, KItemSet() << 0 << 1 << 2 << 3))
+                        << qMakePair(KeyPress(Qt::Key_Space, Qt::ControlModifier), ViewState(1, KItemSet() << 0 << 2 << 3))
+                        << qMakePair(KeyPress(Qt::Key_Space, Qt::ControlModifier), ViewState(1, KItemSet() << 0 << 1 << 2 << 3))
+                        << qMakePair(KeyPress(Qt::Key_End), ViewState(19, KItemSet() << 19))
+                        << qMakePair(KeyPress(previousItemKey, Qt::ShiftModifier), ViewState(18, KItemSet() << 18 << 19))
+                        << qMakePair(KeyPress(Qt::Key_Home), ViewState(0, KItemSet() << 0))
+                        << qMakePair(KeyPress(Qt::Key_Space, Qt::ControlModifier), ViewState(0, KItemSet()))
+                        << qMakePair(KeyPress(Qt::Key_Enter), ViewState(0, KItemSet(), true))
+                        << qMakePair(KeyPress(Qt::Key_Space, Qt::ControlModifier), ViewState(0, KItemSet() << 0))
+                        << qMakePair(KeyPress(Qt::Key_Space, Qt::ControlModifier), ViewState(0, KItemSet()))
+                        << qMakePair(KeyPress(Qt::Key_Space), ViewState(0, KItemSet() << 0))
+                        << qMakePair(KeyPress(Qt::Key_E), ViewState(13, KItemSet() << 13))
+                        << qMakePair(KeyPress(Qt::Key_Space), ViewState(14, KItemSet() << 14))
+                        << qMakePair(KeyPress(Qt::Key_3), ViewState(15, KItemSet() << 15))
+                        << qMakePair(KeyPress(Qt::Key_Home), ViewState(0, KItemSet() << 0))
+                        << qMakePair(KeyPress(Qt::Key_Escape), ViewState(0, KItemSet()));
 
                     // Next, we test combinations of key presses which only work for a
                     // particular number of columns and either enabled or disabled grouping.
@@ -297,12 +297,12 @@ void KItemListControllerTest::testKeyboardNavigation_data()
                     // One column.
                     if (columnCount == 1) {
                         testList
-                            << qMakePair(KeyPress(nextRowKey), ViewState(1, QSet<int>() << 1))
-                            << qMakePair(KeyPress(nextRowKey, Qt::ShiftModifier), ViewState(2, QSet<int>() << 1 << 2))
-                            << qMakePair(KeyPress(nextRowKey, Qt::ControlModifier), ViewState(3, QSet<int>() << 1 << 2))
-                            << qMakePair(KeyPress(previousRowKey), ViewState(2, QSet<int>() << 2))
-                            << qMakePair(KeyPress(previousItemKey), ViewState(1, QSet<int>() << 1))
-                            << qMakePair(KeyPress(Qt::Key_Home), ViewState(0, QSet<int>() << 0));
+                            << qMakePair(KeyPress(nextRowKey), ViewState(1, KItemSet() << 1))
+                            << qMakePair(KeyPress(nextRowKey, Qt::ShiftModifier), ViewState(2, KItemSet() << 1 << 2))
+                            << qMakePair(KeyPress(nextRowKey, Qt::ControlModifier), ViewState(3, KItemSet() << 1 << 2))
+                            << qMakePair(KeyPress(previousRowKey), ViewState(2, KItemSet() << 2))
+                            << qMakePair(KeyPress(previousItemKey), ViewState(1, KItemSet() << 1))
+                            << qMakePair(KeyPress(Qt::Key_Home), ViewState(0, KItemSet() << 0));
                     }
 
                     // Multiple columns: we test both 3 and 5 columns with grouping
@@ -321,26 +321,26 @@ void KItemListControllerTest::testKeyboardNavigation_data()
                         // e3 e4 e5 | 15 16 17
                         // e6 e7    | 18 19
                         testList
-                            << qMakePair(KeyPress(nextRowKey), ViewState(3, QSet<int>() << 3))
-                            << qMakePair(KeyPress(nextItemKey, Qt::ControlModifier), ViewState(4, QSet<int>() << 3))
-                            << qMakePair(KeyPress(nextRowKey), ViewState(7, QSet<int>() << 7))
-                            << qMakePair(KeyPress(nextItemKey, Qt::ShiftModifier), ViewState(8, QSet<int>() << 7 << 8))
-                            << qMakePair(KeyPress(nextItemKey, Qt::ShiftModifier), ViewState(9, QSet<int>() << 7 << 8 << 9))
-                            << qMakePair(KeyPress(previousItemKey, Qt::ShiftModifier), ViewState(8, QSet<int>() << 7 << 8))
-                            << qMakePair(KeyPress(previousItemKey, Qt::ShiftModifier), ViewState(7, QSet<int>() << 7))
-                            << qMakePair(KeyPress(previousItemKey, Qt::ShiftModifier), ViewState(6, QSet<int>() << 6 << 7))
-                            << qMakePair(KeyPress(previousItemKey, Qt::ShiftModifier), ViewState(5, QSet<int>() << 5 << 6 << 7))
-                            << qMakePair(KeyPress(nextItemKey, Qt::ShiftModifier), ViewState(6, QSet<int>() << 6 << 7))
-                            << qMakePair(KeyPress(nextItemKey, Qt::ShiftModifier), ViewState(7, QSet<int>() << 7))
-                            << qMakePair(KeyPress(nextRowKey), ViewState(10, QSet<int>() << 10))
-                            << qMakePair(KeyPress(nextItemKey), ViewState(11, QSet<int>() << 11))
-                            << qMakePair(KeyPress(nextRowKey), ViewState(14, QSet<int>() << 14))
-                            << qMakePair(KeyPress(nextRowKey), ViewState(17, QSet<int>() << 17))
-                            << qMakePair(KeyPress(nextRowKey), ViewState(19, QSet<int>() << 19))
-                            << qMakePair(KeyPress(previousRowKey), ViewState(17, QSet<int>() << 17))
-                            << qMakePair(KeyPress(Qt::Key_End), ViewState(19, QSet<int>() << 19))
-                            << qMakePair(KeyPress(previousRowKey), ViewState(16, QSet<int>() << 16))
-                            << qMakePair(KeyPress(Qt::Key_Home), ViewState(0, QSet<int>() << 0));
+                            << qMakePair(KeyPress(nextRowKey), ViewState(3, KItemSet() << 3))
+                            << qMakePair(KeyPress(nextItemKey, Qt::ControlModifier), ViewState(4, KItemSet() << 3))
+                            << qMakePair(KeyPress(nextRowKey), ViewState(7, KItemSet() << 7))
+                            << qMakePair(KeyPress(nextItemKey, Qt::ShiftModifier), ViewState(8, KItemSet() << 7 << 8))
+                            << qMakePair(KeyPress(nextItemKey, Qt::ShiftModifier), ViewState(9, KItemSet() << 7 << 8 << 9))
+                            << qMakePair(KeyPress(previousItemKey, Qt::ShiftModifier), ViewState(8, KItemSet() << 7 << 8))
+                            << qMakePair(KeyPress(previousItemKey, Qt::ShiftModifier), ViewState(7, KItemSet() << 7))
+                            << qMakePair(KeyPress(previousItemKey, Qt::ShiftModifier), ViewState(6, KItemSet() << 6 << 7))
+                            << qMakePair(KeyPress(previousItemKey, Qt::ShiftModifier), ViewState(5, KItemSet() << 5 << 6 << 7))
+                            << qMakePair(KeyPress(nextItemKey, Qt::ShiftModifier), ViewState(6, KItemSet() << 6 << 7))
+                            << qMakePair(KeyPress(nextItemKey, Qt::ShiftModifier), ViewState(7, KItemSet() << 7))
+                            << qMakePair(KeyPress(nextRowKey), ViewState(10, KItemSet() << 10))
+                            << qMakePair(KeyPress(nextItemKey), ViewState(11, KItemSet() << 11))
+                            << qMakePair(KeyPress(nextRowKey), ViewState(14, KItemSet() << 14))
+                            << qMakePair(KeyPress(nextRowKey), ViewState(17, KItemSet() << 17))
+                            << qMakePair(KeyPress(nextRowKey), ViewState(19, KItemSet() << 19))
+                            << qMakePair(KeyPress(previousRowKey), ViewState(17, KItemSet() << 17))
+                            << qMakePair(KeyPress(Qt::Key_End), ViewState(19, KItemSet() << 19))
+                            << qMakePair(KeyPress(previousRowKey), ViewState(16, KItemSet() << 16))
+                            << qMakePair(KeyPress(Qt::Key_Home), ViewState(0, KItemSet() << 0));
                     }
 
                     if (columnCount == 5 && !groupingEnabled) {
@@ -351,17 +351,17 @@ void KItemListControllerTest::testKeyboardNavigation_data()
                         // d2 d3 d4 e1 e2 | 10 11 12 13 14
                         // e3 e4 e5 e6 e7 | 15 16 17 18 19
                         testList
-                            << qMakePair(KeyPress(nextRowKey), ViewState(5, QSet<int>() << 5))
-                            << qMakePair(KeyPress(nextItemKey, Qt::ControlModifier), ViewState(6, QSet<int>() << 5))
-                            << qMakePair(KeyPress(nextRowKey), ViewState(11, QSet<int>() << 11))
-                            << qMakePair(KeyPress(nextItemKey), ViewState(12, QSet<int>() << 12))
-                            << qMakePair(KeyPress(nextRowKey, Qt::ShiftModifier), ViewState(17, QSet<int>() << 12 << 13 << 14 << 15 << 16 << 17))
-                            << qMakePair(KeyPress(previousRowKey, Qt::ShiftModifier), ViewState(12, QSet<int>() << 12))
-                            << qMakePair(KeyPress(previousRowKey, Qt::ShiftModifier), ViewState(7, QSet<int>() << 7 << 8 << 9 << 10 << 11 << 12))
-                            << qMakePair(KeyPress(nextRowKey, Qt::ShiftModifier), ViewState(12, QSet<int>() << 12))
-                            << qMakePair(KeyPress(Qt::Key_End, Qt::ControlModifier), ViewState(19, QSet<int>() << 12))
-                            << qMakePair(KeyPress(previousRowKey), ViewState(14, QSet<int>() << 14))
-                            << qMakePair(KeyPress(Qt::Key_Home), ViewState(0, QSet<int>() << 0));
+                            << qMakePair(KeyPress(nextRowKey), ViewState(5, KItemSet() << 5))
+                            << qMakePair(KeyPress(nextItemKey, Qt::ControlModifier), ViewState(6, KItemSet() << 5))
+                            << qMakePair(KeyPress(nextRowKey), ViewState(11, KItemSet() << 11))
+                            << qMakePair(KeyPress(nextItemKey), ViewState(12, KItemSet() << 12))
+                            << qMakePair(KeyPress(nextRowKey, Qt::ShiftModifier), ViewState(17, KItemSet() << 12 << 13 << 14 << 15 << 16 << 17))
+                            << qMakePair(KeyPress(previousRowKey, Qt::ShiftModifier), ViewState(12, KItemSet() << 12))
+                            << qMakePair(KeyPress(previousRowKey, Qt::ShiftModifier), ViewState(7, KItemSet() << 7 << 8 << 9 << 10 << 11 << 12))
+                            << qMakePair(KeyPress(nextRowKey, Qt::ShiftModifier), ViewState(12, KItemSet() << 12))
+                            << qMakePair(KeyPress(Qt::Key_End, Qt::ControlModifier), ViewState(19, KItemSet() << 12))
+                            << qMakePair(KeyPress(previousRowKey), ViewState(14, KItemSet() << 14))
+                            << qMakePair(KeyPress(Qt::Key_Home), ViewState(0, KItemSet() << 0));
                     }
 
                     if (columnCount == 3 && groupingEnabled) {
@@ -377,19 +377,19 @@ void KItemListControllerTest::testKeyboardNavigation_data()
                         // e4 e5 e6 | 16 17 18
                         // e7       | 19
                         testList
-                            << qMakePair(KeyPress(nextItemKey), ViewState(1, QSet<int>() << 1))
-                            << qMakePair(KeyPress(nextItemKey), ViewState(2, QSet<int>() << 2))
-                            << qMakePair(KeyPress(nextRowKey, Qt::ShiftModifier), ViewState(3, QSet<int>() << 2 << 3))
-                            << qMakePair(KeyPress(nextRowKey, Qt::ShiftModifier), ViewState(6, QSet<int>() << 2 << 3 << 4 << 5 << 6))
-                            << qMakePair(KeyPress(nextRowKey), ViewState(8, QSet<int>() << 8))
-                            << qMakePair(KeyPress(nextRowKey), ViewState(11, QSet<int>() << 11))
-                            << qMakePair(KeyPress(nextItemKey, Qt::ControlModifier), ViewState(12, QSet<int>() << 11))
-                            << qMakePair(KeyPress(nextRowKey), ViewState(13, QSet<int>() << 13))
-                            << qMakePair(KeyPress(nextRowKey), ViewState(16, QSet<int>() << 16))
-                            << qMakePair(KeyPress(nextItemKey), ViewState(17, QSet<int>() << 17))
-                            << qMakePair(KeyPress(nextRowKey), ViewState(19, QSet<int>() << 19))
-                            << qMakePair(KeyPress(previousRowKey), ViewState(17, QSet<int>() << 17))
-                            << qMakePair(KeyPress(Qt::Key_Home), ViewState(0, QSet<int>() << 0));
+                            << qMakePair(KeyPress(nextItemKey), ViewState(1, KItemSet() << 1))
+                            << qMakePair(KeyPress(nextItemKey), ViewState(2, KItemSet() << 2))
+                            << qMakePair(KeyPress(nextRowKey, Qt::ShiftModifier), ViewState(3, KItemSet() << 2 << 3))
+                            << qMakePair(KeyPress(nextRowKey, Qt::ShiftModifier), ViewState(6, KItemSet() << 2 << 3 << 4 << 5 << 6))
+                            << qMakePair(KeyPress(nextRowKey), ViewState(8, KItemSet() << 8))
+                            << qMakePair(KeyPress(nextRowKey), ViewState(11, KItemSet() << 11))
+                            << qMakePair(KeyPress(nextItemKey, Qt::ControlModifier), ViewState(12, KItemSet() << 11))
+                            << qMakePair(KeyPress(nextRowKey), ViewState(13, KItemSet() << 13))
+                            << qMakePair(KeyPress(nextRowKey), ViewState(16, KItemSet() << 16))
+                            << qMakePair(KeyPress(nextItemKey), ViewState(17, KItemSet() << 17))
+                            << qMakePair(KeyPress(nextRowKey), ViewState(19, KItemSet() << 19))
+                            << qMakePair(KeyPress(previousRowKey), ViewState(17, KItemSet() << 17))
+                            << qMakePair(KeyPress(Qt::Key_Home), ViewState(0, KItemSet() << 0));
                     }
 
                     if (columnCount == 5 && groupingEnabled) {
@@ -402,19 +402,19 @@ void KItemListControllerTest::testKeyboardNavigation_data()
                         // e1 e2 e3 e4 e5 | 13 14 15 16 17
                         // e6 e7          | 18 19
                         testList
-                            << qMakePair(KeyPress(nextItemKey), ViewState(1, QSet<int>() << 1))
-                            << qMakePair(KeyPress(nextRowKey, Qt::ShiftModifier), ViewState(3, QSet<int>() << 1 << 2 << 3))
-                            << qMakePair(KeyPress(nextRowKey, Qt::ShiftModifier), ViewState(5, QSet<int>() << 1 << 2 << 3 << 4 << 5))
-                            << qMakePair(KeyPress(nextItemKey), ViewState(6, QSet<int>() << 6))
-                            << qMakePair(KeyPress(nextItemKey, Qt::ControlModifier), ViewState(7, QSet<int>() << 6))
-                            << qMakePair(KeyPress(nextItemKey, Qt::ControlModifier), ViewState(8, QSet<int>() << 6))
-                            << qMakePair(KeyPress(nextRowKey), ViewState(12, QSet<int>() << 12))
-                            << qMakePair(KeyPress(nextRowKey), ViewState(17, QSet<int>() << 17))
-                            << qMakePair(KeyPress(nextRowKey), ViewState(19, QSet<int>() << 19))
-                            << qMakePair(KeyPress(previousRowKey), ViewState(17, QSet<int>() << 17))
-                            << qMakePair(KeyPress(Qt::Key_End, Qt::ShiftModifier), ViewState(19, QSet<int>() << 17 << 18 << 19))
-                            << qMakePair(KeyPress(previousRowKey, Qt::ShiftModifier), ViewState(14, QSet<int>() << 14 << 15 << 16 << 17))
-                            << qMakePair(KeyPress(Qt::Key_Home), ViewState(0, QSet<int>() << 0));
+                            << qMakePair(KeyPress(nextItemKey), ViewState(1, KItemSet() << 1))
+                            << qMakePair(KeyPress(nextRowKey, Qt::ShiftModifier), ViewState(3, KItemSet() << 1 << 2 << 3))
+                            << qMakePair(KeyPress(nextRowKey, Qt::ShiftModifier), ViewState(5, KItemSet() << 1 << 2 << 3 << 4 << 5))
+                            << qMakePair(KeyPress(nextItemKey), ViewState(6, KItemSet() << 6))
+                            << qMakePair(KeyPress(nextItemKey, Qt::ControlModifier), ViewState(7, KItemSet() << 6))
+                            << qMakePair(KeyPress(nextItemKey, Qt::ControlModifier), ViewState(8, KItemSet() << 6))
+                            << qMakePair(KeyPress(nextRowKey), ViewState(12, KItemSet() << 12))
+                            << qMakePair(KeyPress(nextRowKey), ViewState(17, KItemSet() << 17))
+                            << qMakePair(KeyPress(nextRowKey), ViewState(19, KItemSet() << 19))
+                            << qMakePair(KeyPress(previousRowKey), ViewState(17, KItemSet() << 17))
+                            << qMakePair(KeyPress(Qt::Key_End, Qt::ShiftModifier), ViewState(19, KItemSet() << 17 << 18 << 19))
+                            << qMakePair(KeyPress(previousRowKey, Qt::ShiftModifier), ViewState(14, KItemSet() << 14 << 15 << 16 << 17))
+                            << qMakePair(KeyPress(Qt::Key_Home), ViewState(0, KItemSet() << 0));
                     }
 
                     const QString testName =
@@ -470,14 +470,14 @@ void KItemListControllerTest::testKeyboardNavigation()
     QCOMPARE(m_view->m_layouter->m_columnCount, columnCount);
 
     QSignalSpy spySingleItemActivated(m_controller, SIGNAL(itemActivated(int)));
-    QSignalSpy spyMultipleItemsActivated(m_controller, SIGNAL(itemsActivated(QSet<int>)));
+    QSignalSpy spyMultipleItemsActivated(m_controller, SIGNAL(itemsActivated(KItemSet)));
 
     while (!testList.isEmpty()) {
         const QPair<KeyPress, ViewState> test = testList.takeFirst();
         const Qt::Key key = test.first.m_key;
         const Qt::KeyboardModifiers modifier = test.first.m_modifier;
         const int current = test.second.m_current;
-        const QSet<int> selection = test.second.m_selection;
+        const KItemSet selection = test.second.m_selection;
         const bool activated = test.second.m_activated;
 
         QTest::keyClick(m_container, key, modifier);
@@ -485,7 +485,7 @@ void KItemListControllerTest::testKeyboardNavigation()
         QCOMPARE(m_selectionManager->currentItem(), current);
         switch (selectionBehavior) {
         case KItemListController::NoSelection: QVERIFY(m_selectionManager->selectedItems().isEmpty()); break;
-        case KItemListController::SingleSelection: QCOMPARE(m_selectionManager->selectedItems(), QSet<int>() << current); break;
+        case KItemListController::SingleSelection: QCOMPARE(m_selectionManager->selectedItems(), KItemSet() << current); break;
         case KItemListController::MultiSelection: QCOMPARE(m_selectionManager->selectedItems(), selection); break;
         }
 
@@ -496,12 +496,12 @@ void KItemListControllerTest::testKeyboardNavigation()
                     // The selected items should be activated.
                     if (selection.count() == 1) {
                         QVERIFY(!spySingleItemActivated.isEmpty());
-                        QCOMPARE(qvariant_cast<int>(spySingleItemActivated.takeFirst().at(0)), selection.toList().at(0));
+                        QCOMPARE(qvariant_cast<int>(spySingleItemActivated.takeFirst().at(0)), selection.first());
                         QVERIFY(spyMultipleItemsActivated.isEmpty());
                     } else {
                         QVERIFY(spySingleItemActivated.isEmpty());
                         QVERIFY(!spyMultipleItemsActivated.isEmpty());
-                        QCOMPARE(qvariant_cast<QSet<int> >(spyMultipleItemsActivated.takeFirst().at(0)), selection);
+                        QCOMPARE(qvariant_cast<KItemSet>(spyMultipleItemsActivated.takeFirst().at(0)), selection);
                     }
                     break;
                 }
@@ -641,7 +641,7 @@ void KItemListControllerTest::testMouseClickActivation()
     group.writeEntry("SingleClick", restoreKGlobalSettingsSingleClick, KConfig::Persistent|KConfig::Global);
     config.sync();
     KGlobalSettings::self()->emitChange(KGlobalSettings::SettingsChanged, KGlobalSettings::SETTINGS_MOUSE);
-    
+
     iterations = 0;
     while (KGlobalSettings::singleClick() != restoreKGlobalSettingsSingleClick && iterations < maxIterations) {
         QTest::qWait(50);
index 302985a5f4c72a0696d1f837a8d5ea1e95c6c643..af2610d8c2248e172546c82bddfe1d2a931070f1 100644 (file)
@@ -80,7 +80,7 @@ private slots:
     void testDeleteCurrentItem();
 
 private:
-    void verifySelectionChange(QSignalSpy& spy, const QSet<int>& currentSelection, const QSet<int>& previousSelection) const;
+    void verifySelectionChange(QSignalSpy& spy, const KItemSet& currentSelection, const KItemSet& previousSelection) const;
 
     KItemListSelectionManager* m_selectionManager;
     DummyModel* m_model;
@@ -127,7 +127,7 @@ void KItemListSelectionManagerTest::testCurrentItemAnchorItem()
     QCOMPARE(m_selectionManager->m_anchorItem, 5);
 
     // Items between current and anchor should be selected now
-    QCOMPARE(m_selectionManager->selectedItems(), QSet<int>() << 4 << 5);
+    QCOMPARE(m_selectionManager->selectedItems(), KItemSet() << 4 << 5);
     QVERIFY(m_selectionManager->hasSelection());
 
     // Change current item again and check the selection
@@ -138,7 +138,7 @@ void KItemListSelectionManagerTest::testCurrentItemAnchorItem()
     QCOMPARE(qvariant_cast<int>(spyCurrent.at(0).at(1)), 4);
     spyCurrent.takeFirst();
 
-    QCOMPARE(m_selectionManager->selectedItems(), QSet<int>() << 2 << 3 << 4 << 5);
+    QCOMPARE(m_selectionManager->selectedItems(), KItemSet() << 2 << 3 << 4 << 5);
     QVERIFY(m_selectionManager->hasSelection());
 
     // Inserting items should update current item and anchor item.
@@ -155,7 +155,7 @@ void KItemListSelectionManagerTest::testCurrentItemAnchorItem()
 
     QCOMPARE(m_selectionManager->m_anchorItem, 8);
 
-    QCOMPARE(m_selectionManager->selectedItems(), QSet<int>() << 5 << 6 << 7 << 8);
+    QCOMPARE(m_selectionManager->selectedItems(), KItemSet() << 5 << 6 << 7 << 8);
     QVERIFY(m_selectionManager->hasSelection());
 
     // Removing items should update current item and anchor item.
@@ -172,12 +172,12 @@ void KItemListSelectionManagerTest::testCurrentItemAnchorItem()
 
     QCOMPARE(m_selectionManager->m_anchorItem, 5);
 
-    QCOMPARE(m_selectionManager->selectedItems(), QSet<int>() << 2 << 3 << 4 << 5);
+    QCOMPARE(m_selectionManager->selectedItems(), KItemSet() << 2 << 3 << 4 << 5);
     QVERIFY(m_selectionManager->hasSelection());
 
     // Verify that clearSelection() also clears the anchored selection.
     m_selectionManager->clearSelection();
-    QCOMPARE(m_selectionManager->selectedItems(), QSet<int>());
+    QCOMPARE(m_selectionManager->selectedItems(), KItemSet());
     QVERIFY(!m_selectionManager->hasSelection());
 
     m_selectionManager->endAnchoredSelection();
@@ -212,7 +212,7 @@ void KItemListSelectionManagerTest::testItemsInserted()
 {
     // Select items 10 to 12
     m_selectionManager->setSelected(10, 3);
-    QSet<int> selectedItems = m_selectionManager->selectedItems();
+    KItemSet selectedItems = m_selectionManager->selectedItems();
     QCOMPARE(selectedItems.count(), 3);
     QVERIFY(selectedItems.contains(10));
     QVERIFY(selectedItems.contains(11));
@@ -242,7 +242,7 @@ void KItemListSelectionManagerTest::testItemsRemoved()
 {
     // Select items 10 to 15
     m_selectionManager->setSelected(10, 6);
-    QSet<int> selectedItems = m_selectionManager->selectedItems();
+    KItemSet selectedItems = m_selectionManager->selectedItems();
     QCOMPARE(selectedItems.count(), 6);
     for (int i = 10; i <= 15; ++i) {
         QVERIFY(selectedItems.contains(i));
@@ -276,20 +276,20 @@ void KItemListSelectionManagerTest::testAnchoredSelection()
 
     m_selectionManager->setCurrentItem(6);
     QCOMPARE(m_selectionManager->currentItem(), 6);
-    QCOMPARE(m_selectionManager->selectedItems(), QSet<int>() << 5 << 6);
+    QCOMPARE(m_selectionManager->selectedItems(), KItemSet() << 5 << 6);
 
     m_selectionManager->setCurrentItem(4);
     QCOMPARE(m_selectionManager->currentItem(), 4);
-    QCOMPARE(m_selectionManager->selectedItems(), QSet<int>() << 4 << 5);
+    QCOMPARE(m_selectionManager->selectedItems(), KItemSet() << 4 << 5);
 
     m_selectionManager->setCurrentItem(7);
     QCOMPARE(m_selectionManager->currentItem(), 7);
-    QCOMPARE(m_selectionManager->selectedItems(), QSet<int>() << 5 << 6 << 7);
+    QCOMPARE(m_selectionManager->selectedItems(), KItemSet() << 5 << 6 << 7);
 
     // Ending the anchored selection should not change the selected items.
     m_selectionManager->endAnchoredSelection();
     QVERIFY(!m_selectionManager->isAnchoredSelectionActive());
-    QCOMPARE(m_selectionManager->selectedItems(), QSet<int>() << 5 << 6 << 7);
+    QCOMPARE(m_selectionManager->selectedItems(), KItemSet() << 5 << 6 << 7);
 
     // Start a new anchored selection that overlaps the previous one
     m_selectionManager->beginAnchoredSelection(9);
@@ -298,15 +298,15 @@ void KItemListSelectionManagerTest::testAnchoredSelection()
 
     m_selectionManager->setCurrentItem(6);
     QCOMPARE(m_selectionManager->currentItem(), 6);
-    QCOMPARE(m_selectionManager->selectedItems(), QSet<int>() << 5 << 6 << 7 << 8 << 9);
+    QCOMPARE(m_selectionManager->selectedItems(), KItemSet() << 5 << 6 << 7 << 8 << 9);
 
     m_selectionManager->setCurrentItem(10);
     QCOMPARE(m_selectionManager->currentItem(), 10);
-    QCOMPARE(m_selectionManager->selectedItems(), QSet<int>() << 5 << 6 << 7 << 9 << 10);
+    QCOMPARE(m_selectionManager->selectedItems(), KItemSet() << 5 << 6 << 7 << 9 << 10);
 
     m_selectionManager->endAnchoredSelection();
     QVERIFY(!m_selectionManager->isAnchoredSelectionActive());
-    QCOMPARE(m_selectionManager->selectedItems(), QSet<int>() << 5 << 6 << 7 << 9 << 10);
+    QCOMPARE(m_selectionManager->selectedItems(), KItemSet() << 5 << 6 << 7 << 9 << 10);
 }
 
 namespace {
@@ -320,7 +320,7 @@ namespace {
     };
 }
 
-Q_DECLARE_METATYPE(QSet<int>);
+Q_DECLARE_METATYPE(KItemSet);
 Q_DECLARE_METATYPE(ChangeType);
 Q_DECLARE_METATYPE(KItemRange);
 Q_DECLARE_METATYPE(KItemRangeList);
@@ -355,86 +355,86 @@ Q_DECLARE_METATYPE(QList<int>);
 
 void KItemListSelectionManagerTest::testChangeSelection_data()
 {
-    QTest::addColumn<QSet<int> >("initialSelection");
+    QTest::addColumn<KItemSet>("initialSelection");
     QTest::addColumn<int>("anchor");
     QTest::addColumn<int>("current");
-    QTest::addColumn<QSet<int> >("expectedSelection");
+    QTest::addColumn<KItemSet>("expectedSelection");
     QTest::addColumn<ChangeType>("changeType");
     QTest::addColumn<QList<QVariant> >("data");
-    QTest::addColumn<QSet<int> >("finalSelection");
+    QTest::addColumn<KItemSet>("finalSelection");
 
     QTest::newRow("No change")
-        << (QSet<int>() << 5 << 6)
+        << (KItemSet() << 5 << 6)
         << 2 << 3
-        << (QSet<int>() << 2 << 3 << 5 << 6)
+        << (KItemSet() << 2 << 3 << 5 << 6)
         << NoChange
         << QList<QVariant>()
-        << (QSet<int>() << 2 << 3 << 5 << 6);
+        << (KItemSet() << 2 << 3 << 5 << 6);
 
     QTest::newRow("Insert Items")
-        << (QSet<int>() << 5 << 6)
+        << (KItemSet() << 5 << 6)
         << 2 << 3
-        << (QSet<int>() << 2 << 3 << 5 << 6)
+        << (KItemSet() << 2 << 3 << 5 << 6)
         << InsertItems
         << (QList<QVariant>() << QVariant::fromValue(KItemRangeList() << KItemRange(1, 1) << KItemRange(5, 2) << KItemRange(10, 5)))
-        << (QSet<int>() << 3 << 4 << 8 << 9);
+        << (KItemSet() << 3 << 4 << 8 << 9);
 
     QTest::newRow("Remove Items")
-        << (QSet<int>() << 5 << 6)
+        << (KItemSet() << 5 << 6)
         << 2 << 3
-        << (QSet<int>() << 2 << 3 << 5 << 6)
+        << (KItemSet() << 2 << 3 << 5 << 6)
         << RemoveItems
         << (QList<QVariant>() << QVariant::fromValue(KItemRangeList() << KItemRange(1, 1) << KItemRange(3, 1) << KItemRange(10, 5)))
-        << (QSet<int>() << 1 << 2 << 3 << 4);
+        << (KItemSet() << 1 << 2 << 3 << 4);
 
     QTest::newRow("Empty Anchored Selection")
-        << QSet<int>()
+        << KItemSet()
         << 2 << 2
-        << QSet<int>()
+        << KItemSet()
         << EndAnchoredSelection
         << QList<QVariant>()
-        << QSet<int>();
+        << KItemSet();
 
     QTest::newRow("Toggle selection")
-        << (QSet<int>() << 1 << 3 << 4)
+        << (KItemSet() << 1 << 3 << 4)
         << 6 << 8
-        << (QSet<int>() << 1 << 3 << 4 << 6 << 7 << 8)
+        << (KItemSet() << 1 << 3 << 4 << 6 << 7 << 8)
         << SetSelected
         << (QList<QVariant>() << 0 << 10 << QVariant::fromValue(KItemListSelectionManager::Toggle))
-        << (QSet<int>() << 0 << 2 << 5 << 9);
+        << (KItemSet() << 0 << 2 << 5 << 9);
 
     // Swap items 2, 3 and 4, 5
     QTest::newRow("Move items")
-        << (QSet<int>() << 0 << 1 << 2 << 3)
+        << (KItemSet() << 0 << 1 << 2 << 3)
         << -1 << -1
-        << (QSet<int>() << 0 << 1 << 2 << 3)
+        << (KItemSet() << 0 << 1 << 2 << 3)
         << MoveItems
         << (QList<QVariant>() << QVariant::fromValue(KItemRange(2, 4))
                               << QVariant::fromValue(QList<int>() << 4 << 5 << 2 << 3))
-        << (QSet<int>() << 0 << 1 << 4 << 5);
+        << (KItemSet() << 0 << 1 << 4 << 5);
 
     // Revert sort order
     QTest::newRow("Revert sort order")
-        << (QSet<int>() << 0 << 1)
+        << (KItemSet() << 0 << 1)
         << 3 << 4
-        << (QSet<int>() << 0 << 1 << 3 << 4)
+        << (KItemSet() << 0 << 1 << 3 << 4)
         << MoveItems
         << (QList<QVariant>() << QVariant::fromValue(KItemRange(0, 10))
                               << QVariant::fromValue(QList<int>() << 9 << 8 << 7 << 6 << 5 << 4 << 3 << 2 << 1 << 0))
-        << (QSet<int>() << 5 << 6 << 8 << 9);
+        << (KItemSet() << 5 << 6 << 8 << 9);
 }
 
 void KItemListSelectionManagerTest::testChangeSelection()
 {
-    QFETCH(QSet<int>, initialSelection);
+    QFETCH(KItemSet, initialSelection);
     QFETCH(int, anchor);
     QFETCH(int, current);
-    QFETCH(QSet<int>, expectedSelection);
+    QFETCH(KItemSet, expectedSelection);
     QFETCH(ChangeType, changeType);
     QFETCH(QList<QVariant>, data);
-    QFETCH(QSet<int>, finalSelection);
+    QFETCH(KItemSet, finalSelection);
 
-    QSignalSpy spySelectionChanged(m_selectionManager, SIGNAL(selectionChanged(QSet<int>,QSet<int>)));
+    QSignalSpy spySelectionChanged(m_selectionManager, SIGNAL(selectionChanged(KItemSet,KItemSet)));
 
     // Initial selection should be empty
     QVERIFY(!m_selectionManager->hasSelection());
@@ -443,7 +443,7 @@ void KItemListSelectionManagerTest::testChangeSelection()
     // Perform the initial selectiion
     m_selectionManager->setSelectedItems(initialSelection);
 
-    verifySelectionChange(spySelectionChanged, initialSelection, QSet<int>());
+    verifySelectionChange(spySelectionChanged, initialSelection, KItemSet());
 
     // Perform an anchored selection.
     // Note that current and anchor index are equal first because this is the case in typical uses of the
@@ -486,7 +486,7 @@ void KItemListSelectionManagerTest::testChangeSelection()
     // Finally, clear the selection
     m_selectionManager->clearSelection();
 
-    verifySelectionChange(spySelectionChanged, QSet<int>(), finalSelection);
+    verifySelectionChange(spySelectionChanged, KItemSet(), finalSelection);
 }
 
 void KItemListSelectionManagerTest::testDeleteCurrentItem_data()
@@ -520,8 +520,8 @@ void KItemListSelectionManagerTest::testDeleteCurrentItem()
 }
 
 void KItemListSelectionManagerTest::verifySelectionChange(QSignalSpy& spy,
-                                                          const QSet<int>& currentSelection,
-                                                          const QSet<int>& previousSelection) const
+                                                          const KItemSet& currentSelection,
+                                                          const KItemSet& previousSelection) const
 {
     QCOMPARE(m_selectionManager->selectedItems(), currentSelection);
     QCOMPARE(m_selectionManager->hasSelection(), !currentSelection.isEmpty());
@@ -540,8 +540,8 @@ void KItemListSelectionManagerTest::verifySelectionChange(QSignalSpy& spy,
     else {
         QCOMPARE(spy.count(), 1);
         QList<QVariant> arguments = spy.takeFirst();
-        QCOMPARE(qvariant_cast<QSet<int> >(arguments.at(0)), currentSelection);
-        QCOMPARE(qvariant_cast<QSet<int> >(arguments.at(1)), previousSelection);
+        QCOMPARE(qvariant_cast<KItemSet>(arguments.at(0)), currentSelection);
+        QCOMPARE(qvariant_cast<KItemSet>(arguments.at(1)), previousSelection);
     }
 }
 
diff --git a/src/tests/kitemsettest.cpp b/src/tests/kitemsettest.cpp
new file mode 100644 (file)
index 0000000..2832596
--- /dev/null
@@ -0,0 +1,612 @@
+/***************************************************************************
+ *   Copyright (C) 2013 by Frank Reininghaus <frank78ac@googlemail.com>    *
+ *                                                                         *
+ *   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/kitemset.h"
+
+#include <QVector>
+
+Q_DECLARE_METATYPE(KItemRangeList);
+
+/**
+ * Converts a KItemRangeList to a KItemSet.
+ */
+KItemSet KItemRangeList2KItemSet(const KItemRangeList& itemRanges)
+{
+    KItemSet result;
+    foreach (const KItemRange& range, itemRanges) {
+        for (int i = range.index; i < range.index + range.count; ++i) {
+            result.insert(i);
+        }
+    }
+    return result;
+}
+
+/**
+ * Converts a KItemRangeList to a QSet<int>.
+ */
+QSet<int> KItemRangeList2QSet(const KItemRangeList& itemRanges)
+{
+    QSet<int> result;
+    foreach (const KItemRange& range, itemRanges) {
+        for (int i = range.index; i < range.index + range.count; ++i) {
+            result.insert(i);
+        }
+    }
+    return result;
+}
+
+/**
+ * Converts a KItemRangeList to a QVector<int>.
+ */
+QVector<int> KItemRangeList2QVector(const KItemRangeList& itemRanges)
+{
+    QVector<int> result;
+    foreach (const KItemRange& range, itemRanges) {
+        for (int i = range.index; i < range.index + range.count; ++i) {
+            result.append(i);
+        }
+    }
+    return result;
+}
+
+/**
+ * Converts a KItemSet to a QSet<int>.
+ */
+static QSet<int> KItemSet2QSet(const KItemSet& itemSet)
+{
+    QSet<int> result;
+    foreach (int i, itemSet) {
+        result.insert(i);
+    }
+
+    // Check that the conversion was successful.
+    Q_ASSERT(itemSet.count() == result.count());
+
+    foreach (int i, itemSet) {
+        Q_ASSERT(result.contains(i));
+    }
+
+    foreach (int i, result) {
+        Q_ASSERT(itemSet.contains(i));
+    }
+
+    return result;
+}
+
+
+/**
+ * The main test class.
+ */
+class KItemSetTest : public QObject
+{
+    Q_OBJECT
+
+private slots:
+    void initTestCase();
+
+    void testConstruction_data();
+    void testConstruction();
+    void testIterators_data();
+    void testIterators();
+    void testFind_data();
+    void testFind();
+    void testChangingOneItem_data();
+    void testChangingOneItem();
+    void testAddSets_data();
+    void testAddSets();
+    /*
+    void testSubtractSets_data();
+    void testSubtractSets();
+    */
+    void testSymmetricDifference_data();
+    void testSymmetricDifference();
+
+private:
+    QHash<const char*, KItemRangeList> m_testCases;
+};
+
+void KItemSetTest::initTestCase()
+{
+    m_testCases.insert("empty", KItemRangeList());
+    m_testCases.insert("[0]", KItemRangeList() << KItemRange(0, 1));
+    m_testCases.insert("[1]", KItemRangeList() << KItemRange(1, 1));
+    m_testCases.insert("[1, 2]", KItemRangeList() << KItemRange(1, 2));
+    m_testCases.insert("[1, 2] [5]", KItemRangeList() << KItemRange(1, 2) << KItemRange(5, 1));
+    m_testCases.insert("[1] [4, 5]", KItemRangeList() << KItemRange(1, 1) << KItemRange(4, 2));
+    m_testCases.insert("[1, 2] [4, 5]", KItemRangeList() << KItemRange(1, 2) << KItemRange(4, 2));
+    m_testCases.insert("[1, 5]", KItemRangeList() << KItemRange(1, 5));
+    m_testCases.insert("[1, 2] [4, 5] [7] [9, 10] [13] [20, 25] [30]",
+                       KItemRangeList() << KItemRange(1, 2) << KItemRange(4, 2) << KItemRange(7, 1) << KItemRange(9, 2) << KItemRange(20, 6) << KItemRange(30, 1));
+    m_testCases.insert("[-10, -1]", KItemRangeList() << KItemRange(-10, 10));
+    m_testCases.insert("[-10, 0]", KItemRangeList() << KItemRange(-10, 11));
+    m_testCases.insert("[-10, 1]", KItemRangeList() << KItemRange(-10, 12));
+    m_testCases.insert("[0, 9]", KItemRangeList() << KItemRange(0, 10));
+    m_testCases.insert("[0, 19]", KItemRangeList() << KItemRange(0, 10));
+}
+
+void KItemSetTest::testConstruction_data()
+{
+    QTest::addColumn<KItemRangeList>("itemRanges");
+
+    QHash<const char*, KItemRangeList>::const_iterator it = m_testCases.constBegin();
+    const QHash<const char*, KItemRangeList>::const_iterator end = m_testCases.constEnd();
+
+    while (it != end) {
+        QTest::newRow(it.key()) << it.value();
+        ++it;
+    }
+}
+
+void KItemSetTest::testConstruction()
+{
+    QFETCH(KItemRangeList, itemRanges);
+
+    KItemSet itemSet = KItemRangeList2KItemSet(itemRanges);
+    QSet<int> itemsQSet = KItemRangeList2QSet(itemRanges);
+
+    QVERIFY(itemSet.isValid());
+    QVERIFY(itemSet.count() == itemsQSet.count());
+    QCOMPARE(KItemSet2QSet(itemSet), itemsQSet);
+
+    // Test copy constructor.
+    KItemSet copy(itemSet);
+    QCOMPARE(itemSet, copy);
+    copy.clear();
+    QVERIFY(itemSet != copy || itemSet.isEmpty());
+
+    // Clear the set.
+    itemSet.clear();
+    QVERIFY(itemSet.isEmpty());
+    QCOMPARE(itemSet.count(), 0);
+}
+
+void KItemSetTest::testIterators_data()
+{
+    QTest::addColumn<KItemRangeList>("itemRanges");
+
+    QHash<const char*, KItemRangeList>::const_iterator it = m_testCases.constBegin();
+    const QHash<const char*, KItemRangeList>::const_iterator end = m_testCases.constEnd();
+
+    while (it != end) {
+        QTest::newRow(it.key()) << it.value();
+        ++it;
+    }
+}
+
+/**
+ * Verify that the iterators work exactly like their counterparts for the
+ * equivalent QVector<int>.
+ */
+void KItemSetTest::testIterators()
+{
+    QFETCH(KItemRangeList, itemRanges);
+
+    KItemSet itemSet = KItemRangeList2KItemSet(itemRanges);
+    QVector<int> itemsQVector = KItemRangeList2QVector(itemRanges);
+
+    QVERIFY(itemSet.isValid());
+    QVERIFY(itemSet.count() == itemsQVector.count());
+
+    if (itemSet.count() == 0) {
+        QVERIFY(itemSet.isEmpty());
+        QVERIFY(itemSet.begin() == itemSet.end());
+        QVERIFY(itemSet.constBegin() == itemSet.constEnd());
+    } else {
+        QVERIFY(!itemSet.isEmpty());
+        QVERIFY(itemSet.begin() != itemSet.end());
+        QVERIFY(itemSet.constBegin() != itemSet.constEnd());
+
+        const int min = itemsQVector.first();
+        const int max = itemsQVector.last();
+
+        QCOMPARE(*itemSet.begin(), min);
+        QCOMPARE(*itemSet.constBegin(), min);
+        QCOMPARE(itemSet.first(), min);
+
+        QCOMPARE(*(--itemSet.end()), max);
+        QCOMPARE(*(--itemSet.constEnd()), max);
+        QCOMPARE(itemSet.last(), max);
+    }
+
+    // Test iterating using the different iterators.
+    QVector<int> testQVector;
+    for (KItemSet::iterator it = itemSet.begin(), end = itemSet.end(); it != end; ++it) {
+        testQVector.append(*it);
+    }
+    QCOMPARE(testQVector, itemsQVector);
+
+    testQVector.clear();
+    for (KItemSet::const_iterator it = itemSet.constBegin(), end = itemSet.constEnd(); it != end; ++it) {
+        testQVector.append(*it);
+    }
+    QCOMPARE(testQVector, itemsQVector);
+
+    testQVector.clear();
+    foreach (int i, itemSet) {
+        testQVector.append(i);
+    }
+    QCOMPARE(testQVector, itemsQVector);
+
+    // Verify that both variants of the (const)iterator's operator++ and
+    // operator-- functions behave exactly like their QVector equivalents.
+    KItemSet::iterator it1 = itemSet.begin();
+    KItemSet::iterator it2 = itemSet.begin();
+    KItemSet::const_iterator constIt1 = itemSet.constBegin();
+    KItemSet::const_iterator constIt2 = itemSet.constBegin();
+    QVector<int>::iterator vectorIt1 = itemsQVector.begin();
+    QVector<int>::iterator vectorIt2 = itemsQVector.begin();
+    QVector<int>::const_iterator vectorConstIt1 = itemsQVector.constBegin();
+    QVector<int>::const_iterator vectorConstIt2 = itemsQVector.constBegin();
+
+    while (it1 != itemSet.end()) {
+        if (it1 != --itemSet.end()) {
+            QCOMPARE(*(++it1), *(++vectorIt1));
+            QCOMPARE(*(++constIt1), *(++vectorConstIt1));
+        } else {
+            QCOMPARE(++it1, itemSet.end());
+            QCOMPARE(++vectorIt1, itemsQVector.end());
+            QCOMPARE(++constIt1, itemSet.constEnd());
+            QCOMPARE(++vectorConstIt1, itemsQVector.constEnd());
+        }
+
+        QCOMPARE(*(it2++), *(vectorIt2++));
+        QCOMPARE(*(constIt2++), *(vectorConstIt2++));
+
+        QCOMPARE(it1, it2);
+        QCOMPARE(constIt1, constIt2);
+        QCOMPARE(KItemSet::const_iterator(it1), constIt1);
+    }
+
+    QCOMPARE(it1, itemSet.end());
+    QCOMPARE(it2, itemSet.end());
+    QCOMPARE(constIt1, itemSet.constEnd());
+    QCOMPARE(constIt2, itemSet.constEnd());
+    QCOMPARE(vectorIt1, itemsQVector.end());
+    QCOMPARE(vectorIt2, itemsQVector.end());
+    QCOMPARE(vectorConstIt1, itemsQVector.constEnd());
+    QCOMPARE(vectorConstIt2, itemsQVector.constEnd());
+
+    while (it1 != itemSet.begin()) {
+        QCOMPARE(*(--it1), *(--vectorIt1));
+        QCOMPARE(*(--constIt1), *(--vectorConstIt1));
+
+        if (it2 != itemSet.end()) {
+            QCOMPARE(*(it2--), *(vectorIt2--));
+            QCOMPARE(*(constIt2--), *(vectorConstIt2--));
+        } else {
+            QCOMPARE(it2--, itemSet.end());
+            QCOMPARE(vectorIt2--, itemsQVector.end());
+            QCOMPARE(constIt2--, itemSet.constEnd());
+            QCOMPARE(vectorConstIt2--, itemsQVector.constEnd());
+        }
+
+        QCOMPARE(it1, it2);
+        QCOMPARE(constIt1, constIt2);
+        QCOMPARE(KItemSet::const_iterator(it1), constIt1);
+    }
+
+    QCOMPARE(it1, itemSet.begin());
+    QCOMPARE(it2, itemSet.begin());
+    QCOMPARE(constIt1, itemSet.constBegin());
+    QCOMPARE(constIt2, itemSet.constBegin());
+    QCOMPARE(vectorIt1, itemsQVector.begin());
+    QCOMPARE(vectorIt2, itemsQVector.begin());
+    QCOMPARE(vectorConstIt1, itemsQVector.constBegin());
+    QCOMPARE(vectorConstIt2, itemsQVector.constBegin());
+}
+
+void KItemSetTest::testFind_data()
+{
+    QTest::addColumn<KItemRangeList>("itemRanges");
+
+    QHash<const char*, KItemRangeList>::const_iterator it = m_testCases.constBegin();
+    const QHash<const char*, KItemRangeList>::const_iterator end = m_testCases.constEnd();
+
+    while (it != end) {
+        QTest::newRow(it.key()) << it.value();
+        ++it;
+    }
+}
+
+/**
+ * Test all functions that find items:
+ * contais(int), find(int), constFind(int)
+ */
+void KItemSetTest::testFind()
+{
+    QFETCH(KItemRangeList, itemRanges);
+
+    KItemSet itemSet = KItemRangeList2KItemSet(itemRanges);
+    QSet<int> itemsQSet = KItemRangeList2QSet(itemRanges);
+
+    QVERIFY(itemSet.isValid());
+    QVERIFY(itemSet.count() == itemsQSet.count());
+
+    // Find the minimum and maximum items.
+    int min;
+    int max;
+
+    if (itemSet.count() == 0) {
+        // Use some arbitrary values for the upcoming tests.
+        min = 0;
+        max = 5;
+    } else {
+        min = *itemSet.begin();
+        max = *(--itemSet.end());
+    }
+
+    // Test contains(int), find(int), and constFind(int)
+    // for items between min - 2 and max + 2.
+    for (int i = min - 2; i <= max + 2; ++i) {
+        const KItemSet::iterator it = itemSet.find(i);
+        const KItemSet::const_iterator constIt = itemSet.constFind(i);
+        QCOMPARE(KItemSet::const_iterator(it), constIt);
+
+        if (itemsQSet.contains(i)) {
+            QVERIFY(itemSet.contains(i));
+            QCOMPARE(*it, i);
+            QCOMPARE(*constIt, i);
+        } else {
+            QVERIFY(!itemSet.contains(i));
+            QCOMPARE(it, itemSet.end());
+            QCOMPARE(constIt, itemSet.constEnd());
+        }
+    }
+}
+
+void KItemSetTest::testChangingOneItem_data()
+{
+    QTest::addColumn<KItemRangeList>("itemRanges");
+
+    QHash<const char*, KItemRangeList>::const_iterator it = m_testCases.constBegin();
+    const QHash<const char*, KItemRangeList>::const_iterator end = m_testCases.constEnd();
+
+    while (it != end) {
+        QTest::newRow(it.key()) << it.value();
+        ++it;
+    }
+}
+
+/**
+ * Test all functions that change a single item:
+ * insert(int), remove(int), erase(KItemSet::iterator)
+ */
+void KItemSetTest::testChangingOneItem()
+{
+    QFETCH(KItemRangeList, itemRanges);
+
+    KItemSet itemSet = KItemRangeList2KItemSet(itemRanges);
+    QSet<int> itemsQSet = KItemRangeList2QSet(itemRanges);
+
+    QVERIFY(itemSet.isValid());
+    QVERIFY(itemSet.count() == itemsQSet.count());
+
+    // Find the minimum and maximum items.
+    int min;
+    int max;
+
+    if (itemSet.count() == 0) {
+        // Use some arbitrary values for the upcoming tests.
+        min = 0;
+        max = 5;
+    } else {
+        min = *itemSet.begin();
+        max = *(--itemSet.end());
+    }
+
+    // Test insert(int), remove(int), and erase(KItemSet::iterator)
+    // for items between min - 2 and max + 2.
+    for (int i = min - 2; i <= max + 2; ++i) {
+
+        // Test insert(int).
+        {
+            KItemSet tmp(itemSet);
+            const KItemSet::iterator insertedIt = tmp.insert(i);
+            QCOMPARE(*insertedIt, i);
+
+            QVERIFY(tmp.isValid());
+            QVERIFY(tmp.contains(i));
+
+            QSet<int> expectedQSet = itemsQSet;
+            expectedQSet.insert(i);
+            QCOMPARE(KItemSet2QSet(tmp), expectedQSet);
+
+            if (!itemSet.contains(i)) {
+                QVERIFY(itemSet != tmp);
+                QCOMPARE(tmp.count(), itemSet.count() + 1);
+            } else {
+                QCOMPARE(itemSet, tmp);
+            }
+
+            QCOMPARE(i, *tmp.find(i));
+            QCOMPARE(i, *tmp.constFind(i));
+
+            // Erase the new item and check that we get the old KItemSet back.
+            tmp.erase(tmp.find(i));
+            QVERIFY(tmp.isValid());
+            QVERIFY(!tmp.contains(i));
+
+            if (!itemSet.contains(i)) {
+                QCOMPARE(itemSet, tmp);
+            }
+
+            expectedQSet.remove(i);
+            QCOMPARE(KItemSet2QSet(tmp), expectedQSet);
+        }
+
+        // Test remove(int).
+        {
+            KItemSet tmp(itemSet);
+            const bool removed = tmp.remove(i);
+
+            QCOMPARE(removed, itemSet.contains(i));
+
+            QVERIFY(tmp.isValid());
+            QVERIFY(!tmp.contains(i));
+
+            QSet<int> expectedQSet = itemsQSet;
+            expectedQSet.remove(i);
+            QCOMPARE(KItemSet2QSet(tmp), expectedQSet);
+
+            if (itemSet.contains(i)) {
+                QVERIFY(itemSet != tmp);
+                QCOMPARE(tmp.count(), itemSet.count() - 1);
+            } else {
+                QCOMPARE(itemSet, tmp);
+            }
+
+            QCOMPARE(tmp.end(), tmp.find(i));
+            QCOMPARE(tmp.constEnd(), tmp.constFind(i));
+        }
+
+        // Test erase(KItemSet::iterator).
+        if (itemSet.contains(i)) {
+            KItemSet tmp(itemSet);
+            KItemSet::iterator it = tmp.find(i);
+            it = tmp.erase(it);
+
+            QVERIFY(tmp.isValid());
+            QVERIFY(!tmp.contains(i));
+
+            QSet<int> expectedQSet = itemsQSet;
+            expectedQSet.remove(i);
+            QCOMPARE(KItemSet2QSet(tmp), expectedQSet);
+
+            if (itemSet.contains(i)) {
+                QVERIFY(itemSet != tmp);
+                QCOMPARE(tmp.count(), itemSet.count() - 1);
+            } else {
+                QCOMPARE(itemSet, tmp);
+            }
+
+            QCOMPARE(tmp.end(), tmp.find(i));
+            QCOMPARE(tmp.constEnd(), tmp.constFind(i));
+
+            // Check the returen value, now contained in 'it'.
+            if (i == max) {
+                QCOMPARE(it, tmp.end());
+            } else {
+                // it now points to the next item.
+                QVERIFY(tmp.contains(*it));
+                for (int j = i; j < *it; ++j) {
+                    QVERIFY(!tmp.contains(j));
+                }
+            }
+        }
+    }
+
+    // Clear the set.
+    itemSet.clear();
+    QVERIFY(itemSet.isEmpty());
+    QCOMPARE(itemSet.count(), 0);
+}
+
+void KItemSetTest::testAddSets_data()
+{
+    QTest::addColumn<KItemRangeList>("itemRanges1");
+    QTest::addColumn<KItemRangeList>("itemRanges2");
+
+    QHash<const char*, KItemRangeList>::const_iterator it1 = m_testCases.constBegin();
+    const QHash<const char*, KItemRangeList>::const_iterator end = m_testCases.constEnd();
+
+    while (it1 != end) {
+        QHash<const char*, KItemRangeList>::const_iterator it2 = m_testCases.constBegin();
+
+        while (it2 != end) {
+            QByteArray name = it1.key() + QByteArray(" + ") + it2.key();
+            QTest::newRow(name) << it1.value() << it2.value();
+            ++it2;
+        }
+
+        ++it1;
+    }
+}
+
+void KItemSetTest::testAddSets()
+{
+    QFETCH(KItemRangeList, itemRanges1);
+    QFETCH(KItemRangeList, itemRanges2);
+
+    KItemSet itemSet1 = KItemRangeList2KItemSet(itemRanges1);
+    QSet<int> itemsQSet1 = KItemRangeList2QSet(itemRanges1);
+
+    KItemSet itemSet2 = KItemRangeList2KItemSet(itemRanges2);
+    QSet<int> itemsQSet2 = KItemRangeList2QSet(itemRanges2);
+
+    KItemSet sum = itemSet1 + itemSet2;
+    QSet<int> sumQSet = itemsQSet1 + itemsQSet2;
+
+    QCOMPARE(sum.count(), sumQSet.count());
+    QCOMPARE(KItemSet2QSet(sum), sumQSet);
+}
+
+void KItemSetTest::testSymmetricDifference_data()
+{
+    QTest::addColumn<KItemRangeList>("itemRanges1");
+    QTest::addColumn<KItemRangeList>("itemRanges2");
+
+    QHash<const char*, KItemRangeList>::const_iterator it1 = m_testCases.constBegin();
+    const QHash<const char*, KItemRangeList>::const_iterator end = m_testCases.constEnd();
+
+    while (it1 != end) {
+        QHash<const char*, KItemRangeList>::const_iterator it2 = m_testCases.constBegin();
+
+        while (it2 != end) {
+            QByteArray name = it1.key() + QByteArray(" ^ ") + it2.key();
+            QTest::newRow(name) << it1.value() << it2.value();
+            ++it2;
+        }
+
+        ++it1;
+    }
+}
+
+void KItemSetTest::testSymmetricDifference()
+{
+    QFETCH(KItemRangeList, itemRanges1);
+    QFETCH(KItemRangeList, itemRanges2);
+
+    KItemSet itemSet1 = KItemRangeList2KItemSet(itemRanges1);
+    QSet<int> itemsQSet1 = KItemRangeList2QSet(itemRanges1);
+
+    KItemSet itemSet2 = KItemRangeList2KItemSet(itemRanges2);
+    QSet<int> itemsQSet2 = KItemRangeList2QSet(itemRanges2);
+
+    KItemSet symmetricDifference = itemSet1 ^ itemSet2;
+    QSet<int> symmetricDifferenceQSet = (itemsQSet1 - itemsQSet2) + (itemsQSet2 - itemsQSet1);
+
+    QCOMPARE(symmetricDifference.count(), symmetricDifferenceQSet.count());
+    QCOMPARE(KItemSet2QSet(symmetricDifference), symmetricDifferenceQSet);
+
+    // Check commutativity.
+    QCOMPARE(itemSet2 ^ itemSet1, symmetricDifference);
+
+    // Some more checks:
+    // itemSet1 ^ symmetricDifference == itemSet2,
+    // itemSet2 ^ symmetricDifference == itemSet1.
+    QCOMPARE(itemSet1 ^ symmetricDifference, itemSet2);
+    QCOMPARE(itemSet2 ^ symmetricDifference, itemSet1);
+}
+
+
+QTEST_KDEMAIN(KItemSetTest, NoGUI)
+
+#include "kitemsettest.moc"
index 039b5f230ba7acc95d31f5d7072cefc6dc7a12ad..4799d76794106e9fb7e6d89aa11e22fae27cdd3d 100644 (file)
@@ -89,10 +89,7 @@ void DolphinItemListView::readSettings()
     beginTransaction();
 
     setEnabledSelectionToggles(GeneralSettings::showSelectionToggle());
-
-    const bool expandableFolders = (itemLayout() == KFileItemListView::DetailsLayout) &&
-                                   DetailsModeSettings::expandableFolders();
-    setSupportsItemExpanding(expandableFolders);
+    setSupportsItemExpanding(itemLayoutSupportsItemExpanding(itemLayout()));
 
     updateFont();
     updateGridSize();
@@ -119,19 +116,19 @@ KItemListWidgetCreatorBase* DolphinItemListView::defaultWidgetCreator() const
     return new KItemListWidgetCreator<DolphinFileItemListWidget>();
 }
 
-void DolphinItemListView::onItemLayoutChanged(ItemLayout current, ItemLayout previous)
+bool DolphinItemListView::itemLayoutSupportsItemExpanding(ItemLayout layout) const
 {
-    Q_UNUSED(previous);
+    return layout == DetailsLayout && DetailsModeSettings::expandableFolders();
+}
 
-    if (current == DetailsLayout) {
-        setSupportsItemExpanding(DetailsModeSettings::expandableFolders());
-        setHeaderVisible(true);
-    } else {
-        setHeaderVisible(false);
-    }
+void DolphinItemListView::onItemLayoutChanged(ItemLayout current, ItemLayout previous)
+{
+    setHeaderVisible(current == DetailsLayout);
 
     updateFont();
     updateGridSize();
+
+    KFileItemListView::onItemLayoutChanged(current, previous);
 }
 
 void DolphinItemListView::onPreviewsShownChanged(bool shown)
index c2d86cc5e21efc0344b6662465e00e8fc65cda42..18bb284ace61ccf2efdecacd590fff0bfce567df 100644 (file)
@@ -50,6 +50,7 @@ public:
 
 protected:
     virtual KItemListWidgetCreatorBase* defaultWidgetCreator() const;
+    virtual bool itemLayoutSupportsItemExpanding(ItemLayout layout) const;
     virtual void onItemLayoutChanged(ItemLayout current, ItemLayout previous);
     virtual void onPreviewsShownChanged(bool shown);
     virtual void onVisibleRolesChanged(const QList<QByteArray>& current,
index 1cb5739d796f55d164185385b1475c824a710d37..7669f15616d0615fa4f7fae58fb9c336a3340cab 100644 (file)
@@ -20,7 +20,7 @@
 #include "dolphinnewfilemenuobserver.h"
 
 #include <KGlobal>
-#include <KNewFileMenu>
+#include "dolphinnewfilemenu.h"
 
 class DolphinNewFileMenuObserverSingleton
 {
@@ -34,20 +34,24 @@ DolphinNewFileMenuObserver& DolphinNewFileMenuObserver::instance()
     return s_DolphinNewFileMenuObserver->instance;
 }
 
-void DolphinNewFileMenuObserver::attach(const KNewFileMenu* menu)
+void DolphinNewFileMenuObserver::attach(const DolphinNewFileMenu* menu)
 {
     connect(menu, SIGNAL(fileCreated(KUrl)),
             this, SIGNAL(itemCreated(KUrl)));
     connect(menu, SIGNAL(directoryCreated(KUrl)),
             this, SIGNAL(itemCreated(KUrl)));
+    connect(menu, SIGNAL(errorMessage(QString)),
+            this, SIGNAL(errorMessage(QString)));
 }
 
-void DolphinNewFileMenuObserver::detach(const KNewFileMenu* menu)
+void DolphinNewFileMenuObserver::detach(const DolphinNewFileMenu* menu)
 {
     disconnect(menu, SIGNAL(fileCreated(KUrl)),
                this, SIGNAL(itemCreated(KUrl)));
     disconnect(menu, SIGNAL(directoryCreated(KUrl)),
                this, SIGNAL(itemCreated(KUrl)));
+    disconnect(menu, SIGNAL(errorMessage(QString)),
+               this, SIGNAL(errorMessage(QString)));
 }
 
 DolphinNewFileMenuObserver::DolphinNewFileMenuObserver() :
index 726122cbc0e51503af09f11a2c797d1aa2d80f5b..239476eb9d2193d6f058fdc8be77011b6e88e096 100644 (file)
@@ -24,7 +24,7 @@
 
 #include "libdolphin_export.h"
 
-class KNewFileMenu;
+class DolphinNewFileMenu;
 class KUrl;
 
 /**
@@ -40,11 +40,12 @@ class LIBDOLPHINPRIVATE_EXPORT DolphinNewFileMenuObserver : public QObject
 
 public:
     static DolphinNewFileMenuObserver& instance();
-    void attach(const KNewFileMenu* menu);
-    void detach(const KNewFileMenu* menu);
+    void attach(const DolphinNewFileMenu* menu);
+    void detach(const DolphinNewFileMenu* menu);
 
 signals:
     void itemCreated(const KUrl& url);
+    void errorMessage(const QString& error);
 
 private:
     DolphinNewFileMenuObserver();
index 20bc9f522534feaa5faab871e63180617a5b8ef2..d0a85b3e26ef125112bcca4a3f903fa07d684769 100644 (file)
@@ -145,7 +145,7 @@ DolphinView::DolphinView(const KUrl& url, QWidget* parent) :
 
     controller->setSelectionBehavior(KItemListController::MultiSelection);
     connect(controller, SIGNAL(itemActivated(int)), this, SLOT(slotItemActivated(int)));
-    connect(controller, SIGNAL(itemsActivated(QSet<int>)), this, SLOT(slotItemsActivated(QSet<int>)));
+    connect(controller, SIGNAL(itemsActivated(KItemSet)), this, SLOT(slotItemsActivated(KItemSet)));
     connect(controller, SIGNAL(itemMiddleClicked(int)), this, SLOT(slotItemMiddleClicked(int)));
     connect(controller, SIGNAL(itemContextMenuRequested(int,QPointF)), this, SLOT(slotItemContextMenuRequested(int,QPointF)));
     connect(controller, SIGNAL(viewContextMenuRequested(QPointF)), this, SLOT(slotViewContextMenuRequested(QPointF)));
@@ -154,6 +154,7 @@ DolphinView::DolphinView(const KUrl& url, QWidget* parent) :
     connect(controller, SIGNAL(itemHovered(int)), this, SLOT(slotItemHovered(int)));
     connect(controller, SIGNAL(itemUnhovered(int)), this, SLOT(slotItemUnhovered(int)));
     connect(controller, SIGNAL(itemDropEvent(int,QGraphicsSceneDragDropEvent*)), this, SLOT(slotItemDropEvent(int,QGraphicsSceneDragDropEvent*)));
+    connect(controller, SIGNAL(escapePressed()), this, SLOT(stopLoading()));
     connect(controller, SIGNAL(modelChanged(KItemModelBase*,KItemModelBase*)), this, SLOT(slotModelChanged(KItemModelBase*,KItemModelBase*)));
 
     connect(m_model, SIGNAL(directoryLoadingStarted()),       this, SLOT(slotDirectoryLoadingStarted()));
@@ -183,8 +184,8 @@ DolphinView::DolphinView(const KUrl& url, QWidget* parent) :
             this, SLOT(slotHeaderColumnWidthChanged(QByteArray,qreal,qreal)));
 
     KItemListSelectionManager* selectionManager = controller->selectionManager();
-    connect(selectionManager, SIGNAL(selectionChanged(QSet<int>,QSet<int>)),
-            this, SLOT(slotSelectionChanged(QSet<int>,QSet<int>)));
+    connect(selectionManager, SIGNAL(selectionChanged(KItemSet,KItemSet)),
+            this, SLOT(slotSelectionChanged(KItemSet,KItemSet)));
 
     m_toolTipManager = new ToolTipManager(this);
 
@@ -349,14 +350,9 @@ int DolphinView::itemsCount() const
 KFileItemList DolphinView::selectedItems() const
 {
     const KItemListSelectionManager* selectionManager = m_container->controller()->selectionManager();
-    QList<int> selectedIndexes = selectionManager->selectedItems().toList();
-
-    qSort(selectedIndexes);
 
     KFileItemList selectedItems;
-    QListIterator<int> it(selectedIndexes);
-    while (it.hasNext()) {
-        const int index = it.next();
+    foreach (int index, selectionManager->selectedItems()) {
         selectedItems.append(m_model->fileItem(index));
     }
     return selectedItems;
@@ -389,9 +385,9 @@ void DolphinView::selectItems(const QRegExp& pattern, bool enabled)
     for (int index = 0; index < m_model->count(); index++) {
         const KFileItem item = m_model->fileItem(index);
         if (pattern.exactMatch(item.text())) {
-            // An alternative approach would be to store the matching items in a QSet<int> and
+            // An alternative approach would be to store the matching items in a KItemSet and
             // select them in one go after the loop, but we'd need a new function
-            // KItemListSelectionManager::setSelected(QSet<int>, SelectionMode mode)
+            // KItemListSelectionManager::setSelected(KItemSet, SelectionMode mode)
             // for that.
             selectionManager->setSelected(index, 1, mode);
         }
@@ -485,11 +481,6 @@ void DolphinView::reload()
     restoreState(restoreStream);
 }
 
-void DolphinView::stopLoading()
-{
-    m_model->cancelDirectoryLoading();
-}
-
 void DolphinView::readSettings()
 {
     const int oldZoomLevel = m_view->zoomLevel();
@@ -556,8 +547,8 @@ QString DolphinView::statusBarText() const
         }
 
         if (folderCount + fileCount == 1) {
-            // If only one item is selected, show the filename
-            filesText = i18nc("@info:status", "<filename>%1</filename> selected", list.first().text());
+            // If only one item is selected, show info about it
+            return list.first().getStatusBarInfo();
         } else {
             // At least 2 items are selected
             foldersText = i18ncp("@info:status", "1 Folder selected", "%1 Folders selected", folderCount);
@@ -724,6 +715,11 @@ void DolphinView::pasteIntoFolder()
     }
 }
 
+void DolphinView::stopLoading()
+{
+    m_model->cancelDirectoryLoading();
+}
+
 bool DolphinView::eventFilter(QObject* watched, QEvent* event)
 {
     switch (event->type()) {
@@ -802,7 +798,7 @@ void DolphinView::slotItemActivated(int index)
     }
 }
 
-void DolphinView::slotItemsActivated(const QSet<int>& indexes)
+void DolphinView::slotItemsActivated(const KItemSet& indexes)
 {
     Q_ASSERT(indexes.count() >= 2);
 
@@ -817,9 +813,7 @@ void DolphinView::slotItemsActivated(const QSet<int>& indexes)
     KFileItemList items;
     items.reserve(indexes.count());
 
-    QSetIterator<int> it(indexes);
-    while (it.hasNext()) {
-        const int index = it.next();
+    foreach (int index, indexes) {
         KFileItem item = m_model->fileItem(index);
         const KUrl& url = openItemAsFolderUrl(item);
 
@@ -1103,7 +1097,7 @@ void DolphinView::slotAboutToCreate(const KUrl::List& urls)
     }
 }
 
-void DolphinView::slotSelectionChanged(const QSet<int>& current, const QSet<int>& previous)
+void DolphinView::slotSelectionChanged(const KItemSet& current, const KItemSet& previous)
 {
     const int currentCount = current.count();
     const int previousCount = previous.count();
@@ -1322,7 +1316,7 @@ void DolphinView::updateViewState()
             m_clearSelectionBeforeSelectingNewItems = false;
         }
 
-        QSet<int> selectedItems = selectionManager->selectedItems();
+        KItemSet selectedItems = selectionManager->selectedItems();
 
         QList<KUrl>::iterator it = m_selectedUrls.begin();
         while (it != m_selectedUrls.end()) {
@@ -1658,7 +1652,7 @@ KUrl::List DolphinView::simplifiedSelectedUrls() const
 QMimeData* DolphinView::selectionMimeData() const
 {
     const KItemListSelectionManager* selectionManager = m_container->controller()->selectionManager();
-    const QSet<int> selectedIndexes = selectionManager->selectedItems();
+    const KItemSet selectedIndexes = selectionManager->selectedItems();
 
     return m_model->createMimeData(selectedIndexes);
 }
@@ -1666,7 +1660,7 @@ QMimeData* DolphinView::selectionMimeData() const
 void DolphinView::updateWritableState()
 {
     const bool wasFolderWritable = m_isFolderWritable;
-    m_isFolderWritable = true;
+    m_isFolderWritable = false;
 
     const KFileItem item = m_model->rootItem();
     if (!item.isNull()) {
index 86bc5c159d43702e0330a99ee031785599f7e6ab..b43957f226e05d639a0bb97ec4abae42da092443 100644 (file)
@@ -34,7 +34,6 @@
 #include <QBoxLayout>
 #include <QKeyEvent>
 #include <QLinkedList>
-#include <QSet>
 #include <QWidget>
 
 typedef KIO::FileUndoManager::CommandType CommandType;
@@ -45,6 +44,7 @@ class KActionCollection;
 class KFileItemModel;
 class KItemListContainer;
 class KItemModelBase;
+class KItemSet;
 class KUrl;
 class ToolTipManager;
 class VersionControlObserver;
@@ -217,7 +217,6 @@ public:
     QList<QByteArray> visibleRoles() const;
 
     void reload();
-    void stopLoading();
 
     /**
      * Refreshes the view to get synchronized with the settings (e.g. icons size,
@@ -369,6 +368,8 @@ public slots:
      */
     void pasteIntoFolder();
 
+    void stopLoading();
+
     /** Activates the view if the item list container gets focus. */
     virtual bool eventFilter(QObject* watched, QEvent* event);
 
@@ -562,7 +563,7 @@ private slots:
     void activate();
 
     void slotItemActivated(int index);
-    void slotItemsActivated(const QSet<int>& indexes);
+    void slotItemsActivated(const KItemSet& indexes);
     void slotItemMiddleClicked(int index);
     void slotItemContextMenuRequested(int index, const QPointF& pos);
     void slotViewContextMenuRequested(const QPointF& pos);
@@ -586,7 +587,7 @@ private slots:
      * the signal is emitted only after no selection change has been done
      * within a small delay.
      */
-    void slotSelectionChanged(const QSet<int>& current, const QSet<int>& previous);
+    void slotSelectionChanged(const KItemSet& current, const KItemSet& previous);
 
     /**
      * Is called by emitDelayedSelectionChangedSignal() and emits the