]> 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 9666ca2ab38b1c6478da786dc04c63c1ff5be6fc..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"
@@ -35,7 +38,7 @@
 #include <KFileItemListProperties>
 #include <KFormat>
 #include <KIO/CopyJob>
-#include <KIO/DeleteJob>
+#include <KIO/DeleteOrTrashJob>
 #include <KIO/DropJob>
 #include <KIO/JobUiDelegate>
 #include <KIO/Paste>
 
 #include <kwidgetsaddons_version.h>
 
-#include <kio_version.h>
-#if KIO_VERSION >= QT_VERSION_CHECK(5, 100, 0)
-#include <KIO/DeleteOrTrashJob>
-#endif
-
 #include <QAbstractItemView>
+#ifndef QT_NO_ACCESSIBILITY
+#include <QAccessible>
+#endif
 #include <QActionGroup>
 #include <QApplication>
 #include <QClipboard>
@@ -92,6 +93,7 @@ DolphinView::DolphinView(const QUrl &url, QWidget *parent)
     , m_currentItemUrl()
     , m_scrollToCurrentItem(false)
     , m_restoredContentsPosition()
+    , m_controlWheelAccumulatedDelta(0)
     , m_selectedUrls()
     , m_clearSelectionBeforeSelectingNewItems(false)
     , m_markFirstNewlySelectedItemAsCurrent(false)
@@ -121,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,11 +132,14 @@ DolphinView::DolphinView(const QUrl &url, QWidget *parent)
 
     m_container = new KItemListContainer(controller, this);
     m_container->installEventFilter(this);
+#ifndef QT_NO_ACCESSIBILITY
+    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();
     });
 
@@ -147,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
@@ -164,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);
@@ -232,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);
@@ -268,7 +273,6 @@ void DolphinView::setActive(bool active)
     if (active) {
         m_container->setFocus();
         Q_EMIT activated();
-        Q_EMIT writeStateChanged(m_isFolderWritable);
     }
 }
 
@@ -280,6 +284,10 @@ bool DolphinView::isActive() const
 void DolphinView::setViewMode(Mode mode)
 {
     if (mode != m_mode) {
+        // Reset scrollbars before changing the view mode.
+        m_container->horizontalScrollBar()->setValue(0);
+        m_container->verticalScrollBar()->setValue(0);
+
         ViewProperties props(viewPropertiesUrl());
         props.setViewMode(mode);
 
@@ -306,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
@@ -372,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);
 }
@@ -468,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);
     }
 }
 
@@ -481,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);
     }
 }
 
@@ -553,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) {
@@ -623,7 +646,7 @@ void DolphinView::requestStatusBarText()
             return;
         }
 
-        m_statJobForStatusBarText = KIO::statDetails(m_model->rootItem().url(), KIO::StatJob::SourceSide, KIO::StatRecursiveSize, KIO::HideProgressInfo);
+        m_statJobForStatusBarText = KIO::stat(m_model->rootItem().url(), KIO::StatJob::SourceSide, KIO::StatRecursiveSize, KIO::HideProgressInfo);
         connect(m_statJobForStatusBarText, &KJob::result, this, &DolphinView::slotStatJobResult);
         m_statJobForStatusBarText->start();
     }
@@ -638,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) {
@@ -652,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);
 }
@@ -727,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();
     }
