]> cloud.milkyroute.net Git - dolphin.git/blobdiff - src/dolphinview.cpp
SVN_SILENT: Minor simplificiation of the code. No change of behavior has been done.
[dolphin.git] / src / dolphinview.cpp
index 476f6591d32034d599137bb3eca3b94ed82f8761..606c5202d4e93a8f7f8c2e1c9b47930c28aa4473 100644 (file)
@@ -64,6 +64,7 @@
 #include "settings/dolphinsettings.h"
 #include "viewproperties.h"
 #include "zoomlevelinfo.h"
+#include "dolphindetailsviewexpander.h"
 
 /**
  * Helper function for sorting items with qSort() in
@@ -94,6 +95,7 @@ DolphinView::DolphinView(QWidget* parent,
     m_selectionChangedTimer(0),
     m_rootUrl(),
     m_activeItemUrl(),
+    m_restoredContentsPosition(),
     m_createdItemUrl(),
     m_selectedItems(),
     m_newFileNames()
@@ -134,8 +136,8 @@ DolphinView::DolphinView(QWidget* parent,
             this, SLOT(clearHoverInformation()));
 
     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>>&)),
@@ -154,7 +156,7 @@ DolphinView::DolphinView(QWidget* parent,
             this, SLOT(emitSelectionChangedSignal()));
 
     applyViewProperties();
-    m_topLayout->addWidget(m_viewAccessor.itemView());
+    m_topLayout->addWidget(m_viewAccessor.layoutTarget());
 }
 
 DolphinView::~DolphinView()
@@ -220,6 +222,10 @@ void DolphinView::setMode(Mode mode)
     const int oldZoomLevel = m_controller->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 +252,7 @@ void DolphinView::setMode(Mode mode)
     emit modeChanged();
 
     updateZoomLevel(oldZoomLevel);
-    if (m_showPreview) {   
-        loadDirectory(viewPropsUrl);
-    }
+    loadDirectory(viewPropsUrl);
 }
 
 DolphinView::Mode DolphinView::mode() const
@@ -283,54 +287,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,22 +334,45 @@ KUrl::List DolphinView::selectedUrls() const
 
 int DolphinView::selectedItemsCount() const
 {
-    return m_viewAccessor.itemView()->selectionModel()->selectedIndexes().count();
+    const QAbstractItemView* view = m_viewAccessor.itemView();
+    if (view == 0) {
+        return 0;
+    }
+
+    return view->selectionModel()->selectedIndexes().count();
+}
+
+QItemSelectionModel* DolphinView::selectionModel() const
+{
+    return m_viewAccessor.itemView()->selectionModel();
 }
 
 void DolphinView::setContentsPosition(int x, int y)
 {
     QAbstractItemView* view = m_viewAccessor.itemView();
+    Q_ASSERT(view != 0);
     view->horizontalScrollBar()->setValue(x);
     view->verticalScrollBar()->setValue(y);
 
     m_loadingDirectory = false;
 }
 
+void DolphinView::setRestoredContentsPosition(const QPoint& pos)
+{
+    // TODO: This function is called by DolphinViewContainer.
+    // If it makes use of DolphinView::restoreState(...) to restore the
+    // view state in KDE 4.5, this function can be removed.
+    m_restoredContentsPosition = pos;
+}
+
 QPoint DolphinView::contentsPosition() const
 {
-    const int x = m_viewAccessor.itemView()->horizontalScrollBar()->value();
-    const int y = m_viewAccessor.itemView()->verticalScrollBar()->value();
+    // TODO: If DolphinViewContainer uses DolphinView::saveState(...) to save the
+    // view state in KDE 4.5, this code can be moved to DolphinView::saveState.
+    QAbstractItemView* view = m_viewAccessor.itemView();
+    Q_ASSERT(view != 0);
+    const int x = view->horizontalScrollBar()->value();
+    const int y = view->verticalScrollBar()->value();
     return QPoint(x, y);
 }
 
@@ -450,8 +452,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()
@@ -579,25 +589,27 @@ void DolphinView::setUrl(const KUrl& url)
     updateView(url, KUrl());
 }
 
-void DolphinView::changeSelection(const KFileItemList& selection)
+void DolphinView::selectAll()
 {
-    clearSelection();
-    if (selection.isEmpty()) {
-        return;
-    }
-    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);
-        }
-    }
-    m_viewAccessor.itemView()->selectionModel()->select(newSelection,
-                                         QItemSelectionModel::ClearAndSelect
-                                         | QItemSelectionModel::Current);
+    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()
@@ -624,7 +636,7 @@ void DolphinView::renameSelectedItems()
             return;
         }
         delete dialog;
-        
+
         // the selection would be invalid after renaming the items, so just clear
         // it before
         clearSelection();
@@ -827,20 +839,6 @@ 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()) {
@@ -866,6 +864,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;
     }
@@ -895,11 +911,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();
 }
 
@@ -1041,6 +1062,8 @@ bool DolphinView::isTabsForFilesEnabled() const
 
 void DolphinView::activateItem(const KUrl& url)
 {
+    // TODO: If DolphinViewContainer uses DolphinView::restoreState(...) to restore the
+    // view state in KDE 4.5, this function can be removed.
     m_activeItemUrl = url;
 }
 
@@ -1049,6 +1072,53 @@ bool DolphinView::itemsExpandable() const
     return m_viewAccessor.itemsExpandable();
 }
 
+void DolphinView::restoreState(QDataStream &stream)
+{
+     // current item
+    stream >> m_activeItemUrl;
+
+    // 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) {
+        m_expanderActive = true;
+        connect (expander, SIGNAL(completed()), this, SLOT(slotLoadingCompleted()));
+    }
+    else {
+        m_expanderActive = false;
+    }
+}
+
+void DolphinView::saveState(QDataStream &stream)
+{
+    // current item
+    KFileItem currentItem;
+    const QAbstractItemView* view = m_viewAccessor.itemView();
+
+    if(view) {
+        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
+    stream << contentsPosition();
+
+    // 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)
 {
     m_createdItemUrl = url;
@@ -1069,14 +1139,13 @@ void DolphinView::selectAndScrollToCreatedItem()
     m_createdItemUrl = KUrl();
 }
 
-void DolphinView::restoreSelection()
-{
-    disconnect(m_viewAccessor.dirLister(), SIGNAL(completed()), this, SLOT(restoreSelection()));
-    changeSelection(m_selectedItems);
-}
-
 void DolphinView::emitContentsMoved()
 {
+    // TODO: If DolphinViewContainer uses DolphinView::saveState(...) to save the
+    // view state in KDE 4.5, the contentsMoved signal might not be needed anymore,
+    // depending on how the implementation is done.
+    // In that case, the code in contentsPosition() can be moved to saveState().
+
     // 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) {
@@ -1112,19 +1181,8 @@ void DolphinView::slotRequestUrlChange(const KUrl& url)
 
 void DolphinView::slotDirListerCompleted()
 {
-    if (!m_activeItemUrl.isEmpty()) {
-        // assure that the current item remains visible
-        const QModelIndex dirIndex = m_viewAccessor.dirModel()->indexForUrl(m_activeItemUrl);
-        if (dirIndex.isValid()) {
-            const QModelIndex proxyIndex = m_viewAccessor.proxyModel()->mapFromSource(dirIndex);
-            QAbstractItemView* view = m_viewAccessor.itemView();
-            const bool clearSelection = !hasSelection();
-            view->setCurrentIndex(proxyIndex);
-            if (clearSelection) {
-                view->clearSelection();
-            }
-            m_activeItemUrl.clear();
-        }
+    if (!m_expanderActive) {
+        slotLoadingCompleted();
     }
 
     if (!m_newFileNames.isEmpty()) {
@@ -1146,6 +1204,48 @@ void DolphinView::slotDirListerCompleted()
     }
 }
 
+void DolphinView::slotLoadingCompleted()
+{
+    m_expanderActive = false;
+    m_loadingDirectory = false; 
+
+    if (!m_activeItemUrl.isEmpty()) {
+        // assure that the current item remains visible
+        const QModelIndex dirIndex = m_viewAccessor.dirModel()->indexForUrl(m_activeItemUrl);
+        if (dirIndex.isValid()) {
+            const QModelIndex proxyIndex = m_viewAccessor.proxyModel()->mapFromSource(dirIndex);
+            QAbstractItemView* view = m_viewAccessor.itemView();
+            const bool clearSelection = !hasSelection();
+            view->setCurrentIndex(proxyIndex);
+            if (clearSelection) {
+                view->clearSelection();
+            }
+            m_activeItemUrl.clear();
+        }
+    }
+
+    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(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()
 {
     if (m_assureVisibleCurrentIndex) {
@@ -1167,11 +1267,7 @@ void DolphinView::loadDirectory(const KUrl& url, bool reload)
     }
 
     m_loadingDirectory = true;
-
-    if (reload) {
-        m_selectedItems = selectedItems();
-        connect(m_viewAccessor.dirLister(), SIGNAL(completed()), this, SLOT(restoreSelection()));
-    }
+    m_expanderActive = false;
 
     m_viewAccessor.dirLister()->openUrl(url, reload ? KDirLister::Reload : KDirLister::NoFlags);
 }
@@ -1260,6 +1356,7 @@ void DolphinView::applyViewProperties()
 void DolphinView::createView()
 {
     deleteView();
+
     Q_ASSERT(m_viewAccessor.itemView() == 0);
     m_viewAccessor.createView(this, m_controller, m_mode);
 
@@ -1269,8 +1366,6 @@ void DolphinView::createView()
     view->viewport()->installEventFilter(this);
 
     m_controller->setItemView(view);
-    connect(m_controller, SIGNAL(selectionChanged()),
-            this, SLOT(emitDelayedSelectionChangedSignal()));
 
     // 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
@@ -1281,6 +1376,8 @@ void DolphinView::createView()
         m_selectionModel = view->selectionModel();
     }
     m_selectionModel->setParent(this);
+    connect(view->selectionModel(), SIGNAL(selectionChanged(const QItemSelection&, const QItemSelection&)),
+            this, SLOT(slotSelectionChanged(const QItemSelection&, const QItemSelection&)));
 
     connect(view->verticalScrollBar(), SIGNAL(valueChanged(int)),
             this, SLOT(emitContentsMoved()));
@@ -1305,9 +1402,14 @@ void DolphinView::deleteView()
         m_topLayout->removeWidget(view);
         view->close();
 
+        // disconnect all signal/slots
         disconnect(view);
         m_controller->disconnect(view);
         view->disconnect();
+        disconnect(view->verticalScrollBar(), SIGNAL(valueChanged(int)),
+                   this, SLOT(emitContentsMoved()));
+        disconnect(view->horizontalScrollBar(), SIGNAL(valueChanged(int)),
+                   this, SLOT(emitContentsMoved()));
 
         m_viewAccessor.deleteView();
     }
@@ -1416,7 +1518,9 @@ void DolphinView::ViewAccessor::deleteView()
     m_iconsView = 0;
     m_detailsView = 0;
 
-    m_columnsContainer->deleteLater();
+    if (m_columnsContainer != 0) {
+        m_columnsContainer->deleteLater();
+    }
     m_columnsContainer = 0;
 }
 
@@ -1426,6 +1530,11 @@ void DolphinView::ViewAccessor::prepareUrlChange(const KUrl& url)
     if (m_columnsContainer != 0) {
         m_columnsContainer->showColumn(url);
     }
+
+    if(!m_detailsViewExpander.isNull()) {
+        // Stop expanding items in the current folder
+        m_detailsViewExpander->stop();
+    }
 }
 
 QAbstractItemView* DolphinView::ViewAccessor::itemView() const
@@ -1473,6 +1582,28 @@ bool DolphinView::ViewAccessor::itemsExpandable() const
     return (m_detailsView != 0) && m_detailsView->itemsExpandable();
 }
 
+
+QSet<KUrl> DolphinView::ViewAccessor::expandedUrls() const
+{
+    if(m_detailsView != 0) {
+        return m_detailsView->expandedUrls();
+    }
+    else {
+        return QSet<KUrl>();
+    }
+}
+
+const DolphinDetailsViewExpander* DolphinView::ViewAccessor::setExpandedUrls(const QSet<KUrl>& urlsToExpand)
+{
+    if((m_detailsView != 0) && m_detailsView->itemsExpandable() && !urlsToExpand.isEmpty()) {
+        m_detailsViewExpander = new DolphinDetailsViewExpander(m_detailsView, urlsToExpand);
+        return m_detailsViewExpander;
+    }
+    else {
+        return 0;
+    }
+}
+
 bool DolphinView::ViewAccessor::reloadOnAdditionalInfoChange() const
 {
     // the details view requires no reloading of the directory, as it maps
@@ -1498,4 +1629,18 @@ KDirLister* DolphinView::ViewAccessor::dirLister() const
     return dirModel()->dirLister();
 }
 
+void DolphinView::slotRedirection(const KUrl& oldUrl, const KUrl& newUrl)
+{
+    emit redirection(oldUrl, newUrl);
+    m_controller->redirectToUrl(newUrl); // #186947
+}
+
+void DolphinView::restoreContentsPosition()
+{
+    if (!m_restoredContentsPosition.isNull()) {
+        setContentsPosition(m_restoredContentsPosition.x(), m_restoredContentsPosition.y());
+        m_restoredContentsPosition = QPoint();
+    }
+}
+
 #include "dolphinview.moc"