]> cloud.milkyroute.net Git - dolphin.git/blobdiff - src/dolphinmainwindow.cpp
DolphinMainWindow: Notify current selection changed when changing tabs
[dolphin.git] / src / dolphinmainwindow.cpp
index 49e656ce72bba5301bef3b3affda0b554d0692c1..51772eac28b1abb7897abe3d58387f32f9298472 100644 (file)
@@ -8,6 +8,7 @@
 
 #include "dolphinmainwindow.h"
 
+#include "admin/workerintegration.h"
 #include "dolphin_generalsettings.h"
 #include "dolphinbookmarkhandler.h"
 #include "dolphincontextmenu.h"
 #include "panels/folders/folderspanel.h"
 #include "panels/places/placespanel.h"
 #include "panels/terminal/terminalpanel.h"
+#include "search/dolphinquery.h"
 #include "selectionmode/actiontexthelper.h"
 #include "settings/dolphinsettingsdialog.h"
+#include "statusbar/diskspaceusagemenu.h"
 #include "statusbar/dolphinstatusbar.h"
 #include "views/dolphinnewfilemenuobserver.h"
 #include "views/dolphinremoteencoding.h"
 #include <KActionCollection>
 #include <KActionMenu>
 #include <KAuthorized>
+#include <KColorSchemeManager>
 #include <KConfig>
 #include <KConfigGui>
+#include <KDesktopFile>
+#include <KDialogJobUiDelegate>
 #include <KDualAction>
 #include <KFileItemListProperties>
 #include <KIO/CommandLauncherJob>
@@ -50,6 +56,8 @@
 #include <KMessageBox>
 #include <KProtocolInfo>
 #include <KProtocolManager>
+#include <KRecentFilesAction>
+#include <KRuntimePlatform>
 #include <KShell>
 #include <KShortcutsDialog>
 #include <KStandardAction>
@@ -80,6 +88,7 @@
 #include <QTimer>
 #include <QToolButton>
 #include <QtConcurrentRun>
+#include <dolphindebug.h>
 
 #include <algorithm>
 
@@ -96,7 +105,7 @@ namespace
 const int CurrentDolphinVersion = 202;
 // The maximum number of entries in the back/forward popup menu
 const int MaxNumberOfNavigationentries = 12;
-// The maximum number of "Activate Tab" shortcuts
+// The maximum number of "Go to Tab" shortcuts
 const int MaxActivateTabShortcuts = 9;
 }
 
@@ -109,6 +118,7 @@ DolphinMainWindow::DolphinMainWindow()
     , m_remoteEncoding(nullptr)
     , m_settingsDialog()
     , m_bookmarkHandler(nullptr)
+    , m_disabledActionNotifier(nullptr)
     , m_lastHandleUrlOpenJob(nullptr)
     , m_terminalPanel(nullptr)
     , m_placesPanel(nullptr)
@@ -117,6 +127,7 @@ DolphinMainWindow::DolphinMainWindow()
     , m_forwardAction(nullptr)
     , m_splitViewAction(nullptr)
     , m_splitViewMenuAction(nullptr)
+    , m_diskSpaceUsageMenu(nullptr)
     , m_sessionSaveTimer(nullptr)
     , m_sessionSaveWatcher(nullptr)
     , m_sessionSaveScheduled(false)
@@ -133,6 +144,10 @@ DolphinMainWindow::DolphinMainWindow()
 
     setStateConfigGroup("State");
 
+#if defined(Q_OS_WIN) || defined(Q_OS_MACOS)
+    new KColorSchemeManager(this); // Sets a sensible color scheme which fixes unreadable icons and text on Windows.
+#endif
+
     connect(&DolphinNewFileMenuObserver::instance(), &DolphinNewFileMenuObserver::errorMessage, this, &DolphinMainWindow::showErrorMessage);
 
     KIO::FileUndoManager *undoManager = KIO::FileUndoManager::self();
@@ -165,17 +180,29 @@ DolphinMainWindow::DolphinMainWindow()
     m_actionHandler = new DolphinViewActionHandler(actionCollection(), m_actionTextHelper, this);
     connect(m_actionHandler, &DolphinViewActionHandler::actionBeingHandled, this, &DolphinMainWindow::clearStatusBar);
     connect(m_actionHandler, &DolphinViewActionHandler::createDirectoryTriggered, this, &DolphinMainWindow::createDirectory);
+    connect(m_actionHandler, &DolphinViewActionHandler::createFileTriggered, this, &DolphinMainWindow::createFile);
     connect(m_actionHandler, &DolphinViewActionHandler::selectionModeChangeTriggered, this, &DolphinMainWindow::slotSetSelectionMode);
 
-    Q_CHECK_PTR(actionCollection()->action(QStringLiteral("create_dir")));
-    m_newFileMenu->setNewFolderShortcutAction(actionCollection()->action(QStringLiteral("create_dir")));
+    QAction *newDirAction = actionCollection()->action(QStringLiteral("create_dir"));
+    Q_CHECK_PTR(newDirAction);
+    m_newFileMenu->setNewFolderShortcutAction(newDirAction);
+
+    QAction *newFileAction = actionCollection()->action(QStringLiteral("create_file"));
+    Q_CHECK_PTR(newFileAction);
+    m_newFileMenu->setNewFileShortcutAction(newFileAction);
 
     m_remoteEncoding = new DolphinRemoteEncoding(this, m_actionHandler);
     connect(this, &DolphinMainWindow::urlChanged, m_remoteEncoding, &DolphinRemoteEncoding::slotAboutToOpenUrl);
 
+    m_disabledActionNotifier = new DisabledActionNotifier(this);
+    connect(m_disabledActionNotifier, &DisabledActionNotifier::disabledActionTriggered, this, [this](const QAction *, QString reason) {
+        m_activeViewContainer->showMessage(reason, KMessageWidget::Warning);
+    });
+
     setupDockWidgets();
 
-    setupGUI(Save | Create | ToolBar);
+    const bool usePhoneUi{KRuntimePlatform::runtimePlatform().contains(QLatin1String("phone"))};
+    setupGUI(Save | Create | ToolBar, usePhoneUi ? QStringLiteral("dolphinuiforphones.rc") : QString() /* load the default dolphinui.rc file */);
     stateChanged(QStringLiteral("new_file"));
 
     QClipboard *clipboard = QApplication::clipboard();
@@ -186,6 +213,15 @@ DolphinMainWindow::DolphinMainWindow()
 
     if (firstRun) {
         menuBar()->setVisible(false);
+
+        if (usePhoneUi) {
+            Q_ASSERT(qobject_cast<QDockWidget *>(m_placesPanel->parent()));
+            m_placesPanel->parentWidget()->hide();
+            auto settings = GeneralSettings::self();
+            settings->setShowZoomSlider(false); // Zooming can be done with pinch gestures instead and we are short on horizontal space.
+            settings->setRenameInline(false); // This works around inline renaming currently not working well with virtual keyboards.
+            settings->save(); // Otherwise the RenameInline setting is not picked up for the first time Dolphin is used.
+        }
     }
 
     const bool showMenu = !menuBar()->isHidden();
@@ -202,6 +238,7 @@ DolphinMainWindow::DolphinMainWindow()
     }
 
     updateAllowedToolbarAreas();
+    updateNavigatorsBackground();
 
     // enable middle-click on back/forward/up to open in a new tab
     auto *middleClickEventFilter = new MiddleClickActionEventFilter(this);
@@ -226,6 +263,13 @@ DolphinMainWindow::~DolphinMainWindow()
 {
     // This fixes a crash on Wayland when closing the mainwindow while another dialog is open.
     disconnect(QGuiApplication::clipboard(), &QClipboard::dataChanged, this, &DolphinMainWindow::updatePasteAction);
+
+    // This fixes a crash in dolphinmainwindowtest where the connection below fires even though the KMainWindow destructor of this object is already running.
+    Q_CHECK_PTR(qobject_cast<DolphinDockWidget *>(m_placesPanel->parent()));
+    disconnect(static_cast<DolphinDockWidget *>(m_placesPanel->parent()),
+               &DolphinDockWidget::visibilityChanged,
+               this,
+               &DolphinMainWindow::slotPlacesPanelVisibilityChanged);
 }
 
 QVector<DolphinViewContainer *> DolphinMainWindow::viewContainers() const
@@ -350,6 +394,10 @@ void DolphinMainWindow::changeUrl(const QUrl &url)
     updatePasteAction();
     updateViewActions();
     updateGoActions();
+    m_diskSpaceUsageMenu->setUrl(url);
+
+    // will signal used urls to activities manager, too
+    m_recentFiles->addUrl(url, QString(), "inode/directory");
 
     Q_EMIT urlChanged(url);
 }
@@ -361,9 +409,9 @@ void DolphinMainWindow::slotTerminalDirectoryChanged(const QUrl &url)
         m_tearDownFromPlacesRequested = false;
     }
 
