X-Git-Url: https://cloud.milkyroute.net/gitweb/dolphin.git/blobdiff_plain/0303df092d93a39122fce82aade93d96d52430dc..405dd624fb6b708eea8ec82ef913fe820c51c654:/src/dolphinmainwindow.cpp diff --git a/src/dolphinmainwindow.cpp b/src/dolphinmainwindow.cpp index c69567e26..399901688 100644 --- a/src/dolphinmainwindow.cpp +++ b/src/dolphinmainwindow.cpp @@ -57,6 +57,7 @@ #include #include #include +#include #include #include #include @@ -65,6 +66,7 @@ #include #include #include +#include #include #include #include @@ -90,10 +92,14 @@ namespace { // Used for GeneralSettings::version() to determine whether // an updated version of Dolphin is running. const int CurrentDolphinVersion = 200; + // The maximum number of entries in the back/forward popup menu + const int MaxNumberOfNavigationentries = 12; + // The maximum number of "Activate Tab" shortcuts + const int MaxActivateTabShortcuts = 9; } DolphinMainWindow::DolphinMainWindow() : - KXmlGuiWindow(nullptr, Qt::WindowContextHelpButtonHint), + KXmlGuiWindow(nullptr), m_newFileMenu(nullptr), m_helpMenu(nullptr), m_tabWidget(nullptr), @@ -107,9 +113,14 @@ DolphinMainWindow::DolphinMainWindow() : m_lastHandleUrlStatJob(nullptr), m_terminalPanel(nullptr), m_placesPanel(nullptr), - m_tearDownFromPlacesRequested(false) + m_tearDownFromPlacesRequested(false), + m_backAction(nullptr), + m_forwardAction(nullptr) { Q_INIT_RESOURCE(dolphin); +#ifndef Q_OS_WIN + setWindowFlags(Qt::WindowContextHelpButtonHint); +#endif setComponentName(QStringLiteral("dolphin"), QGuiApplication::applicationDisplayName()); setObjectName(QStringLiteral("Dolphin#")); @@ -187,6 +198,8 @@ DolphinMainWindow::DolphinMainWindow() : toolBar()->installEventFilter(middleClickEventFilter); setupWhatsThis(); + + QTimer::singleShot(0, this, &DolphinMainWindow::setupUpdateOpenPreferredSearchToolAction); } DolphinMainWindow::~DolphinMainWindow() @@ -225,7 +238,8 @@ void DolphinMainWindow::openFiles(const QStringList& files, bool splitView) void DolphinMainWindow::activateWindow() { - KStartupInfo::setNewStartupId(window(), KStartupInfo::startupId()); + window()->setAttribute(Qt::WA_NativeWindow, true); + KStartupInfo::setNewStartupId(window()->windowHandle(), KStartupInfo::startupId()); KWindowSystem::activateWindow(window()->effectiveWinId()); } @@ -509,7 +523,7 @@ void DolphinMainWindow::closeEvent(QCloseEvent* event) } } - if (m_terminalPanel->hasProgramRunning() && GeneralSettings::confirmClosingTerminalRunningProgram() && closedByUser) { + if (m_terminalPanel && m_terminalPanel->hasProgramRunning() && GeneralSettings::confirmClosingTerminalRunningProgram() && closedByUser) { // Ask if the user really wants to quit Dolphin with a program that is still running in the Terminal panel // Open a confirmation dialog with 3 buttons: // QDialogButtonBox::Yes -> Quit @@ -581,13 +595,13 @@ void DolphinMainWindow::updateNewMenu() { m_newFileMenu->setViewShowsHiddenFiles(activeViewContainer()->view()->hiddenFilesShown()); m_newFileMenu->checkUpToDate(); - m_newFileMenu->setPopupFiles(activeViewContainer()->url()); + m_newFileMenu->setPopupFiles(QList() << activeViewContainer()->url()); } void DolphinMainWindow::createDirectory() { m_newFileMenu->setViewShowsHiddenFiles(activeViewContainer()->view()->hiddenFilesShown()); - m_newFileMenu->setPopupFiles(activeViewContainer()->url()); + m_newFileMenu->setPopupFiles(QList() << activeViewContainer()->url()); m_newFileMenu->createDirectory(); } @@ -676,6 +690,56 @@ void DolphinMainWindow::slotToolBarActionMiddleClicked(QAction *action) } } +void DolphinMainWindow::slotAboutToShowBackPopupMenu() +{ + KUrlNavigator* urlNavigator = m_activeViewContainer->urlNavigator(); + int entries = 0; + m_backAction->menu()->clear(); + for (int i = urlNavigator->historyIndex() + 1; i < urlNavigator->historySize() && entries < MaxNumberOfNavigationentries; ++i, ++entries) { + QAction* action = new QAction(urlNavigator->locationUrl(i).toString(QUrl::PreferLocalFile), m_backAction->menu()); + action->setData(i); + m_backAction->menu()->addAction(action); + } +} + +void DolphinMainWindow::slotGoBack(QAction* action) +{ + int gotoIndex = action->data().value(); + KUrlNavigator* urlNavigator = m_activeViewContainer->urlNavigator(); + for (int i = gotoIndex - urlNavigator->historyIndex(); i > 0; --i) { + goBack(); + } +} + +void DolphinMainWindow::slotBackForwardActionMiddleClicked(QAction* action) +{ + if (action) { + KUrlNavigator* urlNavigator = activeViewContainer()->urlNavigator(); + openNewTabAfterCurrentTab(urlNavigator->locationUrl(action->data().value())); + } +} + +void DolphinMainWindow::slotAboutToShowForwardPopupMenu() +{ + KUrlNavigator* urlNavigator = m_activeViewContainer->urlNavigator(); + int entries = 0; + m_forwardAction->menu()->clear(); + for (int i = urlNavigator->historyIndex() - 1; i >= 0 && entries < MaxNumberOfNavigationentries; --i, ++entries) { + QAction* action = new QAction(urlNavigator->locationUrl(i).toString(QUrl::PreferLocalFile), m_forwardAction->menu()); + action->setData(i); + m_forwardAction->menu()->addAction(action); + } +} + +void DolphinMainWindow::slotGoForward(QAction* action) +{ + int gotoIndex = action->data().value(); + KUrlNavigator* urlNavigator = m_activeViewContainer->urlNavigator(); + for (int i = urlNavigator->historyIndex() - gotoIndex; i > 0; --i) { + goForward(); + } +} + void DolphinMainWindow::selectAll() { clearStatusBar(); @@ -872,23 +936,88 @@ void DolphinMainWindow::toggleShowMenuBar() } } -void DolphinMainWindow::openTerminal() +QString DolphinMainWindow::activeContainerLocalPath() { - QString dir(QDir::homePath()); - - // If the given directory is not local, it can still be the URL of an - // ioslave using UDS_LOCAL_PATH which to be converted first. KIO::StatJob* statJob = KIO::mostLocalUrl(m_activeViewContainer->url()); KJobWidgets::setWindow(statJob, this); statJob->exec(); QUrl url = statJob->mostLocalUrl(); - - //If the URL is local after the above conversion, set the directory. if (url.isLocalFile()) { - dir = url.toLocalFile(); + return url.toLocalFile(); } + return QDir::homePath(); +} - KToolInvocation::invokeTerminal(QString(), dir); +QPointer DolphinMainWindow::preferredSearchTool() +{ + m_searchTools.clear(); + KMoreToolsMenuFactory("dolphin/search-tools").fillMenuFromGroupingNames( + &m_searchTools, { "files-find" }, QUrl::fromLocalFile(activeContainerLocalPath()) + ); + QList actions = m_searchTools.actions(); + if (actions.isEmpty()) { + return nullptr; + } + QAction* action = actions.first(); + if (action->isSeparator()) { + return nullptr; + } + return action; +} + +void DolphinMainWindow::setupUpdateOpenPreferredSearchToolAction() +{ + QAction* openPreferredSearchTool = actionCollection()->action(QStringLiteral("open_preferred_search_tool")); + const QList widgets = openPreferredSearchTool->associatedWidgets(); + for (QWidget* widget : widgets) { + QMenu* menu = qobject_cast(widget); + if (menu) { + connect(menu, &QMenu::aboutToShow, this, &DolphinMainWindow::updateOpenPreferredSearchToolAction); + } + } + + // Update the open_preferred_search_tool action *before* the Configure Shortcuts window is shown, + // since this action is then listed in that window and it should be up-to-date when it is displayed. + // This update is instantaneous if user made no changes to the search tools in the meantime. + // Maybe all KStandardActions should defer calls to their slots, so that we could simply connect() to trigger()? + connect( + actionCollection()->action(KStandardAction::name(KStandardAction::KeyBindings)), &QAction::hovered, + this, &DolphinMainWindow::updateOpenPreferredSearchToolAction + ); + + updateOpenPreferredSearchToolAction(); +} + +void DolphinMainWindow::updateOpenPreferredSearchToolAction() +{ + QAction* openPreferredSearchTool = actionCollection()->action(QStringLiteral("open_preferred_search_tool")); + if (!openPreferredSearchTool) { + return; + } + QPointer tool = preferredSearchTool(); + if (tool) { + openPreferredSearchTool->setVisible(true); + openPreferredSearchTool->setText(i18nc("@action:inmenu Tools", "Open %1", tool->text())); + openPreferredSearchTool->setIcon(tool->icon()); + } else { + openPreferredSearchTool->setVisible(false); + // still visible in Shortcuts configuration window + openPreferredSearchTool->setText(i18nc("@action:inmenu Tools", "Open Preferred Search Tool")); + openPreferredSearchTool->setIcon(QIcon::fromTheme(QStringLiteral("search"))); + } +} + +void DolphinMainWindow::openPreferredSearchTool() +{ + QPointer tool = preferredSearchTool(); + if (tool) { + tool->trigger(); + } +} + +void DolphinMainWindow::openTerminal() +{ + KToolInvocation::invokeTerminal(QString(), activeContainerLocalPath()); } void DolphinMainWindow::editSettings() @@ -1030,7 +1159,9 @@ void DolphinMainWindow::updateControlMenu() // Add a curated assortment of items from the "Tools" menu addActionToMenu(ac->action(QStringLiteral("show_filter_bar")), menu); + addActionToMenu(ac->action(QStringLiteral("open_preferred_search_tool")), menu); addActionToMenu(ac->action(QStringLiteral("open_terminal")), menu); + connect(menu, &QMenu::aboutToShow, this, &DolphinMainWindow::updateOpenPreferredSearchToolAction); menu->addSeparator(); @@ -1120,6 +1251,10 @@ void DolphinMainWindow::activeViewChanged(DolphinViewContainer* viewContainer) void DolphinMainWindow::tabCountChanged(int count) { const bool enableTabActions = (count > 1); + for (int i = 0; i < MaxActivateTabShortcuts; ++i) { + actionCollection()->action(QStringLiteral("activate_tab_%1").arg(i))->setEnabled(enableTabActions); + } + actionCollection()->action(QStringLiteral("activate_last_tab"))->setEnabled(enableTabActions); actionCollection()->action(QStringLiteral("activate_next_tab"))->setEnabled(enableTabActions); actionCollection()->action(QStringLiteral("activate_prev_tab"))->setEnabled(enableTabActions); } @@ -1134,7 +1269,7 @@ void DolphinMainWindow::updateWindowTitle() void DolphinMainWindow::slotStorageTearDownFromPlacesRequested(const QString& mountPath) { - if (m_terminalPanel->currentWorkingDirectory().startsWith(mountPath)) { + if (m_terminalPanel && m_terminalPanel->currentWorkingDirectory().startsWith(mountPath)) { m_tearDownFromPlacesRequested = true; m_terminalPanel->goHome(); // m_placesPanel->proceedWithTearDown() will be called in slotTerminalDirectoryChanged @@ -1145,7 +1280,7 @@ void DolphinMainWindow::slotStorageTearDownFromPlacesRequested(const QString& mo void DolphinMainWindow::slotStorageTearDownExternallyRequested(const QString& mountPath) { - if (m_terminalPanel->currentWorkingDirectory().startsWith(mountPath)) { + if (m_terminalPanel && m_terminalPanel->currentWorkingDirectory().startsWith(mountPath)) { m_tearDownFromPlacesRequested = false; m_terminalPanel->goHome(); } @@ -1182,6 +1317,7 @@ void DolphinMainWindow::setupActions() QAction* addToPlaces = actionCollection()->addAction(QStringLiteral("add_to_places")); addToPlaces->setIcon(QIcon::fromTheme(QStringLiteral("bookmark-new"))); + addToPlaces->setText(i18nc("@action:inmenu Add current folder to places", "Add to Places")); addToPlaces->setWhatsThis(xi18nc("@info:whatsthis", "This adds the selected folder " "to the Places panel.")); connect(addToPlaces, &QAction::triggered, this, &DolphinMainWindow::addToPlaces); @@ -1313,10 +1449,22 @@ void DolphinMainWindow::setupActions() connect(replaceLocation, &QAction::triggered, this, &DolphinMainWindow::replaceLocation); // setup 'Go' menu - QAction* backAction = KStandardAction::back(this, &DolphinMainWindow::goBack, actionCollection()); - auto backShortcuts = backAction->shortcuts(); + { + QScopedPointer backAction(KStandardAction::back(nullptr, nullptr, nullptr)); + m_backAction = new KToolBarPopupAction(backAction->icon(), backAction->text(), actionCollection()); + m_backAction->setObjectName(backAction->objectName()); + m_backAction->setShortcuts(backAction->shortcuts()); + } + m_backAction->setDelayed(true); + m_backAction->setStickyMenu(false); + connect(m_backAction, &QAction::triggered, this, &DolphinMainWindow::goBack); + connect(m_backAction->menu(), &QMenu::aboutToShow, this, &DolphinMainWindow::slotAboutToShowBackPopupMenu); + connect(m_backAction->menu(), &QMenu::triggered, this, &DolphinMainWindow::slotGoBack); + actionCollection()->addAction(m_backAction->objectName(), m_backAction); + + auto backShortcuts = m_backAction->shortcuts(); backShortcuts.append(QKeySequence(Qt::Key_Backspace)); - actionCollection()->setDefaultShortcuts(backAction, backShortcuts); + actionCollection()->setDefaultShortcuts(m_backAction, backShortcuts); DolphinRecentTabsMenu* recentTabsMenu = new DolphinRecentTabsMenu(this); actionCollection()->addAction(QStringLiteral("closed_tabs"), recentTabsMenu); @@ -1345,7 +1493,25 @@ void DolphinMainWindow::setupActions() "be undone will ask for your confirmation.")); undoAction->setEnabled(false); // undo should be disabled by default - KStandardAction::forward(this, &DolphinMainWindow::goForward, actionCollection()); + { + QScopedPointer forwardAction(KStandardAction::forward(nullptr, nullptr, nullptr)); + m_forwardAction = new KToolBarPopupAction(forwardAction->icon(), forwardAction->text(), actionCollection()); + m_forwardAction->setObjectName(forwardAction->objectName()); + m_forwardAction->setShortcuts(forwardAction->shortcuts()); + } + m_forwardAction->setDelayed(true); + m_forwardAction->setStickyMenu(false); + connect(m_forwardAction, &QAction::triggered, this, &DolphinMainWindow::goForward); + connect(m_forwardAction->menu(), &QMenu::aboutToShow, this, &DolphinMainWindow::slotAboutToShowForwardPopupMenu); + connect(m_forwardAction->menu(), &QMenu::triggered, this, &DolphinMainWindow::slotGoForward); + actionCollection()->addAction(m_forwardAction->objectName(), m_forwardAction); + actionCollection()->setDefaultShortcuts(m_forwardAction, m_forwardAction->shortcuts()); + + // enable middle-click to open in a new tab + auto *middleClickEventFilter = new MiddleClickActionEventFilter(this); + connect(middleClickEventFilter, &MiddleClickActionEventFilter::actionMiddleClicked, this, &DolphinMainWindow::slotBackForwardActionMiddleClicked); + m_backAction->menu()->installEventFilter(middleClickEventFilter); + m_forwardAction->menu()->installEventFilter(middleClickEventFilter); KStandardAction::up(this, &DolphinMainWindow::goUp, actionCollection()); QAction* homeAction = KStandardAction::home(this, &DolphinMainWindow::goHome, actionCollection()); homeAction->setWhatsThis(xi18nc("@info:whatsthis", "Go to your " @@ -1370,6 +1536,15 @@ void DolphinMainWindow::setupActions() compareFiles->setEnabled(false); connect(compareFiles, &QAction::triggered, this, &DolphinMainWindow::compareFiles); + QAction* openPreferredSearchTool = actionCollection()->addAction(QStringLiteral("open_preferred_search_tool")); + openPreferredSearchTool->setText(i18nc("@action:inmenu Tools", "Open Preferred Search Tool")); + openPreferredSearchTool->setWhatsThis(xi18nc("@info:whatsthis", + "This opens a preferred search tool for the viewed location." + "Use More Search Tools menu to configure it.")); + openPreferredSearchTool->setIcon(QIcon::fromTheme(QStringLiteral("search"))); + actionCollection()->setDefaultShortcut(openPreferredSearchTool, Qt::CTRL + Qt::SHIFT + Qt::Key_F); + connect(openPreferredSearchTool, &QAction::triggered, this, &DolphinMainWindow::openPreferredSearchTool); + #ifdef HAVE_TERMINAL if (KAuthorized::authorize(QStringLiteral("shell_access"))) { QAction* openTerminal = actionCollection()->addAction(QStringLiteral("open_terminal")); @@ -1380,6 +1555,12 @@ void DolphinMainWindow::setupActions() openTerminal->setIcon(QIcon::fromTheme(QStringLiteral("dialog-scripts"))); actionCollection()->setDefaultShortcut(openTerminal, Qt::SHIFT + Qt::Key_F4); connect(openTerminal, &QAction::triggered, this, &DolphinMainWindow::openTerminal); + + QAction* focusTerminalPanel = actionCollection()->addAction(QStringLiteral("focus_terminal_panel")); + focusTerminalPanel->setText(i18nc("@action:inmenu Tools", "Focus Terminal Panel")); + focusTerminalPanel->setIcon(QIcon::fromTheme(QStringLiteral("swap-panels"))); + actionCollection()->setDefaultShortcut(focusTerminalPanel, Qt::CTRL + Qt::SHIFT + Qt::Key_F4); + connect(focusTerminalPanel, &QAction::triggered, this, &DolphinMainWindow::focusTerminalPanel); } #endif @@ -1415,6 +1596,24 @@ void DolphinMainWindow::setupActions() QList prevTabKeys = KStandardShortcut::tabPrev(); prevTabKeys.append(QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_Tab)); + for (int i = 0; i < MaxActivateTabShortcuts; ++i) { + QAction* activateTab = actionCollection()->addAction(QStringLiteral("activate_tab_%1").arg(i)); + activateTab->setText(i18nc("@action:inmenu", "Activate Tab %1", i + 1)); + activateTab->setEnabled(false); + connect(activateTab, &QAction::triggered, this, [this, i]() { m_tabWidget->activateTab(i); }); + + // only add default shortcuts for the first 9 tabs regardless of MaxActivateTabShortcuts + if (i < 9) { + actionCollection()->setDefaultShortcut(activateTab, QStringLiteral("Alt+%1").arg(i + 1)); + } + } + + QAction* activateLastTab = actionCollection()->addAction(QStringLiteral("activate_last_tab")); + activateLastTab->setText(i18nc("@action:inmenu", "Activate Last Tab")); + activateLastTab->setEnabled(false); + connect(activateLastTab, &QAction::triggered, m_tabWidget, &DolphinTabWidget::activateLastTab); + actionCollection()->setDefaultShortcut(activateLastTab, Qt::ALT + Qt::Key_0); + QAction* activateNextTab = actionCollection()->addAction(QStringLiteral("activate_next_tab")); activateNextTab->setIconText(i18nc("@action:inmenu", "Next Tab")); activateNextTab->setText(i18nc("@action:inmenu", "Activate Next Tab")); @@ -1696,7 +1895,7 @@ void DolphinMainWindow::updateFileAndEditActions() if (list.isEmpty()) { stateChanged(QStringLiteral("has_no_selection")); - addToPlacesAction->setText(i18nc("@action:inmenu Add current folder to places", "Add '%1' to Places", m_activeViewContainer->placesText())); + addToPlacesAction->setEnabled(true); } else { stateChanged(QStringLiteral("has_selection")); @@ -1706,13 +1905,12 @@ void DolphinMainWindow::updateFileAndEditActions() QAction* cutAction = col->action(KStandardAction::name(KStandardAction::Cut)); QAction* deleteWithTrashShortcut = col->action(QStringLiteral("delete_shortcut")); // see DolphinViewActionHandler QAction* showTarget = col->action(QStringLiteral("show_target")); + QAction* duplicateAction = col->action(QStringLiteral("duplicate")); // see DolphinViewActionHandler if (list.length() == 1 && list.first().isDir()) { addToPlacesAction->setEnabled(true); - addToPlacesAction->setText(i18nc("@action:inmenu Add current folder to places", "Add '%1' to Places", list.first().name())); } else { addToPlacesAction->setEnabled(false); - addToPlacesAction->setText(i18nc("@action:inmenu Add current folder to places", "Add to Places")); } KFileItemListProperties capabilities(list); @@ -1724,6 +1922,7 @@ void DolphinMainWindow::updateFileAndEditActions() deleteWithTrashShortcut->setEnabled(capabilities.supportsDeleting() && !enableMoveToTrash); cutAction->setEnabled(capabilities.supportsMoving()); showTarget->setEnabled(list.length() == 1 && list.at(0).isLink()); + duplicateAction->setEnabled(capabilities.supportsWriting()); } } @@ -1766,6 +1965,7 @@ void DolphinMainWindow::createControlButton() Q_ASSERT(!m_controlButton); m_controlButton = new QToolButton(this); + m_controlButton->setAccessibleName(i18nc("@action:intoolbar", "Control")); m_controlButton->setIcon(QIcon::fromTheme(QStringLiteral("application-menu"))); m_controlButton->setToolTip(i18nc("@action", "Show menu")); m_controlButton->setAttribute(Qt::WidgetAttribute::WA_CustomWhatsThis); @@ -2093,6 +2293,8 @@ bool DolphinMainWindow::event(QEvent *event) QWhatsThisClickedEvent* whatsThisEvent = dynamic_cast(event); QDesktopServices::openUrl(QUrl(whatsThisEvent->href())); return true; + } else if (event->type() == QEvent::WindowActivate) { + updateOpenPreferredSearchToolAction(); } return KXmlGuiWindow::event(event); } @@ -2109,6 +2311,22 @@ bool DolphinMainWindow::eventFilter(QObject* obj, QEvent* event) return false; } +void DolphinMainWindow::focusTerminalPanel() +{ + if (m_terminalPanel->isVisible()) { + if (m_terminalPanel->terminalHasFocus()) { + m_activeViewContainer->view()->setFocus(Qt::FocusReason::ShortcutFocusReason); + actionCollection()->action(QStringLiteral("focus_terminal_panel"))->setText(i18nc("@action:inmenu Tools", "Focus Terminal Panel")); + } else { + m_terminalPanel->setFocus(Qt::FocusReason::ShortcutFocusReason); + actionCollection()->action(QStringLiteral("focus_terminal_panel"))->setText(i18nc("@action:inmenu Tools", "Defocus Terminal Panel")); + } + } else { + actionCollection()->action(QStringLiteral("show_terminal_panel"))->trigger(); + actionCollection()->action(QStringLiteral("focus_terminal_panel"))->setText(i18nc("@action:inmenu Tools", "Defocus Terminal Panel")); + } +} + DolphinMainWindow::UndoUiInterface::UndoUiInterface() : KIO::FileUndoManager::UiInterface() {