]> cloud.milkyroute.net Git - dolphin.git/blobdiff - src/views/dolphinview.cpp
DolphinView: use correct slot as trashjob results
[dolphin.git] / src / views / dolphinview.cpp
index 5bdc9e15f93ea9856a46218233670b4fef6d7b65..9bbdc3b10e9572fba0e0ff49f280ead6454c043c 100644 (file)
@@ -12,6 +12,9 @@
 #include "dolphinitemlistview.h"
 #include "dolphinnewfilemenuobserver.h"
 #include "draganddrophelper.h"
+#ifndef QT_NO_ACCESSIBILITY
+#include "kitemviews/accessibility/kitemlistviewaccessible.h"
+#endif
 #include "kitemviews/kfileitemlistview.h"
 #include "kitemviews/kfileitemmodel.h"
 #include "kitemviews/kitemlistcontainer.h"
@@ -50,6 +53,9 @@
 #include <kwidgetsaddons_version.h>
 
 #include <QAbstractItemView>
+#ifndef QT_NO_ACCESSIBILITY
+#include <QAccessible>
+#endif
 #include <QActionGroup>
 #include <QApplication>
 #include <QClipboard>
@@ -117,8 +123,8 @@ DolphinView::DolphinView(const QUrl &url, QWidget *parent)
     applyModeToView();
 
     KItemListController *controller = new KItemListController(m_model, m_view, this);
-    const int delay = GeneralSettings::autoExpandFolders() ? 750 : -1;
-    controller->setAutoActivationDelay(delay);
+    controller->setAutoActivationEnabled(GeneralSettings::autoExpandFolders());
+    connect(controller, &KItemListController::doubleClickViewBackground, this, &DolphinView::doubleClickViewBackground);
 
     // The EnlargeSmallPreviews setting can only be changed after the model
     // has been set in the view by KItemListController.
@@ -130,10 +136,10 @@ DolphinView::DolphinView(const QUrl &url, QWidget *parent)
     m_view->setAccessibleParentsObject(m_container);
 #endif
     setFocusProxy(m_container);
-    connect(m_container->horizontalScrollBar(), &QScrollBar::valueChanged, this, [=] {
+    connect(m_container->horizontalScrollBar(), &QScrollBar::valueChanged, this, [this] {
         hideToolTip();
     });
-    connect(m_container->verticalScrollBar(), &QScrollBar::valueChanged, this, [=] {
+    connect(m_container->verticalScrollBar(), &QScrollBar::valueChanged, this, [this] {
         hideToolTip();
     });
 
@@ -146,11 +152,13 @@ DolphinView::DolphinView(const QUrl &url, QWidget *parent)
     // This is made using a heavily-modified QLabel rather than a KTitleWidget
     // because KTitleWidget can't be told to turn off mouse-selectable text
     m_placeholderLabel = new QLabel(this);
+    // Don't consume mouse events
+    m_placeholderLabel->setAttribute(Qt::WA_TransparentForMouseEvents);
+
     QFont placeholderLabelFont;
     // To match the size of a level 2 Heading/KTitleWidget
     placeholderLabelFont.setPointSize(qRound(placeholderLabelFont.pointSize() * 1.3));
     m_placeholderLabel->setFont(placeholderLabelFont);
-    m_placeholderLabel->setTextInteractionFlags(Qt::NoTextInteraction);
     m_placeholderLabel->setWordWrap(true);
     m_placeholderLabel->setAlignment(Qt::AlignCenter);
     // Match opacity of QML placeholder label component
@@ -163,10 +171,6 @@ DolphinView::DolphinView(const QUrl &url, QWidget *parent)
     auto *centeringLayout = new QVBoxLayout(m_container);
     centeringLayout->addWidget(m_placeholderLabel);
     centeringLayout->setAlignment(m_placeholderLabel, Qt::AlignCenter);
-    m_placeholderLabel->setContextMenuPolicy(Qt::CustomContextMenu);
-    connect(m_placeholderLabel, &QWidget::customContextMenuRequested, this, [this](const QPoint &pos) {
-        slotViewContextMenuRequested(m_placeholderLabel->mapToGlobal(pos));
-    });
 
     controller->setSelectionBehavior(KItemListController::MultiSelection);
     connect(controller, &KItemListController::itemActivated, this, &DolphinView::slotItemActivated);
@@ -231,7 +235,9 @@ DolphinView::DolphinView(const QUrl &url, QWidget *parent)
     m_versionControlObserver->setView(this);
     m_versionControlObserver->setModel(m_model);
     connect(m_versionControlObserver, &VersionControlObserver::infoMessage, this, &DolphinView::infoMessage);
-    connect(m_versionControlObserver, &VersionControlObserver::errorMessage, this, &DolphinView::errorMessage);
+    connect(m_versionControlObserver, &VersionControlObserver::errorMessage, this, [this](const QString &message) {
+        Q_EMIT errorMessage(message, KIO::ERR_UNKNOWN);
+    });
     connect(m_versionControlObserver, &VersionControlObserver::operationCompletedMessage, this, &DolphinView::operationCompletedMessage);
 
     m_twoClicksRenamingTimer = new QTimer(this);
@@ -267,7 +273,6 @@ void DolphinView::setActive(bool active)
     if (active) {
         m_container->setFocus();
         Q_EMIT activated();
-        Q_EMIT writeStateChanged(m_isFolderWritable);
     }
 }
 
@@ -309,11 +314,17 @@ void DolphinView::setSelectionModeEnabled(const bool enabled)
         m_view->setStyle(m_proxyStyle.get());
         m_view->setEnabledSelectionToggles(DolphinItemListView::SelectionTogglesEnabled::False);
     } else {
-        setStyle(QApplication::style());
-        m_view->setStyle(QApplication::style());
+        setStyle(nullptr);
+        m_view->setStyle(nullptr);
         m_view->setEnabledSelectionToggles(DolphinItemListView::SelectionTogglesEnabled::FollowSetting);
     }
     m_container->controller()->setSelectionModeEnabled(enabled);
+#ifndef QT_NO_ACCESSIBILITY
+    if (QAccessible::isActive()) {
+        auto accessibleViewInterface = static_cast<KItemListViewAccessible *>(QAccessible::queryAccessibleInterface(m_view));
+        accessibleViewInterface->announceSelectionModeEnabled(enabled);
+    }
+#endif
 }
 
 bool DolphinView::selectionMode() const
@@ -375,9 +386,8 @@ void DolphinView::setGroupedSorting(bool grouped)
 
     ViewProperties props(viewPropertiesUrl());
     props.setGroupedSorting(grouped);
-    props.save();
 
-    m_container->controller()->model()->setGroupedSorting(grouped);
+    m_model->setGroupedSorting(grouped);
 
     Q_EMIT groupedSortingChanged(grouped);
 }