-    m_activeViewContainer->setAutoGrabFocus(false);
+    m_activeViewContainer->setGrabFocusOnUrlChange(false);
     changeUrl(url);
-    m_activeViewContainer->setAutoGrabFocus(true);
+    m_activeViewContainer->setGrabFocusOnUrlChange(true);
 }
 
 void DolphinMainWindow::slotEditableStateChanged(bool editable)
@@ -444,7 +492,7 @@ void DolphinMainWindow::addToPlaces()
     }
     if (url.isValid()) {
         QString icon;
-        if (m_activeViewContainer->isSearchModeEnabled()) {
+        if (isSearchUrl(url)) {
             icon = QStringLiteral("folder-saved-search-symbolic");
         } else {
             icon = KIO::iconNameForUrl(url);
@@ -453,9 +501,9 @@ void DolphinMainWindow::addToPlaces()
     }
 }
 
-void DolphinMainWindow::openNewTab(const QUrl &url)
+DolphinTabPage *DolphinMainWindow::openNewTab(const QUrl &url)
 {
-    m_tabWidget->openNewTab(url, QUrl());
+    return m_tabWidget->openNewTab(url, QUrl());
 }
 
 void DolphinMainWindow::openNewTabAndActivate(const QUrl &url)
@@ -548,7 +596,7 @@ void DolphinMainWindow::showTarget()
         KIO::StatJob *statJob = static_cast<KIO::StatJob *>(job);
 
         if (statJob->error()) {
-            m_activeViewContainer->showMessage(job->errorString(), DolphinViewContainer::Error);
+            m_activeViewContainer->showMessage(job->errorString(), KMessageWidget::Error);
         } else {
             KIO::highlightInFileManager({destinationUrl});
         }
@@ -572,7 +620,7 @@ void DolphinMainWindow::showEvent(QShowEvent *event)
 {
     KXmlGuiWindow::showEvent(event);
 
-    if (!event->spontaneous()) {
+    if (!event->spontaneous() && m_activeViewContainer) {
         m_activeViewContainer->view()->setFocus();
     }
 }
@@ -715,6 +763,8 @@ void DolphinMainWindow::slotSaveSession()
         KConfig *config = KConfigGui::sessionConfig();
         saveGlobalProperties(config);
         savePropertiesInternal(config, 1);
+        KConfigGroup group = config->group(QStringLiteral("Number"));
+        group.writeEntry("NumberOfWindows", 1); // Makes session restore aware that there is a window to restore.
 
         auto future = QtConcurrent::run([config]() {
             config->sync();
@@ -770,8 +820,23 @@ void DolphinMainWindow::updateNewMenu()
 
 void DolphinMainWindow::createDirectory()
 {
-    m_newFileMenu->setWorkingDirectory(activeViewContainer()->url());
-    m_newFileMenu->createDirectory();
+    // When creating directory, namejob is being run. In network folders,
+    // this job can take long time, so instead of starting multiple namejobs,
+    // just check if we are already running one. This prevents opening multiple
+    // dialogs. BUG:481401
+    if (!m_newFileMenu->isCreateDirectoryRunning()) {
+        m_newFileMenu->setWorkingDirectory(activeViewContainer()->url());
+        m_newFileMenu->createDirectory();
+    }
+}
+
+void DolphinMainWindow::createFile()
+{
+    // Use the same logic as in createDirectory()
+    if (!m_newFileMenu->isCreateFileRunning()) {
+        m_newFileMenu->setWorkingDirectory(activeViewContainer()->url());
+        m_newFileMenu->createFile();
+    }
 }
 
 void DolphinMainWindow::quit()
@@ -781,7 +846,7 @@ void DolphinMainWindow::quit()
 
 void DolphinMainWindow::showErrorMessage(const QString &message)
 {
-    m_activeViewContainer->showMessage(message, DolphinViewContainer::Error);
+    m_activeViewContainer->showMessage(message, KMessageWidget::Error);
 }
 
 void DolphinMainWindow::slotUndoAvailable(bool available)
@@ -834,13 +899,14 @@ void DolphinMainWindow::paste()
 
 void DolphinMainWindow::find()
 {
-    m_activeViewContainer->setSearchModeEnabled(true);
+    m_activeViewContainer->setSearchBarVisible(true);
+    m_activeViewContainer->setFocusToSearchBar();
 }
 
 void DolphinMainWindow::updateSearchAction()
 {
     QAction *toggleSearchAction = actionCollection()->action(QStringLiteral("toggle_search"));
-    toggleSearchAction->setChecked(m_activeViewContainer->isSearchModeEnabled());
+    toggleSearchAction->setChecked(m_activeViewContainer->isSearchBarVisible());
 }
 
 void DolphinMainWindow::updatePasteAction()
@@ -848,6 +914,10 @@ void DolphinMainWindow::updatePasteAction()
     QAction *pasteAction = actionCollection()->action(KStandardAction::name(KStandardAction::Paste));
     QPair<bool, QString> pasteInfo = m_activeViewContainer->view()->pasteInfo();
     pasteAction->setEnabled(pasteInfo.first);
+    m_disabledActionNotifier->setDisabledReason(pasteAction,
+                                                m_activeViewContainer->rootItem().isWritable()
+                                                    ? i18nc("@info", "Cannot paste: The clipboard is empty.")
+                                                    : i18nc("@info", "Cannot paste: You do not have permission to write into this folder."));
     pasteAction->setText(pasteInfo.second);
 }
 
@@ -873,9 +943,13 @@ QAction *DolphinMainWindow::urlNavigatorHistoryAction(const KUrlNavigator *urlNa
 {
     const QUrl url = urlNavigator->locationUrl(historyIndex);
 
-    QString text = url.toDisplayString(QUrl::PreferLocalFile);
+    QString text;
 
-    if (!urlNavigator->showFullPath()) {
+    if (isSearchUrl(url)) {
+        text = Search::DolphinQuery(url, QUrl{}).title();
+    } else if (urlNavigator->showFullPath()) {
+        text = url.toDisplayString(QUrl::PreferLocalFile);
+    } else {
         const KFilePlacesModel *placesModel = DolphinPlacesModelSingleton::instance().placesModel();
 
         const QModelIndex closestIdx = placesModel->closestItem(url);
@@ -935,6 +1009,7 @@ void DolphinMainWindow::slotAboutToShowForwardPopupMenu()
     const KUrlNavigator *urlNavigator = m_activeViewContainer->urlNavigatorInternalWithHistory();
     int entries = 0;
     QMenu *menu = m_forwardAction->popupMenu();
+    menu->clear();
     for (int i = urlNavigator->historyIndex() - 1; i >= 0 && entries < MaxNumberOfNavigationentries; --i, ++entries) {
         QAction *action = urlNavigatorHistoryAction(urlNavigator, i, menu);
         menu->addAction(action);
@@ -980,8 +1055,15 @@ void DolphinMainWindow::invertSelection()
 
 void DolphinMainWindow::toggleSplitView()
 {
+    QUrl newSplitViewUrl;
+    const KFileItemList list = m_activeViewContainer->view()->selectedItems();
+    if (list.count() == 1) {
+        const KFileItem &item = list.first();
+        newSplitViewUrl = DolphinView::openItemAsFolderUrl(item);
+    }
+
     DolphinTabPage *tabPage = m_tabWidget->currentTabPage();
-    tabPage->setSplitViewEnabled(!tabPage->splitViewEnabled(), WithAnimation);
+    tabPage->setSplitViewEnabled(!tabPage->splitViewEnabled(), WithAnimation, newSplitViewUrl);
     m_tabWidget->updateTabName(m_tabWidget->indexOf(tabPage));
     updateViewActions();
 }
@@ -1083,8 +1165,10 @@ void DolphinMainWindow::replaceLocation()
 
     // If the text field currently has focus and everything is selected,
     // pressing the keyboard shortcut returns the whole thing to breadcrumb mode
+    // and goes back to the view, just like how it was before this action was triggered the first time.
     if (navigator->isUrlEditable() && lineEdit->hasFocus() && lineEdit->selectedText() == lineEdit->text()) {
         navigator->setUrlEditable(false);
+        m_activeViewContainer->view()->setFocus();
     } else {
         navigator->setUrlEditable(true);
         navigator->setFocus();
@@ -1108,11 +1192,21 @@ void DolphinMainWindow::togglePanelLockState()
     GeneralSettings::setLockPanels(newLockState);
 }
 
-void DolphinMainWindow::slotTerminalPanelVisibilityChanged()
+void DolphinMainWindow::slotTerminalPanelVisibilityChanged(bool visible)
 {
-    if (m_terminalPanel->isHiddenInVisibleWindow() && m_activeViewContainer) {
+    if (!visible && m_activeViewContainer) {
         m_activeViewContainer->view()->setFocus();
     }
+    // Putting focus to the Terminal is not handled here but in TerminalPanel::showEvent().
+}
+
+void DolphinMainWindow::slotPlacesPanelVisibilityChanged(bool visible)
+{
+    if (!visible && m_activeViewContainer) {
+        m_activeViewContainer->view()->setFocus();
+        return;
+    }
+    m_placesPanel->setFocus();
 }
 
 void DolphinMainWindow::goBack()
@@ -1378,6 +1472,13 @@ void DolphinMainWindow::slotWriteStateChanged(bool isFolderWritable)
     // trash:/ is writable but we don't want to create new items in it.
     // TODO: remove the trash check once https://phabricator.kde.org/T8234 is implemented
     newFileMenu()->setEnabled(isFolderWritable && m_activeViewContainer->url().scheme() != QLatin1String("trash"));
+    // When the menu is disabled, actions in it are disabled later in the event loop, and we need to set the disabled reason after that.
+    QTimer::singleShot(0, this, [this]() {
+        m_disabledActionNotifier->setDisabledReason(actionCollection()->action(QStringLiteral("create_file")),
+                                                    i18nc("@info", "Cannot create new file: You do not have permission to create items in this folder."));
+        m_disabledActionNotifier->setDisabledReason(actionCollection()->action(QStringLiteral("create_dir")),
+                                                    i18nc("@info", "Cannot create new folder: You do not have permission to create items in this folder."));
+    });
 }
 
 void DolphinMainWindow::openContextMenu(const QPoint &pos, const KFileItem &item, const KFileItemList &selectedItems, const QUrl &url)
@@ -1410,7 +1511,7 @@ void DolphinMainWindow::updateHamburgerMenu()
         menu = new QMenu(this);
         hamburgerMenu->setMenu(menu);
         hamburgerMenu->hideActionsOf(ac->action(QStringLiteral("basic_actions"))->menu());
-        hamburgerMenu->hideActionsOf(ac->action(QStringLiteral("zoom"))->menu());
+        hamburgerMenu->hideActionsOf(qobject_cast<KToolBarPopupAction *>(ac->action(QStringLiteral("zoom")))->popupMenu());
     } else {
         menu->clear();
     }
@@ -1461,15 +1562,18 @@ void DolphinMainWindow::updateHamburgerMenu()
     // The third group contains actions to change what one sees in the view
     // and to change the more general UI.
     if (!toolBar()->isVisible()
-        || (!toolbarActions.contains(ac->action(QStringLiteral("icons"))) && !toolbarActions.contains(ac->action(QStringLiteral("compact")))
-            && !toolbarActions.contains(ac->action(QStringLiteral("details"))) && !toolbarActions.contains(ac->action(QStringLiteral("view_mode"))))) {
+        || ((!toolbarActions.contains(ac->action(QStringLiteral("icons"))) && !toolbarActions.contains(ac->action(QStringLiteral("compact")))
+             && !toolbarActions.contains(ac->action(QStringLiteral("details"))) && !toolbarActions.contains(ac->action(QStringLiteral("view_mode"))))
+            && !toolbarActions.contains(ac->action(QStringLiteral("view_settings"))))) {
         menu->addAction(ac->action(QStringLiteral("view_mode")));
     }
-    menu->addAction(ac->action(QStringLiteral("show_hidden_files")));
-    menu->addAction(ac->action(QStringLiteral("sort")));
-    menu->addAction(ac->action(QStringLiteral("additional_info")));
-    if (!GeneralSettings::showStatusBar() || !GeneralSettings::showZoomSlider()) {
-        menu->addAction(ac->action(QStringLiteral("zoom")));
+    if (!toolBar()->isVisible() || !toolbarActions.contains(ac->action(QStringLiteral("view_settings")))) {
+        menu->addAction(ac->action(QStringLiteral("show_hidden_files")));
+        menu->addAction(ac->action(QStringLiteral("sort")));
+        menu->addAction(ac->action(QStringLiteral("additional_info")));
+        if (!GeneralSettings::showStatusBar() || !GeneralSettings::showZoomSlider()) {
+            menu->addAction(ac->action(QStringLiteral("zoom")));
+        }
     }
     menu->addAction(ac->action(QStringLiteral("panels")));
 
@@ -1493,6 +1597,8 @@ void DolphinMainWindow::slotPlaceActivated(const QUrl &url)
         // We can end up here if the user clicked a device in the Places Panel
         // which had been unmounted earlier, see https://bugs.kde.org/show_bug.cgi?id=161385.
         reloadView();
+
+        m_activeViewContainer->view()->setFocus(); // We always want the focus on the view after activating a place.
     } else {
         view->disableUrlNavigatorSelectionRequests();
         changeUrl(url);
@@ -1513,9 +1619,6 @@ void DolphinMainWindow::activeViewChanged(DolphinViewContainer *viewContainer)
     m_activeViewContainer = viewContainer;
 
     if (oldViewContainer) {
-        const QAction *toggleSearchAction = actionCollection()->action(QStringLiteral("toggle_search"));
-        toggleSearchAction->disconnect(oldViewContainer);
-
         // Disconnect all signals between the old view container (container,
         // view and url navigator) and main window.
         oldViewContainer->disconnect(this);
@@ -1526,6 +1629,7 @@ void DolphinMainWindow::activeViewChanged(DolphinViewContainer *viewContainer)
         if (auto secondaryUrlNavigator = navigators->secondaryUrlNavigator()) {
             secondaryUrlNavigator->disconnect(this);
         }
+        oldViewContainer->disconnect(m_diskSpaceUsageMenu);
 
         // except the requestItemInfo so that on hover the information panel can still be updated
         connect(oldViewContainer->view(), &DolphinView::requestItemInfo, this, &DolphinMainWindow::requestItemInfo);
@@ -1547,9 +1651,17 @@ void DolphinMainWindow::activeViewChanged(DolphinViewContainer *viewContainer)
     updateViewActions();
     updateGoActions();
     updateSearchAction();
+    connect(m_diskSpaceUsageMenu,
+            &DiskSpaceUsageMenu::showMessage,
+            viewContainer,
+            [viewContainer](const QString &message, KMessageWidget::MessageType messageType) {
+                viewContainer->showMessage(message, messageType);
+            });
+    connect(m_diskSpaceUsageMenu, &DiskSpaceUsageMenu::showInstallationProgress, viewContainer, &DolphinViewContainer::showProgress);
 
     const QUrl url = viewContainer->url();
     Q_EMIT urlChanged(url);
+    Q_EMIT selectionChanged(m_activeViewContainer->view()->selectedItems());
 }
 
 void DolphinMainWindow::tabCountChanged(int count)
@@ -1615,7 +1727,11 @@ void DolphinMainWindow::setViewsToHomeIfMountPathOpen(const QString &mountPath)
 {
     const QVector<DolphinViewContainer *> theViewContainers = viewContainers();
     for (DolphinViewContainer *viewContainer : theViewContainers) {
-        if (viewContainer && viewContainer->url().toLocalFile().startsWith(mountPath)) {
+        if (!viewContainer) {
+            continue;
+        }
+        const auto viewPath = viewContainer->url().toLocalFile();
+        if (viewPath.startsWith(mountPath + QLatin1String("/")) || viewPath == mountPath) {
             viewContainer->setUrl(QUrl::fromLocalFile(QDir::homePath()));
         }
     }
@@ -1627,7 +1743,7 @@ void DolphinMainWindow::setupActions()
     auto hamburgerMenuAction = KStandardAction::hamburgerMenu(nullptr, nullptr, actionCollection());
 
     // setup 'File' menu
-    m_newFileMenu = new DolphinNewFileMenu(nullptr, this);
+    m_newFileMenu = new DolphinNewFileMenu(nullptr, nullptr, this);
     actionCollection()->addAction(QStringLiteral("new_menu"), m_newFileMenu);
     QMenu *menu = m_newFileMenu->menu();
     menu->setTitle(i18nc("@title:menu Create new folder, file, link, etc.", "Create New"));
@@ -1640,7 +1756,7 @@ void DolphinMainWindow::setupActions()
     newWindow->setToolTip(i18nc("@info", "Open a new Dolphin window"));
     newWindow->setWhatsThis(xi18nc("@info:whatsthis",
                                    "This opens a new "
-                                   "window just like this one with the current location and view."
+                                   "window just like this one with the current location."
                                    "<nl/>You can drag and drop items between windows."));
     newWindow->setIcon(QIcon::fromTheme(QStringLiteral("window-new")));
 
@@ -1649,8 +1765,8 @@ void DolphinMainWindow::setupActions()
     newTab->setText(i18nc("@action:inmenu File", "New Tab"));
     newTab->setWhatsThis(xi18nc("@info:whatsthis",
                                 "This opens a new "
-                                "<emphasis>Tab</emphasis> with the current location and view.<nl/>"
-                                "A tab is an additional view within this window. "
+                                "<emphasis>Tab</emphasis> with the current location."
+                                "<nl/>Tabs allow you to quickly switch between multiple locations and views within this window. "
                                 "You can drag and drop items between tabs."));
     actionCollection()->setDefaultShortcut(newTab, Qt::CTRL | Qt::Key_T);
     connect(newTab, &QAction::triggered, this, &DolphinMainWindow::openNewActivatedTab);
@@ -1665,10 +1781,11 @@ void DolphinMainWindow::setupActions()
 
     QAction *closeTab = KStandardAction::close(m_tabWidget, QOverload<>::of(&DolphinTabWidget::closeTab), actionCollection());
     closeTab->setText(i18nc("@action:inmenu File", "Close Tab"));
+    closeTab->setToolTip(i18nc("@info", "Close Tab"));
     closeTab->setWhatsThis(i18nc("@info:whatsthis",
                                  "This closes the "
-                                 "currently viewed tab. If no more tabs are left this window "
-                                 "will close instead."));
+                                 "currently viewed tab. If no more tabs are left, this closes "
+                                 "the whole window instead."));
 
     QAction *quitAction = KStandardAction::quit(this, &DolphinMainWindow::quit, actionCollection());
     quitAction->setWhatsThis(i18nc("@info:whatsthis quit", "This closes this window."));
@@ -1719,9 +1836,10 @@ void DolphinMainWindow::setupActions()
     m_actionTextHelper->registerTextWhenNothingIsSelected(copyToOtherViewAction, i18nc("@action:inmenu", "Copy to Other View…"));
     copyToOtherViewAction->setWhatsThis(xi18nc("@info:whatsthis Copy",
                                                "This copies the selected items from "
-                                               "the <emphasis>active</emphasis> view to the inactive split view."));
+                                               "the view in focus to the other view. "
+                                               "(Only available while in Split View mode.)"));
     copyToOtherViewAction->setIcon(QIcon::fromTheme(QStringLiteral("edit-copy")));
-    copyToOtherViewAction->setIconText(i18nc("@action:inmenu Edit", "Copy to Inactive Split View"));
+    copyToOtherViewAction->setIconText(i18nc("@action:inmenu Edit", "Copy to Other View"));
     actionCollection()->setDefaultShortcut(copyToOtherViewAction, Qt::SHIFT | Qt::Key_F5);
     connect(copyToOtherViewAction, &QAction::triggered, this, &DolphinMainWindow::copyToInactiveSplitView);
 
@@ -1730,9 +1848,10 @@ void DolphinMainWindow::setupActions()
     m_actionTextHelper->registerTextWhenNothingIsSelected(moveToOtherViewAction, i18nc("@action:inmenu", "Move to Other View…"));
     moveToOtherViewAction->setWhatsThis(xi18nc("@info:whatsthis Move",
                                                "This moves the selected items from "
-                                               "the <emphasis>active</emphasis> view to the inactive split view."));
+                                               "the view in focus to the other view. "
+                                               "(Only available while in Split View mode.)"));
     moveToOtherViewAction->setIcon(QIcon::fromTheme(QStringLiteral("edit-cut")));
-    moveToOtherViewAction->setIconText(i18nc("@action:inmenu Edit", "Move to Inactive Split View"));
+    moveToOtherViewAction->setIconText(i18nc("@action:inmenu Edit", "Move to Other View"));
     actionCollection()->setDefaultShortcut(moveToOtherViewAction, Qt::SHIFT | Qt::Key_F6);
     connect(moveToOtherViewAction, &QAction::triggered, this, &DolphinMainWindow::moveToInactiveSplitView);
 
@@ -1742,7 +1861,7 @@ void DolphinMainWindow::setupActions()
     showFilterBar->setWhatsThis(xi18nc("@info:whatsthis",
                                        "This opens the "
                                        "<emphasis>Filter Bar</emphasis> at the bottom of the window.<nl/> "
-                                       "There you can enter text to filter the files and folders currently displayed. "
+                                       "There you can enter text to filter the files and folders currently displayed. "
                                        "Only those that contain the text in their name will be kept in view."));
     showFilterBar->setIcon(QIcon::fromTheme(QStringLiteral("view-filter")));
     actionCollection()->setDefaultShortcuts(showFilterBar, {Qt::CTRL | Qt::Key_I, Qt::Key_Slash});
@@ -1765,11 +1884,9 @@ void DolphinMainWindow::setupActions()
     searchAction->setToolTip(i18nc("@info:tooltip", "Search for files and folders"));
     searchAction->setWhatsThis(xi18nc("@info:whatsthis find",
                                       "<para>This helps you "
-                                      "find files and folders by opening a <emphasis>find bar</emphasis>. "
+                                      "find files and folders by opening a <emphasis>search bar</emphasis>. "
                                       "There you can enter search terms and specify settings to find the "
-                                      "objects you are looking for.</para><para>Use this help again on "
-                                      "the find bar so we can have a look at it while the settings are "
-                                      "explained.</para>"));
+                                      "items you are looking for.</para>"));
 
     // toggle_search acts as a copy of the main searchAction to be used mainly
     // in the toolbar, with no default shortcut attached, to avoid messing with
@@ -1781,6 +1898,13 @@ void DolphinMainWindow::setupActions()
     toggleSearchAction->setToolTip(searchAction->toolTip());
     toggleSearchAction->setWhatsThis(searchAction->whatsThis());
     toggleSearchAction->setCheckable(true);
+    connect(toggleSearchAction, &QAction::triggered, this, [this](bool checked) {
+        if (checked) {
+            find();
+        } else {
+            m_activeViewContainer->setSearchBarVisible(false);
+        }
+    });
 
     QAction *toggleSelectionModeAction = actionCollection()->addAction(QStringLiteral("toggle_selection_mode"));
     // i18n: This action toggles a selection mode.
@@ -1820,7 +1944,7 @@ void DolphinMainWindow::setupActions()
     invertSelection->setText(i18nc("@action:inmenu Edit", "Invert Selection"));
     invertSelection->setWhatsThis(xi18nc("@info:whatsthis invert",
                                          "This selects all "
-                                         "objects that you have currently <emphasis>not</emphasis> selected instead."));
+                                         "items that you have currently <emphasis>not</emphasis> selected instead."));
     invertSelection->setIcon(QIcon::fromTheme(QStringLiteral("edit-select-invert")));
     actionCollection()->setDefaultShortcut(invertSelection, Qt::CTRL | Qt::SHIFT | Qt::Key_A);
     connect(invertSelection, &QAction::triggered, this, &DolphinMainWindow::invertSelection);
