X-Git-Url: https://cloud.milkyroute.net/gitweb/dolphin.git/blobdiff_plain/7d49cb567b595570f59156965332428c9490a04d..fc9e06426746b5b33d31db68904d4d205537a080:/src/dolphinmainwindow.cpp diff --git a/src/dolphinmainwindow.cpp b/src/dolphinmainwindow.cpp index 4cba46554..78560d820 100644 --- a/src/dolphinmainwindow.cpp +++ b/src/dolphinmainwindow.cpp @@ -8,6 +8,7 @@ #include "dolphinmainwindow.h" +#include "admin/workerintegration.h" #include "dolphin_generalsettings.h" #include "dolphinbookmarkhandler.h" #include "dolphincontextmenu.h" @@ -25,8 +26,10 @@ #include "panels/folders/folderspanel.h" #include "panels/places/placespanel.h" #include "panels/terminal/terminalpanel.h" +#include "search/dolphinquery.h" #include "selectionmode/actiontexthelper.h" #include "settings/dolphinsettingsdialog.h" +#include "statusbar/diskspaceusagemenu.h" #include "statusbar/dolphinstatusbar.h" #include "views/dolphinnewfilemenuobserver.h" #include "views/dolphinremoteencoding.h" @@ -40,6 +43,8 @@ #include #include #include +#include +#include #include #include #include @@ -51,6 +56,8 @@ #include #include #include +#include +#include #include #include #include @@ -81,6 +88,7 @@ #include #include #include +#include #include @@ -119,6 +127,7 @@ DolphinMainWindow::DolphinMainWindow() , m_forwardAction(nullptr) , m_splitViewAction(nullptr) , m_splitViewMenuAction(nullptr) + , m_diskSpaceUsageMenu(nullptr) , m_sessionSaveTimer(nullptr) , m_sessionSaveWatcher(nullptr) , m_sessionSaveScheduled(false) @@ -171,22 +180,29 @@ DolphinMainWindow::DolphinMainWindow() m_actionHandler = new DolphinViewActionHandler(actionCollection(), m_actionTextHelper, this); connect(m_actionHandler, &DolphinViewActionHandler::actionBeingHandled, this, &DolphinMainWindow::clearStatusBar); connect(m_actionHandler, &DolphinViewActionHandler::createDirectoryTriggered, this, &DolphinMainWindow::createDirectory); + connect(m_actionHandler, &DolphinViewActionHandler::createFileTriggered, this, &DolphinMainWindow::createFile); connect(m_actionHandler, &DolphinViewActionHandler::selectionModeChangeTriggered, this, &DolphinMainWindow::slotSetSelectionMode); - Q_CHECK_PTR(actionCollection()->action(QStringLiteral("create_dir"))); - m_newFileMenu->setNewFolderShortcutAction(actionCollection()->action(QStringLiteral("create_dir"))); + QAction *newDirAction = actionCollection()->action(QStringLiteral("create_dir")); + Q_CHECK_PTR(newDirAction); + m_newFileMenu->setNewFolderShortcutAction(newDirAction); + + QAction *newFileAction = actionCollection()->action(QStringLiteral("create_file")); + Q_CHECK_PTR(newFileAction); + m_newFileMenu->setNewFileShortcutAction(newFileAction); m_remoteEncoding = new DolphinRemoteEncoding(this, m_actionHandler); connect(this, &DolphinMainWindow::urlChanged, m_remoteEncoding, &DolphinRemoteEncoding::slotAboutToOpenUrl); m_disabledActionNotifier = new DisabledActionNotifier(this); connect(m_disabledActionNotifier, &DisabledActionNotifier::disabledActionTriggered, this, [this](const QAction *, QString reason) { - m_activeViewContainer->showMessage(reason, DolphinViewContainer::Warning); + m_activeViewContainer->showMessage(reason, KMessageWidget::Warning); }); setupDockWidgets(); - setupGUI(Save | Create | ToolBar); + const bool usePhoneUi{KRuntimePlatform::runtimePlatform().contains(QLatin1String("phone"))}; + setupGUI(Save | Create | ToolBar, usePhoneUi ? QStringLiteral("dolphinuiforphones.rc") : QString() /* load the default dolphinui.rc file */); stateChanged(QStringLiteral("new_file")); QClipboard *clipboard = QApplication::clipboard(); @@ -197,6 +213,15 @@ DolphinMainWindow::DolphinMainWindow() if (firstRun) { menuBar()->setVisible(false); + + if (usePhoneUi) { + Q_ASSERT(qobject_cast(m_placesPanel->parent())); + m_placesPanel->parentWidget()->hide(); + auto settings = GeneralSettings::self(); + settings->setShowZoomSlider(false); // Zooming can be done with pinch gestures instead and we are short on horizontal space. + settings->setRenameInline(false); // This works around inline renaming currently not working well with virtual keyboards. + settings->save(); // Otherwise the RenameInline setting is not picked up for the first time Dolphin is used. + } } const bool showMenu = !menuBar()->isHidden(); @@ -213,6 +238,7 @@ DolphinMainWindow::DolphinMainWindow() } updateAllowedToolbarAreas(); + updateNavigatorsBackground(); // enable middle-click on back/forward/up to open in a new tab auto *middleClickEventFilter = new MiddleClickActionEventFilter(this); @@ -237,6 +263,13 @@ DolphinMainWindow::~DolphinMainWindow() { // This fixes a crash on Wayland when closing the mainwindow while another dialog is open. disconnect(QGuiApplication::clipboard(), &QClipboard::dataChanged, this, &DolphinMainWindow::updatePasteAction); + + // This fixes a crash in dolphinmainwindowtest where the connection below fires even though the KMainWindow destructor of this object is already running. + Q_CHECK_PTR(qobject_cast(m_placesPanel->parent())); + disconnect(static_cast(m_placesPanel->parent()), + &DolphinDockWidget::visibilityChanged, + this, + &DolphinMainWindow::slotPlacesPanelVisibilityChanged); } QVector DolphinMainWindow::viewContainers() const @@ -361,6 +394,10 @@ void DolphinMainWindow::changeUrl(const QUrl &url) updatePasteAction(); updateViewActions(); updateGoActions(); + m_diskSpaceUsageMenu->setUrl(url); + + // will signal used urls to activities manager, too + m_recentFiles->addUrl(url, QString(), "inode/directory"); Q_EMIT urlChanged(url); } @@ -372,9 +409,9 @@ void DolphinMainWindow::slotTerminalDirectoryChanged(const QUrl &url) m_tearDownFromPlacesRequested = false; } - m_activeViewContainer->setAutoGrabFocus(false); + m_activeViewContainer->setGrabFocusOnUrlChange(false); changeUrl(url); - m_activeViewContainer->setAutoGrabFocus(true); + m_activeViewContainer->setGrabFocusOnUrlChange(true); } void DolphinMainWindow::slotEditableStateChanged(bool editable) @@ -455,7 +492,7 @@ void DolphinMainWindow::addToPlaces() } if (url.isValid()) { QString icon; - if (m_activeViewContainer->isSearchModeEnabled()) { + if (isSearchUrl(url)) { icon = QStringLiteral("folder-saved-search-symbolic"); } else { icon = KIO::iconNameForUrl(url); @@ -464,9 +501,9 @@ void DolphinMainWindow::addToPlaces() } } -void DolphinMainWindow::openNewTab(const QUrl &url) +DolphinTabPage *DolphinMainWindow::openNewTab(const QUrl &url) { - m_tabWidget->openNewTab(url, QUrl()); + return m_tabWidget->openNewTab(url, QUrl()); } void DolphinMainWindow::openNewTabAndActivate(const QUrl &url) @@ -559,7 +596,7 @@ void DolphinMainWindow::showTarget() KIO::StatJob *statJob = static_cast(job); if (statJob->error()) { - m_activeViewContainer->showMessage(job->errorString(), DolphinViewContainer::Error); + m_activeViewContainer->showMessage(job->errorString(), KMessageWidget::Error); } else { KIO::highlightInFileManager({destinationUrl}); } @@ -583,7 +620,7 @@ void DolphinMainWindow::showEvent(QShowEvent *event) { KXmlGuiWindow::showEvent(event); - if (!event->spontaneous()) { + if (!event->spontaneous() && m_activeViewContainer) { m_activeViewContainer->view()->setFocus(); } } @@ -726,6 +763,8 @@ void DolphinMainWindow::slotSaveSession() KConfig *config = KConfigGui::sessionConfig(); saveGlobalProperties(config); savePropertiesInternal(config, 1); + KConfigGroup group = config->group(QStringLiteral("Number")); + group.writeEntry("NumberOfWindows", 1); // Makes session restore aware that there is a window to restore. auto future = QtConcurrent::run([config]() { config->sync(); @@ -781,8 +820,23 @@ void DolphinMainWindow::updateNewMenu() void DolphinMainWindow::createDirectory() { - m_newFileMenu->setWorkingDirectory(activeViewContainer()->url()); - m_newFileMenu->createDirectory(); + // When creating directory, namejob is being run. In network folders, + // this job can take long time, so instead of starting multiple namejobs, + // just check if we are already running one. This prevents opening multiple + // dialogs. BUG:481401 + if (!m_newFileMenu->isCreateDirectoryRunning()) { + m_newFileMenu->setWorkingDirectory(activeViewContainer()->url()); + m_newFileMenu->createDirectory(); + } +} + +void DolphinMainWindow::createFile() +{ + // Use the same logic as in createDirectory() + if (!m_newFileMenu->isCreateFileRunning()) { + m_newFileMenu->setWorkingDirectory(activeViewContainer()->url()); + m_newFileMenu->createFile(); + } } void DolphinMainWindow::quit() @@ -792,7 +846,7 @@ void DolphinMainWindow::quit() void DolphinMainWindow::showErrorMessage(const QString &message) { - m_activeViewContainer->showMessage(message, DolphinViewContainer::Error); + m_activeViewContainer->showMessage(message, KMessageWidget::Error); } void DolphinMainWindow::slotUndoAvailable(bool available) @@ -845,13 +899,14 @@ void DolphinMainWindow::paste() void DolphinMainWindow::find() { - m_activeViewContainer->setSearchModeEnabled(true); + m_activeViewContainer->setSearchBarVisible(true); + m_activeViewContainer->setFocusToSearchBar(); } void DolphinMainWindow::updateSearchAction() { QAction *toggleSearchAction = actionCollection()->action(QStringLiteral("toggle_search")); - toggleSearchAction->setChecked(m_activeViewContainer->isSearchModeEnabled()); + toggleSearchAction->setChecked(m_activeViewContainer->isSearchBarVisible()); } void DolphinMainWindow::updatePasteAction() @@ -861,8 +916,8 @@ void DolphinMainWindow::updatePasteAction() pasteAction->setEnabled(pasteInfo.first); m_disabledActionNotifier->setDisabledReason(pasteAction, m_activeViewContainer->rootItem().isWritable() - ? i18nc("@info", "Could not paste: The clipboard is empty.") - : i18nc("@info", "Could not paste: You do not have permission to write into this folder.")); + ? i18nc("@info", "Cannot paste: The clipboard is empty.") + : i18nc("@info", "Cannot paste: You do not have permission to write into this folder.")); pasteAction->setText(pasteInfo.second); } @@ -888,9 +943,13 @@ QAction *DolphinMainWindow::urlNavigatorHistoryAction(const KUrlNavigator *urlNa { const QUrl url = urlNavigator->locationUrl(historyIndex); - QString text = url.toDisplayString(QUrl::PreferLocalFile); + QString text; - if (!urlNavigator->showFullPath()) { + if (isSearchUrl(url)) { + text = Search::DolphinQuery(url, QUrl{}).title(); + } else if (urlNavigator->showFullPath()) { + text = url.toDisplayString(QUrl::PreferLocalFile); + } else { const KFilePlacesModel *placesModel = DolphinPlacesModelSingleton::instance().placesModel(); const QModelIndex closestIdx = placesModel->closestItem(url); @@ -950,6 +1009,7 @@ void DolphinMainWindow::slotAboutToShowForwardPopupMenu() const KUrlNavigator *urlNavigator = m_activeViewContainer->urlNavigatorInternalWithHistory(); int entries = 0; QMenu *menu = m_forwardAction->popupMenu(); + menu->clear(); for (int i = urlNavigator->historyIndex() - 1; i >= 0 && entries < MaxNumberOfNavigationentries; --i, ++entries) { QAction *action = urlNavigatorHistoryAction(urlNavigator, i, menu); menu->addAction(action); @@ -995,8 +1055,15 @@ void DolphinMainWindow::invertSelection() void DolphinMainWindow::toggleSplitView() { + QUrl newSplitViewUrl; + const KFileItemList list = m_activeViewContainer->view()->selectedItems(); + if (list.count() == 1) { + const KFileItem &item = list.first(); + newSplitViewUrl = DolphinView::openItemAsFolderUrl(item); + } + DolphinTabPage *tabPage = m_tabWidget->currentTabPage(); - tabPage->setSplitViewEnabled(!tabPage->splitViewEnabled(), WithAnimation); + tabPage->setSplitViewEnabled(!tabPage->splitViewEnabled(), WithAnimation, newSplitViewUrl); m_tabWidget->updateTabName(m_tabWidget->indexOf(tabPage)); updateViewActions(); } @@ -1098,8 +1165,10 @@ void DolphinMainWindow::replaceLocation() // If the text field currently has focus and everything is selected, // pressing the keyboard shortcut returns the whole thing to breadcrumb mode + // and goes back to the view, just like how it was before this action was triggered the first time. if (navigator->isUrlEditable() && lineEdit->hasFocus() && lineEdit->selectedText() == lineEdit->text()) { navigator->setUrlEditable(false); + m_activeViewContainer->view()->setFocus(); } else { navigator->setUrlEditable(true); navigator->setFocus(); @@ -1123,11 +1192,21 @@ void DolphinMainWindow::togglePanelLockState() GeneralSettings::setLockPanels(newLockState); } -void DolphinMainWindow::slotTerminalPanelVisibilityChanged() +void DolphinMainWindow::slotTerminalPanelVisibilityChanged(bool visible) { - if (m_terminalPanel->isHiddenInVisibleWindow() && m_activeViewContainer) { + if (!visible && m_activeViewContainer) { m_activeViewContainer->view()->setFocus(); } + // Putting focus to the Terminal is not handled here but in TerminalPanel::showEvent(). +} + +void DolphinMainWindow::slotPlacesPanelVisibilityChanged(bool visible) +{ + if (!visible && m_activeViewContainer) { + m_activeViewContainer->view()->setFocus(); + return; + } + m_placesPanel->setFocus(); } void DolphinMainWindow::goBack() @@ -1395,8 +1474,10 @@ void DolphinMainWindow::slotWriteStateChanged(bool isFolderWritable) newFileMenu()->setEnabled(isFolderWritable && m_activeViewContainer->url().scheme() != QLatin1String("trash")); // When the menu is disabled, actions in it are disabled later in the event loop, and we need to set the disabled reason after that. QTimer::singleShot(0, this, [this]() { + m_disabledActionNotifier->setDisabledReason(actionCollection()->action(QStringLiteral("create_file")), + i18nc("@info", "Cannot create new file: You do not have permission to create items in this folder.")); m_disabledActionNotifier->setDisabledReason(actionCollection()->action(QStringLiteral("create_dir")), - i18nc("@info", "Could not create new folder: You do not have permission to create items in this folder.")); + i18nc("@info", "Cannot create new folder: You do not have permission to create items in this folder.")); }); } @@ -1430,7 +1511,7 @@ void DolphinMainWindow::updateHamburgerMenu() menu = new QMenu(this); hamburgerMenu->setMenu(menu); hamburgerMenu->hideActionsOf(ac->action(QStringLiteral("basic_actions"))->menu()); - hamburgerMenu->hideActionsOf(ac->action(QStringLiteral("zoom"))->menu()); + hamburgerMenu->hideActionsOf(qobject_cast(ac->action(QStringLiteral("zoom")))->popupMenu()); } else { menu->clear(); } @@ -1481,15 +1562,18 @@ void DolphinMainWindow::updateHamburgerMenu() // The third group contains actions to change what one sees in the view // and to change the more general UI. if (!toolBar()->isVisible() - || (!toolbarActions.contains(ac->action(QStringLiteral("icons"))) && !toolbarActions.contains(ac->action(QStringLiteral("compact"))) - && !toolbarActions.contains(ac->action(QStringLiteral("details"))) && !toolbarActions.contains(ac->action(QStringLiteral("view_mode"))))) { + || ((!toolbarActions.contains(ac->action(QStringLiteral("icons"))) && !toolbarActions.contains(ac->action(QStringLiteral("compact"))) + && !toolbarActions.contains(ac->action(QStringLiteral("details"))) && !toolbarActions.contains(ac->action(QStringLiteral("view_mode")))) + && !toolbarActions.contains(ac->action(QStringLiteral("view_settings"))))) { menu->addAction(ac->action(QStringLiteral("view_mode"))); } - menu->addAction(ac->action(QStringLiteral("show_hidden_files"))); - menu->addAction(ac->action(QStringLiteral("sort"))); - menu->addAction(ac->action(QStringLiteral("additional_info"))); - if (!GeneralSettings::showStatusBar() || !GeneralSettings::showZoomSlider()) { - menu->addAction(ac->action(QStringLiteral("zoom"))); + if (!toolBar()->isVisible() || !toolbarActions.contains(ac->action(QStringLiteral("view_settings")))) { + menu->addAction(ac->action(QStringLiteral("show_hidden_files"))); + menu->addAction(ac->action(QStringLiteral("sort"))); + menu->addAction(ac->action(QStringLiteral("additional_info"))); + if (!GeneralSettings::showStatusBar() || !GeneralSettings::showZoomSlider()) { + menu->addAction(ac->action(QStringLiteral("zoom"))); + } } menu->addAction(ac->action(QStringLiteral("panels"))); @@ -1513,6 +1597,8 @@ void DolphinMainWindow::slotPlaceActivated(const QUrl &url) // We can end up here if the user clicked a device in the Places Panel // which had been unmounted earlier, see https://bugs.kde.org/show_bug.cgi?id=161385. reloadView(); + + m_activeViewContainer->view()->setFocus(); // We always want the focus on the view after activating a place. } else { view->disableUrlNavigatorSelectionRequests(); changeUrl(url); @@ -1533,9 +1619,6 @@ void DolphinMainWindow::activeViewChanged(DolphinViewContainer *viewContainer) m_activeViewContainer = viewContainer; if (oldViewContainer) { - const QAction *toggleSearchAction = actionCollection()->action(QStringLiteral("toggle_search")); - toggleSearchAction->disconnect(oldViewContainer); - // Disconnect all signals between the old view container (container, // view and url navigator) and main window. oldViewContainer->disconnect(this); @@ -1546,6 +1629,7 @@ void DolphinMainWindow::activeViewChanged(DolphinViewContainer *viewContainer) if (auto secondaryUrlNavigator = navigators->secondaryUrlNavigator()) { secondaryUrlNavigator->disconnect(this); } + oldViewContainer->disconnect(m_diskSpaceUsageMenu); // except the requestItemInfo so that on hover the information panel can still be updated connect(oldViewContainer->view(), &DolphinView::requestItemInfo, this, &DolphinMainWindow::requestItemInfo); @@ -1567,6 +1651,13 @@ void DolphinMainWindow::activeViewChanged(DolphinViewContainer *viewContainer) updateViewActions(); updateGoActions(); updateSearchAction(); + connect(m_diskSpaceUsageMenu, + &DiskSpaceUsageMenu::showMessage, + viewContainer, + [viewContainer](const QString &message, KMessageWidget::MessageType messageType) { + viewContainer->showMessage(message, messageType); + }); + connect(m_diskSpaceUsageMenu, &DiskSpaceUsageMenu::showInstallationProgress, viewContainer, &DolphinViewContainer::showProgress); const QUrl url = viewContainer->url(); Q_EMIT urlChanged(url); @@ -1635,7 +1726,11 @@ void DolphinMainWindow::setViewsToHomeIfMountPathOpen(const QString &mountPath) { const QVector theViewContainers = viewContainers(); for (DolphinViewContainer *viewContainer : theViewContainers) { - if (viewContainer && viewContainer->url().toLocalFile().startsWith(mountPath)) { + if (!viewContainer) { + continue; + } + const auto viewPath = viewContainer->url().toLocalFile(); + if (viewPath.startsWith(mountPath + QLatin1String("/")) || viewPath == mountPath) { viewContainer->setUrl(QUrl::fromLocalFile(QDir::homePath())); } } @@ -1647,7 +1742,7 @@ void DolphinMainWindow::setupActions() auto hamburgerMenuAction = KStandardAction::hamburgerMenu(nullptr, nullptr, actionCollection()); // setup 'File' menu - m_newFileMenu = new DolphinNewFileMenu(nullptr, this); + m_newFileMenu = new DolphinNewFileMenu(nullptr, nullptr, this); actionCollection()->addAction(QStringLiteral("new_menu"), m_newFileMenu); QMenu *menu = m_newFileMenu->menu(); menu->setTitle(i18nc("@title:menu Create new folder, file, link, etc.", "Create New")); @@ -1790,9 +1885,7 @@ void DolphinMainWindow::setupActions() "This helps you " "find files and folders by opening a search bar. " "There you can enter search terms and specify settings to find the " - "items you are looking for.Use this help again on " - "the search bar so we can have a look at it while the settings are " - "explained.")); + "items you are looking for.")); // toggle_search acts as a copy of the main searchAction to be used mainly // in the toolbar, with no default shortcut attached, to avoid messing with @@ -1804,6 +1897,13 @@ void DolphinMainWindow::setupActions() toggleSearchAction->setToolTip(searchAction->toolTip()); toggleSearchAction->setWhatsThis(searchAction->whatsThis()); toggleSearchAction->setCheckable(true); + connect(toggleSearchAction, &QAction::triggered, this, [this](bool checked) { + if (checked) { + find(); + } else { + m_activeViewContainer->setSearchBarVisible(false); + } + }); QAction *toggleSelectionModeAction = actionCollection()->addAction(QStringLiteral("toggle_selection_mode")); // i18n: This action toggles a selection mode. @@ -1856,6 +1956,8 @@ void DolphinMainWindow::setupActions() // setup 'View' menu // (note that most of it is set up in DolphinViewActionHandler) + Admin::WorkerIntegration::createActAsAdminAction(actionCollection(), this); + m_splitViewAction = actionCollection()->add(QStringLiteral("split_view")); m_splitViewMenuAction = actionCollection()->addAction(QStringLiteral("split_view_menu")); @@ -2003,6 +2105,12 @@ void DolphinMainWindow::setupActions() compareFiles->setEnabled(false); connect(compareFiles, &QAction::triggered, this, &DolphinMainWindow::compareFiles); + QAction *manageDiskSpaceUsage = actionCollection()->addAction(QStringLiteral("manage_disk_space")); + manageDiskSpaceUsage->setText(i18nc("@action:inmenu Tools", "Manage Disk Space Usage")); + manageDiskSpaceUsage->setIcon(QIcon::fromTheme(QStringLiteral("filelight"))); + m_diskSpaceUsageMenu = new DiskSpaceUsageMenu{this}; + manageDiskSpaceUsage->setMenu(m_diskSpaceUsageMenu); + QAction *openPreferredSearchTool = actionCollection()->addAction(QStringLiteral("open_preferred_search_tool")); openPreferredSearchTool->setText(i18nc("@action:inmenu Tools", "Open Preferred Search Tool")); openPreferredSearchTool->setWhatsThis(xi18nc("@info:whatsthis", @@ -2013,12 +2121,18 @@ void DolphinMainWindow::setupActions() connect(openPreferredSearchTool, &QAction::triggered, this, &DolphinMainWindow::openPreferredSearchTool); if (KAuthorized::authorize(QStringLiteral("shell_access"))) { + // Get icon of user default terminal emulator application + const KConfigGroup group(KSharedConfig::openConfig(QStringLiteral("kdeglobals"), KConfig::SimpleConfig), QStringLiteral("General")); + const QString terminalDesktopFilename = group.readEntry("TerminalService"); + // Use utilities-terminal icon from theme if readEntry() has failed + const QString terminalIcon = terminalDesktopFilename.isEmpty() ? "utilities-terminal" : KDesktopFile(terminalDesktopFilename).readIcon(); + QAction *openTerminal = actionCollection()->addAction(QStringLiteral("open_terminal")); openTerminal->setText(i18nc("@action:inmenu Tools", "Open Terminal")); openTerminal->setWhatsThis(xi18nc("@info:whatsthis", "This opens a terminal application for the viewed location." "To learn more about terminals use the help features in the terminal application.")); - openTerminal->setIcon(QIcon::fromTheme(QStringLiteral("utilities-terminal"))); + openTerminal->setIcon(QIcon::fromTheme(terminalIcon)); actionCollection()->setDefaultShortcut(openTerminal, Qt::SHIFT | Qt::Key_F4); connect(openTerminal, &QAction::triggered, this, &DolphinMainWindow::openTerminal); @@ -2028,17 +2142,9 @@ void DolphinMainWindow::setupActions() openTerminalHere->setWhatsThis(xi18nc("@info:whatsthis", "This opens terminal applications for the selected items' locations." "To learn more about terminals use the help features in the terminal application.")); - openTerminalHere->setIcon(QIcon::fromTheme(QStringLiteral("utilities-terminal"))); + openTerminalHere->setIcon(QIcon::fromTheme(terminalIcon)); actionCollection()->setDefaultShortcut(openTerminalHere, Qt::SHIFT | Qt::ALT | Qt::Key_F4); connect(openTerminalHere, &QAction::triggered, this, &DolphinMainWindow::openTerminalHere); - -#if HAVE_TERMINAL - QAction *focusTerminalPanel = actionCollection()->addAction(QStringLiteral("focus_terminal_panel")); - focusTerminalPanel->setText(i18nc("@action:inmenu Tools", "Focus Terminal Panel")); - focusTerminalPanel->setIcon(QIcon::fromTheme(QStringLiteral("swap-panels"))); - actionCollection()->setDefaultShortcut(focusTerminalPanel, Qt::CTRL | Qt::SHIFT | Qt::Key_F4); - connect(focusTerminalPanel, &QAction::triggered, this, &DolphinMainWindow::focusTerminalPanel); -#endif } // setup 'Bookmarks' menu @@ -2065,14 +2171,6 @@ void DolphinMainWindow::setupActions() &DolphinMainWindow::toggleShowMenuBar, Qt::QueuedConnection); - KToggleAction *showStatusBar = KStandardAction::showStatusbar(nullptr, nullptr, actionCollection()); - showStatusBar->setChecked(GeneralSettings::showStatusBar()); - connect(GeneralSettings::self(), &GeneralSettings::showStatusBarChanged, showStatusBar, &KToggleAction::setChecked); - connect(showStatusBar, &KToggleAction::triggered, this, [this](bool checked) { - GeneralSettings::setShowStatusBar(checked); - refreshViews(); - }); - KStandardAction::keyBindings(this, &DolphinMainWindow::slotKeyBindings, actionCollection()); KStandardAction::preferences(this, &DolphinMainWindow::editSettings, actionCollection()); @@ -2142,10 +2240,12 @@ void DolphinMainWindow::setupActions() QAction *openInSplitViewAction = actionCollection()->addAction(QStringLiteral("open_in_split_view")); openInSplitViewAction->setText(i18nc("@action:inmenu", "Open in Split View")); - openInSplitViewAction->setIcon(QIcon::fromTheme(QStringLiteral("view-right-new"))); + openInSplitViewAction->setIcon(QIcon::fromTheme(QStringLiteral("view-split-left-right"))); connect(openInSplitViewAction, &QAction::triggered, this, [this]() { openInSplitView(QUrl()); }); + + m_recentFiles = new KRecentFilesAction(this); } void DolphinMainWindow::setupDockWidgets() @@ -2180,7 +2280,7 @@ void DolphinMainWindow::setupDockWidgets() connect(infoPanel, &InformationPanel::urlActivated, this, &DolphinMainWindow::handleUrl); infoDock->setWidget(infoPanel); - createPanelAction(QIcon::fromTheme(QStringLiteral("dialog-information")), Qt::Key_F11, infoDock, QStringLiteral("show_information_panel")); + createPanelAction(QIcon::fromTheme(QStringLiteral("documentinfo")), Qt::Key_F11, infoDock, QStringLiteral("show_information_panel")); addDockWidget(Qt::RightDockWidgetArea, infoDock); connect(this, &DolphinMainWindow::urlChanged, infoPanel, &InformationPanel::setUrl); @@ -2292,8 +2392,15 @@ void DolphinMainWindow::setupDockWidgets() "advanced tasks. To learn more about terminals use the help features in a " "standalone terminal application like Konsole.") + panelWhatsThis); - } -#endif + + QAction *focusTerminalPanel = actionCollection()->addAction(QStringLiteral("focus_terminal_panel")); + focusTerminalPanel->setText(i18nc("@action:inmenu Tools", "Focus Terminal Panel")); + focusTerminalPanel->setToolTip(i18nc("@info:tooltip", "Move keyboard focus to and from the Terminal panel.")); + focusTerminalPanel->setIcon(QIcon::fromTheme(QStringLiteral("swap-panels"))); + actionCollection()->setDefaultShortcut(focusTerminalPanel, Qt::CTRL | Qt::SHIFT | Qt::Key_F4); + connect(focusTerminalPanel, &QAction::triggered, this, &DolphinMainWindow::toggleTerminalPanelFocus); + } // endif "shell_access" allowed +#endif // HAVE_TERMINAL if (GeneralSettings::version() < 200) { infoDock->hide(); @@ -2323,6 +2430,7 @@ void DolphinMainWindow::setupDockWidgets() connect(m_placesPanel, &PlacesPanel::errorMessage, this, &DolphinMainWindow::showErrorMessage); connect(this, &DolphinMainWindow::urlChanged, m_placesPanel, &PlacesPanel::setUrl); connect(placesDock, &DolphinDockWidget::visibilityChanged, &DolphinUrlNavigatorsController::slotPlacesPanelVisibilityChanged); + connect(placesDock, &DolphinDockWidget::visibilityChanged, this, &DolphinMainWindow::slotPlacesPanelVisibilityChanged); connect(this, &DolphinMainWindow::settingsChanged, m_placesPanel, &PlacesPanel::readSettings); connect(m_placesPanel, &PlacesPanel::storageTearDownRequested, this, &DolphinMainWindow::slotStorageTearDownFromPlacesRequested); connect(m_placesPanel, &PlacesPanel::storageTearDownExternallyRequested, this, &DolphinMainWindow::slotStorageTearDownExternallyRequested); @@ -2364,6 +2472,13 @@ void DolphinMainWindow::setupDockWidgets() " to display it again.") + panelWhatsThis); + QAction *focusPlacesPanel = actionCollection()->addAction(QStringLiteral("focus_places_panel")); + focusPlacesPanel->setText(i18nc("@action:inmenu View", "Focus Places Panel")); + focusPlacesPanel->setToolTip(i18nc("@info:tooltip", "Move keyboard focus to and from the Places panel.")); + focusPlacesPanel->setIcon(QIcon::fromTheme(QStringLiteral("swap-panels"))); + actionCollection()->setDefaultShortcut(focusPlacesPanel, Qt::CTRL | Qt::Key_P); + connect(focusPlacesPanel, &QAction::triggered, this, &DolphinMainWindow::togglePlacesPanelFocus); + // Add actions into the "Panels" menu KActionMenu *panelsMenu = new KActionMenu(i18nc("@action:inmenu View", "Show Panels"), this); actionCollection()->addAction(QStringLiteral("panels"), panelsMenu); @@ -2377,8 +2492,11 @@ void DolphinMainWindow::setupDockWidgets() panelsMenu->addAction(ac->action(QStringLiteral("show_folders_panel"))); panelsMenu->addAction(ac->action(QStringLiteral("show_terminal_panel"))); panelsMenu->addSeparator(); - panelsMenu->addAction(actionShowAllPlaces); panelsMenu->addAction(lockLayoutAction); + panelsMenu->addSeparator(); + panelsMenu->addAction(actionShowAllPlaces); + panelsMenu->addAction(focusPlacesPanel); + panelsMenu->addAction(ac->action(QStringLiteral("focus_terminal_panel"))); connect(panelsMenu->menu(), &QMenu::aboutToShow, this, [actionShowAllPlaces] { actionShowAllPlaces->setEnabled(DolphinPlacesModelSingleton::instance().placesModel()->hiddenCount()); @@ -2399,7 +2517,7 @@ void DolphinMainWindow::updateFileAndEditActions() QAction *addToPlacesAction = col->action(QStringLiteral("add_to_places")); QAction *copyToOtherViewAction = col->action(QStringLiteral("copy_to_inactive_split_view")); QAction *moveToOtherViewAction = col->action(QStringLiteral("move_to_inactive_split_view")); - QAction *copyLocation = col->action(QString("copy_location")); + QAction *copyLocation = col->action(QStringLiteral("copy_location")); if (list.isEmpty()) { stateChanged(QStringLiteral("has_no_selection")); @@ -2431,18 +2549,17 @@ void DolphinMainWindow::updateFileAndEditActions() const bool enableMoveToTrash = capabilitiesSource.isLocal() && capabilitiesSource.supportsMoving(); renameAction->setEnabled(capabilitiesSource.supportsMoving()); - m_disabledActionNotifier->setDisabledReason(renameAction, - i18nc("@info", "Could not rename: You do not have permission to rename items in this folder.")); + m_disabledActionNotifier->setDisabledReason(renameAction, i18nc("@info", "Cannot rename: You do not have permission to rename items in this folder.")); deleteAction->setEnabled(capabilitiesSource.supportsDeleting()); m_disabledActionNotifier->setDisabledReason(deleteAction, - i18nc("@info", "Could not delete: You do not have permission to remove items from this folder.")); + i18nc("@info", "Cannot delete: You do not have permission to remove items from this folder.")); cutAction->setEnabled(capabilitiesSource.supportsMoving()); - m_disabledActionNotifier->setDisabledReason(cutAction, i18nc("@info", "Could not cut: You do not have permission to move items from this folder.")); + m_disabledActionNotifier->setDisabledReason(cutAction, i18nc("@info", "Cannot cut: You do not have permission to move items from this folder.")); copyLocation->setEnabled(list.length() == 1); showTarget->setEnabled(list.length() == 1 && list.at(0).isLink()); duplicateAction->setEnabled(capabilitiesSource.supportsWriting()); m_disabledActionNotifier->setDisabledReason(duplicateAction, - i18nc("@info", "Could not duplicate here: You do not have permission to create items in this folder.")); + i18nc("@info", "Cannot duplicate here: You do not have permission to create items in this folder.")); if (enableMoveToTrash) { moveToTrashAction->setEnabled(true); @@ -2452,7 +2569,7 @@ void DolphinMainWindow::updateFileAndEditActions() moveToTrashAction->setEnabled(false); deleteWithTrashShortcut->setEnabled(capabilitiesSource.supportsDeleting()); m_disabledActionNotifier->setDisabledReason(deleteWithTrashShortcut, - i18nc("@info", "Could not delete: You do not have permission to remove items from this folder.")); + i18nc("@info", "Cannot delete: You do not have permission to remove items from this folder.")); } } @@ -2464,9 +2581,9 @@ void DolphinMainWindow::updateFileAndEditActions() m_disabledActionNotifier->clearDisabledReason(moveToOtherViewAction); } else if (list.isEmpty()) { copyToOtherViewAction->setEnabled(false); - m_disabledActionNotifier->setDisabledReason(copyToOtherViewAction, i18nc("@info", "Could not copy to other view: No files selected.")); + m_disabledActionNotifier->setDisabledReason(copyToOtherViewAction, i18nc("@info", "Cannot copy to other view: No files selected.")); moveToOtherViewAction->setEnabled(false); - m_disabledActionNotifier->setDisabledReason(moveToOtherViewAction, i18nc("@info", "Could not move to other view: No files selected.")); + m_disabledActionNotifier->setDisabledReason(moveToOtherViewAction, i18nc("@info", "Cannot move to other view: No files selected.")); } else { DolphinTabPage *tabPage = m_tabWidget->currentTabPage(); KFileItem capabilitiesDestination; @@ -2485,25 +2602,25 @@ void DolphinMainWindow::updateFileAndEditActions() if (!allNotTargetOrigin) { copyToOtherViewAction->setEnabled(false); m_disabledActionNotifier->setDisabledReason(copyToOtherViewAction, - i18nc("@info", "Could not copy to other view: The other view already contains these items.")); + i18nc("@info", "Cannot copy to other view: The other view already contains these items.")); moveToOtherViewAction->setEnabled(false); m_disabledActionNotifier->setDisabledReason(moveToOtherViewAction, - i18nc("@info", "Could not move to other view: The other view already contains these items.")); + i18nc("@info", "Cannot move to other view: The other view already contains these items.")); } else if (!capabilitiesDestination.isWritable()) { copyToOtherViewAction->setEnabled(false); m_disabledActionNotifier->setDisabledReason( copyToOtherViewAction, - i18nc("@info", "Could not copy to other view: You do not have permission to write into the destination folder.")); + i18nc("@info", "Cannot copy to other view: You do not have permission to write into the destination folder.")); moveToOtherViewAction->setEnabled(false); m_disabledActionNotifier->setDisabledReason( moveToOtherViewAction, - i18nc("@info", "Could not move to other view: You do not have permission to write into the destination folder.")); + i18nc("@info", "Cannot move to other view: You do not have permission to write into the destination folder.")); } else { copyToOtherViewAction->setEnabled(true); moveToOtherViewAction->setEnabled(capabilitiesSource.supportsMoving()); m_disabledActionNotifier->setDisabledReason( moveToOtherViewAction, - i18nc("@info", "Could not move to other view: You do not have permission to move items from this folder.")); + i18nc("@info", "Cannot move to other view: You do not have permission to move items from this folder.")); } } } @@ -2559,14 +2676,11 @@ void DolphinMainWindow::connectViewSignals(DolphinViewContainer *container) connect(container, &DolphinViewContainer::showFilterBarChanged, this, &DolphinMainWindow::updateFilterBarAction); connect(container, &DolphinViewContainer::writeStateChanged, this, &DolphinMainWindow::slotWriteStateChanged); slotWriteStateChanged(container->view()->isFolderWritable()); - connect(container, &DolphinViewContainer::searchModeEnabledChanged, this, &DolphinMainWindow::updateSearchAction); + connect(container, &DolphinViewContainer::searchBarVisibilityChanged, this, &DolphinMainWindow::updateSearchAction); connect(container, &DolphinViewContainer::captionChanged, this, &DolphinMainWindow::updateWindowTitle); connect(container, &DolphinViewContainer::tabRequested, this, &DolphinMainWindow::openNewTab); connect(container, &DolphinViewContainer::activeTabRequested, this, &DolphinMainWindow::openNewTabAndActivate); - const QAction *toggleSearchAction = actionCollection()->action(QStringLiteral("toggle_search")); - connect(toggleSearchAction, &QAction::triggered, container, &DolphinViewContainer::setSearchModeEnabled); - // Make the toggled state of the selection mode actions visually follow the selection mode state of the view. auto toggleSelectionModeAction = actionCollection()->action(QStringLiteral("toggle_selection_mode")); toggleSelectionModeAction->setChecked(m_activeViewContainer->isSelectionModeEnabled()); @@ -2587,6 +2701,7 @@ void DolphinMainWindow::connectViewSignals(DolphinViewContainer *container) connect(view, &DolphinView::goForwardRequested, this, &DolphinMainWindow::goForward); connect(view, &DolphinView::urlActivated, this, &DolphinMainWindow::handleUrl); connect(view, &DolphinView::goUpRequested, this, &DolphinMainWindow::goUp); + connect(view, &DolphinView::doubleClickViewBackground, this, &DolphinMainWindow::slotDoubleClickViewBackground); connect(container->urlNavigatorInternalWithHistory(), &KUrlNavigator::urlChanged, this, &DolphinMainWindow::changeUrl); connect(container->urlNavigatorInternalWithHistory(), &KUrlNavigator::historyChanged, this, &DolphinMainWindow::updateHistory); @@ -2620,12 +2735,16 @@ void DolphinMainWindow::updateSplitActions() m_splitViewAction->setText(i18nc("@action:intoolbar Close left view", "Close")); m_splitViewAction->setToolTip(i18nc("@info", "Close left view")); m_splitViewAction->setIcon(QIcon::fromTheme(QStringLiteral("view-left-close"))); + m_splitViewMenuAction->setText(i18nc("@action:inmenu Close left view", "Close Left View")); + popoutSplitAction->setText(i18nc("@action:intoolbar Move left view to a new window", "Pop out Left View")); popoutSplitAction->setToolTip(i18nc("@info", "Move left view to a new window")); } else { m_splitViewAction->setText(i18nc("@action:intoolbar Close right view", "Close")); m_splitViewAction->setToolTip(i18nc("@info", "Close right view")); m_splitViewAction->setIcon(QIcon::fromTheme(QStringLiteral("view-right-close"))); + m_splitViewMenuAction->setText(i18nc("@action:inmenu Close left view", "Close Right View")); + popoutSplitAction->setText(i18nc("@action:intoolbar Move right view to a new window", "Pop out Right View")); popoutSplitAction->setToolTip(i18nc("@info", "Move right view to a new window")); } @@ -2637,8 +2756,9 @@ void DolphinMainWindow::updateSplitActions() } } else { m_splitViewAction->setText(i18nc("@action:intoolbar Split view", "Split")); + m_splitViewMenuAction->setText(m_splitViewAction->text()); m_splitViewAction->setToolTip(i18nc("@info", "Split view")); - m_splitViewAction->setIcon(QIcon::fromTheme(QStringLiteral("view-right-new"))); + m_splitViewAction->setIcon(QIcon::fromTheme(QStringLiteral("view-split-left-right"))); popoutSplitAction->setText(i18nc("@action:intoolbar Move view in focus to a new window", "Pop out")); popoutSplitAction->setEnabled(false); if (m_splitViewAction->menu()) { @@ -2650,7 +2770,6 @@ void DolphinMainWindow::updateSplitActions() } // Update state from toolbar action - m_splitViewMenuAction->setText(m_splitViewAction->text()); m_splitViewMenuAction->setToolTip(m_splitViewAction->toolTip()); m_splitViewMenuAction->setIcon(m_splitViewAction->icon()); } @@ -2668,6 +2787,12 @@ void DolphinMainWindow::updateAllowedToolbarAreas() } } +void DolphinMainWindow::updateNavigatorsBackground() +{ + auto navigators = static_cast(actionCollection()->action(QStringLiteral("url_navigators"))); + navigators->setBackgroundEnabled(navigators->isInToolbar()); +} + bool DolphinMainWindow::isKompareInstalled() const { static bool initialized = false; @@ -2854,23 +2979,44 @@ void DolphinMainWindow::saveNewToolbarConfig() m_tabWidget->currentTabPage()->insertNavigatorsWidget(navigators); } updateAllowedToolbarAreas(); + updateNavigatorsBackground(); (static_cast(actionCollection()->action(KStandardAction::name(KStandardAction::HamburgerMenu))))->hideActionsOf(toolBar()); } -void DolphinMainWindow::focusTerminalPanel() +void DolphinMainWindow::toggleTerminalPanelFocus() { - if (m_terminalPanel->isVisible()) { - if (m_terminalPanel->terminalHasFocus()) { - m_activeViewContainer->view()->setFocus(Qt::FocusReason::ShortcutFocusReason); - actionCollection()->action(QStringLiteral("focus_terminal_panel"))->setText(i18nc("@action:inmenu Tools", "Focus Terminal Panel")); - } else { - m_terminalPanel->setFocus(Qt::FocusReason::ShortcutFocusReason); - actionCollection()->action(QStringLiteral("focus_terminal_panel"))->setText(i18nc("@action:inmenu Tools", "Defocus Terminal Panel")); - } - } else { - actionCollection()->action(QStringLiteral("show_terminal_panel"))->trigger(); + if (!m_terminalPanel->isVisible()) { + actionCollection()->action(QStringLiteral("show_terminal_panel"))->trigger(); // Also moves focus to the panel. actionCollection()->action(QStringLiteral("focus_terminal_panel"))->setText(i18nc("@action:inmenu Tools", "Defocus Terminal Panel")); + return; } + + if (m_terminalPanel->terminalHasFocus()) { + m_activeViewContainer->view()->setFocus(Qt::FocusReason::ShortcutFocusReason); + actionCollection()->action(QStringLiteral("focus_terminal_panel"))->setText(i18nc("@action:inmenu Tools", "Focus Terminal Panel")); + return; + } + + m_terminalPanel->setFocus(Qt::FocusReason::ShortcutFocusReason); + actionCollection()->action(QStringLiteral("focus_terminal_panel"))->setText(i18nc("@action:inmenu Tools", "Defocus Terminal Panel")); +} + +void DolphinMainWindow::togglePlacesPanelFocus() +{ + if (!m_placesPanel->isVisible()) { + actionCollection()->action(QStringLiteral("show_places_panel"))->trigger(); // Also moves focus to the panel. + actionCollection()->action(QStringLiteral("focus_places_panel"))->setText(i18nc("@action:inmenu View", "Defocus Terminal Panel")); + return; + } + + if (m_placesPanel->hasFocus()) { + m_activeViewContainer->view()->setFocus(Qt::FocusReason::ShortcutFocusReason); + actionCollection()->action(QStringLiteral("focus_places_panel"))->setText(i18nc("@action:inmenu View", "Focus Places Panel")); + return; + } + + m_placesPanel->setFocus(Qt::FocusReason::ShortcutFocusReason); + actionCollection()->action(QStringLiteral("focus_places_panel"))->setText(i18nc("@action:inmenu View", "Defocus Places Panel")); } DolphinMainWindow::UndoUiInterface::UndoUiInterface() @@ -2887,7 +3033,7 @@ void DolphinMainWindow::UndoUiInterface::jobError(KIO::Job *job) DolphinMainWindow *mainWin = qobject_cast(parentWidget()); if (mainWin) { DolphinViewContainer *container = mainWin->activeViewContainer(); - container->showMessage(job->errorString(), DolphinViewContainer::Error); + container->showMessage(job->errorString(), KMessageWidget::Error); } else { KIO::FileUndoManager::UiInterface::jobError(job); } @@ -2903,4 +3049,41 @@ bool DolphinMainWindow::isItemVisibleInAnyView(const QString &urlOfItem) return m_tabWidget->isItemVisibleInAnyView(QUrl::fromUserInput(urlOfItem)); } +void DolphinMainWindow::slotDoubleClickViewBackground(Qt::MouseButton button) +{ + if (button != Qt::MouseButton::LeftButton) { + // only handle left mouse button for now + return; + } + + GeneralSettings *settings = GeneralSettings::self(); + QString clickAction = settings->doubleClickViewAction(); + + DolphinView *view = activeViewContainer()->view(); + if (view == nullptr || clickAction == "none") { + return; + } + + if (clickAction == customCommand) { + // run custom command set by the user + QString path = view->url().toLocalFile(); + QString clickCustomAction = settings->doubleClickViewCustomAction(); + clickCustomAction.replace("{path}", path.prepend('"').append('"')); + + m_job = new KIO::CommandLauncherJob(clickCustomAction); + m_job->setUiDelegate(new KDialogJobUiDelegate(KJobUiDelegate::AutoHandlingEnabled, this)); + m_job->start(); + + } else { + // get the action set by the user and trigger it + const KActionCollection *actions = actionCollection(); + QAction *action = actions->action(clickAction); + if (action == nullptr) { + qCWarning(DolphinDebug) << QStringLiteral("Double-click view: action `%1` was not found").arg(clickAction); + return; + } + action->trigger(); + } +} + #include "moc_dolphinmainwindow.cpp"