@@ -471,7 +481,13 @@ int DolphinView::zoomLevel() const
 void DolphinView::setSortRole(const QByteArray &role)
 {
     if (role != sortRole()) {
-        updateSortRole(role);
+        ViewProperties props(viewPropertiesUrl());
+        props.setSortRole(role);
+
+        KItemModelBase *model = m_container->controller()->model();
+        model->setSortRole(role);
+
+        Q_EMIT sortRoleChanged(role);
     }
 }
 
@@ -484,7 +500,12 @@ QByteArray DolphinView::sortRole() const
 void DolphinView::setSortOrder(Qt::SortOrder order)
 {
     if (sortOrder() != order) {
-        updateSortOrder(order);
+        ViewProperties props(viewPropertiesUrl());
+        props.setSortOrder(order);
+
+        m_model->setSortOrder(order);
+
+        Q_EMIT sortOrderChanged(order);
     }
 }
 
@@ -556,8 +577,7 @@ void DolphinView::readSettings()
     m_view->readSettings();
     applyViewProperties();
 
-    const int delay = GeneralSettings::autoExpandFolders() ? 750 : -1;
-    m_container->controller()->setAutoActivationDelay(delay);
+    m_container->controller()->setAutoActivationEnabled(GeneralSettings::autoExpandFolders());
 
     const int newZoomLevel = m_view->zoomLevel();
     if (newZoomLevel != oldZoomLevel) {
@@ -641,11 +661,11 @@ void DolphinView::emitStatusBarText(const int folderCount, const int fileCount,
     if (selection == HasSelection) {
         // At least 2 items are selected because the case of 1 selected item is handled in
         // DolphinView::requestStatusBarText().
-        foldersText = i18ncp("@info:status", "1 Folder selected", "%1 Folders selected", folderCount);
-        filesText = i18ncp("@info:status", "1 File selected", "%1 Files selected", fileCount);
+        foldersText = i18ncp("@info:status", "1 folder selected", "%1 folders selected", folderCount);
+        filesText = i18ncp("@info:status", "1 file selected", "%1 files selected", fileCount);
     } else {
-        foldersText = i18ncp("@info:status", "1 Folder", "%1 Folders", folderCount);
-        filesText = i18ncp("@info:status", "1 File", "%1 Files", fileCount);
+        foldersText = i18ncp("@info:status", "1 folder", "%1 folders", folderCount);
+        filesText = i18ncp("@info:status", "1 file", "%1 files", fileCount);
     }
 
     if (fileCount > 0 && folderCount > 0) {
@@ -655,7 +675,7 @@ void DolphinView::emitStatusBarText(const int folderCount, const int fileCount,
     } else if (folderCount > 0) {
         summary = foldersText;
     } else {
-        summary = i18nc("@info:status", "0 Folders, 0 Files");
+        summary = i18nc("@info:status", "0 folders, 0 files");
     }
     Q_EMIT statusBarTextChanged(summary);
 }
@@ -730,22 +750,39 @@ void DolphinView::renameSelectedItems()
     if (items.count() == 1 && GeneralSettings::renameInline()) {
         const int index = m_model->index(items.first());
 
-        QMetaObject::Connection *const connection = new QMetaObject::Connection;
-        *connection = connect(m_view, &KItemListView::scrollingStopped, this, [=]() {
-            QObject::disconnect(*connection);
-            delete connection;
-
-            m_view->editRole(index, "text");
+        connect(
+            m_view,
+            &KItemListView::scrollingStopped,
+            this,
+            [this, index]() {
+                m_view->editRole(index, "text");
 
-            hideToolTip();
+                hideToolTip();
 
-            connect(m_view, &DolphinItemListView::roleEditingFinished, this, &DolphinView::slotRoleEditingFinished);
-        });
+                connect(m_view, &DolphinItemListView::roleEditingFinished, this, &DolphinView::slotRoleEditingFinished);
+            },
+            Qt::SingleShotConnection);
         m_view->scrollToItem(index);
 
     } else {
         KIO::RenameFileDialog *dialog = new KIO::RenameFileDialog(items, this);
-        connect(dialog, &KIO::RenameFileDialog::renamingFinished, this, &DolphinView::slotRenameDialogRenamingFinished);
+        connect(dialog, &KIO::RenameFileDialog::renamingFinished, this, [this, items](const QList<QUrl> &urls) {
+            // The model may have already been updated, so it's possible that we don't find the old items.
+            for (int i = 0; i < items.count(); ++i) {
+                const int index = m_model->index(items[i]);
+                if (index >= 0) {
+                    QHash<QByteArray, QVariant> data;
+                    data.insert("text", urls[i].fileName());
+                    m_model->setData(index, data);
+                }
+            }
+
+            forceUrlsSelection(urls.first(), urls);
+            updateSelectionState();
+        });
+        connect(dialog, &KIO::RenameFileDialog::error, this, [this](KJob *job) {
+            KMessageBox::error(this, job->errorString());
+        });
 
         dialog->open();
     }
@@ -773,7 +810,7 @@ void DolphinView::deleteSelectedItems()
 
     using Iface = KIO::AskUserActionInterface;
     auto *trashJob = new KIO::DeleteOrTrashJob(list, Iface::Delete, Iface::DefaultConfirmation, this);
-    connect(trashJob, &KJob::result, this, &DolphinView::slotTrashFileFinished);
+    connect(trashJob, &KJob::result, this, &DolphinView::slotDeleteFileFinished);
     m_selectNextItem = true;
     trashJob->start();
 }
@@ -806,7 +843,7 @@ void DolphinView::copySelectedItems(const KFileItemList &selection, const QUrl &
     KIO::CopyJob *job = KIO::copy(selection.urlList(), destinationUrl, KIO::DefaultFlags);
     KJobWidgets::setWindow(job, this);
 
-    connect(job, &KIO::DropJob::result, this, &DolphinView::slotJobResult);
+    connect(job, &KIO::CopyJob::result, this, &DolphinView::slotJobResult);
     connect(job, &KIO::CopyJob::copying, this, &DolphinView::slotItemCreatedFromJob);
     connect(job, &KIO::CopyJob::copyingDone, this, &DolphinView::slotItemCreatedFromJob);
     KIO::FileUndoManager::self()->recordCopyJob(job);
@@ -825,7 +862,7 @@ void DolphinView::moveSelectedItems(const KFileItemList &selection, const QUrl &
     KIO::CopyJob *job = KIO::move(selection.urlList(), destinationUrl, KIO::DefaultFlags);
     KJobWidgets::setWindow(job, this);
 
-    connect(job, &KIO::DropJob::result, this, &DolphinView::slotJobResult);
+    connect(job, &KIO::CopyJob::result, this, &DolphinView::slotJobResult);
     connect(job, &KIO::CopyJob::moving, this, &DolphinView::slotItemCreatedFromJob);
     connect(job, &KIO::CopyJob::copyingDone, this, &DolphinView::slotItemCreatedFromJob);
     KIO::FileUndoManager::self()->recordCopyJob(job);
@@ -853,9 +890,12 @@ void DolphinView::duplicateSelectedItems()
 
     const QMimeDatabase db;
 
+    m_clearSelectionBeforeSelectingNewItems = true;
+    m_markFirstNewlySelectedItemAsCurrent = true;
+    m_selectJobCreatedItems = true;
+
     // Duplicate all selected items and append "copy" to the end of the file name
     // but before the filename extension, if present
-    QList<QUrl> newSelection;
     for (const auto &item : itemList) {
         const QUrl originalURL = item.url();
         const QString originalDirectoryPath = originalURL.adjusted(QUrl::RemoveFilename).path();
@@ -880,15 +920,14 @@ void DolphinView::duplicateSelectedItems()
         }
 
         KIO::CopyJob *job = KIO::copyAs(originalURL, duplicateURL);
+        job->setAutoRename(true);
         KJobWidgets::setWindow(job, this);
 
-        if (job) {
-            newSelection << duplicateURL;
-            KIO::FileUndoManager::self()->recordCopyJob(job);
-        }
+        connect(job, &KIO::CopyJob::result, this, &DolphinView::slotJobResult);
+        connect(job, &KIO::CopyJob::copyingDone, this, &DolphinView::slotItemCreatedFromJob);
+        connect(job, &KIO::CopyJob::copyingLinkDone, this, &DolphinView::slotItemLinkCreatedFromJob);
+        KIO::FileUndoManager::self()->recordCopyJob(job);
     }
-
-    forceUrlsSelection(newSelection.first(), newSelection);
 }
 
 void DolphinView::stopLoading()
@@ -1105,9 +1144,8 @@ void DolphinView::slotItemsActivated(const KItemSet &indexes)
 void DolphinView::slotItemMiddleClicked(int index)
 {
     const KFileItem &item = m_model->fileItem(index);
-    const QUrl &url = openItemAsFolderUrl(item);
+    const QUrl &url = openItemAsFolderUrl(item, GeneralSettings::browseThroughArchives());
     const auto modifiers = QGuiApplication::keyboardModifiers();
-    const QString &archiveProtocol = KProtocolManager::protocolForArchiveMimetype(item.mimetype());
     if (!url.isEmpty()) {
         // keep in sync with KUrlNavigator::slotNavigatorButtonClicked
         if (modifiers & Qt::ShiftModifier) {
@@ -1115,7 +1153,7 @@ void DolphinView::slotItemMiddleClicked(int index)
         } else {
             Q_EMIT tabRequested(url);
         }
-    } else if (!archiveProtocol.isEmpty() && isTabsForFilesEnabled()) {
+    } else if (isTabsForFilesEnabled()) {
         // keep in sync with KUrlNavigator::slotNavigatorButtonClicked
         if (modifiers & Qt::ShiftModifier) {
             Q_EMIT activeTabRequested(item.url());
@@ -1134,6 +1172,9 @@ void DolphinView::slotItemContextMenuRequested(int index, const QPointF &pos)
     if (m_selectionChangedTimer->isActive()) {
         emitSelectionChangedSignal();
     }
+    if (m_twoClicksRenamingTimer->isActive()) {
+        abortTwoClicksRenaming();
+    }
 
     const KFileItem item = m_model->fileItem(index);
     Q_EMIT requestContextMenu(pos.toPoint(), item, selectedItems(), url());
@@ -1148,7 +1189,7 @@ void DolphinView::slotHeaderContextMenuRequested(const QPointF &pos)
 {
     ViewProperties props(viewPropertiesUrl());
 
-    QPointer<QMenu> menu = new QMenu(QApplication::activeWindow());
+    QPointer<QMenu> menu = new QMenu(this);
 
     KItemListView *view = m_container->controller()->view();
     const QList<QByteArray> visibleRolesSet = view->visibleRoles();
@@ -1199,7 +1240,7 @@ void DolphinView::slotHeaderContextMenuRequested(const QPointF &pos)
 
     QAction *toggleSidePaddingAction = menu->addAction(i18nc("@action:inmenu", "Side Padding"));
     toggleSidePaddingAction->setCheckable(true);
-    toggleSidePaddingAction->setChecked(view->header()->sidePadding() > 0);
+    toggleSidePaddingAction->setChecked(layoutDirection() == Qt::LeftToRight ? view->header()->leftPadding() > 0 : view->header()->rightPadding() > 0);
 
     QAction *autoAdjustWidthsAction = menu->addAction(i18nc("@action:inmenu", "Automatic Column Widths"));
     autoAdjustWidthsAction->setCheckable(true);
@@ -1232,7 +1273,11 @@ void DolphinView::slotHeaderContextMenuRequested(const QPointF &pos)
             props.setHeaderColumnWidths(columnWidths);
             header->setAutomaticColumnResizing(false);
         } else if (action == toggleSidePaddingAction) {
-            header->setSidePadding(toggleSidePaddingAction->isChecked() ? 20 : 0);
+            if (toggleSidePaddingAction->isChecked()) {
+                header->setSidePadding(20, 20);
+            } else {
+                header->setSidePadding(0, 0);
+            }
         } else {
             // Show or hide the selected role
             const QByteArray selectedRole = action->data().toByteArray();
@@ -1285,10 +1330,11 @@ void DolphinView::slotHeaderColumnWidthChangeFinished(const QByteArray &role, qr
     props.setHeaderColumnWidths(columnWidths);
 }
 
-void DolphinView::slotSidePaddingWidthChanged(qreal width)
+void DolphinView::slotSidePaddingWidthChanged(qreal leftPaddingWidth, qreal rightPaddingWidth)
 {
     ViewProperties props(viewPropertiesUrl());
-    DetailsModeSettings::setSidePadding(int(width));
+    DetailsModeSettings::setLeftPadding(int(leftPaddingWidth));
+    DetailsModeSettings::setRightPadding(int(rightPaddingWidth));
     m_view->writeSettings();
 }
 
@@ -1412,6 +1458,11 @@ void DolphinView::slotItemCreatedFromJob(KIO::Job *, const QUrl &, const QUrl &t
     slotItemCreated(to);
 }
 
+void DolphinView::slotItemLinkCreatedFromJob(KIO::Job *, const QUrl &, const QString &, const QUrl &to)
+{
+    slotItemCreated(to);
+}
+
 void DolphinView::slotItemCreated(const QUrl &url)
 {
     if (m_markFirstNewlySelectedItemAsCurrent) {
@@ -1423,10 +1474,12 @@ void DolphinView::slotItemCreated(const QUrl &url)
     }
 }
 
-void DolphinView::onDirectoryLoadingCompleted()
+void DolphinView::onDirectoryLoadingCompletedAfterJob()
 {
     // the model should now contain all the items created by the job
-    updateSelectionState();
+    m_selectJobCreatedItems = true; // to make sure we overwrite selection
+    // update the view: scroll into View and selection
+    updateViewState();
     m_selectJobCreatedItems = false;
     m_selectedUrls.clear();
 }
@@ -1434,7 +1487,7 @@ void DolphinView::onDirectoryLoadingCompleted()
 void DolphinView::slotJobResult(KJob *job)
 {
     if (job->error() && job->error() != KIO::ERR_USER_CANCELED) {
-        Q_EMIT errorMessage(job->errorString());
+        Q_EMIT errorMessage(job->errorString(), job->error());
     }
     if (!m_selectJobCreatedItems) {
         m_selectedUrls.clear();
@@ -1446,7 +1499,7 @@ void DolphinView::slotJobResult(KJob *job)
         updateSelectionState();
         if (!m_selectedUrls.isEmpty()) {
             // not all urls were found, the model may not be up to date
-            connect(m_model, &KFileItemModel::directoryLoadingCompleted, this, &DolphinView::onDirectoryLoadingCompleted, Qt::UniqueConnection);
+            connect(m_model, &KFileItemModel::directoryLoadingCompleted, this, &DolphinView::onDirectoryLoadingCompletedAfterJob, Qt::SingleShotConnection);
         } else {
             m_selectJobCreatedItems = false;
             m_selectedUrls.clear();
@@ -1503,27 +1556,6 @@ void DolphinView::slotStatJobResult(KJob *job)
     emitStatusBarText(folderCount, fileCount, totalFileSize, NoSelection);
 }
 
-void DolphinView::updateSortRole(const QByteArray &role)
-{
-    ViewProperties props(viewPropertiesUrl());
-    props.setSortRole(role);
-
-    KItemModelBase *model = m_container->controller()->model();
-    model->setSortRole(role);
-
-    Q_EMIT sortRoleChanged(role);
-}
-
-void DolphinView::updateSortOrder(Qt::SortOrder order)
-{
-    ViewProperties props(viewPropertiesUrl());
-    props.setSortOrder(order);
-
-    m_model->setSortOrder(order);
-
-    Q_EMIT sortOrderChanged(order);
-}
-
 void DolphinView::updateSortFoldersFirst(bool foldersFirst)
 {
     ViewProperties props(viewPropertiesUrl());
@@ -1717,6 +1749,7 @@ void DolphinView::updateSelectionState()
     if (!m_selectedUrls.isEmpty()) {
         KItemListSelectionManager *selectionManager = m_container->controller()->selectionManager();
 
+        const bool shouldScrollToCurrentItem = m_clearSelectionBeforeSelectingNewItems;
         // if there is a selection already, leave it that way
         // unless some drop/paste job are in the process of creating items
         if (!selectionManager->hasSelection() || m_selectJobCreatedItems) {
@@ -1741,6 +1774,9 @@ void DolphinView::updateSelectionState()
             if (!selectedItems.isEmpty()) {
                 selectionManager->beginAnchoredSelection(selectionManager->currentItem());
                 selectionManager->setSelectedItems(selectedItems);
+                if (shouldScrollToCurrentItem) {
+                    m_view->scrollToItem(selectedItems.first());
+                }
             }
         }
     }
@@ -1759,7 +1795,7 @@ void DolphinView::updateViewState()
 
                 // scroll to current item and reset the state
                 if (m_scrollToCurrentItem) {
-                    m_view->scrollToItem(currentIndex);
+                    m_view->scrollToItem(currentIndex, KItemListView::ViewItemPosition::Middle);
                     m_scrollToCurrentItem = false;
                 }
                 m_currentItemUrl = QUrl();
@@ -1823,7 +1859,7 @@ void DolphinView::slotTrashFileFinished(KJob *job)
         selectNextItem(); // Fixes BUG: 419914 via selecting next item
         Q_EMIT operationCompletedMessage(i18nc("@info:status", "Trash operation completed."));
     } else if (job->error() != KIO::ERR_USER_CANCELED) {
-        Q_EMIT errorMessage(job->errorString());
+        Q_EMIT errorMessage(job->errorString(), job->error());
     }
 }
 
@@ -1833,7 +1869,7 @@ void DolphinView::slotDeleteFileFinished(KJob *job)
         selectNextItem(); // Fixes BUG: 419914 via selecting next item
         Q_EMIT operationCompletedMessage(i18nc("@info:status", "Delete operation completed."));
     } else if (job->error() != KIO::ERR_USER_CANCELED) {
-        Q_EMIT errorMessage(job->errorString());
+        Q_EMIT errorMessage(job->errorString(), job->error());
     }
 }
 
@@ -1845,7 +1881,7 @@ void DolphinView::selectNextItem()
             Q_ASSERT_X(false, "DolphinView", "Selecting the next item failed.");
             return;
         }
-        const auto lastSelectedIndex = m_model->index(selectedItems().last());
+        const auto lastSelectedIndex = m_model->index(selectedItems().constLast());
         if (lastSelectedIndex < 0) {
             Q_ASSERT_X(false, "DolphinView", "Selecting the next item failed.");
             return;
@@ -1856,6 +1892,7 @@ void DolphinView::selectNextItem()
         }
         if (nextItem >= 0) {
             selectionManager->setSelected(nextItem, 1);
+            selectionManager->beginAnchoredSelection(nextItem);
         }
         m_selectNextItem = false;
     }
@@ -1863,15 +1900,18 @@ void DolphinView::selectNextItem()
 
 void DolphinView::slotRenamingResult(KJob *job)
 {
-    if (job->error()) {
+    // Change model data after renaming has succeeded. On failure we do nothing.
+    // If there is already an item with the newUrl, the copyjob will open a dialog for it, and
+    // KFileItemModel will update the data when the dir lister signals that the file name has changed.
+    if (!job->error()) {
         KIO::CopyJob *copyJob = qobject_cast<KIO::CopyJob *>(job);
         Q_ASSERT(copyJob);
         const QUrl newUrl = copyJob->destUrl();
+        const QUrl oldUrl = copyJob->srcUrls().at(0);
         const int index = m_model->index(newUrl);
-        if (index >= 0) {
+        if (m_model->index(oldUrl) == index) {
             QHash<QByteArray, QVariant> data;
-            const QUrl oldUrl = copyJob->srcUrls().at(0);
-            data.insert("text", oldUrl.fileName());
+            data.insert("text", newUrl.fileName());
             m_model->setData(index, data);
         }
     }
@@ -1905,6 +1945,7 @@ void DolphinView::slotDirectoryLoadingCompleted()
 
     Q_EMIT directoryLoadingCompleted();
 
+    applyDynamicView();
     updatePlaceholderLabel();
     updateWritableState();
 }
@@ -2006,25 +2047,14 @@ void DolphinView::slotRoleEditingFinished(int index, const QByteArray &role, con
             }
 #endif
 
-            const bool newNameExistsAlready = (m_model->index(newUrl) >= 0);
-            if (!newNameExistsAlready && m_model->index(oldUrl) == index) {
-                // Only change the data in the model if no item with the new name
-                // is in the model yet. If there is an item with the new name
-                // already, calling KIO::CopyJob will open a dialog
-                // asking for a new name, and KFileItemModel will update the
-                // data when the dir lister signals that the file name has changed.
-                QHash<QByteArray, QVariant> data;
-                data.insert(role, retVal.newName);
-                m_model->setData(index, data);
-            }
-
             KIO::Job *job = KIO::moveAs(oldUrl, newUrl);
             KJobWidgets::setWindow(job, this);
             KIO::FileUndoManager::self()->recordJob(KIO::FileUndoManager::Rename, {oldUrl}, newUrl, job);
             job->uiDelegate()->setAutoErrorHandlingEnabled(true);
 
-            if (!newNameExistsAlready) {
+            if (m_model->index(newUrl) < 0) {
                 forceUrlsSelection(newUrl, {newUrl});
+                updateSelectionState();
 
                 // Only connect the result signal if there is no item with the new name
                 // in the model yet, see bug 328262.
@@ -2045,9 +2075,9 @@ void DolphinView::loadDirectory(const QUrl &url, bool reload)
     if (!url.isValid()) {
         const QString location(url.toDisplayString(QUrl::PreferLocalFile));
         if (location.isEmpty()) {
-            Q_EMIT errorMessage(i18nc("@info:status", "The location is empty."));
+            Q_EMIT errorMessage(i18nc("@info:status", "The location is empty."), KIO::ERR_UNKNOWN);
         } else {
-            Q_EMIT errorMessage(i18nc("@info:status", "The location '%1' is invalid.", location));
+            Q_EMIT errorMessage(i18nc("@info:status", "The location '%1' is invalid.", location), KIO::ERR_UNKNOWN);
         }
         return;
     }
@@ -2160,7 +2190,7 @@ void DolphinView::applyViewProperties(const ViewProperties &props)
         } else {
             header->setAutomaticColumnResizing(true);
         }
-        header->setSidePadding(DetailsModeSettings::sidePadding());
+        header->setSidePadding(DetailsModeSettings::leftPadding(), DetailsModeSettings::rightPadding());
     }
 
     m_view->endTransaction();
@@ -2184,6 +2214,51 @@ void DolphinView::applyModeToView()
     }
 }
 
+void DolphinView::applyDynamicView()
+{
+    ViewProperties props(viewPropertiesUrl());
+    /* return early if:
+     * - dynamic view is not enabled
+     * - the current view mode is already Icon View
+     * - dynamic view has previously changed the view mode
+     */
+    if (!GeneralSettings::dynamicView() || m_mode == IconsView || props.dynamicViewPassed()) {
+        return;
+    }
+
+    uint imageAndVideoCount = 0;
+    uint checkedItems = 0;
+    const uint totalItems = itemsCount();
+    const KFileItemList itemList = items();
+    bool applyDynamicView = false;
+
+    for (const auto &file : itemList) {
+        ++checkedItems;
+        const QString type = file.mimetype().slice(0, 5);
+
+        if (type == "image" || type == "video") {
+            ++imageAndVideoCount;
+            // if 2/3 or more of the items are images/videos, dynamic view should be applied
+            applyDynamicView = imageAndVideoCount >= (totalItems * 2 / 3);
+            if (applyDynamicView) {
+                break;
+            }
+        } else if (checkedItems - imageAndVideoCount > totalItems / 3) {
+            // if more than a third of the checked files are not media files, return
+            return;
+        }
+    }
+
+    if (!applyDynamicView) {
+        return;
+    }
+
+    props.setAutoSaveEnabled(!GeneralSettings::globalViewProps());
+    props.setDynamicViewPassed(true);
+    props.setViewMode(IconsView);
+    applyViewProperties(props);
+}
+
 void DolphinView::pasteToUrl(const QUrl &url)
 {
     KIO::PasteJob *job = KIO::paste(QApplication::clipboard()->mimeData(), url);
@@ -2191,8 +2266,16 @@ void DolphinView::pasteToUrl(const QUrl &url)
     m_clearSelectionBeforeSelectingNewItems = true;
     m_markFirstNewlySelectedItemAsCurrent = true;
     m_selectJobCreatedItems = true;
-    // TODO KF6 use KIO::PasteJob::copyJobStarted to hook to earlier events copying/moving
     connect(job, &KIO::PasteJob::itemCreated, this, &DolphinView::slotItemCreated);
+    connect(job, &KIO::PasteJob::copyJobStarted, this, [this](const KIO::CopyJob *copyJob) {
+        connect(copyJob, &KIO::CopyJob::copying, this, &DolphinView::slotItemCreatedFromJob);
+        connect(copyJob, &KIO::CopyJob::moving, this, &DolphinView::slotItemCreatedFromJob);
+        connect(copyJob, &KIO::CopyJob::linking, this, [this](KIO::Job *job, const QString &src, const QUrl &dest) {
+            Q_UNUSED(job)
+            Q_UNUSED(src)
+            slotItemCreated(dest);
+        });
+    });
     connect(job, &KIO::PasteJob::result, this, &DolphinView::slotJobResult);
 }
 
@@ -2243,6 +2326,27 @@ void DolphinView::updateWritableState()
     }
 }
 
