]> cloud.milkyroute.net Git - dolphin.git/blobdiff - src/dolphinmainwindow.cpp
Use cmakedefine01
[dolphin.git] / src / dolphinmainwindow.cpp
index f14849e6d437eeac1c877da122881b6d9ae32493..1ee9fcb1f9fa4a54e0c4e66472fe07972ce5697a 100644 (file)
@@ -9,7 +9,7 @@
 #include "dolphinmainwindow.h"
 
 #include "dolphinmainwindowadaptor.h"
-#include "config-terminal.h"
+#include "config-dolphin.h"
 #include "global.h"
 #include "dolphinbookmarkhandler.h"
 #include "dolphindockwidget.h"
 #include "dolphinnavigatorswidgetaction.h"
 #include "dolphinnewfilemenu.h"
 #include "dolphinrecenttabsmenu.h"
+#include "dolphinplacesmodelsingleton.h"
 #include "dolphinurlnavigatorscontroller.h"
 #include "dolphinviewcontainer.h"
 #include "dolphintabpage.h"
 #include "middleclickactioneventfilter.h"
 #include "panels/folders/folderspanel.h"
-#include "panels/places/placesitemmodel.h"
 #include "panels/places/placespanel.h"
 #include "panels/terminal/terminalpanel.h"
 #include "settings/dolphinsettingsdialog.h"
 #include <KJobWidgets>
 #include <KLocalizedString>
 #include <KMessageBox>
-#include <KNS3/KMoreToolsMenuFactory>
+#include <KMoreToolsMenuFactory>
 #include <KProtocolInfo>
 #include <KProtocolManager>
 #include <KShell>
+#include <KShortcutsDialog>
 #include <KStandardAction>
 #include <KStartupInfo>
 #include <KSycoca>
+#include <KTerminalLauncherJob>
 #include <KToggleAction>
 #include <KToolBar>
 #include <KToolBarPopupAction>
-#include <KToolInvocation>
 #include <KUrlComboBox>
 #include <KUrlNavigator>
 #include <KWindowSystem>
+#include <KXMLGUIFactory>
+
+#include <kio_version.h>
 
 #include <QApplication>
 #include <QClipboard>
 #include <QCloseEvent>
 #include <QDesktopServices>
 #include <QDialog>
+#include <QDomDocument>
 #include <QFileInfo>
 #include <QLineEdit>
 #include <QMenuBar>
 
 namespace {
     // Used for GeneralSettings::version() to determine whether
-    // an updated version of Dolphin is running.
-    const int CurrentDolphinVersion = 200;
+    // an updated version of Dolphin is running, so as to migrate
+    // removed/renamed ...etc config entries; increment it in such
+    // cases
+    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
@@ -118,13 +125,15 @@ DolphinMainWindow::DolphinMainWindow() :
     setComponentName(QStringLiteral("dolphin"), QGuiApplication::applicationDisplayName());
     setObjectName(QStringLiteral("Dolphin#"));
 
+    setStateConfigGroup("State");
+
     connect(&DolphinNewFileMenuObserver::instance(), &DolphinNewFileMenuObserver::errorMessage,
             this, &DolphinMainWindow::showErrorMessage);
 
     KIO::FileUndoManager* undoManager = KIO::FileUndoManager::self();
     undoManager->setUiInterface(new UndoUiInterface());
 
-    connect(undoManager, QOverload<bool>::of(&KIO::FileUndoManager::undoAvailable),
+    connect(undoManager, &KIO::FileUndoManager::undoAvailable,
             this, &DolphinMainWindow::slotUndoAvailable);
     connect(undoManager, &KIO::FileUndoManager::undoTextChanged,
             this, &DolphinMainWindow::slotUndoTextChanged);
@@ -164,7 +173,7 @@ DolphinMainWindow::DolphinMainWindow() :
 
     setupDockWidgets();
 
-    setupGUI(Keys | Save | Create | ToolBar);
+    setupGUI(Save | Create | ToolBar);
     stateChanged(QStringLiteral("new_file"));
 
     QClipboard* clipboard = QApplication::clipboard();
@@ -176,15 +185,21 @@ DolphinMainWindow::DolphinMainWindow() :
 
     if (firstRun) {
         menuBar()->setVisible(false);
-        // Assure a proper default size if Dolphin runs the first time
-        resize(760, 550);
     }
 
     const bool showMenu = !menuBar()->isHidden();
     QAction* showMenuBarAction = actionCollection()->action(KStandardAction::name(KStandardAction::ShowMenubar));
     showMenuBarAction->setChecked(showMenu);  // workaround for bug #171080
-    if (!showMenu) {
-        createControlButton();
+
+    auto hamburgerMenu = static_cast<KHamburgerMenu *>(actionCollection()->action(
+                                    KStandardAction::name(KStandardAction::HamburgerMenu)));
+    hamburgerMenu->setMenuBar(menuBar());
+    hamburgerMenu->setShowMenuBarAction(showMenuBarAction);
+    connect(hamburgerMenu, &KHamburgerMenu::aboutToShowMenu,
+            this, &DolphinMainWindow::updateHamburgerMenu);
+    hamburgerMenu->hideActionsOf(toolBar());
+    if (GeneralSettings::version() < 201 && !toolBar()->actions().contains(hamburgerMenu)) {
+        addHamburgerMenuToToolbar();
     }
 
     updateAllowedToolbarAreas();
@@ -196,13 +211,23 @@ DolphinMainWindow::DolphinMainWindow() :
 
     setupWhatsThis();
 
-    connect(KSycoca::self(), QOverload<>::of(&KSycoca::databaseChanged), this, &DolphinMainWindow::updateOpenPreferredSearchToolAction);
+    connect(KSycoca::self(), &KSycoca::databaseChanged, this, &DolphinMainWindow::updateOpenPreferredSearchToolAction);
 
     QTimer::singleShot(0, this, &DolphinMainWindow::updateOpenPreferredSearchToolAction);
+
+    m_fileItemActions.setParentWidget(this);
+    connect(&m_fileItemActions, &KFileItemActions::error, this, [this](const QString &errorMessage) {
+        showErrorMessage(errorMessage);
+    });
+
+    connect(GeneralSettings::self(), &GeneralSettings::splitViewChanged,
+            this, &DolphinMainWindow::slotSplitViewChanged);
 }
 
 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);
 }
 
 QVector<DolphinViewContainer*> DolphinMainWindow::viewContainers() const
@@ -220,20 +245,6 @@ QVector<DolphinViewContainer*> DolphinMainWindow::viewContainers() const
     return viewContainers;
 }
 