@@ -1833,14 +1957,17 @@ void DolphinMainWindow::setupActions()
     // setup 'View' menu
     // (note that most of it is set up in DolphinViewActionHandler)
 
+    Admin::WorkerIntegration::createActAsAdminAction(actionCollection(), this);
+
     m_splitViewAction = actionCollection()->add<KActionMenu>(QStringLiteral("split_view"));
     m_splitViewMenuAction = actionCollection()->addAction(QStringLiteral("split_view_menu"));
 
-    m_splitViewAction->setWhatsThis(xi18nc("@info:whatsthis find",
-                                           "<para>This splits "
-                                           "the folder view below into two autonomous views.</para><para>This "
-                                           "way you can see two locations at once and move items between them "
-                                           "quickly.</para>Click this again afterwards to recombine the views."));
+    m_splitViewAction->setWhatsThis(xi18nc("@info:whatsthis split",
+                                           "<para>This presents "
+                                           "a second view side-by-side with the current view, so you can see "
+                                           "the contents of two folders at once and easily move items between "
+                                           "them.</para><para>The view that is not \"in focus\" will be dimmed. "
+                                           "</para>Click this button again to close one of the views."));
     m_splitViewMenuAction->setWhatsThis(m_splitViewAction->whatsThis());
 
     // only set it for the menu version
