]> cloud.milkyroute.net Git - dolphin.git/blobdiff - src/dolphinview.cpp
Restore the "Edit->Selection" menu from Konqueror 3 for file
[dolphin.git] / src / dolphinview.cpp
index 56825816828629936df345cd508124c4f82b87eb..e057c950acd6d83ce4ccd2347ceb38484e0d5b47 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>>&)),
@@ -246,7 +248,7 @@ void DolphinView::setMode(Mode mode)
     emit modeChanged();
 
     updateZoomLevel(oldZoomLevel);
-    if (m_showPreview) {   
+    if (m_showPreview) {
         loadDirectory(viewPropsUrl);
     }
 }
@@ -283,45 +285,12 @@ 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
 {
     const QAbstractItemView* view = m_viewAccessor.itemView();
     return view && view->selectionModel()->hasSelection();
 }
 
-void DolphinView::clearSelection()
-{
-    QItemSelectionModel* selModel = m_viewAccessor.itemView()->selectionModel();
-    const QModelIndex currentIndex = selModel->currentIndex();
-    selModel->setCurrentIndex(currentIndex, QItemSelectionModel::Current |
-                                            QItemSelectionModel::Clear);
-    m_selectedItems.clear();
-}
-
 KFileItemList DolphinView::selectedItems() const
 {
     const QAbstractItemView* view = m_viewAccessor.itemView();
@@ -359,19 +328,37 @@ int DolphinView::selectedItemsCount() const
     return m_viewAccessor.itemView()->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);
 }
 
@@ -580,6 +567,39 @@ void DolphinView::setUrl(const KUrl& url)
     updateView(url, KUrl());
 }
 
+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);
+}
+
+void DolphinView::clearSelection()
+{
+    QItemSelectionModel* selModel = m_viewAccessor.itemView()->selectionModel();
+    const QModelIndex currentIndex = selModel->currentIndex();
+    selModel->setCurrentIndex(currentIndex, QItemSelectionModel::Current |
+                                            QItemSelectionModel::Clear);
+    m_selectedItems.clear();
+}
+
 void DolphinView::changeSelection(const KFileItemList& selection)
 {
     clearSelection();
@@ -625,7 +645,7 @@ void DolphinView::renameSelectedItems()
             return;
         }
         delete dialog;
-        
+
         // the selection would be invalid after renaming the items, so just clear
         // it before
         clearSelection();
@@ -828,20 +848,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()) {
@@ -867,6 +873,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;
     }
@@ -1042,6 +1066,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;
 }
 
@@ -1050,6 +1076,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;
@@ -1078,6 +1151,11 @@ void DolphinView::restoreSelection()
 
 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) {
@@ -1113,19 +1191,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()) {
@@ -1147,6 +1214,31 @@ 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();
+        }
+    }
+
+    // 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) {
@@ -1168,6 +1260,7 @@ void DolphinView::loadDirectory(const KUrl& url, bool reload)
     }
 
     m_loadingDirectory = true;
+    m_expanderActive = false;
 
     if (reload) {
         m_selectedItems = selectedItems();
@@ -1261,6 +1354,7 @@ void DolphinView::applyViewProperties()
 void DolphinView::createView()
 {
     deleteView();
+
     Q_ASSERT(m_viewAccessor.itemView() == 0);
     m_viewAccessor.createView(this, m_controller, m_mode);
 
@@ -1306,9 +1400,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();
     }
@@ -1417,8 +1516,9 @@ void DolphinView::ViewAccessor::deleteView()
     m_iconsView = 0;
     m_detailsView = 0;
 
-    if (m_columnsContainer)
+    if (m_columnsContainer != 0) {
         m_columnsContainer->deleteLater();
+    }
     m_columnsContainer = 0;
 }
 
@@ -1428,6 +1528,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
@@ -1475,6 +1580,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
@@ -1500,4 +1627,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"