-void DolphinMainWindow::setViewsWithInvalidPathsToHome()
-{
-    const QVector<DolphinViewContainer*> theViewContainers = viewContainers();
-    for (DolphinViewContainer *viewContainer : theViewContainers) {
-
-        // Only consider local dirs, not remote locations and abstract protocols
-        if (viewContainer->url().isLocalFile()) {
-            if (!QFileInfo::exists(viewContainer->url().toLocalFile())) {
-                viewContainer->setUrl(QUrl::fromLocalFile(QDir::homePath()));
-            }
-        }
-    }
-}
-
 void DolphinMainWindow::openDirectories(const QList<QUrl>& dirs, bool splitView)
 {
     m_tabWidget->openDirectories(dirs, splitView);
@@ -256,7 +267,7 @@ bool DolphinMainWindow::isFoldersPanelEnabled() const
 
 bool DolphinMainWindow::isInformationPanelEnabled() const
 {
-#ifdef HAVE_BALOO
+#if HAVE_BALOO
     return actionCollection()->action(QStringLiteral("show_information_panel"))->isChecked();
 #else
     return false;
@@ -418,14 +429,13 @@ void DolphinMainWindow::addToPlaces()
         name = dirToAdd.name();
     }
     if (url.isValid()) {
-        PlacesItemModel model;
         QString icon;
         if (m_activeViewContainer->isSearchModeEnabled()) {
             icon = QStringLiteral("folder-saved-search-symbolic");
         } else {
             icon = KIO::iconNameForUrl(url);
         }
-        model.createPlacesItem(name, url, icon);
+        DolphinPlacesModelSingleton::instance().placesModel()->addPlace(name, url, icon);
     }
 }
 
@@ -434,6 +444,22 @@ void DolphinMainWindow::openNewTab(const QUrl& url)
     m_tabWidget->openNewTab(url, QUrl());
 }
 
+void DolphinMainWindow::openNewTabAndActivate(const QUrl &url)
+{
+    m_tabWidget->openNewActivatedTab(url, QUrl());
+}
+
+void DolphinMainWindow::openNewWindow(const QUrl &url)
+{
+    Dolphin::openNewWindow({url}, this);
+}
+
+void DolphinMainWindow::slotSplitViewChanged()
+{
+    m_tabWidget->currentTabPage()->setSplitViewEnabled(GeneralSettings::splitView(), WithAnimation);
+    updateSplitAction();
+}
+
 void DolphinMainWindow::openInNewTab()
 {
     const KFileItemList& list = m_activeViewContainer->view()->selectedItems();
@@ -630,14 +656,12 @@ void DolphinMainWindow::readProperties(const KConfigGroup& group)
 
 void DolphinMainWindow::updateNewMenu()
 {
-    m_newFileMenu->setViewShowsHiddenFiles(activeViewContainer()->view()->hiddenFilesShown());
     m_newFileMenu->checkUpToDate();
     m_newFileMenu->setPopupFiles(QList<QUrl>() << activeViewContainer()->url());
 }
 
 void DolphinMainWindow::createDirectory()
 {
-    m_newFileMenu->setViewShowsHiddenFiles(activeViewContainer()->view()->hiddenFilesShown());
     m_newFileMenu->setPopupFiles(QList<QUrl>() << activeViewContainer()->url());
     m_newFileMenu->createDirectory();
 }
@@ -727,13 +751,45 @@ void DolphinMainWindow::slotToolBarActionMiddleClicked(QAction *action)
     }
 }
 
+QAction *DolphinMainWindow::urlNavigatorHistoryAction(const KUrlNavigator *urlNavigator, int historyIndex, QObject *parent)
+{
+    const QUrl url = urlNavigator->locationUrl(historyIndex);
+
+    QString text = url.toDisplayString(QUrl::PreferLocalFile);
+
+    if (!urlNavigator->showFullPath()) {
+        const KFilePlacesModel *placesModel = DolphinPlacesModelSingleton::instance().placesModel();
+
+        const QModelIndex closestIdx = placesModel->closestItem(url);
+        if (closestIdx.isValid()) {
+            const QUrl placeUrl = placesModel->url(closestIdx);
+
+            text = placesModel->text(closestIdx);
+
+            QString pathInsidePlace = url.path().mid(placeUrl.path().length());
+
+            if (!pathInsidePlace.isEmpty() && !pathInsidePlace.startsWith(QLatin1Char('/'))) {
+                pathInsidePlace.prepend(QLatin1Char('/'));
+            }
+
+            if (pathInsidePlace != QLatin1Char('/')) {
+                text.append(pathInsidePlace);
+            }
+        }
+    }
+
+    QAction *action = new QAction(QIcon::fromTheme(KIO::iconNameForUrl(url)), text, parent);
+    action->setData(historyIndex);
+    return action;
+}
+
 void DolphinMainWindow::slotAboutToShowBackPopupMenu()
 {
     const KUrlNavigator *urlNavigator = m_activeViewContainer->urlNavigatorInternalWithHistory();
     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());
+        QAction *action = urlNavigatorHistoryAction(urlNavigator, i, m_backAction->menu());
         action->setData(i);
         m_backAction->menu()->addAction(action);
     }
@@ -762,7 +818,7 @@ void DolphinMainWindow::slotAboutToShowForwardPopupMenu()
     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());
+        QAction *action = urlNavigatorHistoryAction(urlNavigator, i, m_forwardAction->menu());
         action->setData(i);
         m_forwardAction->menu()->addAction(action);
     }
@@ -890,6 +946,8 @@ void DolphinMainWindow::togglePanelLockState()
         }
     }
 
+    DolphinPlacesModelSingleton::instance().placesModel()->setPanelsLocked(newLockState);
+
     GeneralSettings::setLockPanels(newLockState);
 }
 
@@ -979,11 +1037,6 @@ void DolphinMainWindow::toggleShowMenuBar()
 {
     const bool visible = menuBar()->isVisible();
     menuBar()->setVisible(!visible);
-    if (visible) {
-        createControlButton();
-    } else {
-        deleteControlButton();
-    }
 }
 
 QPointer<QAction> DolphinMainWindow::preferredSearchTool()