@@ -1851,8 +1978,8 @@ void DolphinMainWindow::setupActions()
 
     QAction *popoutSplit = actionCollection()->addAction(QStringLiteral("popout_split_view"));
     popoutSplit->setWhatsThis(xi18nc("@info:whatsthis",
-                                     "If the folder view has been split, this will pop the active folder "
-                                     "view out into a new window."));
+                                     "If the view has been split, this will pop the view in focus "
+                                     "out into a new window."));
     popoutSplit->setIcon(QIcon::fromTheme(QStringLiteral("window-new")));
     actionCollection()->setDefaultShortcut(popoutSplit, Qt::SHIFT | Qt::Key_F3);
     connect(popoutSplit, &QAction::triggered, this, &DolphinMainWindow::popoutSplitView);
@@ -1940,10 +2067,10 @@ void DolphinMainWindow::setupActions()
     undoAction->setWhatsThis(xi18nc("@info:whatsthis",
                                     "This undoes "
                                     "the last change you made to files or folders.<nl/>"
-                                    "Such changes include <interface>creatingrenaming</interface> "
+                                    "Such changes include <interface>creating</interface>, <interface>renaming</interface> "
                                     "and <interface>moving</interface> them to a different location "
-                                    "or to the <filename>Trash</filename>. <nl/>Changes that can't "
-                                    "be undone will ask for your confirmation."));
+                                    "or to the <filename>Trash</filename>. <nl/>Any changes that cannot be undone "
+                                    "will ask for your confirmation beforehand."));
     undoAction->setEnabled(false); // undo should be disabled by default
 
     {
@@ -1969,8 +2096,8 @@ void DolphinMainWindow::setupActions()
     homeAction->setWhatsThis(xi18nc("@info:whatsthis",
                                     "Go to your "
                                     "<filename>Home</filename> folder.<nl/>Every user account "
-                                    "has their own <filename>Home</filename> that contains their data "
-                                    "including folders that contain personal application data."));
+                                    "has their own <filename>Home</filename> that contains their personal files, "
+                                    "as well as hidden folders for their applications' data and configuration files."));
 
     // setup 'Tools' menu
     QAction *compareFiles = actionCollection()->addAction(QStringLiteral("compare_files"));
