]> cloud.milkyroute.net Git - dolphin.git/blobdiff - src/dolphinview.cpp
* Ignore invalid redirection after renaming an expanded folder in detail view.
[dolphin.git] / src / dolphinview.cpp
index bae7c40f9c4177caa59848a5bc96098805d69be6..e73766d242fc1df98ff618e7d078bfe7f272631b 100644 (file)
@@ -1,5 +1,5 @@
 /***************************************************************************
- *   Copyright (C) 2006 by Peter Penz <peter.penz@gmx.at>                  *
+ *   Copyright (C) 2006-2009 by Peter Penz <peter.penz@gmx.at>             *
  *   Copyright (C) 2006 by Gregor Kališnik <gregor@podnapisi.net>          *
  *                                                                         *
  *   This program is free software; you can redistribute it and/or modify  *
 #include <ktoggleaction.h>
 #include <kurl.h>
 
+#include "additionalinfoaccessor.h"
 #include "dolphinmodel.h"
 #include "dolphincolumnviewcontainer.h"
-#include "dolphincontroller.h"
+#include "dolphinviewcontroller.h"
 #include "dolphindetailsview.h"
 #include "dolphinfileitemdelegate.h"
 #include "dolphinnewmenuobserver.h"
 #include "dolphiniconsview.h"
 #include "dolphin_generalsettings.h"
 #include "draganddrophelper.h"
-#include "folderexpander.h"
 #include "renamedialog.h"
 #include "settings/dolphinsettings.h"
-#include "versioncontrolobserver.h"
+#include "viewmodecontroller.h"
 #include "viewproperties.h"
 #include "zoomlevelinfo.h"
+#include "dolphindetailsviewexpander.h"
 
 /**
  * Helper function for sorting items with qSort() in
@@ -82,7 +83,6 @@ DolphinView::DolphinView(QWidget* parent,
     QWidget(parent),
     m_active(true),
     m_showPreview(false),
-    m_loadingDirectory(false),
     m_storedCategorizedSorting(false),
     m_tabsForFiles(false),
     m_isContextMenuOpen(false),
@@ -90,56 +90,58 @@ DolphinView::DolphinView(QWidget* parent,
     m_assureVisibleCurrentIndex(false),
     m_mode(DolphinView::IconsView),
     m_topLayout(0),
-    m_controller(0),
+    m_dolphinViewController(0),
+    m_viewModeController(0),
     m_viewAccessor(proxyModel),
     m_selectionModel(0),
     m_selectionChangedTimer(0),
-    m_versionControlObserver(0),
     m_rootUrl(),
     m_activeItemUrl(),
+    m_restoredContentsPosition(),
     m_createdItemUrl(),
     m_selectedItems(),
-    m_newFileNames(),
-    m_expandedDragSource(0)
+    m_newFileNames()
 {
     m_topLayout = new QVBoxLayout(this);
     m_topLayout->setSpacing(0);
     m_topLayout->setMargin(0);
 
-    m_controller = new DolphinController(this);
-    m_controller->setUrl(url);
+    m_dolphinViewController = new DolphinViewController(this);
 
-    connect(m_controller, SIGNAL(urlChanged(const KUrl&)),
+    m_viewModeController = new ViewModeController(this);
+    m_viewModeController->setUrl(url);
+
+    connect(m_viewModeController, SIGNAL(urlChanged(const KUrl&)),
             this, SIGNAL(urlChanged(const KUrl&)));
-    connect(m_controller, SIGNAL(requestUrlChange(const KUrl&)),
-            this, SLOT(slotRequestUrlChange(const KUrl&)));
 
-    connect(m_controller, SIGNAL(requestContextMenu(const QPoint&, const QList<QAction*>&)),
+    connect(m_dolphinViewController, SIGNAL(requestContextMenu(const QPoint&, const QList<QAction*>&)),
             this, SLOT(openContextMenu(const QPoint&, const QList<QAction*>&)));
-    connect(m_controller, SIGNAL(urlsDropped(const KFileItem&, const KUrl&, QDropEvent*)),
+    connect(m_dolphinViewController, SIGNAL(urlsDropped(const KFileItem&, const KUrl&, QDropEvent*)),
             this, SLOT(dropUrls(const KFileItem&, const KUrl&, QDropEvent*)));
-    connect(m_controller, SIGNAL(sortingChanged(DolphinView::Sorting)),
+    connect(m_dolphinViewController, SIGNAL(sortingChanged(DolphinView::Sorting)),
             this, SLOT(updateSorting(DolphinView::Sorting)));
-    connect(m_controller, SIGNAL(sortOrderChanged(Qt::SortOrder)),
+    connect(m_dolphinViewController, SIGNAL(sortOrderChanged(Qt::SortOrder)),
             this, SLOT(updateSortOrder(Qt::SortOrder)));
-    connect(m_controller, SIGNAL(sortFoldersFirstChanged(bool)),
+    connect(m_dolphinViewController, SIGNAL(sortFoldersFirstChanged(bool)),
             this, SLOT(updateSortFoldersFirst(bool)));
-    connect(m_controller, SIGNAL(additionalInfoChanged(const KFileItemDelegate::InformationList&)),
+    connect(m_dolphinViewController, SIGNAL(additionalInfoChanged(const KFileItemDelegate::InformationList&)),
             this, SLOT(updateAdditionalInfo(const KFileItemDelegate::InformationList&)));
-    connect(m_controller, SIGNAL(itemTriggered(const KFileItem&)),
+    connect(m_dolphinViewController, SIGNAL(itemTriggered(const KFileItem&)),
             this, SLOT(triggerItem(const KFileItem&)));
-    connect(m_controller, SIGNAL(tabRequested(const KUrl&)),
+    connect(m_dolphinViewController, SIGNAL(tabRequested(const KUrl&)),
             this, SIGNAL(tabRequested(const KUrl&)));
-    connect(m_controller, SIGNAL(activated()),
+    connect(m_dolphinViewController, SIGNAL(activated()),
             this, SLOT(activate()));
-    connect(m_controller, SIGNAL(itemEntered(const KFileItem&)),
+    connect(m_dolphinViewController, SIGNAL(itemEntered(const KFileItem&)),
             this, SLOT(showHoverInformation(const KFileItem&)));
-    connect(m_controller, SIGNAL(viewportEntered()),
+    connect(m_dolphinViewController, SIGNAL(viewportEntered()),
             this, SLOT(clearHoverInformation()));
+    connect(m_dolphinViewController, SIGNAL(urlChangeRequested(KUrl)),
+            m_viewModeController, SLOT(setUrl(KUrl)));
 
     KDirLister* dirLister = m_viewAccessor.dirLister();
-    connect(dirLister, SIGNAL(redirection(KUrl, KUrl)),
-            this, SIGNAL(redirection(KUrl, KUrl)));
+    connect(dirLister, SIGNAL(redirection(KUrl,KUrl)),
+            this, SLOT(slotRedirection(KUrl,KUrl)));
     connect(dirLister, SIGNAL(completed()),
             this, SLOT(slotDirListerCompleted()));
     connect(dirLister, SIGNAL(refreshItems(const QList<QPair<KFileItem,KFileItem>>&)),
@@ -151,19 +153,23 @@ DolphinView::DolphinView(QWidget* parent,
     connect(&DolphinNewMenuObserver::instance(), SIGNAL(itemCreated(const KUrl&)),
             this, SLOT(observeCreatedItem(const KUrl&)));
 
+    m_selectionChangedTimer = new QTimer(this);
+    m_selectionChangedTimer->setSingleShot(true);
+    m_selectionChangedTimer->setInterval(300);
+    connect(m_selectionChangedTimer, SIGNAL(timeout()),
+            this, SLOT(emitSelectionChangedSignal()));
+
     applyViewProperties();
-    m_topLayout->addWidget(m_viewAccessor.itemView());
+    m_topLayout->addWidget(m_viewAccessor.layoutTarget());
 }
 
 DolphinView::~DolphinView()
 {
-    delete m_expandedDragSource;
-    m_expandedDragSource = 0;
 }
 
-const KUrl& DolphinView::url() const
+KUrl DolphinView::url() const
 {
-    return m_controller->url();
+    return m_viewModeController->url();
 }
 
 KUrl DolphinView::rootUrl() const
@@ -203,7 +209,7 @@ void DolphinView::setActive(bool active)
         emit activated();
     }
 
-    m_controller->indicateActivationChange(active);
+    m_viewModeController->indicateActivationChange(active);
 }
 
 bool DolphinView::isActive() const
@@ -217,9 +223,13 @@ void DolphinView::setMode(Mode mode)
         return; // the wished mode is already set
     }
 
-    const int oldZoomLevel = m_controller->zoomLevel();
+    const int oldZoomLevel = m_viewModeController->zoomLevel();
     m_mode = mode;
 
+    // remember the currently selected items, so that they will
+    // be restored after reloading the directory
+    m_selectedItems = selectedItems();
+
     deleteView();
 
     const KUrl viewPropsUrl = rootUrl();
@@ -246,9 +256,7 @@ void DolphinView::setMode(Mode mode)
     emit modeChanged();
 
     updateZoomLevel(oldZoomLevel);
-    if (m_showPreview) {
-        loadDirectory(viewPropsUrl);
-    }
+    loadDirectory(viewPropsUrl);
 }
 
 DolphinView::Mode DolphinView::mode() const
@@ -283,54 +291,29 @@ bool DolphinView::supportsCategorizedSorting() const
     return m_viewAccessor.supportsCategorizedSorting();
 }
 
-void DolphinView::selectAll()
-{
-    QAbstractItemView* view = m_viewAccessor.itemView();
-    // TODO: there seems to be a bug in QAbstractItemView::selectAll(); if
-    // the Ctrl-key is pressed (e. g. for Ctrl+A), selectAll() inverts the
-    // selection instead of selecting all items. This is bypassed for KDE 4.0
-    // by invoking clearSelection() first.
-    view->clearSelection();
-    view->selectAll();
-}
-
-void DolphinView::invertSelection()
-{
-    QItemSelectionModel* selectionModel = m_viewAccessor.itemView()->selectionModel();
-    const QAbstractItemModel* itemModel = selectionModel->model();
-
-    const QModelIndex topLeft = itemModel->index(0, 0);
-    const QModelIndex bottomRight = itemModel->index(itemModel->rowCount() - 1,
-                                                     itemModel->columnCount() - 1);
-
-    const QItemSelection selection(topLeft, bottomRight);
-    selectionModel->select(selection, QItemSelectionModel::Toggle);
-}
-
 bool DolphinView::hasSelection() const
 {
-    return m_viewAccessor.itemView()->selectionModel()->hasSelection();
+    const QAbstractItemView* view = m_viewAccessor.itemView();
+    return (view != 0) && view->selectionModel()->hasSelection();
 }
 
-void DolphinView::clearSelection()
+void DolphinView::markUrlsAsSelected(const QList<KUrl>& urls)
 {
-    QItemSelectionModel* selModel = m_viewAccessor.itemView()->selectionModel();
-    const QModelIndex currentIndex = selModel->currentIndex();
-    selModel->setCurrentIndex(currentIndex, QItemSelectionModel::Current |
-                                            QItemSelectionModel::Clear);
-    m_selectedItems.clear();
+    foreach (const KUrl& url, urls) {
+        KFileItem item(KFileItem::Unknown, KFileItem::Unknown, url);
+        m_selectedItems.append(item);
+    }
 }
 
 KFileItemList DolphinView::selectedItems() const
 {
+    KFileItemList itemList;
     const QAbstractItemView* view = m_viewAccessor.itemView();
-
-    // Our view has a selection, we will map them back to the DolphinModel
-    // and then fill the KFileItemList.
-    Q_ASSERT((view != 0) && (view->selectionModel() != 0));
+    if (view == 0) {
+        return itemList;
+    }
 
     const QItemSelection selection = m_viewAccessor.proxyModel()->mapSelectionToSource(view->selectionModel()->selection());
-    KFileItemList itemList;
 
     const QModelIndexList indexList = selection.indexes();
     foreach (const QModelIndex &index, indexList) {
@@ -355,23 +338,17 @@ KUrl::List DolphinView::selectedUrls() const
 
 int DolphinView::selectedItemsCount() const
 {
-    return m_viewAccessor.itemView()->selectionModel()->selectedIndexes().count();
-}
-
-void DolphinView::setContentsPosition(int x, int y)
-{
-    QAbstractItemView* view = m_viewAccessor.itemView();
-    view->horizontalScrollBar()->setValue(x);
-    view->verticalScrollBar()->setValue(y);
+    const QAbstractItemView* view = m_viewAccessor.itemView();
+    if (view == 0) {
+        return 0;
+    }
 
-    m_loadingDirectory = false;
+    return view->selectionModel()->selectedIndexes().count();
 }
 
-QPoint DolphinView::contentsPosition() const
+QItemSelectionModel* DolphinView::selectionModel() const
 {
-    const int x = m_viewAccessor.itemView()->horizontalScrollBar()->value();
-    const int y = m_viewAccessor.itemView()->verticalScrollBar()->value();
-    return QPoint(x, y);
+    return m_viewAccessor.itemView()->selectionModel();
 }
 
 void DolphinView::setZoomLevel(int level)
@@ -383,14 +360,14 @@ void DolphinView::setZoomLevel(int level)
     }
 
     if (level != zoomLevel()) {
-        m_controller->setZoomLevel(level);
+        m_viewModeController->setZoomLevel(level);
         emit zoomLevelChanged(level);
     }
 }
 
 int DolphinView::zoomLevel() const
 {
-    return m_controller->zoomLevel();
+    return m_viewModeController->zoomLevel();
 }
 
 void DolphinView::setSorting(Sorting sorting)
@@ -450,8 +427,16 @@ KFileItemDelegate::InformationList DolphinView::additionalInfo() const
 
 void DolphinView::reload()
 {
+    QByteArray viewState;
+    QDataStream saveStream(&viewState, QIODevice::WriteOnly);
+    saveState(saveStream);
+    m_selectedItems= selectedItems();
+
     setUrl(url());
     loadDirectory(url(), true);
+
+    QDataStream restoreStream(viewState);
+    restoreState(restoreStream);
 }
 
 void DolphinView::refresh()
@@ -459,7 +444,7 @@ void DolphinView::refresh()
     m_ignoreViewProperties = false;
 
     const bool oldActivationState = m_active;
-    const int oldZoomLevel = m_controller->zoomLevel();
+    const int oldZoomLevel = m_viewModeController->zoomLevel();
     m_active = true;
 
     createView();
@@ -470,29 +455,9 @@ void DolphinView::refresh()
     updateZoomLevel(oldZoomLevel);
 }
 
-void DolphinView::updateView(const KUrl& url, const KUrl& rootUrl)
-{
-    Q_UNUSED(rootUrl); // TODO: remove after columnview-cleanup has been finished
-
-    if (m_controller->url() == url) {
-        return;
-    }
-
-    m_controller->setUrl(url); // emits urlChanged, which we forward
-    m_viewAccessor.prepareUrlChange(url);
-    applyViewProperties();
-    loadDirectory(url);
-
-    // When changing the URL there is no need to keep the version
-    // data of the previous URL.
-    m_viewAccessor.dirModel()->clearVersionData();
-
-    emit startedPathLoading(url);
-}
-
 void DolphinView::setNameFilter(const QString& nameFilter)
 {
-    m_controller->setNameFilter(nameFilter);
+    m_viewModeController->setNameFilter(nameFilter);
 }
 
 void DolphinView::calculateItemCount(int& fileCount,
@@ -540,7 +505,7 @@ QString DolphinView::statusBarText() const
 
         if (folderCount + fileCount == 1) {
             // if only one item is selected, show the filename
-            const QString name = list.first().name();
+            const QString name = list.first().text();
             text = (folderCount == 1) ? i18nc("@info:status", "<filename>%1</filename> selected", name) :
                                         i18nc("@info:status", "<filename>%1</filename> selected (%2)",
                                               name, KIO::convertSize(totalFileSize));
@@ -570,36 +535,58 @@ QString DolphinView::statusBarText() const
 
 QList<QAction*> DolphinView::versionControlActions(const KFileItemList& items) const
 {
-    return items.isEmpty()
-           ? m_versionControlObserver->contextMenuActions(url().path(KUrl::AddTrailingSlash))
-           : m_versionControlObserver->contextMenuActions(items);
+    return m_dolphinViewController->versionControlActions(items);
 }
 
 void DolphinView::setUrl(const KUrl& url)
 {
-    m_newFileNames.clear();
-    updateView(url, KUrl());
-}
+    if (m_viewModeController->url() != url) {
+        m_newFileNames.clear();
 
-void DolphinView::changeSelection(const KFileItemList& selection)
-{
-    clearSelection();
-    if (selection.isEmpty()) {
-        return;
+        m_viewModeController->setUrl(url); // emits urlChanged, which we forward
+        m_viewAccessor.prepareUrlChange(url);
+        applyViewProperties();
+        loadDirectory(url);
+
+        // When changing the URL there is no need to keep the version
+        // data of the previous URL.
+        m_viewAccessor.dirModel()->clearVersionData();
+
+        emit startedPathLoading(url);
     }
-    const KUrl& baseUrl = url();
-    KUrl url;
-    QItemSelection newSelection;
-    foreach(const KFileItem& item, selection) {
-        url = item.url().upUrl();
-        if (baseUrl.equals(url, KUrl::CompareWithoutTrailingSlash)) {
-            QModelIndex index = m_viewAccessor.proxyModel()->mapFromSource(m_viewAccessor.dirModel()->indexForItem(item));
-            newSelection.select(index, index);
-        }
+
+    // the selection model might have changed in the case of a column view
+    QItemSelectionModel* selectionModel = m_viewAccessor.itemView()->selectionModel();
+    if (m_selectionModel != selectionModel) {
+        disconnect(m_selectionModel, SIGNAL(selectionChanged(QItemSelection, QItemSelection)),
+                   this, SLOT(slotSelectionChanged(QItemSelection, QItemSelection)));
+        m_selectionModel = selectionModel;
+        connect(m_selectionModel, SIGNAL(selectionChanged(QItemSelection, QItemSelection)),
+                this, SLOT(slotSelectionChanged(QItemSelection, QItemSelection)));
     }
-    m_viewAccessor.itemView()->selectionModel()->select(newSelection,
-                                         QItemSelectionModel::ClearAndSelect
-                                         | QItemSelectionModel::Current);
+}
+
+void DolphinView::selectAll()
+{
+    m_viewAccessor.itemView()->selectAll();
+}
+
+void DolphinView::invertSelection()
+{
+    QItemSelectionModel* selectionModel = m_viewAccessor.itemView()->selectionModel();
+    const QAbstractItemModel* itemModel = selectionModel->model();
+
+    const QModelIndex topLeft = itemModel->index(0, 0);
+    const QModelIndex bottomRight = itemModel->index(itemModel->rowCount() - 1,
+                                                     itemModel->columnCount() - 1);
+
+    const QItemSelection selection(topLeft, bottomRight);
+    selectionModel->select(selection, QItemSelectionModel::Toggle);
+}
+
+void DolphinView::clearSelection()
+{
+    m_viewAccessor.itemView()->clearSelection();
 }
 
 void DolphinView::renameSelectedItems()
@@ -626,7 +613,7 @@ void DolphinView::renameSelectedItems()
             return;
         }
         delete dialog;
-        
+
         // the selection would be invalid after renaming the items, so just clear
         // it before
         clearSelection();
@@ -746,7 +733,7 @@ void DolphinView::setShowPreview(bool show)
     props.setShowPreview(show);
 
     m_showPreview = show;
-    const int oldZoomLevel = m_controller->zoomLevel();
+    const int oldZoomLevel = m_viewModeController->zoomLevel();
     emit showPreviewChanged();
 
     // Enabling or disabling the preview might change the icon size of the view.
@@ -829,38 +816,12 @@ void DolphinView::mouseReleaseEvent(QMouseEvent* event)
     setActive(true);
 }
 
-void DolphinView::wheelEvent(QWheelEvent* event)
-{
-    if (event->modifiers() & Qt::ControlModifier) {
-        const int delta = event->delta();
-        const int level = zoomLevel();
-        if (delta > 0) {
-            setZoomLevel(level + 1);
-        } else if (delta < 0) {
-            setZoomLevel(level - 1);
-        }
-        event->accept();
-    }
-}
-
 bool DolphinView::eventFilter(QObject* watched, QEvent* event)
 {
     switch (event->type()) {
     case QEvent::FocusIn:
         if (watched == m_viewAccessor.itemView()) {
-            m_controller->requestActivation();
-        }
-        break;
-
-    case QEvent::MouseButtonPress:
-        if ((watched == m_viewAccessor.itemView()->viewport()) && (m_expandedDragSource != 0)) {
-            // Listening to a mousebutton press event to delete expanded views is a
-            // workaround, as it seems impossible for the FolderExpander to know when
-            // a dragging outside a view has been finished. However it works quite well:
-            // A mousebutton press event indicates that a drag operation must be
-            // finished already.
-            m_expandedDragSource->deleteLater();
-            m_expandedDragSource = 0;
+            m_dolphinViewController->requestActivation();
         }
         break;
 
@@ -880,6 +841,24 @@ bool DolphinView::eventFilter(QObject* watched, QEvent* event)
         }
         break;
 
+    case QEvent::Wheel:
+        if (watched == m_viewAccessor.itemView()->viewport()) {
+            // Ctrl+wheel events should cause icon zooming, but not if the left mouse button is pressed
+            // (the user is probably trying to scroll during a selection in that case)
+            QWheelEvent* wheelEvent = static_cast<QWheelEvent*>(event);
+            if (wheelEvent->modifiers() & Qt::ControlModifier && !(wheelEvent->buttons() & Qt::LeftButton)) {
+                const int delta = wheelEvent->delta();
+                const int level = zoomLevel();
+                if (delta > 0) {
+                    setZoomLevel(level + 1);
+                } else if (delta < 0) {
+                    setZoomLevel(level - 1);
+                }
+                return true;
+            }
+        }
+        break;
+
     default:
         break;
     }
@@ -909,11 +888,16 @@ void DolphinView::triggerItem(const KFileItem& item)
     emit itemTriggered(item); // caught by DolphinViewContainer or DolphinPart
 }
 
-void DolphinView::emitDelayedSelectionChangedSignal()
+void DolphinView::slotSelectionChanged(const QItemSelection& selected, const QItemSelection& deselected)
 {
-    // Invoke emitSelectionChangedSignal() with a delay of 300 ms. This assures
-    // that fast selection changes don't result in expensive operations to
-    // collect all file items for the signal (see DolphinView::selectedItems()).
+    const int count = selectedItemsCount();
+    const bool selectionStateChanged = ((count >  0) && (selected.count() == count)) ||
+                                       ((count == 0) && !deselected.isEmpty());
+
+    // If nothing has been selected before and something got selected (or if something
+    // was selected before and now nothing is selected) the selectionChangedSignal must
+    // be emitted asynchronously as fast as possible to update the edit-actions.
+    m_selectionChangedTimer->setInterval(selectionStateChanged ? 0 : 300);
     m_selectionChangedTimer->start();
 }
 
@@ -988,53 +972,20 @@ void DolphinView::updateAdditionalInfo(const KFileItemDelegate::InformationList&
 
 void DolphinView::updateAdditionalInfoActions(KActionCollection* collection)
 {
+    const AdditionalInfoAccessor& infoAccessor = AdditionalInfoAccessor::instance();
+
+    const KFileItemDelegate::InformationList checkedInfos = m_viewAccessor.itemDelegate()->showInformation();
+    const KFileItemDelegate::InformationList infos = infoAccessor.keys();
+
     const bool enable = (m_mode == DolphinView::DetailsView) ||
                         (m_mode == DolphinView::IconsView);
 
-    QAction* showSizeInfo = collection->action("show_size_info");
-    QAction* showDateInfo = collection->action("show_date_info");
-    QAction* showPermissionsInfo = collection->action("show_permissions_info");
-    QAction* showOwnerInfo = collection->action("show_owner_info");
-    QAction* showGroupInfo = collection->action("show_group_info");
-    QAction* showMimeInfo = collection->action("show_mime_info");
-
-    showSizeInfo->setChecked(false);
-    showDateInfo->setChecked(false);
-    showPermissionsInfo->setChecked(false);
-    showOwnerInfo->setChecked(false);
-    showGroupInfo->setChecked(false);
-    showMimeInfo->setChecked(false);
-
-    showSizeInfo->setEnabled(enable);
-    showDateInfo->setEnabled(enable);
-    showPermissionsInfo->setEnabled(enable);
-    showOwnerInfo->setEnabled(enable);
-    showGroupInfo->setEnabled(enable);
-    showMimeInfo->setEnabled(enable);
-
-    foreach (KFileItemDelegate::Information info, m_viewAccessor.itemDelegate()->showInformation()) {
-        switch (info) {
-        case KFileItemDelegate::Size:
-            showSizeInfo->setChecked(true);
-            break;
-        case KFileItemDelegate::ModificationTime:
-            showDateInfo->setChecked(true);
-            break;
-        case KFileItemDelegate::Permissions:
-            showPermissionsInfo->setChecked(true);
-            break;
-        case KFileItemDelegate::Owner:
-            showOwnerInfo->setChecked(true);
-            break;
-        case KFileItemDelegate::OwnerAndGroup:
-            showGroupInfo->setChecked(true);
-            break;
-        case KFileItemDelegate::FriendlyMimeType:
-            showMimeInfo->setChecked(true);
-            break;
-        default:
-            break;
-        }
+    foreach (const KFileItemDelegate::Information& info, infos) {
+        const QString name = infoAccessor.actionCollectionName(info, AdditionalInfoAccessor::AdditionalInfoType);
+        QAction* action = collection->action(name);
+        Q_ASSERT(action != 0);
+        action->setEnabled(enable);
+        action->setChecked(checkedInfos.contains(info));
     }
 }
 
@@ -1053,34 +1004,58 @@ bool DolphinView::isTabsForFilesEnabled() const
     return m_tabsForFiles;
 }
 
-void DolphinView::activateItem(const KUrl& url)
-{
-    m_activeItemUrl = url;
-}
-
 bool DolphinView::itemsExpandable() const
 {
     return m_viewAccessor.itemsExpandable();
 }
 
-void DolphinView::deleteWhenNotDragSource(QAbstractItemView *view)
+void DolphinView::restoreState(QDataStream& stream)
 {
-    if (view == 0)
-        return;
+    // current item
+    stream >> m_activeItemUrl;
 
-    if (DragAndDropHelper::instance().isDragSource(view)) {
-        // We must store for later deletion.
-        if (m_expandedDragSource != 0) {
-            // The old stored view is obviously not the drag source anymore.
-            m_expandedDragSource->deleteLater();
-            m_expandedDragSource = 0;
-        }
-        view->hide();
-        m_expandedDragSource = view;
+    // view position
+    stream >> m_restoredContentsPosition;
+
+    // expanded folders (only relevant for the details view - will be ignored by the view in other view modes)
+    QSet<KUrl> urlsToExpand;
+    stream >> urlsToExpand;
+    const DolphinDetailsViewExpander* expander = m_viewAccessor.setExpandedUrls(urlsToExpand);
+    if (expander != 0) {
+        m_expanderActive = true;
+        connect (expander, SIGNAL(completed()), this, SLOT(slotLoadingCompleted()));
     }
     else {
-        view->deleteLater();
+        m_expanderActive = false;
+    }
+}
+
+void DolphinView::saveState(QDataStream& stream)
+{
+    // current item
+    KFileItem currentItem;
+    const QAbstractItemView* view = m_viewAccessor.itemView();
+
+    if (view != 0) {
+        const QModelIndex proxyIndex = view->currentIndex();
+        const QModelIndex dirModelIndex = m_viewAccessor.proxyModel()->mapToSource(proxyIndex);
+        currentItem = m_viewAccessor.dirModel()->itemForIndex(dirModelIndex);
+    }
+
+    KUrl currentUrl;
+    if (!currentItem.isNull()) {
+        currentUrl = currentItem.url();
     }
+
+    stream << currentUrl;
+
+    // view position
+    const int x = view->horizontalScrollBar()->value();
+    const int y = view->verticalScrollBar()->value();
+    stream << QPoint(x, y);
+
+    // expanded folders (only relevant for the details view - the set will be empty in other view modes)
+    stream << m_viewAccessor.expandedUrls();
 }
 
 void DolphinView::observeCreatedItem(const KUrl& url)
@@ -1103,22 +1078,6 @@ void DolphinView::selectAndScrollToCreatedItem()
     m_createdItemUrl = KUrl();
 }
 
-void DolphinView::restoreSelection()
-{
-    disconnect(m_viewAccessor.dirLister(), SIGNAL(completed()), this, SLOT(restoreSelection()));
-    changeSelection(m_selectedItems);
-}
-
-void DolphinView::emitContentsMoved()
-{
-    // only emit the contents moved signal if no directory loading is ongoing
-    // (this would reset the contents position always to (0, 0))
-    if (!m_loadingDirectory) {
-        const QPoint pos(contentsPosition());
-        emit contentsMoved(pos.x(), pos.y());
-    }
-}
-
 void DolphinView::showHoverInformation(const KFileItem& item)
 {
     emit requestItemInfo(item);
@@ -1138,14 +1097,36 @@ void DolphinView::slotDeleteFileFinished(KJob* job)
     }
 }
 
-void DolphinView::slotRequestUrlChange(const KUrl& url)
+void DolphinView::slotDirListerCompleted()
 {
-    emit requestUrlChange(url);
-    m_controller->setUrl(url);
+    if (!m_expanderActive) {
+        slotLoadingCompleted();
+    }
+
+    if (!m_newFileNames.isEmpty()) {
+        // select all newly added items created by a paste operation or
+        // a drag & drop operation, and clear the previous selection
+        m_viewAccessor.itemView()->clearSelection();
+        const int rowCount = m_viewAccessor.proxyModel()->rowCount();
+        QItemSelection selection;
+        for (int row = 0; row < rowCount; ++row) {
+            const QModelIndex proxyIndex = m_viewAccessor.proxyModel()->index(row, 0);
+            const QModelIndex dirIndex = m_viewAccessor.proxyModel()->mapToSource(proxyIndex);
+            const KUrl url = m_viewAccessor.dirModel()->itemForIndex(dirIndex).url();
+            if (m_newFileNames.contains(url.fileName())) {
+                selection.merge(QItemSelection(proxyIndex, proxyIndex), QItemSelectionModel::Select);
+            }
+        }
+        m_viewAccessor.itemView()->selectionModel()->select(selection, QItemSelectionModel::Select);
+
+        m_newFileNames.clear();
+    }
 }
 
-void DolphinView::slotDirListerCompleted()
+void DolphinView::slotLoadingCompleted()
 {
+    m_expanderActive = false;
+
     if (!m_activeItemUrl.isEmpty()) {
         // assure that the current item remains visible
         const QModelIndex dirIndex = m_viewAccessor.dirModel()->indexForUrl(m_activeItemUrl);
@@ -1161,23 +1142,26 @@ void DolphinView::slotDirListerCompleted()
         }
     }
 
-    if (!m_newFileNames.isEmpty()) {
-        // select all newly added items created by a paste operation or
-        // a drag & drop operation
-        const int rowCount = m_viewAccessor.proxyModel()->rowCount();
-        QItemSelection selection;
-        for (int row = 0; row < rowCount; ++row) {
-            const QModelIndex proxyIndex = m_viewAccessor.proxyModel()->index(row, 0);
-            const QModelIndex dirIndex = m_viewAccessor.proxyModel()->mapToSource(proxyIndex);
-            const KUrl url = m_viewAccessor.dirModel()->itemForIndex(dirIndex).url();
-            if (m_newFileNames.contains(url.fileName())) {
-                selection.merge(QItemSelection(proxyIndex, proxyIndex), QItemSelectionModel::Select);
+    if (!m_selectedItems.isEmpty()) {
+        const KUrl& baseUrl = url();
+        KUrl url;
+        QItemSelection newSelection;
+        foreach(const KFileItem& item, m_selectedItems) {
+            url = item.url().upUrl();
+            if (baseUrl.equals(url, KUrl::CompareWithoutTrailingSlash)) {
+                QModelIndex index = m_viewAccessor.proxyModel()->mapFromSource(m_viewAccessor.dirModel()->indexForItem(item));
+                newSelection.select(index, index);
             }
         }
-        m_viewAccessor.itemView()->selectionModel()->select(selection, QItemSelectionModel::Select);
-
-        m_newFileNames.clear();
+        m_viewAccessor.itemView()->selectionModel()->select(newSelection,
+                                                            QItemSelectionModel::ClearAndSelect
+                                                            | QItemSelectionModel::Current);
+        m_selectedItems.clear();
     }
+
+    // Restore the contents position. This has to be done using a Qt::QueuedConnection
+    // because the view might not be in its final state yet.
+    QMetaObject::invokeMethod(this, "restoreContentsPosition", Qt::QueuedConnection);
 }
 
 void DolphinView::slotRefreshItems()
@@ -1200,15 +1184,8 @@ void DolphinView::loadDirectory(const KUrl& url, bool reload)
         return;
     }
 
-    m_loadingDirectory = true;
-
-    if (reload) {
-        m_selectedItems = selectedItems();
-        connect(m_viewAccessor.dirLister(), SIGNAL(completed()), this, SLOT(restoreSelection()));
-    }
-
-    m_viewAccessor.dirLister()->stop();
-    m_viewAccessor.dirLister()->openUrl(url, reload ? KDirLister::Reload : KDirLister::NoFlags);
+    KDirLister* dirLister = m_viewAccessor.dirLister();
+    dirLister->openUrl(url, reload ? KDirLister::Reload : KDirLister::NoFlags);
 }
 
 void DolphinView::applyViewProperties()
@@ -1221,7 +1198,7 @@ void DolphinView::applyViewProperties()
 
     const Mode mode = props.viewMode();
     if (m_mode != mode) {
-        const int oldZoomLevel = m_controller->zoomLevel();
+        const int oldZoomLevel = m_viewModeController->zoomLevel();
 
         m_mode = mode;
         createView();
@@ -1275,7 +1252,7 @@ void DolphinView::applyViewProperties()
     const bool showPreview = props.showPreview();
     if (showPreview != m_showPreview) {
         m_showPreview = showPreview;
-        const int oldZoomLevel = m_controller->zoomLevel();
+        const int oldZoomLevel = m_viewModeController->zoomLevel();
         emit showPreviewChanged();
 
         // Enabling or disabling the preview might change the icon size of the view.
@@ -1295,40 +1272,16 @@ void DolphinView::applyViewProperties()
 void DolphinView::createView()
 {
     deleteView();
+
     Q_ASSERT(m_viewAccessor.itemView() == 0);
-    m_viewAccessor.createView(this, m_controller, m_mode);
+    m_viewAccessor.createView(this, m_dolphinViewController, m_viewModeController, m_mode);
 
     QAbstractItemView* view = m_viewAccessor.itemView();
     Q_ASSERT(view != 0);
     view->installEventFilter(this);
     view->viewport()->installEventFilter(this);
-    setFocusProxy(view);
-
-    /* TODO: enable folder expanding again later
-
-    if (m_mode != ColumnView) {
-        // Give the view the ability to auto-expand its directories on hovering
-        // (the column view takes care about this itself). If the details view
-        // uses expandable folders, the auto-expanding should be used always.
-        FolderExpander* folderExpander = new FolderExpander(view, m_viewAccessor.proxyModel());
-        folderExpander->setEnabled(m_viewAccessor.hasExpandableFolders());
-        connect(folderExpander, SIGNAL(enterDir(const QModelIndex&)),
-                m_controller, SLOT(triggerItem(const QModelIndex&)));
-
-    }
-    else {
-        // Listen out for requests to delete the current column.
-        connect(m_viewAccessor.columnsContainer(), SIGNAL(requestColumnDeletion(QAbstractItemView*)),
-                this, SLOT(deleteWhenNotDragSource(QAbstractItemView*)));
-    }*/
-
-    m_controller->setItemView(view);
 