@@ -1013,7 +1066,11 @@ void DolphinMainWindow::updateOpenPreferredSearchToolAction()
     if (tool) {
         openPreferredSearchTool->setVisible(true);
         openPreferredSearchTool->setText(i18nc("@action:inmenu Tools", "Open %1", tool->text()));
-        openPreferredSearchTool->setIcon(tool->icon());
+        // Only override with the app icon if it is the default, i.e. the user hasn't configured one manually
+        // https://bugs.kde.org/show_bug.cgi?id=442815
+        if (openPreferredSearchTool->icon().name() == QLatin1String("search")) {
+            openPreferredSearchTool->setIcon(tool->icon());
+        }
     } else {
         openPreferredSearchTool->setVisible(false);
         // still visible in Shortcuts configuration window
@@ -1032,10 +1089,49 @@ void DolphinMainWindow::openPreferredSearchTool()
 
 void DolphinMainWindow::openTerminal()
 {
-    const QUrl url = m_activeViewContainer->url();
+    openTerminalJob(m_activeViewContainer->url());
+}
+
+void DolphinMainWindow::openTerminalHere()
+{
+    QList<QUrl> urls = {};
+
+    for (const KFileItem& item : m_activeViewContainer->view()->selectedItems()) {
+        QUrl url = item.targetUrl();
+        if (item.isFile()) {
+            url.setPath(QFileInfo(url.path()).absolutePath());
+        }
+        if (!urls.contains(url)) {
+            urls << url;
+        }
+    }
+
+    // No items are selected. Open a terminal window for the current location.
+    if (urls.count() == 0) {
+        openTerminal();
+        return;
+    }
+
+    if (urls.count() > 5) {
+        QString question = i18np("Are you sure you want to open 1 terminal window?",
+                                 "Are you sure you want to open %1 terminal windows?", urls.count());
+        const int answer = KMessageBox::warningYesNo(this, question);
+        if (answer != KMessageBox::Yes) {
+            return;
+        }
+    }
 
+    for (const QUrl& url : urls) {
+        openTerminalJob(url);
+    }
+}
+
+void DolphinMainWindow::openTerminalJob(const QUrl& url)
+{
     if (url.isLocalFile()) {
-        KToolInvocation::invokeTerminal(QString(), {}, url.toLocalFile());
+        auto job = new KTerminalLauncherJob(QString());
+        job->setWorkingDirectory(url.toLocalFile());
+        job->start();
         return;
     }
 
@@ -1049,14 +1145,18 @@ void DolphinMainWindow::openTerminal()
                 statUrl = job->mostLocalUrl();
             }
 
-            KToolInvocation::invokeTerminal(QString(), {}, statUrl.isLocalFile() ? statUrl.toLocalFile() : QDir::homePath());
+            auto job = new KTerminalLauncherJob(QString());
+            job->setWorkingDirectory(statUrl.isLocalFile() ? statUrl.toLocalFile() : QDir::homePath());
+            job->start();
         });
 
         return;
     }
 
     // Nothing worked, just use $HOME
-    KToolInvocation::invokeTerminal(QString(), {}, QDir::homePath());
+    auto job = new KTerminalLauncherJob(QString());
+    job->setWorkingDirectory(QDir::homePath());
+    job->start();
 }
 
 void DolphinMainWindow::editSettings()
@@ -1117,32 +1217,11 @@ void DolphinMainWindow::slotWriteStateChanged(bool isFolderWritable)
 
 void DolphinMainWindow::openContextMenu(const QPoint& pos,
                                         const KFileItem& item,
-                                        const QUrl& url,
-                                        const QList<QAction*>& customActions)
+                                        const KFileItemList &selectedItems,
+                                        const QUrl& url)
 {
-    QPointer<DolphinContextMenu> contextMenu = new DolphinContextMenu(this, pos, item, url);
-    contextMenu.data()->setCustomActions(customActions);
-    const DolphinContextMenu::Command command = contextMenu.data()->open();
-
-    switch (command) {
-    case DolphinContextMenu::OpenParentFolder:
-        changeUrl(KIO::upUrl(item.url()));
-        m_activeViewContainer->view()->markUrlsAsSelected({item.url()});
-        m_activeViewContainer->view()->markUrlAsCurrent(item.url());
-        break;
-
-    case DolphinContextMenu::OpenParentFolderInNewWindow:
-        Dolphin::openNewWindow({item.url()}, this, Dolphin::OpenNewWindowFlag::Select);
-        break;
-
-    case DolphinContextMenu::OpenParentFolderInNewTab:
-        openNewTab(KIO::upUrl(item.url()));
-        break;
-
-    case DolphinContextMenu::None:
-    default:
-        break;
-    }
+    QPointer<DolphinContextMenu> contextMenu = new DolphinContextMenu(this, item, selectedItems, url, &m_fileItemActions);
+    contextMenu.data()->exec(pos);
 
     // Delete the menu, unless it has been deleted in its own nested event loop already.
     if (contextMenu) {
@@ -1150,87 +1229,101 @@ void DolphinMainWindow::openContextMenu(const QPoint& pos,
     }
 }
 
-void DolphinMainWindow::updateControlMenu()
+QMenu *DolphinMainWindow::createPopupMenu()
 {
-    QMenu* menu = qobject_cast<QMenu*>(sender());
-    Q_ASSERT(menu);
-
-    // All actions get cleared by QMenu::clear(). This includes the sub-menus
-    // because 'menu' is their parent.
-    menu->clear();
-
-    KActionCollection* ac = actionCollection();
-
-    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);
+    QMenu *menu = KXmlGuiWindow::createPopupMenu();
 
     menu->addSeparator();
+    menu->addAction(actionCollection()->action(QStringLiteral("lock_panels")));
 
-    // Add "Edit" actions
-    bool added = addActionToMenu(ac->action(KStandardAction::name(KStandardAction::Undo)), menu) |
-                 addActionToMenu(ac->action(QString("copy_location")), menu) |
-                 addActionToMenu(ac->action(QStringLiteral("copy_to_inactive_split_view")), menu) |
-                 addActionToMenu(ac->action(QStringLiteral("move_to_inactive_split_view")), menu) |
-                 addActionToMenu(ac->action(KStandardAction::name(KStandardAction::SelectAll)), menu) |
-                 addActionToMenu(ac->action(QStringLiteral("invert_selection")), menu);
+    return menu;
+}
 