@@ -1979,6 +2106,12 @@ void DolphinMainWindow::setupActions()
     compareFiles->setEnabled(false);
     connect(compareFiles, &QAction::triggered, this, &DolphinMainWindow::compareFiles);
 
+    QAction *manageDiskSpaceUsage = actionCollection()->addAction(QStringLiteral("manage_disk_space"));
+    manageDiskSpaceUsage->setText(i18nc("@action:inmenu Tools", "Manage Disk Space Usage"));
+    manageDiskSpaceUsage->setIcon(QIcon::fromTheme(QStringLiteral("filelight")));
+    m_diskSpaceUsageMenu = new DiskSpaceUsageMenu{this};
+    manageDiskSpaceUsage->setMenu(m_diskSpaceUsageMenu);
+
     QAction *openPreferredSearchTool = actionCollection()->addAction(QStringLiteral("open_preferred_search_tool"));
     openPreferredSearchTool->setText(i18nc("@action:inmenu Tools", "Open Preferred Search Tool"));
     openPreferredSearchTool->setWhatsThis(xi18nc("@info:whatsthis",
@@ -1989,12 +2122,18 @@ void DolphinMainWindow::setupActions()
     connect(openPreferredSearchTool, &QAction::triggered, this, &DolphinMainWindow::openPreferredSearchTool);
 
     if (KAuthorized::authorize(QStringLiteral("shell_access"))) {
+        // Get icon of user default terminal emulator application
+        const KConfigGroup group(KSharedConfig::openConfig(QStringLiteral("kdeglobals"), KConfig::SimpleConfig), QStringLiteral("General"));
+        const QString terminalDesktopFilename = group.readEntry("TerminalService");
+        // Use utilities-terminal icon from theme if readEntry() has failed
+        const QString terminalIcon = terminalDesktopFilename.isEmpty() ? "utilities-terminal" : KDesktopFile(terminalDesktopFilename).readIcon();
+
         QAction *openTerminal = actionCollection()->addAction(QStringLiteral("open_terminal"));
         openTerminal->setText(i18nc("@action:inmenu Tools", "Open Terminal"));
         openTerminal->setWhatsThis(xi18nc("@info:whatsthis",
                                           "<para>This opens a <emphasis>terminal</emphasis> application for the viewed location.</para>"
-                                          "<para>To learn more about terminals use the help in the terminal application.</para>"));
-        openTerminal->setIcon(QIcon::fromTheme(QStringLiteral("utilities-terminal")));
+                                          "<para>To learn more about terminals use the help features in the terminal application.</para>"));
+        openTerminal->setIcon(QIcon::fromTheme(terminalIcon));
         actionCollection()->setDefaultShortcut(openTerminal, Qt::SHIFT | Qt::Key_F4);
         connect(openTerminal, &QAction::triggered, this, &DolphinMainWindow::openTerminal);
 
@@ -2003,18 +2142,10 @@ void DolphinMainWindow::setupActions()
         openTerminalHere->setText(i18nc("@action:inmenu Tools", "Open Terminal Here"));
         openTerminalHere->setWhatsThis(xi18nc("@info:whatsthis",
                                               "<para>This opens <emphasis>terminal</emphasis> applications for the selected items' locations.</para>"
-                                              "<para>To learn more about terminals use the help in the terminal application.</para>"));
-        openTerminalHere->setIcon(QIcon::fromTheme(QStringLiteral("utilities-terminal")));
+                                              "<para>To learn more about terminals use the help features in the terminal application.</para>"));
+        openTerminalHere->setIcon(QIcon::fromTheme(terminalIcon));
         actionCollection()->setDefaultShortcut(openTerminalHere, Qt::SHIFT | Qt::ALT | Qt::Key_F4);
         connect(openTerminalHere, &QAction::triggered, this, &DolphinMainWindow::openTerminalHere);
-
-#if HAVE_TERMINAL
-        QAction *focusTerminalPanel = actionCollection()->addAction(QStringLiteral("focus_terminal_panel"));
-        focusTerminalPanel->setText(i18nc("@action:inmenu Tools", "Focus Terminal Panel"));
-        focusTerminalPanel->setIcon(QIcon::fromTheme(QStringLiteral("swap-panels")));
-        actionCollection()->setDefaultShortcut(focusTerminalPanel, Qt::CTRL | Qt::SHIFT | Qt::Key_F4);
-        connect(focusTerminalPanel, &QAction::triggered, this, &DolphinMainWindow::focusTerminalPanel);
-#endif
     }
 
     // setup 'Bookmarks' menu
@@ -2029,10 +2160,10 @@ void DolphinMainWindow::setupActions()
     KToggleAction *showMenuBar = KStandardAction::showMenubar(nullptr, nullptr, actionCollection());
     showMenuBar->setWhatsThis(xi18nc("@info:whatsthis",
                                      "<para>This switches between having a <emphasis>Menubar</emphasis> "
-                                     "and having a <interface>%1</interface> button. Both "
+                                     "and having an <interface>%1</interface> button. Both "
                                      "contain mostly the same actions and configuration options.</para>"
-                                     "<para>The Menubar takes up more space but allows for fast and organised access to all "
-                                     "actions an application has to offer.</para><para>The <interface>%1</interface> button "
+                                     "<para>The Menubar takes up more space but allows for fast and organized access to all "
+                                     "actions an application has to offer.</para><para>The %1 button "
                                      "is simpler and small which makes triggering advanced actions more time consuming.</para>",
                                      hamburgerMenuAction->text().replace('&', "")));
     connect(showMenuBar,
@@ -2041,14 +2172,6 @@ void DolphinMainWindow::setupActions()
             &DolphinMainWindow::toggleShowMenuBar,
             Qt::QueuedConnection);
 
-    KToggleAction *showStatusBar = KStandardAction::showStatusbar(nullptr, nullptr, actionCollection());
-    showStatusBar->setChecked(GeneralSettings::showStatusBar());
-    connect(GeneralSettings::self(), &GeneralSettings::showStatusBarChanged, showStatusBar, &KToggleAction::setChecked);
-    connect(showStatusBar, &KToggleAction::triggered, this, [this](bool checked) {
-        GeneralSettings::setShowStatusBar(checked);
-        refreshViews();
-    });
-
     KStandardAction::keyBindings(this, &DolphinMainWindow::slotKeyBindings, actionCollection());
     KStandardAction::preferences(this, &DolphinMainWindow::editSettings, actionCollection());
 