-    m_selectionChangedTimer = new QTimer(this);
-    m_selectionChangedTimer->setSingleShot(true);
-    m_selectionChangedTimer->setInterval(300);
-    connect(m_selectionChangedTimer, SIGNAL(timeout()),
-            this, SLOT(emitSelectionChangedSignal()));
+    m_dolphinViewController->setItemView(view);
 
     // When changing the view mode, the selection is lost due to reinstantiating
     // a new item view with a custom selection model. Pass the ownership of the
@@ -1339,24 +1292,10 @@ void DolphinView::createView()
         m_selectionModel = view->selectionModel();
     }
     m_selectionModel->setParent(this);
+    connect(m_selectionModel, SIGNAL(selectionChanged(QItemSelection, QItemSelection)),
+            this, SLOT(slotSelectionChanged(QItemSelection, QItemSelection)));
 
-    view->setSelectionMode(QAbstractItemView::ExtendedSelection);
-
-    m_versionControlObserver = new VersionControlObserver(view);
-    connect(m_versionControlObserver, SIGNAL(infoMessage(const QString&)),
-            this, SIGNAL(infoMessage(const QString&)));
-    connect(m_versionControlObserver, SIGNAL(errorMessage(const QString&)),
-            this, SIGNAL(errorMessage(const QString&)));
-    connect(m_versionControlObserver, SIGNAL(operationCompletedMessage(const QString&)),
-            this, SIGNAL(operationCompletedMessage(const QString&)));
-
-    connect(view->selectionModel(), SIGNAL(selectionChanged(const QItemSelection&, const QItemSelection&)),
-            this, SLOT(emitDelayedSelectionChangedSignal()));
-    connect(view->verticalScrollBar(), SIGNAL(valueChanged(int)),
-            this, SLOT(emitContentsMoved()));
-    connect(view->horizontalScrollBar(), SIGNAL(valueChanged(int)),
-            this, SLOT(emitContentsMoved()));
-
+    setFocusProxy(m_viewAccessor.layoutTarget());
     m_topLayout->insertWidget(1, m_viewAccessor.layoutTarget());
 }
 