-    if (added) {
-        menu->addSeparator();
+void DolphinMainWindow::updateHamburgerMenu()
+{
+    KActionCollection* ac = actionCollection();
+    auto hamburgerMenu = static_cast<KHamburgerMenu *>(
+                    ac->action(KStandardAction::name(KStandardAction::HamburgerMenu)));
+    auto menu = hamburgerMenu->menu();
+    if (!menu) {
+        menu = new QMenu(this);
+        hamburgerMenu->setMenu(menu);
+        hamburgerMenu->hideActionsOf(ac->action(QStringLiteral("basic_actions"))->menu());
+        hamburgerMenu->hideActionsOf(ac->action(QStringLiteral("zoom"))->menu());
+    } else {
+        menu->clear();
     }
+    const QList<QAction *> toolbarActions = toolBar()->actions();
 
-    // 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);
+    if (!toolBar()->isVisible()) {
+        // If neither the menu bar nor the toolbar are visible, these actions should be available.
+        menu->addAction(ac->action(KStandardAction::name(KStandardAction::ShowMenubar)));
+        menu->addAction(toolBarMenuAction());
         menu->addSeparator();
     }
 
-    added = addActionToMenu(ac->action(QStringLiteral("show_preview")), menu) |
-            addActionToMenu(ac->action(QStringLiteral("show_in_groups")), menu) |
-            addActionToMenu(ac->action(QStringLiteral("show_hidden_files")), menu) |
-            addActionToMenu(ac->action(QStringLiteral("additional_info")), menu) |
-            addActionToMenu(ac->action(QStringLiteral("view_properties")), menu);
+    // This group of actions (until the next separator) contains all the most basic actions
+    // necessary to use Dolphin effectively.
+    menu->addAction(ac->action(QStringLiteral("go_back")));
+    menu->addAction(ac->action(QStringLiteral("go_forward")));
 
-    if (added) {
-        menu->addSeparator();
+    menu->addMenu(m_newFileMenu->menu());
+    menu->addAction(ac->action(QStringLiteral("basic_actions")));
+    menu->addAction(ac->action(KStandardAction::name(KStandardAction::Undo)));
+    if (!toolBar()->isVisible()
+        || (!toolbarActions.contains(ac->action(QStringLiteral("toggle_search")))
+            && !toolbarActions.contains(ac->action(QStringLiteral("open_preferred_search_tool"))))
+    ) {
+        menu->addAction(ac->action(KStandardAction::name(KStandardAction::Find)));
+        // This way a search action will only be added if none of the three available
+        // search actions is present on the toolbar.
+    }
+    if (!toolBar()->isVisible()
+        || !toolbarActions.contains(ac->action(QStringLiteral("toggle_filter")))
+    ) {
+        menu->addAction(ac->action(QStringLiteral("show_filter_bar")));
+        // This way a filter action will only be added if none of the two available
+        // filter actions is present on the toolbar.
     }
-
-    // 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);
-
     menu->addSeparator();
 
-    // 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
-    auto helpMenu = m_helpMenu->menu();
-    helpMenu->setIcon(QIcon::fromTheme(QStringLiteral("system-help")));
-    menu->addMenu(helpMenu);
-}
-
-void DolphinMainWindow::updateToolBar()
-{
-    if (!menuBar()->isVisible()) {
-        createControlButton();
+    // The second group of actions (up until the next separator) contains actions for opening
+    // additional views to interact with the file system.
+    menu->addAction(ac->action(QStringLiteral("file_new")));
+    menu->addAction(ac->action(QStringLiteral("new_tab")));
+    if (ac->action(QStringLiteral("undo_close_tab"))->isEnabled()) {
+        menu->addAction(ac->action(QStringLiteral("closed_tabs")));
     }
-}
+    menu->addAction(ac->action(QStringLiteral("open_terminal")));
+    menu->addSeparator();
 
-void DolphinMainWindow::slotControlButtonDeleted()
-{
-    m_controlButton = nullptr;
-    m_updateToolBarTimer->start();
+    // 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"))))
+    ) {
+        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")));
+    }
+    menu->addAction(ac->action(QStringLiteral("panels")));
+
+    // The "Configure" menu is not added to the actionCollection() because there is hardly
+    // a good reason for users to put it on their toolbar.
+    auto configureMenu = menu->addMenu(QIcon::fromTheme(QStringLiteral("configure")),
+                            i18nc("@action:inmenu menu for configure actions", "Configure"));
+    configureMenu->addAction(ac->action(KStandardAction::name(KStandardAction::SwitchApplicationLanguage)));
+    configureMenu->addAction(ac->action(KStandardAction::name(KStandardAction::KeyBindings)));
+    configureMenu->addAction(ac->action(KStandardAction::name(KStandardAction::ConfigureToolbars)));
+    configureMenu->addAction(ac->action(KStandardAction::name(KStandardAction::Preferences)));
+    hamburgerMenu->hideActionsOf(configureMenu);
 }
 
 void DolphinMainWindow::slotPlaceActivated(const QUrl& url)
@@ -1268,6 +1361,7 @@ void DolphinMainWindow::activeViewChanged(DolphinViewContainer* viewContainer)
         // view and url navigator) and main window.
         oldViewContainer->disconnect(this);
         oldViewContainer->view()->disconnect(this);
+        oldViewContainer->urlNavigatorInternalWithHistory()->disconnect(this);
         auto navigators = static_cast<DolphinNavigatorsWidgetAction *>
                           (actionCollection()->action(QStringLiteral("url_navigators")));
         navigators->primaryUrlNavigator()->disconnect(this);
@@ -1341,6 +1435,19 @@ void DolphinMainWindow::slotStorageTearDownExternallyRequested(const QString& mo
     }
 }
 
+void DolphinMainWindow::slotKeyBindings()
+{
+    KShortcutsDialog dialog(KShortcutsEditor::AllActions, KShortcutsEditor::LetterShortcutsAllowed, this);
+    dialog.addCollection(actionCollection());
+    if (m_terminalPanel) {
+        KActionCollection *konsolePartActionCollection = m_terminalPanel->actionCollection();
+        if (konsolePartActionCollection) {
+            dialog.addCollection(konsolePartActionCollection, QStringLiteral("KonsolePart"));
+        }
+    }
+    dialog.configure();
+}
+
 void DolphinMainWindow::setViewsToHomeIfMountPathOpen(const QString& mountPath)
 {
     const QVector<DolphinViewContainer*> theViewContainers = viewContainers();
@@ -1354,12 +1461,14 @@ void DolphinMainWindow::setViewsToHomeIfMountPathOpen(const QString& mountPath)
 
 void DolphinMainWindow::setupActions()
 {
+    KStandardAction::hamburgerMenu(nullptr, nullptr, actionCollection());
+
     // setup 'File' menu
     m_newFileMenu = new DolphinNewFileMenu(actionCollection(), this);
     QMenu* menu = m_newFileMenu->menu();
     menu->setTitle(i18nc("@title:menu Create new folder, file, link, etc.", "Create New"));
-    menu->setIcon(QIcon::fromTheme(QStringLiteral("document-new")));
-    m_newFileMenu->setDelayed(false);
+    menu->setIcon(QIcon::fromTheme(QStringLiteral("list-add")));
+    m_newFileMenu->setPopupMode(QToolButton::InstantPopup);
     connect(menu, &QMenu::aboutToShow,
             this, &DolphinMainWindow::updateNewMenu);
 
@@ -1451,7 +1560,7 @@ void DolphinMainWindow::setupActions()
 
     QAction* showFilterBar = actionCollection()->addAction(QStringLiteral("show_filter_bar"));
     showFilterBar->setText(i18nc("@action:inmenu Tools", "Filter..."));
-    showFilterBar->setToolTip(i18nc("@info:tooltip", "Toggle Filter Bar"));
+    showFilterBar->setToolTip(i18nc("@info:tooltip", "Show Filter Bar"));
     showFilterBar->setWhatsThis(xi18nc("@info:whatsthis", "This opens the "
         "<emphasis>Filter Bar</emphasis> at the bottom of the window.<nl/> "
         "There you can enter a text to filter the files and folders currently displayed. "
@@ -1522,7 +1631,8 @@ void DolphinMainWindow::setupActions()
     stashSplit->setToolTip(i18nc("@info", "Opens the stash virtual directory in a split window"));
     stashSplit->setIcon(QIcon::fromTheme(QStringLiteral("folder-stash")));
     stashSplit->setCheckable(false);
-    stashSplit->setVisible(QDBusConnection::sessionBus().interface()->isServiceRegistered(QStringLiteral("org.kde.kio.StashNotifier")));
+    QDBusConnectionInterface *sessionInterface = QDBusConnection::sessionBus().interface();
+    stashSplit->setVisible(sessionInterface && sessionInterface->isServiceRegistered(QStringLiteral("org.kde.kio.StashNotifier")));
     connect(stashSplit, &QAction::triggered, this, &DolphinMainWindow::toggleSplitStash);
 
     KStandardAction::redisplay(this, &DolphinMainWindow::reloadView, actionCollection());
@@ -1562,8 +1672,7 @@ void DolphinMainWindow::setupActions()
         m_backAction->setObjectName(backAction->objectName());
         m_backAction->setShortcuts(backAction->shortcuts());
     }
-    m_backAction->setDelayed(true);
-    m_backAction->setStickyMenu(false);
+    m_backAction->setPopupMode(QToolButton::DelayedPopup);
     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);
@@ -1606,8 +1715,7 @@ void DolphinMainWindow::setupActions()
         m_forwardAction->setObjectName(forwardAction->objectName());
         m_forwardAction->setShortcuts(forwardAction->shortcuts());
     }
-    m_forwardAction->setDelayed(true);
-    m_forwardAction->setStickyMenu(false);
+    m_forwardAction->setPopupMode(QToolButton::DelayedPopup);
     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);
@@ -1652,7 +1760,17 @@ void DolphinMainWindow::setupActions()
         actionCollection()->setDefaultShortcut(openTerminal, Qt::SHIFT | Qt::Key_F4);
         connect(openTerminal, &QAction::triggered, this, &DolphinMainWindow::openTerminal);
 
-#ifdef HAVE_TERMINAL
+        QAction* openTerminalHere = actionCollection()->addAction(QStringLiteral("open_terminal_here"));
+        // i18n: "Here" refers to the location(s) of the currently selected item(s) or the currently viewed location if nothing is selected.
+        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")));
+        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")));
@@ -1665,7 +1783,7 @@ void DolphinMainWindow::setupActions()
     KActionMenu *bookmarkMenu = new KActionMenu(i18nc("@title:menu", "&Bookmarks"), this);
     bookmarkMenu->setIcon(QIcon::fromTheme(QStringLiteral("bookmarks")));
     // Make the toolbar button version work properly on click
-    bookmarkMenu->setDelayed(false);
+    bookmarkMenu->setPopupMode(QToolButton::InstantPopup);
     m_bookmarkHandler = new DolphinBookmarkHandler(this, actionCollection(), bookmarkMenu->menu(), this);
     actionCollection()->addAction(QStringLiteral("bookmarks"), bookmarkMenu);
 
@@ -1677,14 +1795,20 @@ void DolphinMainWindow::setupActions()
             "contain mostly the same commands and configuration options."));
     connect(showMenuBar, &KToggleAction::triggered,                   // Fixes #286822
             this, &DolphinMainWindow::toggleShowMenuBar, Qt::QueuedConnection);
