]> cloud.milkyroute.net Git - dolphin.git/blobdiff - src/dolphinmainwindow.cpp
DolphinView: rename copySelectedItems to copySelectedItemsToClipboard
[dolphin.git] / src / dolphinmainwindow.cpp
index 455d3b481f8e6a1355cdcfbeacb2f9ea0ab7cda6..ab41e2d7ff5afff0ce2b1e04b0cdc4271809d6c0 100644 (file)
@@ -21,6 +21,7 @@
 
 #include "dolphinmainwindow.h"
 
+#include "dolphinmainwindowadaptor.h"
 #include "config-terminal.h"
 #include "global.h"
 #include "dolphinbookmarkhandler.h"
 #include <KActionMenu>
 #include <KAuthorized>
 #include <KConfig>
+#include <KConfigGui>
 #include <KDualAction>
 #include <KFileItemListProperties>
 #include <KHelpMenu>
+#include <KIO/CommandLauncherJob>
 #include <KIO/JobUiDelegate>
 #include <KIO/OpenFileManagerWindowJob>
 #include <KJobWidgets>
 #include <KLocalizedString>
 #include <KMessageBox>
+#include <KNS3/KMoreToolsMenuFactory>
 #include <KProtocolInfo>
 #include <KProtocolManager>
 #include <KRun>
@@ -65,6 +69,7 @@
 #include <KStartupInfo>
 #include <KToggleAction>
 #include <KToolBar>
+#include <KToolBarPopupAction>
 #include <KToolInvocation>
 #include <KUrlComboBox>
 #include <KUrlNavigator>
@@ -90,10 +95,14 @@ namespace {
     // Used for GeneralSettings::version() to determine whether
     // an updated version of Dolphin is running.
     const int CurrentDolphinVersion = 200;
+    // The maximum number of entries in the back/forward popup menu
+    const int MaxNumberOfNavigationentries = 12;
+    // The maximum number of "Activate Tab" shortcuts
+    const int MaxActivateTabShortcuts = 9;
 }
 
 DolphinMainWindow::DolphinMainWindow() :
-    KXmlGuiWindow(nullptr, Qt::WindowContextHelpButtonHint),
+    KXmlGuiWindow(nullptr),
     m_newFileMenu(nullptr),
     m_helpMenu(nullptr),
     m_tabWidget(nullptr),
@@ -107,9 +116,17 @@ DolphinMainWindow::DolphinMainWindow() :
     m_lastHandleUrlStatJob(nullptr),
     m_terminalPanel(nullptr),
     m_placesPanel(nullptr),
-    m_tearDownFromPlacesRequested(false)
+    m_tearDownFromPlacesRequested(false),
+    m_backAction(nullptr),
+    m_forwardAction(nullptr)
 {
     Q_INIT_RESOURCE(dolphin);
+
+    new MainWindowAdaptor(this);
+
+#ifndef Q_OS_WIN
+       setWindowFlags(Qt::WindowContextHelpButtonHint);
+#endif
     setComponentName(QStringLiteral("dolphin"), QGuiApplication::applicationDisplayName());
     setObjectName(QStringLiteral("Dolphin#"));
 
@@ -187,6 +204,8 @@ DolphinMainWindow::DolphinMainWindow() :
     toolBar()->installEventFilter(middleClickEventFilter);
 
     setupWhatsThis();
+
+    QTimer::singleShot(0, this, &DolphinMainWindow::setupUpdateOpenPreferredSearchToolAction);
 }
 
 DolphinMainWindow::~DolphinMainWindow()
@@ -225,7 +244,8 @@ void DolphinMainWindow::openFiles(const QStringList& files, bool splitView)
 
 void DolphinMainWindow::activateWindow()
 {
-    KStartupInfo::setNewStartupId(window(), KStartupInfo::startupId());
+    window()->setAttribute(Qt::WA_NativeWindow, true);
+    KStartupInfo::setNewStartupId(window()->windowHandle(), KStartupInfo::startupId());
     KWindowSystem::activateWindow(window()->effectiveWinId());
 }
 
@@ -359,7 +379,7 @@ void DolphinMainWindow::addToPlaces()
     QString name;
 
     // If nothing is selected, act on the current dir
-    if (m_activeViewContainer->view()->selectedItems().count() == 0) {
+    if (m_activeViewContainer->view()->selectedItems().isEmpty()) {
         url = m_activeViewContainer->url();
         name = m_activeViewContainer->placesText();
     } else {
@@ -509,7 +529,7 @@ void DolphinMainWindow::closeEvent(QCloseEvent* event)
         }
     }
 