@@ -1374,14 +1313,11 @@ void DolphinView::deleteView()
         m_topLayout->removeWidget(view);
         view->close();
 
+        // disconnect all signal/slots
         disconnect(view);
-        m_controller->disconnect(view);
+        m_viewModeController->disconnect(view);
         view->disconnect();
 
-        // TODO: move this code into ViewAccessor::deleteView()
-        deleteWhenNotDragSource(view);
-        view = 0;
-
         m_viewAccessor.deleteView();
     }
 }
@@ -1396,7 +1332,7 @@ void DolphinView::updateZoomLevel(int oldZoomLevel)
 {
     const int newZoomLevel = ZoomLevelInfo::zoomLevelForIconSize(m_viewAccessor.itemView()->iconSize());
     if (oldZoomLevel != newZoomLevel) {
-        m_controller->setZoomLevel(newZoomLevel);
+        m_viewModeController->setZoomLevel(newZoomLevel);
         emit zoomLevelChanged(newZoomLevel);
     }
 }
@@ -1430,27 +1366,43 @@ DolphinView::ViewAccessor::ViewAccessor(DolphinSortFilterProxyModel* proxyModel)
     m_iconsView(0),
     m_detailsView(0),
     m_columnsContainer(0),
-    m_proxyModel(proxyModel)
+    m_proxyModel(proxyModel),
+    m_dragSource(0)
+{
+}
+
+DolphinView::ViewAccessor::~ViewAccessor()
 {
+    delete m_dragSource;
+    m_dragSource = 0;
 }
 
 void DolphinView::ViewAccessor::createView(QWidget* parent,
-                                           DolphinController* controller,
+                                           DolphinViewController* dolphinViewController,
+                                           const ViewModeController* viewModeController,
                                            Mode mode)
 {
     Q_ASSERT(itemView() == 0);
 
     switch (mode) {
     case IconsView:
-        m_iconsView = new DolphinIconsView(parent, controller, m_proxyModel);
+        m_iconsView = new DolphinIconsView(parent,
+                                           dolphinViewController,
+                                           viewModeController,
+                                           m_proxyModel);
         break;
 
     case DetailsView:
-        m_detailsView = new DolphinDetailsView(parent, controller, m_proxyModel);
+        m_detailsView = new DolphinDetailsView(parent,
+                                               dolphinViewController,
+                                               viewModeController,
+                                               m_proxyModel);
         break;
 
     case ColumnView:
-        m_columnsContainer = new DolphinColumnViewContainer(parent, controller);
+        m_columnsContainer = new DolphinColumnViewContainer(parent,
+                                                            dolphinViewController,
+                                                            viewModeController);
         break;
 
     default:
@@ -1460,13 +1412,31 @@ void DolphinView::ViewAccessor::createView(QWidget* parent,
 
 void DolphinView::ViewAccessor::deleteView()
 {
-    // TODO: Move the deleteWhenNotDragSource() code into the view
-    // accessor, so that creating and deleting is fully done by
-    // the view accessor.
+    QAbstractItemView* view = itemView();
+    if (view != 0) {
+        if (DragAndDropHelper::instance().isDragSource(view)) {
+            // The view is a drag source (the feature "Open folders
+            // during drag operations" is used). Deleting the view
+            // during an ongoing drag operation is not allowed, so
+            // this will postponed.
+            if (m_dragSource != 0) {
+                // the old stored view is obviously not the drag source anymore
+                m_dragSource->deleteLater();
+                m_dragSource = 0;
+            }
+            view->hide();
+            m_dragSource = view;
+        } else {
+            view->deleteLater();
+        }
+    }
+
     m_iconsView = 0;
     m_detailsView = 0;
 
-    m_columnsContainer->deleteLater();
+    if (m_columnsContainer != 0) {
+        m_columnsContainer->deleteLater();
+    }
     m_columnsContainer = 0;
 }
 
@@ -1518,16 +1488,35 @@ bool DolphinView::ViewAccessor::supportsCategorizedSorting() const
     return m_iconsView != 0;
 }
 
-bool DolphinView::ViewAccessor::hasExpandableFolders() const
+bool DolphinView::ViewAccessor::itemsExpandable() const
 {
-    const DolphinSettings& settings = DolphinSettings::instance();
-    return settings.generalSettings()->autoExpandFolders() ||
-           ((m_detailsView != 0) && settings.detailsModeSettings()->expandableFolders());
+    return (m_detailsView != 0) && m_detailsView->itemsExpandable();
 }
 
-bool DolphinView::ViewAccessor::itemsExpandable() const
+
+QSet<KUrl> DolphinView::ViewAccessor::expandedUrls() const
 {
-    return (m_detailsView != 0) && m_detailsView->itemsExpandable();
+    if (m_detailsView != 0) {
+        return m_detailsView->expandedUrls();
+    }
+
+    return QSet<KUrl>();
+}
+
+const DolphinDetailsViewExpander* DolphinView::ViewAccessor::setExpandedUrls(const QSet<KUrl>& urlsToExpand)
+{
+    if ((m_detailsView != 0) && m_detailsView->itemsExpandable() && !urlsToExpand.isEmpty()) {
+        // Check if another expander is already active and stop it if necessary.
+        if(!m_detailsViewExpander.isNull()) {
+            m_detailsViewExpander->stop();
+        }
+
+        m_detailsViewExpander = new DolphinDetailsViewExpander(m_detailsView, urlsToExpand);
+        return m_detailsViewExpander;
+    }
+    else {
+        return 0;
+    }
 }
 
 bool DolphinView::ViewAccessor::reloadOnAdditionalInfoChange() const
@@ -1537,7 +1526,6 @@ bool DolphinView::ViewAccessor::reloadOnAdditionalInfoChange() const
     return m_detailsView != 0;
 }
 
-
 DolphinModel* DolphinView::ViewAccessor::dirModel() const
 {
     return static_cast<DolphinModel*>(proxyModel()->sourceModel());
@@ -1556,4 +1544,26 @@ KDirLister* DolphinView::ViewAccessor::dirLister() const
     return dirModel()->dirLister();
 }
 
+void DolphinView::slotRedirection(const KUrl& oldUrl, const KUrl& newUrl)
+{
+    if (oldUrl.equals(url(), KUrl::CompareWithoutTrailingSlash)) {
+        emit redirection(oldUrl, newUrl);
+        m_controller->redirectToUrl(newUrl); // #186947
+    }
+}
+
+void DolphinView::restoreContentsPosition()
+{
+    if (!m_restoredContentsPosition.isNull()) {
+        const int x = m_restoredContentsPosition.x();
+        const int y = m_restoredContentsPosition.y();
+        m_restoredContentsPosition = QPoint();
+
+        QAbstractItemView* view = m_viewAccessor.itemView();
+        Q_ASSERT(view != 0);
+        view->horizontalScrollBar()->setValue(x);
+        view->verticalScrollBar()->setValue(y);
+    }
+}
+
 #include "dolphinview.moc"