+    KStandardAction::keyBindings(this, &DolphinMainWindow::slotKeyBindings, actionCollection());
     KStandardAction::preferences(this, &DolphinMainWindow::editSettings, actionCollection());
 
     // setup 'Help' menu for the m_controlButton. The other one is set up in the base class.
     m_helpMenu = new KHelpMenu(nullptr);
     m_helpMenu->menu()->installEventFilter(this);
     // remove duplicate shortcuts
-    m_helpMenu->action(KHelpMenu::menuHelpContents)->setShortcut(QKeySequence());
-    m_helpMenu->action(KHelpMenu::menuWhatsThis)->setShortcut(QKeySequence());
+    auto removeHelpActionShortcut = [this](KHelpMenu::MenuId menuId) {
+        if (auto *action = m_helpMenu->action(menuId)) {
+            action->setShortcut(QKeySequence());
+        }
+    };
+    removeHelpActionShortcut(KHelpMenu::menuHelpContents);
+    removeHelpActionShortcut(KHelpMenu::menuWhatsThis);
 
     // not in menu actions
     QList<QKeySequence> nextTabKeys = KStandardShortcut::tabNext();
@@ -1752,6 +1876,8 @@ void DolphinMainWindow::setupDockWidgets()
 {
     const bool lock = GeneralSettings::lockPanels();
 
+    DolphinPlacesModelSingleton::instance().placesModel()->setPanelsLocked(lock);
+
     KDualAction* lockLayoutAction = actionCollection()->add<KDualAction>(QStringLiteral("lock_panels"));
     lockLayoutAction->setActiveText(i18nc("@action:inmenu Panels", "Unlock Panels"));
     lockLayoutAction->setActiveIcon(QIcon::fromTheme(QStringLiteral("object-unlocked")));
@@ -1771,7 +1897,7 @@ void DolphinMainWindow::setupDockWidgets()
     infoDock->setObjectName(QStringLiteral("infoDock"));
     infoDock->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea);
 
-#ifdef HAVE_BALOO
+#if HAVE_BALOO
     InformationPanel* infoPanel = new InformationPanel(infoDock);
     infoPanel->setCustomContextMenuActions({lockLayoutAction});
     connect(infoPanel, &InformationPanel::urlActivated, this, &DolphinMainWindow::handleUrl);
@@ -1795,7 +1921,7 @@ void DolphinMainWindow::setupDockWidgets()
     const QString panelWhatsThis = xi18nc("@info:whatsthis", "<para>To show or "
         "hide panels like this go to <interface>Control|Panels</interface> "
         "or <interface>View|Panels</interface>.</para>");