-    if (m_terminalPanel->hasProgramRunning() && GeneralSettings::confirmClosingTerminalRunningProgram() && closedByUser) {
+    if (m_terminalPanel && m_terminalPanel->hasProgramRunning() && GeneralSettings::confirmClosingTerminalRunningProgram() && closedByUser) {
         // Ask if the user really wants to quit Dolphin with a program that is still running in the Terminal panel
         // Open a confirmation dialog with 3 buttons:
         // QDialogButtonBox::Yes    -> Quit
@@ -527,7 +547,7 @@ void DolphinMainWindow::closeEvent(QCloseEvent* event)
         if (!m_terminalPanel->isVisible()) {
             KGuiItem::assign(
                     buttons->button(QDialogButtonBox::No),
-                    KGuiItem(i18n("Show &Terminal Panel"), QIcon::fromTheme(QStringLiteral("utilities-terminal"))));
+                    KGuiItem(i18n("Show &Terminal Panel"), QIcon::fromTheme(QStringLiteral("dialog-scripts"))));
         }
         KGuiItem::assign(buttons->button(QDialogButtonBox::Cancel), KStandardGuiItem::cancel());
 
@@ -561,6 +581,14 @@ void DolphinMainWindow::closeEvent(QCloseEvent* event)
         }
     }
 
+    if (GeneralSettings::rememberOpenedTabs())  {
+        KConfigGui::setSessionConfig(QStringLiteral("dolphin"), QStringLiteral("dolphin"));
+        KConfig *config = KConfigGui::sessionConfig();
+        saveGlobalProperties(config);
+        savePropertiesInternal(config, 1);
+        config->sync();
+    }
+
     GeneralSettings::setVersion(CurrentDolphinVersion);
     GeneralSettings::self()->save();
 
@@ -581,13 +609,13 @@ void DolphinMainWindow::updateNewMenu()
 {
     m_newFileMenu->setViewShowsHiddenFiles(activeViewContainer()->view()->hiddenFilesShown());
     m_newFileMenu->checkUpToDate();
-    m_newFileMenu->setPopupFiles(activeViewContainer()->url());
+    m_newFileMenu->setPopupFiles(QList<QUrl>() << activeViewContainer()->url());
 }
 
 void DolphinMainWindow::createDirectory()
 {
     m_newFileMenu->setViewShowsHiddenFiles(activeViewContainer()->view()->hiddenFilesShown());
-    m_newFileMenu->setPopupFiles(activeViewContainer()->url());
+    m_newFileMenu->setPopupFiles(QList<QUrl>() << activeViewContainer()->url());
     m_newFileMenu->createDirectory();
 }
 
@@ -631,7 +659,7 @@ void DolphinMainWindow::cut()
 
 void DolphinMainWindow::copy()
 {
-    m_activeViewContainer->view()->copySelectedItems();
+    m_activeViewContainer->view()->copySelectedItemsToClipboard();
 }
 
 void DolphinMainWindow::paste()
@@ -676,6 +704,56 @@ void DolphinMainWindow::slotToolBarActionMiddleClicked(QAction *action)
     }
 }
 