@@ -2061,7 +2184,7 @@ void DolphinMainWindow::setupActions()
 
     for (int i = 0; i < MaxActivateTabShortcuts; ++i) {
         QAction *activateTab = actionCollection()->addAction(QStringLiteral("activate_tab_%1").arg(i));
-        activateTab->setText(i18nc("@action:inmenu", "Activate Tab %1", i + 1));
+        activateTab->setText(i18nc("@action:inmenu", "Go to Tab %1", i + 1));
         activateTab->setEnabled(false);
         connect(activateTab, &QAction::triggered, this, [this, i]() {
             m_tabWidget->activateTab(i);
@@ -2074,21 +2197,22 @@ void DolphinMainWindow::setupActions()
     }
 
     QAction *activateLastTab = actionCollection()->addAction(QStringLiteral("activate_last_tab"));
-    activateLastTab->setText(i18nc("@action:inmenu", "Activate Last Tab"));
+    activateLastTab->setIconText(i18nc("@action:inmenu", "Last Tab"));
+    activateLastTab->setText(i18nc("@action:inmenu", "Go to Last Tab"));
     activateLastTab->setEnabled(false);
     connect(activateLastTab, &QAction::triggered, m_tabWidget, &DolphinTabWidget::activateLastTab);
     actionCollection()->setDefaultShortcut(activateLastTab, Qt::ALT | Qt::Key_0);
 
     QAction *activateNextTab = actionCollection()->addAction(QStringLiteral("activate_next_tab"));
     activateNextTab->setIconText(i18nc("@action:inmenu", "Next Tab"));
-    activateNextTab->setText(i18nc("@action:inmenu", "Activate Next Tab"));
+    activateNextTab->setText(i18nc("@action:inmenu", "Go to Next Tab"));
     activateNextTab->setEnabled(false);
     connect(activateNextTab, &QAction::triggered, m_tabWidget, &DolphinTabWidget::activateNextTab);
     actionCollection()->setDefaultShortcuts(activateNextTab, nextTabKeys);
 
     QAction *activatePrevTab = actionCollection()->addAction(QStringLiteral("activate_prev_tab"));
     activatePrevTab->setIconText(i18nc("@action:inmenu", "Previous Tab"));
-    activatePrevTab->setText(i18nc("@action:inmenu", "Activate Previous Tab"));
+    activatePrevTab->setText(i18nc("@action:inmenu", "Go to Previous Tab"));
     activatePrevTab->setEnabled(false);
     connect(activatePrevTab, &QAction::triggered, m_tabWidget, &DolphinTabWidget::activatePrevTab);
     actionCollection()->setDefaultShortcuts(activatePrevTab, prevTabKeys);
@@ -2117,10 +2241,12 @@ void DolphinMainWindow::setupActions()
 
     QAction *openInSplitViewAction = actionCollection()->addAction(QStringLiteral("open_in_split_view"));
     openInSplitViewAction->setText(i18nc("@action:inmenu", "Open in Split View"));
-    openInSplitViewAction->setIcon(QIcon::fromTheme(QStringLiteral("view-right-new")));
+    openInSplitViewAction->setIcon(QIcon::fromTheme(QStringLiteral("view-split-left-right")));
     connect(openInSplitViewAction, &QAction::triggered, this, [this]() {
         openInSplitView(QUrl());
     });
+
+    m_recentFiles = new KRecentFilesAction(this);
 }
 
 void DolphinMainWindow::setupDockWidgets()
@@ -2155,13 +2281,14 @@ void DolphinMainWindow::setupDockWidgets()
     connect(infoPanel, &InformationPanel::urlActivated, this, &DolphinMainWindow::handleUrl);
     infoDock->setWidget(infoPanel);
 
-    createPanelAction(QIcon::fromTheme(QStringLiteral("dialog-information")), Qt::Key_F11, infoDock, QStringLiteral("show_information_panel"));
+    createPanelAction(QIcon::fromTheme(QStringLiteral("documentinfo")), Qt::Key_F11, infoDock, QStringLiteral("show_information_panel"));
 
     addDockWidget(Qt::RightDockWidgetArea, infoDock);
     connect(this, &DolphinMainWindow::urlChanged, infoPanel, &InformationPanel::setUrl);
     connect(this, &DolphinMainWindow::selectionChanged, infoPanel, &InformationPanel::setSelection);
     connect(this, &DolphinMainWindow::requestItemInfo, infoPanel, &InformationPanel::requestDelayedItemInfo);
     connect(this, &DolphinMainWindow::fileItemsChanged, infoPanel, &InformationPanel::slotFilesItemChanged);
+    connect(this, &DolphinMainWindow::settingsChanged, infoPanel, &InformationPanel::readSettings);
 #endif
 
     // i18n: This is the last paragraph for the "What's This"-texts of all four panels.
@@ -2255,7 +2382,7 @@ void DolphinMainWindow::setupDockWidgets()
                                   "<nl/>The location in the terminal will always match the folder "
                                   "view so you can navigate using either.</para><para>The terminal "
                                   "panel is not needed for basic computer usage but can be useful "
-                                  "for advanced tasks. To learn more about terminals use the help "
+                                  "for advanced tasks. To learn more about terminals use the help features "
                                   "in a standalone terminal application like Konsole.</para>"));
         terminalDock->setWhatsThis(xi18nc("@info:whatsthis",
                                           "<para>This is "
@@ -2263,11 +2390,18 @@ void DolphinMainWindow::setupDockWidgets()
                                           "normal terminal but will match the location of the folder view "
                                           "so you can navigate using either.</para><para>The terminal panel "
                                           "is not needed for basic computer usage but can be useful for "
-                                          "advanced tasks. To learn more about terminals use the help in a "
+                                          "advanced tasks. To learn more about terminals use the help features in a "
                                           "standalone terminal application like Konsole.</para>")
                                    + panelWhatsThis);
-    }
-#endif
+
+        QAction *focusTerminalPanel = actionCollection()->addAction(QStringLiteral("focus_terminal_panel"));
+        focusTerminalPanel->setText(i18nc("@action:inmenu Tools", "Focus Terminal Panel"));
+        focusTerminalPanel->setToolTip(i18nc("@info:tooltip", "Move keyboard focus to and from the Terminal panel."));
+        focusTerminalPanel->setIcon(QIcon::fromTheme(QStringLiteral("swap-panels")));
+        actionCollection()->setDefaultShortcut(focusTerminalPanel, Qt::CTRL | Qt::SHIFT | Qt::Key_F4);
+        connect(focusTerminalPanel, &QAction::triggered, this, &DolphinMainWindow::toggleTerminalPanelFocus);
+    } // endif "shell_access" allowed
+#endif // HAVE_TERMINAL
 
     if (GeneralSettings::version() < 200) {
         infoDock->hide();
@@ -2297,6 +2431,7 @@ void DolphinMainWindow::setupDockWidgets()
     connect(m_placesPanel, &PlacesPanel::errorMessage, this, &DolphinMainWindow::showErrorMessage);
     connect(this, &DolphinMainWindow::urlChanged, m_placesPanel, &PlacesPanel::setUrl);
     connect(placesDock, &DolphinDockWidget::visibilityChanged, &DolphinUrlNavigatorsController::slotPlacesPanelVisibilityChanged);
+    connect(placesDock, &DolphinDockWidget::visibilityChanged, this, &DolphinMainWindow::slotPlacesPanelVisibilityChanged);
     connect(this, &DolphinMainWindow::settingsChanged, m_placesPanel, &PlacesPanel::readSettings);
     connect(m_placesPanel, &PlacesPanel::storageTearDownRequested, this, &DolphinMainWindow::slotStorageTearDownFromPlacesRequested);
     connect(m_placesPanel, &PlacesPanel::storageTearDownExternallyRequested, this, &DolphinMainWindow::slotStorageTearDownExternallyRequested);
@@ -2308,7 +2443,7 @@ void DolphinMainWindow::setupDockWidgets()
     actionShowAllPlaces->setWhatsThis(i18nc("@info:whatsthis",
                                             "This displays "
                                             "all places in the places panel that have been hidden. They will "
-                                            "appear semi-transparent unless you uncheck their hide property."));
+                                            "appear semi-transparent and allow you to uncheck their \"Hide\" property."));
 
     connect(actionShowAllPlaces, &QAction::triggered, this, [this](bool checked) {
         m_placesPanel->setShowAll(checked);
@@ -2338,6 +2473,13 @@ void DolphinMainWindow::setupDockWidgets()
                                     "</interface> to display it again.</para>")
                              + panelWhatsThis);
 
+    QAction *focusPlacesPanel = actionCollection()->addAction(QStringLiteral("focus_places_panel"));
+    focusPlacesPanel->setText(i18nc("@action:inmenu View", "Focus Places Panel"));
+    focusPlacesPanel->setToolTip(i18nc("@info:tooltip", "Move keyboard focus to and from the Places panel."));
+    focusPlacesPanel->setIcon(QIcon::fromTheme(QStringLiteral("swap-panels")));
+    actionCollection()->setDefaultShortcut(focusPlacesPanel, Qt::CTRL | Qt::Key_P);
+    connect(focusPlacesPanel, &QAction::triggered, this, &DolphinMainWindow::togglePlacesPanelFocus);
+
     // Add actions into the "Panels" menu
     KActionMenu *panelsMenu = new KActionMenu(i18nc("@action:inmenu View", "Show Panels"), this);
     actionCollection()->addAction(QStringLiteral("panels"), panelsMenu);
@@ -2351,8 +2493,11 @@ void DolphinMainWindow::setupDockWidgets()
     panelsMenu->addAction(ac->action(QStringLiteral("show_folders_panel")));
     panelsMenu->addAction(ac->action(QStringLiteral("show_terminal_panel")));
     panelsMenu->addSeparator();
-    panelsMenu->addAction(actionShowAllPlaces);
     panelsMenu->addAction(lockLayoutAction);