-#ifdef HAVE_BALOO
+#if HAVE_BALOO
     actionCollection()->action(QStringLiteral("show_information_panel"))
         ->setWhatsThis(xi18nc("@info:whatsthis", "<para> This toggles the "
         "<emphasis>information</emphasis> panel at the right side of the "
@@ -1828,8 +1954,10 @@ void DolphinMainWindow::setupDockWidgets()
             foldersPanel, &FoldersPanel::setUrl);
     connect(foldersPanel, &FoldersPanel::folderActivated,
             this, &DolphinMainWindow::changeUrl);
-    connect(foldersPanel, &FoldersPanel::folderMiddleClicked,
+    connect(foldersPanel, &FoldersPanel::folderInNewTab,
             this, &DolphinMainWindow::openNewTab);
+    connect(foldersPanel, &FoldersPanel::folderInNewActiveTab,
+            this, &DolphinMainWindow::openNewTabAndActivate);
     connect(foldersPanel, &FoldersPanel::errorMessage,
             this, &DolphinMainWindow::showErrorMessage);
 
@@ -1845,7 +1973,7 @@ void DolphinMainWindow::setupDockWidgets()
         "This allows quick switching between any folders.</para>") + panelWhatsThis);
 
     // Setup "Terminal"
-#ifdef HAVE_TERMINAL
+#if HAVE_TERMINAL
     if (KAuthorized::authorize(QStringLiteral("shell_access"))) {
         DolphinDockWidget* terminalDock = new DolphinDockWidget(i18nc("@title:window Shell terminal", "Terminal"));
         terminalDock->setLocked(lock);
@@ -1911,8 +2039,13 @@ void DolphinMainWindow::setupDockWidgets()
     addDockWidget(Qt::LeftDockWidgetArea, placesDock);
     connect(m_placesPanel, &PlacesPanel::placeActivated,
             this, &DolphinMainWindow::slotPlaceActivated);
-    connect(m_placesPanel, &PlacesPanel::placeMiddleClicked,
+    connect(m_placesPanel, &PlacesPanel::tabRequested,
             this, &DolphinMainWindow::openNewTab);
+    connect(m_placesPanel, &PlacesPanel::activeTabRequested,
+            this, &DolphinMainWindow::openNewTabAndActivate);
+    connect(m_placesPanel, &PlacesPanel::newWindowRequested, this, [this](const QUrl &url) {
+        Dolphin::openNewWindow({url}, this);
+    });
     connect(m_placesPanel, &PlacesPanel::errorMessage,
             this, &DolphinMainWindow::showErrorMessage);
     connect(this, &DolphinMainWindow::urlChanged,
@@ -1935,14 +2068,9 @@ void DolphinMainWindow::setupDockWidgets()
         "appear semi-transparent unless you uncheck their hide property."));
 
     connect(actionShowAllPlaces, &QAction::triggered, this, [actionShowAllPlaces, this](bool checked){
-        actionShowAllPlaces->setIcon(QIcon::fromTheme(checked ? QStringLiteral("view-visible") : QStringLiteral("view-hidden")));
-        m_placesPanel->showHiddenEntries(checked);
+        m_placesPanel->setShowAll(checked);
     });
-
-    connect(m_placesPanel, &PlacesPanel::showHiddenEntriesChanged, this, [actionShowAllPlaces] (bool checked){
-        actionShowAllPlaces->setChecked(checked);
-        actionShowAllPlaces->setIcon(QIcon::fromTheme(checked ? QStringLiteral("view-visible") : QStringLiteral("view-hidden")));
-   });
+    connect(m_placesPanel, &PlacesPanel::allPlacesShownChanged, actionShowAllPlaces, &QAction::setChecked);
 
     actionCollection()->action(QStringLiteral("show_places_panel"))
         ->setWhatsThis(xi18nc("@info:whatsthis", "<para>This toggles the "
@@ -1967,10 +2095,10 @@ void DolphinMainWindow::setupDockWidgets()
     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);
+    panelsMenu->setPopupMode(QToolButton::InstantPopup);
     const KActionCollection* ac = actionCollection();
     panelsMenu->addAction(ac->action(QStringLiteral("show_places_panel")));
-#ifdef HAVE_BALOO
+#if HAVE_BALOO
     panelsMenu->addAction(ac->action(QStringLiteral("show_information_panel")));
 #endif
     panelsMenu->addAction(ac->action(QStringLiteral("show_folders_panel")));
@@ -1980,7 +2108,7 @@ void DolphinMainWindow::setupDockWidgets()
     panelsMenu->addAction(lockLayoutAction);
 
     connect(panelsMenu->menu(), &QMenu::aboutToShow, this, [actionShowAllPlaces, this]{
-        actionShowAllPlaces->setEnabled(m_placesPanel->hiddenListCount());
+        actionShowAllPlaces->setEnabled(DolphinPlacesModelSingleton::instance().placesModel()->hiddenCount());
     });
 }
 
@@ -2077,75 +2205,11 @@ void DolphinMainWindow::updateGoActions()
     goUpAction->setEnabled(KIO::upUrl(currentUrl) != currentUrl);
 }
 
-void DolphinMainWindow::createControlButton()
-{
-    if (m_controlButton) {
-        return;
-    }
-    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);
-    m_controlButton->setPopupMode(QToolButton::InstantPopup);
-
-    QMenu* controlMenu = new QMenu(m_controlButton);
-    connect(controlMenu, &QMenu::aboutToShow, this, &DolphinMainWindow::updateControlMenu);
-    controlMenu->installEventFilter(this);
-
-    m_controlButton->setMenu(controlMenu);
-
-    toolBar()->addWidget(m_controlButton);
-    connect(toolBar(), &KToolBar::iconSizeChanged,
-            m_controlButton, &QToolButton::setIconSize);
-
-    // The added widgets are owned by the toolbar and may get deleted when e.g. the toolbar
-    // gets edited. In this case we must add them again. The adding is done asynchronously by
-    // m_updateToolBarTimer.
-    connect(m_controlButton, &QToolButton::destroyed, this, &DolphinMainWindow::slotControlButtonDeleted);
-    m_updateToolBarTimer = new QTimer(this);
-    m_updateToolBarTimer->setInterval(500);
-    connect(m_updateToolBarTimer, &QTimer::timeout, this, &DolphinMainWindow::updateToolBar);
-}
-
-void DolphinMainWindow::deleteControlButton()
-{
-    delete m_controlButton;
-    m_controlButton = nullptr;
-
-    delete m_updateToolBarTimer;
-    m_updateToolBarTimer = nullptr;
-}
-
-bool DolphinMainWindow::addActionToMenu(QAction* action, QMenu* menu)
-{
-    Q_ASSERT(action);
-    Q_ASSERT(menu);
-
-    const KToolBar* toolBarWidget = toolBar();
-    const auto associatedWidgets = action->associatedWidgets();
-    for (const QWidget* widget : associatedWidgets) {
-        if (widget == toolBarWidget) {
-            return false;
-        }
-    }
-
-    menu->addAction(action);
-    return true;
-}
-
 void DolphinMainWindow::refreshViews()
 {
     m_tabWidget->refreshViews();
 
     if (GeneralSettings::modifiedStartupSettings()) {
-        // The startup settings have been changed by the user (see bug #254947).
-        // Synchronize the split-view setting with the active view:
-        const bool splitView = GeneralSettings::splitView();
-        m_tabWidget->currentTabPage()->setSplitViewEnabled(splitView, WithAnimation);
-        updateSplitAction();
         updateWindowTitle();
     }
 
@@ -2165,6 +2229,12 @@ void DolphinMainWindow::connectViewSignals(DolphinViewContainer* container)
             this, &DolphinMainWindow::slotWriteStateChanged);
     connect(container, &DolphinViewContainer::searchModeEnabledChanged,
             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);