+bool DolphinView::isFolderWritable() const
+{
+    return m_isFolderWritable;
+}
+
+int DolphinView::horizontalScrollBarHeight() const
+{
+    if (m_container && m_container->horizontalScrollBar() && m_container->horizontalScrollBar()->isVisible()) {
+        return m_container->horizontalScrollBar()->height();
+    }
+    return 0;
+}
+
+void DolphinView::setStatusBarOffset(int offset)
+{
+    KItemListView *view = m_container->controller()->view();
+    if (view) {
+        view->setStatusBarOffset(offset);
+    }
+}
+
 QUrl DolphinView::viewPropertiesUrl() const
 {
     if (m_viewPropertiesContext.isEmpty()) {
@@ -2255,11 +2359,6 @@ QUrl DolphinView::viewPropertiesUrl() const
     return url;
 }
 
-void DolphinView::slotRenameDialogRenamingFinished(const QList<QUrl> &urls)
-{
-    forceUrlsSelection(urls.first(), urls);
-}
-
 void DolphinView::forceUrlsSelection(const QUrl &current, const QList<QUrl> &selected)
 {
     clearSelection();
@@ -2305,12 +2404,22 @@ void DolphinView::showLoadingPlaceholder()
 {
     m_placeholderLabel->setText(i18n("Loading…"));
     m_placeholderLabel->setVisible(true);
+#ifndef QT_NO_ACCESSIBILITY
+    if (QAccessible::isActive()) {
+        static_cast<KItemListViewAccessible *>(QAccessible::queryAccessibleInterface(m_view))->announceNewlyLoadedLocation(m_placeholderLabel->text());
+    }
+#endif
 }
 
 void DolphinView::updatePlaceholderLabel()
 {
     m_showLoadingPlaceholderTimer->stop();
     if (itemsCount() > 0) {
+#ifndef QT_NO_ACCESSIBILITY
+        if (QAccessible::isActive()) {
+            static_cast<KItemListViewAccessible *>(QAccessible::queryAccessibleInterface(m_view))->announceNewlyLoadedLocation(QString());
+        }
+#endif
         m_placeholderLabel->setVisible(false);
         return;
     }
@@ -2354,6 +2463,11 @@ void DolphinView::updatePlaceholderLabel()
     }
 
     m_placeholderLabel->setVisible(true);
+#ifndef QT_NO_ACCESSIBILITY
+    if (QAccessible::isActive()) {
+        static_cast<KItemListViewAccessible *>(QAccessible::queryAccessibleInterface(m_view))->announceNewlyLoadedLocation(m_placeholderLabel->text());
+    }
+#endif
 }
 
 bool DolphinView::tryShowNameToolTip(QHelpEvent *event)
@@ -2372,7 +2486,7 @@ bool DolphinView::tryShowNameToolTip(QHelpEvent *event)
             const KFileItem item = m_model->fileItem(index.value());
             const QString text = item.text();
             const QPoint pos = mapToGlobal(event->pos());
-            QToolTip::showText(pos, text);
+            QToolTip::showText(pos, text, this);
             return true;
         }
     }