+void DolphinMainWindow::slotAboutToShowBackPopupMenu()
+{
+    KUrlNavigator* urlNavigator = m_activeViewContainer->urlNavigator();
+    int entries = 0;
+    m_backAction->menu()->clear();
+    for (int i = urlNavigator->historyIndex() + 1; i < urlNavigator->historySize() && entries < MaxNumberOfNavigationentries; ++i, ++entries) {
+        QAction* action = new QAction(urlNavigator->locationUrl(i).toString(QUrl::PreferLocalFile), m_backAction->menu());
+        action->setData(i);
+        m_backAction->menu()->addAction(action);
+    }
+}
+
+void DolphinMainWindow::slotGoBack(QAction* action)
+{
+    int gotoIndex = action->data().value<int>();
+    KUrlNavigator* urlNavigator = m_activeViewContainer->urlNavigator();
+    for (int i = gotoIndex - urlNavigator->historyIndex(); i > 0; --i) {
+        goBack();
+    }
+}
+
+void DolphinMainWindow::slotBackForwardActionMiddleClicked(QAction* action)
+{
+    if (action) {
+        KUrlNavigator* urlNavigator = activeViewContainer()->urlNavigator();
+        openNewTabAfterCurrentTab(urlNavigator->locationUrl(action->data().value<int>()));
+    }
+}
+
+void DolphinMainWindow::slotAboutToShowForwardPopupMenu()
+{
+    KUrlNavigator* urlNavigator = m_activeViewContainer->urlNavigator();
+    int entries = 0;
+    m_forwardAction->menu()->clear();
+    for (int i = urlNavigator->historyIndex() - 1; i >= 0 && entries < MaxNumberOfNavigationentries; --i, ++entries) {
+        QAction* action = new QAction(urlNavigator->locationUrl(i).toString(QUrl::PreferLocalFile), m_forwardAction->menu());
+        action->setData(i);
+        m_forwardAction->menu()->addAction(action);
+    }
+}
+
+void DolphinMainWindow::slotGoForward(QAction* action)
+{
+    int gotoIndex = action->data().value<int>();
+    KUrlNavigator* urlNavigator = m_activeViewContainer->urlNavigator();
+    for (int i = urlNavigator->historyIndex() - gotoIndex; i > 0; --i) {
+        goForward();
+    }
+}
+
 void DolphinMainWindow::selectAll()
 {
     clearStatusBar();
@@ -858,7 +936,10 @@ void DolphinMainWindow::compareFiles()
     command.append("\" \"");
     command.append(urlB.toDisplayString(QUrl::PreferLocalFile));
     command.append('\"');
-    KRun::runCommand(command, QStringLiteral("Kompare"), QStringLiteral("kompare"), this);
+
+    KIO::CommandLauncherJob *job = new KIO::CommandLauncherJob(command, this);
+    job->setDesktopName(QStringLiteral("org.kde.kompare"));
+    job->start();
 }
 
 void DolphinMainWindow::toggleShowMenuBar()
@@ -872,23 +953,88 @@ void DolphinMainWindow::toggleShowMenuBar()
     }
 }
 
-void DolphinMainWindow::openTerminal()
+QString DolphinMainWindow::activeContainerLocalPath()
 {
-    QString dir(QDir::homePath());
-
-    // If the given directory is not local, it can still be the URL of an
-    // ioslave using UDS_LOCAL_PATH which to be converted first.
     KIO::StatJob* statJob = KIO::mostLocalUrl(m_activeViewContainer->url());
     KJobWidgets::setWindow(statJob, this);
     statJob->exec();
     QUrl url = statJob->mostLocalUrl();
-
-    //If the URL is local after the above conversion, set the directory.
     if (url.isLocalFile()) {
-        dir = url.toLocalFile();
+        return url.toLocalFile();
+    }
+    return QDir::homePath();
+}
+
+QPointer<QAction> DolphinMainWindow::preferredSearchTool()
+{
+    m_searchTools.clear();
+    KMoreToolsMenuFactory("dolphin/search-tools").fillMenuFromGroupingNames(
+        &m_searchTools, { "files-find" }, m_activeViewContainer->url()
+    );
+    QList<QAction*> actions = m_searchTools.actions();
+    if (actions.isEmpty()) {
+        return nullptr;
+    }
+    QAction* action = actions.first();
+    if (action->isSeparator()) {
+        return nullptr;
+    }
+    return action;
+}
+
+void DolphinMainWindow::setupUpdateOpenPreferredSearchToolAction()
+{
+    QAction* openPreferredSearchTool = actionCollection()->action(QStringLiteral("open_preferred_search_tool"));
+    const QList<QWidget*> widgets = openPreferredSearchTool->associatedWidgets();
+    for (QWidget* widget : widgets) {
+        QMenu* menu = qobject_cast<QMenu*>(widget);
+        if (menu) {
+            connect(menu, &QMenu::aboutToShow, this, &DolphinMainWindow::updateOpenPreferredSearchToolAction);
+        }
     }
 
-    KToolInvocation::invokeTerminal(QString(), dir);
+    // Update the open_preferred_search_tool action *before* the Configure Shortcuts window is shown,
+    // since this action is then listed in that window and it should be up-to-date when it is displayed.
+    // This update is instantaneous if user made no changes to the search tools in the meantime.
+    // Maybe all KStandardActions should defer calls to their slots, so that we could simply connect() to trigger()?
+    connect(
+        actionCollection()->action(KStandardAction::name(KStandardAction::KeyBindings)), &QAction::hovered,
+        this, &DolphinMainWindow::updateOpenPreferredSearchToolAction
+    );
+
+    updateOpenPreferredSearchToolAction();
+}
+
+void DolphinMainWindow::updateOpenPreferredSearchToolAction()
+{
+    QAction* openPreferredSearchTool = actionCollection()->action(QStringLiteral("open_preferred_search_tool"));
+    if (!openPreferredSearchTool) {
+        return;
+    }
+    QPointer<QAction> tool = preferredSearchTool();
+    if (tool) {
+        openPreferredSearchTool->setVisible(true);
+        openPreferredSearchTool->setText(i18nc("@action:inmenu Tools", "Open %1", tool->text()));
+        openPreferredSearchTool->setIcon(tool->icon());
+    } else {
+        openPreferredSearchTool->setVisible(false);
+        // still visible in Shortcuts configuration window
+        openPreferredSearchTool->setText(i18nc("@action:inmenu Tools", "Open Preferred Search Tool"));
+        openPreferredSearchTool->setIcon(QIcon::fromTheme(QStringLiteral("search")));
+    }
+}
+
+void DolphinMainWindow::openPreferredSearchTool()
+{
+    QPointer<QAction> tool = preferredSearchTool();
+    if (tool) {
+        tool->trigger();
+    }
+}
+
+void DolphinMainWindow::openTerminal()
+{
+    KToolInvocation::invokeTerminal(QString(), activeContainerLocalPath());
 }
 
 void DolphinMainWindow::editSettings()