@@ -2178,6 +2248,10 @@ void DolphinMainWindow::connectViewSignals(DolphinViewContainer* container)
             this, &DolphinMainWindow::fileItemsChanged);
     connect(view, &DolphinView::tabRequested,
             this, &DolphinMainWindow::openNewTab);
+    connect(view, &DolphinView::activeTabRequested,
+            this, &DolphinMainWindow::openNewTabAndActivate);
+    connect(view, &DolphinView::windowRequested,
+            this, &DolphinMainWindow::openNewWindow);
     connect(view, &DolphinView::requestContextMenu,
             this, &DolphinMainWindow::openContextMenu);
     connect(view, &DolphinView::directoryLoadingStarted,
@@ -2195,26 +2269,28 @@ void DolphinMainWindow::connectViewSignals(DolphinViewContainer* container)
     connect(view, &DolphinView::goUpRequested,
             this, &DolphinMainWindow::goUp);
 
+    connect(container->urlNavigatorInternalWithHistory(), &KUrlNavigator::urlChanged,
+            this, &DolphinMainWindow::changeUrl);
+    connect(container->urlNavigatorInternalWithHistory(), &KUrlNavigator::historyChanged,
+            this, &DolphinMainWindow::updateHistory);
+
     auto navigators = static_cast<DolphinNavigatorsWidgetAction *>
         (actionCollection()->action(QStringLiteral("url_navigators")));
-
     const KUrlNavigator *navigator = m_tabWidget->currentTabPage()->primaryViewActive() ?
                                      navigators->primaryUrlNavigator() :
                                      navigators->secondaryUrlNavigator();
 
-    connect(navigator, &KUrlNavigator::urlChanged,
-            this, &DolphinMainWindow::changeUrl);
     QAction *editableLocactionAction = actionCollection()->action(QStringLiteral("editable_location"));
     editableLocactionAction->setChecked(navigator->isUrlEditable());
     connect(navigator, &KUrlNavigator::editableStateChanged,
             this, &DolphinMainWindow::slotEditableStateChanged);
     connect(navigator, &KUrlNavigator::tabRequested,
             this, &DolphinMainWindow::openNewTab);
+    connect(navigator, &KUrlNavigator::activeTabRequested,
+            this, &DolphinMainWindow::openNewTabAndActivate);
+    connect(navigator, &KUrlNavigator::newWindowRequested,
+            this, &DolphinMainWindow::openNewWindow);
 
-    disconnect(m_updateHistoryConnection);
-    m_updateHistoryConnection = connect(
-            container->urlNavigatorInternalWithHistory(), &KUrlNavigator::historyChanged,
-            this, &DolphinMainWindow::updateHistory);
 }
 
 void DolphinMainWindow::updateSplitAction()