@@ -757,43 +797,22 @@ void DolphinView::trashSelectedItems()
 {
     const QList<QUrl> list = simplifiedSelectedUrls();
 
-#if KIO_VERSION >= QT_VERSION_CHECK(5, 100, 0)
     using Iface = KIO::AskUserActionInterface;
     auto *trashJob = new KIO::DeleteOrTrashJob(list, Iface::Trash, Iface::DefaultConfirmation, this);
     connect(trashJob, &KJob::result, this, &DolphinView::slotTrashFileFinished);
     m_selectNextItem = true;
     trashJob->start();
-#else
-    KIO::JobUiDelegate uiDelegate;
-    uiDelegate.setWindow(window());
-    if (uiDelegate.askDeleteConfirmation(list, KIO::JobUiDelegate::Trash, KIO::JobUiDelegate::DefaultConfirmation)) {
-        KIO::Job *job = KIO::trash(list);
-        KIO::FileUndoManager::self()->recordJob(KIO::FileUndoManager::Trash, list, QUrl(QStringLiteral("trash:/")), job);
-        KJobWidgets::setWindow(job, this);
-        connect(job, &KIO::Job::result, this, &DolphinView::slotTrashFileFinished);
-    }
-#endif
 }
 
 void DolphinView::deleteSelectedItems()
 {
     const QList<QUrl> list = simplifiedSelectedUrls();
 
-#if KIO_VERSION >= QT_VERSION_CHECK(5, 100, 0)
     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();
-#else
-    KIO::JobUiDelegate uiDelegate;
-    uiDelegate.setWindow(window());
-    if (uiDelegate.askDeleteConfirmation(list, KIO::JobUiDelegate::Delete, KIO::JobUiDelegate::DefaultConfirmation)) {
-        KIO::Job *job = KIO::del(list);
-        KJobWidgets::setWindow(job, this);
-        connect(job, &KIO::Job::result, this, &DolphinView::slotDeleteFileFinished);
-    }
-#endif
 }
 
 void DolphinView::cutSelectedItemsToClipboard()
@@ -824,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);
@@ -843,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);
@@ -871,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();
@@ -898,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()
@@ -1080,24 +1101,14 @@ void DolphinView::slotItemsActivated(const KItemSet &indexes)
 
     if (indexes.count() > 5) {
         QString question = i18np("Are you sure you want to open 1 item?", "Are you sure you want to open %1 items?", indexes.count());
-#if KWIDGETSADDONS_VERSION >= QT_VERSION_CHECK(5, 100, 0)
-        const int answer = KMessageBox::warningTwoActions(
+        const int answer = KMessageBox::warningContinueCancel(
             this,
             question,
             {},
-#else
-        const int answer =
-            KMessageBox::warningYesNo(this,
-                                      question,
-                                      {},
-#endif
             KGuiItem(i18ncp("@action:button", "Open %1 Item", "Open %1 Items", indexes.count()), QStringLiteral("document-open")),
-            KStandardGuiItem::cancel());
-#if KWIDGETSADDONS_VERSION >= QT_VERSION_CHECK(5, 100, 0)
-        if (answer != KMessageBox::PrimaryAction) {
-#else
-        if (answer != KMessageBox::Yes) {
-#endif
+            KStandardGuiItem::cancel(),
+            QStringLiteral("ConfirmOpenManyFolders"));
+        if (answer != KMessageBox::PrimaryAction && answer != KMessageBox::Continue) {
             return;
         }
     }
@@ -1133,7 +1144,7 @@ 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();
     if (!url.isEmpty()) {
         // keep in sync with KUrlNavigator::slotNavigatorButtonClicked
@@ -1149,6 +1160,8 @@ void DolphinView::slotItemMiddleClicked(int index)
         } else {
             Q_EMIT tabRequested(item.url());
         }
+    } else {
+        Q_EMIT fileMiddleClickActivated(item);
     }
 }
 
@@ -1159,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());
@@ -1173,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();
@@ -1224,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);
@@ -1257,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();
@@ -1310,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();
 }
 
@@ -1437,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) {
@@ -1448,10 +1474,20 @@ void DolphinView::slotItemCreated(const QUrl &url)
     }
 }
 
+void DolphinView::onDirectoryLoadingCompletedAfterJob()
+{
+    // the model should now contain all the items created by the job
+    m_selectJobCreatedItems = true; // to make sure we overwrite selection
+    // update the view: scroll into View and selection
+    updateViewState();
+    m_selectJobCreatedItems = false;
+    m_selectedUrls.clear();
+}
+
 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();