@@ -994,18 +1140,15 @@ void DolphinMainWindow::updateControlMenu()
 
     KActionCollection* ac = actionCollection();
 
-    // Add "Create New" menu
     menu->addMenu(m_newFileMenu->menu());
+    addActionToMenu(ac->action(QStringLiteral("file_new")), menu);
+    addActionToMenu(ac->action(QStringLiteral("new_tab")), menu);
+    addActionToMenu(ac->action(QStringLiteral("closed_tabs")), menu);
 
     menu->addSeparator();
 
-    // Overwrite Find action to Search action
-    QAction *searchAction = ac->action(KStandardAction::name(KStandardAction::Find));
-    searchAction->setText(i18n("Search..."));
-
     // Add "Edit" actions
     bool added = addActionToMenu(ac->action(KStandardAction::name(KStandardAction::Undo)), menu) |
-                 addActionToMenu(searchAction, menu) |
                  addActionToMenu(ac->action(KStandardAction::name(KStandardAction::SelectAll)), menu) |
                  addActionToMenu(ac->action(QStringLiteral("invert_selection")), menu);
 
@@ -1016,66 +1159,42 @@ void DolphinMainWindow::updateControlMenu()
     // Add "View" actions
     if (!GeneralSettings::showZoomSlider()) {
         addActionToMenu(ac->action(KStandardAction::name(KStandardAction::ZoomIn)), menu);
+        addActionToMenu(ac->action(QStringLiteral("view_zoom_reset")), menu);
         addActionToMenu(ac->action(KStandardAction::name(KStandardAction::ZoomOut)), menu);
         menu->addSeparator();
     }
 
-    added = addActionToMenu(ac->action(QStringLiteral("sort")), menu) |
-            addActionToMenu(ac->action(QStringLiteral("view_mode")), menu) |
-            addActionToMenu(ac->action(QStringLiteral("additional_info")), menu) |
-            addActionToMenu(ac->action(QStringLiteral("show_preview")), menu) |
+    added = addActionToMenu(ac->action(QStringLiteral("show_preview")), menu) |
             addActionToMenu(ac->action(QStringLiteral("show_in_groups")), menu) |
-            addActionToMenu(ac->action(QStringLiteral("show_hidden_files")), menu);
-
-    if (added) {
-        menu->addSeparator();
-    }
-
-    added = addActionToMenu(ac->action(QStringLiteral("split_view")), menu) |
-            addActionToMenu(ac->action(KStandardAction::name(KStandardAction::Redisplay)), menu) |
+            addActionToMenu(ac->action(QStringLiteral("show_hidden_files")), menu) |
+            addActionToMenu(ac->action(QStringLiteral("additional_info")), menu) |
             addActionToMenu(ac->action(QStringLiteral("view_properties")), menu);
+
     if (added) {
         menu->addSeparator();
     }
 