@@ -2276,6 +2352,7 @@ void DolphinMainWindow::createPanelAction(const QIcon& icon,
     panelAction->setChecked(dockAction->isChecked());
     panelAction->setText(dockAction->text());
     panelAction->setIcon(icon);
+    dockAction->setIcon(icon);
     actionCollection()->setDefaultShortcut(panelAction, shortcut);
 
     connect(panelAction, &QAction::triggered, dockAction, &QAction::trigger);
@@ -2341,6 +2418,18 @@ void DolphinMainWindow::setupWhatsThis()
     // StandardAction separately because both are used in different locations.
     // m_helpMenu is only used for createControlButton() button.
 
+    auto setStandardActionWhatsThis = [this](KStandardAction::StandardAction actionId,
+                                             const QString &whatsThis) {
+        if (auto *action = actionCollection()->action(KStandardAction::name(actionId))) {
+            action->setWhatsThis(whatsThis);
+        }
+    };
+    auto setHelpActionWhatsThis = [this](KHelpMenu::MenuId menuId, const QString &whatsThis) {
+        if (auto *action = m_helpMenu->action(menuId)) {
+            action->setWhatsThis(whatsThis);
+        }
+    };
+
     // Links do not work within the Menubar so texts without links are provided there.
 
     // i18n: If the external link isn't available in your language you should
@@ -2351,13 +2440,12 @@ void DolphinMainWindow::setupWhatsThis()
     const QString whatsThisHelpContents = xi18nc("@info:whatsthis handbook",
         "<para>This opens the Handbook for this application. It provides "
         "explanations for every part of <emphasis>Dolphin</emphasis>.</para>");
-    actionCollection()->action(KStandardAction::name(KStandardAction::HelpContents))
-        ->setWhatsThis(whatsThisHelpContents
+    setStandardActionWhatsThis(KStandardAction::HelpContents, whatsThisHelpContents
         + xi18nc("@info:whatsthis second half of handbook hb text without link",
         "<para>If you want more elaborate introductions to the "
         "different features of <emphasis>Dolphin</emphasis> "
         "go to the KDE UserBase Wiki.</para>"));
-    m_helpMenu->action(KHelpMenu::menuHelpContents)->setWhatsThis(whatsThisHelpContents
+    setHelpActionWhatsThis(KHelpMenu::menuHelpContents, whatsThisHelpContents
         + xi18nc("@info:whatsthis second half of handbook text with link",
         "<para>If you want more elaborate introductions to the "
         "different features of <emphasis>Dolphin</emphasis> "
@@ -2369,8 +2457,7 @@ void DolphinMainWindow::setupWhatsThis()
         "using right now! Click it, then click any component of this "
         "application to ask \"What's this?\" about it. The mouse cursor "
         "will change appearance if no help is available for a spot.</para>");
-    actionCollection()->action(KStandardAction::name(KStandardAction::WhatsThis))
-       ->setWhatsThis(whatsThisWhatsThis
+    setStandardActionWhatsThis(KStandardAction::WhatsThis, whatsThisWhatsThis
         + xi18nc("@info:whatsthis second half of whatsthis button text without link",
         "<para>There are two other ways to get help for this application: The "
         "<interface>Dolphin Handbook</interface> in the <interface>Help"
@@ -2378,7 +2465,7 @@ void DolphinMainWindow::setupWhatsThis()
         "article about <emphasis>File Management</emphasis> online."
         "</para><para>The \"What's this?\" help is "
         "missing in most other windows so don't get too used to this.</para>"));
-    m_helpMenu->action(KHelpMenu::menuWhatsThis)->setWhatsThis(whatsThisWhatsThis
+    setHelpActionWhatsThis(KHelpMenu::menuWhatsThis, whatsThisWhatsThis
         + xi18nc("@info:whatsthis second half of whatsthis button text with link",
         "<para>There are two other ways to get help: "
         "The <link url='help:/dolphin/index.html'>Dolphin Handbook</link> and "
@@ -2389,9 +2476,8 @@ void DolphinMainWindow::setupWhatsThis()
     const QString whatsThisReportBug = xi18nc("@info:whatsthis","<para>This opens a "
         "window that will guide you through reporting errors or flaws "
         "in this application or in other KDE software.</para>");
-    actionCollection()->action(KStandardAction::name(KStandardAction::ReportBug))
-       ->setWhatsThis(whatsThisReportBug);
-    m_helpMenu->action(KHelpMenu::menuReportBug)->setWhatsThis(whatsThisReportBug
+    setStandardActionWhatsThis(KStandardAction::ReportBug, whatsThisReportBug);
+    setHelpActionWhatsThis(KHelpMenu::menuReportBug, whatsThisReportBug
         + xi18nc("@info:whatsthis second half of reportbug text with link",
         "<para>High-quality bug reports are much appreciated. To learn "
         "how to make your bug report as effective as possible "
@@ -2408,33 +2494,53 @@ void DolphinMainWindow::setupWhatsThis()
         "require money like servers, contributor meetings, etc.</para>"
         "<para><emphasis>KDE e.V.</emphasis> is the non-profit "
         "organization behind the KDE community.</para>");
-    actionCollection()->action(KStandardAction::name(KStandardAction::Donate))
-       ->setWhatsThis(whatsThisDonate);
-    m_helpMenu->action(KHelpMenu::menuDonate)->setWhatsThis(whatsThisDonate);
+    setStandardActionWhatsThis(KStandardAction::Donate, whatsThisDonate);
+    setHelpActionWhatsThis(KHelpMenu::menuDonate, whatsThisDonate);
 
     const QString whatsThisSwitchLanguage = xi18nc("@info:whatsthis",
         "With this you can change the language this application uses."
         "<nl/>You can even set secondary languages which will be used "
         "if texts are not available in your preferred language.");
-    actionCollection()->action(KStandardAction::name(KStandardAction::SwitchApplicationLanguage))
-       ->setWhatsThis(whatsThisSwitchLanguage);
-    m_helpMenu->action(KHelpMenu::menuSwitchLanguage)->setWhatsThis(whatsThisSwitchLanguage);
+    setStandardActionWhatsThis(KStandardAction::SwitchApplicationLanguage,
+                               whatsThisSwitchLanguage);
+    setHelpActionWhatsThis(KHelpMenu::menuSwitchLanguage, whatsThisSwitchLanguage);
 
     const QString whatsThisAboutApp = xi18nc("@info:whatsthis","This opens a "
         "window that informs you about the version, license, "
         "used libraries and maintainers of this application.");
-    actionCollection()->action(KStandardAction::name(KStandardAction::AboutApp))
-       ->setWhatsThis(whatsThisAboutApp);
-    m_helpMenu->action(KHelpMenu::menuAboutApp)->setWhatsThis(whatsThisAboutApp);
+    setStandardActionWhatsThis(KStandardAction::AboutApp, whatsThisAboutApp);
+    setHelpActionWhatsThis(KHelpMenu::menuAboutApp, whatsThisAboutApp);
 
     const QString whatsThisAboutKDE = xi18nc("@info:whatsthis","This opens a "
         "window with information about <emphasis>KDE</emphasis>. "
         "The KDE community are the people behind this free software."
         "<nl/>If you like using this application but don't know "
         "about KDE or want to see a cute dragon have a look!");
-    actionCollection()->action(KStandardAction::name(KStandardAction::AboutKDE))
-       ->setWhatsThis(whatsThisAboutKDE);
-    m_helpMenu->action(KHelpMenu::menuAboutKDE)->setWhatsThis(whatsThisAboutKDE);
+    setStandardActionWhatsThis(KStandardAction::AboutKDE, whatsThisAboutKDE);
+    setHelpActionWhatsThis(KHelpMenu::menuAboutKDE, whatsThisAboutKDE);
+}
+
+bool DolphinMainWindow::addHamburgerMenuToToolbar()
+{
+    QDomDocument domDocument = KXMLGUIClient::domDocument();
+    if (domDocument.isNull()) {
+        return false;
+    }
+    QDomNode toolbar = domDocument.elementsByTagName(QStringLiteral("ToolBar")).at(0);
+    if (toolbar.isNull()) {
+        return false;
+    }
+
+    QDomElement hamburgerMenuElement = domDocument.createElement(QStringLiteral("Action"));
+    hamburgerMenuElement.setAttribute(QStringLiteral("name"), QStringLiteral("hamburger_menu"));
+    toolbar.appendChild(hamburgerMenuElement);
+
+    KXMLGUIFactory::saveConfigFile(domDocument, xmlFile());
+    reloadXML();
+    createGUI();
+    return true;
+    // Make sure to also remove the <KXMLGUIFactory> and <QDomDocument> include
+    // whenever this method is removed (maybe in the year ~2026).
 }
 
 bool DolphinMainWindow::event(QEvent *event)
@@ -2460,6 +2566,12 @@ bool DolphinMainWindow::eventFilter(QObject* obj, QEvent* event)
     return false;
 }
 
+// Set a sane initial window size
+QSize DolphinMainWindow::sizeHint() const
+{
+    return KXmlGuiWindow::sizeHint().expandedTo(QSize(760, 550));
+}
+
 void DolphinMainWindow::saveNewToolbarConfig()
 {
     KXmlGuiWindow::saveNewToolbarConfig(); // Applies the new config. This has to be called first
@@ -2471,6 +2583,8 @@ void DolphinMainWindow::saveNewToolbarConfig()
         m_tabWidget->currentTabPage()->insertNavigatorsWidget(navigators);
     }
     updateAllowedToolbarAreas();
+    (static_cast<KHamburgerMenu *>(actionCollection()->action(KStandardAction::name(
+                            KStandardAction::HamburgerMenu))))->hideActionsOf(toolBar());
 }
 
 void DolphinMainWindow::focusTerminalPanel()