X-Git-Url: https://cloud.milkyroute.net/gitweb/dolphin.git/blobdiff_plain/6cec386192e58089b4572996a265c9696cdcb3c0..8fda69892284144a8aea3845d2aad844021c99d2:/src/dolphinmainwindow.cpp diff --git a/src/dolphinmainwindow.cpp b/src/dolphinmainwindow.cpp index f14849e6d..589c2c185 100644 --- a/src/dolphinmainwindow.cpp +++ b/src/dolphinmainwindow.cpp @@ -17,12 +17,12 @@ #include "dolphinnavigatorswidgetaction.h" #include "dolphinnewfilemenu.h" #include "dolphinrecenttabsmenu.h" +#include "dolphinplacesmodelsingleton.h" #include "dolphinurlnavigatorscontroller.h" #include "dolphinviewcontainer.h" #include "dolphintabpage.h" #include "middleclickactioneventfilter.h" #include "panels/folders/folderspanel.h" -#include "panels/places/placesitemmodel.h" #include "panels/places/placespanel.h" #include "panels/terminal/terminalpanel.h" #include "settings/dolphinsettingsdialog.h" @@ -53,22 +53,27 @@ #include #include #include +#include #include #include #include +#include #include #include #include -#include #include #include #include +#include + +#include #include #include #include #include #include +#include #include #include #include @@ -82,7 +87,7 @@ namespace { // Used for GeneralSettings::version() to determine whether // an updated version of Dolphin is running. - const int CurrentDolphinVersion = 200; + const int CurrentDolphinVersion = 201; // The maximum number of entries in the back/forward popup menu const int MaxNumberOfNavigationentries = 12; // The maximum number of "Activate Tab" shortcuts @@ -118,13 +123,15 @@ DolphinMainWindow::DolphinMainWindow() : setComponentName(QStringLiteral("dolphin"), QGuiApplication::applicationDisplayName()); setObjectName(QStringLiteral("Dolphin#")); + setStateConfigGroup("State"); + connect(&DolphinNewFileMenuObserver::instance(), &DolphinNewFileMenuObserver::errorMessage, this, &DolphinMainWindow::showErrorMessage); KIO::FileUndoManager* undoManager = KIO::FileUndoManager::self(); undoManager->setUiInterface(new UndoUiInterface()); - connect(undoManager, QOverload::of(&KIO::FileUndoManager::undoAvailable), + connect(undoManager, &KIO::FileUndoManager::undoAvailable, this, &DolphinMainWindow::slotUndoAvailable); connect(undoManager, &KIO::FileUndoManager::undoTextChanged, this, &DolphinMainWindow::slotUndoTextChanged); @@ -164,7 +171,7 @@ DolphinMainWindow::DolphinMainWindow() : setupDockWidgets(); - setupGUI(Keys | Save | Create | ToolBar); + setupGUI(Save | Create | ToolBar); stateChanged(QStringLiteral("new_file")); QClipboard* clipboard = QApplication::clipboard(); @@ -176,15 +183,21 @@ DolphinMainWindow::DolphinMainWindow() : if (firstRun) { menuBar()->setVisible(false); - // Assure a proper default size if Dolphin runs the first time - resize(760, 550); } const bool showMenu = !menuBar()->isHidden(); QAction* showMenuBarAction = actionCollection()->action(KStandardAction::name(KStandardAction::ShowMenubar)); showMenuBarAction->setChecked(showMenu); // workaround for bug #171080 - if (!showMenu) { - createControlButton(); + + auto hamburgerMenu = static_cast(actionCollection()->action( + KStandardAction::name(KStandardAction::HamburgerMenu))); + hamburgerMenu->setMenuBar(menuBar()); + hamburgerMenu->setShowMenuBarAction(showMenuBarAction); + connect(hamburgerMenu, &KHamburgerMenu::aboutToShowMenu, + this, &DolphinMainWindow::updateHamburgerMenu); + hamburgerMenu->hideActionsOf(toolBar()); + if (GeneralSettings::version() < 201 && !toolBar()->actions().contains(hamburgerMenu)) { + addHamburgerMenuToToolbar(); } updateAllowedToolbarAreas(); @@ -196,13 +209,23 @@ DolphinMainWindow::DolphinMainWindow() : setupWhatsThis(); - connect(KSycoca::self(), QOverload<>::of(&KSycoca::databaseChanged), this, &DolphinMainWindow::updateOpenPreferredSearchToolAction); + connect(KSycoca::self(), &KSycoca::databaseChanged, this, &DolphinMainWindow::updateOpenPreferredSearchToolAction); QTimer::singleShot(0, this, &DolphinMainWindow::updateOpenPreferredSearchToolAction); + + m_fileItemActions.setParentWidget(this); + connect(&m_fileItemActions, &KFileItemActions::error, this, [this](const QString &errorMessage) { + showErrorMessage(errorMessage); + }); + + connect(GeneralSettings::self(), &GeneralSettings::splitViewChanged, + this, &DolphinMainWindow::slotSplitViewChanged); } DolphinMainWindow::~DolphinMainWindow() { + // This fixes a crash on Wayland when closing the mainwindow while another dialog is open. + disconnect(QGuiApplication::clipboard(), &QClipboard::dataChanged, this, &DolphinMainWindow::updatePasteAction); } QVector DolphinMainWindow::viewContainers() const @@ -220,20 +243,6 @@ QVector DolphinMainWindow::viewContainers() const return viewContainers; } -void DolphinMainWindow::setViewsWithInvalidPathsToHome() -{ - const QVector theViewContainers = viewContainers(); - for (DolphinViewContainer *viewContainer : theViewContainers) { - - // Only consider local dirs, not remote locations and abstract protocols - if (viewContainer->url().isLocalFile()) { - if (!QFileInfo::exists(viewContainer->url().toLocalFile())) { - viewContainer->setUrl(QUrl::fromLocalFile(QDir::homePath())); - } - } - } -} - void DolphinMainWindow::openDirectories(const QList& dirs, bool splitView) { m_tabWidget->openDirectories(dirs, splitView); @@ -418,14 +427,13 @@ void DolphinMainWindow::addToPlaces() name = dirToAdd.name(); } if (url.isValid()) { - PlacesItemModel model; QString icon; if (m_activeViewContainer->isSearchModeEnabled()) { icon = QStringLiteral("folder-saved-search-symbolic"); } else { icon = KIO::iconNameForUrl(url); } - model.createPlacesItem(name, url, icon); + DolphinPlacesModelSingleton::instance().placesModel()->addPlace(name, url, icon); } } @@ -434,6 +442,22 @@ void DolphinMainWindow::openNewTab(const QUrl& url) m_tabWidget->openNewTab(url, QUrl()); } +void DolphinMainWindow::openNewTabAndActivate(const QUrl &url) +{ + m_tabWidget->openNewActivatedTab(url, QUrl()); +} + +void DolphinMainWindow::openNewWindow(const QUrl &url) +{ + Dolphin::openNewWindow({url}, this); +} + +void DolphinMainWindow::slotSplitViewChanged() +{ + m_tabWidget->currentTabPage()->setSplitViewEnabled(GeneralSettings::splitView(), WithAnimation); + updateSplitAction(); +} + void DolphinMainWindow::openInNewTab() { const KFileItemList& list = m_activeViewContainer->view()->selectedItems(); @@ -630,14 +654,12 @@ void DolphinMainWindow::readProperties(const KConfigGroup& group) void DolphinMainWindow::updateNewMenu() { - m_newFileMenu->setViewShowsHiddenFiles(activeViewContainer()->view()->hiddenFilesShown()); m_newFileMenu->checkUpToDate(); m_newFileMenu->setPopupFiles(QList() << activeViewContainer()->url()); } void DolphinMainWindow::createDirectory() { - m_newFileMenu->setViewShowsHiddenFiles(activeViewContainer()->view()->hiddenFilesShown()); m_newFileMenu->setPopupFiles(QList() << activeViewContainer()->url()); m_newFileMenu->createDirectory(); } @@ -727,13 +749,45 @@ void DolphinMainWindow::slotToolBarActionMiddleClicked(QAction *action) } } +QAction *DolphinMainWindow::urlNavigatorHistoryAction(const KUrlNavigator *urlNavigator, int historyIndex, QObject *parent) +{ + const QUrl url = urlNavigator->locationUrl(historyIndex); + + QString text = url.toDisplayString(QUrl::PreferLocalFile); + + if (!urlNavigator->showFullPath()) { + const KFilePlacesModel *placesModel = DolphinPlacesModelSingleton::instance().placesModel(); + + const QModelIndex closestIdx = placesModel->closestItem(url); + if (closestIdx.isValid()) { + const QUrl placeUrl = placesModel->url(closestIdx); + + text = placesModel->text(closestIdx); + + QString pathInsidePlace = url.path().mid(placeUrl.path().length()); + + if (!pathInsidePlace.isEmpty() && !pathInsidePlace.startsWith(QLatin1Char('/'))) { + pathInsidePlace.prepend(QLatin1Char('/')); + } + + if (pathInsidePlace != QLatin1Char('/')) { + text.append(pathInsidePlace); + } + } + } + + QAction *action = new QAction(QIcon::fromTheme(KIO::iconNameForUrl(url)), text, parent); + action->setData(historyIndex); + return action; +} + void DolphinMainWindow::slotAboutToShowBackPopupMenu() { const KUrlNavigator *urlNavigator = m_activeViewContainer->urlNavigatorInternalWithHistory(); int entries = 0; m_backAction->menu()->clear(); for (int i = urlNavigator->historyIndex() + 1; i < urlNavigator->historySize() && entries < MaxNumberOfNavigationentries; ++i, ++entries) { - QAction* action = new QAction(urlNavigator->locationUrl(i).toString(QUrl::PreferLocalFile), m_backAction->menu()); + QAction *action = urlNavigatorHistoryAction(urlNavigator, i, m_backAction->menu()); action->setData(i); m_backAction->menu()->addAction(action); } @@ -762,7 +816,7 @@ void DolphinMainWindow::slotAboutToShowForwardPopupMenu() int entries = 0; m_forwardAction->menu()->clear(); for (int i = urlNavigator->historyIndex() - 1; i >= 0 && entries < MaxNumberOfNavigationentries; --i, ++entries) { - QAction* action = new QAction(urlNavigator->locationUrl(i).toString(QUrl::PreferLocalFile), m_forwardAction->menu()); + QAction *action = urlNavigatorHistoryAction(urlNavigator, i, m_forwardAction->menu()); action->setData(i); m_forwardAction->menu()->addAction(action); } @@ -890,6 +944,8 @@ void DolphinMainWindow::togglePanelLockState() } } + DolphinPlacesModelSingleton::instance().placesModel()->setPanelsLocked(newLockState); + GeneralSettings::setLockPanels(newLockState); } @@ -979,11 +1035,6 @@ void DolphinMainWindow::toggleShowMenuBar() { const bool visible = menuBar()->isVisible(); menuBar()->setVisible(!visible); - if (visible) { - createControlButton(); - } else { - deleteControlButton(); - } } QPointer DolphinMainWindow::preferredSearchTool() @@ -1013,7 +1064,11 @@ void DolphinMainWindow::updateOpenPreferredSearchToolAction() if (tool) { openPreferredSearchTool->setVisible(true); openPreferredSearchTool->setText(i18nc("@action:inmenu Tools", "Open %1", tool->text())); - openPreferredSearchTool->setIcon(tool->icon()); + // Only override with the app icon if it is the default, i.e. the user hasn't configured one manually + // https://bugs.kde.org/show_bug.cgi?id=442815 + if (openPreferredSearchTool->icon().name() == QLatin1String("search")) { + openPreferredSearchTool->setIcon(tool->icon()); + } } else { openPreferredSearchTool->setVisible(false); // still visible in Shortcuts configuration window @@ -1035,7 +1090,9 @@ void DolphinMainWindow::openTerminal() const QUrl url = m_activeViewContainer->url(); if (url.isLocalFile()) { - KToolInvocation::invokeTerminal(QString(), {}, url.toLocalFile()); + auto job = new KTerminalLauncherJob(QString()); + job->setWorkingDirectory(url.toLocalFile()); + job->start(); return; } @@ -1049,14 +1106,18 @@ void DolphinMainWindow::openTerminal() statUrl = job->mostLocalUrl(); } - KToolInvocation::invokeTerminal(QString(), {}, statUrl.isLocalFile() ? statUrl.toLocalFile() : QDir::homePath()); + auto job = new KTerminalLauncherJob(QString()); + job->setWorkingDirectory(statUrl.isLocalFile() ? statUrl.toLocalFile() : QDir::homePath()); + job->start(); }); return; } // Nothing worked, just use $HOME - KToolInvocation::invokeTerminal(QString(), {}, QDir::homePath()); + auto job = new KTerminalLauncherJob(QString()); + job->setWorkingDirectory(QDir::homePath()); + job->start(); } void DolphinMainWindow::editSettings() @@ -1117,32 +1178,11 @@ void DolphinMainWindow::slotWriteStateChanged(bool isFolderWritable) void DolphinMainWindow::openContextMenu(const QPoint& pos, const KFileItem& item, - const QUrl& url, - const QList& customActions) + const KFileItemList &selectedItems, + const QUrl& url) { - QPointer contextMenu = new DolphinContextMenu(this, pos, item, url); - contextMenu.data()->setCustomActions(customActions); - const DolphinContextMenu::Command command = contextMenu.data()->open(); - - switch (command) { - case DolphinContextMenu::OpenParentFolder: - changeUrl(KIO::upUrl(item.url())); - m_activeViewContainer->view()->markUrlsAsSelected({item.url()}); - m_activeViewContainer->view()->markUrlAsCurrent(item.url()); - break; - - case DolphinContextMenu::OpenParentFolderInNewWindow: - Dolphin::openNewWindow({item.url()}, this, Dolphin::OpenNewWindowFlag::Select); - break; - - case DolphinContextMenu::OpenParentFolderInNewTab: - openNewTab(KIO::upUrl(item.url())); - break; - - case DolphinContextMenu::None: - default: - break; - } + QPointer contextMenu = new DolphinContextMenu(this, item, selectedItems, url, &m_fileItemActions); + contextMenu.data()->exec(pos); // Delete the menu, unless it has been deleted in its own nested event loop already. if (contextMenu) { @@ -1150,87 +1190,101 @@ void DolphinMainWindow::openContextMenu(const QPoint& pos, } } -void DolphinMainWindow::updateControlMenu() +QMenu *DolphinMainWindow::createPopupMenu() { - QMenu* menu = qobject_cast(sender()); - Q_ASSERT(menu); - - // All actions get cleared by QMenu::clear(). This includes the sub-menus - // because 'menu' is their parent. - menu->clear(); - - KActionCollection* ac = actionCollection(); - - menu->addMenu(m_newFileMenu->menu()); - addActionToMenu(ac->action(QStringLiteral("file_new")), menu); - addActionToMenu(ac->action(QStringLiteral("new_tab")), menu); - addActionToMenu(ac->action(QStringLiteral("closed_tabs")), menu); + QMenu *menu = KXmlGuiWindow::createPopupMenu(); menu->addSeparator(); + menu->addAction(actionCollection()->action(QStringLiteral("lock_panels"))); - // Add "Edit" actions - bool added = addActionToMenu(ac->action(KStandardAction::name(KStandardAction::Undo)), menu) | - addActionToMenu(ac->action(QString("copy_location")), menu) | - addActionToMenu(ac->action(QStringLiteral("copy_to_inactive_split_view")), menu) | - addActionToMenu(ac->action(QStringLiteral("move_to_inactive_split_view")), menu) | - addActionToMenu(ac->action(KStandardAction::name(KStandardAction::SelectAll)), menu) | - addActionToMenu(ac->action(QStringLiteral("invert_selection")), menu); + return menu; +} - if (added) { - menu->addSeparator(); +void DolphinMainWindow::updateHamburgerMenu() +{ + KActionCollection* ac = actionCollection(); + auto hamburgerMenu = static_cast( + ac->action(KStandardAction::name(KStandardAction::HamburgerMenu))); + auto menu = hamburgerMenu->menu(); + if (!menu) { + menu = new QMenu(this); + hamburgerMenu->setMenu(menu); + hamburgerMenu->hideActionsOf(ac->action(QStringLiteral("basic_actions"))->menu()); + hamburgerMenu->hideActionsOf(ac->action(QStringLiteral("zoom"))->menu()); + } else { + menu->clear(); } + const QList toolbarActions = toolBar()->actions(); - // Add "View" actions - if (!GeneralSettings::showZoomSlider()) { - addActionToMenu(ac->action(KStandardAction::name(KStandardAction::ZoomIn)), menu); - addActionToMenu(ac->action(QStringLiteral("view_zoom_reset")), menu); - addActionToMenu(ac->action(KStandardAction::name(KStandardAction::ZoomOut)), menu); + if (!toolBar()->isVisible()) { + // If neither the menu bar nor the toolbar are visible, these actions should be available. + menu->addAction(ac->action(KStandardAction::name(KStandardAction::ShowMenubar))); + menu->addAction(toolBarMenuAction()); menu->addSeparator(); } - added = addActionToMenu(ac->action(QStringLiteral("show_preview")), menu) | - addActionToMenu(ac->action(QStringLiteral("show_in_groups")), menu) | - addActionToMenu(ac->action(QStringLiteral("show_hidden_files")), menu) | - addActionToMenu(ac->action(QStringLiteral("additional_info")), menu) | - addActionToMenu(ac->action(QStringLiteral("view_properties")), menu); + // This group of actions (until the next separator) contains all the most basic actions + // necessary to use Dolphin effectively. + menu->addAction(ac->action(QStringLiteral("go_back"))); + menu->addAction(ac->action(QStringLiteral("go_forward"))); - if (added) { - menu->addSeparator(); + menu->addMenu(m_newFileMenu->menu()); + menu->addAction(ac->action(QStringLiteral("basic_actions"))); + menu->addAction(ac->action(KStandardAction::name(KStandardAction::Undo))); + if (!toolBar()->isVisible() + || (!toolbarActions.contains(ac->action(QStringLiteral("toggle_search"))) + && !toolbarActions.contains(ac->action(QStringLiteral("open_preferred_search_tool")))) + ) { + menu->addAction(ac->action(KStandardAction::name(KStandardAction::Find))); + // This way a search action will only be added if none of the three available + // search actions is present on the toolbar. + } + if (!toolBar()->isVisible() + || !toolbarActions.contains(ac->action(QStringLiteral("toggle_filter"))) + ) { + menu->addAction(ac->action(QStringLiteral("show_filter_bar"))); + // This way a filter action will only be added if none of the two available + // filter actions is present on the toolbar. } - - // Add a curated assortment of items from the "Tools" menu - addActionToMenu(ac->action(QStringLiteral("show_filter_bar")), menu); - addActionToMenu(ac->action(QStringLiteral("open_preferred_search_tool")), menu); - addActionToMenu(ac->action(QStringLiteral("open_terminal")), menu); - menu->addSeparator(); - // Add "Show Panels" menu - addActionToMenu(ac->action(QStringLiteral("panels")), menu); - - // Add "Settings" menu entries - addActionToMenu(ac->action(KStandardAction::name(KStandardAction::KeyBindings)), menu); - addActionToMenu(ac->action(KStandardAction::name(KStandardAction::ConfigureToolbars)), menu); - addActionToMenu(ac->action(KStandardAction::name(KStandardAction::Preferences)), menu); - addActionToMenu(ac->action(KStandardAction::name(KStandardAction::ShowMenubar)), menu); - - // Add "Help" menu - auto helpMenu = m_helpMenu->menu(); - helpMenu->setIcon(QIcon::fromTheme(QStringLiteral("system-help"))); - menu->addMenu(helpMenu); -} - -void DolphinMainWindow::updateToolBar() -{ - if (!menuBar()->isVisible()) { - createControlButton(); + // The second group of actions (up until the next separator) contains actions for opening + // additional views to interact with the file system. + menu->addAction(ac->action(QStringLiteral("file_new"))); + menu->addAction(ac->action(QStringLiteral("new_tab"))); + if (ac->action(QStringLiteral("undo_close_tab"))->isEnabled()) { + menu->addAction(ac->action(QStringLiteral("closed_tabs"))); } -} + menu->addAction(ac->action(QStringLiteral("open_terminal"))); + menu->addSeparator(); -void DolphinMainWindow::slotControlButtonDeleted() -{ - m_controlButton = nullptr; - m_updateToolBarTimer->start(); + // The third group contains actions to change what one sees in the view + // and to change the more general UI. + if (!toolBar()->isVisible() + || (!toolbarActions.contains(ac->action(QStringLiteral("icons"))) + && !toolbarActions.contains(ac->action(QStringLiteral("compact"))) + && !toolbarActions.contains(ac->action(QStringLiteral("details"))) + && !toolbarActions.contains(ac->action(QStringLiteral("view_mode")))) + ) { + menu->addAction(ac->action(QStringLiteral("view_mode"))); + } + menu->addAction(ac->action(QStringLiteral("show_hidden_files"))); + menu->addAction(ac->action(QStringLiteral("sort"))); + menu->addAction(ac->action(QStringLiteral("additional_info"))); + if (!GeneralSettings::showStatusBar() || !GeneralSettings::showZoomSlider()) { + menu->addAction(ac->action(QStringLiteral("zoom"))); + } + menu->addAction(ac->action(QStringLiteral("panels"))); + + // The "Configure" menu is not added to the actionCollection() because there is hardly + // a good reason for users to put it on their toolbar. + auto configureMenu = menu->addMenu(QIcon::fromTheme(QStringLiteral("configure")), + i18nc("@action:inmenu menu for configure actions", "Configure")); + configureMenu->addAction(ac->action(KStandardAction::name(KStandardAction::SwitchApplicationLanguage))); + configureMenu->addAction(ac->action(KStandardAction::name(KStandardAction::KeyBindings))); + configureMenu->addAction(ac->action(KStandardAction::name(KStandardAction::ConfigureToolbars))); + configureMenu->addAction(ac->action(KStandardAction::name(KStandardAction::Preferences))); + hamburgerMenu->hideActionsOf(configureMenu); } void DolphinMainWindow::slotPlaceActivated(const QUrl& url) @@ -1268,6 +1322,7 @@ void DolphinMainWindow::activeViewChanged(DolphinViewContainer* viewContainer) // view and url navigator) and main window. oldViewContainer->disconnect(this); oldViewContainer->view()->disconnect(this); + oldViewContainer->urlNavigatorInternalWithHistory()->disconnect(this); auto navigators = static_cast (actionCollection()->action(QStringLiteral("url_navigators"))); navigators->primaryUrlNavigator()->disconnect(this); @@ -1341,6 +1396,19 @@ void DolphinMainWindow::slotStorageTearDownExternallyRequested(const QString& mo } } +void DolphinMainWindow::slotKeyBindings() +{ + KShortcutsDialog dialog(KShortcutsEditor::AllActions, KShortcutsEditor::LetterShortcutsAllowed, this); + dialog.addCollection(actionCollection()); + if (m_terminalPanel) { + KActionCollection *konsolePartActionCollection = m_terminalPanel->actionCollection(); + if (konsolePartActionCollection) { + dialog.addCollection(konsolePartActionCollection, QStringLiteral("KonsolePart")); + } + } + dialog.configure(); +} + void DolphinMainWindow::setViewsToHomeIfMountPathOpen(const QString& mountPath) { const QVector theViewContainers = viewContainers(); @@ -1354,12 +1422,14 @@ void DolphinMainWindow::setViewsToHomeIfMountPathOpen(const QString& mountPath) void DolphinMainWindow::setupActions() { + KStandardAction::hamburgerMenu(nullptr, nullptr, actionCollection()); + // setup 'File' menu m_newFileMenu = new DolphinNewFileMenu(actionCollection(), this); QMenu* menu = m_newFileMenu->menu(); menu->setTitle(i18nc("@title:menu Create new folder, file, link, etc.", "Create New")); - menu->setIcon(QIcon::fromTheme(QStringLiteral("document-new"))); - m_newFileMenu->setDelayed(false); + menu->setIcon(QIcon::fromTheme(QStringLiteral("list-add"))); + m_newFileMenu->setPopupMode(QToolButton::InstantPopup); connect(menu, &QMenu::aboutToShow, this, &DolphinMainWindow::updateNewMenu); @@ -1451,7 +1521,7 @@ void DolphinMainWindow::setupActions() QAction* showFilterBar = actionCollection()->addAction(QStringLiteral("show_filter_bar")); showFilterBar->setText(i18nc("@action:inmenu Tools", "Filter...")); - showFilterBar->setToolTip(i18nc("@info:tooltip", "Toggle Filter Bar")); + showFilterBar->setToolTip(i18nc("@info:tooltip", "Show Filter Bar")); showFilterBar->setWhatsThis(xi18nc("@info:whatsthis", "This opens the " "Filter Bar at the bottom of the window. " "There you can enter a text to filter the files and folders currently displayed. " @@ -1522,7 +1592,8 @@ void DolphinMainWindow::setupActions() stashSplit->setToolTip(i18nc("@info", "Opens the stash virtual directory in a split window")); stashSplit->setIcon(QIcon::fromTheme(QStringLiteral("folder-stash"))); stashSplit->setCheckable(false); - stashSplit->setVisible(QDBusConnection::sessionBus().interface()->isServiceRegistered(QStringLiteral("org.kde.kio.StashNotifier"))); + QDBusConnectionInterface *sessionInterface = QDBusConnection::sessionBus().interface(); + stashSplit->setVisible(sessionInterface && sessionInterface->isServiceRegistered(QStringLiteral("org.kde.kio.StashNotifier"))); connect(stashSplit, &QAction::triggered, this, &DolphinMainWindow::toggleSplitStash); KStandardAction::redisplay(this, &DolphinMainWindow::reloadView, actionCollection()); @@ -1562,8 +1633,7 @@ void DolphinMainWindow::setupActions() m_backAction->setObjectName(backAction->objectName()); m_backAction->setShortcuts(backAction->shortcuts()); } - m_backAction->setDelayed(true); - m_backAction->setStickyMenu(false); + m_backAction->setPopupMode(QToolButton::DelayedPopup); connect(m_backAction, &QAction::triggered, this, &DolphinMainWindow::goBack); connect(m_backAction->menu(), &QMenu::aboutToShow, this, &DolphinMainWindow::slotAboutToShowBackPopupMenu); connect(m_backAction->menu(), &QMenu::triggered, this, &DolphinMainWindow::slotGoBack); @@ -1606,8 +1676,7 @@ void DolphinMainWindow::setupActions() m_forwardAction->setObjectName(forwardAction->objectName()); m_forwardAction->setShortcuts(forwardAction->shortcuts()); } - m_forwardAction->setDelayed(true); - m_forwardAction->setStickyMenu(false); + m_forwardAction->setPopupMode(QToolButton::DelayedPopup); connect(m_forwardAction, &QAction::triggered, this, &DolphinMainWindow::goForward); connect(m_forwardAction->menu(), &QMenu::aboutToShow, this, &DolphinMainWindow::slotAboutToShowForwardPopupMenu); connect(m_forwardAction->menu(), &QMenu::triggered, this, &DolphinMainWindow::slotGoForward); @@ -1665,7 +1734,7 @@ void DolphinMainWindow::setupActions() KActionMenu *bookmarkMenu = new KActionMenu(i18nc("@title:menu", "&Bookmarks"), this); bookmarkMenu->setIcon(QIcon::fromTheme(QStringLiteral("bookmarks"))); // Make the toolbar button version work properly on click - bookmarkMenu->setDelayed(false); + bookmarkMenu->setPopupMode(QToolButton::InstantPopup); m_bookmarkHandler = new DolphinBookmarkHandler(this, actionCollection(), bookmarkMenu->menu(), this); actionCollection()->addAction(QStringLiteral("bookmarks"), bookmarkMenu); @@ -1677,14 +1746,20 @@ void DolphinMainWindow::setupActions() "contain mostly the same commands and configuration options.")); connect(showMenuBar, &KToggleAction::triggered, // Fixes #286822 this, &DolphinMainWindow::toggleShowMenuBar, Qt::QueuedConnection); + KStandardAction::keyBindings(this, &DolphinMainWindow::slotKeyBindings, actionCollection()); KStandardAction::preferences(this, &DolphinMainWindow::editSettings, actionCollection()); // setup 'Help' menu for the m_controlButton. The other one is set up in the base class. m_helpMenu = new KHelpMenu(nullptr); m_helpMenu->menu()->installEventFilter(this); // remove duplicate shortcuts - m_helpMenu->action(KHelpMenu::menuHelpContents)->setShortcut(QKeySequence()); - m_helpMenu->action(KHelpMenu::menuWhatsThis)->setShortcut(QKeySequence()); + auto removeHelpActionShortcut = [this](KHelpMenu::MenuId menuId) { + if (auto *action = m_helpMenu->action(menuId)) { + action->setShortcut(QKeySequence()); + } + }; + removeHelpActionShortcut(KHelpMenu::menuHelpContents); + removeHelpActionShortcut(KHelpMenu::menuWhatsThis); // not in menu actions QList nextTabKeys = KStandardShortcut::tabNext(); @@ -1752,6 +1827,8 @@ void DolphinMainWindow::setupDockWidgets() { const bool lock = GeneralSettings::lockPanels(); + DolphinPlacesModelSingleton::instance().placesModel()->setPanelsLocked(lock); + KDualAction* lockLayoutAction = actionCollection()->add(QStringLiteral("lock_panels")); lockLayoutAction->setActiveText(i18nc("@action:inmenu Panels", "Unlock Panels")); lockLayoutAction->setActiveIcon(QIcon::fromTheme(QStringLiteral("object-unlocked"))); @@ -1828,8 +1905,10 @@ void DolphinMainWindow::setupDockWidgets() foldersPanel, &FoldersPanel::setUrl); connect(foldersPanel, &FoldersPanel::folderActivated, this, &DolphinMainWindow::changeUrl); - connect(foldersPanel, &FoldersPanel::folderMiddleClicked, + connect(foldersPanel, &FoldersPanel::folderInNewTab, this, &DolphinMainWindow::openNewTab); + connect(foldersPanel, &FoldersPanel::folderInNewActiveTab, + this, &DolphinMainWindow::openNewTabAndActivate); connect(foldersPanel, &FoldersPanel::errorMessage, this, &DolphinMainWindow::showErrorMessage); @@ -1911,8 +1990,13 @@ void DolphinMainWindow::setupDockWidgets() addDockWidget(Qt::LeftDockWidgetArea, placesDock); connect(m_placesPanel, &PlacesPanel::placeActivated, this, &DolphinMainWindow::slotPlaceActivated); - connect(m_placesPanel, &PlacesPanel::placeMiddleClicked, + connect(m_placesPanel, &PlacesPanel::tabRequested, this, &DolphinMainWindow::openNewTab); + connect(m_placesPanel, &PlacesPanel::activeTabRequested, + this, &DolphinMainWindow::openNewTabAndActivate); + connect(m_placesPanel, &PlacesPanel::newWindowRequested, this, [this](const QUrl &url) { + Dolphin::openNewWindow({url}, this); + }); connect(m_placesPanel, &PlacesPanel::errorMessage, this, &DolphinMainWindow::showErrorMessage); connect(this, &DolphinMainWindow::urlChanged, @@ -1935,14 +2019,9 @@ void DolphinMainWindow::setupDockWidgets() "appear semi-transparent unless you uncheck their hide property.")); connect(actionShowAllPlaces, &QAction::triggered, this, [actionShowAllPlaces, this](bool checked){ - actionShowAllPlaces->setIcon(QIcon::fromTheme(checked ? QStringLiteral("view-visible") : QStringLiteral("view-hidden"))); - m_placesPanel->showHiddenEntries(checked); + m_placesPanel->setShowAll(checked); }); - - connect(m_placesPanel, &PlacesPanel::showHiddenEntriesChanged, this, [actionShowAllPlaces] (bool checked){ - actionShowAllPlaces->setChecked(checked); - actionShowAllPlaces->setIcon(QIcon::fromTheme(checked ? QStringLiteral("view-visible") : QStringLiteral("view-hidden"))); - }); + connect(m_placesPanel, &PlacesPanel::allPlacesShownChanged, actionShowAllPlaces, &QAction::setChecked); actionCollection()->action(QStringLiteral("show_places_panel")) ->setWhatsThis(xi18nc("@info:whatsthis", "This toggles the " @@ -1967,7 +2046,7 @@ void DolphinMainWindow::setupDockWidgets() KActionMenu* panelsMenu = new KActionMenu(i18nc("@action:inmenu View", "Show Panels"), this); actionCollection()->addAction(QStringLiteral("panels"), panelsMenu); panelsMenu->setIcon(QIcon::fromTheme(QStringLiteral("view-sidetree"))); - panelsMenu->setDelayed(false); + panelsMenu->setPopupMode(QToolButton::InstantPopup); const KActionCollection* ac = actionCollection(); panelsMenu->addAction(ac->action(QStringLiteral("show_places_panel"))); #ifdef HAVE_BALOO @@ -1980,7 +2059,7 @@ void DolphinMainWindow::setupDockWidgets() panelsMenu->addAction(lockLayoutAction); connect(panelsMenu->menu(), &QMenu::aboutToShow, this, [actionShowAllPlaces, this]{ - actionShowAllPlaces->setEnabled(m_placesPanel->hiddenListCount()); + actionShowAllPlaces->setEnabled(DolphinPlacesModelSingleton::instance().placesModel()->hiddenCount()); }); } @@ -2077,75 +2156,11 @@ void DolphinMainWindow::updateGoActions() goUpAction->setEnabled(KIO::upUrl(currentUrl) != currentUrl); } -void DolphinMainWindow::createControlButton() -{ - if (m_controlButton) { - return; - } - Q_ASSERT(!m_controlButton); - - m_controlButton = new QToolButton(this); - m_controlButton->setAccessibleName(i18nc("@action:intoolbar", "Control")); - m_controlButton->setIcon(QIcon::fromTheme(QStringLiteral("application-menu"))); - m_controlButton->setToolTip(i18nc("@action", "Show menu")); - m_controlButton->setAttribute(Qt::WidgetAttribute::WA_CustomWhatsThis); - m_controlButton->setPopupMode(QToolButton::InstantPopup); - - QMenu* controlMenu = new QMenu(m_controlButton); - connect(controlMenu, &QMenu::aboutToShow, this, &DolphinMainWindow::updateControlMenu); - controlMenu->installEventFilter(this); - - m_controlButton->setMenu(controlMenu); - - toolBar()->addWidget(m_controlButton); - connect(toolBar(), &KToolBar::iconSizeChanged, - m_controlButton, &QToolButton::setIconSize); - - // The added widgets are owned by the toolbar and may get deleted when e.g. the toolbar - // gets edited. In this case we must add them again. The adding is done asynchronously by - // m_updateToolBarTimer. - connect(m_controlButton, &QToolButton::destroyed, this, &DolphinMainWindow::slotControlButtonDeleted); - m_updateToolBarTimer = new QTimer(this); - m_updateToolBarTimer->setInterval(500); - connect(m_updateToolBarTimer, &QTimer::timeout, this, &DolphinMainWindow::updateToolBar); -} - -void DolphinMainWindow::deleteControlButton() -{ - delete m_controlButton; - m_controlButton = nullptr; - - delete m_updateToolBarTimer; - m_updateToolBarTimer = nullptr; -} - -bool DolphinMainWindow::addActionToMenu(QAction* action, QMenu* menu) -{ - Q_ASSERT(action); - Q_ASSERT(menu); - - const KToolBar* toolBarWidget = toolBar(); - const auto associatedWidgets = action->associatedWidgets(); - for (const QWidget* widget : associatedWidgets) { - if (widget == toolBarWidget) { - return false; - } - } - - menu->addAction(action); - return true; -} - void DolphinMainWindow::refreshViews() { m_tabWidget->refreshViews(); if (GeneralSettings::modifiedStartupSettings()) { - // The startup settings have been changed by the user (see bug #254947). - // Synchronize the split-view setting with the active view: - const bool splitView = GeneralSettings::splitView(); - m_tabWidget->currentTabPage()->setSplitViewEnabled(splitView, WithAnimation); - updateSplitAction(); updateWindowTitle(); } @@ -2165,6 +2180,12 @@ void DolphinMainWindow::connectViewSignals(DolphinViewContainer* container) this, &DolphinMainWindow::slotWriteStateChanged); connect(container, &DolphinViewContainer::searchModeEnabledChanged, this, &DolphinMainWindow::updateSearchAction); + connect(container, &DolphinViewContainer::captionChanged, + this, &DolphinMainWindow::updateWindowTitle); + connect(container, &DolphinViewContainer::tabRequested, + this, &DolphinMainWindow::openNewTab); + connect(container, &DolphinViewContainer::activeTabRequested, + this, &DolphinMainWindow::openNewTabAndActivate); const QAction* toggleSearchAction = actionCollection()->action(QStringLiteral("toggle_search")); connect(toggleSearchAction, &QAction::triggered, container, &DolphinViewContainer::setSearchModeEnabled); @@ -2178,6 +2199,10 @@ void DolphinMainWindow::connectViewSignals(DolphinViewContainer* container) this, &DolphinMainWindow::fileItemsChanged); connect(view, &DolphinView::tabRequested, this, &DolphinMainWindow::openNewTab); + connect(view, &DolphinView::activeTabRequested, + this, &DolphinMainWindow::openNewTabAndActivate); + connect(view, &DolphinView::windowRequested, + this, &DolphinMainWindow::openNewWindow); connect(view, &DolphinView::requestContextMenu, this, &DolphinMainWindow::openContextMenu); connect(view, &DolphinView::directoryLoadingStarted, @@ -2195,26 +2220,28 @@ void DolphinMainWindow::connectViewSignals(DolphinViewContainer* container) connect(view, &DolphinView::goUpRequested, this, &DolphinMainWindow::goUp); + connect(container->urlNavigatorInternalWithHistory(), &KUrlNavigator::urlChanged, + this, &DolphinMainWindow::changeUrl); + connect(container->urlNavigatorInternalWithHistory(), &KUrlNavigator::historyChanged, + this, &DolphinMainWindow::updateHistory); + auto navigators = static_cast (actionCollection()->action(QStringLiteral("url_navigators"))); - const KUrlNavigator *navigator = m_tabWidget->currentTabPage()->primaryViewActive() ? navigators->primaryUrlNavigator() : navigators->secondaryUrlNavigator(); - connect(navigator, &KUrlNavigator::urlChanged, - this, &DolphinMainWindow::changeUrl); QAction *editableLocactionAction = actionCollection()->action(QStringLiteral("editable_location")); editableLocactionAction->setChecked(navigator->isUrlEditable()); connect(navigator, &KUrlNavigator::editableStateChanged, this, &DolphinMainWindow::slotEditableStateChanged); connect(navigator, &KUrlNavigator::tabRequested, this, &DolphinMainWindow::openNewTab); + connect(navigator, &KUrlNavigator::activeTabRequested, + this, &DolphinMainWindow::openNewTabAndActivate); + connect(navigator, &KUrlNavigator::newWindowRequested, + this, &DolphinMainWindow::openNewWindow); - disconnect(m_updateHistoryConnection); - m_updateHistoryConnection = connect( - container->urlNavigatorInternalWithHistory(), &KUrlNavigator::historyChanged, - this, &DolphinMainWindow::updateHistory); } void DolphinMainWindow::updateSplitAction() @@ -2276,6 +2303,7 @@ void DolphinMainWindow::createPanelAction(const QIcon& icon, panelAction->setChecked(dockAction->isChecked()); panelAction->setText(dockAction->text()); panelAction->setIcon(icon); + dockAction->setIcon(icon); actionCollection()->setDefaultShortcut(panelAction, shortcut); connect(panelAction, &QAction::triggered, dockAction, &QAction::trigger); @@ -2341,6 +2369,18 @@ void DolphinMainWindow::setupWhatsThis() // StandardAction separately because both are used in different locations. // m_helpMenu is only used for createControlButton() button. + auto setStandardActionWhatsThis = [this](KStandardAction::StandardAction actionId, + const QString &whatsThis) { + if (auto *action = actionCollection()->action(KStandardAction::name(actionId))) { + action->setWhatsThis(whatsThis); + } + }; + auto setHelpActionWhatsThis = [this](KHelpMenu::MenuId menuId, const QString &whatsThis) { + if (auto *action = m_helpMenu->action(menuId)) { + action->setWhatsThis(whatsThis); + } + }; + // Links do not work within the Menubar so texts without links are provided there. // i18n: If the external link isn't available in your language you should @@ -2351,13 +2391,12 @@ void DolphinMainWindow::setupWhatsThis() const QString whatsThisHelpContents = xi18nc("@info:whatsthis handbook", "This opens the Handbook for this application. It provides " "explanations for every part of Dolphin."); - actionCollection()->action(KStandardAction::name(KStandardAction::HelpContents)) - ->setWhatsThis(whatsThisHelpContents + setStandardActionWhatsThis(KStandardAction::HelpContents, whatsThisHelpContents + xi18nc("@info:whatsthis second half of handbook hb text without link", "If you want more elaborate introductions to the " "different features of Dolphin " "go to the KDE UserBase Wiki.")); - m_helpMenu->action(KHelpMenu::menuHelpContents)->setWhatsThis(whatsThisHelpContents + setHelpActionWhatsThis(KHelpMenu::menuHelpContents, whatsThisHelpContents + xi18nc("@info:whatsthis second half of handbook text with link", "If you want more elaborate introductions to the " "different features of Dolphin " @@ -2369,8 +2408,7 @@ void DolphinMainWindow::setupWhatsThis() "using right now! Click it, then click any component of this " "application to ask \"What's this?\" about it. The mouse cursor " "will change appearance if no help is available for a spot."); - actionCollection()->action(KStandardAction::name(KStandardAction::WhatsThis)) - ->setWhatsThis(whatsThisWhatsThis + setStandardActionWhatsThis(KStandardAction::WhatsThis, whatsThisWhatsThis + xi18nc("@info:whatsthis second half of whatsthis button text without link", "There are two other ways to get help for this application: The " "Dolphin Handbook in the Help" @@ -2378,7 +2416,7 @@ void DolphinMainWindow::setupWhatsThis() "article about File Management online." "The \"What's this?\" help is " "missing in most other windows so don't get too used to this.")); - m_helpMenu->action(KHelpMenu::menuWhatsThis)->setWhatsThis(whatsThisWhatsThis + setHelpActionWhatsThis(KHelpMenu::menuWhatsThis, whatsThisWhatsThis + xi18nc("@info:whatsthis second half of whatsthis button text with link", "There are two other ways to get help: " "The Dolphin Handbook and " @@ -2389,9 +2427,8 @@ void DolphinMainWindow::setupWhatsThis() const QString whatsThisReportBug = xi18nc("@info:whatsthis","This opens a " "window that will guide you through reporting errors or flaws " "in this application or in other KDE software."); - actionCollection()->action(KStandardAction::name(KStandardAction::ReportBug)) - ->setWhatsThis(whatsThisReportBug); - m_helpMenu->action(KHelpMenu::menuReportBug)->setWhatsThis(whatsThisReportBug + setStandardActionWhatsThis(KStandardAction::ReportBug, whatsThisReportBug); + setHelpActionWhatsThis(KHelpMenu::menuReportBug, whatsThisReportBug + xi18nc("@info:whatsthis second half of reportbug text with link", "High-quality bug reports are much appreciated. To learn " "how to make your bug report as effective as possible " @@ -2408,33 +2445,53 @@ void DolphinMainWindow::setupWhatsThis() "require money like servers, contributor meetings, etc." "KDE e.V. is the non-profit " "organization behind the KDE community."); - actionCollection()->action(KStandardAction::name(KStandardAction::Donate)) - ->setWhatsThis(whatsThisDonate); - m_helpMenu->action(KHelpMenu::menuDonate)->setWhatsThis(whatsThisDonate); + setStandardActionWhatsThis(KStandardAction::Donate, whatsThisDonate); + setHelpActionWhatsThis(KHelpMenu::menuDonate, whatsThisDonate); const QString whatsThisSwitchLanguage = xi18nc("@info:whatsthis", "With this you can change the language this application uses." "You can even set secondary languages which will be used " "if texts are not available in your preferred language."); - actionCollection()->action(KStandardAction::name(KStandardAction::SwitchApplicationLanguage)) - ->setWhatsThis(whatsThisSwitchLanguage); - m_helpMenu->action(KHelpMenu::menuSwitchLanguage)->setWhatsThis(whatsThisSwitchLanguage); + setStandardActionWhatsThis(KStandardAction::SwitchApplicationLanguage, + whatsThisSwitchLanguage); + setHelpActionWhatsThis(KHelpMenu::menuSwitchLanguage, whatsThisSwitchLanguage); const QString whatsThisAboutApp = xi18nc("@info:whatsthis","This opens a " "window that informs you about the version, license, " "used libraries and maintainers of this application."); - actionCollection()->action(KStandardAction::name(KStandardAction::AboutApp)) - ->setWhatsThis(whatsThisAboutApp); - m_helpMenu->action(KHelpMenu::menuAboutApp)->setWhatsThis(whatsThisAboutApp); + setStandardActionWhatsThis(KStandardAction::AboutApp, whatsThisAboutApp); + setHelpActionWhatsThis(KHelpMenu::menuAboutApp, whatsThisAboutApp); const QString whatsThisAboutKDE = xi18nc("@info:whatsthis","This opens a " "window with information about KDE. " "The KDE community are the people behind this free software." "If you like using this application but don't know " "about KDE or want to see a cute dragon have a look!"); - actionCollection()->action(KStandardAction::name(KStandardAction::AboutKDE)) - ->setWhatsThis(whatsThisAboutKDE); - m_helpMenu->action(KHelpMenu::menuAboutKDE)->setWhatsThis(whatsThisAboutKDE); + setStandardActionWhatsThis(KStandardAction::AboutKDE, whatsThisAboutKDE); + setHelpActionWhatsThis(KHelpMenu::menuAboutKDE, whatsThisAboutKDE); +} + +bool DolphinMainWindow::addHamburgerMenuToToolbar() +{ + QDomDocument domDocument = KXMLGUIClient::domDocument(); + if (domDocument.isNull()) { + return false; + } + QDomNode toolbar = domDocument.elementsByTagName(QStringLiteral("ToolBar")).at(0); + if (toolbar.isNull()) { + return false; + } + + QDomElement hamburgerMenuElement = domDocument.createElement(QStringLiteral("Action")); + hamburgerMenuElement.setAttribute(QStringLiteral("name"), QStringLiteral("hamburger_menu")); + toolbar.appendChild(hamburgerMenuElement); + + KXMLGUIFactory::saveConfigFile(domDocument, xmlFile()); + reloadXML(); + createGUI(); + return true; + // Make sure to also remove the and include + // whenever this method is removed (maybe in the year ~2026). } bool DolphinMainWindow::event(QEvent *event) @@ -2460,6 +2517,12 @@ bool DolphinMainWindow::eventFilter(QObject* obj, QEvent* event) return false; } +// Set a sane initial window size +QSize DolphinMainWindow::sizeHint() const +{ + return KXmlGuiWindow::sizeHint().expandedTo(QSize(760, 550)); +} + void DolphinMainWindow::saveNewToolbarConfig() { KXmlGuiWindow::saveNewToolbarConfig(); // Applies the new config. This has to be called first @@ -2471,6 +2534,8 @@ void DolphinMainWindow::saveNewToolbarConfig() m_tabWidget->currentTabPage()->insertNavigatorsWidget(navigators); } updateAllowedToolbarAreas(); + (static_cast(actionCollection()->action(KStandardAction::name( + KStandardAction::HamburgerMenu))))->hideActionsOf(toolBar()); } void DolphinMainWindow::focusTerminalPanel()