@@ -1463,21 +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
-            // TODO KF6 replace with Qt::singleShotConnection
-            QMetaObject::Connection *const connection = new QMetaObject::Connection;
-            *connection = connect(
-                m_model,
-                &KFileItemModel::directoryLoadingCompleted,
-                this,
-                [this, connection]() {
-                    // the model should now contain all the items created by the job
-                    updateSelectionState();
-                    m_selectJobCreatedItems = false;
-                    m_selectedUrls.clear();
-                    QObject::disconnect(*connection);
-                    delete connection;
-                },
-                Qt::UniqueConnection);
+            connect(m_model, &KFileItemModel::directoryLoadingCompleted, this, &DolphinView::onDirectoryLoadingCompletedAfterJob, Qt::SingleShotConnection);
         } else {
             m_selectJobCreatedItems = false;
             m_selectedUrls.clear();
@@ -1534,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());
@@ -1748,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) {
@@ -1772,6 +1774,9 @@ void DolphinView::updateSelectionState()
             if (!selectedItems.isEmpty()) {
                 selectionManager->beginAnchoredSelection(selectionManager->currentItem());
                 selectionManager->setSelectedItems(selectedItems);
+                if (shouldScrollToCurrentItem) {
+                    m_view->scrollToItem(selectedItems.first());
+                }
             }
         }
     }
@@ -1790,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();
@@ -1854,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());
     }
 }
 
@@ -1864,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());
     }
 }
 
@@ -1876,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;
@@ -1887,6 +1892,7 @@ void DolphinView::selectNextItem()
         }
         if (nextItem >= 0) {
             selectionManager->setSelected(nextItem, 1);
+            selectionManager->beginAnchoredSelection(nextItem);
         }
         m_selectNextItem = false;
     }
@@ -1894,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);
         }
     }
@@ -1936,6 +1945,7 @@ void DolphinView::slotDirectoryLoadingCompleted()
 
     Q_EMIT directoryLoadingCompleted();
 
+    applyDynamicView();
     updatePlaceholderLabel();
     updateWritableState();
 }
@@ -2020,13 +2030,8 @@ void DolphinView::slotRoleEditingFinished(int index, const QByteArray &role, con
             if (!hiddenFilesShown() && newName.startsWith(QLatin1Char('.')) && !oldItem.name().startsWith(QLatin1Char('.'))) {
                 KGuiItem yesGuiItem(i18nc("@action:button", "Rename and Hide"), QStringLiteral("view-hidden"));
 
-#if KWIDGETSADDONS_VERSION >= QT_VERSION_CHECK(5, 100, 0)
                 const auto code =
                     KMessageBox::questionTwoActions(this,
-#else
-                const auto code =
-                    KMessageBox::questionYesNo(this,
-#endif
                                                     oldItem.isFile() ? i18n("Adding a dot to the beginning of this file's name will hide it from view.\n"
                                                                             "Do you still want to rename it?")
                                                                      : i18n("Adding a dot to the beginning of this folder's name will hide it from view.\n"
@@ -2036,36 +2041,21 @@ void DolphinView::slotRoleEditingFinished(int index, const QByteArray &role, con
                                                     KStandardGuiItem::cancel(),
                                                     QStringLiteral("ConfirmHide"));
 
-#if KWIDGETSADDONS_VERSION >= QT_VERSION_CHECK(5, 100, 0)
                 if (code == KMessageBox::SecondaryAction) {
-#else
-                if (code == KMessageBox::No) {
-#endif
                     return;
                 }
             }
 #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);
 
-            forceUrlsSelection(newUrl, {newUrl});
+            if (m_model->index(newUrl) < 0) {
+                forceUrlsSelection(newUrl, {newUrl});
+                updateSelectionState();
 
-            if (!newNameExistsAlready) {
                 // Only connect the result signal if there is no item with the new name
                 // in the model yet, see bug 328262.
                 connect(job, &KJob::result, this, &DolphinView::slotRenamingResult);
@@ -2085,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;
     }
@@ -2200,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();
@@ -2224,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);
@@ -2231,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);
 }
 
@@ -2283,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()) {
@@ -2295,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();
@@ -2323,7 +2382,7 @@ void DolphinView::copyPathToClipboard()
     if (clipboard == nullptr) {
         return;
     }
-    clipboard->setText(path);
+    clipboard->setText(QDir::toNativeSeparators(path));
 }
 
 void DolphinView::slotIncreaseZoom()
@@ -2345,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;
     }
@@ -2394,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)
@@ -2412,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;
         }
     }