-    addActionToMenu(ac->action(QStringLiteral("panels")), menu);
-    QMenu* locationBarMenu = new QMenu(i18nc("@action:inmenu", "Location Bar"), menu);
-    locationBarMenu->addAction(ac->action(QStringLiteral("editable_location")));
-    locationBarMenu->addAction(ac->action(QStringLiteral("replace_location")));
-    menu->addMenu(locationBarMenu);
+    // Add a curated assortment of items from the "Tools" menu
+    addActionToMenu(ac->action(QStringLiteral("show_filter_bar")), menu);
+    addActionToMenu(ac->action(QStringLiteral("open_preferred_search_tool")), menu);
+    addActionToMenu(ac->action(QStringLiteral("open_terminal")), menu);
+    connect(menu, &QMenu::aboutToShow, this, &DolphinMainWindow::updateOpenPreferredSearchToolAction);
 
     menu->addSeparator();
 
-    // Add "Go" menu
-    QMenu* goMenu = new QMenu(i18nc("@action:inmenu", "Go"), menu);
-    goMenu->addAction(ac->action(KStandardAction::name(KStandardAction::Back)));
-    goMenu->addAction(ac->action(KStandardAction::name(KStandardAction::Forward)));
-    goMenu->addAction(ac->action(KStandardAction::name(KStandardAction::Up)));
-    goMenu->addAction(ac->action(KStandardAction::name(KStandardAction::Home)));
-    goMenu->addAction(ac->action(QStringLiteral("closed_tabs")));
-    KActionMenu *bookmarkMenu = new KActionMenu(i18nc("@title:menu", "&Bookmarks"), goMenu);
-    m_bookmarkHandler->fillControlMenu(bookmarkMenu->menu(), ac);
-    goMenu->addAction(bookmarkMenu);
-    menu->addMenu(goMenu);
-
-    // Add "Tool" menu
-    QMenu* toolsMenu = new QMenu(i18nc("@action:inmenu", "Tools"), menu);
-    toolsMenu->addAction(ac->action(QStringLiteral("show_filter_bar")));
-    toolsMenu->addAction(ac->action(QStringLiteral("compare_files")));
-    toolsMenu->addAction(ac->action(QStringLiteral("open_terminal")));
-    toolsMenu->addAction(ac->action(QStringLiteral("change_remote_encoding")));
-    menu->addMenu(toolsMenu);
+    // Add "Show Panels" menu
+    addActionToMenu(ac->action(QStringLiteral("panels")), menu);
 
     // Add "Settings" menu entries
     addActionToMenu(ac->action(KStandardAction::name(KStandardAction::KeyBindings)), menu);
     addActionToMenu(ac->action(KStandardAction::name(KStandardAction::ConfigureToolbars)), menu);
     addActionToMenu(ac->action(KStandardAction::name(KStandardAction::Preferences)), menu);
+    addActionToMenu(ac->action(KStandardAction::name(KStandardAction::ShowMenubar)), menu);
 
     // Add "Help" menu
-    menu->addMenu(m_helpMenu->menu());
-
-    menu->addSeparator();
-    addActionToMenu(ac->action(KStandardAction::name(KStandardAction::ShowMenubar)), menu);
+    auto helpMenu = m_helpMenu->menu();
+    helpMenu->setIcon(QIcon::fromTheme(QStringLiteral("system-help")));
+    menu->addMenu(helpMenu);
 }
 
 void DolphinMainWindow::updateToolBar()