+    panelsMenu->addSeparator();
+    panelsMenu->addAction(actionShowAllPlaces);
+    panelsMenu->addAction(focusPlacesPanel);
+    panelsMenu->addAction(ac->action(QStringLiteral("focus_terminal_panel")));
 
     connect(panelsMenu->menu(), &QMenu::aboutToShow, this, [actionShowAllPlaces] {
         actionShowAllPlaces->setEnabled(DolphinPlacesModelSingleton::instance().placesModel()->hiddenCount());
@@ -2373,7 +2518,7 @@ void DolphinMainWindow::updateFileAndEditActions()
     QAction *addToPlacesAction = col->action(QStringLiteral("add_to_places"));
     QAction *copyToOtherViewAction = col->action(QStringLiteral("copy_to_inactive_split_view"));
     QAction *moveToOtherViewAction = col->action(QStringLiteral("move_to_inactive_split_view"));
-    QAction *copyLocation = col->action(QString("copy_location"));
+    QAction *copyLocation = col->action(QStringLiteral("copy_location"));
 
     if (list.isEmpty()) {
         stateChanged(QStringLiteral("has_no_selection"));
@@ -2405,16 +2550,42 @@ void DolphinMainWindow::updateFileAndEditActions()
         const bool enableMoveToTrash = capabilitiesSource.isLocal() && capabilitiesSource.supportsMoving();
 
         renameAction->setEnabled(capabilitiesSource.supportsMoving());
-        moveToTrashAction->setEnabled(enableMoveToTrash);
+        m_disabledActionNotifier->setDisabledReason(renameAction, i18nc("@info", "Cannot rename: You do not have permission to rename items in this folder."));
         deleteAction->setEnabled(capabilitiesSource.supportsDeleting());
-        deleteWithTrashShortcut->setEnabled(capabilitiesSource.supportsDeleting() && !enableMoveToTrash);
+        m_disabledActionNotifier->setDisabledReason(deleteAction,
+                                                    i18nc("@info", "Cannot delete: You do not have permission to remove items from this folder."));
         cutAction->setEnabled(capabilitiesSource.supportsMoving());
+        m_disabledActionNotifier->setDisabledReason(cutAction, i18nc("@info", "Cannot cut: You do not have permission to move items from this folder."));
         copyLocation->setEnabled(list.length() == 1);
         showTarget->setEnabled(list.length() == 1 && list.at(0).isLink());
         duplicateAction->setEnabled(capabilitiesSource.supportsWriting());
+        m_disabledActionNotifier->setDisabledReason(duplicateAction,
+                                                    i18nc("@info", "Cannot duplicate here: You do not have permission to create items in this folder."));
+
+        if (enableMoveToTrash) {
+            moveToTrashAction->setEnabled(true);
+            deleteWithTrashShortcut->setEnabled(false);
+            m_disabledActionNotifier->clearDisabledReason(deleteWithTrashShortcut);
+        } else {
+            moveToTrashAction->setEnabled(false);
+            deleteWithTrashShortcut->setEnabled(capabilitiesSource.supportsDeleting());
+            m_disabledActionNotifier->setDisabledReason(deleteWithTrashShortcut,
+                                                        i18nc("@info", "Cannot delete: You do not have permission to remove items from this folder."));
+        }
     }
 
-    if (m_tabWidget->currentTabPage()->splitViewEnabled() && !list.isEmpty()) {
+    if (!m_tabWidget->currentTabPage()->splitViewEnabled()) {
+        // No need to set the disabled reason here, as it's obvious to the user that the reason is the split view being disabled.
+        copyToOtherViewAction->setEnabled(false);
+        m_disabledActionNotifier->clearDisabledReason(copyToOtherViewAction);
+        moveToOtherViewAction->setEnabled(false);
+        m_disabledActionNotifier->clearDisabledReason(moveToOtherViewAction);
+    } else if (list.isEmpty()) {
+        copyToOtherViewAction->setEnabled(false);
+        m_disabledActionNotifier->setDisabledReason(copyToOtherViewAction, i18nc("@info", "Cannot copy to other view: No files selected."));
+        moveToOtherViewAction->setEnabled(false);
+        m_disabledActionNotifier->setDisabledReason(moveToOtherViewAction, i18nc("@info", "Cannot move to other view: No files selected."));
+    } else {
         DolphinTabPage *tabPage = m_tabWidget->currentTabPage();
         KFileItem capabilitiesDestination;
 
@@ -2429,12 +2600,29 @@ void DolphinMainWindow::updateFileAndEditActions()
             return item.url().adjusted(QUrl::RemoveFilename | QUrl::StripTrailingSlash) != destUrl;
         });
 
-        copyToOtherViewAction->setEnabled(capabilitiesDestination.isWritable() && allNotTargetOrigin);
-        moveToOtherViewAction->setEnabled((list.isEmpty() || capabilitiesSource.supportsMoving()) && capabilitiesDestination.isWritable()
-                                          && allNotTargetOrigin);
-    } else {
-        copyToOtherViewAction->setEnabled(false);
-        moveToOtherViewAction->setEnabled(false);
+        if (!allNotTargetOrigin) {
+            copyToOtherViewAction->setEnabled(false);
+            m_disabledActionNotifier->setDisabledReason(copyToOtherViewAction,
+                                                        i18nc("@info", "Cannot copy to other view: The other view already contains these items."));
+            moveToOtherViewAction->setEnabled(false);
+            m_disabledActionNotifier->setDisabledReason(moveToOtherViewAction,
+                                                        i18nc("@info", "Cannot move to other view: The other view already contains these items."));
+        } else if (!capabilitiesDestination.isWritable()) {
+            copyToOtherViewAction->setEnabled(false);
+            m_disabledActionNotifier->setDisabledReason(
+                copyToOtherViewAction,
+                i18nc("@info", "Cannot copy to other view: You do not have permission to write into the destination folder."));
+            moveToOtherViewAction->setEnabled(false);
+            m_disabledActionNotifier->setDisabledReason(
+                moveToOtherViewAction,
+                i18nc("@info", "Cannot move to other view: You do not have permission to write into the destination folder."));
+        } else {
+            copyToOtherViewAction->setEnabled(true);
+            moveToOtherViewAction->setEnabled(capabilitiesSource.supportsMoving());
+            m_disabledActionNotifier->setDisabledReason(
+                moveToOtherViewAction,
+                i18nc("@info", "Cannot move to other view: You do not have permission to move items from this folder."));
+        }
     }
 }
 
@@ -2489,14 +2677,11 @@ void DolphinMainWindow::connectViewSignals(DolphinViewContainer *container)
     connect(container, &DolphinViewContainer::showFilterBarChanged, this, &DolphinMainWindow::updateFilterBarAction);
     connect(container, &DolphinViewContainer::writeStateChanged, this, &DolphinMainWindow::slotWriteStateChanged);
     slotWriteStateChanged(container->view()->isFolderWritable());
-    connect(container, &DolphinViewContainer::searchModeEnabledChanged, this, &DolphinMainWindow::updateSearchAction);
+    connect(container, &DolphinViewContainer::searchBarVisibilityChanged, this, &DolphinMainWindow::updateSearchAction);
     connect(container, &DolphinViewContainer::captionChanged, this, &DolphinMainWindow::updateWindowTitle);
     connect(container, &DolphinViewContainer::tabRequested, this, &DolphinMainWindow::openNewTab);
     connect(container, &DolphinViewContainer::activeTabRequested, this, &DolphinMainWindow::openNewTabAndActivate);
 
-    const QAction *toggleSearchAction = actionCollection()->action(QStringLiteral("toggle_search"));
-    connect(toggleSearchAction, &QAction::triggered, container, &DolphinViewContainer::setSearchModeEnabled);
-
     // Make the toggled state of the selection mode actions visually follow the selection mode state of the view.
     auto toggleSelectionModeAction = actionCollection()->action(QStringLiteral("toggle_selection_mode"));
     toggleSelectionModeAction->setChecked(m_activeViewContainer->isSelectionModeEnabled());
@@ -2517,6 +2702,7 @@ void DolphinMainWindow::connectViewSignals(DolphinViewContainer *container)
     connect(view, &DolphinView::goForwardRequested, this, &DolphinMainWindow::goForward);
     connect(view, &DolphinView::urlActivated, this, &DolphinMainWindow::handleUrl);
     connect(view, &DolphinView::goUpRequested, this, &DolphinMainWindow::goUp);
+    connect(view, &DolphinView::doubleClickViewBackground, this, &DolphinMainWindow::slotDoubleClickViewBackground);
 
     connect(container->urlNavigatorInternalWithHistory(), &KUrlNavigator::urlChanged, this, &DolphinMainWindow::changeUrl);
     connect(container->urlNavigatorInternalWithHistory(), &KUrlNavigator::historyChanged, this, &DolphinMainWindow::updateHistory);
@@ -2550,14 +2736,18 @@ void DolphinMainWindow::updateSplitActions()
             m_splitViewAction->setText(i18nc("@action:intoolbar Close left view", "Close"));
             m_splitViewAction->setToolTip(i18nc("@info", "Close left view"));
             m_splitViewAction->setIcon(QIcon::fromTheme(QStringLiteral("view-left-close")));
