File indexing completed on 2024-04-28 09:40:54

0001 /*
0002  * SPDX-FileCopyrightText: 2006 Peter Penz <peter.penz19@gmail.com>
0003  * SPDX-FileCopyrightText: 2006 Stefan Monov <logixoul@gmail.com>
0004  * SPDX-FileCopyrightText: 2006 Cvetoslav Ludmiloff <ludmiloff@gmail.com>
0005  *
0006  * SPDX-License-Identifier: GPL-2.0-or-later
0007  */
0008 
0009 #include "dolphinmainwindow.h"
0010 
0011 #include "dolphin_generalsettings.h"
0012 #include "dolphinbookmarkhandler.h"
0013 #include "dolphincontextmenu.h"
0014 #include "dolphindockwidget.h"
0015 #include "dolphinmainwindowadaptor.h"
0016 #include "dolphinnavigatorswidgetaction.h"
0017 #include "dolphinnewfilemenu.h"
0018 #include "dolphinplacesmodelsingleton.h"
0019 #include "dolphinrecenttabsmenu.h"
0020 #include "dolphintabpage.h"
0021 #include "dolphinurlnavigatorscontroller.h"
0022 #include "dolphinviewcontainer.h"
0023 #include "global.h"
0024 #include "middleclickactioneventfilter.h"
0025 #include "panels/folders/folderspanel.h"
0026 #include "panels/places/placespanel.h"
0027 #include "panels/terminal/terminalpanel.h"
0028 #include "selectionmode/actiontexthelper.h"
0029 #include "settings/dolphinsettingsdialog.h"
0030 #include "statusbar/dolphinstatusbar.h"
0031 #include "views/dolphinnewfilemenuobserver.h"
0032 #include "views/dolphinremoteencoding.h"
0033 #include "views/dolphinviewactionhandler.h"
0034 #include "views/draganddrophelper.h"
0035 #include "views/viewproperties.h"
0036 
0037 #include <KActionCollection>
0038 #include <KActionMenu>
0039 #include <KAuthorized>
0040 #include <KConfig>
0041 #include <KConfigGui>
0042 #include <KDualAction>
0043 #include <KFileItemListProperties>
0044 #include <KIO/CommandLauncherJob>
0045 #include <KIO/JobUiDelegateFactory>
0046 #include <KIO/OpenFileManagerWindowJob>
0047 #include <KIO/OpenUrlJob>
0048 #include <KJobWidgets>
0049 #include <KLocalizedString>
0050 #include <KMessageBox>
0051 #include <KProtocolInfo>
0052 #include <KProtocolManager>
0053 #include <KShell>
0054 #include <KShortcutsDialog>
0055 #include <KStandardAction>
0056 #include <KSycoca>
0057 #include <KTerminalLauncherJob>
0058 #include <KToggleAction>
0059 #include <KToolBar>
0060 #include <KToolBarPopupAction>
0061 #include <KUrlComboBox>
0062 #include <KUrlNavigator>
0063 #include <KWindowSystem>
0064 #include <KXMLGUIFactory>
0065 
0066 #include <kwidgetsaddons_version.h>
0067 
0068 #include <QApplication>
0069 #include <QClipboard>
0070 #include <QCloseEvent>
0071 #include <QDesktopServices>
0072 #include <QDialog>
0073 #include <QDomDocument>
0074 #include <QFileInfo>
0075 #include <QLineEdit>
0076 #include <QMenuBar>
0077 #include <QPushButton>
0078 #include <QShowEvent>
0079 #include <QStandardPaths>
0080 #include <QTimer>
0081 #include <QToolButton>
0082 
0083 #include <algorithm>
0084 
0085 #if HAVE_X11
0086 #include <KStartupInfo>
0087 #endif
0088 
0089 namespace
0090 {
0091 // Used for GeneralSettings::version() to determine whether
0092 // an updated version of Dolphin is running, so as to migrate
0093 // removed/renamed ...etc config entries; increment it in such
0094 // cases
0095 const int CurrentDolphinVersion = 202;
0096 // The maximum number of entries in the back/forward popup menu
0097 const int MaxNumberOfNavigationentries = 12;
0098 // The maximum number of "Activate Tab" shortcuts
0099 const int MaxActivateTabShortcuts = 9;
0100 }
0101 
0102 DolphinMainWindow::DolphinMainWindow()
0103     : KXmlGuiWindow(nullptr)
0104     , m_newFileMenu(nullptr)
0105     , m_tabWidget(nullptr)
0106     , m_activeViewContainer(nullptr)
0107     , m_actionHandler(nullptr)
0108     , m_remoteEncoding(nullptr)
0109     , m_settingsDialog()
0110     , m_bookmarkHandler(nullptr)
0111     , m_lastHandleUrlOpenJob(nullptr)
0112     , m_terminalPanel(nullptr)
0113     , m_placesPanel(nullptr)
0114     , m_tearDownFromPlacesRequested(false)
0115     , m_backAction(nullptr)
0116     , m_forwardAction(nullptr)
0117 {
0118     Q_INIT_RESOURCE(dolphin);
0119 
0120     new MainWindowAdaptor(this);
0121 
0122 #ifndef Q_OS_WIN
0123     setWindowFlags(Qt::WindowContextHelpButtonHint);
0124 #endif
0125     setComponentName(QStringLiteral("dolphin"), QGuiApplication::applicationDisplayName());
0126     setObjectName(QStringLiteral("Dolphin#"));
0127 
0128     setStateConfigGroup("State");
0129 
0130     connect(&DolphinNewFileMenuObserver::instance(), &DolphinNewFileMenuObserver::errorMessage, this, &DolphinMainWindow::showErrorMessage);
0131 
0132     KIO::FileUndoManager *undoManager = KIO::FileUndoManager::self();
0133     undoManager->setUiInterface(new UndoUiInterface());
0134 
0135     connect(undoManager, &KIO::FileUndoManager::undoAvailable, this, &DolphinMainWindow::slotUndoAvailable);
0136     connect(undoManager, &KIO::FileUndoManager::undoTextChanged, this, &DolphinMainWindow::slotUndoTextChanged);
0137     connect(undoManager, &KIO::FileUndoManager::jobRecordingStarted, this, &DolphinMainWindow::clearStatusBar);
0138     connect(undoManager, &KIO::FileUndoManager::jobRecordingFinished, this, &DolphinMainWindow::showCommand);
0139 
0140     const bool firstRun = (GeneralSettings::version() < 200);
0141     if (firstRun) {
0142         GeneralSettings::setViewPropsTimestamp(QDateTime::currentDateTime());
0143     }
0144 
0145     setAcceptDrops(true);
0146 
0147     auto *navigatorsWidgetAction = new DolphinNavigatorsWidgetAction(this);
0148     actionCollection()->addAction(QStringLiteral("url_navigators"), navigatorsWidgetAction);
0149     m_tabWidget = new DolphinTabWidget(navigatorsWidgetAction, this);
0150     m_tabWidget->setObjectName("tabWidget");
0151     connect(m_tabWidget, &DolphinTabWidget::activeViewChanged, this, &DolphinMainWindow::activeViewChanged);
0152     connect(m_tabWidget, &DolphinTabWidget::tabCountChanged, this, &DolphinMainWindow::tabCountChanged);
0153     connect(m_tabWidget, &DolphinTabWidget::currentUrlChanged, this, &DolphinMainWindow::updateWindowTitle);
0154     setCentralWidget(m_tabWidget);
0155 
0156     m_actionTextHelper = new SelectionMode::ActionTextHelper(this);
0157     setupActions();
0158 
0159     m_actionHandler = new DolphinViewActionHandler(actionCollection(), m_actionTextHelper, this);
0160     connect(m_actionHandler, &DolphinViewActionHandler::actionBeingHandled, this, &DolphinMainWindow::clearStatusBar);
0161     connect(m_actionHandler, &DolphinViewActionHandler::createDirectoryTriggered, this, &DolphinMainWindow::createDirectory);
0162     connect(m_actionHandler, &DolphinViewActionHandler::selectionModeChangeTriggered, this, &DolphinMainWindow::slotSetSelectionMode);
0163 
0164     Q_CHECK_PTR(actionCollection()->action(QStringLiteral("create_dir")));
0165     m_newFileMenu->setNewFolderShortcutAction(actionCollection()->action(QStringLiteral("create_dir")));
0166 
0167     m_remoteEncoding = new DolphinRemoteEncoding(this, m_actionHandler);
0168     connect(this, &DolphinMainWindow::urlChanged, m_remoteEncoding, &DolphinRemoteEncoding::slotAboutToOpenUrl);
0169 
0170     setupDockWidgets();
0171 
0172     setupGUI(Save | Create | ToolBar);
0173     stateChanged(QStringLiteral("new_file"));
0174 
0175     QClipboard *clipboard = QApplication::clipboard();
0176     connect(clipboard, &QClipboard::dataChanged, this, &DolphinMainWindow::updatePasteAction);
0177 
0178     QAction *toggleFilterBarAction = actionCollection()->action(QStringLiteral("toggle_filter"));
0179     toggleFilterBarAction->setChecked(GeneralSettings::filterBar());
0180 
0181     if (firstRun) {
0182         menuBar()->setVisible(false);
0183     }
0184 
0185     const bool showMenu = !menuBar()->isHidden();
0186     QAction *showMenuBarAction = actionCollection()->action(KStandardAction::name(KStandardAction::ShowMenubar));
0187     showMenuBarAction->setChecked(showMenu); // workaround for bug #171080
0188 
0189     auto hamburgerMenu = static_cast<KHamburgerMenu *>(actionCollection()->action(KStandardAction::name(KStandardAction::HamburgerMenu)));
0190     hamburgerMenu->setMenuBar(menuBar());
0191     hamburgerMenu->setShowMenuBarAction(showMenuBarAction);
0192     connect(hamburgerMenu, &KHamburgerMenu::aboutToShowMenu, this, &DolphinMainWindow::updateHamburgerMenu);
0193     hamburgerMenu->hideActionsOf(toolBar());
0194     if (GeneralSettings::version() < 201 && !toolBar()->actions().contains(hamburgerMenu)) {
0195         addHamburgerMenuToToolbar();
0196     }
0197 
0198     updateAllowedToolbarAreas();
0199 
0200     // enable middle-click on back/forward/up to open in a new tab
0201     auto *middleClickEventFilter = new MiddleClickActionEventFilter(this);
0202     connect(middleClickEventFilter, &MiddleClickActionEventFilter::actionMiddleClicked, this, &DolphinMainWindow::slotToolBarActionMiddleClicked);
0203     toolBar()->installEventFilter(middleClickEventFilter);
0204 
0205     setupWhatsThis();
0206 
0207     connect(KSycoca::self(), &KSycoca::databaseChanged, this, &DolphinMainWindow::updateOpenPreferredSearchToolAction);
0208 
0209     QTimer::singleShot(0, this, &DolphinMainWindow::updateOpenPreferredSearchToolAction);
0210 
0211     m_fileItemActions.setParentWidget(this);
0212     connect(&m_fileItemActions, &KFileItemActions::error, this, [this](const QString &errorMessage) {
0213         showErrorMessage(errorMessage);
0214     });
0215 
0216     connect(GeneralSettings::self(), &GeneralSettings::splitViewChanged, this, &DolphinMainWindow::slotSplitViewChanged);
0217 }
0218 
0219 DolphinMainWindow::~DolphinMainWindow()
0220 {
0221     // This fixes a crash on Wayland when closing the mainwindow while another dialog is open.
0222     disconnect(QGuiApplication::clipboard(), &QClipboard::dataChanged, this, &DolphinMainWindow::updatePasteAction);
0223 }
0224 
0225 QVector<DolphinViewContainer *> DolphinMainWindow::viewContainers() const
0226 {
0227     QVector<DolphinViewContainer *> viewContainers;
0228 
0229     for (int i = 0; i < m_tabWidget->count(); ++i) {
0230         DolphinTabPage *tabPage = m_tabWidget->tabPageAt(i);
0231 
0232         viewContainers << tabPage->primaryViewContainer();
0233         if (tabPage->splitViewEnabled()) {
0234             viewContainers << tabPage->secondaryViewContainer();
0235         }
0236     }
0237     return viewContainers;
0238 }
0239 
0240 void DolphinMainWindow::openDirectories(const QList<QUrl> &dirs, bool splitView)
0241 {
0242     m_tabWidget->openDirectories(dirs, splitView);
0243 }
0244 
0245 void DolphinMainWindow::openDirectories(const QStringList &dirs, bool splitView)
0246 {
0247     openDirectories(QUrl::fromStringList(dirs), splitView);
0248 }
0249 
0250 void DolphinMainWindow::openFiles(const QList<QUrl> &files, bool splitView)
0251 {
0252     m_tabWidget->openFiles(files, splitView);
0253 }
0254 
0255 bool DolphinMainWindow::isFoldersPanelEnabled() const
0256 {
0257     return actionCollection()->action(QStringLiteral("show_folders_panel"))->isChecked();
0258 }
0259 
0260 bool DolphinMainWindow::isInformationPanelEnabled() const
0261 {
0262 #if HAVE_BALOO
0263     return actionCollection()->action(QStringLiteral("show_information_panel"))->isChecked();
0264 #else
0265     return false;
0266 #endif
0267 }
0268 
0269 bool DolphinMainWindow::isSplitViewEnabledInCurrentTab() const
0270 {
0271     return m_tabWidget->currentTabPage()->splitViewEnabled();
0272 }
0273 
0274 void DolphinMainWindow::openFiles(const QStringList &files, bool splitView)
0275 {
0276     openFiles(QUrl::fromStringList(files), splitView);
0277 }
0278 
0279 void DolphinMainWindow::activateWindow(const QString &activationToken)
0280 {
0281     window()->setAttribute(Qt::WA_NativeWindow, true);
0282 
0283     if (KWindowSystem::isPlatformWayland()) {
0284         KWindowSystem::setCurrentXdgActivationToken(activationToken);
0285     } else if (KWindowSystem::isPlatformX11()) {
0286 #if HAVE_X11
0287         KStartupInfo::setNewStartupId(window()->windowHandle(), activationToken.toUtf8());
0288 #endif
0289     }
0290 
0291     KWindowSystem::activateWindow(window()->windowHandle());
0292 }
0293 
0294 bool DolphinMainWindow::isActiveWindow()
0295 {
0296     return window()->isActiveWindow();
0297 }
0298 
0299 void DolphinMainWindow::showCommand(CommandType command)
0300 {
0301     DolphinStatusBar *statusBar = m_activeViewContainer->statusBar();
0302     switch (command) {
0303     case KIO::FileUndoManager::Copy:
0304         statusBar->setText(i18nc("@info:status", "Successfully copied."));
0305         break;
0306     case KIO::FileUndoManager::Move:
0307         statusBar->setText(i18nc("@info:status", "Successfully moved."));
0308         break;
0309     case KIO::FileUndoManager::Link:
0310         statusBar->setText(i18nc("@info:status", "Successfully linked."));
0311         break;
0312     case KIO::FileUndoManager::Trash:
0313         statusBar->setText(i18nc("@info:status", "Successfully moved to trash."));
0314         break;
0315     case KIO::FileUndoManager::Rename:
0316         statusBar->setText(i18nc("@info:status", "Successfully renamed."));
0317         break;
0318 
0319     case KIO::FileUndoManager::Mkdir:
0320         statusBar->setText(i18nc("@info:status", "Created folder."));
0321         break;
0322 
0323     default:
0324         break;
0325     }
0326 }
0327 
0328 void DolphinMainWindow::pasteIntoFolder()
0329 {
0330     m_activeViewContainer->view()->pasteIntoFolder();
0331 }
0332 
0333 void DolphinMainWindow::changeUrl(const QUrl &url)
0334 {
0335     if (!KProtocolManager::supportsListing(url)) {
0336         // The URL navigator only checks for validity, not
0337         // if the URL can be listed. An error message is
0338         // shown due to DolphinViewContainer::restoreView().
0339         return;
0340     }
0341 
0342     m_activeViewContainer->setUrl(url);
0343     updateFileAndEditActions();
0344     updatePasteAction();
0345     updateViewActions();
0346     updateGoActions();
0347 
0348     Q_EMIT urlChanged(url);
0349 }
0350 
0351 void DolphinMainWindow::slotTerminalDirectoryChanged(const QUrl &url)
0352 {
0353     if (m_tearDownFromPlacesRequested && url == QUrl::fromLocalFile(QDir::homePath())) {
0354         m_placesPanel->proceedWithTearDown();
0355         m_tearDownFromPlacesRequested = false;
0356     }
0357 
0358     m_activeViewContainer->setAutoGrabFocus(false);
0359     changeUrl(url);
0360     m_activeViewContainer->setAutoGrabFocus(true);
0361 }
0362 
0363 void DolphinMainWindow::slotEditableStateChanged(bool editable)
0364 {
0365     KToggleAction *editableLocationAction = static_cast<KToggleAction *>(actionCollection()->action(QStringLiteral("editable_location")));
0366     editableLocationAction->setChecked(editable);
0367 }
0368 
0369 void DolphinMainWindow::slotSelectionChanged(const KFileItemList &selection)
0370 {
0371     updateFileAndEditActions();
0372 
0373     const int selectedUrlsCount = m_tabWidget->currentTabPage()->selectedItemsCount();
0374 
0375     QAction *compareFilesAction = actionCollection()->action(QStringLiteral("compare_files"));
0376     if (selectedUrlsCount == 2) {
0377         compareFilesAction->setEnabled(isKompareInstalled());
0378     } else {
0379         compareFilesAction->setEnabled(false);
0380     }
0381 
0382     Q_EMIT selectionChanged(selection);
0383 }
0384 
0385 void DolphinMainWindow::updateHistory()
0386 {
0387     const KUrlNavigator *urlNavigator = m_activeViewContainer->urlNavigatorInternalWithHistory();
0388     const int index = urlNavigator->historyIndex();
0389 
0390     QAction *backAction = actionCollection()->action(KStandardAction::name(KStandardAction::Back));
0391     if (backAction) {
0392         backAction->setToolTip(i18nc("@info", "Go back"));
0393         backAction->setWhatsThis(i18nc("@info:whatsthis go back", "Return to the previously viewed folder."));
0394         backAction->setEnabled(index < urlNavigator->historySize() - 1);
0395     }
0396 
0397     QAction *forwardAction = actionCollection()->action(KStandardAction::name(KStandardAction::Forward));
0398     if (forwardAction) {
0399         forwardAction->setToolTip(i18nc("@info", "Go forward"));
0400         forwardAction->setWhatsThis(xi18nc("@info:whatsthis go forward", "This undoes a <interface>Go|Back</interface> action."));
0401         forwardAction->setEnabled(index > 0);
0402     }
0403 }
0404 
0405 void DolphinMainWindow::updateFilterBarAction(bool show)
0406 {
0407     QAction *toggleFilterBarAction = actionCollection()->action(QStringLiteral("toggle_filter"));
0408     toggleFilterBarAction->setChecked(show);
0409 }
0410 
0411 void DolphinMainWindow::openNewMainWindow()
0412 {
0413     Dolphin::openNewWindow({m_activeViewContainer->url()}, this);
0414 }
0415 
0416 void DolphinMainWindow::openNewActivatedTab()
0417 {
0418     // keep browsers compatibility, new tab is always after last one
0419     auto openNewTabAfterLastTabConfigured = GeneralSettings::openNewTabAfterLastTab();
0420     GeneralSettings::setOpenNewTabAfterLastTab(true);
0421     m_tabWidget->openNewActivatedTab();
0422     GeneralSettings::setOpenNewTabAfterLastTab(openNewTabAfterLastTabConfigured);
0423 }
0424 
0425 void DolphinMainWindow::addToPlaces()
0426 {
0427     QUrl url;
0428     QString name;
0429 
0430     // If nothing is selected, act on the current dir
0431     if (m_activeViewContainer->view()->selectedItems().isEmpty()) {
0432         url = m_activeViewContainer->url();
0433         name = m_activeViewContainer->placesText();
0434     } else {
0435         const auto dirToAdd = m_activeViewContainer->view()->selectedItems().first();
0436         url = dirToAdd.url();
0437         name = dirToAdd.name();
0438     }
0439     if (url.isValid()) {
0440         QString icon;
0441         if (m_activeViewContainer->isSearchModeEnabled()) {
0442             icon = QStringLiteral("folder-saved-search-symbolic");
0443         } else {
0444             icon = KIO::iconNameForUrl(url);
0445         }
0446         DolphinPlacesModelSingleton::instance().placesModel()->addPlace(name, url, icon);
0447     }
0448 }
0449 
0450 void DolphinMainWindow::openNewTab(const QUrl &url)
0451 {
0452     m_tabWidget->openNewTab(url, QUrl());
0453 }
0454 
0455 void DolphinMainWindow::openNewTabAndActivate(const QUrl &url)
0456 {
0457     m_tabWidget->openNewActivatedTab(url, QUrl());
0458 }
0459 
0460 void DolphinMainWindow::openNewWindow(const QUrl &url)
0461 {
0462     Dolphin::openNewWindow({url}, this);
0463 }
0464 
0465 void DolphinMainWindow::slotSplitViewChanged()
0466 {
0467     m_tabWidget->currentTabPage()->setSplitViewEnabled(GeneralSettings::splitView(), WithAnimation);
0468     updateSplitAction();
0469 }
0470 
0471 void DolphinMainWindow::openInNewTab()
0472 {
0473     const KFileItemList &list = m_activeViewContainer->view()->selectedItems();
0474     bool tabCreated = false;
0475 
0476     for (const KFileItem &item : list) {
0477         const QUrl &url = DolphinView::openItemAsFolderUrl(item);
0478         if (!url.isEmpty()) {
0479             openNewTab(url);
0480             tabCreated = true;
0481         }
0482     }
0483 
0484     // if no new tab has been created from the selection
0485     // open the current directory in a new tab
0486     if (!tabCreated) {
0487         openNewTab(m_activeViewContainer->url());
0488     }
0489 }
0490 
0491 void DolphinMainWindow::openInNewWindow()
0492 {
0493     QUrl newWindowUrl;
0494 
0495     const KFileItemList list = m_activeViewContainer->view()->selectedItems();
0496     if (list.isEmpty()) {
0497         newWindowUrl = m_activeViewContainer->url();
0498     } else if (list.count() == 1) {
0499         const KFileItem &item = list.first();
0500         newWindowUrl = DolphinView::openItemAsFolderUrl(item);
0501     }
0502 
0503     if (!newWindowUrl.isEmpty()) {
0504         Dolphin::openNewWindow({newWindowUrl}, this);
0505     }
0506 }
0507 
0508 void DolphinMainWindow::openInSplitView(const QUrl &url)
0509 {
0510     QUrl newSplitViewUrl = url;
0511 
0512     if (newSplitViewUrl.isEmpty()) {
0513         const KFileItemList list = m_activeViewContainer->view()->selectedItems();
0514         if (list.count() == 1) {
0515             const KFileItem &item = list.first();
0516             newSplitViewUrl = DolphinView::openItemAsFolderUrl(item);
0517         }
0518     }
0519 
0520     if (newSplitViewUrl.isEmpty()) {
0521         return;
0522     }
0523 
0524     DolphinTabPage *tabPage = m_tabWidget->currentTabPage();
0525     if (tabPage->splitViewEnabled()) {
0526         tabPage->switchActiveView();
0527         tabPage->activeViewContainer()->setUrl(newSplitViewUrl);
0528     } else {
0529         tabPage->setSplitViewEnabled(true, WithAnimation, newSplitViewUrl);
0530         updateViewActions();
0531     }
0532 }
0533 
0534 void DolphinMainWindow::showTarget()
0535 {
0536     const KFileItem link = m_activeViewContainer->view()->selectedItems().at(0);
0537     const QUrl destinationUrl = link.url().resolved(QUrl(link.linkDest()));
0538 
0539     auto job = KIO::stat(destinationUrl, KIO::StatJob::SourceSide, KIO::StatNoDetails);
0540 
0541     connect(job, &KJob::finished, this, [this, destinationUrl](KJob *job) {
0542         KIO::StatJob *statJob = static_cast<KIO::StatJob *>(job);
0543 
0544         if (statJob->error()) {
0545             m_activeViewContainer->showMessage(job->errorString(), DolphinViewContainer::Error);
0546         } else {
0547             KIO::highlightInFileManager({destinationUrl});
0548         }
0549     });
0550 }
0551 
0552 bool DolphinMainWindow::event(QEvent *event)
0553 {
0554     if (event->type() == QEvent::ShortcutOverride) {
0555         const QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
0556         if (keyEvent->key() == Qt::Key_Space && m_activeViewContainer->view()->handleSpaceAsNormalKey()) {
0557             event->accept();
0558             return true;
0559         }
0560     }
0561 
0562     return KXmlGuiWindow::event(event);
0563 }
0564 
0565 void DolphinMainWindow::showEvent(QShowEvent *event)
0566 {
0567     KXmlGuiWindow::showEvent(event);
0568 
0569     if (!event->spontaneous()) {
0570         m_activeViewContainer->view()->setFocus();
0571     }
0572 }
0573 
0574 void DolphinMainWindow::closeEvent(QCloseEvent *event)
0575 {
0576     // Find out if Dolphin is closed directly by the user or
0577     // by the session manager because the session is closed
0578     bool closedByUser = true;
0579     if (qApp->isSavingSession()) {
0580         closedByUser = false;
0581     }
0582 
0583     if (m_tabWidget->count() > 1 && GeneralSettings::confirmClosingMultipleTabs() && !GeneralSettings::rememberOpenedTabs() && closedByUser) {
0584         // Ask the user if he really wants to quit and close all tabs.
0585         // Open a confirmation dialog with 3 buttons:
0586         // QDialogButtonBox::Yes    -> Quit
0587         // QDialogButtonBox::No     -> Close only the current tab
0588         // QDialogButtonBox::Cancel -> do nothing
0589         QDialog *dialog = new QDialog(this, Qt::Dialog);
0590         dialog->setWindowTitle(i18nc("@title:window", "Confirmation"));
0591         dialog->setModal(true);
0592         QDialogButtonBox *buttons = new QDialogButtonBox(QDialogButtonBox::Yes | QDialogButtonBox::No | QDialogButtonBox::Cancel);
0593         KGuiItem::assign(buttons->button(QDialogButtonBox::Yes),
0594                          KGuiItem(i18nc("@action:button 'Quit Dolphin' button", "&Quit %1", QGuiApplication::applicationDisplayName()),
0595                                   QIcon::fromTheme(QStringLiteral("application-exit"))));
0596         KGuiItem::assign(buttons->button(QDialogButtonBox::No), KGuiItem(i18n("C&lose Current Tab"), QIcon::fromTheme(QStringLiteral("tab-close"))));
0597         KGuiItem::assign(buttons->button(QDialogButtonBox::Cancel), KStandardGuiItem::cancel());
0598         buttons->button(QDialogButtonBox::Yes)->setDefault(true);
0599 
0600         bool doNotAskAgainCheckboxResult = false;
0601 
0602         const auto result = KMessageBox::createKMessageBox(dialog,
0603                                                            buttons,
0604                                                            QMessageBox::Warning,
0605                                                            i18n("You have multiple tabs open in this window, are you sure you want to quit?"),
0606                                                            QStringList(),
0607                                                            i18n("Do not ask again"),
0608                                                            &doNotAskAgainCheckboxResult,
0609                                                            KMessageBox::Notify);
0610 
0611         if (doNotAskAgainCheckboxResult) {
0612             GeneralSettings::setConfirmClosingMultipleTabs(false);
0613         }
0614 
0615         switch (result) {
0616         case QDialogButtonBox::Yes:
0617             // Quit
0618             break;
0619         case QDialogButtonBox::No:
0620             // Close only the current tab
0621             m_tabWidget->closeTab();
0622             Q_FALLTHROUGH();
0623         default:
0624             event->ignore();
0625             return;
0626         }
0627     }
0628 
0629     if (m_terminalPanel && m_terminalPanel->hasProgramRunning() && GeneralSettings::confirmClosingTerminalRunningProgram() && closedByUser) {
0630         // Ask if the user really wants to quit Dolphin with a program that is still running in the Terminal panel
0631         // Open a confirmation dialog with 3 buttons:
0632         // QDialogButtonBox::Yes    -> Quit
0633         // QDialogButtonBox::No     -> Show Terminal Panel
0634         // QDialogButtonBox::Cancel -> do nothing
0635         QDialog *dialog = new QDialog(this, Qt::Dialog);
0636         dialog->setWindowTitle(i18nc("@title:window", "Confirmation"));
0637         dialog->setModal(true);
0638         auto standardButtons = QDialogButtonBox::Yes | QDialogButtonBox::Cancel;
0639         if (!m_terminalPanel->isVisible()) {
0640             standardButtons |= QDialogButtonBox::No;
0641         }
0642         QDialogButtonBox *buttons = new QDialogButtonBox(standardButtons);
0643         KGuiItem::assign(buttons->button(QDialogButtonBox::Yes), KStandardGuiItem::quit());
0644         if (!m_terminalPanel->isVisible()) {
0645             KGuiItem::assign(buttons->button(QDialogButtonBox::No), KGuiItem(i18n("Show &Terminal Panel"), QIcon::fromTheme(QStringLiteral("dialog-scripts"))));
0646         }
0647         KGuiItem::assign(buttons->button(QDialogButtonBox::Cancel), KStandardGuiItem::cancel());
0648 
0649         bool doNotAskAgainCheckboxResult = false;
0650 
0651         const auto result = KMessageBox::createKMessageBox(
0652             dialog,
0653             buttons,
0654             QMessageBox::Warning,
0655             i18n("The program '%1' is still running in the Terminal panel. Are you sure you want to quit?", m_terminalPanel->runningProgramName()),
0656             QStringList(),
0657             i18n("Do not ask again"),
0658             &doNotAskAgainCheckboxResult,
0659             KMessageBox::Notify | KMessageBox::Dangerous);
0660 
0661         if (doNotAskAgainCheckboxResult) {
0662             GeneralSettings::setConfirmClosingTerminalRunningProgram(false);
0663         }
0664 
0665         switch (result) {
0666         case QDialogButtonBox::Yes:
0667             // Quit
0668             break;
0669         case QDialogButtonBox::No:
0670             actionCollection()->action("show_terminal_panel")->trigger();
0671             // Do not quit, ignore quit event
0672             Q_FALLTHROUGH();
0673         default:
0674             event->ignore();
0675             return;
0676         }
0677     }
0678 
0679     if (GeneralSettings::rememberOpenedTabs()) {
0680         KConfigGui::setSessionConfig(QStringLiteral("dolphin"), QStringLiteral("dolphin"));
0681         KConfig *config = KConfigGui::sessionConfig();
0682         saveGlobalProperties(config);
0683         savePropertiesInternal(config, 1);
0684         config->sync();
0685     }
0686 
0687     GeneralSettings::setVersion(CurrentDolphinVersion);
0688     GeneralSettings::self()->save();
0689 
0690     KXmlGuiWindow::closeEvent(event);
0691 }
0692 
0693 void DolphinMainWindow::saveProperties(KConfigGroup &group)
0694 {
0695     m_tabWidget->saveProperties(group);
0696 }
0697 
0698 void DolphinMainWindow::readProperties(const KConfigGroup &group)
0699 {
0700     m_tabWidget->readProperties(group);
0701 }
0702 
0703 void DolphinMainWindow::updateNewMenu()
0704 {
0705     m_newFileMenu->checkUpToDate();
0706     m_newFileMenu->setWorkingDirectory(activeViewContainer()->url());
0707 }
0708 
0709 void DolphinMainWindow::createDirectory()
0710 {
0711     m_newFileMenu->setWorkingDirectory(activeViewContainer()->url());
0712     m_newFileMenu->createDirectory();
0713 }
0714 
0715 void DolphinMainWindow::quit()
0716 {
0717     close();
0718 }
0719 
0720 void DolphinMainWindow::showErrorMessage(const QString &message)
0721 {
0722     m_activeViewContainer->showMessage(message, DolphinViewContainer::Error);
0723 }
0724 
0725 void DolphinMainWindow::slotUndoAvailable(bool available)
0726 {
0727     QAction *undoAction = actionCollection()->action(KStandardAction::name(KStandardAction::Undo));
0728     if (undoAction) {
0729         undoAction->setEnabled(available);
0730     }
0731 }
0732 
0733 void DolphinMainWindow::slotUndoTextChanged(const QString &text)
0734 {
0735     QAction *undoAction = actionCollection()->action(KStandardAction::name(KStandardAction::Undo));
0736     if (undoAction) {
0737         undoAction->setText(text);
0738     }
0739 }
0740 
0741 void DolphinMainWindow::undo()
0742 {
0743     clearStatusBar();
0744     KIO::FileUndoManager::self()->uiInterface()->setParentWidget(this);
0745     KIO::FileUndoManager::self()->undo();
0746 }
0747 
0748 void DolphinMainWindow::cut()
0749 {
0750     if (m_activeViewContainer->view()->selectedItems().isEmpty()) {
0751         m_activeViewContainer->setSelectionModeEnabled(true, actionCollection(), SelectionMode::BottomBar::Contents::CutContents);
0752     } else {
0753         m_activeViewContainer->view()->cutSelectedItemsToClipboard();
0754         m_activeViewContainer->setSelectionModeEnabled(false);
0755     }
0756 }
0757 
0758 void DolphinMainWindow::copy()
0759 {
0760     if (m_activeViewContainer->view()->selectedItems().isEmpty()) {
0761         m_activeViewContainer->setSelectionModeEnabled(true, actionCollection(), SelectionMode::BottomBar::Contents::CopyContents);
0762     } else {
0763         m_activeViewContainer->view()->copySelectedItemsToClipboard();
0764         m_activeViewContainer->setSelectionModeEnabled(false);
0765     }
0766 }
0767 
0768 void DolphinMainWindow::paste()
0769 {
0770     m_activeViewContainer->view()->paste();
0771 }
0772 
0773 void DolphinMainWindow::find()
0774 {
0775     m_activeViewContainer->setSearchModeEnabled(true);
0776 }
0777 
0778 void DolphinMainWindow::updateSearchAction()
0779 {
0780     QAction *toggleSearchAction = actionCollection()->action(QStringLiteral("toggle_search"));
0781     toggleSearchAction->setChecked(m_activeViewContainer->isSearchModeEnabled());
0782 }
0783 
0784 void DolphinMainWindow::updatePasteAction()
0785 {
0786     QAction *pasteAction = actionCollection()->action(KStandardAction::name(KStandardAction::Paste));
0787     QPair<bool, QString> pasteInfo = m_activeViewContainer->view()->pasteInfo();
0788     pasteAction->setEnabled(pasteInfo.first);
0789     pasteAction->setText(pasteInfo.second);
0790 }
0791 
0792 void DolphinMainWindow::slotDirectoryLoadingCompleted()
0793 {
0794     updatePasteAction();
0795 }
0796 
0797 void DolphinMainWindow::slotToolBarActionMiddleClicked(QAction *action)
0798 {
0799     if (action == actionCollection()->action(KStandardAction::name(KStandardAction::Back))) {
0800         goBackInNewTab();
0801     } else if (action == actionCollection()->action(KStandardAction::name(KStandardAction::Forward))) {
0802         goForwardInNewTab();
0803     } else if (action == actionCollection()->action(QStringLiteral("go_up"))) {
0804         goUpInNewTab();
0805     } else if (action == actionCollection()->action(QStringLiteral("go_home"))) {
0806         goHomeInNewTab();
0807     }
0808 }
0809 
0810 QAction *DolphinMainWindow::urlNavigatorHistoryAction(const KUrlNavigator *urlNavigator, int historyIndex, QObject *parent)
0811 {
0812     const QUrl url = urlNavigator->locationUrl(historyIndex);
0813 
0814     QString text = url.toDisplayString(QUrl::PreferLocalFile);
0815 
0816     if (!urlNavigator->showFullPath()) {
0817         const KFilePlacesModel *placesModel = DolphinPlacesModelSingleton::instance().placesModel();
0818 
0819         const QModelIndex closestIdx = placesModel->closestItem(url);
0820         if (closestIdx.isValid()) {
0821             const QUrl placeUrl = placesModel->url(closestIdx);
0822 
0823             text = placesModel->text(closestIdx);
0824 
0825             QString pathInsidePlace = url.path().mid(placeUrl.path().length());
0826 
0827             if (!pathInsidePlace.isEmpty() && !pathInsidePlace.startsWith(QLatin1Char('/'))) {
0828                 pathInsidePlace.prepend(QLatin1Char('/'));
0829             }
0830 
0831             if (pathInsidePlace != QLatin1Char('/')) {
0832                 text.append(pathInsidePlace);
0833             }
0834         }
0835     }
0836 
0837     QAction *action = new QAction(QIcon::fromTheme(KIO::iconNameForUrl(url)), text, parent);
0838     action->setData(historyIndex);
0839     return action;
0840 }
0841 
0842 void DolphinMainWindow::slotAboutToShowBackPopupMenu()
0843 {
0844     const KUrlNavigator *urlNavigator = m_activeViewContainer->urlNavigatorInternalWithHistory();
0845     int entries = 0;
0846     QMenu *menu = m_backAction->popupMenu();
0847     menu->clear();
0848     for (int i = urlNavigator->historyIndex() + 1; i < urlNavigator->historySize() && entries < MaxNumberOfNavigationentries; ++i, ++entries) {
0849         QAction *action = urlNavigatorHistoryAction(urlNavigator, i, menu);
0850         menu->addAction(action);
0851     }
0852 }
0853 
0854 void DolphinMainWindow::slotGoBack(QAction *action)
0855 {
0856     int gotoIndex = action->data().value<int>();
0857     const KUrlNavigator *urlNavigator = m_activeViewContainer->urlNavigatorInternalWithHistory();
0858     for (int i = gotoIndex - urlNavigator->historyIndex(); i > 0; --i) {
0859         goBack();
0860     }
0861 }
0862 
0863 void DolphinMainWindow::slotBackForwardActionMiddleClicked(QAction *action)
0864 {
0865     if (action) {
0866         const KUrlNavigator *urlNavigator = activeViewContainer()->urlNavigatorInternalWithHistory();
0867         openNewTab(urlNavigator->locationUrl(action->data().value<int>()));
0868     }
0869 }
0870 
0871 void DolphinMainWindow::slotAboutToShowForwardPopupMenu()
0872 {
0873     const KUrlNavigator *urlNavigator = m_activeViewContainer->urlNavigatorInternalWithHistory();
0874     int entries = 0;
0875     QMenu *menu = m_forwardAction->popupMenu();
0876     for (int i = urlNavigator->historyIndex() - 1; i >= 0 && entries < MaxNumberOfNavigationentries; --i, ++entries) {
0877         QAction *action = urlNavigatorHistoryAction(urlNavigator, i, menu);
0878         menu->addAction(action);
0879     }
0880 }
0881 
0882 void DolphinMainWindow::slotGoForward(QAction *action)
0883 {
0884     int gotoIndex = action->data().value<int>();
0885     const KUrlNavigator *urlNavigator = m_activeViewContainer->urlNavigatorInternalWithHistory();
0886     for (int i = urlNavigator->historyIndex() - gotoIndex; i > 0; --i) {
0887         goForward();
0888     }
0889 }
0890 
0891 void DolphinMainWindow::slotSetSelectionMode(bool enabled, SelectionMode::BottomBar::Contents bottomBarContents)
0892 {
0893     m_activeViewContainer->setSelectionModeEnabled(enabled, actionCollection(), bottomBarContents);
0894 }
0895 
0896 void DolphinMainWindow::selectAll()
0897 {
0898     clearStatusBar();
0899 
0900     // if the URL navigator is editable and focused, select the whole
0901     // URL instead of all items of the view
0902 
0903     KUrlNavigator *urlNavigator = m_activeViewContainer->urlNavigator();
0904     QLineEdit *lineEdit = urlNavigator->editor()->lineEdit();
0905     const bool selectUrl = urlNavigator->isUrlEditable() && lineEdit->hasFocus();
0906     if (selectUrl) {
0907         lineEdit->selectAll();
0908     } else {
0909         m_activeViewContainer->view()->selectAll();
0910     }
0911 }
0912 
0913 void DolphinMainWindow::invertSelection()
0914 {
0915     clearStatusBar();
0916     m_activeViewContainer->view()->invertSelection();
0917 }
0918 
0919 void DolphinMainWindow::toggleSplitView()
0920 {
0921     DolphinTabPage *tabPage = m_tabWidget->currentTabPage();
0922     tabPage->setSplitViewEnabled(!tabPage->splitViewEnabled(), WithAnimation);
0923     updateViewActions();
0924 }
0925 
0926 void DolphinMainWindow::toggleSplitStash()
0927 {
0928     DolphinTabPage *tabPage = m_tabWidget->currentTabPage();
0929     tabPage->setSplitViewEnabled(false, WithAnimation);
0930     tabPage->setSplitViewEnabled(true, WithAnimation, QUrl("stash:/"));
0931 }
0932 
0933 void DolphinMainWindow::copyToInactiveSplitView()
0934 {
0935     if (m_activeViewContainer->view()->selectedItems().isEmpty()) {
0936         m_activeViewContainer->setSelectionModeEnabled(true, actionCollection(), SelectionMode::BottomBar::Contents::CopyToOtherViewContents);
0937     } else {
0938         m_tabWidget->copyToInactiveSplitView();
0939         m_activeViewContainer->setSelectionModeEnabled(false);
0940     }
0941 }
0942 
0943 void DolphinMainWindow::moveToInactiveSplitView()
0944 {
0945     if (m_activeViewContainer->view()->selectedItems().isEmpty()) {
0946         m_activeViewContainer->setSelectionModeEnabled(true, actionCollection(), SelectionMode::BottomBar::Contents::MoveToOtherViewContents);
0947     } else {
0948         m_tabWidget->moveToInactiveSplitView();
0949         m_activeViewContainer->setSelectionModeEnabled(false);
0950     }
0951 }
0952 
0953 void DolphinMainWindow::reloadView()
0954 {
0955     clearStatusBar();
0956     m_activeViewContainer->reload();
0957     m_activeViewContainer->statusBar()->updateSpaceInfo();
0958 }
0959 
0960 void DolphinMainWindow::stopLoading()
0961 {
0962     m_activeViewContainer->view()->stopLoading();
0963 }
0964 
0965 void DolphinMainWindow::enableStopAction()
0966 {
0967     actionCollection()->action(QStringLiteral("stop"))->setEnabled(true);
0968 }
0969 
0970 void DolphinMainWindow::disableStopAction()
0971 {
0972     actionCollection()->action(QStringLiteral("stop"))->setEnabled(false);
0973 }
0974 
0975 void DolphinMainWindow::toggleSelectionMode()
0976 {
0977     const bool checked = !m_activeViewContainer->isSelectionModeEnabled();
0978 
0979     m_activeViewContainer->setSelectionModeEnabled(checked, actionCollection(), SelectionMode::BottomBar::Contents::GeneralContents);
0980     actionCollection()->action(QStringLiteral("toggle_selection_mode"))->setChecked(checked);
0981 }
0982 
0983 void DolphinMainWindow::showFilterBar()
0984 {
0985     m_activeViewContainer->setFilterBarVisible(true);
0986 }
0987 
0988 void DolphinMainWindow::toggleFilterBar()
0989 {
0990     const bool checked = !m_activeViewContainer->isFilterBarVisible();
0991     m_activeViewContainer->setFilterBarVisible(checked);
0992 
0993     QAction *toggleFilterBarAction = actionCollection()->action(QStringLiteral("toggle_filter"));
0994     toggleFilterBarAction->setChecked(checked);
0995 }
0996 
0997 void DolphinMainWindow::toggleEditLocation()
0998 {
0999     clearStatusBar();
1000 
1001     QAction *action = actionCollection()->action(QStringLiteral("editable_location"));
1002     KUrlNavigator *urlNavigator = m_activeViewContainer->urlNavigator();
1003     urlNavigator->setUrlEditable(action->isChecked());
1004 }
1005 
1006 void DolphinMainWindow::replaceLocation()
1007 {
1008     KUrlNavigator *navigator = m_activeViewContainer->urlNavigator();
1009     QLineEdit *lineEdit = navigator->editor()->lineEdit();
1010 
1011     // If the text field currently has focus and everything is selected,
1012     // pressing the keyboard shortcut returns the whole thing to breadcrumb mode
1013     if (navigator->isUrlEditable() && lineEdit->hasFocus() && lineEdit->selectedText() == lineEdit->text()) {
1014         navigator->setUrlEditable(false);
1015     } else {
1016         navigator->setUrlEditable(true);
1017         navigator->setFocus();
1018         lineEdit->selectAll();
1019     }
1020 }
1021 
1022 void DolphinMainWindow::togglePanelLockState()
1023 {
1024     const bool newLockState = !GeneralSettings::lockPanels();
1025     const auto childrenObjects = children();
1026     for (QObject *child : childrenObjects) {
1027         DolphinDockWidget *dock = qobject_cast<DolphinDockWidget *>(child);
1028         if (dock) {
1029             dock->setLocked(newLockState);
1030         }
1031     }
1032 
1033     DolphinPlacesModelSingleton::instance().placesModel()->setPanelsLocked(newLockState);
1034 
1035     GeneralSettings::setLockPanels(newLockState);
1036 }
1037 
1038 void DolphinMainWindow::slotTerminalPanelVisibilityChanged()
1039 {
1040     if (m_terminalPanel->isHiddenInVisibleWindow() && m_activeViewContainer) {
1041         m_activeViewContainer->view()->setFocus();
1042     }
1043 }
1044 
1045 void DolphinMainWindow::goBack()
1046 {
1047     DolphinUrlNavigator *urlNavigator = m_activeViewContainer->urlNavigatorInternalWithHistory();
1048     urlNavigator->goBack();
1049 
1050     if (urlNavigator->locationState().isEmpty()) {
1051         // An empty location state indicates a redirection URL,
1052         // which must be skipped too
1053         urlNavigator->goBack();
1054     }
1055 }
1056 
1057 void DolphinMainWindow::goForward()
1058 {
1059     m_activeViewContainer->urlNavigatorInternalWithHistory()->goForward();
1060 }
1061 
1062 void DolphinMainWindow::goUp()
1063 {
1064     m_activeViewContainer->urlNavigatorInternalWithHistory()->goUp();
1065 }
1066 
1067 void DolphinMainWindow::goHome()
1068 {
1069     m_activeViewContainer->urlNavigatorInternalWithHistory()->goHome();
1070 }
1071 
1072 void DolphinMainWindow::goBackInNewTab()
1073 {
1074     const KUrlNavigator *urlNavigator = activeViewContainer()->urlNavigatorInternalWithHistory();
1075     const int index = urlNavigator->historyIndex() + 1;
1076     openNewTab(urlNavigator->locationUrl(index));
1077 }
1078 
1079 void DolphinMainWindow::goForwardInNewTab()
1080 {
1081     const KUrlNavigator *urlNavigator = activeViewContainer()->urlNavigatorInternalWithHistory();
1082     const int index = urlNavigator->historyIndex() - 1;
1083     openNewTab(urlNavigator->locationUrl(index));
1084 }
1085 
1086 void DolphinMainWindow::goUpInNewTab()
1087 {
1088     const QUrl currentUrl = activeViewContainer()->urlNavigator()->locationUrl();
1089     openNewTab(KIO::upUrl(currentUrl));
1090 }
1091 
1092 void DolphinMainWindow::goHomeInNewTab()
1093 {
1094     openNewTab(Dolphin::homeUrl());
1095 }
1096 
1097 void DolphinMainWindow::compareFiles()
1098 {
1099     const KFileItemList items = m_tabWidget->currentTabPage()->selectedItems();
1100     if (items.count() != 2) {
1101         // The action is disabled in this case, but it could have been triggered
1102         // via D-Bus, see https://bugs.kde.org/show_bug.cgi?id=325517
1103         return;
1104     }
1105 
1106     QUrl urlA = items.at(0).url();
1107     QUrl urlB = items.at(1).url();
1108 
1109     QString command(QStringLiteral("kompare -c \""));
1110     command.append(urlA.toDisplayString(QUrl::PreferLocalFile));
1111     command.append("\" \"");
1112     command.append(urlB.toDisplayString(QUrl::PreferLocalFile));
1113     command.append('\"');
1114 
1115     KIO::CommandLauncherJob *job = new KIO::CommandLauncherJob(command, this);
1116     job->setDesktopName(QStringLiteral("org.kde.kompare"));
1117     job->start();
1118 }
1119 
1120 void DolphinMainWindow::toggleShowMenuBar()
1121 {
1122     const bool visible = menuBar()->isVisible();
1123     menuBar()->setVisible(!visible);
1124 }
1125 
1126 QPointer<QAction> DolphinMainWindow::preferredSearchTool()
1127 {
1128     m_searchTools.clear();
1129 
1130     KService::Ptr kfind = KService::serviceByDesktopName(QStringLiteral("org.kde.kfind"));
1131 
1132     if (!kfind) {
1133         return nullptr;
1134     }
1135 
1136     auto *action = new QAction(QIcon::fromTheme(kfind->icon()), kfind->name(), this);
1137 
1138     connect(action, &QAction::triggered, this, [kfind] {
1139         auto *job = new KIO::ApplicationLauncherJob(kfind);
1140         job->start();
1141     });
1142 
1143     return action;
1144 }
1145 
1146 void DolphinMainWindow::updateOpenPreferredSearchToolAction()
1147 {
1148     QAction *openPreferredSearchTool = actionCollection()->action(QStringLiteral("open_preferred_search_tool"));
1149     if (!openPreferredSearchTool) {
1150         return;
1151     }
1152     QPointer<QAction> tool = preferredSearchTool();
1153     if (tool) {
1154         openPreferredSearchTool->setVisible(true);
1155         openPreferredSearchTool->setText(i18nc("@action:inmenu Tools", "Open %1", tool->text()));
1156         // Only override with the app icon if it is the default, i.e. the user hasn't configured one manually
1157         // https://bugs.kde.org/show_bug.cgi?id=442815
1158         if (openPreferredSearchTool->icon().name() == QLatin1String("search")) {
1159             openPreferredSearchTool->setIcon(tool->icon());
1160         }
1161     } else {
1162         openPreferredSearchTool->setVisible(false);
1163         // still visible in Shortcuts configuration window
1164         openPreferredSearchTool->setText(i18nc("@action:inmenu Tools", "Open Preferred Search Tool"));
1165         openPreferredSearchTool->setIcon(QIcon::fromTheme(QStringLiteral("search")));
1166     }
1167 }
1168 
1169 void DolphinMainWindow::openPreferredSearchTool()
1170 {
1171     QPointer<QAction> tool = preferredSearchTool();
1172     if (tool) {
1173         tool->trigger();
1174     }
1175 }
1176 
1177 void DolphinMainWindow::openTerminal()
1178 {
1179     openTerminalJob(m_activeViewContainer->url());
1180 }
1181 
1182 void DolphinMainWindow::openTerminalHere()
1183 {
1184     QList<QUrl> urls = {};
1185 
1186     const auto selectedItems = m_activeViewContainer->view()->selectedItems();
1187     for (const KFileItem &item : selectedItems) {
1188         QUrl url = item.targetUrl();
1189         if (item.isFile()) {
1190             url.setPath(QFileInfo(url.path()).absolutePath());
1191         }
1192         if (!urls.contains(url)) {
1193             urls << url;
1194         }
1195     }
1196 
1197     // No items are selected. Open a terminal window for the current location.
1198     if (urls.count() == 0) {
1199         openTerminal();
1200         return;
1201     }
1202 
1203     if (urls.count() > 5) {
1204         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());
1205         const int answer = KMessageBox::warningContinueCancel(
1206             this,
1207             question,
1208             {},
1209             KGuiItem(i18ncp("@action:button", "Open %1 Terminal", "Open %1 Terminals", urls.count()), QStringLiteral("utilities-terminal")),
1210             KStandardGuiItem::cancel(),
1211             QStringLiteral("ConfirmOpenManyTerminals"));
1212         if (answer != KMessageBox::PrimaryAction && answer != KMessageBox::Continue) {
1213             return;
1214         }
1215     }
1216 
1217     for (const QUrl &url : std::as_const(urls)) {
1218         openTerminalJob(url);
1219     }
1220 }
1221 
1222 void DolphinMainWindow::openTerminalJob(const QUrl &url)
1223 {
1224     if (url.isLocalFile()) {
1225         auto job = new KTerminalLauncherJob(QString());
1226         job->setWorkingDirectory(url.toLocalFile());
1227         job->start();
1228         return;
1229     }
1230 
1231     // Not a local file, with protocol Class ":local", try stat'ing
1232     if (KProtocolInfo::protocolClass(url.scheme()) == QLatin1String(":local")) {
1233         KIO::StatJob *job = KIO::mostLocalUrl(url);
1234         KJobWidgets::setWindow(job, this);
1235         connect(job, &KJob::result, this, [job]() {
1236             QUrl statUrl;
1237             if (!job->error()) {
1238                 statUrl = job->mostLocalUrl();
1239             }
1240 
1241             auto job = new KTerminalLauncherJob(QString());
1242             job->setWorkingDirectory(statUrl.isLocalFile() ? statUrl.toLocalFile() : QDir::homePath());
1243             job->start();
1244         });
1245 
1246         return;
1247     }
1248 
1249     // Nothing worked, just use $HOME
1250     auto job = new KTerminalLauncherJob(QString());
1251     job->setWorkingDirectory(QDir::homePath());
1252     job->start();
1253 }
1254 
1255 void DolphinMainWindow::editSettings()
1256 {
1257     if (!m_settingsDialog) {
1258         DolphinViewContainer *container = activeViewContainer();
1259         container->view()->writeSettings();
1260 
1261         const QUrl url = container->url();
1262         DolphinSettingsDialog *settingsDialog = new DolphinSettingsDialog(url, this, actionCollection());
1263         connect(settingsDialog, &DolphinSettingsDialog::settingsChanged, this, &DolphinMainWindow::refreshViews);
1264         connect(settingsDialog, &DolphinSettingsDialog::settingsChanged, &DolphinUrlNavigatorsController::slotReadSettings);
1265         settingsDialog->setAttribute(Qt::WA_DeleteOnClose);
1266         settingsDialog->show();
1267         m_settingsDialog = settingsDialog;
1268     } else {
1269         m_settingsDialog.data()->raise();
1270     }
1271 }
1272 
1273 void DolphinMainWindow::handleUrl(const QUrl &url)
1274 {
1275     delete m_lastHandleUrlOpenJob;
1276     m_lastHandleUrlOpenJob = nullptr;
1277 
1278     if (url.isLocalFile() && QFileInfo(url.toLocalFile()).isDir()) {
1279         activeViewContainer()->setUrl(url);
1280     } else {
1281         m_lastHandleUrlOpenJob = new KIO::OpenUrlJob(url);
1282         m_lastHandleUrlOpenJob->setUiDelegate(KIO::createDefaultJobUiDelegate(KJobUiDelegate::AutoHandlingEnabled, this));
1283         m_lastHandleUrlOpenJob->setShowOpenOrExecuteDialog(true);
1284 
1285         connect(m_lastHandleUrlOpenJob, &KIO::OpenUrlJob::mimeTypeFound, this, [this, url](const QString &mimetype) {
1286             if (mimetype == QLatin1String("inode/directory")) {
1287                 // If it's a dir, we'll take it from here
1288                 m_lastHandleUrlOpenJob->kill();
1289                 m_lastHandleUrlOpenJob = nullptr;
1290                 activeViewContainer()->setUrl(url);
1291             }
1292         });
1293 
1294         connect(m_lastHandleUrlOpenJob, &KIO::OpenUrlJob::result, this, [this]() {
1295             m_lastHandleUrlOpenJob = nullptr;
1296         });
1297 
1298         m_lastHandleUrlOpenJob->start();
1299     }
1300 }
1301 
1302 void DolphinMainWindow::slotWriteStateChanged(bool isFolderWritable)
1303 {
1304     // trash:/ is writable but we don't want to create new items in it.
1305     // TODO: remove the trash check once https://phabricator.kde.org/T8234 is implemented
1306     newFileMenu()->setEnabled(isFolderWritable && m_activeViewContainer->url().scheme() != QLatin1String("trash"));
1307 }
1308 
1309 void DolphinMainWindow::openContextMenu(const QPoint &pos, const KFileItem &item, const KFileItemList &selectedItems, const QUrl &url)
1310 {
1311     QPointer<DolphinContextMenu> contextMenu = new DolphinContextMenu(this, item, selectedItems, url, &m_fileItemActions);
1312     contextMenu->exec(pos);
1313 
1314     // Delete the menu, unless it has been deleted in its own nested event loop already.
1315     if (contextMenu) {
1316         contextMenu->deleteLater();
1317     }
1318 }
1319 
1320 QMenu *DolphinMainWindow::createPopupMenu()
1321 {
1322     QMenu *menu = KXmlGuiWindow::createPopupMenu();
1323 
1324     menu->addSeparator();
1325     menu->addAction(actionCollection()->action(QStringLiteral("lock_panels")));
1326 
1327     return menu;
1328 }
1329 
1330 void DolphinMainWindow::updateHamburgerMenu()
1331 {
1332     KActionCollection *ac = actionCollection();
1333     auto hamburgerMenu = static_cast<KHamburgerMenu *>(ac->action(KStandardAction::name(KStandardAction::HamburgerMenu)));
1334     auto menu = hamburgerMenu->menu();
1335     if (!menu) {
1336         menu = new QMenu(this);
1337         hamburgerMenu->setMenu(menu);
1338         hamburgerMenu->hideActionsOf(ac->action(QStringLiteral("basic_actions"))->menu());
1339         hamburgerMenu->hideActionsOf(ac->action(QStringLiteral("zoom"))->menu());
1340     } else {
1341         menu->clear();
1342     }
1343     const QList<QAction *> toolbarActions = toolBar()->actions();
1344 
1345     if (!toolBar()->isVisible()) {
1346         // If neither the menu bar nor the toolbar are visible, these actions should be available.
1347         menu->addAction(ac->action(KStandardAction::name(KStandardAction::ShowMenubar)));
1348         menu->addAction(toolBarMenuAction());
1349         menu->addSeparator();
1350     }
1351 
1352     // This group of actions (until the next separator) contains all the most basic actions
1353     // necessary to use Dolphin effectively.
1354     menu->addAction(ac->action(QStringLiteral("go_back")));
1355     menu->addAction(ac->action(QStringLiteral("go_forward")));
1356 
1357     menu->addMenu(m_newFileMenu->menu());
1358     if (!toolBar()->isVisible() || !toolbarActions.contains(ac->action(QStringLiteral("toggle_selection_mode_tool_bar")))) {
1359         menu->addAction(ac->action(QStringLiteral("toggle_selection_mode")));
1360     }
1361     menu->addAction(ac->action(QStringLiteral("basic_actions")));
1362     menu->addAction(ac->action(KStandardAction::name(KStandardAction::Undo)));
1363     if (!toolBar()->isVisible()
1364         || (!toolbarActions.contains(ac->action(QStringLiteral("toggle_search")))
1365             && !toolbarActions.contains(ac->action(QStringLiteral("open_preferred_search_tool"))))) {
1366         menu->addAction(ac->action(KStandardAction::name(KStandardAction::Find)));
1367         // This way a search action will only be added if none of the three available
1368         // search actions is present on the toolbar.
1369     }
1370     if (!toolBar()->isVisible() || !toolbarActions.contains(ac->action(QStringLiteral("toggle_filter")))) {
1371         menu->addAction(ac->action(QStringLiteral("show_filter_bar")));
1372         // This way a filter action will only be added if none of the two available
1373         // filter actions is present on the toolbar.
1374     }
1375     menu->addSeparator();
1376 
1377     // The second group of actions (up until the next separator) contains actions for opening
1378     // additional views to interact with the file system.
1379     menu->addAction(ac->action(QStringLiteral("file_new")));
1380     menu->addAction(ac->action(QStringLiteral("new_tab")));
1381     if (ac->action(QStringLiteral("undo_close_tab"))->isEnabled()) {
1382         menu->addAction(ac->action(QStringLiteral("closed_tabs")));
1383     }
1384     menu->addAction(ac->action(QStringLiteral("open_terminal")));
1385     menu->addSeparator();
1386 
1387     // The third group contains actions to change what one sees in the view
1388     // and to change the more general UI.
1389     if (!toolBar()->isVisible()
1390         || (!toolbarActions.contains(ac->action(QStringLiteral("icons"))) && !toolbarActions.contains(ac->action(QStringLiteral("compact")))
1391             && !toolbarActions.contains(ac->action(QStringLiteral("details"))) && !toolbarActions.contains(ac->action(QStringLiteral("view_mode"))))) {
1392         menu->addAction(ac->action(QStringLiteral("view_mode")));
1393     }
1394     menu->addAction(ac->action(QStringLiteral("show_hidden_files")));
1395     menu->addAction(ac->action(QStringLiteral("sort")));
1396     menu->addAction(ac->action(QStringLiteral("additional_info")));
1397     if (!GeneralSettings::showStatusBar() || !GeneralSettings::showZoomSlider()) {
1398         menu->addAction(ac->action(QStringLiteral("zoom")));
1399     }
1400     menu->addAction(ac->action(QStringLiteral("panels")));
1401 
1402     // The "Configure" menu is not added to the actionCollection() because there is hardly
1403     // a good reason for users to put it on their toolbar.
1404     auto configureMenu = menu->addMenu(QIcon::fromTheme(QStringLiteral("configure")), i18nc("@action:inmenu menu for configure actions", "Configure"));
1405     configureMenu->addAction(ac->action(KStandardAction::name(KStandardAction::SwitchApplicationLanguage)));
1406     configureMenu->addAction(ac->action(KStandardAction::name(KStandardAction::KeyBindings)));
1407     configureMenu->addAction(ac->action(KStandardAction::name(KStandardAction::ConfigureToolbars)));
1408     configureMenu->addAction(ac->action(KStandardAction::name(KStandardAction::Preferences)));
1409     hamburgerMenu->hideActionsOf(configureMenu);
1410 }
1411 
1412 void DolphinMainWindow::slotPlaceActivated(const QUrl &url)
1413 {
1414     DolphinViewContainer *view = activeViewContainer();
1415 
1416     if (view->url() == url) {
1417         view->clearFilterBar(); // Fixes bug 259382.
1418 
1419         // We can end up here if the user clicked a device in the Places Panel
1420         // which had been unmounted earlier, see https://bugs.kde.org/show_bug.cgi?id=161385.
1421         reloadView();
1422     } else {
1423         view->disableUrlNavigatorSelectionRequests();
1424         changeUrl(url);
1425         view->enableUrlNavigatorSelectionRequests();
1426     }
1427 }
1428 
1429 void DolphinMainWindow::closedTabsCountChanged(unsigned int count)
1430 {
1431     actionCollection()->action(QStringLiteral("undo_close_tab"))->setEnabled(count > 0);
1432 }
1433 
1434 void DolphinMainWindow::activeViewChanged(DolphinViewContainer *viewContainer)
1435 {
1436     DolphinViewContainer *oldViewContainer = m_activeViewContainer;
1437     Q_ASSERT(viewContainer);
1438 
1439     m_activeViewContainer = viewContainer;
1440 
1441     if (oldViewContainer) {
1442         const QAction *toggleSearchAction = actionCollection()->action(QStringLiteral("toggle_search"));
1443         toggleSearchAction->disconnect(oldViewContainer);
1444 
1445         // Disconnect all signals between the old view container (container,
1446         // view and url navigator) and main window.
1447         oldViewContainer->disconnect(this);
1448         oldViewContainer->view()->disconnect(this);
1449         oldViewContainer->urlNavigatorInternalWithHistory()->disconnect(this);
1450         auto navigators = static_cast<DolphinNavigatorsWidgetAction *>(actionCollection()->action(QStringLiteral("url_navigators")));
1451         navigators->primaryUrlNavigator()->disconnect(this);
1452         if (auto secondaryUrlNavigator = navigators->secondaryUrlNavigator()) {
1453             secondaryUrlNavigator->disconnect(this);
1454         }
1455 
1456         // except the requestItemInfo so that on hover the information panel can still be updated
1457         connect(oldViewContainer->view(), &DolphinView::requestItemInfo, this, &DolphinMainWindow::requestItemInfo);
1458 
1459         // Disconnect other slots.
1460         disconnect(oldViewContainer,
1461                    &DolphinViewContainer::selectionModeChanged,
1462                    actionCollection()->action(QStringLiteral("toggle_selection_mode")),
1463                    &QAction::setChecked);
1464     }
1465 
1466     connectViewSignals(viewContainer);
1467 
1468     m_actionHandler->setCurrentView(viewContainer->view());
1469 
1470     updateHistory();
1471     updateFileAndEditActions();
1472     updatePasteAction();
1473     updateViewActions();
1474     updateGoActions();
1475     updateSearchAction();
1476 
1477     const QUrl url = viewContainer->url();
1478     Q_EMIT urlChanged(url);
1479 }
1480 
1481 void DolphinMainWindow::tabCountChanged(int count)
1482 {
1483     const bool enableTabActions = (count > 1);
1484     for (int i = 0; i < MaxActivateTabShortcuts; ++i) {
1485         actionCollection()->action(QStringLiteral("activate_tab_%1").arg(i))->setEnabled(enableTabActions);
1486     }
1487     actionCollection()->action(QStringLiteral("activate_last_tab"))->setEnabled(enableTabActions);
1488     actionCollection()->action(QStringLiteral("activate_next_tab"))->setEnabled(enableTabActions);
1489     actionCollection()->action(QStringLiteral("activate_prev_tab"))->setEnabled(enableTabActions);
1490 }
1491 
1492 void DolphinMainWindow::updateWindowTitle()
1493 {
1494     const QString newTitle = m_activeViewContainer->captionWindowTitle();
1495     if (windowTitle() != newTitle) {
1496         setWindowTitle(newTitle);
1497     }
1498 }
1499 
1500 void DolphinMainWindow::slotStorageTearDownFromPlacesRequested(const QString &mountPath)
1501 {
1502     connect(m_placesPanel, &PlacesPanel::storageTearDownSuccessful, this, [this, mountPath]() {
1503         setViewsToHomeIfMountPathOpen(mountPath);
1504     });
1505 
1506     if (m_terminalPanel && m_terminalPanel->currentWorkingDirectoryIsChildOf(mountPath)) {
1507         m_tearDownFromPlacesRequested = true;
1508         m_terminalPanel->goHome();
1509         // m_placesPanel->proceedWithTearDown() will be called in slotTerminalDirectoryChanged
1510     } else {
1511         m_placesPanel->proceedWithTearDown();
1512     }
1513 }
1514 
1515 void DolphinMainWindow::slotStorageTearDownExternallyRequested(const QString &mountPath)
1516 {
1517     connect(m_placesPanel, &PlacesPanel::storageTearDownSuccessful, this, [this, mountPath]() {
1518         setViewsToHomeIfMountPathOpen(mountPath);
1519     });
1520 
1521     if (m_terminalPanel && m_terminalPanel->currentWorkingDirectoryIsChildOf(mountPath)) {
1522         m_tearDownFromPlacesRequested = false;
1523         m_terminalPanel->goHome();
1524     }
1525 }
1526 
1527 void DolphinMainWindow::slotKeyBindings()
1528 {
1529     KShortcutsDialog dialog(KShortcutsEditor::AllActions, KShortcutsEditor::LetterShortcutsAllowed, this);
1530     dialog.addCollection(actionCollection());
1531     if (m_terminalPanel) {
1532         KActionCollection *konsolePartActionCollection = m_terminalPanel->actionCollection();
1533         if (konsolePartActionCollection) {
1534             dialog.addCollection(konsolePartActionCollection, QStringLiteral("KonsolePart"));
1535         }
1536     }
1537     dialog.configure();
1538 }
1539 
1540 void DolphinMainWindow::setViewsToHomeIfMountPathOpen(const QString &mountPath)
1541 {
1542     const QVector<DolphinViewContainer *> theViewContainers = viewContainers();
1543     for (DolphinViewContainer *viewContainer : theViewContainers) {
1544         if (viewContainer && viewContainer->url().toLocalFile().startsWith(mountPath)) {
1545             viewContainer->setUrl(QUrl::fromLocalFile(QDir::homePath()));
1546         }
1547     }
1548     disconnect(m_placesPanel, &PlacesPanel::storageTearDownSuccessful, nullptr, nullptr);
1549 }
1550 
1551 void DolphinMainWindow::setupActions()
1552 {
1553     auto hamburgerMenuAction = KStandardAction::hamburgerMenu(nullptr, nullptr, actionCollection());
1554 
1555     // setup 'File' menu
1556     m_newFileMenu = new DolphinNewFileMenu(nullptr, this);
1557     actionCollection()->addAction(QStringLiteral("new_menu"), m_newFileMenu);
1558     QMenu *menu = m_newFileMenu->menu();
1559     menu->setTitle(i18nc("@title:menu Create new folder, file, link, etc.", "Create New"));
1560     menu->setIcon(QIcon::fromTheme(QStringLiteral("list-add")));
1561     m_newFileMenu->setPopupMode(QToolButton::InstantPopup);
1562     connect(menu, &QMenu::aboutToShow, this, &DolphinMainWindow::updateNewMenu);
1563 
1564     QAction *newWindow = KStandardAction::openNew(this, &DolphinMainWindow::openNewMainWindow, actionCollection());
1565     newWindow->setText(i18nc("@action:inmenu File", "New &Window"));
1566     newWindow->setToolTip(i18nc("@info", "Open a new Dolphin window"));
1567     newWindow->setWhatsThis(xi18nc("@info:whatsthis",
1568                                    "This opens a new "
1569                                    "window just like this one with the current location and view."
1570                                    "<nl/>You can drag and drop items between windows."));
1571     newWindow->setIcon(QIcon::fromTheme(QStringLiteral("window-new")));
1572 
1573     QAction *newTab = actionCollection()->addAction(QStringLiteral("new_tab"));
1574     newTab->setIcon(QIcon::fromTheme(QStringLiteral("tab-new")));
1575     newTab->setText(i18nc("@action:inmenu File", "New Tab"));
1576     newTab->setWhatsThis(xi18nc("@info:whatsthis",
1577                                 "This opens a new "
1578                                 "<emphasis>Tab</emphasis> with the current location and view.<nl/>"
1579                                 "A tab is an additional view within this window. "
1580                                 "You can drag and drop items between tabs."));
1581     actionCollection()->setDefaultShortcut(newTab, Qt::CTRL | Qt::Key_T);
1582     connect(newTab, &QAction::triggered, this, &DolphinMainWindow::openNewActivatedTab);
1583 
1584     QAction *addToPlaces = actionCollection()->addAction(QStringLiteral("add_to_places"));
1585     addToPlaces->setIcon(QIcon::fromTheme(QStringLiteral("bookmark-new")));
1586     addToPlaces->setText(i18nc("@action:inmenu Add current folder to places", "Add to Places"));
1587     addToPlaces->setWhatsThis(xi18nc("@info:whatsthis",
1588                                      "This adds the selected folder "
1589                                      "to the Places panel."));
1590     connect(addToPlaces, &QAction::triggered, this, &DolphinMainWindow::addToPlaces);
1591 
1592     QAction *closeTab = KStandardAction::close(m_tabWidget, QOverload<>::of(&DolphinTabWidget::closeTab), actionCollection());
1593     closeTab->setText(i18nc("@action:inmenu File", "Close Tab"));
1594     closeTab->setWhatsThis(i18nc("@info:whatsthis",
1595                                  "This closes the "
1596                                  "currently viewed tab. If no more tabs are left this window "
1597                                  "will close instead."));
1598 
1599     QAction *quitAction = KStandardAction::quit(this, &DolphinMainWindow::quit, actionCollection());
1600     quitAction->setWhatsThis(i18nc("@info:whatsthis quit", "This closes this window."));
1601 
1602     // setup 'Edit' menu
1603     KStandardAction::undo(this, &DolphinMainWindow::undo, actionCollection());
1604 
1605     // i18n: This will be the last paragraph for the whatsthis for all three:
1606     // Cut, Copy and Paste
1607     const QString cutCopyPastePara = xi18nc("@info:whatsthis",
1608                                             "<para><emphasis>Cut, "
1609                                             "Copy</emphasis> and <emphasis>Paste</emphasis> work between many "
1610                                             "applications and are among the most used commands. That's why their "
1611                                             "<emphasis>keyboard shortcuts</emphasis> are prominently placed right "
1612                                             "next to each other on the keyboard: <shortcut>Ctrl+X</shortcut>, "
1613                                             "<shortcut>Ctrl+C</shortcut> and <shortcut>Ctrl+V</shortcut>.</para>");
1614     QAction *cutAction = KStandardAction::cut(this, &DolphinMainWindow::cut, actionCollection());
1615     m_actionTextHelper->registerTextWhenNothingIsSelected(cutAction, i18nc("@action", "Cut…"));
1616     cutAction->setWhatsThis(xi18nc("@info:whatsthis cut",
1617                                    "This copies the items "
1618                                    "in your current selection to the <emphasis>clipboard</emphasis>.<nl/>"
1619                                    "Use the <emphasis>Paste</emphasis> action afterwards to copy them from "
1620                                    "the clipboard to a new location. The items will be removed from their "
1621                                    "initial location.")
1622                             + cutCopyPastePara);
1623     QAction *copyAction = KStandardAction::copy(this, &DolphinMainWindow::copy, actionCollection());
1624     m_actionTextHelper->registerTextWhenNothingIsSelected(copyAction, i18nc("@action", "Copy…"));
1625     copyAction->setWhatsThis(xi18nc("@info:whatsthis copy",
1626                                     "This copies the "
1627                                     "items in your current selection to the <emphasis>clipboard</emphasis>."
1628                                     "<nl/>Use the <emphasis>Paste</emphasis> action afterwards to copy them "
1629                                     "from the clipboard to a new location.")
1630                              + cutCopyPastePara);
1631     QAction *paste = KStandardAction::paste(this, &DolphinMainWindow::paste, actionCollection());
1632     // The text of the paste-action is modified dynamically by Dolphin
1633     // (e. g. to "Paste One Folder"). To prevent that the size of the toolbar changes
1634     // due to the long text, the text "Paste" is used:
1635     paste->setIconText(i18nc("@action:inmenu Edit", "Paste"));
1636     paste->setWhatsThis(xi18nc("@info:whatsthis paste",
1637                                "This copies the items from "
1638                                "your <emphasis>clipboard</emphasis> to the currently viewed folder.<nl/>"
1639                                "If the items were added to the clipboard by the <emphasis>Cut</emphasis> "
1640                                "action they are removed from their old location.")
1641                         + cutCopyPastePara);
1642 
1643     QAction *copyToOtherViewAction = actionCollection()->addAction(QStringLiteral("copy_to_inactive_split_view"));
1644     copyToOtherViewAction->setText(i18nc("@action:inmenu", "Copy to Other View"));
1645     m_actionTextHelper->registerTextWhenNothingIsSelected(copyToOtherViewAction, i18nc("@action:inmenu", "Copy to Other View…"));
1646     copyToOtherViewAction->setWhatsThis(xi18nc("@info:whatsthis Copy",
1647                                                "This copies the selected items from "
1648                                                "the <emphasis>active</emphasis> view to the inactive split view."));
1649     copyToOtherViewAction->setIcon(QIcon::fromTheme(QStringLiteral("edit-copy")));
1650     copyToOtherViewAction->setIconText(i18nc("@action:inmenu Edit", "Copy to Inactive Split View"));
1651     actionCollection()->setDefaultShortcut(copyToOtherViewAction, Qt::SHIFT | Qt::Key_F5);
1652     connect(copyToOtherViewAction, &QAction::triggered, this, &DolphinMainWindow::copyToInactiveSplitView);
1653 
1654     QAction *moveToOtherViewAction = actionCollection()->addAction(QStringLiteral("move_to_inactive_split_view"));
1655     moveToOtherViewAction->setText(i18nc("@action:inmenu", "Move to Other View"));
1656     m_actionTextHelper->registerTextWhenNothingIsSelected(moveToOtherViewAction, i18nc("@action:inmenu", "Move to Other View…"));
1657     moveToOtherViewAction->setWhatsThis(xi18nc("@info:whatsthis Move",
1658                                                "This moves the selected items from "
1659                                                "the <emphasis>active</emphasis> view to the inactive split view."));
1660     moveToOtherViewAction->setIcon(QIcon::fromTheme(QStringLiteral("edit-cut")));
1661     moveToOtherViewAction->setIconText(i18nc("@action:inmenu Edit", "Move to Inactive Split View"));
1662     actionCollection()->setDefaultShortcut(moveToOtherViewAction, Qt::SHIFT | Qt::Key_F6);
1663     connect(moveToOtherViewAction, &QAction::triggered, this, &DolphinMainWindow::moveToInactiveSplitView);
1664 
1665     QAction *showFilterBar = actionCollection()->addAction(QStringLiteral("show_filter_bar"));
1666     showFilterBar->setText(i18nc("@action:inmenu Tools", "Filter…"));
1667     showFilterBar->setToolTip(i18nc("@info:tooltip", "Show Filter Bar"));
1668     showFilterBar->setWhatsThis(xi18nc("@info:whatsthis",
1669                                        "This opens the "
1670                                        "<emphasis>Filter Bar</emphasis> at the bottom of the window.<nl/> "
1671                                        "There you can enter a text to filter the files and folders currently displayed. "
1672                                        "Only those that contain the text in their name will be kept in view."));
1673     showFilterBar->setIcon(QIcon::fromTheme(QStringLiteral("view-filter")));
1674     actionCollection()->setDefaultShortcuts(showFilterBar, {Qt::CTRL | Qt::Key_I, Qt::Key_Slash});
1675     connect(showFilterBar, &QAction::triggered, this, &DolphinMainWindow::showFilterBar);
1676 
1677     // toggle_filter acts as a copy of the main showFilterBar to be used mainly
1678     // in the toolbar, with no default shortcut attached, to avoid messing with
1679     // existing workflows (filter bar always open and Ctrl-I to focus)
1680     QAction *toggleFilter = actionCollection()->addAction(QStringLiteral("toggle_filter"));
1681     toggleFilter->setText(i18nc("@action:inmenu", "Toggle Filter Bar"));
1682     toggleFilter->setIconText(i18nc("@action:intoolbar", "Filter"));
1683     toggleFilter->setIcon(showFilterBar->icon());
1684     toggleFilter->setToolTip(showFilterBar->toolTip());
1685     toggleFilter->setWhatsThis(showFilterBar->whatsThis());
1686     toggleFilter->setCheckable(true);
1687     connect(toggleFilter, &QAction::triggered, this, &DolphinMainWindow::toggleFilterBar);
1688 
1689     QAction *searchAction = KStandardAction::find(this, &DolphinMainWindow::find, actionCollection());
1690     searchAction->setText(i18n("Search…"));
1691     searchAction->setToolTip(i18nc("@info:tooltip", "Search for files and folders"));
1692     searchAction->setWhatsThis(xi18nc("@info:whatsthis find",
1693                                       "<para>This helps you "
1694                                       "find files and folders by opening a <emphasis>find bar</emphasis>. "
1695                                       "There you can enter search terms and specify settings to find the "
1696                                       "objects you are looking for.</para><para>Use this help again on "
1697                                       "the find bar so we can have a look at it while the settings are "
1698                                       "explained.</para>"));
1699 
1700     // toggle_search acts as a copy of the main searchAction to be used mainly
1701     // in the toolbar, with no default shortcut attached, to avoid messing with
1702     // existing workflows (search bar always open and Ctrl-F to focus)
1703     QAction *toggleSearchAction = actionCollection()->addAction(QStringLiteral("toggle_search"));
1704     toggleSearchAction->setText(i18nc("@action:inmenu", "Toggle Search Bar"));
1705     toggleSearchAction->setIconText(i18nc("@action:intoolbar", "Search"));
1706     toggleSearchAction->setIcon(searchAction->icon());
1707     toggleSearchAction->setToolTip(searchAction->toolTip());
1708     toggleSearchAction->setWhatsThis(searchAction->whatsThis());
1709     toggleSearchAction->setCheckable(true);
1710 
1711     QAction *toggleSelectionModeAction = actionCollection()->addAction(QStringLiteral("toggle_selection_mode"));
1712     // i18n: This action toggles a selection mode.
1713     toggleSelectionModeAction->setText(i18nc("@action:inmenu", "Select Files and Folders"));
1714     // i18n: Opens a selection mode for selecting files/folders.
1715     // The text is kept so unspecific because it will be shown on the toolbar where space is at a premium.
1716     toggleSelectionModeAction->setIconText(i18nc("@action:intoolbar", "Select"));
1717     toggleSelectionModeAction->setWhatsThis(xi18nc(
1718         "@info:whatsthis",
1719         "<para>This application only knows which files or folders should be acted on if they are"
1720         " <emphasis>selected</emphasis> first. Press this to toggle a <emphasis>Selection Mode</emphasis> which makes selecting and deselecting as easy as "
1721         "pressing an item once.</para><para>While in this mode, a quick access bar at the bottom shows available actions for the currently selected items."
1722         "</para>"));
1723     toggleSelectionModeAction->setIcon(QIcon::fromTheme(QStringLiteral("quickwizard")));
1724     toggleSelectionModeAction->setCheckable(true);
1725     actionCollection()->setDefaultShortcut(toggleSelectionModeAction, Qt::Key_Space);
1726     connect(toggleSelectionModeAction, &QAction::triggered, this, &DolphinMainWindow::toggleSelectionMode);
1727 
1728     // A special version of the toggleSelectionModeAction for the toolbar that also contains a menu
1729     // with the selectAllAction and invertSelectionAction.
1730     auto *toggleSelectionModeToolBarAction =
1731         new KToolBarPopupAction(toggleSelectionModeAction->icon(), toggleSelectionModeAction->iconText(), actionCollection());
1732     toggleSelectionModeToolBarAction->setToolTip(toggleSelectionModeAction->text());
1733     toggleSelectionModeToolBarAction->setWhatsThis(toggleSelectionModeAction->whatsThis());
1734     actionCollection()->addAction(QStringLiteral("toggle_selection_mode_tool_bar"), toggleSelectionModeToolBarAction);
1735     toggleSelectionModeToolBarAction->setCheckable(true);
1736     toggleSelectionModeToolBarAction->setPopupMode(KToolBarPopupAction::DelayedPopup);
1737     connect(toggleSelectionModeToolBarAction, &QAction::triggered, toggleSelectionModeAction, &QAction::trigger);
1738     connect(toggleSelectionModeAction, &QAction::toggled, toggleSelectionModeToolBarAction, &QAction::setChecked);
1739 
1740     QAction *selectAllAction = KStandardAction::selectAll(this, &DolphinMainWindow::selectAll, actionCollection());
1741     selectAllAction->setWhatsThis(xi18nc("@info:whatsthis",
1742                                          "This selects all "
1743                                          "files and folders in the current location."));
1744 
1745     QAction *invertSelection = actionCollection()->addAction(QStringLiteral("invert_selection"));
1746     invertSelection->setText(i18nc("@action:inmenu Edit", "Invert Selection"));
1747     invertSelection->setWhatsThis(xi18nc("@info:whatsthis invert",
1748                                          "This selects all "
1749                                          "objects that you have currently <emphasis>not</emphasis> selected instead."));
1750     invertSelection->setIcon(QIcon::fromTheme(QStringLiteral("edit-select-invert")));
1751     actionCollection()->setDefaultShortcut(invertSelection, Qt::CTRL | Qt::SHIFT | Qt::Key_A);
1752     connect(invertSelection, &QAction::triggered, this, &DolphinMainWindow::invertSelection);
1753 
1754     QMenu *toggleSelectionModeActionMenu = new QMenu(this);
1755     toggleSelectionModeActionMenu->addAction(selectAllAction);
1756     toggleSelectionModeActionMenu->addAction(invertSelection);
1757     toggleSelectionModeToolBarAction->setMenu(toggleSelectionModeActionMenu);
1758 
1759     // setup 'View' menu
1760     // (note that most of it is set up in DolphinViewActionHandler)
1761 
1762     QAction *split = actionCollection()->addAction(QStringLiteral("split_view"));
1763     split->setWhatsThis(xi18nc("@info:whatsthis find",
1764                                "<para>This splits "
1765                                "the folder view below into two autonomous views.</para><para>This "
1766                                "way you can see two locations at once and move items between them "
1767                                "quickly.</para>Click this again afterwards to recombine the views."));
1768     actionCollection()->setDefaultShortcut(split, Qt::Key_F3);
1769     connect(split, &QAction::triggered, this, &DolphinMainWindow::toggleSplitView);
1770 
1771     QAction *stashSplit = actionCollection()->addAction(QStringLiteral("split_stash"));
1772     actionCollection()->setDefaultShortcut(stashSplit, Qt::CTRL | Qt::Key_S);
1773     stashSplit->setText(i18nc("@action:intoolbar Stash", "Stash"));
1774     stashSplit->setToolTip(i18nc("@info", "Opens the stash virtual directory in a split window"));
1775     stashSplit->setIcon(QIcon::fromTheme(QStringLiteral("folder-stash")));
1776     stashSplit->setCheckable(false);
1777     QDBusConnectionInterface *sessionInterface = QDBusConnection::sessionBus().interface();
1778     stashSplit->setVisible(sessionInterface && sessionInterface->isServiceRegistered(QStringLiteral("org.kde.kio.StashNotifier")));
1779     connect(stashSplit, &QAction::triggered, this, &DolphinMainWindow::toggleSplitStash);
1780 
1781     QAction *redisplay = KStandardAction::redisplay(this, &DolphinMainWindow::reloadView, actionCollection());
1782     redisplay->setToolTip(i18nc("@info:tooltip", "Refresh view"));
1783     redisplay->setWhatsThis(xi18nc("@info:whatsthis refresh",
1784                                    "<para>This refreshes "
1785                                    "the folder view.</para>"
1786                                    "<para>If the contents of this folder have changed, refreshing will re-scan this folder "
1787                                    "and show you a newly-updated view of the files and folders contained here.</para>"
1788                                    "<para>If the view is split, this refreshes the one that is currently in focus.</para>"));
1789 
1790     QAction *stop = actionCollection()->addAction(QStringLiteral("stop"));
1791     stop->setText(i18nc("@action:inmenu View", "Stop"));
1792     stop->setToolTip(i18nc("@info", "Stop loading"));
1793     stop->setWhatsThis(i18nc("@info", "This stops the loading of the contents of the current folder."));
1794     stop->setIcon(QIcon::fromTheme(QStringLiteral("process-stop")));
1795     connect(stop, &QAction::triggered, this, &DolphinMainWindow::stopLoading);
1796 
1797     KToggleAction *editableLocation = actionCollection()->add<KToggleAction>(QStringLiteral("editable_location"));
1798     editableLocation->setText(i18nc("@action:inmenu Navigation Bar", "Editable Location"));
1799     editableLocation->setWhatsThis(xi18nc("@info:whatsthis",
1800                                           "This toggles the <emphasis>Location Bar</emphasis> to be "
1801                                           "editable so you can directly enter a location you want to go to.<nl/>"
1802                                           "You can also switch to editing by clicking to the right of the "
1803                                           "location and switch back by confirming the edited location."));
1804     actionCollection()->setDefaultShortcut(editableLocation, Qt::Key_F6);
1805     connect(editableLocation, &KToggleAction::triggered, this, &DolphinMainWindow::toggleEditLocation);
1806 
1807     QAction *replaceLocation = actionCollection()->addAction(QStringLiteral("replace_location"));
1808     replaceLocation->setText(i18nc("@action:inmenu Navigation Bar", "Replace Location"));
1809     // i18n: "enter" is used both in the meaning of "writing" and "going to" a new location here.
1810     // Both meanings are useful but not necessary to understand the use of "Replace Location".
1811     // So you might want to be more verbose in your language to convey the meaning but it's up to you.
1812     replaceLocation->setWhatsThis(xi18nc("@info:whatsthis",
1813                                          "This switches to editing the location and selects it "
1814                                          "so you can quickly enter a different location."));
1815     actionCollection()->setDefaultShortcuts(replaceLocation, {Qt::CTRL | Qt::Key_L, Qt::ALT | Qt::Key_D});
1816     connect(replaceLocation, &QAction::triggered, this, &DolphinMainWindow::replaceLocation);
1817 
1818     // setup 'Go' menu
1819     {
1820         QScopedPointer<QAction> backAction(KStandardAction::back(nullptr, nullptr, nullptr));
1821         m_backAction = new KToolBarPopupAction(backAction->icon(), backAction->text(), actionCollection());
1822         m_backAction->setObjectName(backAction->objectName());
1823         m_backAction->setShortcuts(backAction->shortcuts());
1824     }
1825     m_backAction->setPopupMode(KToolBarPopupAction::DelayedPopup);
1826     connect(m_backAction, &QAction::triggered, this, &DolphinMainWindow::goBack);
1827     connect(m_backAction->popupMenu(), &QMenu::aboutToShow, this, &DolphinMainWindow::slotAboutToShowBackPopupMenu);
1828     connect(m_backAction->popupMenu(), &QMenu::triggered, this, &DolphinMainWindow::slotGoBack);
1829     actionCollection()->addAction(m_backAction->objectName(), m_backAction);
1830 
1831     auto backShortcuts = m_backAction->shortcuts();
1832     // Prepend this shortcut, to avoid being hidden by the two-slot UI (#371130)
1833     backShortcuts.prepend(QKeySequence(Qt::Key_Backspace));
1834     actionCollection()->setDefaultShortcuts(m_backAction, backShortcuts);
1835 
1836     DolphinRecentTabsMenu *recentTabsMenu = new DolphinRecentTabsMenu(this);
1837     actionCollection()->addAction(QStringLiteral("closed_tabs"), recentTabsMenu);
1838     connect(m_tabWidget, &DolphinTabWidget::rememberClosedTab, recentTabsMenu, &DolphinRecentTabsMenu::rememberClosedTab);
1839     connect(recentTabsMenu, &DolphinRecentTabsMenu::restoreClosedTab, m_tabWidget, &DolphinTabWidget::restoreClosedTab);
1840     connect(recentTabsMenu, &DolphinRecentTabsMenu::closedTabsCountChanged, this, &DolphinMainWindow::closedTabsCountChanged);
1841 
1842     QAction *undoCloseTab = actionCollection()->addAction(QStringLiteral("undo_close_tab"));
1843     undoCloseTab->setText(i18nc("@action:inmenu File", "Undo close tab"));
1844     undoCloseTab->setWhatsThis(i18nc("@info:whatsthis undo close tab", "This returns you to the previously closed tab."));
1845     actionCollection()->setDefaultShortcut(undoCloseTab, Qt::CTRL | Qt::SHIFT | Qt::Key_T);
1846     undoCloseTab->setIcon(QIcon::fromTheme(QStringLiteral("edit-undo")));
1847     undoCloseTab->setEnabled(false);
1848     connect(undoCloseTab, &QAction::triggered, recentTabsMenu, &DolphinRecentTabsMenu::undoCloseTab);
1849 
1850     auto undoAction = actionCollection()->action(KStandardAction::name(KStandardAction::Undo));
1851     undoAction->setWhatsThis(xi18nc("@info:whatsthis",
1852                                     "This undoes "
1853                                     "the last change you made to files or folders.<nl/>"
1854                                     "Such changes include <interface>creating, renaming</interface> "
1855                                     "and <interface>moving</interface> them to a different location "
1856                                     "or to the <filename>Trash</filename>. <nl/>Changes that can't "
1857                                     "be undone will ask for your confirmation."));
1858     undoAction->setEnabled(false); // undo should be disabled by default
1859 
1860     {
1861         QScopedPointer<QAction> forwardAction(KStandardAction::forward(nullptr, nullptr, nullptr));
1862         m_forwardAction = new KToolBarPopupAction(forwardAction->icon(), forwardAction->text(), actionCollection());
1863         m_forwardAction->setObjectName(forwardAction->objectName());
1864         m_forwardAction->setShortcuts(forwardAction->shortcuts());
1865     }
1866     m_forwardAction->setPopupMode(KToolBarPopupAction::DelayedPopup);
1867     connect(m_forwardAction, &QAction::triggered, this, &DolphinMainWindow::goForward);
1868     connect(m_forwardAction->popupMenu(), &QMenu::aboutToShow, this, &DolphinMainWindow::slotAboutToShowForwardPopupMenu);
1869     connect(m_forwardAction->popupMenu(), &QMenu::triggered, this, &DolphinMainWindow::slotGoForward);
1870     actionCollection()->addAction(m_forwardAction->objectName(), m_forwardAction);
1871     actionCollection()->setDefaultShortcuts(m_forwardAction, m_forwardAction->shortcuts());
1872 
1873     // enable middle-click to open in a new tab
1874     auto *middleClickEventFilter = new MiddleClickActionEventFilter(this);
1875     connect(middleClickEventFilter, &MiddleClickActionEventFilter::actionMiddleClicked, this, &DolphinMainWindow::slotBackForwardActionMiddleClicked);
1876     m_backAction->popupMenu()->installEventFilter(middleClickEventFilter);
1877     m_forwardAction->popupMenu()->installEventFilter(middleClickEventFilter);
1878     KStandardAction::up(this, &DolphinMainWindow::goUp, actionCollection());
1879     QAction *homeAction = KStandardAction::home(this, &DolphinMainWindow::goHome, actionCollection());
1880     homeAction->setWhatsThis(xi18nc("@info:whatsthis",
1881                                     "Go to your "
1882                                     "<filename>Home</filename> folder.<nl/>Every user account "
1883                                     "has their own <filename>Home</filename> that contains their data "
1884                                     "including folders that contain personal application data."));
1885 
1886     // setup 'Tools' menu
1887     QAction *compareFiles = actionCollection()->addAction(QStringLiteral("compare_files"));
1888     compareFiles->setText(i18nc("@action:inmenu Tools", "Compare Files"));
1889     compareFiles->setIcon(QIcon::fromTheme(QStringLiteral("kompare")));
1890     compareFiles->setEnabled(false);
1891     connect(compareFiles, &QAction::triggered, this, &DolphinMainWindow::compareFiles);
1892 
1893     QAction *openPreferredSearchTool = actionCollection()->addAction(QStringLiteral("open_preferred_search_tool"));
1894     openPreferredSearchTool->setText(i18nc("@action:inmenu Tools", "Open Preferred Search Tool"));
1895     openPreferredSearchTool->setWhatsThis(xi18nc("@info:whatsthis",
1896                                                  "<para>This opens a preferred search tool for the viewed location.</para>"
1897                                                  "<para>Use <emphasis>More Search Tools</emphasis> menu to configure it.</para>"));
1898     openPreferredSearchTool->setIcon(QIcon::fromTheme(QStringLiteral("search")));
1899     actionCollection()->setDefaultShortcut(openPreferredSearchTool, Qt::CTRL | Qt::SHIFT | Qt::Key_F);
1900     connect(openPreferredSearchTool, &QAction::triggered, this, &DolphinMainWindow::openPreferredSearchTool);
1901 
1902     if (KAuthorized::authorize(QStringLiteral("shell_access"))) {
1903         QAction *openTerminal = actionCollection()->addAction(QStringLiteral("open_terminal"));
1904         openTerminal->setText(i18nc("@action:inmenu Tools", "Open Terminal"));
1905         openTerminal->setWhatsThis(xi18nc("@info:whatsthis",
1906                                           "<para>This opens a <emphasis>terminal</emphasis> application for the viewed location.</para>"
1907                                           "<para>To learn more about terminals use the help in the terminal application.</para>"));
1908         openTerminal->setIcon(QIcon::fromTheme(QStringLiteral("utilities-terminal")));
1909         actionCollection()->setDefaultShortcut(openTerminal, Qt::SHIFT | Qt::Key_F4);
1910         connect(openTerminal, &QAction::triggered, this, &DolphinMainWindow::openTerminal);
1911 
1912         QAction *openTerminalHere = actionCollection()->addAction(QStringLiteral("open_terminal_here"));
1913         // i18n: "Here" refers to the location(s) of the currently selected item(s) or the currently viewed location if nothing is selected.
1914         openTerminalHere->setText(i18nc("@action:inmenu Tools", "Open Terminal Here"));
1915         openTerminalHere->setWhatsThis(xi18nc("@info:whatsthis",
1916                                               "<para>This opens <emphasis>terminal</emphasis> applications for the selected items' locations.</para>"
1917                                               "<para>To learn more about terminals use the help in the terminal application.</para>"));
1918         openTerminalHere->setIcon(QIcon::fromTheme(QStringLiteral("utilities-terminal")));
1919         actionCollection()->setDefaultShortcut(openTerminalHere, Qt::SHIFT | Qt::ALT | Qt::Key_F4);
1920         connect(openTerminalHere, &QAction::triggered, this, &DolphinMainWindow::openTerminalHere);
1921 
1922 #if HAVE_TERMINAL
1923         QAction *focusTerminalPanel = actionCollection()->addAction(QStringLiteral("focus_terminal_panel"));
1924         focusTerminalPanel->setText(i18nc("@action:inmenu Tools", "Focus Terminal Panel"));
1925         focusTerminalPanel->setIcon(QIcon::fromTheme(QStringLiteral("swap-panels")));
1926         actionCollection()->setDefaultShortcut(focusTerminalPanel, Qt::CTRL | Qt::SHIFT | Qt::Key_F4);
1927         connect(focusTerminalPanel, &QAction::triggered, this, &DolphinMainWindow::focusTerminalPanel);
1928 #endif
1929     }
1930 
1931     // setup 'Bookmarks' menu
1932     KActionMenu *bookmarkMenu = new KActionMenu(i18nc("@title:menu", "&Bookmarks"), this);
1933     bookmarkMenu->setIcon(QIcon::fromTheme(QStringLiteral("bookmarks")));
1934     // Make the toolbar button version work properly on click
1935     bookmarkMenu->setPopupMode(QToolButton::InstantPopup);
1936     m_bookmarkHandler = new DolphinBookmarkHandler(this, actionCollection(), bookmarkMenu->menu(), this);
1937     actionCollection()->addAction(QStringLiteral("bookmarks"), bookmarkMenu);
1938 
1939     // setup 'Settings' menu
1940     KToggleAction *showMenuBar = KStandardAction::showMenubar(nullptr, nullptr, actionCollection());
1941     showMenuBar->setWhatsThis(xi18nc("@info:whatsthis",
1942                                      "<para>This switches between having a <emphasis>Menubar</emphasis> "
1943                                      "and having a <interface>%1</interface> button. Both "
1944                                      "contain mostly the same actions and configuration options.</para>"
1945                                      "<para>The Menubar takes up more space but allows for fast and organised access to all "
1946                                      "actions an application has to offer.</para><para>The <interface>%1</interface> button "
1947                                      "is simpler and small which makes triggering advanced actions more time consuming.</para>",
1948                                      hamburgerMenuAction->text().replace('&', "")));
1949     connect(showMenuBar,
1950             &KToggleAction::triggered, // Fixes #286822
1951             this,
1952             &DolphinMainWindow::toggleShowMenuBar,
1953             Qt::QueuedConnection);
1954 
1955     KToggleAction *showStatusBar = KStandardAction::showStatusbar(nullptr, nullptr, actionCollection());
1956     showStatusBar->setChecked(GeneralSettings::showStatusBar());
1957     connect(GeneralSettings::self(), &GeneralSettings::showStatusBarChanged, showStatusBar, &KToggleAction::setChecked);
1958     connect(showStatusBar, &KToggleAction::triggered, this, [this](bool checked) {
1959         GeneralSettings::setShowStatusBar(checked);
1960         refreshViews();
1961     });
1962 
1963     KStandardAction::keyBindings(this, &DolphinMainWindow::slotKeyBindings, actionCollection());
1964     KStandardAction::preferences(this, &DolphinMainWindow::editSettings, actionCollection());
1965 
1966     // not in menu actions
1967     QList<QKeySequence> nextTabKeys = KStandardShortcut::tabNext();
1968     nextTabKeys.append(QKeySequence(Qt::CTRL | Qt::Key_Tab));
1969 
1970     QList<QKeySequence> prevTabKeys = KStandardShortcut::tabPrev();
1971     prevTabKeys.append(QKeySequence(Qt::CTRL | Qt::SHIFT | Qt::Key_Tab));
1972 
1973     for (int i = 0; i < MaxActivateTabShortcuts; ++i) {
1974         QAction *activateTab = actionCollection()->addAction(QStringLiteral("activate_tab_%1").arg(i));
1975         activateTab->setText(i18nc("@action:inmenu", "Activate Tab %1", i + 1));
1976         activateTab->setEnabled(false);
1977         connect(activateTab, &QAction::triggered, this, [this, i]() {
1978             m_tabWidget->activateTab(i);
1979         });
1980 
1981         // only add default shortcuts for the first 9 tabs regardless of MaxActivateTabShortcuts
1982         if (i < 9) {
1983             actionCollection()->setDefaultShortcut(activateTab, QStringLiteral("Alt+%1").arg(i + 1));
1984         }
1985     }
1986 
1987     QAction *activateLastTab = actionCollection()->addAction(QStringLiteral("activate_last_tab"));
1988     activateLastTab->setText(i18nc("@action:inmenu", "Activate Last Tab"));
1989     activateLastTab->setEnabled(false);
1990     connect(activateLastTab, &QAction::triggered, m_tabWidget, &DolphinTabWidget::activateLastTab);
1991     actionCollection()->setDefaultShortcut(activateLastTab, Qt::ALT | Qt::Key_0);
1992 
1993     QAction *activateNextTab = actionCollection()->addAction(QStringLiteral("activate_next_tab"));
1994     activateNextTab->setIconText(i18nc("@action:inmenu", "Next Tab"));
1995     activateNextTab->setText(i18nc("@action:inmenu", "Activate Next Tab"));
1996     activateNextTab->setEnabled(false);
1997     connect(activateNextTab, &QAction::triggered, m_tabWidget, &DolphinTabWidget::activateNextTab);
1998     actionCollection()->setDefaultShortcuts(activateNextTab, nextTabKeys);
1999 
2000     QAction *activatePrevTab = actionCollection()->addAction(QStringLiteral("activate_prev_tab"));
2001     activatePrevTab->setIconText(i18nc("@action:inmenu", "Previous Tab"));
2002     activatePrevTab->setText(i18nc("@action:inmenu", "Activate Previous Tab"));
2003     activatePrevTab->setEnabled(false);
2004     connect(activatePrevTab, &QAction::triggered, m_tabWidget, &DolphinTabWidget::activatePrevTab);
2005     actionCollection()->setDefaultShortcuts(activatePrevTab, prevTabKeys);
2006 
2007     // for context menu
2008     QAction *showTarget = actionCollection()->addAction(QStringLiteral("show_target"));
2009     showTarget->setText(i18nc("@action:inmenu", "Show Target"));
2010     showTarget->setIcon(QIcon::fromTheme(QStringLiteral("document-open-folder")));
2011     showTarget->setEnabled(false);
2012     connect(showTarget, &QAction::triggered, this, &DolphinMainWindow::showTarget);
2013 
2014     QAction *openInNewTab = actionCollection()->addAction(QStringLiteral("open_in_new_tab"));
2015     openInNewTab->setText(i18nc("@action:inmenu", "Open in New Tab"));
2016     openInNewTab->setIcon(QIcon::fromTheme(QStringLiteral("tab-new")));
2017     connect(openInNewTab, &QAction::triggered, this, &DolphinMainWindow::openInNewTab);
2018 
2019     QAction *openInNewTabs = actionCollection()->addAction(QStringLiteral("open_in_new_tabs"));
2020     openInNewTabs->setText(i18nc("@action:inmenu", "Open in New Tabs"));
2021     openInNewTabs->setIcon(QIcon::fromTheme(QStringLiteral("tab-new")));
2022     connect(openInNewTabs, &QAction::triggered, this, &DolphinMainWindow::openInNewTab);
2023 
2024     QAction *openInNewWindow = actionCollection()->addAction(QStringLiteral("open_in_new_window"));
2025     openInNewWindow->setText(i18nc("@action:inmenu", "Open in New Window"));
2026     openInNewWindow->setIcon(QIcon::fromTheme(QStringLiteral("window-new")));
2027     connect(openInNewWindow, &QAction::triggered, this, &DolphinMainWindow::openInNewWindow);
2028 
2029     QAction *openInSplitViewAction = actionCollection()->addAction(QStringLiteral("open_in_split_view"));
2030     openInSplitViewAction->setText(i18nc("@action:inmenu", "Open in Split View"));
2031     openInSplitViewAction->setIcon(QIcon::fromTheme(QStringLiteral("view-right-new")));
2032     connect(openInSplitViewAction, &QAction::triggered, this, [this]() {
2033         openInSplitView(QUrl());
2034     });
2035 }
2036 
2037 void DolphinMainWindow::setupDockWidgets()
2038 {
2039     const bool lock = GeneralSettings::lockPanels();
2040 
2041     DolphinPlacesModelSingleton::instance().placesModel()->setPanelsLocked(lock);
2042 
2043     KDualAction *lockLayoutAction = actionCollection()->add<KDualAction>(QStringLiteral("lock_panels"));
2044     lockLayoutAction->setActiveText(i18nc("@action:inmenu Panels", "Unlock Panels"));
2045     lockLayoutAction->setActiveIcon(QIcon::fromTheme(QStringLiteral("object-unlocked")));
2046     lockLayoutAction->setInactiveText(i18nc("@action:inmenu Panels", "Lock Panels"));
2047     lockLayoutAction->setInactiveIcon(QIcon::fromTheme(QStringLiteral("object-locked")));
2048     lockLayoutAction->setWhatsThis(xi18nc("@info:whatsthis",
2049                                           "This "
2050                                           "switches between having panels <emphasis>locked</emphasis> or "
2051                                           "<emphasis>unlocked</emphasis>.<nl/>Unlocked panels can be "
2052                                           "dragged to the other side of the window and have a close "
2053                                           "button.<nl/>Locked panels are embedded more cleanly."));
2054     lockLayoutAction->setActive(lock);
2055     connect(lockLayoutAction, &KDualAction::triggered, this, &DolphinMainWindow::togglePanelLockState);
2056 
2057     // Setup "Information"
2058     DolphinDockWidget *infoDock = new DolphinDockWidget(i18nc("@title:window", "Information"));
2059     infoDock->setLocked(lock);
2060     infoDock->setObjectName(QStringLiteral("infoDock"));
2061     infoDock->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea);
2062 
2063 #if HAVE_BALOO
2064     InformationPanel *infoPanel = new InformationPanel(infoDock);
2065     infoPanel->setCustomContextMenuActions({lockLayoutAction});
2066     connect(infoPanel, &InformationPanel::urlActivated, this, &DolphinMainWindow::handleUrl);
2067     infoDock->setWidget(infoPanel);
2068 
2069     createPanelAction(QIcon::fromTheme(QStringLiteral("dialog-information")), Qt::Key_F11, infoDock, QStringLiteral("show_information_panel"));
2070 
2071     addDockWidget(Qt::RightDockWidgetArea, infoDock);
2072     connect(this, &DolphinMainWindow::urlChanged, infoPanel, &InformationPanel::setUrl);
2073     connect(this, &DolphinMainWindow::selectionChanged, infoPanel, &InformationPanel::setSelection);
2074     connect(this, &DolphinMainWindow::requestItemInfo, infoPanel, &InformationPanel::requestDelayedItemInfo);
2075     connect(this, &DolphinMainWindow::fileItemsChanged, infoPanel, &InformationPanel::slotFilesItemChanged);
2076 #endif
2077 
2078     // i18n: This is the last paragraph for the "What's This"-texts of all four panels.
2079     const QString panelWhatsThis = xi18nc("@info:whatsthis",
2080                                           "<para>To show or "
2081                                           "hide panels like this go to <interface>Menu|Panels</interface> "
2082                                           "or <interface>View|Panels</interface>.</para>");
2083 #if HAVE_BALOO
2084     actionCollection()
2085         ->action(QStringLiteral("show_information_panel"))
2086         ->setWhatsThis(xi18nc("@info:whatsthis",
2087                               "<para> This toggles the "
2088                               "<emphasis>information</emphasis> panel at the right side of the "
2089                               "window.</para><para>The panel provides in-depth information "
2090                               "about the items your mouse is hovering over or about the selected "
2091                               "items. Otherwise it informs you about the currently viewed folder.<nl/>"
2092                               "For single items a preview of their contents is provided.</para>"));
2093 #endif
2094     infoDock->setWhatsThis(xi18nc("@info:whatsthis",
2095                                   "<para>This panel "
2096                                   "provides in-depth information about the items your mouse is "
2097                                   "hovering over or about the selected items. Otherwise it informs "
2098                                   "you about the currently viewed folder.<nl/>For single items a "
2099                                   "preview of their contents is provided.</para><para>You can configure "
2100                                   "which and how details are given here by right-clicking.</para>")
2101                            + panelWhatsThis);
2102 
2103     // Setup "Folders"
2104     DolphinDockWidget *foldersDock = new DolphinDockWidget(i18nc("@title:window", "Folders"));
2105     foldersDock->setLocked(lock);
2106     foldersDock->setObjectName(QStringLiteral("foldersDock"));
2107     foldersDock->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea);
2108     FoldersPanel *foldersPanel = new FoldersPanel(foldersDock);
2109     foldersPanel->setCustomContextMenuActions({lockLayoutAction});
2110     foldersDock->setWidget(foldersPanel);
2111 
2112     createPanelAction(QIcon::fromTheme(QStringLiteral("folder")), Qt::Key_F7, foldersDock, QStringLiteral("show_folders_panel"));
2113 
2114     addDockWidget(Qt::LeftDockWidgetArea, foldersDock);
2115     connect(this, &DolphinMainWindow::urlChanged, foldersPanel, &FoldersPanel::setUrl);
2116     connect(foldersPanel, &FoldersPanel::folderActivated, this, &DolphinMainWindow::changeUrl);
2117     connect(foldersPanel, &FoldersPanel::folderInNewTab, this, &DolphinMainWindow::openNewTab);
2118     connect(foldersPanel, &FoldersPanel::folderInNewActiveTab, this, &DolphinMainWindow::openNewTabAndActivate);
2119     connect(foldersPanel, &FoldersPanel::errorMessage, this, &DolphinMainWindow::showErrorMessage);
2120 
2121     actionCollection()
2122         ->action(QStringLiteral("show_folders_panel"))
2123         ->setWhatsThis(xi18nc("@info:whatsthis",
2124                               "This toggles the "
2125                               "<emphasis>folders</emphasis> panel at the left side of the window."
2126                               "<nl/><nl/>It shows the folders of the <emphasis>file system"
2127                               "</emphasis> in a <emphasis>tree view</emphasis>."));
2128     foldersDock->setWhatsThis(xi18nc("@info:whatsthis",
2129                                      "<para>This panel "
2130                                      "shows the folders of the <emphasis>file system</emphasis> in a "
2131                                      "<emphasis>tree view</emphasis>.</para><para>Click a folder to go "
2132                                      "there. Click the arrow to the left of a folder to see its subfolders. "
2133                                      "This allows quick switching between any folders.</para>")
2134                               + panelWhatsThis);
2135 
2136     // Setup "Terminal"
2137 #if HAVE_TERMINAL
2138     if (KAuthorized::authorize(QStringLiteral("shell_access"))) {
2139         DolphinDockWidget *terminalDock = new DolphinDockWidget(i18nc("@title:window Shell terminal", "Terminal"));
2140         terminalDock->setLocked(lock);
2141         terminalDock->setObjectName(QStringLiteral("terminalDock"));
2142         terminalDock->setContentsMargins(0, 0, 0, 0);
2143         m_terminalPanel = new TerminalPanel(terminalDock);
2144         m_terminalPanel->setCustomContextMenuActions({lockLayoutAction});
2145         terminalDock->setWidget(m_terminalPanel);
2146 
2147         connect(m_terminalPanel, &TerminalPanel::hideTerminalPanel, terminalDock, &DolphinDockWidget::hide);
2148         connect(m_terminalPanel, &TerminalPanel::changeUrl, this, &DolphinMainWindow::slotTerminalDirectoryChanged);
2149         connect(terminalDock, &DolphinDockWidget::visibilityChanged, m_terminalPanel, &TerminalPanel::dockVisibilityChanged);
2150         connect(terminalDock, &DolphinDockWidget::visibilityChanged, this, &DolphinMainWindow::slotTerminalPanelVisibilityChanged);
2151 
2152         createPanelAction(QIcon::fromTheme(QStringLiteral("dialog-scripts")), Qt::Key_F4, terminalDock, QStringLiteral("show_terminal_panel"));
2153 
2154         addDockWidget(Qt::BottomDockWidgetArea, terminalDock);
2155         connect(this, &DolphinMainWindow::urlChanged, m_terminalPanel, &TerminalPanel::setUrl);
2156 
2157         if (GeneralSettings::version() < 200) {
2158             terminalDock->hide();
2159         }
2160 
2161         actionCollection()
2162             ->action(QStringLiteral("show_terminal_panel"))
2163             ->setWhatsThis(xi18nc("@info:whatsthis",
2164                                   "<para>This toggles the "
2165                                   "<emphasis>terminal</emphasis> panel at the bottom of the window."
2166                                   "<nl/>The location in the terminal will always match the folder "
2167                                   "view so you can navigate using either.</para><para>The terminal "
2168                                   "panel is not needed for basic computer usage but can be useful "
2169                                   "for advanced tasks. To learn more about terminals use the help "
2170                                   "in a standalone terminal application like Konsole.</para>"));
2171         terminalDock->setWhatsThis(xi18nc("@info:whatsthis",
2172                                           "<para>This is "
2173                                           "the <emphasis>terminal</emphasis> panel. It behaves like a "
2174                                           "normal terminal but will match the location of the folder view "
2175                                           "so you can navigate using either.</para><para>The terminal panel "
2176                                           "is not needed for basic computer usage but can be useful for "
2177                                           "advanced tasks. To learn more about terminals use the help in a "
2178                                           "standalone terminal application like Konsole.</para>")
2179                                    + panelWhatsThis);
2180     }
2181 #endif
2182 
2183     if (GeneralSettings::version() < 200) {
2184         infoDock->hide();
2185         foldersDock->hide();
2186     }
2187 
2188     // Setup "Places"
2189     DolphinDockWidget *placesDock = new DolphinDockWidget(i18nc("@title:window", "Places"));
2190     placesDock->setLocked(lock);
2191     placesDock->setObjectName(QStringLiteral("placesDock"));
2192     placesDock->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea);
2193 
2194     m_placesPanel = new PlacesPanel(placesDock);
2195     m_placesPanel->setCustomContextMenuActions({lockLayoutAction});
2196     placesDock->setWidget(m_placesPanel);
2197 
2198     createPanelAction(QIcon::fromTheme(QStringLiteral("compass")), Qt::Key_F9, placesDock, QStringLiteral("show_places_panel"));
2199 
2200     addDockWidget(Qt::LeftDockWidgetArea, placesDock);
2201     connect(m_placesPanel, &PlacesPanel::placeActivated, this, &DolphinMainWindow::slotPlaceActivated);
2202     connect(m_placesPanel, &PlacesPanel::tabRequested, this, &DolphinMainWindow::openNewTab);
2203     connect(m_placesPanel, &PlacesPanel::activeTabRequested, this, &DolphinMainWindow::openNewTabAndActivate);
2204     connect(m_placesPanel, &PlacesPanel::newWindowRequested, this, [this](const QUrl &url) {
2205         Dolphin::openNewWindow({url}, this);
2206     });
2207     connect(m_placesPanel, &PlacesPanel::openInSplitViewRequested, this, &DolphinMainWindow::openInSplitView);
2208     connect(m_placesPanel, &PlacesPanel::errorMessage, this, &DolphinMainWindow::showErrorMessage);
2209     connect(this, &DolphinMainWindow::urlChanged, m_placesPanel, &PlacesPanel::setUrl);
2210     connect(placesDock, &DolphinDockWidget::visibilityChanged, &DolphinUrlNavigatorsController::slotPlacesPanelVisibilityChanged);
2211     connect(this, &DolphinMainWindow::settingsChanged, m_placesPanel, &PlacesPanel::readSettings);
2212     connect(m_placesPanel, &PlacesPanel::storageTearDownRequested, this, &DolphinMainWindow::slotStorageTearDownFromPlacesRequested);
2213     connect(m_placesPanel, &PlacesPanel::storageTearDownExternallyRequested, this, &DolphinMainWindow::slotStorageTearDownExternallyRequested);
2214     DolphinUrlNavigatorsController::slotPlacesPanelVisibilityChanged(m_placesPanel->isVisible());
2215 
2216     auto actionShowAllPlaces = new QAction(QIcon::fromTheme(QStringLiteral("view-hidden")), i18nc("@item:inmenu", "Show Hidden Places"), this);
2217     actionShowAllPlaces->setCheckable(true);
2218     actionShowAllPlaces->setDisabled(true);
2219     actionShowAllPlaces->setWhatsThis(i18nc("@info:whatsthis",
2220                                             "This displays "
2221                                             "all places in the places panel that have been hidden. They will "
2222                                             "appear semi-transparent unless you uncheck their hide property."));
2223 
2224     connect(actionShowAllPlaces, &QAction::triggered, this, [this](bool checked) {
2225         m_placesPanel->setShowAll(checked);
2226     });
2227     connect(m_placesPanel, &PlacesPanel::allPlacesShownChanged, actionShowAllPlaces, &QAction::setChecked);
2228 
2229     actionCollection()
2230         ->action(QStringLiteral("show_places_panel"))
2231         ->setWhatsThis(xi18nc("@info:whatsthis",
2232                               "<para>This toggles the "
2233                               "<emphasis>places</emphasis> panel at the left side of the window."
2234                               "</para><para>It allows you to go to locations you have "
2235                               "bookmarked and to access disk or media attached to the computer "
2236                               "or to the network. It also contains sections to find recently "
2237                               "saved files or files of a certain type.</para>"));
2238     placesDock->setWhatsThis(xi18nc("@info:whatsthis",
2239                                     "<para>This is the "
2240                                     "<emphasis>Places</emphasis> panel. It allows you to go to locations "
2241                                     "you have bookmarked and to access disk or media attached to the "
2242                                     "computer or to the network. It also contains sections to find "
2243                                     "recently saved files or files of a certain type.</para><para>"
2244                                     "Click on an entry to go there. Click with the right mouse button "
2245                                     "instead to open any entry in a new tab or new window.</para>"
2246                                     "<para>New entries can be added by dragging folders onto this panel. "
2247                                     "Right-click any section or entry to hide it. Right-click an empty "
2248                                     "space on this panel and select <interface>Show Hidden Places"
2249                                     "</interface> to display it again.</para>")
2250                              + panelWhatsThis);
2251 
2252     // Add actions into the "Panels" menu
2253     KActionMenu *panelsMenu = new KActionMenu(i18nc("@action:inmenu View", "Show Panels"), this);
2254     actionCollection()->addAction(QStringLiteral("panels"), panelsMenu);
2255     panelsMenu->setIcon(QIcon::fromTheme(QStringLiteral("view-sidetree")));
2256     panelsMenu->setPopupMode(QToolButton::InstantPopup);
2257     const KActionCollection *ac = actionCollection();
2258     panelsMenu->addAction(ac->action(QStringLiteral("show_places_panel")));
2259 #if HAVE_BALOO
2260     panelsMenu->addAction(ac->action(QStringLiteral("show_information_panel")));
2261 #endif
2262     panelsMenu->addAction(ac->action(QStringLiteral("show_folders_panel")));
2263     panelsMenu->addAction(ac->action(QStringLiteral("show_terminal_panel")));
2264     panelsMenu->addSeparator();
2265     panelsMenu->addAction(actionShowAllPlaces);
2266     panelsMenu->addAction(lockLayoutAction);
2267 
2268     connect(panelsMenu->menu(), &QMenu::aboutToShow, this, [actionShowAllPlaces] {
2269         actionShowAllPlaces->setEnabled(DolphinPlacesModelSingleton::instance().placesModel()->hiddenCount());
2270     });
2271 }
2272 
2273 void DolphinMainWindow::updateFileAndEditActions()
2274 {
2275     const KFileItemList list = m_activeViewContainer->view()->selectedItems();
2276     const KActionCollection *col = actionCollection();
2277     KFileItemListProperties capabilitiesSource(list);
2278 
2279     QAction *renameAction = col->action(KStandardAction::name(KStandardAction::RenameFile));
2280     QAction *moveToTrashAction = col->action(KStandardAction::name(KStandardAction::MoveToTrash));
2281     QAction *deleteAction = col->action(KStandardAction::name(KStandardAction::DeleteFile));
2282     QAction *cutAction = col->action(KStandardAction::name(KStandardAction::Cut));
2283     QAction *duplicateAction = col->action(QStringLiteral("duplicate")); // see DolphinViewActionHandler
2284     QAction *addToPlacesAction = col->action(QStringLiteral("add_to_places"));
2285     QAction *copyToOtherViewAction = col->action(QStringLiteral("copy_to_inactive_split_view"));
2286     QAction *moveToOtherViewAction = col->action(QStringLiteral("move_to_inactive_split_view"));
2287     QAction *copyLocation = col->action(QString("copy_location"));
2288 
2289     if (list.isEmpty()) {
2290         stateChanged(QStringLiteral("has_no_selection"));
2291 
2292         // All actions that need a selection to function can be enabled because they should trigger selection mode.
2293         renameAction->setEnabled(true);
2294         moveToTrashAction->setEnabled(true);
2295         deleteAction->setEnabled(true);
2296         cutAction->setEnabled(true);
2297         duplicateAction->setEnabled(true);
2298         addToPlacesAction->setEnabled(true);
2299         copyLocation->setEnabled(true);
2300         // Them triggering selection mode and not directly acting on selected items is signified by adding "…" to their text.
2301         m_actionTextHelper->textsWhenNothingIsSelectedEnabled(true);
2302 
2303     } else {
2304         m_actionTextHelper->textsWhenNothingIsSelectedEnabled(false);
2305         stateChanged(QStringLiteral("has_selection"));
2306 
2307         QAction *deleteWithTrashShortcut = col->action(QStringLiteral("delete_shortcut")); // see DolphinViewActionHandler
2308         QAction *showTarget = col->action(QStringLiteral("show_target"));
2309 
2310         if (list.length() == 1 && list.first().isDir()) {
2311             addToPlacesAction->setEnabled(true);
2312         } else {
2313             addToPlacesAction->setEnabled(false);
2314         }
2315 
2316         const bool enableMoveToTrash = capabilitiesSource.isLocal() && capabilitiesSource.supportsMoving();
2317 
2318         renameAction->setEnabled(capabilitiesSource.supportsMoving());
2319         moveToTrashAction->setEnabled(enableMoveToTrash);
2320         deleteAction->setEnabled(capabilitiesSource.supportsDeleting());
2321         deleteWithTrashShortcut->setEnabled(capabilitiesSource.supportsDeleting() && !enableMoveToTrash);
2322         cutAction->setEnabled(capabilitiesSource.supportsMoving());
2323         copyLocation->setEnabled(list.length() == 1);
2324         showTarget->setEnabled(list.length() == 1 && list.at(0).isLink());
2325         duplicateAction->setEnabled(capabilitiesSource.supportsWriting());
2326     }
2327 
2328     if (m_tabWidget->currentTabPage()->splitViewEnabled() && !list.isEmpty()) {
2329         DolphinTabPage *tabPage = m_tabWidget->currentTabPage();
2330         KFileItem capabilitiesDestination;
2331 
2332         if (tabPage->primaryViewActive()) {
2333             capabilitiesDestination = tabPage->secondaryViewContainer()->rootItem();
2334         } else {
2335             capabilitiesDestination = tabPage->primaryViewContainer()->rootItem();
2336         }
2337 
2338         const auto destUrl = capabilitiesDestination.url();
2339         const bool allNotTargetOrigin = std::all_of(list.cbegin(), list.cend(), [destUrl](const KFileItem &item) {
2340             return item.url().adjusted(QUrl::RemoveFilename | QUrl::StripTrailingSlash) != destUrl;
2341         });
2342 
2343         copyToOtherViewAction->setEnabled(capabilitiesDestination.isWritable() && allNotTargetOrigin);
2344         moveToOtherViewAction->setEnabled((list.isEmpty() || capabilitiesSource.supportsMoving()) && capabilitiesDestination.isWritable()
2345                                           && allNotTargetOrigin);
2346     } else {
2347         copyToOtherViewAction->setEnabled(false);
2348         moveToOtherViewAction->setEnabled(false);
2349     }
2350 }
2351 
2352 void DolphinMainWindow::updateViewActions()
2353 {
2354     m_actionHandler->updateViewActions();
2355 
2356     QAction *toggleFilterBarAction = actionCollection()->action(QStringLiteral("toggle_filter"));
2357     toggleFilterBarAction->setChecked(m_activeViewContainer->isFilterBarVisible());
2358 
2359     updateSplitAction();
2360 }
2361 
2362 void DolphinMainWindow::updateGoActions()
2363 {
2364     QAction *goUpAction = actionCollection()->action(KStandardAction::name(KStandardAction::Up));
2365     const QUrl currentUrl = m_activeViewContainer->url();
2366     // I think this is one of the best places to firstly be confronted
2367     // with a file system and its hierarchy. Talking about the root
2368     // directory might seem too much here but it is the question that
2369     // naturally arises in this context.
2370     goUpAction->setWhatsThis(xi18nc("@info:whatsthis",
2371                                     "<para>Go to "
2372                                     "the folder that contains the currently viewed one.</para>"
2373                                     "<para>All files and folders are organized in a hierarchical "
2374                                     "<emphasis>file system</emphasis>. At the top of this hierarchy is "
2375                                     "a directory that contains all data connected to this computer"
2376                                     "—the <emphasis>root directory</emphasis>.</para>"));
2377     goUpAction->setEnabled(KIO::upUrl(currentUrl) != currentUrl);
2378 }
2379 
2380 void DolphinMainWindow::refreshViews()
2381 {
2382     m_tabWidget->refreshViews();
2383 
2384     if (GeneralSettings::modifiedStartupSettings()) {
2385         updateWindowTitle();
2386     }
2387 
2388     updateSplitAction();
2389 
2390     Q_EMIT settingsChanged();
2391 }
2392 
2393 void DolphinMainWindow::clearStatusBar()
2394 {
2395     m_activeViewContainer->statusBar()->resetToDefaultText();
2396 }
2397 
2398 void DolphinMainWindow::connectViewSignals(DolphinViewContainer *container)
2399 {
2400     connect(container, &DolphinViewContainer::showFilterBarChanged, this, &DolphinMainWindow::updateFilterBarAction);
2401     connect(container, &DolphinViewContainer::writeStateChanged, this, &DolphinMainWindow::slotWriteStateChanged);
2402     slotWriteStateChanged(container->view()->isFolderWritable());
2403     connect(container, &DolphinViewContainer::searchModeEnabledChanged, this, &DolphinMainWindow::updateSearchAction);
2404     connect(container, &DolphinViewContainer::captionChanged, this, &DolphinMainWindow::updateWindowTitle);
2405     connect(container, &DolphinViewContainer::tabRequested, this, &DolphinMainWindow::openNewTab);
2406     connect(container, &DolphinViewContainer::activeTabRequested, this, &DolphinMainWindow::openNewTabAndActivate);
2407 
2408     const QAction *toggleSearchAction = actionCollection()->action(QStringLiteral("toggle_search"));
2409     connect(toggleSearchAction, &QAction::triggered, container, &DolphinViewContainer::setSearchModeEnabled);
2410 
2411     // Make the toggled state of the selection mode actions visually follow the selection mode state of the view.
2412     auto toggleSelectionModeAction = actionCollection()->action(QStringLiteral("toggle_selection_mode"));
2413     toggleSelectionModeAction->setChecked(m_activeViewContainer->isSelectionModeEnabled());
2414     connect(m_activeViewContainer, &DolphinViewContainer::selectionModeChanged, toggleSelectionModeAction, &QAction::setChecked);
2415 
2416     const DolphinView *view = container->view();
2417     connect(view, &DolphinView::selectionChanged, this, &DolphinMainWindow::slotSelectionChanged);
2418     connect(view, &DolphinView::requestItemInfo, this, &DolphinMainWindow::requestItemInfo);
2419     connect(view, &DolphinView::fileItemsChanged, this, &DolphinMainWindow::fileItemsChanged);
2420     connect(view, &DolphinView::tabRequested, this, &DolphinMainWindow::openNewTab);
2421     connect(view, &DolphinView::activeTabRequested, this, &DolphinMainWindow::openNewTabAndActivate);
2422     connect(view, &DolphinView::windowRequested, this, &DolphinMainWindow::openNewWindow);
2423     connect(view, &DolphinView::requestContextMenu, this, &DolphinMainWindow::openContextMenu);
2424     connect(view, &DolphinView::directoryLoadingStarted, this, &DolphinMainWindow::enableStopAction);
2425     connect(view, &DolphinView::directoryLoadingCompleted, this, &DolphinMainWindow::disableStopAction);
2426     connect(view, &DolphinView::directoryLoadingCompleted, this, &DolphinMainWindow::slotDirectoryLoadingCompleted);
2427     connect(view, &DolphinView::goBackRequested, this, &DolphinMainWindow::goBack);
2428     connect(view, &DolphinView::goForwardRequested, this, &DolphinMainWindow::goForward);
2429     connect(view, &DolphinView::urlActivated, this, &DolphinMainWindow::handleUrl);
2430     connect(view, &DolphinView::goUpRequested, this, &DolphinMainWindow::goUp);
2431 
2432     connect(container->urlNavigatorInternalWithHistory(), &KUrlNavigator::urlChanged, this, &DolphinMainWindow::changeUrl);
2433     connect(container->urlNavigatorInternalWithHistory(), &KUrlNavigator::historyChanged, this, &DolphinMainWindow::updateHistory);
2434 
2435     auto navigators = static_cast<DolphinNavigatorsWidgetAction *>(actionCollection()->action(QStringLiteral("url_navigators")));
2436     const KUrlNavigator *navigator =
2437         m_tabWidget->currentTabPage()->primaryViewActive() ? navigators->primaryUrlNavigator() : navigators->secondaryUrlNavigator();
2438 
2439     QAction *editableLocactionAction = actionCollection()->action(QStringLiteral("editable_location"));
2440     editableLocactionAction->setChecked(navigator->isUrlEditable());
2441     connect(navigator, &KUrlNavigator::editableStateChanged, this, &DolphinMainWindow::slotEditableStateChanged);
2442     connect(navigator, &KUrlNavigator::tabRequested, this, &DolphinMainWindow::openNewTab);
2443     connect(navigator, &KUrlNavigator::activeTabRequested, this, &DolphinMainWindow::openNewTabAndActivate);
2444     connect(navigator, &KUrlNavigator::newWindowRequested, this, &DolphinMainWindow::openNewWindow);
2445 }
2446 
2447 void DolphinMainWindow::updateSplitAction()
2448 {
2449     QAction *splitAction = actionCollection()->action(QStringLiteral("split_view"));
2450     const DolphinTabPage *tabPage = m_tabWidget->currentTabPage();
2451     if (tabPage->splitViewEnabled()) {
2452         if (GeneralSettings::closeActiveSplitView() ? tabPage->primaryViewActive() : !tabPage->primaryViewActive()) {
2453             splitAction->setText(i18nc("@action:intoolbar Close left view", "Close"));
2454             splitAction->setToolTip(i18nc("@info", "Close left view"));
2455             splitAction->setIcon(QIcon::fromTheme(QStringLiteral("view-left-close")));
2456         } else {
2457             splitAction->setText(i18nc("@action:intoolbar Close right view", "Close"));
2458             splitAction->setToolTip(i18nc("@info", "Close right view"));
2459             splitAction->setIcon(QIcon::fromTheme(QStringLiteral("view-right-close")));
2460         }
2461     } else {
2462         splitAction->setText(i18nc("@action:intoolbar Split view", "Split"));
2463         splitAction->setToolTip(i18nc("@info", "Split view"));
2464         splitAction->setIcon(QIcon::fromTheme(QStringLiteral("view-right-new")));
2465     }
2466 }
2467 
2468 void DolphinMainWindow::updateAllowedToolbarAreas()
2469 {
2470     auto navigators = static_cast<DolphinNavigatorsWidgetAction *>(actionCollection()->action(QStringLiteral("url_navigators")));
2471     if (toolBar()->actions().contains(navigators)) {
2472         toolBar()->setAllowedAreas(Qt::TopToolBarArea | Qt::BottomToolBarArea);
2473         if (toolBarArea(toolBar()) == Qt::LeftToolBarArea || toolBarArea(toolBar()) == Qt::RightToolBarArea) {
2474             addToolBar(Qt::TopToolBarArea, toolBar());
2475         }
2476     } else {
2477         toolBar()->setAllowedAreas(Qt::AllToolBarAreas);
2478     }
2479 }
2480 
2481 bool DolphinMainWindow::isKompareInstalled() const
2482 {
2483     static bool initialized = false;
2484     static bool installed = false;
2485     if (!initialized) {
2486         // TODO: maybe replace this approach later by using a menu
2487         // plugin like kdiff3plugin.cpp
2488         installed = !QStandardPaths::findExecutable(QStringLiteral("kompare")).isEmpty();
2489         initialized = true;
2490     }
2491     return installed;
2492 }
2493 
2494 void DolphinMainWindow::createPanelAction(const QIcon &icon, const QKeySequence &shortcut, QDockWidget *dockWidget, const QString &actionName)
2495 {
2496     auto dockAction = dockWidget->toggleViewAction();
2497     dockAction->setIcon(icon);
2498     dockAction->setEnabled(true);
2499 
2500     QAction *panelAction = actionCollection()->addAction(actionName, dockAction);
2501     actionCollection()->setDefaultShortcut(panelAction, shortcut);
2502 
2503     connect(panelAction, &QAction::toggled, dockWidget, &QWidget::setVisible);
2504 }
2505 // clang-format off
2506 void DolphinMainWindow::setupWhatsThis()
2507 {
2508     // main widgets
2509     menuBar()->setWhatsThis(xi18nc("@info:whatsthis", "<para>This is the "
2510         "<emphasis>Menubar</emphasis>. It provides access to commands and "
2511         "configuration options. Left-click on any of the menus on this "
2512         "bar to see its contents.</para><para>The Menubar can be hidden "
2513         "by unchecking <interface>Settings|Show Menubar</interface>. Then "
2514         "most of its contents become available through a <interface>Menu"
2515         "</interface> button on the <emphasis>Toolbar</emphasis>.</para>"));
2516     toolBar()->setWhatsThis(xi18nc("@info:whatsthis", "<para>This is the "
2517         "<emphasis>Toolbar</emphasis>. It allows quick access to "
2518         "frequently used actions.</para><para>It is highly customizable. "
2519         "All items you see in the <interface>Menu</interface> or "
2520         "in the <interface>Menubar</interface> can be placed on the "
2521         "Toolbar. Just right-click on it and select <interface>Configure "
2522         "Toolbars…</interface> or find this action within the <interface>"
2523         "menu</interface>."
2524         "</para><para>The location of the bar and the style of its "
2525         "buttons can also be changed in the right-click menu. Right-click "
2526         "a button if you want to show or hide its text.</para>"));
2527     m_tabWidget->setWhatsThis(xi18nc("@info:whatsthis main view",
2528         "<para>Here you can see the <emphasis>folders</emphasis> and "
2529         "<emphasis>files</emphasis> that are at the location described in "
2530         "the <interface>Location Bar</interface> above. This area is the "
2531         "central part of this application where you navigate to the files "
2532         "you want to use.</para><para>For an elaborate and general "
2533         "introduction to this application <link "
2534         "url='https://userbase.kde.org/Dolphin/File_Management#Introduction_to_Dolphin'>"
2535         "click here</link>. This will open an introductory article from "
2536         "the <emphasis>KDE UserBase Wiki</emphasis>.</para><para>For brief "
2537         "explanations of all the features of this <emphasis>view</emphasis> "
2538         "<link url='help:/dolphin/dolphin-view.html'>click here</link> "
2539         "instead. This will open a page from the <emphasis>Handbook"
2540         "</emphasis> that covers the basics.</para>"));
2541 
2542     // Settings menu
2543     actionCollection()->action(KStandardAction::name(KStandardAction::KeyBindings))
2544         ->setWhatsThis(xi18nc("@info:whatsthis","<para>This opens a window "
2545         "that lists the <emphasis>keyboard shortcuts</emphasis>.<nl/>"
2546         "There you can set up key combinations to trigger an action when "
2547         "they are pressed simultaneously. All commands in this application can "
2548         "be triggered this way.</para>"));
2549     actionCollection()->action(KStandardAction::name(KStandardAction::ConfigureToolbars))
2550         ->setWhatsThis(xi18nc("@info:whatsthis","<para>This opens a window in which "
2551         "you can change which buttons appear on the <emphasis>Toolbar</emphasis>.</para>"
2552         "<para>All items you see in the <interface>Menu</interface> can also be placed on the Toolbar.</para>"));
2553     actionCollection()->action(KStandardAction::name(KStandardAction::Preferences))
2554         ->setWhatsThis(xi18nc("@info:whatsthis","This opens a window where you can "
2555         "change a multitude of settings for this application. For an explanation "
2556         "of the various settings go to the chapter <emphasis>Configuring Dolphin"
2557         "</emphasis> in <interface>Help|Dolphin Handbook</interface>."));
2558 
2559     // Help menu
2560 
2561     auto setStandardActionWhatsThis = [this](KStandardAction::StandardAction actionId,
2562                                              const QString &whatsThis) {
2563         // Check for the existence of an action since it can be restricted through the Kiosk system
2564         if (auto *action = actionCollection()->action(KStandardAction::name(actionId))) {
2565             action->setWhatsThis(whatsThis);
2566         }
2567     };
2568 
2569     // i18n: If the external link isn't available in your language it might make
2570     // sense to state the external link's language in brackets to not
2571     // frustrate the user. If there are multiple languages that the user might
2572     // know with a reasonable chance you might want to have 2 external links.
2573     // The same might be true for any external link you translate.
2574     setStandardActionWhatsThis(KStandardAction::HelpContents, xi18nc("@info:whatsthis handbook", "<para>This opens the Handbook for this application. It provides explanations for every part of <emphasis>Dolphin</emphasis>.</para><para>If you want more elaborate introductions to the different features of <emphasis>Dolphin</emphasis> <link url='https://userbase.kde.org/Dolphin/File_Management'>click here</link>. It will open the dedicated page in the KDE UserBase Wiki.</para>"));
2575     // (The i18n call should be completely in the line following the i18n: comment without any line breaks within the i18n call or the comment might not be correctly extracted. See: https://commits.kde.org/kxmlgui/a31135046e1b3335b5d7bbbe6aa9a883ce3284c1 )
2576 
2577     setStandardActionWhatsThis(KStandardAction::WhatsThis,
2578         xi18nc("@info:whatsthis whatsthis button",
2579         "<para>This is the button that invokes the help feature you are "
2580         "using right now! Click it, then click any component of this "
2581         "application to ask \"What's this?\" about it. The mouse cursor "
2582         "will change appearance if no help is available for a spot.</para>"
2583         "<para>There are two other ways to get help: "
2584         "The <link url='help:/dolphin/index.html'>Dolphin Handbook</link> and "
2585         "the <link url='https://userbase.kde.org/Dolphin/File_Management'>KDE "
2586         "UserBase Wiki</link>.</para><para>The \"What's this?\" help is "
2587         "missing in most other windows so don't get too used to this.</para>"));
2588 
2589     setStandardActionWhatsThis(KStandardAction::ReportBug,
2590         xi18nc("@info:whatsthis","<para>This opens a "
2591         "window that will guide you through reporting errors or flaws "
2592         "in this application or in other KDE software.</para>"
2593         "<para>High-quality bug reports are much appreciated. To learn "
2594         "how to make your bug report as effective as possible "
2595         "<link url='https://community.kde.org/Get_Involved/Bug_Reporting'>"
2596         "click here</link>.</para>"));
2597 
2598     setStandardActionWhatsThis(KStandardAction::Donate,
2599         xi18nc("@info:whatsthis", "<para>This opens a "
2600         "<emphasis>web page</emphasis> where you can donate to "
2601         "support the continued work on this application and many "
2602         "other projects by the <emphasis>KDE</emphasis> community.</para>"
2603         "<para>Donating is the easiest and fastest way to efficiently "
2604         "support KDE and its projects. KDE projects are available for "
2605         "free therefore your donation is needed to cover things that "
2606         "require money like servers, contributor meetings, etc.</para>"
2607         "<para><emphasis>KDE e.V.</emphasis> is the non-profit "
2608         "organization behind the KDE community.</para>"));
2609 
2610     setStandardActionWhatsThis(KStandardAction::SwitchApplicationLanguage,
2611         xi18nc("@info:whatsthis",
2612         "With this you can change the language this application uses."
2613         "<nl/>You can even set secondary languages which will be used "
2614         "if texts are not available in your preferred language."));
2615 
2616     setStandardActionWhatsThis(KStandardAction::AboutApp,
2617         xi18nc("@info:whatsthis","This opens a "
2618         "window that informs you about the version, license, "
2619         "used libraries and maintainers of this application."));
2620 
2621     setStandardActionWhatsThis(KStandardAction::AboutKDE,
2622         xi18nc("@info:whatsthis","This opens a "
2623         "window with information about <emphasis>KDE</emphasis>. "
2624         "The KDE community are the people behind this free software."
2625         "<nl/>If you like using this application but don't know "
2626         "about KDE or want to see a cute dragon have a look!"));
2627 }
2628 // clang-format on
2629 
2630 bool DolphinMainWindow::addHamburgerMenuToToolbar()
2631 {
2632     QDomDocument domDocument = KXMLGUIClient::domDocument();
2633     if (domDocument.isNull()) {
2634         return false;
2635     }
2636     QDomNode toolbar = domDocument.elementsByTagName(QStringLiteral("ToolBar")).at(0);
2637     if (toolbar.isNull()) {
2638         return false;
2639     }
2640 
2641     QDomElement hamburgerMenuElement = domDocument.createElement(QStringLiteral("Action"));
2642     hamburgerMenuElement.setAttribute(QStringLiteral("name"), QStringLiteral("hamburger_menu"));
2643     toolbar.appendChild(hamburgerMenuElement);
2644 
2645     KXMLGUIFactory::saveConfigFile(domDocument, xmlFile());
2646     reloadXML();
2647     createGUI();
2648     return true;
2649     // Make sure to also remove the <KXMLGUIFactory> and <QDomDocument> include
2650     // whenever this method is removed (maybe in the year ~2026).
2651 }
2652 
2653 // Set a sane initial window size
2654 QSize DolphinMainWindow::sizeHint() const
2655 {
2656     return KXmlGuiWindow::sizeHint().expandedTo(QSize(760, 550));
2657 }
2658 
2659 void DolphinMainWindow::saveNewToolbarConfig()
2660 {
2661     KXmlGuiWindow::saveNewToolbarConfig(); // Applies the new config. This has to be called first
2662                                            // because the rest of this method decides things
2663                                            // based on the new config.
2664     auto navigators = static_cast<DolphinNavigatorsWidgetAction *>(actionCollection()->action(QStringLiteral("url_navigators")));
2665     if (!toolBar()->actions().contains(navigators)) {
2666         m_tabWidget->currentTabPage()->insertNavigatorsWidget(navigators);
2667     }
2668     updateAllowedToolbarAreas();
2669     (static_cast<KHamburgerMenu *>(actionCollection()->action(KStandardAction::name(KStandardAction::HamburgerMenu))))->hideActionsOf(toolBar());
2670 }
2671 
2672 void DolphinMainWindow::focusTerminalPanel()
2673 {
2674     if (m_terminalPanel->isVisible()) {
2675         if (m_terminalPanel->terminalHasFocus()) {
2676             m_activeViewContainer->view()->setFocus(Qt::FocusReason::ShortcutFocusReason);
2677             actionCollection()->action(QStringLiteral("focus_terminal_panel"))->setText(i18nc("@action:inmenu Tools", "Focus Terminal Panel"));
2678         } else {
2679             m_terminalPanel->setFocus(Qt::FocusReason::ShortcutFocusReason);
2680             actionCollection()->action(QStringLiteral("focus_terminal_panel"))->setText(i18nc("@action:inmenu Tools", "Defocus Terminal Panel"));
2681         }
2682     } else {
2683         actionCollection()->action(QStringLiteral("show_terminal_panel"))->trigger();
2684         actionCollection()->action(QStringLiteral("focus_terminal_panel"))->setText(i18nc("@action:inmenu Tools", "Defocus Terminal Panel"));
2685     }
2686 }
2687 
2688 DolphinMainWindow::UndoUiInterface::UndoUiInterface()
2689     : KIO::FileUndoManager::UiInterface()
2690 {
2691 }
2692 
2693 DolphinMainWindow::UndoUiInterface::~UndoUiInterface()
2694 {
2695 }
2696 
2697 void DolphinMainWindow::UndoUiInterface::jobError(KIO::Job *job)
2698 {
2699     DolphinMainWindow *mainWin = qobject_cast<DolphinMainWindow *>(parentWidget());
2700     if (mainWin) {
2701         DolphinViewContainer *container = mainWin->activeViewContainer();
2702         container->showMessage(job->errorString(), DolphinViewContainer::Error);
2703     } else {
2704         KIO::FileUndoManager::UiInterface::jobError(job);
2705     }
2706 }
2707 
2708 bool DolphinMainWindow::isUrlOpen(const QString &url)
2709 {
2710     return m_tabWidget->isUrlOpen(QUrl::fromUserInput(url));
2711 }
2712 
2713 bool DolphinMainWindow::isItemVisibleInAnyView(const QString &urlOfItem)
2714 {
2715     return m_tabWidget->isItemVisibleInAnyView(QUrl::fromUserInput(urlOfItem));
2716 }
2717 
2718 #include "moc_dolphinmainwindow.cpp"