@@ -1149,13 +1268,17 @@ void DolphinMainWindow::activeViewChanged(DolphinViewContainer* viewContainer)
 void DolphinMainWindow::tabCountChanged(int count)
 {
     const bool enableTabActions = (count > 1);
+    for (int i = 0; i < MaxActivateTabShortcuts; ++i) {
+        actionCollection()->action(QStringLiteral("activate_tab_%1").arg(i))->setEnabled(enableTabActions);
+    }
+    actionCollection()->action(QStringLiteral("activate_last_tab"))->setEnabled(enableTabActions);
     actionCollection()->action(QStringLiteral("activate_next_tab"))->setEnabled(enableTabActions);
     actionCollection()->action(QStringLiteral("activate_prev_tab"))->setEnabled(enableTabActions);
 }
 
 void DolphinMainWindow::updateWindowTitle()
 {
-    const QString newTitle = m_activeViewContainer->caption();
+    const QString newTitle = m_activeViewContainer->captionWindowTitle();
     if (windowTitle() != newTitle) {
         setWindowTitle(newTitle);
     }
@@ -1163,7 +1286,7 @@ void DolphinMainWindow::updateWindowTitle()
 
 void DolphinMainWindow::slotStorageTearDownFromPlacesRequested(const QString& mountPath)
 {
-    if (m_terminalPanel->currentWorkingDirectory().startsWith(mountPath)) {
+    if (m_terminalPanel && m_terminalPanel->currentWorkingDirectory().startsWith(mountPath)) {
         m_tearDownFromPlacesRequested = true;
         m_terminalPanel->goHome();
         // m_placesPanel->proceedWithTearDown() will be called in slotTerminalDirectoryChanged
@@ -1174,7 +1297,7 @@ void DolphinMainWindow::slotStorageTearDownFromPlacesRequested(const QString& mo
 
 void DolphinMainWindow::slotStorageTearDownExternallyRequested(const QString& mountPath)
 {
-    if (m_terminalPanel->currentWorkingDirectory().startsWith(mountPath)) {
+    if (m_terminalPanel && m_terminalPanel->currentWorkingDirectory().startsWith(mountPath)) {
         m_tearDownFromPlacesRequested = false;
         m_terminalPanel->goHome();
     }
@@ -1211,6 +1334,7 @@ void DolphinMainWindow::setupActions()
 
     QAction* addToPlaces = actionCollection()->addAction(QStringLiteral("add_to_places"));
     addToPlaces->setIcon(QIcon::fromTheme(QStringLiteral("bookmark-new")));
+    addToPlaces->setText(i18nc("@action:inmenu Add current folder to places", "Add to Places"));
     addToPlaces->setWhatsThis(xi18nc("@info:whatsthis", "This adds the selected folder "
         "to the Places panel."));
     connect(addToPlaces, &QAction::triggered, this, &DolphinMainWindow::addToPlaces);
@@ -1342,10 +1466,22 @@ void DolphinMainWindow::setupActions()
     connect(replaceLocation, &QAction::triggered, this, &DolphinMainWindow::replaceLocation);
 
     // setup 'Go' menu
-    QAction* backAction = KStandardAction::back(this, &DolphinMainWindow::goBack, actionCollection());
-    auto backShortcuts = backAction->shortcuts();
+    {
+        QScopedPointer<QAction> backAction(KStandardAction::back(nullptr, nullptr, nullptr));
+        m_backAction = new KToolBarPopupAction(backAction->icon(), backAction->text(), actionCollection());
+        m_backAction->setObjectName(backAction->objectName());
+        m_backAction->setShortcuts(backAction->shortcuts());
+    }
+    m_backAction->setDelayed(true);
+    m_backAction->setStickyMenu(false);
+    connect(m_backAction, &QAction::triggered, this, &DolphinMainWindow::goBack);
+    connect(m_backAction->menu(), &QMenu::aboutToShow, this, &DolphinMainWindow::slotAboutToShowBackPopupMenu);
+    connect(m_backAction->menu(), &QMenu::triggered, this, &DolphinMainWindow::slotGoBack);
+    actionCollection()->addAction(m_backAction->objectName(), m_backAction);
+
+    auto backShortcuts = m_backAction->shortcuts();
     backShortcuts.append(QKeySequence(Qt::Key_Backspace));
-    actionCollection()->setDefaultShortcuts(backAction, backShortcuts);
+    actionCollection()->setDefaultShortcuts(m_backAction, backShortcuts);
 
     DolphinRecentTabsMenu* recentTabsMenu = new DolphinRecentTabsMenu(this);
     actionCollection()->addAction(QStringLiteral("closed_tabs"), recentTabsMenu);
@@ -1374,7 +1510,25 @@ void DolphinMainWindow::setupActions()
         "be undone will ask for your confirmation."));
     undoAction->setEnabled(false); // undo should be disabled by default
 
-    KStandardAction::forward(this, &DolphinMainWindow::goForward, actionCollection());
+    {
+        QScopedPointer<QAction> forwardAction(KStandardAction::forward(nullptr, nullptr, nullptr));
+        m_forwardAction = new KToolBarPopupAction(forwardAction->icon(), forwardAction->text(), actionCollection());
+        m_forwardAction->setObjectName(forwardAction->objectName());
+        m_forwardAction->setShortcuts(forwardAction->shortcuts());
+    }
+    m_forwardAction->setDelayed(true);
+    m_forwardAction->setStickyMenu(false);
+    connect(m_forwardAction, &QAction::triggered, this, &DolphinMainWindow::goForward);
+    connect(m_forwardAction->menu(), &QMenu::aboutToShow, this, &DolphinMainWindow::slotAboutToShowForwardPopupMenu);
+    connect(m_forwardAction->menu(), &QMenu::triggered, this, &DolphinMainWindow::slotGoForward);
+    actionCollection()->addAction(m_forwardAction->objectName(), m_forwardAction);
+    actionCollection()->setDefaultShortcuts(m_forwardAction, m_forwardAction->shortcuts());
+
+    // enable middle-click to open in a new tab
+    auto *middleClickEventFilter = new MiddleClickActionEventFilter(this);
+    connect(middleClickEventFilter, &MiddleClickActionEventFilter::actionMiddleClicked, this, &DolphinMainWindow::slotBackForwardActionMiddleClicked);
+    m_backAction->menu()->installEventFilter(middleClickEventFilter);
+    m_forwardAction->menu()->installEventFilter(middleClickEventFilter);
     KStandardAction::up(this, &DolphinMainWindow::goUp, actionCollection());
     QAction* homeAction = KStandardAction::home(this, &DolphinMainWindow::goHome, actionCollection());
     homeAction->setWhatsThis(xi18nc("@info:whatsthis", "Go to your "
@@ -1399,6 +1553,15 @@ void DolphinMainWindow::setupActions()
     compareFiles->setEnabled(false);
     connect(compareFiles, &QAction::triggered, this, &DolphinMainWindow::compareFiles);
 
+    QAction* openPreferredSearchTool = actionCollection()->addAction(QStringLiteral("open_preferred_search_tool"));
+    openPreferredSearchTool->setText(i18nc("@action:inmenu Tools", "Open Preferred Search Tool"));
+    openPreferredSearchTool->setWhatsThis(xi18nc("@info:whatsthis",
+        "<para>This opens a preferred search tool for the viewed location.</para>"
+        "<para>Use <emphasis>More Search Tools</emphasis> menu to configure it.</para>"));
+    openPreferredSearchTool->setIcon(QIcon::fromTheme(QStringLiteral("search")));
+    actionCollection()->setDefaultShortcut(openPreferredSearchTool, Qt::CTRL + Qt::SHIFT + Qt::Key_F);
+    connect(openPreferredSearchTool, &QAction::triggered, this, &DolphinMainWindow::openPreferredSearchTool);
+
 #ifdef HAVE_TERMINAL
     if (KAuthorized::authorize(QStringLiteral("shell_access"))) {
         QAction* openTerminal = actionCollection()->addAction(QStringLiteral("open_terminal"));
@@ -1406,9 +1569,15 @@ void DolphinMainWindow::setupActions()
         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")));
+        openTerminal->setIcon(QIcon::fromTheme(QStringLiteral("dialog-scripts")));
         actionCollection()->setDefaultShortcut(openTerminal, Qt::SHIFT + Qt::Key_F4);
         connect(openTerminal, &QAction::triggered, this, &DolphinMainWindow::openTerminal);
+
+        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
 
@@ -1444,6 +1613,24 @@ void DolphinMainWindow::setupActions()
     QList<QKeySequence> prevTabKeys = KStandardShortcut::tabPrev();
     prevTabKeys.append(QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_Tab));
 
+    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->setEnabled(false);
+        connect(activateTab, &QAction::triggered, this, [this, i]() { m_tabWidget->activateTab(i); });
+
+        // only add default shortcuts for the first 9 tabs regardless of MaxActivateTabShortcuts
+        if (i < 9) {
+            actionCollection()->setDefaultShortcut(activateTab, QStringLiteral("Alt+%1").arg(i + 1));
+        }
+    }
+
+    QAction* activateLastTab = actionCollection()->addAction(QStringLiteral("activate_last_tab"));
+    activateLastTab->setText(i18nc("@action:inmenu", "Activate 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"));
@@ -1593,7 +1780,7 @@ void DolphinMainWindow::setupDockWidgets()
                 this, &DolphinMainWindow::slotTerminalPanelVisibilityChanged);
 
         QAction* terminalAction = terminalDock->toggleViewAction();
-        createPanelAction(QIcon::fromTheme(QStringLiteral("utilities-terminal")), Qt::Key_F4, terminalAction, QStringLiteral("show_terminal_panel"));
+        createPanelAction(QIcon::fromTheme(QStringLiteral("dialog-scripts")), Qt::Key_F4, terminalAction, QStringLiteral("show_terminal_panel"));
 
         addDockWidget(Qt::BottomDockWidgetArea, terminalDock);
         connect(this, &DolphinMainWindow::urlChanged,
@@ -1695,8 +1882,9 @@ void DolphinMainWindow::setupDockWidgets()
         "</interface> to display it again.</para>") + panelWhatsThis);
 
     // Add actions into the "Panels" menu
-    KActionMenu* panelsMenu = new KActionMenu(i18nc("@action:inmenu View", "Panels"), this);
+    KActionMenu* panelsMenu = new KActionMenu(i18nc("@action:inmenu View", "Show Panels"), this);
     actionCollection()->addAction(QStringLiteral("panels"), panelsMenu);
+    panelsMenu->setIcon(QIcon::fromTheme(QStringLiteral("view-sidetree")));
     panelsMenu->setDelayed(false);
     const KActionCollection* ac = actionCollection();
     panelsMenu->addAction(ac->action(QStringLiteral("show_places_panel")));
@@ -1724,7 +1912,7 @@ void DolphinMainWindow::updateFileAndEditActions()
     if (list.isEmpty()) {
         stateChanged(QStringLiteral("has_no_selection"));
 
-        addToPlacesAction->setText(i18nc("@action:inmenu Add current folder to places", "Add '%1' to Places", m_activeViewContainer->placesText()));
+        addToPlacesAction->setEnabled(true);
     } else {
         stateChanged(QStringLiteral("has_selection"));
 
@@ -1734,13 +1922,12 @@ void DolphinMainWindow::updateFileAndEditActions()
         QAction* cutAction               = col->action(KStandardAction::name(KStandardAction::Cut));
         QAction* deleteWithTrashShortcut = col->action(QStringLiteral("delete_shortcut")); // see DolphinViewActionHandler
         QAction* showTarget              = col->action(QStringLiteral("show_target"));
+        QAction* duplicateAction         = col->action(QStringLiteral("duplicate")); // see DolphinViewActionHandler
 
         if (list.length() == 1 && list.first().isDir()) {
             addToPlacesAction->setEnabled(true);
-            addToPlacesAction->setText(i18nc("@action:inmenu Add current folder to places", "Add '%1' to Places", list.first().name()));
         } else {
             addToPlacesAction->setEnabled(false);
-            addToPlacesAction->setText(i18nc("@action:inmenu Add current folder to places", "Add to Places"));
         }
 
         KFileItemListProperties capabilities(list);
@@ -1752,6 +1939,7 @@ void DolphinMainWindow::updateFileAndEditActions()
         deleteWithTrashShortcut->setEnabled(capabilities.supportsDeleting() && !enableMoveToTrash);
         cutAction->setEnabled(capabilities.supportsMoving());
         showTarget->setEnabled(list.length() == 1 && list.at(0).isLink());
+        duplicateAction->setEnabled(capabilities.supportsWriting());
     }
 }
 
@@ -1794,6 +1982,7 @@ void DolphinMainWindow::createControlButton()
     Q_ASSERT(!m_controlButton);
 
     m_controlButton = new QToolButton(this);
+    m_controlButton->setAccessibleName(i18nc("@action:intoolbar", "Control"));
     m_controlButton->setIcon(QIcon::fromTheme(QStringLiteral("application-menu")));
     m_controlButton->setToolTip(i18nc("@action", "Show menu"));
     m_controlButton->setAttribute(Qt::WidgetAttribute::WA_CustomWhatsThis);
@@ -2137,6 +2326,22 @@ bool DolphinMainWindow::eventFilter(QObject* obj, QEvent* event)
     return false;
 }
 
+void DolphinMainWindow::focusTerminalPanel()
+{
+    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();
+        actionCollection()->action(QStringLiteral("focus_terminal_panel"))->setText(i18nc("@action:inmenu Tools", "Defocus Terminal Panel"));
+    }
+}
+
 DolphinMainWindow::UndoUiInterface::UndoUiInterface() :
     KIO::FileUndoManager::UiInterface()
 {
@@ -2159,10 +2364,6 @@ void DolphinMainWindow::UndoUiInterface::jobError(KIO::Job* job)
 
 bool DolphinMainWindow::isUrlOpen(const QString& url)
 {
-    if (m_tabWidget->getIndexByUrl(QUrl::fromUserInput((url))).first >= 0) {
-        return true;
-    } else {
-        return false;
-    }
+    return m_tabWidget->isUrlOpen(QUrl::fromUserInput((url)));
 }