-            popoutSplitAction->setText(i18nc("@action:intoolbar Move left split view to a new window", "Pop out"));
-            popoutSplitAction->setToolTip(i18nc("@info", "Move left split view to a new window"));
+            m_splitViewMenuAction->setText(i18nc("@action:inmenu Close left view", "Close Left View"));
+
+            popoutSplitAction->setText(i18nc("@action:intoolbar Move left view to a new window", "Pop out Left View"));
+            popoutSplitAction->setToolTip(i18nc("@info", "Move left view to a new window"));
         } else {
             m_splitViewAction->setText(i18nc("@action:intoolbar Close right view", "Close"));
             m_splitViewAction->setToolTip(i18nc("@info", "Close right view"));
             m_splitViewAction->setIcon(QIcon::fromTheme(QStringLiteral("view-right-close")));
-            popoutSplitAction->setText(i18nc("@action:intoolbar Move right split view to a new window", "Pop out"));
-            popoutSplitAction->setToolTip(i18nc("@info", "Move right split view to a new window"));
+            m_splitViewMenuAction->setText(i18nc("@action:inmenu Close left view", "Close Right View"));
+
+            popoutSplitAction->setText(i18nc("@action:intoolbar Move right view to a new window", "Pop out Right View"));
+            popoutSplitAction->setToolTip(i18nc("@info", "Move right view to a new window"));
         }
         popoutSplitAction->setEnabled(true);
         if (!m_splitViewAction->menu()) {
@@ -2567,9 +2757,10 @@ void DolphinMainWindow::updateSplitActions()
         }
     } else {
         m_splitViewAction->setText(i18nc("@action:intoolbar Split view", "Split"));
+        m_splitViewMenuAction->setText(m_splitViewAction->text());
         m_splitViewAction->setToolTip(i18nc("@info", "Split view"));
-        m_splitViewAction->setIcon(QIcon::fromTheme(QStringLiteral("view-right-new")));
-        popoutSplitAction->setText(i18nc("@action:intoolbar Move active split view to a new window", "Pop out"));
+        m_splitViewAction->setIcon(QIcon::fromTheme(QStringLiteral("view-split-left-right")));
+        popoutSplitAction->setText(i18nc("@action:intoolbar Move view in focus to a new window", "Pop out"));
         popoutSplitAction->setEnabled(false);
         if (m_splitViewAction->menu()) {
             m_splitViewAction->removeAction(popoutSplitAction);
@@ -2580,7 +2771,6 @@ void DolphinMainWindow::updateSplitActions()
     }
 
     // Update state from toolbar action
-    m_splitViewMenuAction->setText(m_splitViewAction->text());
     m_splitViewMenuAction->setToolTip(m_splitViewAction->toolTip());
     m_splitViewMenuAction->setIcon(m_splitViewAction->icon());
 }
@@ -2598,6 +2788,12 @@ void DolphinMainWindow::updateAllowedToolbarAreas()
     }
 }
 
+void DolphinMainWindow::updateNavigatorsBackground()
+{
+    auto navigators = static_cast<DolphinNavigatorsWidgetAction *>(actionCollection()->action(QStringLiteral("url_navigators")));
+    navigators->setBackgroundEnabled(navigators->isInToolbar());
+}
+
 bool DolphinMainWindow::isKompareInstalled() const
 {
     static bool initialized = false;
@@ -2784,23 +2980,44 @@ void DolphinMainWindow::saveNewToolbarConfig()
         m_tabWidget->currentTabPage()->insertNavigatorsWidget(navigators);
     }
     updateAllowedToolbarAreas();
+    updateNavigatorsBackground();
     (static_cast<KHamburgerMenu *>(actionCollection()->action(KStandardAction::name(KStandardAction::HamburgerMenu))))->hideActionsOf(toolBar());
 }
 
-void DolphinMainWindow::focusTerminalPanel()
+void DolphinMainWindow::toggleTerminalPanelFocus()
 {
-    if (m_terminalPanel->isVisible()) {
-        if (m_terminalPanel->terminalHasFocus()) {
-            m_activeViewContainer->view()->setFocus(Qt::FocusReason::ShortcutFocusReason);
-            actionCollection()->action(QStringLiteral("focus_terminal_panel"))->setText(i18nc("@action:inmenu Tools", "Focus Terminal Panel"));
-        } else {
-            m_terminalPanel->setFocus(Qt::FocusReason::ShortcutFocusReason);
-            actionCollection()->action(QStringLiteral("focus_terminal_panel"))->setText(i18nc("@action:inmenu Tools", "Defocus Terminal Panel"));
-        }
-    } else {
-        actionCollection()->action(QStringLiteral("show_terminal_panel"))->trigger();
+    if (!m_terminalPanel->isVisible()) {
+        actionCollection()->action(QStringLiteral("show_terminal_panel"))->trigger(); // Also moves focus to the panel.
         actionCollection()->action(QStringLiteral("focus_terminal_panel"))->setText(i18nc("@action:inmenu Tools", "Defocus Terminal Panel"));
+        return;
+    }
+
+    if (m_terminalPanel->terminalHasFocus()) {
+        m_activeViewContainer->view()->setFocus(Qt::FocusReason::ShortcutFocusReason);
+        actionCollection()->action(QStringLiteral("focus_terminal_panel"))->setText(i18nc("@action:inmenu Tools", "Focus Terminal Panel"));
+        return;
+    }
+
+    m_terminalPanel->setFocus(Qt::FocusReason::ShortcutFocusReason);
+    actionCollection()->action(QStringLiteral("focus_terminal_panel"))->setText(i18nc("@action:inmenu Tools", "Defocus Terminal Panel"));
+}
+
+void DolphinMainWindow::togglePlacesPanelFocus()
+{
+    if (!m_placesPanel->isVisible()) {
+        actionCollection()->action(QStringLiteral("show_places_panel"))->trigger(); // Also moves focus to the panel.
+        actionCollection()->action(QStringLiteral("focus_places_panel"))->setText(i18nc("@action:inmenu View", "Defocus Terminal Panel"));
+        return;
+    }
+
+    if (m_placesPanel->hasFocus()) {
+        m_activeViewContainer->view()->setFocus(Qt::FocusReason::ShortcutFocusReason);
+        actionCollection()->action(QStringLiteral("focus_places_panel"))->setText(i18nc("@action:inmenu View", "Focus Places Panel"));
+        return;
     }
+
+    m_placesPanel->setFocus(Qt::FocusReason::ShortcutFocusReason);
+    actionCollection()->action(QStringLiteral("focus_places_panel"))->setText(i18nc("@action:inmenu View", "Defocus Places Panel"));
 }
 
 DolphinMainWindow::UndoUiInterface::UndoUiInterface()
@@ -2817,7 +3034,7 @@ void DolphinMainWindow::UndoUiInterface::jobError(KIO::Job *job)
     DolphinMainWindow *mainWin = qobject_cast<DolphinMainWindow *>(parentWidget());
     if (mainWin) {
         DolphinViewContainer *container = mainWin->activeViewContainer();
-        container->showMessage(job->errorString(), DolphinViewContainer::Error);
+        container->showMessage(job->errorString(), KMessageWidget::Error);
     } else {
         KIO::FileUndoManager::UiInterface::jobError(job);
     }
@@ -2833,4 +3050,41 @@ bool DolphinMainWindow::isItemVisibleInAnyView(const QString &urlOfItem)
     return m_tabWidget->isItemVisibleInAnyView(QUrl::fromUserInput(urlOfItem));
 }
 
+void DolphinMainWindow::slotDoubleClickViewBackground(Qt::MouseButton button)
+{
+    if (button != Qt::MouseButton::LeftButton) {
+        // only handle left mouse button for now
+        return;
+    }
+
+    GeneralSettings *settings = GeneralSettings::self();
+    QString clickAction = settings->doubleClickViewAction();
+
+    DolphinView *view = activeViewContainer()->view();
+    if (view == nullptr || clickAction == "none") {
+        return;
+    }
+
+    if (clickAction == customCommand) {
+        // run custom command set by the user
+        QString path = view->url().toLocalFile();
+        QString clickCustomAction = settings->doubleClickViewCustomAction();
+        clickCustomAction.replace("{path}", path.prepend('"').append('"'));
+
+        m_job = new KIO::CommandLauncherJob(clickCustomAction);
+        m_job->setUiDelegate(new KDialogJobUiDelegate(KJobUiDelegate::AutoHandlingEnabled, this));
+        m_job->start();
+
+    } else {
+        // get the action set by the user and trigger it
+        const KActionCollection *actions = actionCollection();
+        QAction *action = actions->action(clickAction);
+        if (action == nullptr) {
+            qCWarning(DolphinDebug) << QStringLiteral("Double-click view: action `%1` was not found").arg(clickAction);
+            return;
+        }
+        action->trigger();
+    }
+}
+
 #include "moc_dolphinmainwindow.cpp"