diff --git a/src/dolphinmainwindow.cpp b/src/dolphinmainwindow.cpp index e851fa2b0..455d3b481 100644 --- a/src/dolphinmainwindow.cpp +++ b/src/dolphinmainwindow.cpp @@ -1,2168 +1,2168 @@ /*************************************************************************** * Copyright (C) 2006 by Peter Penz * * Copyright (C) 2006 by Stefan Monov * * Copyright (C) 2006 by Cvetoslav Ludmiloff * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * ***************************************************************************/ #include "dolphinmainwindow.h" #include "config-terminal.h" #include "global.h" #include "dolphinbookmarkhandler.h" #include "dolphindockwidget.h" #include "dolphincontextmenu.h" #include "dolphinnewfilemenu.h" #include "dolphinrecenttabsmenu.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/information/informationpanel.h" #include "panels/terminal/terminalpanel.h" #include "settings/dolphinsettingsdialog.h" #include "statusbar/dolphinstatusbar.h" #include "views/dolphinviewactionhandler.h" #include "views/dolphinremoteencoding.h" #include "views/draganddrophelper.h" #include "views/viewproperties.h" #include "views/dolphinnewfilemenuobserver.h" #include "dolphin_generalsettings.h" #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 #include #include #include #include #include #include #include #include #include #include #include #include #include namespace { // Used for GeneralSettings::version() to determine whether // an updated version of Dolphin is running. const int CurrentDolphinVersion = 200; } DolphinMainWindow::DolphinMainWindow() : KXmlGuiWindow(nullptr, Qt::WindowContextHelpButtonHint), m_newFileMenu(nullptr), m_helpMenu(nullptr), m_tabWidget(nullptr), m_activeViewContainer(nullptr), m_actionHandler(nullptr), m_remoteEncoding(nullptr), m_settingsDialog(), m_bookmarkHandler(nullptr), m_controlButton(nullptr), m_updateToolBarTimer(nullptr), m_lastHandleUrlStatJob(nullptr), m_terminalPanel(nullptr), m_placesPanel(nullptr), m_tearDownFromPlacesRequested(false) { Q_INIT_RESOURCE(dolphin); setComponentName(QStringLiteral("dolphin"), QGuiApplication::applicationDisplayName()); setObjectName(QStringLiteral("Dolphin#")); 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), this, &DolphinMainWindow::slotUndoAvailable); connect(undoManager, &KIO::FileUndoManager::undoTextChanged, this, &DolphinMainWindow::slotUndoTextChanged); connect(undoManager, &KIO::FileUndoManager::jobRecordingStarted, this, &DolphinMainWindow::clearStatusBar); connect(undoManager, &KIO::FileUndoManager::jobRecordingFinished, this, &DolphinMainWindow::showCommand); GeneralSettings* generalSettings = GeneralSettings::self(); const bool firstRun = (generalSettings->version() < 200); if (firstRun) { generalSettings->setViewPropsTimestamp(QDateTime::currentDateTime()); } setAcceptDrops(true); m_tabWidget = new DolphinTabWidget(this); m_tabWidget->setObjectName("tabWidget"); connect(m_tabWidget, &DolphinTabWidget::activeViewChanged, this, &DolphinMainWindow::activeViewChanged); connect(m_tabWidget, &DolphinTabWidget::tabCountChanged, this, &DolphinMainWindow::tabCountChanged); connect(m_tabWidget, &DolphinTabWidget::currentUrlChanged, this, &DolphinMainWindow::updateWindowTitle); setCentralWidget(m_tabWidget); setupActions(); m_actionHandler = new DolphinViewActionHandler(actionCollection(), this); connect(m_actionHandler, &DolphinViewActionHandler::actionBeingHandled, this, &DolphinMainWindow::clearStatusBar); connect(m_actionHandler, &DolphinViewActionHandler::createDirectoryTriggered, this, &DolphinMainWindow::createDirectory); m_remoteEncoding = new DolphinRemoteEncoding(this, m_actionHandler); connect(this, &DolphinMainWindow::urlChanged, m_remoteEncoding, &DolphinRemoteEncoding::slotAboutToOpenUrl); setupDockWidgets(); setupGUI(Keys | Save | Create | ToolBar); stateChanged(QStringLiteral("new_file")); QClipboard* clipboard = QApplication::clipboard(); connect(clipboard, &QClipboard::dataChanged, this, &DolphinMainWindow::updatePasteAction); QAction* showFilterBarAction = actionCollection()->action(QStringLiteral("show_filter_bar")); showFilterBarAction->setChecked(generalSettings->filterBar()); if (firstRun) { menuBar()->setVisible(false); // Assure a proper default size if Dolphin runs the first time resize(750, 500); } const bool showMenu = !menuBar()->isHidden(); QAction* showMenuBarAction = actionCollection()->action(KStandardAction::name(KStandardAction::ShowMenubar)); showMenuBarAction->setChecked(showMenu); // workaround for bug #171080 if (!showMenu) { createControlButton(); } // enable middle-click on back/forward/up to open in a new tab auto *middleClickEventFilter = new MiddleClickActionEventFilter(this); connect(middleClickEventFilter, &MiddleClickActionEventFilter::actionMiddleClicked, this, &DolphinMainWindow::slotToolBarActionMiddleClicked); toolBar()->installEventFilter(middleClickEventFilter); setupWhatsThis(); } DolphinMainWindow::~DolphinMainWindow() { } QVector DolphinMainWindow::viewContainers() const { QVector viewContainers; viewContainers.reserve(m_tabWidget->count()); for (int i = 0; i < m_tabWidget->count(); ++i) { viewContainers << m_tabWidget->tabPageAt(i)->activeViewContainer(); } return viewContainers; } void DolphinMainWindow::openDirectories(const QList& dirs, bool splitView) { m_tabWidget->openDirectories(dirs, splitView); } void DolphinMainWindow::openDirectories(const QStringList& dirs, bool splitView) { openDirectories(QUrl::fromStringList(dirs), splitView); } void DolphinMainWindow::openFiles(const QList& files, bool splitView) { m_tabWidget->openFiles(files, splitView); } void DolphinMainWindow::openFiles(const QStringList& files, bool splitView) { openFiles(QUrl::fromStringList(files), splitView); } void DolphinMainWindow::activateWindow() { KStartupInfo::setNewStartupId(window(), KStartupInfo::startupId()); KWindowSystem::activateWindow(window()->effectiveWinId()); } void DolphinMainWindow::showCommand(CommandType command) { DolphinStatusBar* statusBar = m_activeViewContainer->statusBar(); switch (command) { case KIO::FileUndoManager::Copy: statusBar->setText(i18nc("@info:status", "Successfully copied.")); break; case KIO::FileUndoManager::Move: statusBar->setText(i18nc("@info:status", "Successfully moved.")); break; case KIO::FileUndoManager::Link: statusBar->setText(i18nc("@info:status", "Successfully linked.")); break; case KIO::FileUndoManager::Trash: statusBar->setText(i18nc("@info:status", "Successfully moved to trash.")); break; case KIO::FileUndoManager::Rename: statusBar->setText(i18nc("@info:status", "Successfully renamed.")); break; case KIO::FileUndoManager::Mkdir: statusBar->setText(i18nc("@info:status", "Created folder.")); break; default: break; } } void DolphinMainWindow::pasteIntoFolder() { m_activeViewContainer->view()->pasteIntoFolder(); } void DolphinMainWindow::changeUrl(const QUrl &url) { if (!KProtocolManager::supportsListing(url)) { // The URL navigator only checks for validity, not // if the URL can be listed. An error message is // shown due to DolphinViewContainer::restoreView(). return; } m_activeViewContainer->setUrl(url); updateFileAndEditActions(); updatePasteAction(); updateViewActions(); updateGoActions(); emit urlChanged(url); } void DolphinMainWindow::slotTerminalDirectoryChanged(const QUrl& url) { if (m_tearDownFromPlacesRequested && url == QUrl::fromLocalFile(QDir::homePath())) { m_placesPanel->proceedWithTearDown(); m_tearDownFromPlacesRequested = false; } m_activeViewContainer->setAutoGrabFocus(false); changeUrl(url); m_activeViewContainer->setAutoGrabFocus(true); } void DolphinMainWindow::slotEditableStateChanged(bool editable) { KToggleAction* editableLocationAction = static_cast(actionCollection()->action(QStringLiteral("editable_location"))); editableLocationAction->setChecked(editable); } void DolphinMainWindow::slotSelectionChanged(const KFileItemList& selection) { updateFileAndEditActions(); const int selectedUrlsCount = m_tabWidget->currentTabPage()->selectedItemsCount(); QAction* compareFilesAction = actionCollection()->action(QStringLiteral("compare_files")); if (selectedUrlsCount == 2) { compareFilesAction->setEnabled(isKompareInstalled()); } else { compareFilesAction->setEnabled(false); } emit selectionChanged(selection); } void DolphinMainWindow::updateHistory() { const KUrlNavigator* urlNavigator = m_activeViewContainer->urlNavigator(); const int index = urlNavigator->historyIndex(); QAction* backAction = actionCollection()->action(KStandardAction::name(KStandardAction::Back)); if (backAction) { backAction->setToolTip(i18nc("@info", "Go back")); backAction->setWhatsThis(i18nc("@info:whatsthis go back", "Return to the previously viewed folder.")); backAction->setEnabled(index < urlNavigator->historySize() - 1); } QAction* forwardAction = actionCollection()->action(KStandardAction::name(KStandardAction::Forward)); if (forwardAction) { forwardAction->setToolTip(i18nc("@info", "Go forward")); forwardAction->setWhatsThis(xi18nc("@info:whatsthis go forward", "This undoes a Go|Back action.")); forwardAction->setEnabled(index > 0); } } void DolphinMainWindow::updateFilterBarAction(bool show) { QAction* showFilterBarAction = actionCollection()->action(QStringLiteral("show_filter_bar")); showFilterBarAction->setChecked(show); } void DolphinMainWindow::openNewMainWindow() { Dolphin::openNewWindow({m_activeViewContainer->url()}, this); } void DolphinMainWindow::openNewActivatedTab() { m_tabWidget->openNewActivatedTab(); } void DolphinMainWindow::addToPlaces() { QUrl url; QString name; // If nothing is selected, act on the current dir if (m_activeViewContainer->view()->selectedItems().count() == 0) { url = m_activeViewContainer->url(); name = m_activeViewContainer->placesText(); } else { const auto dirToAdd = m_activeViewContainer->view()->selectedItems().first(); url = dirToAdd.url(); 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); } } void DolphinMainWindow::openNewTab(const QUrl& url, DolphinTabWidget::TabPlacement tabPlacement) { m_tabWidget->openNewTab(url, QUrl(), tabPlacement); } void DolphinMainWindow::openNewTabAfterCurrentTab(const QUrl& url) { m_tabWidget->openNewTab(url, QUrl(), DolphinTabWidget::AfterCurrentTab); } void DolphinMainWindow::openNewTabAfterLastTab(const QUrl& url) { m_tabWidget->openNewTab(url, QUrl(), DolphinTabWidget::AfterLastTab); } void DolphinMainWindow::openInNewTab() { const KFileItemList& list = m_activeViewContainer->view()->selectedItems(); bool tabCreated = false; foreach (const KFileItem& item, list) { const QUrl& url = DolphinView::openItemAsFolderUrl(item); if (!url.isEmpty()) { openNewTabAfterCurrentTab(url); tabCreated = true; } } // if no new tab has been created from the selection // open the current directory in a new tab if (!tabCreated) { openNewTabAfterCurrentTab(m_activeViewContainer->url()); } } void DolphinMainWindow::openInNewWindow() { QUrl newWindowUrl; const KFileItemList list = m_activeViewContainer->view()->selectedItems(); if (list.isEmpty()) { newWindowUrl = m_activeViewContainer->url(); } else if (list.count() == 1) { const KFileItem& item = list.first(); newWindowUrl = DolphinView::openItemAsFolderUrl(item); } if (!newWindowUrl.isEmpty()) { Dolphin::openNewWindow({newWindowUrl}, this); } } void DolphinMainWindow::showTarget() { const auto link = m_activeViewContainer->view()->selectedItems().at(0); const auto linkLocationDir = QFileInfo(link.localPath()).absoluteDir(); auto linkDestination = link.linkDest(); if (QFileInfo(linkDestination).isRelative()) { linkDestination = linkLocationDir.filePath(linkDestination); } if (QFileInfo::exists(linkDestination)) { KIO::highlightInFileManager({QUrl::fromLocalFile(linkDestination).adjusted(QUrl::StripTrailingSlash)}); } else { m_activeViewContainer->showMessage(xi18nc("@info", "Could not access %1.", linkDestination), DolphinViewContainer::Warning); } } void DolphinMainWindow::showEvent(QShowEvent* event) { KXmlGuiWindow::showEvent(event); if (!event->spontaneous()) { m_activeViewContainer->view()->setFocus(); } } void DolphinMainWindow::closeEvent(QCloseEvent* event) { // Find out if Dolphin is closed directly by the user or // by the session manager because the session is closed bool closedByUser = true; if (qApp->isSavingSession()) { closedByUser = false; } if (m_tabWidget->count() > 1 && GeneralSettings::confirmClosingMultipleTabs() && closedByUser) { // Ask the user if he really wants to quit and close all tabs. // Open a confirmation dialog with 3 buttons: // QDialogButtonBox::Yes -> Quit // QDialogButtonBox::No -> Close only the current tab // QDialogButtonBox::Cancel -> do nothing QDialog *dialog = new QDialog(this, Qt::Dialog); dialog->setWindowTitle(i18nc("@title:window", "Confirmation")); dialog->setModal(true); QDialogButtonBox* buttons = new QDialogButtonBox(QDialogButtonBox::Yes | QDialogButtonBox::No | QDialogButtonBox::Cancel); KGuiItem::assign(buttons->button(QDialogButtonBox::Yes), KGuiItem(i18nc("@action:button 'Quit Dolphin' button", "&Quit %1", QGuiApplication::applicationDisplayName()), QIcon::fromTheme(QStringLiteral("application-exit")))); KGuiItem::assign(buttons->button(QDialogButtonBox::No), KGuiItem(i18n("C&lose Current Tab"), QIcon::fromTheme(QStringLiteral("tab-close")))); KGuiItem::assign(buttons->button(QDialogButtonBox::Cancel), KStandardGuiItem::cancel()); buttons->button(QDialogButtonBox::Yes)->setDefault(true); bool doNotAskAgainCheckboxResult = false; const auto result = KMessageBox::createKMessageBox(dialog, buttons, QMessageBox::Warning, i18n("You have multiple tabs open in this window, are you sure you want to quit?"), QStringList(), i18n("Do not ask again"), &doNotAskAgainCheckboxResult, KMessageBox::Notify); if (doNotAskAgainCheckboxResult) { GeneralSettings::setConfirmClosingMultipleTabs(false); } switch (result) { case QDialogButtonBox::Yes: // Quit break; case QDialogButtonBox::No: // Close only the current tab m_tabWidget->closeTab(); Q_FALLTHROUGH(); default: event->ignore(); return; } } if (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 // QDialogButtonBox::No -> Show Terminal Panel // QDialogButtonBox::Cancel -> do nothing QDialog *dialog = new QDialog(this, Qt::Dialog); dialog->setWindowTitle(i18nc("@title:window", "Confirmation")); dialog->setModal(true); auto standardButtons = QDialogButtonBox::Yes | QDialogButtonBox::Cancel; if (!m_terminalPanel->isVisible()) { standardButtons |= QDialogButtonBox::No; } QDialogButtonBox *buttons = new QDialogButtonBox(standardButtons); KGuiItem::assign(buttons->button(QDialogButtonBox::Yes), KStandardGuiItem::quit()); if (!m_terminalPanel->isVisible()) { KGuiItem::assign( buttons->button(QDialogButtonBox::No), KGuiItem(i18n("Show &Terminal Panel"), QIcon::fromTheme(QStringLiteral("utilities-terminal")))); } KGuiItem::assign(buttons->button(QDialogButtonBox::Cancel), KStandardGuiItem::cancel()); bool doNotAskAgainCheckboxResult = false; const auto result = KMessageBox::createKMessageBox( dialog, buttons, QMessageBox::Warning, i18n("The program '%1' is still running in the Terminal panel. Are you sure you want to quit?", m_terminalPanel->runningProgramName()), QStringList(), i18n("Do not ask again"), &doNotAskAgainCheckboxResult, KMessageBox::Dangerous); if (doNotAskAgainCheckboxResult) { GeneralSettings::setConfirmClosingTerminalRunningProgram(false); } switch (result) { case QDialogButtonBox::Yes: // Quit break; case QDialogButtonBox::No: actionCollection()->action("show_terminal_panel")->trigger(); // Do not quit, ignore quit event Q_FALLTHROUGH(); default: event->ignore(); return; } } GeneralSettings::setVersion(CurrentDolphinVersion); GeneralSettings::self()->save(); KXmlGuiWindow::closeEvent(event); } void DolphinMainWindow::saveProperties(KConfigGroup& group) { m_tabWidget->saveProperties(group); } void DolphinMainWindow::readProperties(const KConfigGroup& group) { m_tabWidget->readProperties(group); } void DolphinMainWindow::updateNewMenu() { m_newFileMenu->setViewShowsHiddenFiles(activeViewContainer()->view()->hiddenFilesShown()); m_newFileMenu->checkUpToDate(); m_newFileMenu->setPopupFiles(activeViewContainer()->url()); } void DolphinMainWindow::createDirectory() { m_newFileMenu->setViewShowsHiddenFiles(activeViewContainer()->view()->hiddenFilesShown()); m_newFileMenu->setPopupFiles(activeViewContainer()->url()); m_newFileMenu->createDirectory(); } void DolphinMainWindow::quit() { close(); } void DolphinMainWindow::showErrorMessage(const QString& message) { m_activeViewContainer->showMessage(message, DolphinViewContainer::Error); } void DolphinMainWindow::slotUndoAvailable(bool available) { QAction* undoAction = actionCollection()->action(KStandardAction::name(KStandardAction::Undo)); if (undoAction) { undoAction->setEnabled(available); } } void DolphinMainWindow::slotUndoTextChanged(const QString& text) { QAction* undoAction = actionCollection()->action(KStandardAction::name(KStandardAction::Undo)); if (undoAction) { undoAction->setText(text); } } void DolphinMainWindow::undo() { clearStatusBar(); KIO::FileUndoManager::self()->uiInterface()->setParentWidget(this); KIO::FileUndoManager::self()->undo(); } void DolphinMainWindow::cut() { m_activeViewContainer->view()->cutSelectedItems(); } void DolphinMainWindow::copy() { m_activeViewContainer->view()->copySelectedItems(); } void DolphinMainWindow::paste() { m_activeViewContainer->view()->paste(); } void DolphinMainWindow::find() { m_activeViewContainer->setSearchModeEnabled(true); } void DolphinMainWindow::updateSearchAction() { QAction* toggleSearchAction = actionCollection()->action(QStringLiteral("toggle_search")); toggleSearchAction->setChecked(m_activeViewContainer->isSearchModeEnabled()); } void DolphinMainWindow::updatePasteAction() { QAction* pasteAction = actionCollection()->action(KStandardAction::name(KStandardAction::Paste)); QPair pasteInfo = m_activeViewContainer->view()->pasteInfo(); pasteAction->setEnabled(pasteInfo.first); pasteAction->setText(pasteInfo.second); } void DolphinMainWindow::slotDirectoryLoadingCompleted() { updatePasteAction(); } void DolphinMainWindow::slotToolBarActionMiddleClicked(QAction *action) { if (action == actionCollection()->action(KStandardAction::name(KStandardAction::Back))) { goBackInNewTab(); } else if (action == actionCollection()->action(KStandardAction::name(KStandardAction::Forward))) { goForwardInNewTab(); } else if (action == actionCollection()->action(QStringLiteral("go_up"))) { goUpInNewTab(); } else if (action == actionCollection()->action(QStringLiteral("go_home"))) { goHomeInNewTab(); } } void DolphinMainWindow::selectAll() { clearStatusBar(); // if the URL navigator is editable and focused, select the whole // URL instead of all items of the view KUrlNavigator* urlNavigator = m_activeViewContainer->urlNavigator(); QLineEdit* lineEdit = urlNavigator->editor()->lineEdit(); const bool selectUrl = urlNavigator->isUrlEditable() && lineEdit->hasFocus(); if (selectUrl) { lineEdit->selectAll(); } else { m_activeViewContainer->view()->selectAll(); } } void DolphinMainWindow::invertSelection() { clearStatusBar(); m_activeViewContainer->view()->invertSelection(); } void DolphinMainWindow::toggleSplitView() { DolphinTabPage* tabPage = m_tabWidget->currentTabPage(); tabPage->setSplitViewEnabled(!tabPage->splitViewEnabled()); updateViewActions(); } void DolphinMainWindow::toggleSplitStash() { DolphinTabPage* tabPage = m_tabWidget->currentTabPage(); tabPage->setSplitViewEnabled(false); tabPage->setSplitViewEnabled(true, QUrl("stash:/")); } void DolphinMainWindow::reloadView() { clearStatusBar(); m_activeViewContainer->reload(); m_activeViewContainer->statusBar()->updateSpaceInfo(); } void DolphinMainWindow::stopLoading() { m_activeViewContainer->view()->stopLoading(); } void DolphinMainWindow::enableStopAction() { actionCollection()->action(QStringLiteral("stop"))->setEnabled(true); } void DolphinMainWindow::disableStopAction() { actionCollection()->action(QStringLiteral("stop"))->setEnabled(false); } void DolphinMainWindow::showFilterBar() { m_activeViewContainer->setFilterBarVisible(true); } void DolphinMainWindow::toggleEditLocation() { clearStatusBar(); QAction* action = actionCollection()->action(QStringLiteral("editable_location")); KUrlNavigator* urlNavigator = m_activeViewContainer->urlNavigator(); urlNavigator->setUrlEditable(action->isChecked()); } void DolphinMainWindow::replaceLocation() { KUrlNavigator* navigator = m_activeViewContainer->urlNavigator(); QLineEdit* lineEdit = navigator->editor()->lineEdit(); // If the text field currently has focus and everything is selected, // pressing the keyboard shortcut returns the whole thing to breadcrumb mode if (navigator->isUrlEditable() && lineEdit->hasFocus() && lineEdit->selectedText() == lineEdit->text() ) { navigator->setUrlEditable(false); } else { navigator->setUrlEditable(true); navigator->setFocus(); lineEdit->selectAll(); } } void DolphinMainWindow::togglePanelLockState() { const bool newLockState = !GeneralSettings::lockPanels(); foreach (QObject* child, children()) { DolphinDockWidget* dock = qobject_cast(child); if (dock) { dock->setLocked(newLockState); } } GeneralSettings::setLockPanels(newLockState); } void DolphinMainWindow::slotTerminalPanelVisibilityChanged() { if (m_terminalPanel->isHiddenInVisibleWindow() && m_activeViewContainer) { m_activeViewContainer->view()->setFocus(); } } void DolphinMainWindow::goBack() { KUrlNavigator* urlNavigator = m_activeViewContainer->urlNavigator(); urlNavigator->goBack(); if (urlNavigator->locationState().isEmpty()) { // An empty location state indicates a redirection URL, // which must be skipped too urlNavigator->goBack(); } } void DolphinMainWindow::goForward() { m_activeViewContainer->urlNavigator()->goForward(); } void DolphinMainWindow::goUp() { m_activeViewContainer->urlNavigator()->goUp(); } void DolphinMainWindow::goHome() { m_activeViewContainer->urlNavigator()->goHome(); } void DolphinMainWindow::goBackInNewTab() { KUrlNavigator* urlNavigator = activeViewContainer()->urlNavigator(); const int index = urlNavigator->historyIndex() + 1; openNewTabAfterCurrentTab(urlNavigator->locationUrl(index)); } void DolphinMainWindow::goForwardInNewTab() { KUrlNavigator* urlNavigator = activeViewContainer()->urlNavigator(); const int index = urlNavigator->historyIndex() - 1; openNewTabAfterCurrentTab(urlNavigator->locationUrl(index)); } void DolphinMainWindow::goUpInNewTab() { const QUrl currentUrl = activeViewContainer()->urlNavigator()->locationUrl(); openNewTabAfterCurrentTab(KIO::upUrl(currentUrl)); } void DolphinMainWindow::goHomeInNewTab() { openNewTabAfterCurrentTab(Dolphin::homeUrl()); } void DolphinMainWindow::compareFiles() { const KFileItemList items = m_tabWidget->currentTabPage()->selectedItems(); if (items.count() != 2) { // The action is disabled in this case, but it could have been triggered // via D-Bus, see https://bugs.kde.org/show_bug.cgi?id=325517 return; } QUrl urlA = items.at(0).url(); QUrl urlB = items.at(1).url(); QString command(QStringLiteral("kompare -c \"")); command.append(urlA.toDisplayString(QUrl::PreferLocalFile)); command.append("\" \""); command.append(urlB.toDisplayString(QUrl::PreferLocalFile)); command.append('\"'); KRun::runCommand(command, QStringLiteral("Kompare"), QStringLiteral("kompare"), this); } void DolphinMainWindow::toggleShowMenuBar() { const bool visible = menuBar()->isVisible(); menuBar()->setVisible(!visible); if (visible) { createControlButton(); } else { deleteControlButton(); } } void DolphinMainWindow::openTerminal() { 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(); } KToolInvocation::invokeTerminal(QString(), dir); } void DolphinMainWindow::editSettings() { if (!m_settingsDialog) { DolphinViewContainer* container = activeViewContainer(); container->view()->writeSettings(); const QUrl url = container->url(); DolphinSettingsDialog* settingsDialog = new DolphinSettingsDialog(url, this); connect(settingsDialog, &DolphinSettingsDialog::settingsChanged, this, &DolphinMainWindow::refreshViews); settingsDialog->setAttribute(Qt::WA_DeleteOnClose); settingsDialog->show(); m_settingsDialog = settingsDialog; } else { m_settingsDialog.data()->raise(); } } void DolphinMainWindow::handleUrl(const QUrl& url) { delete m_lastHandleUrlStatJob; m_lastHandleUrlStatJob = nullptr; if (url.isLocalFile() && QFileInfo(url.toLocalFile()).isDir()) { activeViewContainer()->setUrl(url); } else if (KProtocolManager::supportsListing(url)) { // stat the URL to see if it is a dir or not m_lastHandleUrlStatJob = KIO::stat(url, KIO::HideProgressInfo); if (m_lastHandleUrlStatJob->uiDelegate()) { KJobWidgets::setWindow(m_lastHandleUrlStatJob, this); } connect(m_lastHandleUrlStatJob, &KIO::Job::result, this, &DolphinMainWindow::slotHandleUrlStatFinished); } else { new KRun(url, this); // Automatically deletes itself after being finished } } void DolphinMainWindow::slotHandleUrlStatFinished(KJob* job) { m_lastHandleUrlStatJob = nullptr; const KIO::UDSEntry entry = static_cast(job)->statResult(); const QUrl url = static_cast(job)->url(); if (entry.isDir()) { activeViewContainer()->setUrl(url); } else { new KRun(url, this); // Automatically deletes itself after being finished } } void DolphinMainWindow::slotWriteStateChanged(bool isFolderWritable) { // trash:/ is writable but we don't want to create new items in it. // TODO: remove the trash check once https://phabricator.kde.org/T8234 is implemented newFileMenu()->setEnabled(isFolderWritable && m_activeViewContainer->url().scheme() != QLatin1String("trash")); } void DolphinMainWindow::openContextMenu(const QPoint& pos, const KFileItem& item, const QUrl& url, const QList& customActions) { 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: openNewTabAfterLastTab(KIO::upUrl(item.url())); break; case DolphinContextMenu::None: default: break; } // Delete the menu, unless it has been deleted in its own nested event loop already. if (contextMenu) { contextMenu->deleteLater(); } } void DolphinMainWindow::updateControlMenu() { 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(); // Add "Create New" menu menu->addMenu(m_newFileMenu->menu()); menu->addSeparator(); // Overwrite Find action to Search action QAction *searchAction = ac->action(KStandardAction::name(KStandardAction::Find)); searchAction->setText(i18n("Search...")); // Add "Edit" actions bool added = addActionToMenu(ac->action(KStandardAction::name(KStandardAction::Undo)), menu) | addActionToMenu(searchAction, menu) | addActionToMenu(ac->action(KStandardAction::name(KStandardAction::SelectAll)), menu) | addActionToMenu(ac->action(QStringLiteral("invert_selection")), menu); if (added) { menu->addSeparator(); } // Add "View" actions if (!GeneralSettings::showZoomSlider()) { addActionToMenu(ac->action(KStandardAction::name(KStandardAction::ZoomIn)), menu); addActionToMenu(ac->action(KStandardAction::name(KStandardAction::ZoomOut)), menu); menu->addSeparator(); } added = addActionToMenu(ac->action(QStringLiteral("sort")), menu) | addActionToMenu(ac->action(QStringLiteral("view_mode")), menu) | addActionToMenu(ac->action(QStringLiteral("additional_info")), menu) | addActionToMenu(ac->action(QStringLiteral("show_preview")), menu) | addActionToMenu(ac->action(QStringLiteral("show_in_groups")), menu) | addActionToMenu(ac->action(QStringLiteral("show_hidden_files")), menu); if (added) { menu->addSeparator(); } added = addActionToMenu(ac->action(QStringLiteral("split_view")), menu) | addActionToMenu(ac->action(KStandardAction::name(KStandardAction::Redisplay)), menu) | addActionToMenu(ac->action(QStringLiteral("view_properties")), menu); if (added) { menu->addSeparator(); } addActionToMenu(ac->action(QStringLiteral("panels")), menu); QMenu* locationBarMenu = new QMenu(i18nc("@action:inmenu", "Location Bar"), menu); locationBarMenu->addAction(ac->action(QStringLiteral("editable_location"))); locationBarMenu->addAction(ac->action(QStringLiteral("replace_location"))); menu->addMenu(locationBarMenu); menu->addSeparator(); // Add "Go" menu QMenu* goMenu = new QMenu(i18nc("@action:inmenu", "Go"), menu); goMenu->addAction(ac->action(KStandardAction::name(KStandardAction::Back))); goMenu->addAction(ac->action(KStandardAction::name(KStandardAction::Forward))); goMenu->addAction(ac->action(KStandardAction::name(KStandardAction::Up))); goMenu->addAction(ac->action(KStandardAction::name(KStandardAction::Home))); goMenu->addAction(ac->action(QStringLiteral("closed_tabs"))); KActionMenu *bookmarkMenu = new KActionMenu(i18nc("@title:menu", "&Bookmarks"), goMenu); m_bookmarkHandler->fillControlMenu(bookmarkMenu->menu(), ac); goMenu->addAction(bookmarkMenu); menu->addMenu(goMenu); // Add "Tool" menu QMenu* toolsMenu = new QMenu(i18nc("@action:inmenu", "Tools"), menu); toolsMenu->addAction(ac->action(QStringLiteral("show_filter_bar"))); toolsMenu->addAction(ac->action(QStringLiteral("compare_files"))); toolsMenu->addAction(ac->action(QStringLiteral("open_terminal"))); toolsMenu->addAction(ac->action(QStringLiteral("change_remote_encoding"))); menu->addMenu(toolsMenu); // 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); // Add "Help" menu menu->addMenu(m_helpMenu->menu()); menu->addSeparator(); addActionToMenu(ac->action(KStandardAction::name(KStandardAction::ShowMenubar)), menu); } void DolphinMainWindow::updateToolBar() { if (!menuBar()->isVisible()) { createControlButton(); } } void DolphinMainWindow::slotControlButtonDeleted() { m_controlButton = nullptr; m_updateToolBarTimer->start(); } void DolphinMainWindow::slotPlaceActivated(const QUrl& url) { DolphinViewContainer* view = activeViewContainer(); if (view->url() == 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(); } else { changeUrl(url); } } void DolphinMainWindow::closedTabsCountChanged(unsigned int count) { actionCollection()->action(QStringLiteral("undo_close_tab"))->setEnabled(count > 0); } void DolphinMainWindow::activeViewChanged(DolphinViewContainer* viewContainer) { DolphinViewContainer* oldViewContainer = m_activeViewContainer; Q_ASSERT(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); oldViewContainer->view()->disconnect(this); oldViewContainer->urlNavigator()->disconnect(this); // except the requestItemInfo so that on hover the information panel can still be updated connect(oldViewContainer->view(), &DolphinView::requestItemInfo, this, &DolphinMainWindow::requestItemInfo); } connectViewSignals(viewContainer); m_actionHandler->setCurrentView(viewContainer->view()); updateHistory(); updateFileAndEditActions(); updatePasteAction(); updateViewActions(); updateGoActions(); updateSearchAction(); const QUrl url = viewContainer->url(); emit urlChanged(url); } void DolphinMainWindow::tabCountChanged(int count) { const bool enableTabActions = (count > 1); actionCollection()->action(QStringLiteral("activate_next_tab"))->setEnabled(enableTabActions); actionCollection()->action(QStringLiteral("activate_prev_tab"))->setEnabled(enableTabActions); } void DolphinMainWindow::updateWindowTitle() { const QString newTitle = m_activeViewContainer->caption(); if (windowTitle() != newTitle) { setWindowTitle(newTitle); } } void DolphinMainWindow::slotStorageTearDownFromPlacesRequested(const QString& mountPath) { if (m_terminalPanel->currentWorkingDirectory().startsWith(mountPath)) { m_tearDownFromPlacesRequested = true; m_terminalPanel->goHome(); // m_placesPanel->proceedWithTearDown() will be called in slotTerminalDirectoryChanged } else { m_placesPanel->proceedWithTearDown(); } } void DolphinMainWindow::slotStorageTearDownExternallyRequested(const QString& mountPath) { if (m_terminalPanel->currentWorkingDirectory().startsWith(mountPath)) { m_tearDownFromPlacesRequested = false; m_terminalPanel->goHome(); } } void DolphinMainWindow::setupActions() { // 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); connect(menu, &QMenu::aboutToShow, this, &DolphinMainWindow::updateNewMenu); QAction* newWindow = KStandardAction::openNew(this, &DolphinMainWindow::openNewMainWindow, actionCollection()); newWindow->setText(i18nc("@action:inmenu File", "New &Window")); newWindow->setToolTip(i18nc("@info", "Open a new Dolphin window")); newWindow->setWhatsThis(xi18nc("@info:whatsthis", "This opens a new " "window just like this one with the current location and view." "You can drag and drop items between windows.")); newWindow->setIcon(QIcon::fromTheme(QStringLiteral("window-new"))); QAction* newTab = actionCollection()->addAction(QStringLiteral("new_tab")); newTab->setIcon(QIcon::fromTheme(QStringLiteral("tab-new"))); newTab->setText(i18nc("@action:inmenu File", "New Tab")); newTab->setWhatsThis(xi18nc("@info:whatsthis", "This opens a new " "Tab with the current location and view." "A tab is an additional view within this window. " "You can drag and drop items between tabs.")); actionCollection()->setDefaultShortcuts(newTab, {Qt::CTRL + Qt::Key_T, Qt::CTRL + Qt::SHIFT + Qt::Key_N}); connect(newTab, &QAction::triggered, this, &DolphinMainWindow::openNewActivatedTab); QAction* addToPlaces = actionCollection()->addAction(QStringLiteral("add_to_places")); addToPlaces->setIcon(QIcon::fromTheme(QStringLiteral("bookmark-new"))); addToPlaces->setWhatsThis(xi18nc("@info:whatsthis", "This adds the selected folder " "to the Places panel.")); connect(addToPlaces, &QAction::triggered, this, &DolphinMainWindow::addToPlaces); QAction* closeTab = KStandardAction::close(m_tabWidget, QOverload<>::of(&DolphinTabWidget::closeTab), actionCollection()); closeTab->setText(i18nc("@action:inmenu File", "Close Tab")); closeTab->setWhatsThis(i18nc("@info:whatsthis", "This closes the " "currently viewed tab. If no more tabs are left this window " "will close instead.")); QAction* quitAction = KStandardAction::quit(this, &DolphinMainWindow::quit, actionCollection()); quitAction->setWhatsThis(i18nc("@info:whatsthis quit", "This closes this window.")); // setup 'Edit' menu KStandardAction::undo(this, &DolphinMainWindow::undo, actionCollection()); // i18n: This will be the last paragraph for the whatsthis for all three: // Cut, Copy and Paste const QString cutCopyPastePara = xi18nc("@info:whatsthis", "Cut, " "Copy and Paste work between many " "applications and are among the most used commands. That's why their " "keyboard shortcuts are prominently placed right " "next to each other on the keyboard: Ctrl+X, " "Ctrl+C and Ctrl+V."); QAction* cutAction = KStandardAction::cut(this, &DolphinMainWindow::cut, actionCollection()); cutAction->setWhatsThis(xi18nc("@info:whatsthis cut", "This copies the items " "in your current selection to the clipboard." "Use the Paste action afterwards to copy them from " "the clipboard to a new location. The items will be removed from their " "initial location.") + cutCopyPastePara); QAction* copyAction = KStandardAction::copy(this, &DolphinMainWindow::copy, actionCollection()); copyAction->setWhatsThis(xi18nc("@info:whatsthis copy", "This copies the " "items in your current selection to the clipboard." "Use the Paste action afterwards to copy them " "from the clipboard to a new location.") + cutCopyPastePara); QAction* paste = KStandardAction::paste(this, &DolphinMainWindow::paste, actionCollection()); // The text of the paste-action is modified dynamically by Dolphin // (e. g. to "Paste One Folder"). To prevent that the size of the toolbar changes // due to the long text, the text "Paste" is used: paste->setIconText(i18nc("@action:inmenu Edit", "Paste")); paste->setWhatsThis(xi18nc("@info:whatsthis paste", "This copies the items from " "your clipboard to the currently viewed folder." "If the items were added to the clipboard by the Cut " "action they are removed from their old location.") + cutCopyPastePara); QAction *searchAction = KStandardAction::find(this, &DolphinMainWindow::find, actionCollection()); searchAction->setText(i18n("Search...")); searchAction->setToolTip(i18nc("@info:tooltip", "Search for files and folders")); searchAction->setWhatsThis(xi18nc("@info:whatsthis find", "This helps you " "find files and folders by opening a find bar. " "There you can enter search terms and specify settings to find the " "objects you are looking for.Use this help again on " "the find bar so we can have a look at it while the settings are " "explained.")); // 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 // existing workflows (search bar always open and Ctrl-F to focus) QAction *toggleSearchAction = actionCollection()->addAction(QStringLiteral("toggle_search")); toggleSearchAction->setText(i18nc("@action:inmenu", "Toggle Search Bar")); toggleSearchAction->setIconText(i18nc("@action:intoolbar", "Search")); toggleSearchAction->setIcon(searchAction->icon()); toggleSearchAction->setToolTip(searchAction->toolTip()); toggleSearchAction->setWhatsThis(searchAction->whatsThis()); toggleSearchAction->setCheckable(true); QAction* selectAllAction = KStandardAction::selectAll(this, &DolphinMainWindow::selectAll, actionCollection()); selectAllAction->setWhatsThis(xi18nc("@info:whatsthis", "This selects all " "files and folders in the current location.")); QAction* invertSelection = actionCollection()->addAction(QStringLiteral("invert_selection")); invertSelection->setText(i18nc("@action:inmenu Edit", "Invert Selection")); invertSelection->setWhatsThis(xi18nc("@info:whatsthis invert", "This selects all " "objects that you have currently not selected instead.")); invertSelection->setIcon(QIcon::fromTheme(QStringLiteral("edit-select-invert"))); actionCollection()->setDefaultShortcut(invertSelection, Qt::CTRL + Qt::SHIFT + Qt::Key_A); connect(invertSelection, &QAction::triggered, this, &DolphinMainWindow::invertSelection); // setup 'View' menu // (note that most of it is set up in DolphinViewActionHandler) QAction* split = actionCollection()->addAction(QStringLiteral("split_view")); split->setWhatsThis(xi18nc("@info:whatsthis find", "This splits " "the folder view below into two autonomous views.This " "way you can see two locations at once and move items between them " "quickly.Click this again afterwards to recombine the views.")); actionCollection()->setDefaultShortcut(split, Qt::Key_F3); connect(split, &QAction::triggered, this, &DolphinMainWindow::toggleSplitView); QAction* stashSplit = actionCollection()->addAction(QStringLiteral("split_stash")); actionCollection()->setDefaultShortcut(stashSplit, Qt::CTRL + Qt::Key_S); stashSplit->setText(i18nc("@action:intoolbar Stash", "Stash")); 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(KProtocolInfo::isKnownProtocol("stash")); connect(stashSplit, &QAction::triggered, this, &DolphinMainWindow::toggleSplitStash); KStandardAction::redisplay(this, &DolphinMainWindow::reloadView, actionCollection()); QAction* stop = actionCollection()->addAction(QStringLiteral("stop")); stop->setText(i18nc("@action:inmenu View", "Stop")); stop->setToolTip(i18nc("@info", "Stop loading")); stop->setWhatsThis(i18nc("@info", "This stops the loading of the contents of the current folder.")); stop->setIcon(QIcon::fromTheme(QStringLiteral("process-stop"))); connect(stop, &QAction::triggered, this, &DolphinMainWindow::stopLoading); KToggleAction* editableLocation = actionCollection()->add(QStringLiteral("editable_location")); editableLocation->setText(i18nc("@action:inmenu Navigation Bar", "Editable Location")); editableLocation->setWhatsThis(xi18nc("@info:whatsthis", "This toggles the Location Bar to be " "editable so you can directly enter a location you want to go to." "You can also switch to editing by clicking to the right of the " "location and switch back by confirming the edited location.")); actionCollection()->setDefaultShortcut(editableLocation, Qt::Key_F6); connect(editableLocation, &KToggleAction::triggered, this, &DolphinMainWindow::toggleEditLocation); QAction* replaceLocation = actionCollection()->addAction(QStringLiteral("replace_location")); replaceLocation->setText(i18nc("@action:inmenu Navigation Bar", "Replace Location")); // i18n: "enter" is used both in the meaning of "writing" and "going to" a new location here. // Both meanings are useful but not necessary to understand the use of "Replace Location". // So you might want to be more verbose in your language to convey the meaning but it's up to you. replaceLocation->setWhatsThis(xi18nc("@info:whatsthis", "This switches to editing the location and selects it " "so you can quickly enter a different location.")); actionCollection()->setDefaultShortcut(replaceLocation, Qt::CTRL + Qt::Key_L); connect(replaceLocation, &QAction::triggered, this, &DolphinMainWindow::replaceLocation); // setup 'Go' menu QAction* backAction = KStandardAction::back(this, &DolphinMainWindow::goBack, actionCollection()); auto backShortcuts = backAction->shortcuts(); backShortcuts.append(QKeySequence(Qt::Key_Backspace)); actionCollection()->setDefaultShortcuts(backAction, backShortcuts); DolphinRecentTabsMenu* recentTabsMenu = new DolphinRecentTabsMenu(this); actionCollection()->addAction(QStringLiteral("closed_tabs"), recentTabsMenu); connect(m_tabWidget, &DolphinTabWidget::rememberClosedTab, recentTabsMenu, &DolphinRecentTabsMenu::rememberClosedTab); connect(recentTabsMenu, &DolphinRecentTabsMenu::restoreClosedTab, m_tabWidget, &DolphinTabWidget::restoreClosedTab); connect(recentTabsMenu, &DolphinRecentTabsMenu::closedTabsCountChanged, this, &DolphinMainWindow::closedTabsCountChanged); QAction* undoCloseTab = actionCollection()->addAction(QStringLiteral("undo_close_tab")); undoCloseTab->setText(i18nc("@action:inmenu File", "Undo close tab")); undoCloseTab->setWhatsThis(i18nc("@info:whatsthis undo close tab", "This returns you to the previously closed tab.")); actionCollection()->setDefaultShortcut(undoCloseTab, Qt::CTRL + Qt::SHIFT + Qt::Key_T); undoCloseTab->setIcon(QIcon::fromTheme(QStringLiteral("edit-undo"))); undoCloseTab->setEnabled(false); connect(undoCloseTab, &QAction::triggered, recentTabsMenu, &DolphinRecentTabsMenu::undoCloseTab); auto undoAction = actionCollection()->action(KStandardAction::name(KStandardAction::Undo)); undoAction->setWhatsThis(xi18nc("@info:whatsthis", "This undoes " "the last change you made to files or folders." "Such changes include creating, renaming " "and moving them to a different location " "or to the Trash. Changes that can't " "be undone will ask for your confirmation.")); undoAction->setEnabled(false); // undo should be disabled by default KStandardAction::forward(this, &DolphinMainWindow::goForward, actionCollection()); KStandardAction::up(this, &DolphinMainWindow::goUp, actionCollection()); QAction* homeAction = KStandardAction::home(this, &DolphinMainWindow::goHome, actionCollection()); homeAction->setWhatsThis(xi18nc("@info:whatsthis", "Go to your " "Home folder.Every user account " "has their own Home that contains their data " "including folders that contain personal application data.")); // setup 'Tools' menu QAction* showFilterBar = actionCollection()->addAction(QStringLiteral("show_filter_bar")); showFilterBar->setText(i18nc("@action:inmenu Tools", "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. " "Only those that contain the text in their name will be kept in view.")); showFilterBar->setIcon(QIcon::fromTheme(QStringLiteral("view-filter"))); actionCollection()->setDefaultShortcuts(showFilterBar, {Qt::CTRL + Qt::Key_I, Qt::Key_Slash}); connect(showFilterBar, &QAction::triggered, this, &DolphinMainWindow::showFilterBar); QAction* compareFiles = actionCollection()->addAction(QStringLiteral("compare_files")); compareFiles->setText(i18nc("@action:inmenu Tools", "Compare Files")); compareFiles->setIcon(QIcon::fromTheme(QStringLiteral("kompare"))); compareFiles->setEnabled(false); connect(compareFiles, &QAction::triggered, this, &DolphinMainWindow::compareFiles); #ifdef HAVE_TERMINAL if (KAuthorized::authorize(QStringLiteral("shell_access"))) { 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 in the terminal application.")); openTerminal->setIcon(QIcon::fromTheme(QStringLiteral("utilities-terminal"))); actionCollection()->setDefaultShortcut(openTerminal, Qt::SHIFT + Qt::Key_F4); connect(openTerminal, &QAction::triggered, this, &DolphinMainWindow::openTerminal); } #endif // setup 'Bookmarks' menu 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); m_bookmarkHandler = new DolphinBookmarkHandler(this, actionCollection(), bookmarkMenu->menu(), this); actionCollection()->addAction(QStringLiteral("bookmarks"), bookmarkMenu); // setup 'Settings' menu KToggleAction* showMenuBar = KStandardAction::showMenubar(nullptr, nullptr, actionCollection()); showMenuBar->setWhatsThis(xi18nc("@info:whatsthis", "This switches between having a Menubar " "and having a Control button. Both " "contain mostly the same commands and configuration options.")); connect(showMenuBar, &KToggleAction::triggered, // Fixes #286822 this, &DolphinMainWindow::toggleShowMenuBar, Qt::QueuedConnection); 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()); // not in menu actions QList nextTabKeys = KStandardShortcut::tabNext(); nextTabKeys.append(QKeySequence(Qt::CTRL + Qt::Key_Tab)); QList prevTabKeys = KStandardShortcut::tabPrev(); prevTabKeys.append(QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_Tab)); QAction* activateNextTab = actionCollection()->addAction(QStringLiteral("activate_next_tab")); activateNextTab->setIconText(i18nc("@action:inmenu", "Next Tab")); activateNextTab->setText(i18nc("@action:inmenu", "Activate Next Tab")); activateNextTab->setEnabled(false); connect(activateNextTab, &QAction::triggered, m_tabWidget, &DolphinTabWidget::activateNextTab); actionCollection()->setDefaultShortcuts(activateNextTab, nextTabKeys); QAction* activatePrevTab = actionCollection()->addAction(QStringLiteral("activate_prev_tab")); activatePrevTab->setIconText(i18nc("@action:inmenu", "Previous Tab")); activatePrevTab->setText(i18nc("@action:inmenu", "Activate Previous Tab")); activatePrevTab->setEnabled(false); connect(activatePrevTab, &QAction::triggered, m_tabWidget, &DolphinTabWidget::activatePrevTab); actionCollection()->setDefaultShortcuts(activatePrevTab, prevTabKeys); // for context menu QAction* showTarget = actionCollection()->addAction(QStringLiteral("show_target")); showTarget->setText(i18nc("@action:inmenu", "Show Target")); showTarget->setIcon(QIcon::fromTheme(QStringLiteral("document-open-folder"))); showTarget->setEnabled(false); connect(showTarget, &QAction::triggered, this, &DolphinMainWindow::showTarget); QAction* openInNewTab = actionCollection()->addAction(QStringLiteral("open_in_new_tab")); openInNewTab->setText(i18nc("@action:inmenu", "Open in New Tab")); openInNewTab->setIcon(QIcon::fromTheme(QStringLiteral("tab-new"))); connect(openInNewTab, &QAction::triggered, this, &DolphinMainWindow::openInNewTab); QAction* openInNewTabs = actionCollection()->addAction(QStringLiteral("open_in_new_tabs")); openInNewTabs->setText(i18nc("@action:inmenu", "Open in New Tabs")); openInNewTabs->setIcon(QIcon::fromTheme(QStringLiteral("tab-new"))); connect(openInNewTabs, &QAction::triggered, this, &DolphinMainWindow::openInNewTab); QAction* openInNewWindow = actionCollection()->addAction(QStringLiteral("open_in_new_window")); openInNewWindow->setText(i18nc("@action:inmenu", "Open in New Window")); openInNewWindow->setIcon(QIcon::fromTheme(QStringLiteral("window-new"))); connect(openInNewWindow, &QAction::triggered, this, &DolphinMainWindow::openInNewWindow); } void DolphinMainWindow::setupDockWidgets() { const bool lock = GeneralSettings::lockPanels(); KDualAction* lockLayoutAction = actionCollection()->add(QStringLiteral("lock_panels")); lockLayoutAction->setActiveText(i18nc("@action:inmenu Panels", "Unlock Panels")); lockLayoutAction->setActiveIcon(QIcon::fromTheme(QStringLiteral("object-unlocked"))); lockLayoutAction->setInactiveText(i18nc("@action:inmenu Panels", "Lock Panels")); lockLayoutAction->setInactiveIcon(QIcon::fromTheme(QStringLiteral("object-locked"))); lockLayoutAction->setWhatsThis(xi18nc("@info:whatsthis", "This " "switches between having panels locked or " "unlocked.Unlocked panels can be " "dragged to the other side of the window and have a close " "button.Locked panels are embedded more cleanly.")); lockLayoutAction->setActive(lock); connect(lockLayoutAction, &KDualAction::triggered, this, &DolphinMainWindow::togglePanelLockState); // Setup "Information" DolphinDockWidget* infoDock = new DolphinDockWidget(i18nc("@title:window", "Information")); infoDock->setLocked(lock); infoDock->setObjectName(QStringLiteral("infoDock")); infoDock->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea); #ifdef HAVE_BALOO InformationPanel* infoPanel = new InformationPanel(infoDock); infoPanel->setCustomContextMenuActions({lockLayoutAction}); connect(infoPanel, &InformationPanel::urlActivated, this, &DolphinMainWindow::handleUrl); infoDock->setWidget(infoPanel); QAction* infoAction = infoDock->toggleViewAction(); createPanelAction(QIcon::fromTheme(QStringLiteral("dialog-information")), Qt::Key_F11, infoAction, QStringLiteral("show_information_panel")); addDockWidget(Qt::RightDockWidgetArea, infoDock); connect(this, &DolphinMainWindow::urlChanged, infoPanel, &InformationPanel::setUrl); connect(this, &DolphinMainWindow::selectionChanged, infoPanel, &InformationPanel::setSelection); connect(this, &DolphinMainWindow::requestItemInfo, infoPanel, &InformationPanel::requestDelayedItemInfo); #endif // i18n: This is the last paragraph for the "What's This"-texts of all four panels. const QString panelWhatsThis = xi18nc("@info:whatsthis", "To show or " "hide panels like this go to Control|Panels " "or View|Panels."); #ifdef HAVE_BALOO actionCollection()->action(QStringLiteral("show_information_panel")) ->setWhatsThis(xi18nc("@info:whatsthis", " This toggles the " "information panel at the right side of the " "window.The panel provides in-depth information " "about the items your mouse is hovering over or about the selected " "items. Otherwise it informs you about the currently viewed folder." "For single items a preview of their contents is provided.")); #endif infoDock->setWhatsThis(xi18nc("@info:whatsthis", "This panel " "provides in-depth information about the items your mouse is " "hovering over or about the selected items. Otherwise it informs " "you about the currently viewed folder.For single items a " "preview of their contents is provided.You can configure " "which and how details are given here by right-clicking.") + panelWhatsThis); // Setup "Folders" DolphinDockWidget* foldersDock = new DolphinDockWidget(i18nc("@title:window", "Folders")); foldersDock->setLocked(lock); foldersDock->setObjectName(QStringLiteral("foldersDock")); foldersDock->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea); FoldersPanel* foldersPanel = new FoldersPanel(foldersDock); foldersPanel->setCustomContextMenuActions({lockLayoutAction}); foldersDock->setWidget(foldersPanel); QAction* foldersAction = foldersDock->toggleViewAction(); createPanelAction(QIcon::fromTheme(QStringLiteral("folder")), Qt::Key_F7, foldersAction, QStringLiteral("show_folders_panel")); addDockWidget(Qt::LeftDockWidgetArea, foldersDock); connect(this, &DolphinMainWindow::urlChanged, foldersPanel, &FoldersPanel::setUrl); connect(foldersPanel, &FoldersPanel::folderActivated, this, &DolphinMainWindow::changeUrl); connect(foldersPanel, &FoldersPanel::folderMiddleClicked, this, &DolphinMainWindow::openNewTabAfterCurrentTab); connect(foldersPanel, &FoldersPanel::errorMessage, this, &DolphinMainWindow::showErrorMessage); actionCollection()->action(QStringLiteral("show_folders_panel")) ->setWhatsThis(xi18nc("@info:whatsthis", "This toggles the " "folders panel at the left side of the window." "It shows the folders of the file system" " in a tree view.")); foldersDock->setWhatsThis(xi18nc("@info:whatsthis", "This panel " "shows the folders of the file system in a " "tree view.Click a folder to go " "there. Click the arrow to the left of a folder to see its subfolders. " "This allows quick switching between any folders.") + panelWhatsThis); // Setup "Terminal" #ifdef HAVE_TERMINAL if (KAuthorized::authorize(QStringLiteral("shell_access"))) { DolphinDockWidget* terminalDock = new DolphinDockWidget(i18nc("@title:window Shell terminal", "Terminal")); terminalDock->setLocked(lock); terminalDock->setObjectName(QStringLiteral("terminalDock")); m_terminalPanel = new TerminalPanel(terminalDock); m_terminalPanel->setCustomContextMenuActions({lockLayoutAction}); terminalDock->setWidget(m_terminalPanel); connect(m_terminalPanel, &TerminalPanel::hideTerminalPanel, terminalDock, &DolphinDockWidget::hide); connect(m_terminalPanel, &TerminalPanel::changeUrl, this, &DolphinMainWindow::slotTerminalDirectoryChanged); connect(terminalDock, &DolphinDockWidget::visibilityChanged, m_terminalPanel, &TerminalPanel::dockVisibilityChanged); connect(terminalDock, &DolphinDockWidget::visibilityChanged, this, &DolphinMainWindow::slotTerminalPanelVisibilityChanged); QAction* terminalAction = terminalDock->toggleViewAction(); createPanelAction(QIcon::fromTheme(QStringLiteral("utilities-terminal")), Qt::Key_F4, terminalAction, QStringLiteral("show_terminal_panel")); addDockWidget(Qt::BottomDockWidgetArea, terminalDock); connect(this, &DolphinMainWindow::urlChanged, m_terminalPanel, &TerminalPanel::setUrl); if (GeneralSettings::version() < 200) { terminalDock->hide(); } actionCollection()->action(QStringLiteral("show_terminal_panel")) ->setWhatsThis(xi18nc("@info:whatsthis", "This toggles the " "terminal panel at the bottom of the window." "The location in the terminal will always match the folder " "view so you can navigate using either.The terminal " "panel is not needed for basic computer usage but can be useful " "for advanced tasks. To learn more about terminals use the help " "in a standalone terminal application like Konsole.")); terminalDock->setWhatsThis(xi18nc("@info:whatsthis", "This is " "the terminal panel. It behaves like a " "normal terminal but will match the location of the folder view " "so you can navigate using either.The terminal panel " "is not needed for basic computer usage but can be useful for " "advanced tasks. To learn more about terminals use the help in a " "standalone terminal application like Konsole.") + panelWhatsThis); } #endif if (GeneralSettings::version() < 200) { infoDock->hide(); foldersDock->hide(); } // Setup "Places" DolphinDockWidget* placesDock = new DolphinDockWidget(i18nc("@title:window", "Places")); placesDock->setLocked(lock); placesDock->setObjectName(QStringLiteral("placesDock")); placesDock->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea); m_placesPanel = new PlacesPanel(placesDock); m_placesPanel->setCustomContextMenuActions({lockLayoutAction}); placesDock->setWidget(m_placesPanel); QAction *placesAction = placesDock->toggleViewAction(); createPanelAction(QIcon::fromTheme(QStringLiteral("bookmarks")), Qt::Key_F9, placesAction, QStringLiteral("show_places_panel")); addDockWidget(Qt::LeftDockWidgetArea, placesDock); connect(m_placesPanel, &PlacesPanel::placeActivated, this, &DolphinMainWindow::slotPlaceActivated); connect(m_placesPanel, &PlacesPanel::placeMiddleClicked, this, &DolphinMainWindow::openNewTabAfterCurrentTab); connect(m_placesPanel, &PlacesPanel::errorMessage, this, &DolphinMainWindow::showErrorMessage); connect(this, &DolphinMainWindow::urlChanged, m_placesPanel, &PlacesPanel::setUrl); connect(placesDock, &DolphinDockWidget::visibilityChanged, m_tabWidget, &DolphinTabWidget::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); m_tabWidget->slotPlacesPanelVisibilityChanged(m_placesPanel->isVisible()); - auto actionShowAllPlaces = new QAction(QIcon::fromTheme(QStringLiteral("hint")), i18nc("@item:inmenu", "Show Hidden Places"), this); + auto actionShowAllPlaces = new QAction(QIcon::fromTheme(QStringLiteral("view-hidden")), i18nc("@item:inmenu", "Show Hidden Places"), this); actionShowAllPlaces->setCheckable(true); actionShowAllPlaces->setDisabled(true); actionShowAllPlaces->setWhatsThis(i18nc("@info:whatsthis", "This displays " "all places in the places panel that have been hidden. They will " "appear semi-transparent unless you uncheck their hide property.")); connect(actionShowAllPlaces, &QAction::triggered, this, [actionShowAllPlaces, this](bool checked){ - actionShowAllPlaces->setIcon(QIcon::fromTheme(checked ? QStringLiteral("visibility") : QStringLiteral("hint"))); + actionShowAllPlaces->setIcon(QIcon::fromTheme(checked ? QStringLiteral("view-visible") : QStringLiteral("view-hidden"))); m_placesPanel->showHiddenEntries(checked); }); connect(m_placesPanel, &PlacesPanel::showHiddenEntriesChanged, this, [actionShowAllPlaces] (bool checked){ actionShowAllPlaces->setChecked(checked); - actionShowAllPlaces->setIcon(QIcon::fromTheme(checked ? QStringLiteral("visibility") : QStringLiteral("hint"))); + actionShowAllPlaces->setIcon(QIcon::fromTheme(checked ? QStringLiteral("view-visible") : QStringLiteral("view-hidden"))); }); actionCollection()->action(QStringLiteral("show_places_panel")) ->setWhatsThis(xi18nc("@info:whatsthis", "This toggles the " "places panel at the left side of the window." "It allows you to go to locations you have " "bookmarked and to access disk or media attached to the computer " "or to the network. It also contains sections to find recently " "saved files or files of a certain type.")); placesDock->setWhatsThis(xi18nc("@info:whatsthis", "This is the " "Places panel. It allows you to go to locations " "you have bookmarked and to access disk or media attached to the " "computer or to the network. It also contains sections to find " "recently saved files or files of a certain type." "Click on an entry to go there. Click with the right mouse button " "instead to open any entry in a new tab or new window." "New entries can be added by dragging folders onto this panel. " "Right-click any section or entry to hide it. Right-click an empty " "space on this panel and select Show Hidden Places" " to display it again.") + panelWhatsThis); // Add actions into the "Panels" menu KActionMenu* panelsMenu = new KActionMenu(i18nc("@action:inmenu View", "Panels"), this); actionCollection()->addAction(QStringLiteral("panels"), panelsMenu); panelsMenu->setDelayed(false); const KActionCollection* ac = actionCollection(); panelsMenu->addAction(ac->action(QStringLiteral("show_places_panel"))); #ifdef HAVE_BALOO panelsMenu->addAction(ac->action(QStringLiteral("show_information_panel"))); #endif panelsMenu->addAction(ac->action(QStringLiteral("show_folders_panel"))); panelsMenu->addAction(ac->action(QStringLiteral("show_terminal_panel"))); panelsMenu->addSeparator(); panelsMenu->addAction(actionShowAllPlaces); panelsMenu->addAction(lockLayoutAction); connect(panelsMenu->menu(), &QMenu::aboutToShow, this, [actionShowAllPlaces, this]{ actionShowAllPlaces->setEnabled(m_placesPanel->hiddenListCount()); }); } void DolphinMainWindow::updateFileAndEditActions() { const KFileItemList list = m_activeViewContainer->view()->selectedItems(); const KActionCollection* col = actionCollection(); QAction* addToPlacesAction = col->action(QStringLiteral("add_to_places")); 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())); } else { stateChanged(QStringLiteral("has_selection")); QAction* renameAction = col->action(KStandardAction::name(KStandardAction::RenameFile)); QAction* moveToTrashAction = col->action(KStandardAction::name(KStandardAction::MoveToTrash)); QAction* deleteAction = col->action(KStandardAction::name(KStandardAction::DeleteFile)); QAction* cutAction = col->action(KStandardAction::name(KStandardAction::Cut)); QAction* deleteWithTrashShortcut = col->action(QStringLiteral("delete_shortcut")); // see DolphinViewActionHandler QAction* showTarget = col->action(QStringLiteral("show_target")); 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); const bool enableMoveToTrash = capabilities.isLocal() && capabilities.supportsMoving(); renameAction->setEnabled(capabilities.supportsMoving()); moveToTrashAction->setEnabled(enableMoveToTrash); deleteAction->setEnabled(capabilities.supportsDeleting()); deleteWithTrashShortcut->setEnabled(capabilities.supportsDeleting() && !enableMoveToTrash); cutAction->setEnabled(capabilities.supportsMoving()); showTarget->setEnabled(list.length() == 1 && list.at(0).isLink()); } } void DolphinMainWindow::updateViewActions() { m_actionHandler->updateViewActions(); QAction* showFilterBarAction = actionCollection()->action(QStringLiteral("show_filter_bar")); showFilterBarAction->setChecked(m_activeViewContainer->isFilterBarVisible()); updateSplitAction(); QAction* editableLocactionAction = actionCollection()->action(QStringLiteral("editable_location")); const KUrlNavigator* urlNavigator = m_activeViewContainer->urlNavigator(); editableLocactionAction->setChecked(urlNavigator->isUrlEditable()); } void DolphinMainWindow::updateGoActions() { QAction* goUpAction = actionCollection()->action(KStandardAction::name(KStandardAction::Up)); const QUrl currentUrl = m_activeViewContainer->url(); // I think this is one of the best places to firstly be confronted // with a file system and its hierarchy. Talking about the root // directory might seem too much here but it is the question that // naturally arises in this context. goUpAction->setWhatsThis(xi18nc("@info:whatsthis", "Go to " "the folder that contains the currently viewed one." "All files and folders are organized in a hierarchical " "file system. At the top of this hierarchy is " "a directory that contains all data connected to this computer" "—the root directory.")); goUpAction->setEnabled(KIO::upUrl(currentUrl) != currentUrl); } void DolphinMainWindow::createControlButton() { if (m_controlButton) { return; } Q_ASSERT(!m_controlButton); m_controlButton = new QToolButton(this); 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(); foreach (const QWidget* widget, action->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); updateSplitAction(); updateWindowTitle(); } emit settingsChanged(); } void DolphinMainWindow::clearStatusBar() { m_activeViewContainer->statusBar()->resetToDefaultText(); } void DolphinMainWindow::connectViewSignals(DolphinViewContainer* container) { connect(container, &DolphinViewContainer::showFilterBarChanged, this, &DolphinMainWindow::updateFilterBarAction); connect(container, &DolphinViewContainer::writeStateChanged, this, &DolphinMainWindow::slotWriteStateChanged); connect(container, &DolphinViewContainer::searchModeEnabledChanged, this, &DolphinMainWindow::updateSearchAction); const QAction* toggleSearchAction = actionCollection()->action(QStringLiteral("toggle_search")); connect(toggleSearchAction, &QAction::triggered, container, &DolphinViewContainer::setSearchModeEnabled); const DolphinView* view = container->view(); connect(view, &DolphinView::selectionChanged, this, &DolphinMainWindow::slotSelectionChanged); connect(view, &DolphinView::requestItemInfo, this, &DolphinMainWindow::requestItemInfo); connect(view, &DolphinView::tabRequested, this, &DolphinMainWindow::openNewTab); connect(view, &DolphinView::requestContextMenu, this, &DolphinMainWindow::openContextMenu); connect(view, &DolphinView::directoryLoadingStarted, this, &DolphinMainWindow::enableStopAction); connect(view, &DolphinView::directoryLoadingCompleted, this, &DolphinMainWindow::disableStopAction); connect(view, &DolphinView::directoryLoadingCompleted, this, &DolphinMainWindow::slotDirectoryLoadingCompleted); connect(view, &DolphinView::goBackRequested, this, &DolphinMainWindow::goBack); connect(view, &DolphinView::goForwardRequested, this, &DolphinMainWindow::goForward); connect(view, &DolphinView::urlActivated, this, &DolphinMainWindow::handleUrl); const KUrlNavigator* navigator = container->urlNavigator(); connect(navigator, &KUrlNavigator::urlChanged, this, &DolphinMainWindow::changeUrl); connect(navigator, &KUrlNavigator::historyChanged, this, &DolphinMainWindow::updateHistory); connect(navigator, &KUrlNavigator::editableStateChanged, this, &DolphinMainWindow::slotEditableStateChanged); connect(navigator, &KUrlNavigator::tabRequested, this, &DolphinMainWindow::openNewTabAfterLastTab); } void DolphinMainWindow::updateSplitAction() { QAction* splitAction = actionCollection()->action(QStringLiteral("split_view")); const DolphinTabPage* tabPage = m_tabWidget->currentTabPage(); if (tabPage->splitViewEnabled()) { if (GeneralSettings::closeActiveSplitView() ? tabPage->primaryViewActive() : !tabPage->primaryViewActive()) { splitAction->setText(i18nc("@action:intoolbar Close left view", "Close")); splitAction->setToolTip(i18nc("@info", "Close left view")); splitAction->setIcon(QIcon::fromTheme(QStringLiteral("view-left-close"))); } else { splitAction->setText(i18nc("@action:intoolbar Close right view", "Close")); splitAction->setToolTip(i18nc("@info", "Close right view")); splitAction->setIcon(QIcon::fromTheme(QStringLiteral("view-right-close"))); } } else { splitAction->setText(i18nc("@action:intoolbar Split view", "Split")); splitAction->setToolTip(i18nc("@info", "Split view")); splitAction->setIcon(QIcon::fromTheme(QStringLiteral("view-right-new"))); } } bool DolphinMainWindow::isKompareInstalled() const { static bool initialized = false; static bool installed = false; if (!initialized) { // TODO: maybe replace this approach later by using a menu // plugin like kdiff3plugin.cpp installed = !QStandardPaths::findExecutable(QStringLiteral("kompare")).isEmpty(); initialized = true; } return installed; } void DolphinMainWindow::createPanelAction(const QIcon& icon, const QKeySequence& shortcut, QAction* dockAction, const QString& actionName) { QAction* panelAction = actionCollection()->addAction(actionName); panelAction->setCheckable(true); panelAction->setChecked(dockAction->isChecked()); panelAction->setText(dockAction->text()); panelAction->setIcon(icon); actionCollection()->setDefaultShortcut(panelAction, shortcut); connect(panelAction, &QAction::triggered, dockAction, &QAction::trigger); connect(dockAction, &QAction::toggled, panelAction, &QAction::setChecked); } void DolphinMainWindow::setupWhatsThis() { // main widgets menuBar()->setWhatsThis(xi18nc("@info:whatsthis", "This is the " "Menubar. It provides access to commands and " "configuration options. Left-click on any of the menus on this " "bar to see its contents.The Menubar can be hidden " "by unchecking Settings|Show Menubar. Then " "most of its contents become available through a Control" " button on the Toolbar.")); toolBar()->setWhatsThis(xi18nc("@info:whatsthis", "This is the " "Toolbar. It allows quick access to " "frequently used actions.It is highly customizable. " "All items you see in the Control menu or " "in the Menubar can be placed on the " "Toolbar. Just right-click on it and select Configure " "Toolbars… or find this action in the " "Control or Settings menu." "The location of the bar and the style of its " "buttons can also be changed in the right-click menu. Right-click " "a button if you want to show or hide its text.")); m_tabWidget->setWhatsThis(xi18nc("@info:whatsthis main view", "Here you can see the folders and " "files that are at the location described in " "the Location Bar above. This area is the " "central part of this application where you navigate to the files " "you want to use.For an elaborate and general " "introduction to this application " "click here. This will open an introductory article from " "the KDE UserBase Wiki.For brief " "explanations of all the features of this view " "click here " "instead. This will open a page from the Handbook" " that covers the basics.")); // Settings menu actionCollection()->action(KStandardAction::name(KStandardAction::KeyBindings)) ->setWhatsThis(xi18nc("@info:whatsthis","This opens a window " "that lists the keyboard shortcuts." "There you can set up key combinations to trigger an action when " "they are pressed simultaneously. All commands in this application can " "be triggered this way.")); actionCollection()->action(KStandardAction::name(KStandardAction::ConfigureToolbars)) ->setWhatsThis(xi18nc("@info:whatsthis","This opens a window in which " "you can change which buttons appear on the Toolbar." "All items you see in the Control menu " "or in the Menubar can also be placed on the Toolbar.")); actionCollection()->action(KStandardAction::name(KStandardAction::Preferences)) ->setWhatsThis(xi18nc("@info:whatsthis","This opens a window where you can " "change a multitude of settings for this application. For an explanation " "of the various settings go to the chapter Configuring Dolphin" " in Help|Dolphin Handbook.")); // Help menu // The whatsthis has to be set for the m_helpMenu and for the // StandardAction separately because both are used in different locations. // m_helpMenu is only used for createControlButton() button. // 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 // probably state the external link language at least in brackets to not // frustrate the user. If there are multiple languages that the user might // know with a reasonable chance you might want to have 2 external links. // The same is in my opinion true for every external link you translate. 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 + 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 + xi18nc("@info:whatsthis second half of handbook text with link", "If you want more elaborate introductions to the " "different features of Dolphin " "click here. " "It will open the dedicated page in the KDE UserBase Wiki.")); const QString whatsThisWhatsThis = xi18nc("@info:whatsthis whatsthis button", "This is the button that invokes the help feature you are " "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 + 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" " menu and the KDE UserBase Wiki " "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 + xi18nc("@info:whatsthis second half of whatsthis button text with link", "There are two other ways to get help: " "The Dolphin Handbook and " "the KDE " "UserBase Wiki.The \"What's this?\" help is " "missing in most other windows so don't get too used to this.")); 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 + 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 " "" "click here.")); const QString whatsThisDonate = xi18nc("@info:whatsthis","This opens a " "web page where you can donate to " "support the continued work on this application and many " "other projects by the KDE community." "Donating is the easiest and fastest way to efficiently " "support KDE and its projects. KDE projects are available for " "free therefore your donation is needed to cover things that " "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); 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); 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); 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); } bool DolphinMainWindow::event(QEvent *event) { if (event->type() == QEvent::WhatsThisClicked) { event->accept(); QWhatsThisClickedEvent* whatsThisEvent = dynamic_cast(event); QDesktopServices::openUrl(QUrl(whatsThisEvent->href())); return true; } return KXmlGuiWindow::event(event); } bool DolphinMainWindow::eventFilter(QObject* obj, QEvent* event) { Q_UNUSED(obj) if (event->type() == QEvent::WhatsThisClicked) { event->accept(); QWhatsThisClickedEvent* whatsThisEvent = dynamic_cast(event); QDesktopServices::openUrl(QUrl(whatsThisEvent->href())); return true; } return false; } DolphinMainWindow::UndoUiInterface::UndoUiInterface() : KIO::FileUndoManager::UiInterface() { } DolphinMainWindow::UndoUiInterface::~UndoUiInterface() { } void DolphinMainWindow::UndoUiInterface::jobError(KIO::Job* job) { DolphinMainWindow* mainWin= qobject_cast(parentWidget()); if (mainWin) { DolphinViewContainer* container = mainWin->activeViewContainer(); container->showMessage(job->errorString(), DolphinViewContainer::Error); } else { KIO::FileUndoManager::UiInterface::jobError(job); } } bool DolphinMainWindow::isUrlOpen(const QString& url) { if (m_tabWidget->getIndexByUrl(QUrl::fromUserInput((url))).first >= 0) { return true; } else { return false; } } diff --git a/src/panels/places/placespanel.cpp b/src/panels/places/placespanel.cpp index a2f0e0655..3ee36dc08 100644 --- a/src/panels/places/placespanel.cpp +++ b/src/panels/places/placespanel.cpp @@ -1,562 +1,562 @@ /*************************************************************************** * Copyright (C) 2008-2012 by Peter Penz * * * * Based on KFilePlacesView from kdelibs: * * Copyright (C) 2007 Kevin Ottens * * Copyright (C) 2007 David Faure * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * ***************************************************************************/ #include "placespanel.h" #include "dolphin_generalsettings.h" #include "global.h" #include "kitemviews/kitemlistcontainer.h" #include "kitemviews/kitemlistcontroller.h" #include "kitemviews/kitemlistselectionmanager.h" #include "kitemviews/kstandarditem.h" #include "placesitem.h" #include "placesitemlistgroupheader.h" #include "placesitemlistwidget.h" #include "placesitemmodel.h" #include "placesview.h" #include "trash/dolphintrash.h" #include "views/draganddrophelper.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include PlacesPanel::PlacesPanel(QWidget* parent) : Panel(parent), m_controller(nullptr), m_model(nullptr), m_view(nullptr), m_storageSetupFailedUrl(), m_triggerStorageSetupButton(), m_itemDropEventIndex(-1), m_itemDropEventMimeData(nullptr), m_itemDropEvent(nullptr) { } PlacesPanel::~PlacesPanel() { } void PlacesPanel::proceedWithTearDown() { m_model->proceedWithTearDown(); } bool PlacesPanel::urlChanged() { if (!url().isValid() || url().scheme().contains(QStringLiteral("search"))) { // Skip results shown by a search, as possible identical // directory names are useless without parent-path information. return false; } if (m_controller) { selectClosestItem(); } return true; } void PlacesPanel::readSettings() { if (m_controller) { const int delay = GeneralSettings::autoExpandFolders() ? 750 : -1; m_controller->setAutoActivationDelay(delay); } } void PlacesPanel::showEvent(QShowEvent* event) { if (event->spontaneous()) { Panel::showEvent(event); return; } if (!m_controller) { // Postpone the creating of the controller to the first show event. // This assures that no performance and memory overhead is given when the folders panel is not // used at all and stays invisible. m_model = new PlacesItemModel(this); m_model->setGroupedSorting(true); connect(m_model, &PlacesItemModel::errorMessage, this, &PlacesPanel::errorMessage); connect(m_model, &PlacesItemModel::storageTearDownRequested, this, &PlacesPanel::storageTearDownRequested); connect(m_model, &PlacesItemModel::storageTearDownExternallyRequested, this, &PlacesPanel::storageTearDownExternallyRequested); m_view = new PlacesView(); m_view->setWidgetCreator(new KItemListWidgetCreator()); m_view->setGroupHeaderCreator(new KItemListGroupHeaderCreator()); m_controller = new KItemListController(m_model, m_view, this); m_controller->setSelectionBehavior(KItemListController::SingleSelection); m_controller->setSingleClickActivationEnforced(true); readSettings(); connect(m_controller, &KItemListController::itemActivated, this, &PlacesPanel::slotItemActivated); connect(m_controller, &KItemListController::itemMiddleClicked, this, &PlacesPanel::slotItemMiddleClicked); connect(m_controller, &KItemListController::itemContextMenuRequested, this, &PlacesPanel::slotItemContextMenuRequested); connect(m_controller, &KItemListController::viewContextMenuRequested, this, &PlacesPanel::slotViewContextMenuRequested); connect(m_controller, &KItemListController::itemDropEvent, this, &PlacesPanel::slotItemDropEvent); connect(m_controller, &KItemListController::aboveItemDropEvent, this, &PlacesPanel::slotAboveItemDropEvent); KItemListContainer* container = new KItemListContainer(m_controller, this); container->setEnabledFrame(false); QVBoxLayout* layout = new QVBoxLayout(this); layout->setContentsMargins(0, 0, 0, 0); layout->addWidget(container); selectClosestItem(); } Panel::showEvent(event); } void PlacesPanel::slotItemActivated(int index) { triggerItem(index, Qt::LeftButton); } void PlacesPanel::slotItemMiddleClicked(int index) { triggerItem(index, Qt::MiddleButton); } void PlacesPanel::slotItemContextMenuRequested(int index, const QPointF& pos) { PlacesItem* item = m_model->placesItem(index); if (!item) { return; } QMenu menu(this); QAction* emptyTrashAction = nullptr; QAction* editAction = nullptr; QAction* teardownAction = nullptr; QAction* ejectAction = nullptr; QAction* mountAction = nullptr; const bool isDevice = !item->udi().isEmpty(); const bool isTrash = (item->url().scheme() == QLatin1String("trash")); if (isTrash) { emptyTrashAction = menu.addAction(QIcon::fromTheme(QStringLiteral("trash-empty")), i18nc("@action:inmenu", "Empty Trash")); emptyTrashAction->setEnabled(item->icon() == QLatin1String("user-trash-full")); menu.addSeparator(); } QAction* openInNewWindowAction = menu.addAction(QIcon::fromTheme(QStringLiteral("window-new")), i18nc("@item:inmenu", "Open in New Window")); QAction* openInNewTabAction = menu.addAction(QIcon::fromTheme(QStringLiteral("tab-new")), i18nc("@item:inmenu", "Open in New Tab")); QAction* propertiesAction = nullptr; if (item->url().isLocalFile()) { propertiesAction = menu.addAction(QIcon::fromTheme(QStringLiteral("document-properties")), i18nc("@action:inmenu", "Properties")); } if (!isDevice && !isTrash) { menu.addSeparator(); } if (isDevice) { ejectAction = m_model->ejectAction(index); if (ejectAction) { ejectAction->setParent(&menu); menu.addAction(ejectAction); } teardownAction = m_model->teardownAction(index); if (teardownAction) { // Disable teardown option for root and home partitions bool teardownEnabled = item->url() != QUrl::fromLocalFile(QDir::rootPath()); if (teardownEnabled) { KMountPoint::Ptr mountPoint = KMountPoint::currentMountPoints().findByPath(QDir::homePath()); if (mountPoint && item->url() == QUrl::fromLocalFile(mountPoint->mountPoint())) { teardownEnabled = false; } } teardownAction->setEnabled(teardownEnabled); teardownAction->setParent(&menu); menu.addAction(teardownAction); } if (item->storageSetupNeeded()) { mountAction = menu.addAction(QIcon::fromTheme(QStringLiteral("media-mount")), i18nc("@action:inmenu", "Mount")); } if (teardownAction || ejectAction || mountAction) { menu.addSeparator(); } } if (!isDevice) { editAction = menu.addAction(QIcon::fromTheme(QStringLiteral("edit-entry")), i18nc("@item:inmenu", "Edit...")); } QAction* removeAction = nullptr; if (!isDevice && !item->isSystemItem()) { removeAction = menu.addAction(QIcon::fromTheme(QStringLiteral("edit-delete")), i18nc("@item:inmenu", "Remove")); } - QAction* hideAction = menu.addAction(QIcon::fromTheme(QStringLiteral("hint")), i18nc("@item:inmenu", "Hide")); + QAction* hideAction = menu.addAction(QIcon::fromTheme(QStringLiteral("view-hidden")), i18nc("@item:inmenu", "Hide")); hideAction->setCheckable(true); hideAction->setChecked(item->isHidden()); buildGroupContextMenu(&menu, index); QAction* action = menu.exec(pos.toPoint()); if (action) { if (action == emptyTrashAction) { Trash::empty(this); } else { // The index might have changed if devices were added/removed while // the context menu was open. index = m_model->index(item); if (index < 0) { // The item is not in the model any more, probably because it was an // external device that has been removed while the context menu was open. return; } if (action == editAction) { editEntry(index); } else if (action == removeAction) { m_model->deleteItem(index); } else if (action == hideAction) { item->setHidden(hideAction->isChecked()); if (!m_model->hiddenCount()) { showHiddenEntries(false); } } else if (action == openInNewWindowAction) { Dolphin::openNewWindow({KFilePlacesModel::convertedUrl(m_model->data(index).value("url").toUrl())}, this); } else if (action == openInNewTabAction) { // TriggerItem does set up the storage first and then it will // emit the slotItemMiddleClicked signal, because of Qt::MiddleButton. triggerItem(index, Qt::MiddleButton); } else if (action == mountAction) { m_model->requestStorageSetup(index); } else if (action == teardownAction) { m_model->requestTearDown(index); } else if (action == ejectAction) { m_model->requestEject(index); } else if (action == propertiesAction) { KPropertiesDialog* dialog = new KPropertiesDialog(item->url(), this); dialog->setAttribute(Qt::WA_DeleteOnClose); dialog->show(); } } } selectClosestItem(); } void PlacesPanel::slotViewContextMenuRequested(const QPointF& pos) { QMenu menu(this); QAction* addAction = menu.addAction(QIcon::fromTheme(QStringLiteral("document-new")), i18nc("@item:inmenu", "Add Entry...")); QAction* showAllAction = menu.addAction(i18nc("@item:inmenu", "Show Hidden Places")); showAllAction->setCheckable(true); showAllAction->setChecked(m_model->hiddenItemsShown()); - showAllAction->setIcon(QIcon::fromTheme(m_model->hiddenItemsShown() ? QStringLiteral("visibility") : QStringLiteral("hint"))); + showAllAction->setIcon(QIcon::fromTheme(m_model->hiddenItemsShown() ? QStringLiteral("view-visible") : QStringLiteral("view-hidden"))); showAllAction->setEnabled(m_model->hiddenCount()); buildGroupContextMenu(&menu, m_controller->indexCloseToMousePressedPosition()); QMenu* iconSizeSubMenu = new QMenu(i18nc("@item:inmenu", "Icon Size"), &menu); struct IconSizeInfo { int size; const char* context; const char* text; }; const int iconSizeCount = 4; static const IconSizeInfo iconSizes[iconSizeCount] = { {KIconLoader::SizeSmall, I18N_NOOP2_NOSTRIP("Small icon size", "Small (%1x%2)")}, {KIconLoader::SizeSmallMedium, I18N_NOOP2_NOSTRIP("Medium icon size", "Medium (%1x%2)")}, {KIconLoader::SizeMedium, I18N_NOOP2_NOSTRIP("Large icon size", "Large (%1x%2)")}, {KIconLoader::SizeLarge, I18N_NOOP2_NOSTRIP("Huge icon size", "Huge (%1x%2)")} }; QHash iconSizeActionMap; QActionGroup* iconSizeGroup = new QActionGroup(iconSizeSubMenu); for (int i = 0; i < iconSizeCount; ++i) { const int size = iconSizes[i].size; const QString text = i18nc(iconSizes[i].context, iconSizes[i].text, size, size); QAction* action = iconSizeSubMenu->addAction(text); iconSizeActionMap.insert(action, size); action->setActionGroup(iconSizeGroup); action->setCheckable(true); action->setChecked(m_view->iconSize() == size); } menu.addMenu(iconSizeSubMenu); menu.addSeparator(); foreach (QAction* action, customContextMenuActions()) { menu.addAction(action); } QAction* action = menu.exec(pos.toPoint()); if (action) { if (action == addAction) { addEntry(); } else if (action == showAllAction) { showHiddenEntries(showAllAction->isChecked()); } else if (iconSizeActionMap.contains(action)) { m_view->setIconSize(iconSizeActionMap.value(action)); } } selectClosestItem(); } QAction *PlacesPanel::buildGroupContextMenu(QMenu *menu, int index) { if (index == -1) { return nullptr; } KFilePlacesModel::GroupType groupType = m_model->groupType(index); - QAction *hideGroupAction = menu->addAction(QIcon::fromTheme(QStringLiteral("hint")), i18nc("@item:inmenu", "Hide Section '%1'", m_model->item(index)->group())); + QAction *hideGroupAction = menu->addAction(QIcon::fromTheme(QStringLiteral("view-hidden")), i18nc("@item:inmenu", "Hide Section '%1'", m_model->item(index)->group())); hideGroupAction->setCheckable(true); hideGroupAction->setChecked(m_model->isGroupHidden(groupType)); connect(hideGroupAction, &QAction::triggered, this, [this, groupType, hideGroupAction]{ m_model->setGroupHidden(groupType, hideGroupAction->isChecked()); if (!m_model->hiddenCount()) { showHiddenEntries(false); } }); return hideGroupAction; } void PlacesPanel::slotItemDropEvent(int index, QGraphicsSceneDragDropEvent* event) { if (index < 0) { return; } const PlacesItem* destItem = m_model->placesItem(index); if (destItem->isSearchOrTimelineUrl()) { return; } if (m_model->storageSetupNeeded(index)) { connect(m_model, &PlacesItemModel::storageSetupDone, this, &PlacesPanel::slotItemDropEventStorageSetupDone); m_itemDropEventIndex = index; // Make a full copy of the Mime-Data m_itemDropEventMimeData = new QMimeData; m_itemDropEventMimeData->setText(event->mimeData()->text()); m_itemDropEventMimeData->setHtml(event->mimeData()->html()); m_itemDropEventMimeData->setUrls(event->mimeData()->urls()); m_itemDropEventMimeData->setImageData(event->mimeData()->imageData()); m_itemDropEventMimeData->setColorData(event->mimeData()->colorData()); m_itemDropEvent = new QDropEvent(event->pos().toPoint(), event->possibleActions(), m_itemDropEventMimeData, event->buttons(), event->modifiers()); m_model->requestStorageSetup(index); return; } QUrl destUrl = destItem->url(); QDropEvent dropEvent(event->pos().toPoint(), event->possibleActions(), event->mimeData(), event->buttons(), event->modifiers()); slotUrlsDropped(destUrl, &dropEvent, this); } void PlacesPanel::slotItemDropEventStorageSetupDone(int index, bool success) { disconnect(m_model, &PlacesItemModel::storageSetupDone, this, &PlacesPanel::slotItemDropEventStorageSetupDone); if ((index == m_itemDropEventIndex) && m_itemDropEvent && m_itemDropEventMimeData) { if (success) { QUrl destUrl = m_model->placesItem(index)->url(); slotUrlsDropped(destUrl, m_itemDropEvent, this); } delete m_itemDropEventMimeData; delete m_itemDropEvent; m_itemDropEventIndex = -1; m_itemDropEventMimeData = nullptr; m_itemDropEvent = nullptr; } } void PlacesPanel::slotAboveItemDropEvent(int index, QGraphicsSceneDragDropEvent* event) { m_model->dropMimeDataBefore(index, event->mimeData()); } void PlacesPanel::slotUrlsDropped(const QUrl& dest, QDropEvent* event, QWidget* parent) { KIO::DropJob *job = DragAndDropHelper::dropUrls(dest, event, parent); if (job) { connect(job, &KIO::DropJob::result, this, [this](KJob *job) { if (job->error()) emit errorMessage(job->errorString()); }); } } void PlacesPanel::slotStorageSetupDone(int index, bool success) { disconnect(m_model, &PlacesItemModel::storageSetupDone, this, &PlacesPanel::slotStorageSetupDone); if (m_triggerStorageSetupButton == Qt::NoButton) { return; } if (success) { Q_ASSERT(!m_model->storageSetupNeeded(index)); triggerItem(index, m_triggerStorageSetupButton); m_triggerStorageSetupButton = Qt::NoButton; } else { setUrl(m_storageSetupFailedUrl); m_storageSetupFailedUrl = QUrl(); } } void PlacesPanel::addEntry() { const int index = m_controller->selectionManager()->currentItem(); const QUrl url = m_model->data(index).value("url").toUrl(); const QString text = url.fileName().isEmpty() ? url.toDisplayString(QUrl::PreferLocalFile) : url.fileName(); QPointer dialog = new KFilePlaceEditDialog(true, url, text, QString(), true, false, KIconLoader::SizeMedium, this); if (dialog->exec() == QDialog::Accepted) { const QString appName = dialog->applicationLocal() ? QCoreApplication::applicationName() : QString(); m_model->createPlacesItem(dialog->label(), dialog->url(), dialog->icon(), appName); } delete dialog; } void PlacesPanel::editEntry(int index) { QHash data = m_model->data(index); const QUrl url = data.value("url").toUrl(); const QString text = data.value("text").toString(); const QString iconName = data.value("iconName").toString(); const bool applicationLocal = !data.value("applicationName").toString().isEmpty(); QPointer dialog = new KFilePlaceEditDialog(true, url, text, iconName, true, applicationLocal, KIconLoader::SizeMedium, this); if (dialog->exec() == QDialog::Accepted) { PlacesItem* oldItem = m_model->placesItem(index); if (oldItem) { const QString appName = dialog->applicationLocal() ? QCoreApplication::applicationName() : QString(); oldItem->setApplicationName(appName); oldItem->setText(dialog->label()); oldItem->setUrl(dialog->url()); oldItem->setIcon(dialog->icon()); m_model->refresh(); } } delete dialog; } void PlacesPanel::selectClosestItem() { const int index = m_model->closestItem(url()); KItemListSelectionManager* selectionManager = m_controller->selectionManager(); selectionManager->setCurrentItem(index); selectionManager->clearSelection(); selectionManager->setSelected(index); } void PlacesPanel::triggerItem(int index, Qt::MouseButton button) { const PlacesItem* item = m_model->placesItem(index); if (!item) { return; } if (m_model->storageSetupNeeded(index)) { m_triggerStorageSetupButton = button; m_storageSetupFailedUrl = url(); connect(m_model, &PlacesItemModel::storageSetupDone, this, &PlacesPanel::slotStorageSetupDone); m_model->requestStorageSetup(index); } else { m_triggerStorageSetupButton = Qt::NoButton; const QUrl url = m_model->data(index).value("url").toUrl(); if (!url.isEmpty()) { if (button == Qt::MiddleButton) { emit placeMiddleClicked(KFilePlacesModel::convertedUrl(url)); } else { emit placeActivated(KFilePlacesModel::convertedUrl(url)); } } } } void PlacesPanel::showHiddenEntries(bool shown) { m_model->setHiddenItemsShown(shown); emit showHiddenEntriesChanged(shown); } int PlacesPanel::hiddenListCount() { if(!m_model) { return 0; } return m_model->hiddenCount(); } diff --git a/src/views/dolphinviewactionhandler.cpp b/src/views/dolphinviewactionhandler.cpp index 5746bb7cf..7086ab627 100644 --- a/src/views/dolphinviewactionhandler.cpp +++ b/src/views/dolphinviewactionhandler.cpp @@ -1,682 +1,682 @@ /*************************************************************************** * Copyright (C) 2008 by David Faure * * Copyright (C) 2012 by Peter Penz * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * ***************************************************************************/ #include "dolphinviewactionhandler.h" #include "dolphindebug.h" #include "kitemviews/kfileitemmodel.h" #include "settings/viewpropertiesdialog.h" #include "views/zoomlevelinfo.h" #ifdef HAVE_BALOO #include #endif #include #include #include #include #include #include #include #include DolphinViewActionHandler::DolphinViewActionHandler(KActionCollection* collection, QObject* parent) : QObject(parent), m_actionCollection(collection), m_currentView(nullptr), m_sortByActions(), m_visibleRoles() { Q_ASSERT(m_actionCollection); createActions(); } void DolphinViewActionHandler::setCurrentView(DolphinView* view) { Q_ASSERT(view); if (m_currentView) { disconnect(m_currentView, nullptr, this, nullptr); } m_currentView = view; connect(view, &DolphinView::modeChanged, this, &DolphinViewActionHandler::updateViewActions); connect(view, &DolphinView::previewsShownChanged, this, &DolphinViewActionHandler::slotPreviewsShownChanged); connect(view, &DolphinView::sortOrderChanged, this, &DolphinViewActionHandler::slotSortOrderChanged); connect(view, &DolphinView::sortFoldersFirstChanged, this, &DolphinViewActionHandler::slotSortFoldersFirstChanged); connect(view, &DolphinView::visibleRolesChanged, this, &DolphinViewActionHandler::slotVisibleRolesChanged); connect(view, &DolphinView::groupedSortingChanged, this, &DolphinViewActionHandler::slotGroupedSortingChanged); connect(view, &DolphinView::hiddenFilesShownChanged, this, &DolphinViewActionHandler::slotHiddenFilesShownChanged); connect(view, &DolphinView::sortRoleChanged, this, &DolphinViewActionHandler::slotSortRoleChanged); connect(view, &DolphinView::zoomLevelChanged, this, &DolphinViewActionHandler::slotZoomLevelChanged); connect(view, &DolphinView::writeStateChanged, this, &DolphinViewActionHandler::slotWriteStateChanged); } DolphinView* DolphinViewActionHandler::currentView() { return m_currentView; } void DolphinViewActionHandler::createActions() { // This action doesn't appear in the GUI, it's for the shortcut only. // KNewFileMenu takes care of the GUI stuff. QAction* newDirAction = m_actionCollection->addAction(QStringLiteral("create_dir")); newDirAction->setText(i18nc("@action", "Create Folder...")); m_actionCollection->setDefaultShortcut(newDirAction, Qt::Key_F10); newDirAction->setIcon(QIcon::fromTheme(QStringLiteral("folder-new"))); newDirAction->setEnabled(false); // Will be enabled in slotWriteStateChanged(bool) if the current URL is writable connect(newDirAction, &QAction::triggered, this, &DolphinViewActionHandler::createDirectoryTriggered); // File menu auto renameAction = KStandardAction::renameFile(this, &DolphinViewActionHandler::slotRename, m_actionCollection); renameAction->setWhatsThis(xi18nc("@info:whatsthis", "This renames the " "items in your current selection.Renaming multiple items " "at once amounts to their new names differing only in a number.")); auto trashAction = KStandardAction::moveToTrash(this, &DolphinViewActionHandler::slotTrashActivated, m_actionCollection); auto trashShortcuts = trashAction->shortcuts(); if (!trashShortcuts.contains(QKeySequence::Delete)) { trashShortcuts.append(QKeySequence::Delete); m_actionCollection->setDefaultShortcuts(trashAction, trashShortcuts); } trashAction->setWhatsThis(xi18nc("@info:whatsthis", "This moves the " "items in your current selection to the Trash" ".The trash is a temporary storage where " "items can be deleted from if disk space is needed.")); auto deleteAction = KStandardAction::deleteFile(this, &DolphinViewActionHandler::slotDeleteItems, m_actionCollection); auto deleteShortcuts = deleteAction->shortcuts(); if (!deleteShortcuts.contains(Qt::SHIFT | Qt::Key_Delete)) { deleteShortcuts.append(Qt::SHIFT | Qt::Key_Delete); m_actionCollection->setDefaultShortcuts(deleteAction, deleteShortcuts); } deleteAction->setWhatsThis(xi18nc("@info:whatsthis", "This deletes " "the items in your current selection completely. They can " "not be recovered by normal means.")); // This action is useful for being enabled when KStandardAction::MoveToTrash should be // disabled and KStandardAction::DeleteFile is enabled (e.g. non-local files), so that Key_Del // can be used for deleting the file (#76016). It needs to be a separate action // so that the Edit menu isn't affected. QAction* deleteWithTrashShortcut = m_actionCollection->addAction(QStringLiteral("delete_shortcut")); // The descriptive text is just for the shortcuts editor. deleteWithTrashShortcut->setText(i18nc("@action \"Move to Trash\" for non-local files, etc.", "Delete (using shortcut for Trash)")); m_actionCollection->setDefaultShortcuts(deleteWithTrashShortcut, KStandardShortcut::moveToTrash()); deleteWithTrashShortcut->setEnabled(false); connect(deleteWithTrashShortcut, &QAction::triggered, this, &DolphinViewActionHandler::slotDeleteItems); QAction *propertiesAction = m_actionCollection->addAction( QStringLiteral("properties") ); // Well, it's the File menu in dolphinmainwindow and the Edit menu in dolphinpart... :) propertiesAction->setText( i18nc("@action:inmenu File", "Properties") ); propertiesAction->setWhatsThis(xi18nc("@info:whatsthis properties", "This shows a complete list of properties of the currently " "selected items in a new window.If nothing is selected the " "window will be about the currently viewed folder instead." "You can configure advanced options there like managing " "read- and write-permissions.")); propertiesAction->setIcon(QIcon::fromTheme(QStringLiteral("document-properties"))); m_actionCollection->setDefaultShortcuts(propertiesAction, {Qt::ALT + Qt::Key_Return, Qt::ALT + Qt::Key_Enter}); connect(propertiesAction, &QAction::triggered, this, &DolphinViewActionHandler::slotProperties); // View menu KToggleAction* iconsAction = iconsModeAction(); KToggleAction* compactAction = compactModeAction(); KToggleAction* detailsAction = detailsModeAction(); iconsAction->setWhatsThis(xi18nc("@info:whatsthis Icons view mode", "This switches to a view mode that focuses on the folder " "and file icons. This mode makes it easy to distinguish folders " "from files and to detect items with distinctive " "file types. This mode is handy to " "browse through pictures when the Preview" " option is enabled.")); compactAction->setWhatsThis(xi18nc("@info:whatsthis Compact view mode", "This switches to a compact view mode that lists the folders " "and files in columns with the names beside the icons." "This helps to keep the overview in folders with many items.")); detailsAction->setWhatsThis(xi18nc("@info:whatsthis Details view mode", "This switches to a list view mode that focuses on folder " "and file details.Click on a detail in the column " "header to sort the items by it. Click again to sort the other " "way around. To select which details should be displayed click " "the header with the right mouse button.You can " "view the contents of a folder without leaving the current " "location by clicking to the left of it. This way you can view " "the contents of multiple folders in the same list.")); KSelectAction* viewModeActions = m_actionCollection->add(QStringLiteral("view_mode")); viewModeActions->setText(i18nc("@action:intoolbar", "View Mode")); viewModeActions->addAction(iconsAction); viewModeActions->addAction(compactAction); viewModeActions->addAction(detailsAction); viewModeActions->setToolBarMode(KSelectAction::MenuMode); connect(viewModeActions, QOverload::of(&KSelectAction::triggered), this, &DolphinViewActionHandler::slotViewModeActionTriggered); QAction* zoomInAction = KStandardAction::zoomIn(this, &DolphinViewActionHandler::zoomIn, m_actionCollection); zoomInAction->setWhatsThis(i18nc("@info:whatsthis zoom in", "This increases the icon size.")); QAction* zoomOutAction = KStandardAction::zoomOut(this, &DolphinViewActionHandler::zoomOut, m_actionCollection); zoomOutAction->setWhatsThis(i18nc("@info:whatsthis zoom out", "This reduces the icon size.")); KToggleAction* showPreview = m_actionCollection->add(QStringLiteral("show_preview")); showPreview->setText(i18nc("@action:intoolbar", "Preview")); showPreview->setToolTip(i18nc("@info", "Show preview of files and folders")); showPreview->setWhatsThis(xi18nc("@info:whatsthis", "When this is " "enabled, the icons are based on the actual file or folder " "contents.For example the icons of images become scaled " "down versions of the images.")); showPreview->setIcon(QIcon::fromTheme(QStringLiteral("view-preview"))); connect(showPreview, &KToggleAction::triggered, this, &DolphinViewActionHandler::togglePreview); KToggleAction* sortFoldersFirst = m_actionCollection->add(QStringLiteral("folders_first")); sortFoldersFirst->setText(i18nc("@action:inmenu Sort", "Folders First")); connect(sortFoldersFirst, &KToggleAction::triggered, this, &DolphinViewActionHandler::toggleSortFoldersFirst); // View -> Sort By QActionGroup* sortByActionGroup = createFileItemRolesActionGroup(QStringLiteral("sort_by_")); KActionMenu* sortByActionMenu = m_actionCollection->add(QStringLiteral("sort")); sortByActionMenu->setIcon(QIcon::fromTheme(QStringLiteral("view-sort"))); sortByActionMenu->setText(i18nc("@action:inmenu View", "Sort By")); sortByActionMenu->setDelayed(false); foreach (QAction* action, sortByActionGroup->actions()) { sortByActionMenu->addAction(action); } sortByActionMenu->addSeparator(); QActionGroup* group = new QActionGroup(sortByActionMenu); group->setExclusive(true); KToggleAction* ascendingAction = m_actionCollection->add(QStringLiteral("ascending")); ascendingAction->setActionGroup(group); connect(ascendingAction, &QAction::triggered, this, [this] { m_currentView->setSortOrder(Qt::AscendingOrder); }); KToggleAction* descendingAction = m_actionCollection->add(QStringLiteral("descending")); descendingAction->setActionGroup(group); connect(descendingAction, &QAction::triggered, this, [this] { m_currentView->setSortOrder(Qt::DescendingOrder); }); sortByActionMenu->addAction(ascendingAction); sortByActionMenu->addAction(descendingAction); sortByActionMenu->addSeparator(); sortByActionMenu->addAction(sortFoldersFirst); // View -> Additional Information QActionGroup* visibleRolesGroup = createFileItemRolesActionGroup(QStringLiteral("show_")); KActionMenu* visibleRolesMenu = m_actionCollection->add(QStringLiteral("additional_info")); visibleRolesMenu->setText(i18nc("@action:inmenu View", "Additional Information")); visibleRolesMenu->setDelayed(false); foreach (QAction* action, visibleRolesGroup->actions()) { visibleRolesMenu->addAction(action); } KToggleAction* showInGroups = m_actionCollection->add(QStringLiteral("show_in_groups")); showInGroups->setIcon(QIcon::fromTheme(QStringLiteral("view-group"))); showInGroups->setText(i18nc("@action:inmenu View", "Show in Groups")); showInGroups->setWhatsThis(i18nc("@info:whatsthis", "This groups files and folders by their first letter.")); connect(showInGroups, &KToggleAction::triggered, this, &DolphinViewActionHandler::toggleGroupedSorting); KToggleAction* showHiddenFiles = m_actionCollection->add(QStringLiteral("show_hidden_files")); showHiddenFiles->setText(i18nc("@action:inmenu View", "Hidden Files")); showHiddenFiles->setToolTip(i18nc("@info", "Visibility of hidden files and folders")); showHiddenFiles->setWhatsThis(xi18nc("@info:whatsthis", "When " "this is enabled hidden files and folders " "are visible. They will be displayed semi-transparent." "Hidden items only differ from other ones in that their " "name starts with a \".\". In general there is no need for " "users to access them which is why they are hidden.")); m_actionCollection->setDefaultShortcuts(showHiddenFiles, {Qt::ALT + Qt::Key_Period, Qt::CTRL + Qt::Key_H, Qt::Key_F8}); connect(showHiddenFiles, &KToggleAction::triggered, this, &DolphinViewActionHandler::toggleShowHiddenFiles); QAction* adjustViewProps = m_actionCollection->addAction(QStringLiteral("view_properties")); adjustViewProps->setText(i18nc("@action:inmenu View", "Adjust View Properties...")); adjustViewProps->setWhatsThis(i18nc("@info:whatsthis", "This opens a window " "in which all folder view properties can be adjusted.")); connect(adjustViewProps, &QAction::triggered, this, &DolphinViewActionHandler::slotAdjustViewProperties); } QActionGroup* DolphinViewActionHandler::createFileItemRolesActionGroup(const QString& groupPrefix) { const bool isSortGroup = (groupPrefix == QLatin1String("sort_by_")); Q_ASSERT(isSortGroup || groupPrefix == QLatin1String("show_")); QActionGroup* rolesActionGroup = new QActionGroup(m_actionCollection); rolesActionGroup->setExclusive(isSortGroup); if (isSortGroup) { connect(rolesActionGroup, &QActionGroup::triggered, this, &DolphinViewActionHandler::slotSortTriggered); } else { connect(rolesActionGroup, &QActionGroup::triggered, this, &DolphinViewActionHandler::toggleVisibleRole); } QString groupName; KActionMenu* groupMenu = nullptr; QActionGroup* groupMenuGroup = nullptr; bool indexingEnabled = false; #ifdef HAVE_BALOO Baloo::IndexerConfig config; indexingEnabled = config.fileIndexingEnabled(); #endif const QList rolesInfo = KFileItemModel::rolesInformation(); foreach (const KFileItemModel::RoleInfo& info, rolesInfo) { if (!isSortGroup && info.role == "text") { // It should not be possible to hide the "text" role continue; } KToggleAction* action = nullptr; const QString name = groupPrefix + info.role; if (info.group.isEmpty()) { action = m_actionCollection->add(name); action->setActionGroup(rolesActionGroup); } else { if (!groupMenu || info.group != groupName) { groupName = info.group; groupMenu = m_actionCollection->add(groupName); groupMenu->setText(groupName); groupMenu->setActionGroup(rolesActionGroup); groupMenuGroup = new QActionGroup(groupMenu); groupMenuGroup->setExclusive(isSortGroup); if (isSortGroup) { connect(groupMenuGroup, &QActionGroup::triggered, this, &DolphinViewActionHandler::slotSortTriggered); } else { connect(groupMenuGroup, &QActionGroup::triggered, this, &DolphinViewActionHandler::toggleVisibleRole); } } action = new KToggleAction(groupMenu); action->setActionGroup(groupMenuGroup); groupMenu->addAction(action); } action->setText(info.translation); action->setData(info.role); const bool enable = (!info.requiresBaloo && !info.requiresIndexer) || (info.requiresBaloo) || (info.requiresIndexer && indexingEnabled); action->setEnabled(enable); if (isSortGroup) { m_sortByActions.insert(info.role, action); } else { m_visibleRoles.insert(info.role, action); } } return rolesActionGroup; } void DolphinViewActionHandler::slotViewModeActionTriggered(QAction* action) { const DolphinView::Mode mode = action->data().value(); m_currentView->setMode(mode); QAction* viewModeMenu = m_actionCollection->action(QStringLiteral("view_mode")); viewModeMenu->setIcon(action->icon()); } void DolphinViewActionHandler::slotRename() { emit actionBeingHandled(); m_currentView->renameSelectedItems(); } void DolphinViewActionHandler::slotTrashActivated() { emit actionBeingHandled(); m_currentView->trashSelectedItems(); } void DolphinViewActionHandler::slotDeleteItems() { emit actionBeingHandled(); m_currentView->deleteSelectedItems(); } void DolphinViewActionHandler::togglePreview(bool show) { emit actionBeingHandled(); m_currentView->setPreviewsShown(show); } void DolphinViewActionHandler::slotPreviewsShownChanged(bool shown) { Q_UNUSED(shown); // It is not enough to update the 'Show Preview' action, also // the 'Zoom In' and 'Zoom Out' actions must be adapted. updateViewActions(); } QString DolphinViewActionHandler::currentViewModeActionName() const { switch (m_currentView->mode()) { case DolphinView::IconsView: return QStringLiteral("icons"); case DolphinView::DetailsView: return QStringLiteral("details"); case DolphinView::CompactView: return QStringLiteral("compact"); default: Q_ASSERT(false); break; } return QString(); // can't happen } KActionCollection* DolphinViewActionHandler::actionCollection() { return m_actionCollection; } void DolphinViewActionHandler::updateViewActions() { QAction* viewModeAction = m_actionCollection->action(currentViewModeActionName()); if (viewModeAction) { viewModeAction->setChecked(true); QAction* viewModeMenu = m_actionCollection->action(QStringLiteral("view_mode")); viewModeMenu->setIcon(viewModeAction->icon()); } QAction* showPreviewAction = m_actionCollection->action(QStringLiteral("show_preview")); showPreviewAction->setChecked(m_currentView->previewsShown()); slotSortOrderChanged(m_currentView->sortOrder()); slotSortFoldersFirstChanged(m_currentView->sortFoldersFirst()); slotVisibleRolesChanged(m_currentView->visibleRoles(), QList()); slotGroupedSortingChanged(m_currentView->groupedSorting()); slotSortRoleChanged(m_currentView->sortRole()); slotZoomLevelChanged(m_currentView->zoomLevel(), -1); // Updates the "show_hidden_files" action state and icon slotHiddenFilesShownChanged(m_currentView->hiddenFilesShown()); } void DolphinViewActionHandler::zoomIn() { const int level = m_currentView->zoomLevel(); m_currentView->setZoomLevel(level + 1); updateViewActions(); } void DolphinViewActionHandler::zoomOut() { const int level = m_currentView->zoomLevel(); m_currentView->setZoomLevel(level - 1); updateViewActions(); } void DolphinViewActionHandler::toggleSortFoldersFirst() { const bool sortFirst = m_currentView->sortFoldersFirst(); m_currentView->setSortFoldersFirst(!sortFirst); } void DolphinViewActionHandler::slotSortOrderChanged(Qt::SortOrder order) { QAction* descending = m_actionCollection->action(QStringLiteral("descending")); QAction* ascending = m_actionCollection->action(QStringLiteral("ascending")); const bool sortDescending = (order == Qt::DescendingOrder); descending->setChecked(sortDescending); ascending->setChecked(!sortDescending); } void DolphinViewActionHandler::slotSortFoldersFirstChanged(bool foldersFirst) { m_actionCollection->action(QStringLiteral("folders_first"))->setChecked(foldersFirst); } void DolphinViewActionHandler::toggleVisibleRole(QAction* action) { emit actionBeingHandled(); const QByteArray toggledRole = action->data().toByteArray(); QList roles = m_currentView->visibleRoles(); const bool show = action->isChecked(); const int index = roles.indexOf(toggledRole); const bool containsInfo = (index >= 0); if (show && !containsInfo) { roles.append(toggledRole); m_currentView->setVisibleRoles(roles); } else if (!show && containsInfo) { roles.removeAt(index); m_currentView->setVisibleRoles(roles); Q_ASSERT(roles.indexOf(toggledRole) < 0); } } void DolphinViewActionHandler::slotVisibleRolesChanged(const QList& current, const QList& previous) { Q_UNUSED(previous); const QSet checkedRoles = current.toSet(); QHashIterator it(m_visibleRoles); while (it.hasNext()) { it.next(); const QByteArray& role = it.key(); KToggleAction* action = it.value(); action->setChecked(checkedRoles.contains(role)); } } void DolphinViewActionHandler::toggleGroupedSorting(bool grouped) { m_currentView->setGroupedSorting(grouped); } void DolphinViewActionHandler::slotGroupedSortingChanged(bool groupedSorting) { QAction* showInGroupsAction = m_actionCollection->action(QStringLiteral("show_in_groups")); showInGroupsAction->setChecked(groupedSorting); } void DolphinViewActionHandler::toggleShowHiddenFiles(bool show) { emit actionBeingHandled(); m_currentView->setHiddenFilesShown(show); } void DolphinViewActionHandler::slotHiddenFilesShownChanged(bool shown) { QAction* showHiddenFilesAction = m_actionCollection->action(QStringLiteral("show_hidden_files")); showHiddenFilesAction->setChecked(shown); // #374508: don't overwrite custom icons. const QString iconName = showHiddenFilesAction->icon().name(); - if (!iconName.isEmpty() && iconName != QLatin1String("visibility") && iconName != QLatin1String("hint")) { + if (!iconName.isEmpty() && iconName != QLatin1String("view-visible") && iconName != QLatin1String("view-hidden")) { return; } - showHiddenFilesAction->setIcon(QIcon::fromTheme(shown ? QStringLiteral("visibility") : QStringLiteral("hint"))); + showHiddenFilesAction->setIcon(QIcon::fromTheme(shown ? QStringLiteral("view-visible") : QStringLiteral("view-hidden"))); } void DolphinViewActionHandler::slotWriteStateChanged(bool isFolderWritable) { m_actionCollection->action(QStringLiteral("create_dir"))->setEnabled(isFolderWritable && KProtocolManager::supportsMakeDir(currentView()->url())); } KToggleAction* DolphinViewActionHandler::iconsModeAction() { KToggleAction* iconsView = m_actionCollection->add(QStringLiteral("icons")); iconsView->setText(i18nc("@action:inmenu View Mode", "Icons")); iconsView->setToolTip(i18nc("@info", "Icons view mode")); m_actionCollection->setDefaultShortcut(iconsView, Qt::CTRL + Qt::Key_1); iconsView->setIcon(QIcon::fromTheme(QStringLiteral("view-list-icons"))); iconsView->setData(QVariant::fromValue(DolphinView::IconsView)); return iconsView; } KToggleAction* DolphinViewActionHandler::compactModeAction() { KToggleAction* iconsView = m_actionCollection->add(QStringLiteral("compact")); iconsView->setText(i18nc("@action:inmenu View Mode", "Compact")); iconsView->setToolTip(i18nc("@info", "Compact view mode")); m_actionCollection->setDefaultShortcut(iconsView, Qt::CTRL + Qt::Key_2); iconsView->setIcon(QIcon::fromTheme(QStringLiteral("view-list-details"))); // TODO: discuss with Oxygen-team the wrong (?) name iconsView->setData(QVariant::fromValue(DolphinView::CompactView)); return iconsView; } KToggleAction* DolphinViewActionHandler::detailsModeAction() { KToggleAction* detailsView = m_actionCollection->add(QStringLiteral("details")); detailsView->setText(i18nc("@action:inmenu View Mode", "Details")); detailsView->setToolTip(i18nc("@info", "Details view mode")); m_actionCollection->setDefaultShortcut(detailsView, Qt::CTRL + Qt::Key_3); detailsView->setIcon(QIcon::fromTheme(QStringLiteral("view-list-tree"))); detailsView->setData(QVariant::fromValue(DolphinView::DetailsView)); return detailsView; } void DolphinViewActionHandler::slotSortRoleChanged(const QByteArray& role) { KToggleAction* action = m_sortByActions.value(role); if (action) { action->setChecked(true); if (!action->icon().isNull()) { QAction* sortByMenu = m_actionCollection->action(QStringLiteral("sort")); sortByMenu->setIcon(action->icon()); } } QAction* descending = m_actionCollection->action(QStringLiteral("descending")); QAction* ascending = m_actionCollection->action(QStringLiteral("ascending")); if (role == "text" || role == "type" || role == "tags" || role == "comment") { descending->setText(i18nc("Sort descending", "Z-A")); ascending->setText(i18nc("Sort ascending", "A-Z")); } else if (role == "size") { descending->setText(i18nc("Sort descending", "Largest first")); ascending->setText(i18nc("Sort ascending", "Smallest first")); } else if (role == "modificationtime" || role == "creationtime" || role == "accesstime") { descending->setText(i18nc("Sort descending", "Newest first")); ascending->setText(i18nc("Sort ascending", "Oldest first")); } else if (role == "rating") { descending->setText(i18nc("Sort descending", "Highest first")); ascending->setText(i18nc("Sort ascending", "Lowest first")); } else { descending->setText(i18nc("Sort descending", "Descending")); ascending->setText(i18nc("Sort ascending", "Ascending")); } slotSortOrderChanged(m_currentView->sortOrder()); } void DolphinViewActionHandler::slotZoomLevelChanged(int current, int previous) { Q_UNUSED(previous); QAction* zoomInAction = m_actionCollection->action(KStandardAction::name(KStandardAction::ZoomIn)); if (zoomInAction) { zoomInAction->setEnabled(current < ZoomLevelInfo::maximumLevel()); } QAction* zoomOutAction = m_actionCollection->action(KStandardAction::name(KStandardAction::ZoomOut)); if (zoomOutAction) { zoomOutAction->setEnabled(current > ZoomLevelInfo::minimumLevel()); } } void DolphinViewActionHandler::slotSortTriggered(QAction* action) { // The radiobuttons of the "Sort By"-menu are split between the main-menu // and several sub-menus. Because of this they don't have a common // action-group that assures an exclusive toggle-state between the main-menu // actions and the sub-menu-actions. If an action gets checked, it must // be assured that all other actions get unchecked. QAction* sortByMenu = m_actionCollection->action(QStringLiteral("sort")); foreach (QAction* groupAction, sortByMenu->menu()->actions()) { KActionMenu* actionMenu = qobject_cast(groupAction); if (actionMenu) { foreach (QAction* subAction, actionMenu->menu()->actions()) { subAction->setChecked(false); } } else if (groupAction->actionGroup()) { groupAction->setChecked(false); } } action->setChecked(true); // Apply the activated sort-role to the view const QByteArray role = action->data().toByteArray(); m_currentView->setSortRole(role); } void DolphinViewActionHandler::slotAdjustViewProperties() { emit actionBeingHandled(); QPointer dialog = new ViewPropertiesDialog(m_currentView); dialog->exec(); delete dialog; } void DolphinViewActionHandler::slotProperties() { KPropertiesDialog* dialog = nullptr; const KFileItemList list = m_currentView->selectedItems(); if (list.isEmpty()) { const QUrl url = m_currentView->url(); dialog = new KPropertiesDialog(url, m_currentView); } else { dialog = new KPropertiesDialog(list, m_currentView); } dialog->setAttribute(Qt::WA_DeleteOnClose); dialog->show(); dialog->raise(); dialog->activateWindow(); }