diff options
Diffstat (limited to 'src')
53 files changed, 909 insertions, 435 deletions
diff --git a/src/dolphincontextmenu.cpp b/src/dolphincontextmenu.cpp index 1dfbe054d..783abc71f 100644 --- a/src/dolphincontextmenu.cpp +++ b/src/dolphincontextmenu.cpp @@ -1,4 +1,4 @@ -/*************************************************************************** + /*************************************************************************** * Copyright (C) 2006 by Peter Penz ([email protected]) and * * Cvetoslav Ludmiloff * * * @@ -192,7 +192,6 @@ void DolphinContextMenu::openItemContextMenu() QAction* openParentAction = nullptr; QAction* openParentInNewWindowAction = nullptr; QAction* openParentInNewTabAction = nullptr; - QAction* addToPlacesAction = nullptr; const KFileItemListProperties& selectedItemsProps = selectedItemsProperties(); KFileItemActions fileItemActions; @@ -208,32 +207,23 @@ void DolphinContextMenu::openItemContextMenu() // Insert 'Open With' entries addOpenWithActions(fileItemActions); - // insert 'Add to Places' entry - if (!placeExists(m_fileInfo.url())) { - addToPlacesAction = addAction(QIcon::fromTheme(QStringLiteral("bookmark-new")), - i18nc("@action:inmenu Add selected folder to places", - "Add to Places")); - } - - addSeparator(); - // set up 'Create New' menu - DolphinNewFileMenu* newFileMenu = new DolphinNewFileMenu(m_mainWindow->actionCollection(), m_mainWindow); - const DolphinView* view = m_mainWindow->activeViewContainer()->view(); - newFileMenu->setViewShowsHiddenFiles(view->hiddenFilesShown()); - newFileMenu->checkUpToDate(); - newFileMenu->setPopupFiles(m_fileInfo.url()); - newFileMenu->setEnabled(selectedItemsProps.supportsWriting()); - connect(newFileMenu, &DolphinNewFileMenu::fileCreated, newFileMenu, &DolphinNewFileMenu::deleteLater); - connect(newFileMenu, &DolphinNewFileMenu::directoryCreated, newFileMenu, &DolphinNewFileMenu::deleteLater); + DolphinNewFileMenu* newFileMenu = new DolphinNewFileMenu(m_mainWindow->actionCollection(), m_mainWindow); + const DolphinView* view = m_mainWindow->activeViewContainer()->view(); + newFileMenu->setViewShowsHiddenFiles(view->hiddenFilesShown()); + newFileMenu->checkUpToDate(); + newFileMenu->setPopupFiles(m_fileInfo.url()); + newFileMenu->setEnabled(selectedItemsProps.supportsWriting()); + connect(newFileMenu, &DolphinNewFileMenu::fileCreated, newFileMenu, &DolphinNewFileMenu::deleteLater); + connect(newFileMenu, &DolphinNewFileMenu::directoryCreated, newFileMenu, &DolphinNewFileMenu::deleteLater); - QMenu* menu = newFileMenu->menu(); - menu->setTitle(i18nc("@title:menu Create new folder, file, link, etc.", "Create New")); - menu->setIcon(QIcon::fromTheme(QStringLiteral("document-new"))); - addMenu(menu); + QMenu* menu = newFileMenu->menu(); + menu->setTitle(i18nc("@title:menu Create new folder, file, link, etc.", "Create New")); + menu->setIcon(QIcon::fromTheme(QStringLiteral("document-new"))); + addMenu(menu); - addSeparator(); - } else if (m_baseUrl.scheme().contains(QStringLiteral("search")) || m_baseUrl.scheme().contains(QStringLiteral("timeline"))) { + addSeparator(); + } else if (m_baseUrl.scheme().contains(QLatin1String("search")) || m_baseUrl.scheme().contains(QLatin1String("timeline"))) { addOpenWithActions(fileItemActions); openParentAction = new QAction(QIcon::fromTheme(QStringLiteral("document-open-folder")), @@ -255,15 +245,6 @@ void DolphinContextMenu::openItemContextMenu() addAction(openParentInNewTabAction); addSeparator(); - } else if (!DolphinView::openItemAsFolderUrl(m_fileInfo).isEmpty()) { - // Insert 'Open With" entries - addOpenWithActions(fileItemActions); - - // insert 'Open in new window' and 'Open in new tab' entries - addAction(m_mainWindow->actionCollection()->action(QStringLiteral("open_in_new_window"))); - addAction(m_mainWindow->actionCollection()->action(QStringLiteral("open_in_new_tab"))); - - addSeparator(); } else { // Insert 'Open With" entries addOpenWithActions(fileItemActions); @@ -292,6 +273,15 @@ void DolphinContextMenu::openItemContextMenu() insertDefaultItemActions(selectedItemsProps); + // insert 'Add to Places' entry if appropriate + if (m_selectedItems.count() == 1) { + if (m_fileInfo.isDir()) { + if (!placeExists(m_fileInfo.url())) { + addAction(m_mainWindow->actionCollection()->action(QStringLiteral("add_to_places"))); + } + } + } + addSeparator(); fileItemActions.addServiceActionsTo(this); @@ -314,14 +304,7 @@ void DolphinContextMenu::openItemContextMenu() QAction* activatedAction = exec(m_pos); if (activatedAction) { - if (activatedAction == addToPlacesAction) { - const QUrl selectedUrl(m_fileInfo.url()); - if (selectedUrl.isValid()) { - PlacesItemModel model; - const QString text = selectedUrl.fileName(); - model.createPlacesItem(text, selectedUrl, KIO::iconNameForUrl(selectedUrl)); - } - } else if (activatedAction == openParentAction) { + if (activatedAction == openParentAction) { m_command = OpenParentFolder; } else if (activatedAction == openParentInNewWindowAction) { m_command = OpenParentFolderInNewWindow; @@ -333,14 +316,7 @@ void DolphinContextMenu::openItemContextMenu() void DolphinContextMenu::openViewportContextMenu() { - // setup 'Create New' menu - KNewFileMenu* newFileMenu = m_mainWindow->newFileMenu(); const DolphinView* view = m_mainWindow->activeViewContainer()->view(); - newFileMenu->setViewShowsHiddenFiles(view->hiddenFilesShown()); - newFileMenu->checkUpToDate(); - newFileMenu->setPopupFiles(m_baseUrl); - addMenu(newFileMenu->menu()); - addSeparator(); // Insert 'Open With' entries KFileItem baseItem = view->rootItem(); @@ -359,22 +335,20 @@ void DolphinContextMenu::openViewportContextMenu() addOpenWithActions(fileItemActions); } - // Insert 'New Window' and 'New Tab' entries. Don't use "open_in_new_window" and - // "open_in_new_tab" here, as the current selection should get ignored. - addAction(m_mainWindow->actionCollection()->action(QStringLiteral("file_new"))); - addAction(m_mainWindow->actionCollection()->action(QStringLiteral("new_tab"))); - - // Insert 'Add to Places' entry if exactly one item is selected - QAction* addToPlacesAction = nullptr; - if (!placeExists(m_mainWindow->activeViewContainer()->url())) { - addToPlacesAction = addAction(QIcon::fromTheme(QStringLiteral("bookmark-new")), - i18nc("@action:inmenu Add current folder to places", "Add to Places")); - } - - addSeparator(); + // Set up and insert 'Create New' menu + KNewFileMenu* newFileMenu = m_mainWindow->newFileMenu(); + newFileMenu->setViewShowsHiddenFiles(view->hiddenFilesShown()); + newFileMenu->checkUpToDate(); + newFileMenu->setPopupFiles(m_baseUrl); + addMenu(newFileMenu->menu()); QAction* pasteAction = createPasteAction(); addAction(pasteAction); + + // Insert 'Add to Places' entry if it's not already in the places panel + if (!placeExists(m_mainWindow->activeViewContainer()->url())) { + addAction(m_mainWindow->actionCollection()->action(QStringLiteral("add_to_places"))); + } addSeparator(); // Insert 'Sort By' and 'View Mode' @@ -391,26 +365,14 @@ void DolphinContextMenu::openViewportContextMenu() addCustomActions(); + addSeparator(); + QAction* propertiesAction = m_mainWindow->actionCollection()->action(QStringLiteral("properties")); addAction(propertiesAction); addShowMenuBarAction(); - QAction* action = exec(m_pos); - if (addToPlacesAction && (action == addToPlacesAction)) { - const DolphinViewContainer* container = m_mainWindow->activeViewContainer(); - const QUrl url = container->url(); - if (url.isValid()) { - PlacesItemModel model; - QString icon; - if (container->isSearchModeEnabled()) { - icon = QStringLiteral("folder-saved-search-symbolic"); - } else { - icon = KIO::iconNameForUrl(url); - } - model.createPlacesItem(container->placesText(), url, icon); - } - } + exec(m_pos); } void DolphinContextMenu::insertDefaultItemActions(const KFileItemListProperties& properties) diff --git a/src/dolphinmainwindow.cpp b/src/dolphinmainwindow.cpp index c9ed9c5cd..83dcd1b18 100644 --- a/src/dolphinmainwindow.cpp +++ b/src/dolphinmainwindow.cpp @@ -32,6 +32,7 @@ #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" @@ -64,6 +65,7 @@ #include <KStartupInfo> #include <KToggleAction> #include <KToolBar> +#include <KToolBarPopupAction> #include <KToolInvocation> #include <KUrlComboBox> #include <KUrlNavigator> @@ -89,10 +91,12 @@ namespace { // Used for GeneralSettings::version() to determine whether // an updated version of Dolphin is running. const int CurrentDolphinVersion = 200; + // The maximum number of entries in the back/forward popup menu + const int MaxNumberOfNavigationentries = 12; } DolphinMainWindow::DolphinMainWindow() : - KXmlGuiWindow(nullptr, Qt::WindowContextHelpButtonHint), + KXmlGuiWindow(nullptr), m_newFileMenu(nullptr), m_helpMenu(nullptr), m_tabWidget(nullptr), @@ -106,9 +110,14 @@ DolphinMainWindow::DolphinMainWindow() : m_lastHandleUrlStatJob(nullptr), m_terminalPanel(nullptr), m_placesPanel(nullptr), - m_tearDownFromPlacesRequested(false) + m_tearDownFromPlacesRequested(false), + m_backAction(nullptr), + m_forwardAction(nullptr) { Q_INIT_RESOURCE(dolphin); +#ifndef Q_OS_WIN + setWindowFlags(Qt::WindowContextHelpButtonHint); +#endif setComponentName(QStringLiteral("dolphin"), QGuiApplication::applicationDisplayName()); setObjectName(QStringLiteral("Dolphin#")); @@ -272,7 +281,7 @@ void DolphinMainWindow::changeUrl(const QUrl &url) } m_activeViewContainer->setUrl(url); - updateEditActions(); + updateFileAndEditActions(); updatePasteAction(); updateViewActions(); updateGoActions(); @@ -301,7 +310,7 @@ void DolphinMainWindow::slotEditableStateChanged(bool editable) void DolphinMainWindow::slotSelectionChanged(const KFileItemList& selection) { - updateEditActions(); + updateFileAndEditActions(); const int selectedUrlsCount = m_tabWidget->currentTabPage()->selectedItemsCount(); @@ -352,6 +361,32 @@ 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().isEmpty()) { + 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); @@ -482,7 +517,7 @@ void DolphinMainWindow::closeEvent(QCloseEvent* event) } } - if (m_terminalPanel->hasProgramRunning() && GeneralSettings::confirmClosingTerminalRunningProgram() && closedByUser) { + if (m_terminalPanel && m_terminalPanel->hasProgramRunning() && GeneralSettings::confirmClosingTerminalRunningProgram() && closedByUser) { // Ask if the user really wants to quit Dolphin with a program that is still running in the Terminal panel // Open a confirmation dialog with 3 buttons: // QDialogButtonBox::Yes -> Quit @@ -500,7 +535,7 @@ void DolphinMainWindow::closeEvent(QCloseEvent* event) if (!m_terminalPanel->isVisible()) { KGuiItem::assign( buttons->button(QDialogButtonBox::No), - KGuiItem(i18n("Show &Terminal Panel"), QIcon::fromTheme(QStringLiteral("utilities-terminal")))); + KGuiItem(i18n("Show &Terminal Panel"), QIcon::fromTheme(QStringLiteral("dialog-scripts")))); } KGuiItem::assign(buttons->button(QDialogButtonBox::Cancel), KStandardGuiItem::cancel()); @@ -617,6 +652,12 @@ 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)); @@ -643,6 +684,56 @@ void DolphinMainWindow::slotToolBarActionMiddleClicked(QAction *action) } } +void DolphinMainWindow::slotAboutToShowBackPopupMenu() +{ + KUrlNavigator* urlNavigator = m_activeViewContainer->urlNavigator(); + int entries = 0; + m_backAction->menu()->clear(); + for (int i = urlNavigator->historyIndex() + 1; i < urlNavigator->historySize() && entries < MaxNumberOfNavigationentries; ++i, ++entries) { + QAction* action = new QAction(urlNavigator->locationUrl(i).toString(QUrl::PreferLocalFile), m_backAction->menu()); + action->setData(i); + m_backAction->menu()->addAction(action); + } +} + +void DolphinMainWindow::slotGoBack(QAction* action) +{ + int gotoIndex = action->data().value<int>(); + KUrlNavigator* urlNavigator = m_activeViewContainer->urlNavigator(); + for (int i = gotoIndex - urlNavigator->historyIndex(); i > 0; --i) { + goBack(); + } +} + +void DolphinMainWindow::slotBackForwardActionMiddleClicked(QAction* action) +{ + if (action) { + KUrlNavigator* urlNavigator = activeViewContainer()->urlNavigator(); + openNewTabAfterCurrentTab(urlNavigator->locationUrl(action->data().value<int>())); + } +} + +void DolphinMainWindow::slotAboutToShowForwardPopupMenu() +{ + KUrlNavigator* urlNavigator = m_activeViewContainer->urlNavigator(); + int entries = 0; + m_forwardAction->menu()->clear(); + for (int i = urlNavigator->historyIndex() - 1; i >= 0 && entries < MaxNumberOfNavigationentries; --i, ++entries) { + QAction* action = new QAction(urlNavigator->locationUrl(i).toString(QUrl::PreferLocalFile), m_forwardAction->menu()); + action->setData(i); + m_forwardAction->menu()->addAction(action); + } +} + +void DolphinMainWindow::slotGoForward(QAction* action) +{ + int gotoIndex = action->data().value<int>(); + KUrlNavigator* urlNavigator = m_activeViewContainer->urlNavigator(); + for (int i = urlNavigator->historyIndex() - gotoIndex; i > 0; --i) { + goForward(); + } +} + void DolphinMainWindow::selectAll() { clearStatusBar(); @@ -751,7 +842,7 @@ void DolphinMainWindow::togglePanelLockState() void DolphinMainWindow::slotTerminalPanelVisibilityChanged() { - if (m_terminalPanel->isHiddenInVisibleWindow()) { + if (m_terminalPanel->isHiddenInVisibleWindow() && m_activeViewContainer) { m_activeViewContainer->view()->setFocus(); } } @@ -961,18 +1052,15 @@ void DolphinMainWindow::updateControlMenu() KActionCollection* ac = actionCollection(); - // Add "Create New" menu menu->addMenu(m_newFileMenu->menu()); + addActionToMenu(ac->action(QStringLiteral("file_new")), menu); + addActionToMenu(ac->action(QStringLiteral("new_tab")), menu); + addActionToMenu(ac->action(QStringLiteral("closed_tabs")), menu); 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); @@ -983,66 +1071,40 @@ void DolphinMainWindow::updateControlMenu() // Add "View" actions if (!GeneralSettings::showZoomSlider()) { addActionToMenu(ac->action(KStandardAction::name(KStandardAction::ZoomIn)), menu); + addActionToMenu(ac->action(QStringLiteral("view_zoom_reset")), menu); addActionToMenu(ac->action(KStandardAction::name(KStandardAction::ZoomOut)), menu); 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) | + added = 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("show_hidden_files")), menu) | + addActionToMenu(ac->action(QStringLiteral("additional_info")), 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); + // Add a curated assortment of items from the "Tools" menu + addActionToMenu(ac->action(QStringLiteral("show_filter_bar")), menu); + addActionToMenu(ac->action(QStringLiteral("open_terminal")), menu); 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 "Show Panels" menu + addActionToMenu(ac->action(QStringLiteral("panels")), menu); // Add "Settings" menu entries addActionToMenu(ac->action(KStandardAction::name(KStandardAction::KeyBindings)), menu); addActionToMenu(ac->action(KStandardAction::name(KStandardAction::ConfigureToolbars)), menu); addActionToMenu(ac->action(KStandardAction::name(KStandardAction::Preferences)), menu); + addActionToMenu(ac->action(KStandardAction::name(KStandardAction::ShowMenubar)), menu); // Add "Help" menu - menu->addMenu(m_helpMenu->menu()); - - menu->addSeparator(); - addActionToMenu(ac->action(KStandardAction::name(KStandardAction::ShowMenubar)), menu); + auto helpMenu = m_helpMenu->menu(); + helpMenu->setIcon(QIcon::fromTheme(QStringLiteral("system-help"))); + menu->addMenu(helpMenu); } void DolphinMainWindow::updateToolBar() @@ -1084,6 +1146,9 @@ void DolphinMainWindow::activeViewChanged(DolphinViewContainer* viewContainer) m_activeViewContainer = viewContainer; if (oldViewContainer) { + const QAction* toggleSearchAction = actionCollection()->action(QStringLiteral("toggle_search")); + toggleSearchAction->disconnect(oldViewContainer); + // Disconnect all signals between the old view container (container, // view and url navigator) and main window. oldViewContainer->disconnect(this); @@ -1100,10 +1165,11 @@ void DolphinMainWindow::activeViewChanged(DolphinViewContainer* viewContainer) m_actionHandler->setCurrentView(viewContainer->view()); updateHistory(); - updateEditActions(); + updateFileAndEditActions(); updatePasteAction(); updateViewActions(); updateGoActions(); + updateSearchAction(); const QUrl url = viewContainer->url(); emit urlChanged(url); @@ -1126,7 +1192,7 @@ void DolphinMainWindow::updateWindowTitle() void DolphinMainWindow::slotStorageTearDownFromPlacesRequested(const QString& mountPath) { - if (m_terminalPanel->currentWorkingDirectory().startsWith(mountPath)) { + if (m_terminalPanel && m_terminalPanel->currentWorkingDirectory().startsWith(mountPath)) { m_tearDownFromPlacesRequested = true; m_terminalPanel->goHome(); // m_placesPanel->proceedWithTearDown() will be called in slotTerminalDirectoryChanged @@ -1137,7 +1203,7 @@ void DolphinMainWindow::slotStorageTearDownFromPlacesRequested(const QString& mo void DolphinMainWindow::slotStorageTearDownExternallyRequested(const QString& mountPath) { - if (m_terminalPanel->currentWorkingDirectory().startsWith(mountPath)) { + if (m_terminalPanel && m_terminalPanel->currentWorkingDirectory().startsWith(mountPath)) { m_tearDownFromPlacesRequested = false; m_terminalPanel->goHome(); } @@ -1156,6 +1222,7 @@ void DolphinMainWindow::setupActions() 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." "<nl/>You can drag and drop items between windows.")); @@ -1171,6 +1238,12 @@ void DolphinMainWindow::setupActions() 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 " @@ -1224,6 +1297,17 @@ void DolphinMainWindow::setupActions() "the find bar so we can have a look at it while the settings are " "explained.</para>")); + // 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.")); @@ -1287,10 +1371,22 @@ void DolphinMainWindow::setupActions() connect(replaceLocation, &QAction::triggered, this, &DolphinMainWindow::replaceLocation); // setup 'Go' menu - QAction* backAction = KStandardAction::back(this, &DolphinMainWindow::goBack, actionCollection()); - auto backShortcuts = backAction->shortcuts(); + { + QScopedPointer<QAction> backAction(KStandardAction::back(nullptr, nullptr, nullptr)); + m_backAction = new KToolBarPopupAction(backAction->icon(), backAction->text(), actionCollection()); + m_backAction->setObjectName(backAction->objectName()); + m_backAction->setShortcuts(backAction->shortcuts()); + } + m_backAction->setDelayed(true); + m_backAction->setStickyMenu(false); + connect(m_backAction, &QAction::triggered, this, &DolphinMainWindow::goBack); + connect(m_backAction->menu(), &QMenu::aboutToShow, this, &DolphinMainWindow::slotAboutToShowBackPopupMenu); + connect(m_backAction->menu(), &QMenu::triggered, this, &DolphinMainWindow::slotGoBack); + actionCollection()->addAction(m_backAction->objectName(), m_backAction); + + auto backShortcuts = m_backAction->shortcuts(); backShortcuts.append(QKeySequence(Qt::Key_Backspace)); - actionCollection()->setDefaultShortcuts(backAction, backShortcuts); + actionCollection()->setDefaultShortcuts(m_backAction, backShortcuts); DolphinRecentTabsMenu* recentTabsMenu = new DolphinRecentTabsMenu(this); actionCollection()->addAction(QStringLiteral("closed_tabs"), recentTabsMenu); @@ -1319,7 +1415,23 @@ void DolphinMainWindow::setupActions() "be undone will ask for your confirmation.")); undoAction->setEnabled(false); // undo should be disabled by default - KStandardAction::forward(this, &DolphinMainWindow::goForward, actionCollection()); + { + QScopedPointer<QAction> forwardAction(KStandardAction::forward(nullptr, nullptr, nullptr)); + m_forwardAction = new KToolBarPopupAction(forwardAction->icon(), forwardAction->text(), actionCollection()); + m_forwardAction->setObjectName(forwardAction->objectName()); + m_forwardAction->setShortcuts(forwardAction->shortcuts()); + } + m_forwardAction->setDelayed(true); + m_forwardAction->setStickyMenu(false); + connect(m_forwardAction, &QAction::triggered, this, &DolphinMainWindow::goForward); + connect(m_forwardAction->menu(), &QMenu::aboutToShow, this, &DolphinMainWindow::slotAboutToShowForwardPopupMenu); + connect(m_forwardAction->menu(), &QMenu::triggered, this, &DolphinMainWindow::slotGoForward); + actionCollection()->addAction(m_forwardAction->objectName(), m_forwardAction); + // enable middle-click to open in a new tab + auto *middleClickEventFilter = new MiddleClickActionEventFilter(this); + connect(middleClickEventFilter, &MiddleClickActionEventFilter::actionMiddleClicked, this, &DolphinMainWindow::slotBackForwardActionMiddleClicked); + m_backAction->menu()->installEventFilter(middleClickEventFilter); + m_forwardAction->menu()->installEventFilter(middleClickEventFilter); KStandardAction::up(this, &DolphinMainWindow::goUp, actionCollection()); QAction* homeAction = KStandardAction::home(this, &DolphinMainWindow::goHome, actionCollection()); homeAction->setWhatsThis(xi18nc("@info:whatsthis", "Go to your " @@ -1351,7 +1463,7 @@ void DolphinMainWindow::setupActions() openTerminal->setWhatsThis(xi18nc("@info:whatsthis", "<para>This opens a <emphasis>terminal</emphasis> application for the viewed location.</para>" "<para>To learn more about terminals use the help in the terminal application.</para>")); - openTerminal->setIcon(QIcon::fromTheme(QStringLiteral("utilities-terminal"))); + openTerminal->setIcon(QIcon::fromTheme(QStringLiteral("dialog-scripts"))); actionCollection()->setDefaultShortcut(openTerminal, Qt::SHIFT + Qt::Key_F4); connect(openTerminal, &QAction::triggered, this, &DolphinMainWindow::openTerminal); } @@ -1538,7 +1650,7 @@ void DolphinMainWindow::setupDockWidgets() this, &DolphinMainWindow::slotTerminalPanelVisibilityChanged); QAction* terminalAction = terminalDock->toggleViewAction(); - createPanelAction(QIcon::fromTheme(QStringLiteral("utilities-terminal")), Qt::Key_F4, terminalAction, QStringLiteral("show_terminal_panel")); + createPanelAction(QIcon::fromTheme(QStringLiteral("dialog-scripts")), Qt::Key_F4, terminalAction, QStringLiteral("show_terminal_panel")); addDockWidget(Qt::BottomDockWidgetArea, terminalDock); connect(this, &DolphinMainWindow::urlChanged, @@ -1603,7 +1715,7 @@ void DolphinMainWindow::setupDockWidgets() 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 " @@ -1611,13 +1723,13 @@ void DolphinMainWindow::setupDockWidgets() "appear semi-transparent unless you uncheck their hide property.")); connect(actionShowAllPlaces, &QAction::triggered, this, [actionShowAllPlaces, this](bool checked){ - actionShowAllPlaces->setIcon(QIcon::fromTheme(checked ? QStringLiteral("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")) @@ -1640,8 +1752,9 @@ void DolphinMainWindow::setupDockWidgets() "</interface> to display it again.</para>") + panelWhatsThis); // Add actions into the "Panels" menu - KActionMenu* panelsMenu = new KActionMenu(i18nc("@action:inmenu View", "Panels"), this); + KActionMenu* panelsMenu = new KActionMenu(i18nc("@action:inmenu View", "Show Panels"), this); actionCollection()->addAction(QStringLiteral("panels"), panelsMenu); + panelsMenu->setIcon(QIcon::fromTheme(QStringLiteral("view-sidetree"))); panelsMenu->setDelayed(false); const KActionCollection* ac = actionCollection(); panelsMenu->addAction(ac->action(QStringLiteral("show_places_panel"))); @@ -1659,15 +1772,20 @@ void DolphinMainWindow::setupDockWidgets() }); } -void DolphinMainWindow::updateEditActions() + +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")); - KActionCollection* col = actionCollection(); QAction* renameAction = col->action(KStandardAction::name(KStandardAction::RenameFile)); QAction* moveToTrashAction = col->action(KStandardAction::name(KStandardAction::MoveToTrash)); QAction* deleteAction = col->action(KStandardAction::name(KStandardAction::DeleteFile)); @@ -1675,6 +1793,14 @@ void DolphinMainWindow::updateEditActions() 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(); @@ -1727,10 +1853,9 @@ void DolphinMainWindow::createControlButton() m_controlButton = new QToolButton(this); m_controlButton->setIcon(QIcon::fromTheme(QStringLiteral("application-menu"))); - m_controlButton->setText(i18nc("@action", "Control")); + m_controlButton->setToolTip(i18nc("@action", "Show menu")); m_controlButton->setAttribute(Qt::WidgetAttribute::WA_CustomWhatsThis); m_controlButton->setPopupMode(QToolButton::InstantPopup); - m_controlButton->setToolButtonStyle(toolBar()->toolButtonStyle()); QMenu* controlMenu = new QMenu(m_controlButton); connect(controlMenu, &QMenu::aboutToShow, this, &DolphinMainWindow::updateControlMenu); @@ -1741,8 +1866,6 @@ void DolphinMainWindow::createControlButton() toolBar()->addWidget(m_controlButton); connect(toolBar(), &KToolBar::iconSizeChanged, m_controlButton, &QToolButton::setIconSize); - connect(toolBar(), &KToolBar::toolButtonStyleChanged, - m_controlButton, &QToolButton::setToolButtonStyle); // 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 @@ -1805,6 +1928,11 @@ void DolphinMainWindow::connectViewSignals(DolphinViewContainer* container) 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, @@ -2089,10 +2217,6 @@ void DolphinMainWindow::UndoUiInterface::jobError(KIO::Job* job) bool DolphinMainWindow::isUrlOpen(const QString& url) { - if (m_tabWidget->getIndexByUrl(QUrl::fromUserInput((url))).first >= 0) { - return true; - } else { - return false; - } + return m_tabWidget->isUrlOpen(QUrl::fromUserInput((url))); } diff --git a/src/dolphinmainwindow.h b/src/dolphinmainwindow.h index dcfd9bce2..3d86340d6 100644 --- a/src/dolphinmainwindow.h +++ b/src/dolphinmainwindow.h @@ -47,6 +47,7 @@ class KFileItemList; class KJob; class KNewFileMenu; class KHelpMenu; +class KToolBarPopupAction; class QToolButton; class QIcon; class PlacesPanel; @@ -255,6 +256,9 @@ private slots: /** Replaces the URL navigator by a search box to find files. */ void find(); + /** Updates the state of the search action according to the view container. */ + void updateSearchAction(); + /** * Updates the text of the paste action dependent on * the number of items which are in the clipboard. @@ -382,6 +386,11 @@ private slots: void openNewActivatedTab(); /** + * Adds the current URL as an entry to the Places panel + */ + void addToPlaces(); + + /** * Opens a new tab in the background showing the URL \a url. */ void openNewTab(const QUrl& url, DolphinTabWidget::TabPlacement tabPlacement); @@ -501,6 +510,36 @@ private slots: */ void slotToolBarActionMiddleClicked(QAction *action); + /** + * Is called before the Back popup menu is shown. This slot will populate + * the menu with history data + */ + void slotAboutToShowBackPopupMenu(); + + /** + * This slot is used by the Back Popup Menu to go back to a specific + * history index. The QAction::data will carry an int with the index + * to go to. + */ + void slotGoBack(QAction* action); + + /** + * Middle clicking Back/Forward will open the resulting folder in a new tab. + */ + void slotBackForwardActionMiddleClicked(QAction *action); + + /** + * Is called before the Forward popup menu is shown. This slot will populate + * the menu with history data + */ + void slotAboutToShowForwardPopupMenu(); + + /** + * This slot is used by the Forward Popup Menu to go forward to a specific + * history index. The QAction::data will carry an int with the index + * to go to. + */ + void slotGoForward(QAction* action); private: /** * Sets up the various menus and actions and connects them. @@ -512,7 +551,7 @@ private: */ void setupDockWidgets(); - void updateEditActions(); + void updateFileAndEditActions(); void updateViewActions(); void updateGoActions(); @@ -591,6 +630,9 @@ private: TerminalPanel* m_terminalPanel; PlacesPanel* m_placesPanel; bool m_tearDownFromPlacesRequested; + + KToolBarPopupAction* m_backAction; + KToolBarPopupAction* m_forwardAction; }; inline DolphinViewContainer* DolphinMainWindow::activeViewContainer() const diff --git a/src/dolphinpart.cpp b/src/dolphinpart.cpp index a4d7fdf78..607917f9a 100644 --- a/src/dolphinpart.cpp +++ b/src/dolphinpart.cpp @@ -222,7 +222,7 @@ void DolphinPart::createActions() #ifndef Q_OS_WIN if (KAuthorized::authorize(QStringLiteral("shell_access"))) { m_openTerminalAction = actionCollection()->addAction(QStringLiteral("open_terminal")); - m_openTerminalAction->setIcon(QIcon::fromTheme(QStringLiteral("utilities-terminal"))); + m_openTerminalAction->setIcon(QIcon::fromTheme(QStringLiteral("dialog-scripts"))); m_openTerminalAction->setText(i18nc("@action:inmenu Tools", "Open &Terminal")); connect(m_openTerminalAction, &QAction::triggered, this, &DolphinPart::slotOpenTerminal); actionCollection()->setDefaultShortcut(m_openTerminalAction, Qt::Key_F4); diff --git a/src/dolphinpart.desktop b/src/dolphinpart.desktop index 19c072a02..3d53ecbd6 100644 --- a/src/dolphinpart.desktop +++ b/src/dolphinpart.desktop @@ -22,7 +22,7 @@ Name[id]=Tampilan Dolphin Name[it]=Vista di Dolphin Name[ja]=Dolphin ビュー Name[ko]=Dolphin 보기 -Name[lt]=Dolphin žiūryklė +Name[lt]=Dolphin rodinys Name[ml]=ഡോള്ഫിന് അവതരണരീതി Name[nb]=Dolphin visning Name[nl]=Dolphin-weergave @@ -82,7 +82,7 @@ Name[id]=Ikon Name[it]=Icone Name[ja]=アイコン Name[ko]=아이콘 -Name[lt]=Ženkliukai +Name[lt]=Piktogramos Name[ml]=സൂചനാചിത്രങ്ങള് Name[nb]=Ikoner Name[nl]=Pictogrammen @@ -183,7 +183,7 @@ Name[id]=Perincian Name[it]=Dettagli Name[ja]=詳細 Name[ko]=자세히 -Name[lt]=Informacija +Name[lt]=Išsamus Name[ml]=വിശദമായി Name[nb]=Detaljer Name[nl]=Details diff --git a/src/dolphintabwidget.cpp b/src/dolphintabwidget.cpp index 7928c510e..ec0c783bc 100644 --- a/src/dolphintabwidget.cpp +++ b/src/dolphintabwidget.cpp @@ -130,6 +130,11 @@ void DolphinTabWidget::refreshViews() } } +bool DolphinTabWidget::isUrlOpen(const QUrl &url) const +{ + return indexByUrl(url).first >= 0; +} + void DolphinTabWidget::openNewActivatedTab() { const DolphinViewContainer* oldActiveViewContainer = currentTabPage()->activeViewContainer(); @@ -161,6 +166,7 @@ void DolphinTabWidget::openNewTab(const QUrl& primaryUrl, const QUrl& secondaryU QWidget* focusWidget = QApplication::focusWidget(); DolphinTabPage* tabPage = new DolphinTabPage(primaryUrl, secondaryUrl, this); + tabPage->setActive(false); tabPage->setPlacesSelectorVisible(m_placesSelectorVisible); connect(tabPage, &DolphinTabPage::activeViewChanged, this, &DolphinTabWidget::activeViewChanged); @@ -170,7 +176,7 @@ void DolphinTabWidget::openNewTab(const QUrl& primaryUrl, const QUrl& secondaryU if (tabPlacement == AfterCurrentTab) { newTabIndex = currentIndex() + 1; } - insertTab(newTabIndex, tabPage, QIcon::fromTheme(KIO::iconNameForUrl(primaryUrl)), tabName(tabPage)); + insertTab(newTabIndex, tabPage, QIcon() /* loaded in tabInserted */, tabName(tabPage)); if (focusWidget) { // The DolphinViewContainer grabbed the keyboard focus. As the tab is opened @@ -186,11 +192,13 @@ void DolphinTabWidget::openDirectories(const QList<QUrl>& dirs, bool splitView) QList<QUrl>::const_iterator it = dirs.constBegin(); while (it != dirs.constEnd()) { const QUrl& primaryUrl = *(it++); - const QPair<int, bool> viewLocation = getIndexByUrl(primaryUrl); - if (viewLocation.first >= 0) { - setCurrentIndex(viewLocation.first); - const auto tabPage = tabPageAt(viewLocation.first); - if (viewLocation.second) { + const QPair<int, bool> indexInfo = indexByUrl(primaryUrl); + const int index = indexInfo.first; + const bool isInPrimaryView = indexInfo.second; + if (index >= 0) { + setCurrentIndex(index); + const auto tabPage = tabPageAt(index); + if (isInPrimaryView) { tabPage->primaryViewContainer()->setActive(true); } else { tabPage->secondaryViewContainer()->setActive(true); @@ -329,7 +337,12 @@ void DolphinTabWidget::tabUrlChanged(const QUrl& url) const int index = indexOf(qobject_cast<QWidget*>(sender())); if (index >= 0) { tabBar()->setTabText(index, tabName(tabPageAt(index))); - tabBar()->setTabIcon(index, QIcon::fromTheme(KIO::iconNameForUrl(url))); + if (tabBar()->isVisible()) { + tabBar()->setTabIcon(index, QIcon::fromTheme(KIO::iconNameForUrl(url))); + } else { + // Mark as dirty, actually load once the tab bar actually gets shown + tabBar()->setTabIcon(index, QIcon()); + } // Emit the currentUrlChanged signal if the url of the current tab has been changed. if (index == currentIndex()) { @@ -357,6 +370,13 @@ void DolphinTabWidget::tabInserted(int index) QTabWidget::tabInserted(index); if (count() > 1) { + // Resolve all pending tab icons + for (int i = 0; i < count(); ++i) { + if (tabBar()->tabIcon(i).isNull()) { + tabBar()->setTabIcon(i, QIcon::fromTheme(KIO::iconNameForUrl(tabPageAt(i)->activeViewContainer()->url()))); + } + } + tabBar()->show(); } @@ -387,7 +407,7 @@ QString DolphinTabWidget::tabName(DolphinTabPage* tabPage) const return name.replace('&', QLatin1String("&&")); } -QPair<int, bool> DolphinTabWidget::getIndexByUrl(const QUrl& url) const +QPair<int, bool> DolphinTabWidget::indexByUrl(const QUrl& url) const { for (int i = 0; i < count(); i++) { const auto tabPage = tabPageAt(i); diff --git a/src/dolphintabwidget.h b/src/dolphintabwidget.h index 7eb001b21..4351a40a8 100644 --- a/src/dolphintabwidget.h +++ b/src/dolphintabwidget.h @@ -79,14 +79,10 @@ public: void refreshViews(); /** - * @param url The URL that we would like - * @return a QPair with first containing the index of the tab with the - * desired URL or -1 if not found. Second says true if URL is in primary - * view container, false otherwise. False means the URL is in the secondary - * view container, unless first == -1. In that case the value of second - * is meaningless. + * @return Whether any of the tab pages contains @p url in their primary + * or secondary view. */ - QPair<int, bool> getIndexByUrl(const QUrl& url) const; + bool isUrlOpen(const QUrl& url) const; signals: /** @@ -221,6 +217,16 @@ private: */ QString tabName(DolphinTabPage* tabPage) const; + /** + * @param url The URL that we would like + * @return a QPair with first containing the index of the tab with the + * desired URL or -1 if not found. Second says true if URL is in primary + * view container, false otherwise. False means the URL is in the secondary + * view container, unless first == -1. In that case the value of second + * is meaningless. + */ + QPair<int, bool> indexByUrl(const QUrl& url) const; + private: /** Caches the (negated) places panel visibility */ bool m_placesSelectorVisible; diff --git a/src/dolphinui.rc b/src/dolphinui.rc index b90321d05..dcacc56c4 100644 --- a/src/dolphinui.rc +++ b/src/dolphinui.rc @@ -1,5 +1,5 @@ <!DOCTYPE kpartgui SYSTEM "kpartgui.dtd"> -<kpartgui name="dolphin" version="22"> +<kpartgui name="dolphin" version="27"> <MenuBar> <Menu name="file"> <Action name="new_menu" /> @@ -8,6 +8,8 @@ <Action name="file_close" /> <Action name="undo_close_tab" /> <Separator/> + <Action name="add_to_places" /> + <Separator/> <Action name="renamefile" /> <Action name="movetotrash" /> <Action name="deletefile" /> @@ -21,6 +23,10 @@ <Action name="invert_selection" /> </Menu> <Menu name="view"> + <Action name="view_zoom_in"/> + <Action name="view_zoom_reset"/> + <Action name="view_zoom_out"/> + <Separator/> <Action name="sort" /> <Action name="view_mode" /> <Action name="additional_info" /> @@ -34,7 +40,7 @@ <Action name="stop" /> <Separator/> <Action name="panels" /> - <Menu name="location_bar"> + <Menu name="location_bar" icon="edit-select-text"> <text context="@title:menu">Location Bar</text> <Action name="editable_location" /> <Action name="replace_location" /> @@ -98,10 +104,11 @@ <Action name="compact" /> <Action name="details" /> <Separator name="separator_0" /> - <Action name="edit_find"/> - <Action name="show_preview" /> + <Action name="sort" /> + <Spacer name="spacer_0" /> <Action name="split_view" /> <Action name="split_stash" /> + <Action name="toggle_search" /> </ToolBar> <ActionProperties scheme="Default"> <Action priority="0" name="go_back"/> @@ -113,9 +120,11 @@ <Action priority="0" name="compact"/> <Action priority="0" name="details"/> <Action priority="0" name="view_zoom_in"/> + <Action priority="0" name="view_zoom_reset"/> <Action priority="0" name="view_zoom_out"/> <Action priority="0" name="edit_cut"/> <Action priority="0" name="edit_copy"/> <Action priority="0" name="edit_paste"/> + <Action priority="0" name="toggle_search"/> </ActionProperties> </kpartgui> diff --git a/src/dolphinviewcontainer.cpp b/src/dolphinviewcontainer.cpp index 8e821d8aa..98b812497 100644 --- a/src/dolphinviewcontainer.cpp +++ b/src/dolphinviewcontainer.cpp @@ -409,6 +409,8 @@ void DolphinViewContainer::setSearchModeEnabled(bool enabled) } m_searchModeEnabled = enabled; + + emit searchModeEnabledChanged(enabled); } bool DolphinViewContainer::isSearchModeEnabled() const @@ -451,7 +453,7 @@ QString DolphinViewContainer::caption() const } KFilePlacesModel *placesModel = DolphinPlacesModelSingleton::instance().placesModel(); - const auto& matchedPlaces = placesModel->match(placesModel->index(0,0), KFilePlacesModel::UrlRole, url(), 1, Qt::MatchExactly); + const auto& matchedPlaces = placesModel->match(placesModel->index(0,0), KFilePlacesModel::UrlRole, QUrl(url().adjusted(QUrl::StripTrailingSlash).toString(QUrl::FullyEncoded).append("/?")), 1, Qt::MatchRegExp); if (!matchedPlaces.isEmpty()) { return placesModel->text(matchedPlaces.first()); @@ -698,7 +700,7 @@ void DolphinViewContainer::slotUrlNavigatorLocationChanged(const QUrl& url) app = browser; if (app.startsWith('!')) { // a literal command has been configured, remove the '!' prefix - app = app.mid(1); + app.remove(0, 1); } } } else { @@ -786,7 +788,7 @@ void DolphinViewContainer::showErrorMessage(const QString& msg) bool DolphinViewContainer::isSearchUrl(const QUrl& url) const { - return url.scheme().contains(QStringLiteral("search")); + return url.scheme().contains(QLatin1String("search")); } void DolphinViewContainer::saveViewState() diff --git a/src/dolphinviewcontainer.h b/src/dolphinviewcontainer.h index 2c4c7a7e1..5207d2d35 100644 --- a/src/dolphinviewcontainer.h +++ b/src/dolphinviewcontainer.h @@ -118,11 +118,8 @@ public: /** Returns true, if the filter bar is visible. */ bool isFilterBarVisible() const; - /** - * Enables the search mode, if \p enabled is true. In the search mode the URL navigator - * will be hidden and replaced by a line editor that allows to enter a search term. - */ - void setSearchModeEnabled(bool enabled); + + /** Returns true if the search mode is enabled. */ bool isSearchModeEnabled() const; /** @@ -160,11 +157,21 @@ public slots: */ void setFilterBarVisible(bool visible); + /** + * Enables the search mode, if \p enabled is true. In the search mode the URL navigator + * will be hidden and replaced by a line editor that allows to enter a search term. + */ + void setSearchModeEnabled(bool enabled); + signals: /** * Is emitted whenever the filter bar has changed its visibility state. */ void showFilterBarChanged(bool shown); + /** + * Is emitted whenever the search mode has changed its state. + */ + void searchModeEnabledChanged(bool enabled); /** * Is emitted when the write state of the folder has been changed. The application diff --git a/src/global.cpp b/src/global.cpp index 21660a828..48e78e9ea 100644 --- a/src/global.cpp +++ b/src/global.cpp @@ -77,12 +77,6 @@ bool Dolphin::attachToExistingInstance(const QList<QUrl>& inputUrls, bool openFi return false; } - const QStringList services = QDBusConnection::sessionBus().interface()->registeredServiceNames().value(); - - // Don't match the service without trailing "-" (unique instance) - const QString pattern = QStringLiteral("org.kde.dolphin-"); - // Don't match the pid without leading "-" - const QString myPid = QStringLiteral("-") + QString::number(QCoreApplication::applicationPid()); QVector<QPair<QSharedPointer<QDBusInterface>, QStringList>> dolphinServices; if (!preferredService.isEmpty()) { QSharedPointer<QDBusInterface> preferred( @@ -91,11 +85,16 @@ bool Dolphin::attachToExistingInstance(const QList<QUrl>& inputUrls, bool openFi QStringLiteral("org.kde.dolphin.MainWindow")) ); if (preferred->isValid() && !preferred->lastError().isValid()) { - dolphinServices.append(qMakePair(preferred, QStringList() )); + dolphinServices.append(qMakePair(preferred, QStringList())); } } // find all dolphin instances + const QStringList services = QDBusConnection::sessionBus().interface()->registeredServiceNames().value(); + // Don't match the service without trailing "-" (unique instance) + const QString pattern = QStringLiteral("org.kde.dolphin-"); + // Don't match the pid without leading "-" + const QString myPid = QLatin1Char('-') + QString::number(QCoreApplication::applicationPid()); for (const QString& service : services) { if (service.startsWith(pattern) && !service.endsWith(myPid)) { // Check if instance can handle our URLs @@ -104,10 +103,9 @@ bool Dolphin::attachToExistingInstance(const QList<QUrl>& inputUrls, bool openFi QStringLiteral("/dolphin/Dolphin_1"), QStringLiteral("org.kde.dolphin.MainWindow")) ); - if (!instance->isValid() || instance->lastError().isValid()) { - continue; + if (instance->isValid() && !instance->lastError().isValid()) { + dolphinServices.append(qMakePair(instance, QStringList())); } - dolphinServices.append(qMakePair(instance, QStringList())); } } @@ -124,9 +122,9 @@ bool Dolphin::attachToExistingInstance(const QList<QUrl>& inputUrls, bool openFi for (auto& service: dolphinServices) { QDBusReply<bool> isUrlOpen = service.first->call(QStringLiteral("isUrlOpen"), url); if (isUrlOpen.isValid() && isUrlOpen.value()) { - service.second.append(url); - urlFound = true; - break; + service.second.append(url); + urlFound = true; + break; } } if (!urlFound) { diff --git a/src/kitemviews/kfileitemmodel.cpp b/src/kitemviews/kfileitemmodel.cpp index 8145a00f1..c11aae3d2 100644 --- a/src/kitemviews/kfileitemmodel.cpp +++ b/src/kitemviews/kfileitemmodel.cpp @@ -917,6 +917,7 @@ void KFileItemModel::resortAllItems() void KFileItemModel::slotCompleted() { + m_maximumUpdateIntervalTimer->stop(); dispatchPendingItemsToInsert(); if (!m_urlsToExpand.isEmpty()) { @@ -1007,7 +1008,7 @@ void KFileItemModel::slotItemsAdded(const QUrl &directoryUrl, const KFileItemLis } } - if (useMaximumUpdateInterval() && !m_maximumUpdateIntervalTimer->isActive()) { + if (!m_maximumUpdateIntervalTimer->isActive()) { // Assure that items get dispatched if no completed() or canceled() signal is // emitted during the maximum update interval. m_maximumUpdateIntervalTimer->start(); @@ -1606,7 +1607,7 @@ QHash<QByteArray, QVariant> KFileItemModel::retrieveData(const KFileItem& item, if (m_requestRole[DestinationRole]) { QString destination = item.linkDest(); if (destination.isEmpty()) { - destination = QStringLiteral("-"); + destination = QLatin1Char('-'); } data.insert(sharedValue("destination"), destination); } @@ -1828,8 +1829,15 @@ int KFileItemModel::sortRoleCompare(const ItemData* a, const ItemData* b, const default: { const QByteArray role = roleForType(m_sortRole); - result = QString::compare(a->values.value(role).toString(), - b->values.value(role).toString()); + const QString roleValueA = a->values.value(role).toString(); + const QString roleValueB = b->values.value(role).toString(); + if (!roleValueA.isEmpty() && roleValueB.isEmpty()) { + result = -1; + } else if (roleValueA.isEmpty() && !roleValueB.isEmpty()) { + result = +1; + } else { + result = QString::compare(roleValueA, roleValueB); + } break; } @@ -1875,11 +1883,6 @@ int KFileItemModel::stringCompare(const QString& a, const QString& b, const QCol return QString::compare(a, b, Qt::CaseSensitive); } -bool KFileItemModel::useMaximumUpdateInterval() const -{ - return !m_dirLister->url().isLocalFile(); -} - QList<QPair<int, QVariant> > KFileItemModel::nameRoleGroups() const { Q_ASSERT(!m_itemData.isEmpty()); @@ -1905,28 +1908,35 @@ QList<QPair<int, QVariant> > KFileItemModel::nameRoleGroups() const if (firstChar != newFirstChar) { QString newGroupValue; if (newFirstChar.isLetter()) { - // Try to find a matching group in the range 'A' to 'Z'. - static std::vector<QChar> lettersAtoZ; - lettersAtoZ.reserve('Z' - 'A' + 1); - if (lettersAtoZ.empty()) { - for (char c = 'A'; c <= 'Z'; ++c) { - lettersAtoZ.push_back(QLatin1Char(c)); + + if (m_collator.compare(newFirstChar, QChar(QLatin1Char('A'))) >= 0 && m_collator.compare(newFirstChar, QChar(QLatin1Char('Z'))) <= 0) { + // WARNING! Symbols based on latin 'Z' like 'Z' with acute are treated wrong as non Latin and put in a new group. + + // Try to find a matching group in the range 'A' to 'Z'. + static std::vector<QChar> lettersAtoZ; + lettersAtoZ.reserve('Z' - 'A' + 1); + if (lettersAtoZ.empty()) { + for (char c = 'A'; c <= 'Z'; ++c) { + lettersAtoZ.push_back(QLatin1Char(c)); + } } - } - auto localeAwareLessThan = [this](QChar c1, QChar c2) -> bool { - return m_collator.compare(c1, c2) < 0; - }; + auto localeAwareLessThan = [this](QChar c1, QChar c2) -> bool { + return m_collator.compare(c1, c2) < 0; + }; - std::vector<QChar>::iterator it = std::lower_bound(lettersAtoZ.begin(), lettersAtoZ.end(), newFirstChar, localeAwareLessThan); - if (it != lettersAtoZ.end()) { - if (localeAwareLessThan(newFirstChar, *it) && it != lettersAtoZ.begin()) { - // newFirstChar belongs to the group preceding *it. - // Example: for an umlaut 'A' in the German locale, *it would be 'B' now. - --it; + std::vector<QChar>::iterator it = std::lower_bound(lettersAtoZ.begin(), lettersAtoZ.end(), newFirstChar, localeAwareLessThan); + if (it != lettersAtoZ.end()) { + if (localeAwareLessThan(newFirstChar, *it)) { + // newFirstChar belongs to the group preceding *it. + // Example: for an umlaut 'A' in the German locale, *it would be 'B' now. + --it; + } + newGroupValue = *it; } - newGroupValue = *it; + } else { + // Symbols from non Latin-based scripts newGroupValue = newFirstChar; } } else if (newFirstChar >= QLatin1Char('0') && newFirstChar <= QLatin1Char('9')) { diff --git a/src/kitemviews/kfileitemmodel.h b/src/kitemviews/kfileitemmodel.h index 0f7926aae..c2dfd0167 100644 --- a/src/kitemviews/kfileitemmodel.h +++ b/src/kitemviews/kfileitemmodel.h @@ -382,8 +382,6 @@ private: int stringCompare(const QString& a, const QString& b, const QCollator& collator) const; - bool useMaximumUpdateInterval() const; - QList<QPair<int, QVariant> > nameRoleGroups() const; QList<QPair<int, QVariant> > sizeRoleGroups() const; QList<QPair<int, QVariant> > timeRoleGroups(const std::function<QDateTime(const ItemData *)> &fileTimeCb) const; diff --git a/src/kitemviews/kstandarditemlistwidget.cpp b/src/kitemviews/kstandarditemlistwidget.cpp index 15c01726f..c963b7196 100644 --- a/src/kitemviews/kstandarditemlistwidget.cpp +++ b/src/kitemviews/kstandarditemlistwidget.cpp @@ -1165,7 +1165,7 @@ void KStandardItemListWidget::updateIconsLayoutTextCache() do { QString lastTextLine = nameText.mid(line.textStart()); lastTextLine = m_customizedFontMetrics.elidedText(lastTextLine, - Qt::ElideRight, + Qt::ElideMiddle, elidingWidth); const QString elidedText = nameText.left(line.textStart()) + lastTextLine; nameTextInfo->staticText.setText(elidedText); @@ -1221,7 +1221,7 @@ void KStandardItemListWidget::updateIconsLayoutTextCache() textLine.setLineWidth(maxWidth); requiredWidth = textLine.naturalTextWidth(); if (requiredWidth > maxWidth) { - const QString elidedText = m_customizedFontMetrics.elidedText(text, Qt::ElideRight, maxWidth); + const QString elidedText = m_customizedFontMetrics.elidedText(text, Qt::ElideMiddle, maxWidth); textInfo->staticText.setText(elidedText); requiredWidth = m_customizedFontMetrics.width(elidedText); } else if (role == "rating") { @@ -1270,7 +1270,7 @@ void KStandardItemListWidget::updateCompactLayoutTextCache() qreal requiredWidth = m_customizedFontMetrics.width(text); if (requiredWidth > maxWidth) { requiredWidth = maxWidth; - const QString elidedText = m_customizedFontMetrics.elidedText(text, Qt::ElideRight, maxWidth); + const QString elidedText = m_customizedFontMetrics.elidedText(text, Qt::ElideMiddle, maxWidth); textInfo->staticText.setText(elidedText); } @@ -1327,7 +1327,7 @@ void KStandardItemListWidget::updateDetailsLayoutTextCache() } if (requiredWidth > availableTextWidth) { - text = m_customizedFontMetrics.elidedText(text, Qt::ElideRight, availableTextWidth); + text = m_customizedFontMetrics.elidedText(text, Qt::ElideMiddle, availableTextWidth); requiredWidth = m_customizedFontMetrics.width(text); } @@ -1463,33 +1463,16 @@ QPixmap KStandardItemListWidget::pixmapForIcon(const QString& name, const QStrin { static const QIcon fallbackIcon = QIcon::fromTheme(QStringLiteral("unknown")); - int requestedSize = size; - if (size <= KIconLoader::SizeSmall) { - requestedSize = KIconLoader::SizeSmall; - } else if (size <= KIconLoader::SizeSmallMedium) { - requestedSize = KIconLoader::SizeSmallMedium; - } else if (size <= KIconLoader::SizeMedium) { - requestedSize = KIconLoader::SizeMedium; - } else if (size <= KIconLoader::SizeLarge) { - requestedSize = KIconLoader::SizeLarge; - } else if (size <= KIconLoader::SizeHuge) { - requestedSize = KIconLoader::SizeHuge; - } else if (size <= KIconLoader::SizeEnormous) { - requestedSize = KIconLoader::SizeEnormous; - } else if (size <= KIconLoader::SizeEnormous * 2) { - requestedSize = KIconLoader::SizeEnormous * 2; - } size *= qApp->devicePixelRatio(); - requestedSize *= qApp->devicePixelRatio(); - const QString key = "KStandardItemListWidget:" % name % ":" % overlays.join(QStringLiteral(":")) % ":" % QString::number(size) % ":" % QString::number(mode); + const QString key = "KStandardItemListWidget:" % name % ":" % overlays.join(QLatin1Char(':')) % ":" % QString::number(size) % ":" % QString::number(mode); QPixmap pixmap; if (!QPixmapCache::find(key, pixmap)) { const QIcon icon = QIcon::fromTheme(name, fallbackIcon); - pixmap = icon.pixmap(requestedSize / qApp->devicePixelRatio(), requestedSize / qApp->devicePixelRatio(), mode); - if (requestedSize != size) { + pixmap = icon.pixmap(size / qApp->devicePixelRatio(), size / qApp->devicePixelRatio(), mode); + if (pixmap.width() != size || pixmap.height() != size) { KPixmapModifier::scale(pixmap, QSize(size, size)); } diff --git a/src/kitemviews/private/kbaloorolesprovider.cpp b/src/kitemviews/private/kbaloorolesprovider.cpp index f3671540d..8a2a64b31 100644 --- a/src/kitemviews/private/kbaloorolesprovider.cpp +++ b/src/kitemviews/private/kbaloorolesprovider.cpp @@ -155,5 +155,5 @@ QString KBalooRolesProvider::tagsFromValues(const QStringList& values) const QCollator coll; coll.setNumericMode(true); std::sort(alphabeticalOrderTags.begin(), alphabeticalOrderTags.end(), [&](const QString& s1, const QString& s2){ return coll.compare(s1, s2) < 0; }); - return alphabeticalOrderTags.join(QStringLiteral(", ")); + return alphabeticalOrderTags.join(QLatin1String(", ")); } diff --git a/src/kitemviews/private/kitemlistheaderwidget.cpp b/src/kitemviews/private/kitemlistheaderwidget.cpp index a3f3f521f..93d1389aa 100644 --- a/src/kitemviews/private/kitemlistheaderwidget.cpp +++ b/src/kitemviews/private/kitemlistheaderwidget.cpp @@ -93,7 +93,6 @@ void KItemListHeaderWidget::setColumns(const QList<QByteArray>& roles) { foreach (const QByteArray& role, roles) { if (!m_columnWidths.contains(role)) { - m_columnWidths.remove(role); m_preferredColumnWidths.remove(role); } } diff --git a/src/kitemviews/private/kitemlistkeyboardsearchmanager.cpp b/src/kitemviews/private/kitemlistkeyboardsearchmanager.cpp index ffa92a1cf..8f18b92bf 100644 --- a/src/kitemviews/private/kitemlistkeyboardsearchmanager.cpp +++ b/src/kitemviews/private/kitemlistkeyboardsearchmanager.cpp @@ -46,7 +46,7 @@ void KItemListKeyboardSearchManager::addKeys(const QString& keys) // Do not start a new search if the user pressed Space. Only add // it to the search string if a search is in progress already. - if (newSearch && keys == QLatin1String(" ")) { + if (newSearch && keys == QLatin1Char(' ')) { return; } diff --git a/src/middleclickactioneventfilter.cpp b/src/middleclickactioneventfilter.cpp index e0917850a..293e16e0c 100644 --- a/src/middleclickactioneventfilter.cpp +++ b/src/middleclickactioneventfilter.cpp @@ -21,6 +21,7 @@ #include <QAction> #include <QEvent> +#include <QMenu> #include <QMouseEvent> #include <QToolBar> @@ -39,16 +40,32 @@ bool MiddleClickActionEventFilter::eventFilter(QObject *watched, QEvent *event) if (me->button() == Qt::MiddleButton) { QToolBar *toolBar = qobject_cast<QToolBar *>(watched); - - QAction *action = toolBar->actionAt(me->pos()); - if (action) { - if (event->type() == QEvent::MouseButtonPress) { - m_lastMiddlePressedAction = action; - } else if (event->type() == QEvent::MouseButtonRelease) { - if (m_lastMiddlePressedAction == action) { - emit actionMiddleClicked(action); + if (toolBar) { + QAction *action = toolBar->actionAt(me->pos()); + if (action) { + if (event->type() == QEvent::MouseButtonPress) { + m_lastMiddlePressedAction = action; + } else if (event->type() == QEvent::MouseButtonRelease) { + if (m_lastMiddlePressedAction == action) { + emit actionMiddleClicked(action); + } + m_lastMiddlePressedAction = nullptr; + } + } + } + QMenu *menu = qobject_cast<QMenu *>(watched); + if (menu) { + QAction *action = menu->actionAt(me->pos()); + if (action) { + if (event->type() == QEvent::MouseButtonPress) { + m_lastMiddlePressedAction = action; + } else if (event->type() == QEvent::MouseButtonRelease) { + if (m_lastMiddlePressedAction == action) { + emit actionMiddleClicked(action); + return true; + } + m_lastMiddlePressedAction = nullptr; } - m_lastMiddlePressedAction = nullptr; } } } diff --git a/src/org.kde.dolphin.desktop b/src/org.kde.dolphin.desktop index e75eeab99..3e71051ef 100755 --- a/src/org.kde.dolphin.desktop +++ b/src/org.kde.dolphin.desktop @@ -72,7 +72,7 @@ GenericName[id]=Pengelola File GenericName[it]=Gestore dei file GenericName[ja]=ファイルマネージャ GenericName[ko]=파일 관리자 -GenericName[lt]=Failų tvarkyklė +GenericName[lt]=Failų tvarkytuvė GenericName[ml]=ഫയല് മാനേജര് GenericName[nb]=Filbehandler GenericName[nl]=Bestandsbeheerder diff --git a/src/panels/folders/folderspanel.cpp b/src/panels/folders/folderspanel.cpp index 020c41e55..01f338461 100644 --- a/src/panels/folders/folderspanel.cpp +++ b/src/panels/folders/folderspanel.cpp @@ -111,7 +111,7 @@ void FoldersPanel::rename(const KFileItem& item) bool FoldersPanel::urlChanged() { - if (!url().isValid() || url().scheme().contains(QStringLiteral("search"))) { + if (!url().isValid() || url().scheme().contains(QLatin1String("search"))) { // Skip results shown by a search, as possible identical // directory names are useless without parent-path information. return false; @@ -258,7 +258,7 @@ void FoldersPanel::slotRoleEditingFinished(int index, const QByteArray& role, co if (role == "text") { const KFileItem item = m_model->fileItem(index); const QString newName = value.toString(); - if (!newName.isEmpty() && newName != item.text() && newName != QLatin1String(".") && newName != QLatin1String("..")) { + if (!newName.isEmpty() && newName != item.text() && newName != QLatin1Char('.') && newName != QLatin1String("..")) { const QUrl oldUrl = item.url(); QUrl newUrl = oldUrl.adjusted(QUrl::RemoveFilename); newUrl.setPath(newUrl.path() + KIO::encodeFileName(newName)); diff --git a/src/panels/information/informationpanelcontent.cpp b/src/panels/information/informationpanelcontent.cpp index 363ad818d..2a8682a12 100644 --- a/src/panels/information/informationpanelcontent.cpp +++ b/src/panels/information/informationpanelcontent.cpp @@ -46,11 +46,18 @@ #include <QTimer> #include <QVBoxLayout> #include <QStyle> +#include <QPainter> +#include <QBitmap> +#include <QLinearGradient> +#include <QPolygon> #include "dolphin_informationpanelsettings.h" #include "phononwidget.h" #include "pixmapviewer.h" +const int PLAY_ARROW_SIZE = 24; +const int PLAY_ARROW_BORDER_SIZE = 2; + InformationPanelContent::InformationPanelContent(QWidget* parent) : QWidget(parent), m_item(), @@ -61,7 +68,8 @@ InformationPanelContent::InformationPanelContent(QWidget* parent) : m_nameLabel(nullptr), m_metaDataWidget(nullptr), m_metaDataArea(nullptr), - m_placesItemModel(nullptr) + m_placesItemModel(nullptr), + m_isVideo(false) { parent->installEventFilter(this); @@ -161,11 +169,47 @@ void InformationPanelContent::showItem(const KFileItem& item) if (item != m_item) { m_item = item; + m_preview->stopAnimatedImage(); refreshMetaData(); } refreshPreview(); } +void InformationPanelContent::refreshPixmapView() +{ + // If there is a preview job, kill it to prevent that we have jobs for + // multiple items running, and thus a race condition (bug 250787). + if (m_previewJob) { + m_previewJob->kill(); + } + + // try to get a preview pixmap from the item... + + // Mark the currently shown preview as outdated. This is done + // with a small delay to prevent a flickering when the next preview + // can be shown within a short timeframe. This timer is not started + // for directories, as directory previews might fail and return the + // same icon. + if (!m_item.isDir()) { + m_outdatedPreviewTimer->start(); + } + + QStringList plugins = KIO::PreviewJob::availablePlugins(); + m_previewJob = new KIO::PreviewJob(KFileItemList() << m_item, + QSize(m_preview->width(), m_preview->height()), + &plugins); + m_previewJob->setScaleType(KIO::PreviewJob::Unscaled); + m_previewJob->setIgnoreMaximumSize(m_item.isLocalFile()); + if (m_previewJob->uiDelegate()) { + KJobWidgets::setWindow(m_previewJob, this); + } + + connect(m_previewJob.data(), &KIO::PreviewJob::gotPreview, + this, &InformationPanelContent::showPreview); + connect(m_previewJob.data(), &KIO::PreviewJob::failed, + this, &InformationPanelContent::showIcon); +} + void InformationPanelContent::refreshPreview() { // If there is a preview job, kill it to prevent that we have jobs for @@ -174,11 +218,13 @@ void InformationPanelContent::refreshPreview() m_previewJob->kill(); } + m_preview->setCursor(Qt::ArrowCursor); + bool usePhonon = false; setNameLabelText(m_item.text()); if (InformationPanelSettings::previewsShown()) { const QUrl itemUrl = m_item.url(); - const bool isSearchUrl = itemUrl.scheme().contains(QStringLiteral("search")) && m_item.localPath().isEmpty(); + const bool isSearchUrl = itemUrl.scheme().contains(QLatin1String("search")) && m_item.localPath().isEmpty(); if (isSearchUrl) { m_preview->show(); @@ -188,56 +234,54 @@ void InformationPanelContent::refreshPreview() QIcon::fromTheme(QStringLiteral("nepomuk")).pixmap(KIconLoader::SizeEnormous, KIconLoader::SizeEnormous) ); } else { - // try to get a preview pixmap from the item... - - // Mark the currently shown preview as outdated. This is done - // with a small delay to prevent a flickering when the next preview - // can be shown within a short timeframe. This timer is not started - // for directories, as directory previews might fail and return the - // same icon. - if (!m_item.isDir()) { - m_outdatedPreviewTimer->start(); - } - - QStringList plugins = KIO::PreviewJob::availablePlugins(); - m_previewJob = new KIO::PreviewJob(KFileItemList() << m_item, - QSize(m_preview->width(), m_preview->height()), - &plugins); - m_previewJob->setScaleType(KIO::PreviewJob::Unscaled); - m_previewJob->setIgnoreMaximumSize(m_item.isLocalFile()); - if (m_previewJob->uiDelegate()) { - KJobWidgets::setWindow(m_previewJob, this); - } - connect(m_previewJob.data(), &KIO::PreviewJob::gotPreview, - this, &InformationPanelContent::showPreview); - connect(m_previewJob.data(), &KIO::PreviewJob::failed, - this, &InformationPanelContent::showIcon); + refreshPixmapView(); const QString mimeType = m_item.mimetype(); - const bool isVideo = mimeType.startsWith(QLatin1String("video/")); - const bool usePhonon = mimeType.startsWith(QLatin1String("audio/")) || isVideo; + const bool isAnimatedImage = m_preview->isAnimatedImage(itemUrl.toLocalFile()); + m_isVideo = !isAnimatedImage && mimeType.startsWith(QLatin1String("video/")); + usePhonon = m_isVideo || mimeType.startsWith(QLatin1String("audio/")); if (usePhonon) { + // change the cursor of the preview + m_preview->setCursor(Qt::PointingHandCursor); + m_preview->installEventFilter(m_phononWidget); - if (InformationPanelSettings::previewsAutoPlay() && isVideo) { - // hides the preview now to avoid flickering when the autoplay video starts - m_preview->hide(); - } else { - // the video won't play before the preview is displayed - m_preview->show(); - } + // if the video is playing, has been paused or stopped + // we don't need to update the preview/phonon widget states + // unless the previewed file has changed, + // or the setting previewshown has changed + if ((m_phononWidget->state() != Phonon::State::PlayingState && + m_phononWidget->state() != Phonon::State::PausedState && + m_phononWidget->state() != Phonon::State::StoppedState) || + m_item.targetUrl() != m_phononWidget->url() || + (!m_preview->isVisible() &&! m_phononWidget->isVisible())) { + + if (InformationPanelSettings::previewsAutoPlay() && m_isVideo) { + // hides the preview now to avoid flickering when the autoplay video starts + m_preview->hide(); + } else { + // the video won't play before the preview is displayed + m_preview->show(); + } - m_phononWidget->show(); - m_phononWidget->setUrl(m_item.targetUrl(), isVideo ? PhononWidget::MediaKind::Video : PhononWidget::MediaKind::Audio); - m_phononWidget->setVideoSize(m_preview->size()); + m_phononWidget->show(); + m_phononWidget->setUrl(m_item.targetUrl(), m_isVideo ? PhononWidget::MediaKind::Video : PhononWidget::MediaKind::Audio); + adjustWidgetSizes(parentWidget()->width()); + } } else { + if (isAnimatedImage) { + m_preview->setAnimatedImageFileName(itemUrl.toLocalFile()); + } // When we don't need it, hide the phonon widget first to avoid flickering m_phononWidget->hide(); m_preview->show(); + m_preview->removeEventFilter(m_phononWidget); + m_phononWidget->clearUrl(); } } } else { + m_preview->stopAnimatedImage(); m_preview->hide(); m_phononWidget->hide(); } @@ -265,6 +309,8 @@ void InformationPanelContent::showItems(const KFileItemList& items) m_previewJob->kill(); } + m_preview->stopAnimatedImage(); + m_preview->setPixmap( QIcon::fromTheme(QStringLiteral("dialog-information")).pixmap(KIconLoader::SizeEnormous, KIconLoader::SizeEnormous) ); @@ -319,10 +365,45 @@ void InformationPanelContent::showPreview(const KFileItem& item, const QPixmap& pixmap) { m_outdatedPreviewTimer->stop(); - Q_UNUSED(item); QPixmap p = pixmap; KIconLoader::global()->drawOverlays(item.overlays(), p, KIconLoader::Desktop); + + if (m_isVideo) { + // adds a play arrow + + // compute relative pixel positions + const int zeroX = static_cast<int>(p.width() / 2 - PLAY_ARROW_SIZE / 2 / devicePixelRatio()); + const int zeroY = static_cast<int>(p.height() / 2 - PLAY_ARROW_SIZE / 2 / devicePixelRatio()); + + QPolygon arrow; + arrow << QPoint(zeroX, zeroY); + arrow << QPoint(zeroX, zeroY + PLAY_ARROW_SIZE); + arrow << QPoint(zeroX + PLAY_ARROW_SIZE, zeroY + PLAY_ARROW_SIZE / 2); + + QPainterPath path; + path.addPolygon(arrow); + + QLinearGradient gradient(QPointF(zeroX, zeroY), + QPointF(zeroX + PLAY_ARROW_SIZE,zeroY + PLAY_ARROW_SIZE)); + + QColor whiteColor = Qt::white; + QColor blackColor = Qt::black; + gradient.setColorAt(0, whiteColor); + gradient.setColorAt(1, blackColor); + + QBrush brush(gradient); + + QPainter painter(&p); + + QPen pen(blackColor, PLAY_ARROW_BORDER_SIZE, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin); + painter.setPen(pen); + + painter.setRenderHint(QPainter::Antialiasing); + painter.drawPolygon(arrow); + painter.fillPath(path, brush); + } + m_preview->setPixmap(p); } @@ -343,6 +424,11 @@ KFileItemList InformationPanelContent::items() void InformationPanelContent::slotHasVideoChanged(bool hasVideo) { m_preview->setVisible(InformationPanelSettings::previewsShown() && !hasVideo); + if (m_preview->isVisible() && m_preview->size().width() != m_preview->pixmap().size().width()) { + // in case the information panel has been resized when the preview was not displayed + // we need to refresh its content + refreshPixmapView(); + } } void InformationPanelContent::setPreviewAutoPlay(bool autoPlay) { diff --git a/src/panels/information/informationpanelcontent.h b/src/panels/information/informationpanelcontent.h index 0d838b268..8daeb95b0 100644 --- a/src/panels/information/informationpanelcontent.h +++ b/src/panels/information/informationpanelcontent.h @@ -139,6 +139,11 @@ private: */ void adjustWidgetSizes(int width); + /** + * Refreshes the image in the PixmapViewer + */ + void refreshPixmapView(); + private: KFileItem m_item; @@ -154,6 +159,7 @@ private: QDialogButtonBox* m_configureButtons; PlacesItemModel* m_placesItemModel; + bool m_isVideo; }; #endif // INFORMATIONPANELCONTENT_H diff --git a/src/panels/information/phononwidget.cpp b/src/panels/information/phononwidget.cpp index 4ea2e6666..6911f79bd 100644 --- a/src/panels/information/phononwidget.cpp +++ b/src/panels/information/phononwidget.cpp @@ -60,7 +60,7 @@ PhononWidget::PhononWidget(QWidget *parent) : QWidget(parent), m_url(), m_playButton(nullptr), - m_stopButton(nullptr), + m_pauseButton(nullptr), m_topLayout(nullptr), m_media(nullptr), m_seekSlider(nullptr), @@ -95,6 +95,34 @@ QUrl PhononWidget::url() const return m_url; } +void PhononWidget::clearUrl() +{ + m_url.clear(); +} + +void PhononWidget::togglePlayback() +{ + if (m_media && m_media->state() == Phonon::State::PlayingState) { + m_media->pause(); + } else { + play(); + } +} + +bool PhononWidget::eventFilter(QObject *object, QEvent *event) +{ + Q_UNUSED(object) + if (event->type() == QEvent::MouseButtonPress) { + const QMouseEvent *mouseEvent = static_cast<QMouseEvent*>(event); + if (mouseEvent->button() == Qt::LeftButton) { + // toggle playback + togglePlayback(); + return true; + } + } + return false; +} + void PhononWidget::setVideoSize(const QSize& size) { if (m_videoSize != size) { @@ -124,11 +152,11 @@ void PhononWidget::showEvent(QShowEvent *event) controlsLayout->setSpacing(0); m_playButton = new QToolButton(this); - m_stopButton = new QToolButton(this); + m_pauseButton = new QToolButton(this); m_seekSlider = new Phonon::SeekSlider(this); controlsLayout->addWidget(m_playButton); - controlsLayout->addWidget(m_stopButton); + controlsLayout->addWidget(m_pauseButton); controlsLayout->addWidget(m_seekSlider); m_topLayout->addLayout(controlsLayout); @@ -142,12 +170,12 @@ void PhononWidget::showEvent(QShowEvent *event) m_playButton->setAutoRaise(true); connect(m_playButton, &QToolButton::clicked, this, &PhononWidget::play); - m_stopButton->setToolTip(i18n("stop")); - m_stopButton->setIconSize(buttonSize); - m_stopButton->setIcon(QIcon::fromTheme(QStringLiteral("media-playback-stop"))); - m_stopButton->setAutoRaise(true); - m_stopButton->hide(); - connect(m_stopButton, &QToolButton::clicked, this, &PhononWidget::stop); + m_pauseButton->setToolTip(i18n("pause")); + m_pauseButton->setIconSize(buttonSize); + m_pauseButton->setIcon(QIcon::fromTheme(QStringLiteral("media-playback-pause"))); + m_pauseButton->setAutoRaise(true); + m_pauseButton->hide(); + connect(m_pauseButton, &QToolButton::clicked, this, &PhononWidget::togglePlayback); m_seekSlider->setIconVisible(false); @@ -172,11 +200,11 @@ void PhononWidget::stateChanged(Phonon::State newstate) switch (newstate) { case Phonon::PlayingState: case Phonon::BufferingState: - m_stopButton->show(); m_playButton->hide(); + m_pauseButton->show(); break; default: - m_stopButton->hide(); + m_pauseButton->hide(); m_playButton->show(); break; } @@ -196,6 +224,7 @@ void PhononWidget::play() if (!m_videoPlayer) { m_videoPlayer = new EmbeddedVideoPlayer(this); + m_videoPlayer->setCursor(Qt::PointingHandCursor); m_videoPlayer->installEventFilter(this); m_topLayout->insertWidget(0, m_videoPlayer); Phonon::createPath(m_media, m_videoPlayer); @@ -227,6 +256,11 @@ void PhononWidget::finished() } } +Phonon::State PhononWidget::state() const +{ + return m_media == nullptr ? Phonon::State::StoppedState : m_media->state(); +} + void PhononWidget::stop() { if (m_media) { diff --git a/src/panels/information/phononwidget.h b/src/panels/information/phononwidget.h index b9e7d4f05..961443d2e 100644 --- a/src/panels/information/phononwidget.h +++ b/src/panels/information/phononwidget.h @@ -53,11 +53,14 @@ class PhononWidget : public QWidget void setUrl(const QUrl &url, MediaKind kind); QUrl url() const; + void clearUrl(); void setVideoSize(const QSize& size); QSize videoSize() const; + Phonon::State state() const; void setAutoPlay(bool autoPlay); + bool eventFilter(QObject *object, QEvent *event) override; signals: /** @@ -86,11 +89,13 @@ class PhononWidget : public QWidget void applyVideoSize(); private: + void togglePlayback(); + QUrl m_url; QSize m_videoSize; QToolButton *m_playButton; - QToolButton *m_stopButton; + QToolButton *m_pauseButton; QVBoxLayout *m_topLayout; Phonon::MediaObject *m_media; diff --git a/src/panels/information/pixmapviewer.cpp b/src/panels/information/pixmapviewer.cpp index 311995ec2..2601e82ae 100644 --- a/src/panels/information/pixmapviewer.cpp +++ b/src/panels/information/pixmapviewer.cpp @@ -21,14 +21,18 @@ #include <KIconLoader> +#include <QImageReader> +#include <QMovie> #include <QPainter> #include <QStyle> PixmapViewer::PixmapViewer(QWidget* parent, Transition transition) : QWidget(parent), + m_animatedImage(nullptr), m_transition(transition), m_animationStep(0), - m_sizeHint() + m_sizeHint(), + m_hasAnimatedImage(false) { setMinimumWidth(KIconLoader::SizeEnormous); setMinimumHeight(KIconLoader::SizeEnormous); @@ -52,6 +56,11 @@ void PixmapViewer::setPixmap(const QPixmap& pixmap) return; } + // Avoid flicker with static pixmap if an animated image is running + if (m_animatedImage && m_animatedImage->state() == QMovie::Running) { + return; + } + if ((m_transition != NoTransition) && (m_animation.state() == QTimeLine::Running)) { m_pendingPixmaps.enqueue(pixmap); if (m_pendingPixmaps.count() > 5) { @@ -65,15 +74,26 @@ void PixmapViewer::setPixmap(const QPixmap& pixmap) m_pixmap = pixmap; update(); - const bool animate = (m_transition != NoTransition) && - (m_pixmap.size() != m_oldPixmap.size()); - if (animate) { + const bool animateTransition = (m_transition != NoTransition) && + (m_pixmap.size() != m_oldPixmap.size()); + if (animateTransition) { m_animation.start(); + } else if (m_hasAnimatedImage) { + // If there is no transition animation but an animatedImage + // and it is not already running, start animating now + if (m_animatedImage->state() != QMovie::Running) { + m_animatedImage->setScaledSize(m_pixmap.size()); + m_animatedImage->start(); + } } } void PixmapViewer::setSizeHint(const QSize& size) { + if (m_animatedImage && size != m_sizeHint) { + m_animatedImage->stop(); + } + m_sizeHint = size; updateGeometry(); } @@ -83,13 +103,37 @@ QSize PixmapViewer::sizeHint() const return m_sizeHint; } +void PixmapViewer::setAnimatedImageFileName(const QString &fileName) +{ + if (!m_animatedImage) { + m_animatedImage = new QMovie(this); + connect(m_animatedImage, &QMovie::frameChanged, this, &PixmapViewer::updateAnimatedImageFrame); + } + + if (m_animatedImage->fileName() != fileName) { + m_animatedImage->stop(); + m_animatedImage->setFileName(fileName); + } + + m_hasAnimatedImage = m_animatedImage->isValid() && (m_animatedImage->frameCount() > 1); +} + + +QString PixmapViewer::animatedImageFileName() const +{ + if (!m_hasAnimatedImage) { + return QString(); + } + return m_animatedImage->fileName(); +} + void PixmapViewer::paintEvent(QPaintEvent* event) { QWidget::paintEvent(event); QPainter painter(this); - if (m_transition != NoTransition) { + if (m_transition != NoTransition || (m_hasAnimatedImage && m_animatedImage->state() != QMovie::Running)) { const float value = m_animation.currentValue(); const int scaledWidth = static_cast<int>((m_oldPixmap.width() * (1.0 - value)) + (m_pixmap.width() * value)); const int scaledHeight = static_cast<int>((m_oldPixmap.height() * (1.0 - value)) + (m_pixmap.height() * value)); @@ -118,8 +162,32 @@ void PixmapViewer::checkPendingPixmaps() m_pixmap = pixmap; update(); m_animation.start(); + } else if (m_hasAnimatedImage) { + m_animatedImage->setScaledSize(m_pixmap.size()); + m_animatedImage->start(); } else { m_oldPixmap = m_pixmap; } } +void PixmapViewer::updateAnimatedImageFrame() +{ + Q_ASSERT (m_animatedImage); + + m_pixmap = m_animatedImage->currentPixmap(); + update(); +} + +void PixmapViewer::stopAnimatedImage() +{ + if (m_hasAnimatedImage) { + m_animatedImage->stop(); + m_hasAnimatedImage = false; + } +} + +bool PixmapViewer::isAnimatedImage(const QString &fileName) +{ + const QByteArray imageFormat = QImageReader::imageFormat(fileName); + return !imageFormat.isEmpty() && QMovie::supportedFormats().contains(imageFormat); +} diff --git a/src/panels/information/pixmapviewer.h b/src/panels/information/pixmapviewer.h index 46e5cf5fc..37071045f 100644 --- a/src/panels/information/pixmapviewer.h +++ b/src/panels/information/pixmapviewer.h @@ -26,6 +26,7 @@ #include <QWidget> class QPaintEvent; +class QMovie; /** * @brief Widget which shows a pixmap centered inside the boundaries. @@ -73,20 +74,33 @@ public: void setSizeHint(const QSize& size); QSize sizeHint() const override; + void setAnimatedImageFileName(const QString& fileName); + QString animatedImageFileName() const; + + void stopAnimatedImage(); + + /** + * Checks if \a fileName contains an animated image supported by QMovie. + */ + static bool isAnimatedImage(const QString &fileName); + protected: void paintEvent(QPaintEvent* event) override; private Q_SLOTS: void checkPendingPixmaps(); + void updateAnimatedImageFrame(); private: QPixmap m_pixmap; QPixmap m_oldPixmap; + QMovie* m_animatedImage; QQueue<QPixmap> m_pendingPixmaps; QTimeLine m_animation; Transition m_transition; int m_animationStep; QSize m_sizeHint; + bool m_hasAnimatedImage; }; inline QPixmap PixmapViewer::pixmap() const diff --git a/src/panels/places/placespanel.cpp b/src/panels/places/placespanel.cpp index a2f0e0655..8469399a7 100644 --- a/src/panels/places/placespanel.cpp +++ b/src/panels/places/placespanel.cpp @@ -77,7 +77,7 @@ void PlacesPanel::proceedWithTearDown() bool PlacesPanel::urlChanged() { - if (!url().isValid() || url().scheme().contains(QStringLiteral("search"))) { + if (!url().isValid() || url().scheme().contains(QLatin1String("search"))) { // Skip results shown by a search, as possible identical // directory names are useless without parent-path information. return false; @@ -232,7 +232,7 @@ void PlacesPanel::slotItemContextMenuRequested(int index, const QPointF& pos) 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()); @@ -293,7 +293,7 @@ void PlacesPanel::slotViewContextMenuRequested(const QPointF& pos) 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()); @@ -358,7 +358,7 @@ QAction *PlacesPanel::buildGroupContextMenu(QMenu *menu, int index) } 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)); diff --git a/src/panels/terminal/terminalpanel.cpp b/src/panels/terminal/terminalpanel.cpp index 52d2a77df..86974d200 100644 --- a/src/panels/terminal/terminalpanel.cpp +++ b/src/panels/terminal/terminalpanel.cpp @@ -82,16 +82,14 @@ void TerminalPanel::terminalExited() bool TerminalPanel::isHiddenInVisibleWindow() const { return parentWidget() - && parentWidget()->isHidden() - && m_terminal - && !hasProgramRunning(); + && parentWidget()->isHidden(); } void TerminalPanel::dockVisibilityChanged() { // Only react when the DockWidget itself (not some parent) is hidden. This way we don't // respond when e.g. Dolphin is minimized. - if (isHiddenInVisibleWindow()) { + if (isHiddenInVisibleWindow() && m_terminal && !hasProgramRunning()) { // Make sure that the following "cd /" command will not affect the view. disconnect(m_konsolePart, SIGNAL(currentDirectoryChanged(QString)), this, SLOT(slotKonsolePartCurrentDirectoryChanged(QString))); diff --git a/src/search/dolphinfacetswidget.cpp b/src/search/dolphinfacetswidget.cpp index 7c0e97f86..e4388f2e7 100644 --- a/src/search/dolphinfacetswidget.cpp +++ b/src/search/dolphinfacetswidget.cpp @@ -150,7 +150,7 @@ QString DolphinFacetsWidget::ratingTerm() const terms << QStringLiteral("modified>=%1").arg(date.toString(Qt::ISODate)); } - return terms.join(QStringLiteral(" AND ")); + return terms.join(QLatin1String(" AND ")); } QString DolphinFacetsWidget::facetType() const diff --git a/src/search/dolphinsearchbox.cpp b/src/search/dolphinsearchbox.cpp index d846e5b6c..f3dd20462 100644 --- a/src/search/dolphinsearchbox.cpp +++ b/src/search/dolphinsearchbox.cpp @@ -505,7 +505,7 @@ QUrl DolphinSearchBox::balooUrlForSearching() const query.setIncludeFolder(m_searchPath.toLocalFile()); } - query.setSearchString(queryStrings.join(QStringLiteral(" "))); + query.setSearchString(queryStrings.join(QLatin1Char(' '))); return query.toSearchUrl(i18nc("@title UDS_DISPLAY_NAME for a KIO directory listing. %1 is the query the user entered.", "Query Results from '%1'", text)); diff --git a/src/settings/applyviewpropsjob.cpp b/src/settings/applyviewpropsjob.cpp index b274bb8fb..183c85225 100644 --- a/src/settings/applyviewpropsjob.cpp +++ b/src/settings/applyviewpropsjob.cpp @@ -54,7 +54,7 @@ void ApplyViewPropsJob::slotEntries(KIO::Job*, const KIO::UDSEntryList& list) { foreach (const KIO::UDSEntry& entry, list) { const QString name = entry.stringValue(KIO::UDSEntry::UDS_NAME); - if (name != QLatin1String(".") && name != QLatin1String("..") && entry.isDir()) { + if (name != QLatin1Char('.') && name != QLatin1String("..") && entry.isDir()) { ++m_progress; QUrl url(m_dir); diff --git a/src/settings/dolphinsettingsdialog.cpp b/src/settings/dolphinsettingsdialog.cpp index f4da53c9d..01cfd9f86 100644 --- a/src/settings/dolphinsettingsdialog.cpp +++ b/src/settings/dolphinsettingsdialog.cpp @@ -61,35 +61,35 @@ DolphinSettingsDialog::DolphinSettingsDialog(const QUrl& url, QWidget* parent) : GeneralSettingsPage* generalSettingsPage = new GeneralSettingsPage(url, this); KPageWidgetItem* generalSettingsFrame = addPage(generalSettingsPage, i18nc("@title:group General settings", "General")); - generalSettingsFrame->setIcon(QIcon::fromTheme(QStringLiteral("view-preview"))); + generalSettingsFrame->setIcon(QIcon::fromTheme(QStringLiteral("system-file-manager"))); connect(generalSettingsPage, &GeneralSettingsPage::changed, this, &DolphinSettingsDialog::enableApply); // Startup StartupSettingsPage* startupSettingsPage = new StartupSettingsPage(url, this); KPageWidgetItem* startupSettingsFrame = addPage(startupSettingsPage, i18nc("@title:group", "Startup")); - startupSettingsFrame->setIcon(QIcon::fromTheme(QStringLiteral("go-home"))); + startupSettingsFrame->setIcon(QIcon::fromTheme(QStringLiteral("preferences-desktop-launch-feedback"))); connect(startupSettingsPage, &StartupSettingsPage::changed, this, &DolphinSettingsDialog::enableApply); // View Modes ViewSettingsPage* viewSettingsPage = new ViewSettingsPage(this); KPageWidgetItem* viewSettingsFrame = addPage(viewSettingsPage, i18nc("@title:group", "View Modes")); - viewSettingsFrame->setIcon(QIcon::fromTheme(QStringLiteral("view-choose"))); + viewSettingsFrame->setIcon(QIcon::fromTheme(QStringLiteral("preferences-desktop-icons"))); connect(viewSettingsPage, &ViewSettingsPage::changed, this, &DolphinSettingsDialog::enableApply); // Navigation NavigationSettingsPage* navigationSettingsPage = new NavigationSettingsPage(this); KPageWidgetItem* navigationSettingsFrame = addPage(navigationSettingsPage, i18nc("@title:group", "Navigation")); - navigationSettingsFrame->setIcon(QIcon::fromTheme(QStringLiteral("edit-select"))); + navigationSettingsFrame->setIcon(QIcon::fromTheme(QStringLiteral("preferences-desktop-navigation"))); connect(navigationSettingsPage, &NavigationSettingsPage::changed, this, &DolphinSettingsDialog::enableApply); // Services ServicesSettingsPage* servicesSettingsPage = new ServicesSettingsPage(this); KPageWidgetItem* servicesSettingsFrame = addPage(servicesSettingsPage, i18nc("@title:group", "Services")); - servicesSettingsFrame->setIcon(QIcon::fromTheme(QStringLiteral("flag"))); + servicesSettingsFrame->setIcon(QIcon::fromTheme(QStringLiteral("preferences-system-services"))); connect(servicesSettingsPage, &ServicesSettingsPage::changed, this, &DolphinSettingsDialog::enableApply); // Trash @@ -97,7 +97,7 @@ DolphinSettingsDialog::DolphinSettingsDialog(const QUrl& url, QWidget* parent) : if (trashSettingsPage) { KPageWidgetItem* trashSettingsFrame = addPage(trashSettingsPage, i18nc("@title:group", "Trash")); - trashSettingsFrame->setIcon(QIcon::fromTheme(QStringLiteral("trash-empty"))); + trashSettingsFrame->setIcon(QIcon::fromTheme(QStringLiteral("user-trash"))); connect(trashSettingsPage, &TrashSettingsPage::changed, this, &DolphinSettingsDialog::enableApply); } diff --git a/src/settings/general/behaviorsettingspage.cpp b/src/settings/general/behaviorsettingspage.cpp index c7a909eca..df7ea2113 100644 --- a/src/settings/general/behaviorsettingspage.cpp +++ b/src/settings/general/behaviorsettingspage.cpp @@ -50,7 +50,7 @@ BehaviorSettingsPage::BehaviorSettingsPage(const QUrl& url, QWidget* parent) : // View properties m_globalViewProps = new QRadioButton(i18nc("@option:radio", "Use common properties for all folders")); m_localViewProps = new QRadioButton(i18nc("@option:radio", "Remember properties for each folder")); - m_localViewProps->setToolTip(i18nc("@info", "Dolphin will create an hidden .directory file in each folder you change view properties for.")); + m_localViewProps->setToolTip(i18nc("@info", "Dolphin will create a hidden .directory file in each folder you change view properties for.")); QButtonGroup* viewGroup = new QButtonGroup(this); viewGroup->addButton(m_globalViewProps); diff --git a/src/settings/general/confirmationssettingspage.cpp b/src/settings/general/confirmationssettingspage.cpp index 1d0eebdd3..dd4d60f3b 100644 --- a/src/settings/general/confirmationssettingspage.cpp +++ b/src/settings/general/confirmationssettingspage.cpp @@ -25,14 +25,23 @@ #include <KLocalizedString> #include <QCheckBox> +#include <QComboBox> #include <QLabel> +#include <QHBoxLayout> #include <QVBoxLayout> namespace { + enum ScriptExecution + { + AlwaysAsk = 0, + Open = 1, + Execute = 2 + }; + const bool ConfirmEmptyTrash = true; const bool ConfirmTrash = false; const bool ConfirmDelete = true; - const bool ConfirmScriptExecution = true; + const int ConfirmScriptExecution = ScriptExecution::AlwaysAsk; } ConfirmationsSettingsPage::ConfirmationsSettingsPage(QWidget* parent) : @@ -58,8 +67,6 @@ ConfirmationsSettingsPage::ConfirmationsSettingsPage(QWidget* parent) : "Emptying trash"), this); m_confirmDelete = new QCheckBox(i18nc("@option:check Ask for confirmation when", "Deleting files or folders"), this); - m_confirmScriptExecution = new QCheckBox(i18nc("@option:check Ask for confirmation when", - "Executing scripts or desktop files"), this); QLabel* confirmLabelDolphin = new QLabel(i18nc("@title:group", "Ask for confirmation in Dolphin when:"), this); confirmLabelDolphin->setWordWrap(true); @@ -72,11 +79,19 @@ ConfirmationsSettingsPage::ConfirmationsSettingsPage(QWidget* parent) : "Closing windows with a program running in the Terminal panel"), this); #endif + QHBoxLayout* executableScriptLayout = new QHBoxLayout(); + QLabel* executableScriptLabel = new QLabel(i18nc("@title:group", "When opening an executable file:"), this); + confirmLabelKde->setWordWrap(true); + executableScriptLayout->addWidget(executableScriptLabel); + + m_confirmScriptExecution = new QComboBox(this); + m_confirmScriptExecution->addItems({i18n("Always ask"), i18n("Open in application"), i18n("Run script")}); + executableScriptLayout->addWidget(m_confirmScriptExecution); + topLayout->addWidget(confirmLabelKde); topLayout->addWidget(m_confirmMoveToTrash); topLayout->addWidget(m_confirmEmptyTrash); topLayout->addWidget(m_confirmDelete); - topLayout->addWidget(m_confirmScriptExecution); topLayout->addSpacing(Dolphin::VERTICAL_SPACER_HEIGHT); topLayout->addWidget(confirmLabelDolphin); topLayout->addWidget(m_confirmClosingMultipleTabs); @@ -85,6 +100,9 @@ ConfirmationsSettingsPage::ConfirmationsSettingsPage(QWidget* parent) : topLayout->addWidget(m_confirmClosingTerminalRunningProgram); #endif + topLayout->addSpacing(Dolphin::VERTICAL_SPACER_HEIGHT); + topLayout->addLayout(executableScriptLayout); + topLayout->addStretch(); loadSettings(); @@ -92,7 +110,7 @@ ConfirmationsSettingsPage::ConfirmationsSettingsPage(QWidget* parent) : connect(m_confirmMoveToTrash, &QCheckBox::toggled, this, &ConfirmationsSettingsPage::changed); connect(m_confirmEmptyTrash, &QCheckBox::toggled, this, &ConfirmationsSettingsPage::changed); connect(m_confirmDelete, &QCheckBox::toggled, this, &ConfirmationsSettingsPage::changed); - connect(m_confirmScriptExecution, &QCheckBox::toggled, this, &ConfirmationsSettingsPage::changed); + connect(m_confirmScriptExecution, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &ConfirmationsSettingsPage::changed); connect(m_confirmClosingMultipleTabs, &QCheckBox::toggled, this, &ConfirmationsSettingsPage::changed); #ifdef HAVE_TERMINAL @@ -113,10 +131,17 @@ void ConfirmationsSettingsPage::applySettings() confirmationGroup.writeEntry("ConfirmDelete", m_confirmDelete->isChecked()); KConfigGroup scriptExecutionGroup(kioConfig, "Executable scripts"); - if (m_confirmScriptExecution->isChecked()) { + const int index = m_confirmScriptExecution->currentIndex(); + switch (index) { + case ScriptExecution::AlwaysAsk: scriptExecutionGroup.writeEntry("behaviourOnLaunch", "alwaysAsk"); - } else { + break; + case ScriptExecution::Open: scriptExecutionGroup.writeEntry("behaviourOnLaunch", "dontAsk"); + break; + case ScriptExecution::Execute: + scriptExecutionGroup.writeEntry("behaviourOnLaunch", "execute"); + break; } kioConfig->sync(); @@ -140,7 +165,7 @@ void ConfirmationsSettingsPage::restoreDefaults() m_confirmMoveToTrash->setChecked(ConfirmTrash); m_confirmEmptyTrash->setChecked(ConfirmEmptyTrash); m_confirmDelete->setChecked(ConfirmDelete); - m_confirmScriptExecution->setChecked(ConfirmScriptExecution); + m_confirmScriptExecution->setCurrentIndex(ConfirmScriptExecution); } void ConfirmationsSettingsPage::loadSettings() @@ -153,7 +178,13 @@ void ConfirmationsSettingsPage::loadSettings() const KConfigGroup scriptExecutionGroup(KSharedConfig::openConfig(QStringLiteral("kiorc")), "Executable scripts"); const QString value = scriptExecutionGroup.readEntry("behaviourOnLaunch", "alwaysAsk"); - m_confirmScriptExecution->setChecked(value == QLatin1String("alwaysAsk")); + if (value == QLatin1String("dontAsk")) { + m_confirmScriptExecution->setCurrentIndex(ScriptExecution::Open); + } else if (value == QLatin1String("execute")) { + m_confirmScriptExecution->setCurrentIndex(ScriptExecution::Execute); + } else /* if (value == QLatin1String("alwaysAsk"))*/ { + m_confirmScriptExecution->setCurrentIndex(ScriptExecution::AlwaysAsk); + } m_confirmClosingMultipleTabs->setChecked(GeneralSettings::confirmClosingMultipleTabs()); diff --git a/src/settings/general/confirmationssettingspage.h b/src/settings/general/confirmationssettingspage.h index 52b101b2a..c15afdc38 100644 --- a/src/settings/general/confirmationssettingspage.h +++ b/src/settings/general/confirmationssettingspage.h @@ -23,6 +23,7 @@ #include "settings/settingspagebase.h" class QCheckBox; +class QComboBox; /** * @brief Page for the enabling or disabling confirmation dialogs. @@ -54,7 +55,7 @@ private: #endif QCheckBox* m_confirmClosingMultipleTabs; - QCheckBox* m_confirmScriptExecution; + QComboBox* m_confirmScriptExecution; }; #endif diff --git a/src/settings/kcm/kcmdolphingeneral.desktop b/src/settings/kcm/kcmdolphingeneral.desktop index ac3956144..75a5aaf0c 100644 --- a/src/settings/kcm/kcmdolphingeneral.desktop +++ b/src/settings/kcm/kcmdolphingeneral.desktop @@ -20,7 +20,7 @@ Name[id]=Dolphin Umum Name[it]=Impostazioni generali di Dolphin Name[ja]=Dolphin 全般 Name[ko]=Dolphin 일반 -Name[lt]=Dolphin bendrieji +Name[lt]=Dolphin bendrosios Name[ml]=പൊതു സജ്ജീകരണങ്ങള് Name[nb]=Dolphin generelt Name[nl]=Dolphin algemeen @@ -65,7 +65,7 @@ Comment[id]=Layanan ini memungkinkan konfigurasi pengaturan umum Dolphin. Comment[it]=Questo servizio permette di configurare le impostazioni generali di Dolphin. Comment[ja]=Dolphin の全般的な設定を行います Comment[ko]=이 서비스는 일반 Dolphin 설정을 담당합니다. -Comment[lt]=Ši tarnyba leidžia konfigūruoti Dolphin bendrąsias nuostatas. +Comment[lt]=Ši paslauga leidžia bendrųjų Dolphin nuostatų konfigūravimą. Comment[ml]=പൊതുവായ ഡോള്ഫിന് സജ്ജീകരണങ്ങള് ക്രമീകരിയ്ക്കാന് ഈ സേവനം അനുവദിക്കുന്നു. Comment[nb]=Med denne tjenesten kan du sette opp generelle innstillinger for Dolphin. Comment[nl]=Met deze dienst kunt u algemene Dolphin-instellingen configureren. @@ -89,7 +89,7 @@ Comment[zh_CN]=此服务允许您配置 Dolphin 的常规设置。 Comment[zh_TW]=此服務允許設定 Dolphin 的一般設定。 [Desktop Entry] -Icon=system-run +Icon=system-file-manager Type=Service X-KDE-ServiceTypes=KCModule Exec=kcmshell5 kcmdolphingeneral @@ -122,7 +122,7 @@ Name[id]=Umum Name[it]=Generale Name[ja]=全般 Name[ko]=일반 -Name[lt]=Bendra +Name[lt]=Bendrosios Name[ml]=പൊതുവായതു് Name[nb]=Generelt Name[nl]=Algemeen @@ -166,7 +166,7 @@ Comment[id]=Konfigurasikan pengaturan pengelola file umum Comment[it]=Configura le impostazioni generali del gestore dei file Comment[ja]=ファイルマネージャの全般的な設定を行います Comment[ko]=일반 파일 관리자 설정 -Comment[lt]=Bendrųjų failų tvarkyklės nuostatų konfigūravimas +Comment[lt]=Konfigūruoti bendras failų tvarkytuvės nuostatas Comment[ml]=ഫയൽ മാനേജറിന്റെ പൊതുവായ സജ്ജീകരണങ്ങള് ക്രമീകരിയ്ക്കുക Comment[nb]=Sett opp generelle innstillinger for filbehandleren Comment[nl]=Algemene bestandsbeheerderinstellingen configureren @@ -211,7 +211,7 @@ X-KDE-Keywords[id]=pengelola file X-KDE-Keywords[it]=gestore dei file X-KDE-Keywords[ja]=ファイルマネージャ X-KDE-Keywords[ko]=파일 관리자 -X-KDE-Keywords[lt]=Failų tvarkyklė +X-KDE-Keywords[lt]=failų tvarkytuvė X-KDE-Keywords[ml]=ഫയൽ മാനേജർ X-KDE-Keywords[nb]=filbehandler X-KDE-Keywords[nl]=bestandsbeheerder diff --git a/src/settings/kcm/kcmdolphinnavigation.desktop b/src/settings/kcm/kcmdolphinnavigation.desktop index f6686fbc8..24bf0dff5 100644 --- a/src/settings/kcm/kcmdolphinnavigation.desktop +++ b/src/settings/kcm/kcmdolphinnavigation.desktop @@ -20,7 +20,7 @@ Name[id]=Navigasi Dolphin Name[it]=Navigazione di Dolphin Name[ja]=Dolphin ナビゲーション Name[ko]=Dolphin 탐색 -Name[lt]=Dolphin navigacija +Name[lt]=Dolphin naršymas Name[ml]=ഡോള്ഫിന് നാവിഗേഷന് Name[nb]=Navigasjon i Dolphin Name[nl]=Dolphin-navigatie @@ -65,7 +65,7 @@ Comment[id]=Layanan ini memungkinkan konfigurasi navigasi Dolphin. Comment[it]=Questo servizio permette di configurare la navigazione con Dolphin. Comment[ja]=Dolphin でのナビゲーションを設定します Comment[ko]=이 서비스는 Dolphin 탐색을 설정합니다. -Comment[lt]=Ši tarnyba leidžia konfigūruoti Dolphin navigaciją. +Comment[lt]=Ši paslauga leidžia Dolphin naršymo konfigūravimą. Comment[ml]=ഡോള്ഫിന് നാവിഗേഷൻ ക്രമീകരിയ്ക്കാന് ഈ സേവനം അനുവദിയ്ക്കുന്നു. Comment[nb]=Med denne tjenesten kan du sette opp navigasjon for Dolphin. Comment[nl]=Met deze dienst kunt u Dolphin-navigatie configureren. @@ -89,7 +89,7 @@ Comment[zh_CN]=此服务允许您配置 Dolphin 的导航。 Comment[zh_TW]=此服務允許設定 Dolphin 的導覽。 [Desktop Entry] -Icon=input-mouse +Icon=preferences-desktop-navigation Type=Service X-KDE-ServiceTypes=KCModule Exec=kcmshell5 kcmdolphinnavigation @@ -121,7 +121,7 @@ Name[id]=Navigasi Name[it]=Navigazione Name[ja]=ナビゲーション Name[ko]=탐색 -Name[lt]=Navigacija +Name[lt]=Naršymas Name[ml]=നാവിഗേഷന് Name[nb]=Navigasjon Name[nl]=Navigatie @@ -166,7 +166,7 @@ Comment[id]=Konfigurasikan navigasi pengelola file Comment[it]=Configura la navigazione col gestore dei file Comment[ja]=ファイルマネージャでのナビゲーションを設定します Comment[ko]=파일 관리자 탐색 설정 -Comment[lt]=Konfigūruokite failų tvarkyklės navigaciją +Comment[lt]=Konfigūruoti failų tvarkytuvės naršymą Comment[ml]=ഫയല് മാനേജർ നാവിഗേഷൻ ക്രമീകരിയ്ക്കുക Comment[nb]=Sett opp navigasjon i filbehandleren Comment[nl]=Bestandsbeheerdernavigatie configureren @@ -212,7 +212,7 @@ X-KDE-Keywords[id]=pengelola file X-KDE-Keywords[it]=gestore dei file X-KDE-Keywords[ja]=ファイルマネージャ X-KDE-Keywords[ko]=파일 관리자 -X-KDE-Keywords[lt]=Failų tvarkyklė +X-KDE-Keywords[lt]=failų tvarkytuvė X-KDE-Keywords[ml]=ഫയൽ മാനേജർ X-KDE-Keywords[nb]=filbehandler X-KDE-Keywords[nl]=bestandsbeheerder diff --git a/src/settings/kcm/kcmdolphinservices.desktop b/src/settings/kcm/kcmdolphinservices.desktop index 07ac89c52..e3330af5d 100644 --- a/src/settings/kcm/kcmdolphinservices.desktop +++ b/src/settings/kcm/kcmdolphinservices.desktop @@ -20,7 +20,7 @@ Name[id]=Layanan Dolphin Name[it]=Servizi di Dolphin Name[ja]=Dolphin サービス Name[ko]=Dolphin 서비스 -Name[lt]=Dolphin tarnybos +Name[lt]=Dolphin paslaugos Name[ml]=ഡോള്ഫിന് സേവനങ്ങള് Name[nb]=Dolphin-tjenester Name[nl]=Dolphin-services @@ -44,7 +44,7 @@ Name[zh_CN]=Dolphin 服务 Name[zh_TW]=Dolphin 服務 [Desktop Entry] -Icon=services +Icon=preferences-system-services Type=Service X-KDE-ServiceTypes=KCModule Exec=kcmshell5 kcmdolphinservices @@ -120,7 +120,7 @@ Comment[id]=Konfigurasikan layanan pengelola file Comment[it]=Configura i servizi del gestore dei file Comment[ja]=ファイルマネージャのサービスを設定します Comment[ko]=파일 관리자 서비스 설정 -Comment[lt]=Konfigūruokite failų tvarkyklės tarnybas +Comment[lt]=Konfigūruoti failų tvarkytuvės paslaugas Comment[ml]=ഫയല് മാനേജർ സേവനങ്ങള് ക്രമീകരിയ്ക്കുക Comment[nb]=Sett opp tjenester i filbehandleren Comment[nl]=Bestandsbeheerderservices configureren @@ -165,7 +165,7 @@ X-KDE-Keywords[id]=pengelola file X-KDE-Keywords[it]=gestore dei file X-KDE-Keywords[ja]=ファイルマネージャ X-KDE-Keywords[ko]=파일 관리자 -X-KDE-Keywords[lt]=Failų tvarkyklė +X-KDE-Keywords[lt]=failų tvarkytuvė X-KDE-Keywords[ml]=ഫയൽ മാനേജർ X-KDE-Keywords[nb]=filbehandler X-KDE-Keywords[nl]=bestandsbeheerder diff --git a/src/settings/kcm/kcmdolphinviewmodes.desktop b/src/settings/kcm/kcmdolphinviewmodes.desktop index c50fa3800..f093f49a1 100644 --- a/src/settings/kcm/kcmdolphinviewmodes.desktop +++ b/src/settings/kcm/kcmdolphinviewmodes.desktop @@ -20,7 +20,7 @@ Name[id]=Mode Tampilan Dolphin Name[it]=Viste di Dolphin Name[ja]=Dolphin 表示モード Name[ko]=Dolphin 보기 모드 -Name[lt]=Dolphin rodymo būdai +Name[lt]=Dolphin rodinio veiksenos Name[ml]=ഡോള്ഫിന് അവതരണദശകള് Name[nb]=Dolphin visningsmåter Name[nl]=Dolphin-weergavemodussen @@ -65,7 +65,7 @@ Comment[id]=Layanan ini memungkinkan konfigurasi mode tampilan Dolphin. Comment[it]=Questo servizio permette di configurare le viste di Dolphin. Comment[ja]=Dolphin の表示モードを設定します Comment[ko]=이 서비스는 Dolphin 보기 모드를 설정합니다. -Comment[lt]=Ši tarnyba leidžia konfigūruoti Dolphin rodymo būdus. +Comment[lt]=Ši paslauga leidžia Dolphin rodinio veiksenų konfigūravimą. Comment[ml]=ഡോള്ഫിന് അവതരണദശകള് ക്രമീകരിയ്ക്കാന് ഈ സേവനം അനുവദിയ്ക്കുന്നു. Comment[nb]=Med denne tjenesten kan du sette opp Dolphins visningsmåter. Comment[nl]=Met deze dienst kunt u Dolphin-weergavemodussen configureren. @@ -89,7 +89,7 @@ Comment[zh_CN]=此服务允许您配置 Dolphin 的视图模式。 Comment[zh_TW]=此服務允許設定 Dolphin 的檢視模式。 [Desktop Entry] -Icon=view-choose +Icon=preferences-desktop-icons Type=Service X-KDE-ServiceTypes=KCModule Exec=kcmshell5 kcmdolphinviewmodes @@ -120,7 +120,7 @@ Name[id]=Mode Tampilan Name[it]=Viste Name[ja]=表示モード Name[ko]=보기 모드 -Name[lt]=Rodymo būdai +Name[lt]=Rodinio veiksenos Name[ml]=അവതരണ ദശകള് Name[nb]=Visningsmåter Name[nl]=Weergavemodi @@ -165,7 +165,7 @@ Comment[id]=Konfigurasikan mode tampilan pengelola file Comment[it]=Configura le viste del gestore dei file Comment[ja]=ファイルマネージャの表示モードを設定します Comment[ko]=파일 관리자 보기 모드 설정 -Comment[lt]=Failų tvarkyklės rodymo būdų konfigūravimas +Comment[lt]=Konfigūruoti failų tvarkytuvės rodinio veiksenas Comment[ml]=ഫയല് മാനേജറിന്റെ അവതരണ ദശകള് ക്രമീകരിയ്ക്കുക Comment[nb]=Tilpass filbehandlerens visningsmåter Comment[nl]=Bestandsbeheerderweergavemodussen configureren @@ -211,7 +211,7 @@ X-KDE-Keywords[id]=pengelola file X-KDE-Keywords[it]=gestore dei file X-KDE-Keywords[ja]=ファイルマネージャ X-KDE-Keywords[ko]=파일 관리자 -X-KDE-Keywords[lt]=Failų tvarkyklė +X-KDE-Keywords[lt]=failų tvarkytuvė X-KDE-Keywords[ml]=ഫയൽ മാനേജർ X-KDE-Keywords[nb]=filbehandler X-KDE-Keywords[nl]=bestandsbeheerder diff --git a/src/settings/services/servicemenuinstaller/servicemenuinstaller.cpp b/src/settings/services/servicemenuinstaller/servicemenuinstaller.cpp index 037874539..1144a50b8 100644 --- a/src/settings/services/servicemenuinstaller/servicemenuinstaller.cpp +++ b/src/settings/services/servicemenuinstaller/servicemenuinstaller.cpp @@ -24,6 +24,7 @@ #include <QDir> #include <QDirIterator> #include <QCommandLineParser> +#include <QMimeDatabase> #include <KLocalizedString> @@ -42,41 +43,6 @@ Q_NORETURN void fail(const QString &str) exit(1); } -bool evaluateShell(const QString &program, const QStringList &arguments, QString &output, QString &errorText) -{ - QProcess process; - process.start(program, arguments, QIODevice::ReadOnly); - if (!process.waitForStarted()) { - fail(i18n("Failed to run process: %1 %2", program, arguments.join(" "))); - } - - if (!process.waitForFinished()) { - fail(i18n("Process did not finish in reasonable time: %1 %2", program, arguments.join(" "))); - } - - const auto stdoutResult = QString::fromUtf8(process.readAllStandardOutput()).trimmed(); - const auto stderrResult = QString::fromUtf8(process.readAllStandardError()).trimmed(); - - if (process.exitStatus() == QProcess::NormalExit && process.exitCode() == 0) { - output = stdoutResult; - return true; - } else { - errorText = stderrResult + stdoutResult; - return false; - } -} - -QString mimeType(const QString &path) -{ - QString result; - QString errorText; - if (evaluateShell("xdg-mime", QStringList{"query", "filetype", path}, result, errorText)) { - return result; - } else { - fail(i18n("Failed to run xdg-mime %1: %2", path, errorText)); - } -} - QString getServiceMenusDir() { const QString dataLocation = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation); @@ -114,7 +80,7 @@ void runUncompress(const QString &inputPath, const QString &outputPath) { "multipart/x-zip"}, UncompressCommand{"unzip", QStringList{}, QStringList{"-d"}}}); - const auto mime = mimeType(inputPath); + const auto mime = QMimeDatabase().mimeTypeForFile(inputPath).name(); UncompressCommand command{}; for (const auto &pair : mimeTypeToCommand) { diff --git a/src/settings/services/servicessettingspage.cpp b/src/settings/services/servicessettingspage.cpp index ff00ca177..83672b556 100644 --- a/src/settings/services/servicessettingspage.cpp +++ b/src/settings/services/servicessettingspage.cpp @@ -222,7 +222,7 @@ void ServicesSettingsPage::loadServices() // Load JSON-based plugins that implement the KFileItemActionPlugin interface const auto jsonPlugins = KPluginLoader::findPlugins(QStringLiteral("kf5/kfileitemaction"), [](const KPluginMetaData& metaData) { - return metaData.serviceTypes().contains(QStringLiteral("KFileItemAction/Plugin")); + return metaData.serviceTypes().contains(QLatin1String("KFileItemAction/Plugin")); }); foreach (const auto& jsonMetadata, jsonPlugins) { diff --git a/src/tests/dolphinmainwindowtest.cpp b/src/tests/dolphinmainwindowtest.cpp index 3d09699fe..e57c79b02 100644 --- a/src/tests/dolphinmainwindowtest.cpp +++ b/src/tests/dolphinmainwindowtest.cpp @@ -44,6 +44,9 @@ private slots: void testOpenInNewTabTitle(); void testNewFileMenuEnabled_data(); void testNewFileMenuEnabled(); + void testWindowTitle_data(); + void testWindowTitle(); + private: @@ -250,6 +253,29 @@ void DolphinMainWindowTest::testNewFileMenuEnabled() QTRY_COMPARE(newFileMenu->isEnabled(), expectedEnabled); } +void DolphinMainWindowTest::testWindowTitle_data() +{ + QTest::addColumn<QUrl>("activeViewUrl"); + QTest::addColumn<QString>("expectedWindowTitle"); + + // TODO: this test should enforce the english locale. + QTest::newRow("home") << QUrl::fromLocalFile(QDir::homePath()) << QStringLiteral("Home"); + QTest::newRow("home with trailing slash") << QUrl::fromLocalFile(QStringLiteral("%1/").arg(QDir::homePath())) << QStringLiteral("Home"); + QTest::newRow("trash") << QUrl::fromUserInput(QStringLiteral("trash:/")) << QStringLiteral("Trash"); +} + +void DolphinMainWindowTest::testWindowTitle() +{ + QFETCH(QUrl, activeViewUrl); + m_mainWindow->openDirectories({ activeViewUrl }, false); + m_mainWindow->show(); + QVERIFY(QTest::qWaitForWindowExposed(m_mainWindow.data())); + QVERIFY(m_mainWindow->isVisible()); + + QFETCH(QString, expectedWindowTitle); + QCOMPARE(m_mainWindow->windowTitle(), expectedWindowTitle); +} + QTEST_MAIN(DolphinMainWindowTest) #include "dolphinmainwindowtest.moc" diff --git a/src/tests/kfileitemmodeltest.cpp b/src/tests/kfileitemmodeltest.cpp index c53979970..f081eba86 100644 --- a/src/tests/kfileitemmodeltest.cpp +++ b/src/tests/kfileitemmodeltest.cpp @@ -460,7 +460,7 @@ void KFileItemModelTest::testModelConsistencyWhenInsertingItems() } m_model->m_dirLister->updateDirectory(m_testDir->url()); - if (itemsInsertedSpy.count() == 0) { + if (itemsInsertedSpy.isEmpty()) { QVERIFY(itemsInsertedSpy.wait()); } diff --git a/src/tests/kitemsettest.cpp b/src/tests/kitemsettest.cpp index 27f8413a7..ca2c16b27 100644 --- a/src/tests/kitemsettest.cpp +++ b/src/tests/kitemsettest.cpp @@ -203,7 +203,7 @@ void KItemSetTest::testIterators() QVERIFY(itemSet.isValid()); QVERIFY(itemSet.count() == itemsQVector.count()); - if (itemSet.count() == 0) { + if (itemSet.isEmpty()) { QVERIFY(itemSet.isEmpty()); QVERIFY(itemSet.begin() == itemSet.end()); QVERIFY(itemSet.constBegin() == itemSet.constEnd()); @@ -342,7 +342,7 @@ void KItemSetTest::testFind() int min; int max; - if (itemSet.count() == 0) { + if (itemSet.isEmpty()) { // Use some arbitrary values for the upcoming tests. min = 0; max = 5; @@ -401,7 +401,7 @@ void KItemSetTest::testChangingOneItem() int min; int max; - if (itemSet.count() == 0) { + if (itemSet.isEmpty()) { // Use some arbitrary values for the upcoming tests. min = 0; max = 5; diff --git a/src/views/dolphinview.cpp b/src/views/dolphinview.cpp index 5b00fa36d..3597a2aa4 100644 --- a/src/views/dolphinview.cpp +++ b/src/views/dolphinview.cpp @@ -66,6 +66,7 @@ #include <QPixmapCache> #include <QPointer> #include <QScrollBar> +#include <QSize> #include <QTimer> #include <QVBoxLayout> @@ -128,8 +129,8 @@ DolphinView::DolphinView(const QUrl& url, QWidget* parent) : m_container = new KItemListContainer(controller, this); m_container->installEventFilter(this); setFocusProxy(m_container); - connect(m_container->horizontalScrollBar(), &QScrollBar::valueChanged, this, &DolphinView::hideToolTip); - connect(m_container->verticalScrollBar(), &QScrollBar::valueChanged, this, &DolphinView::hideToolTip); + connect(m_container->horizontalScrollBar(), &QScrollBar::valueChanged, this, [=] { hideToolTip(); }); + connect(m_container->verticalScrollBar(), &QScrollBar::valueChanged, this, [=] { hideToolTip(); }); controller->setSelectionBehavior(KItemListController::MultiSelection); connect(controller, &KItemListController::itemActivated, this, &DolphinView::slotItemActivated); @@ -744,6 +745,7 @@ bool DolphinView::eventFilter(QObject* watched, QEvent* event) break; case QEvent::KeyPress: + hideToolTip(ToolTipManager::HideBehavior::Instantly); if (GeneralSettings::useTabForSwitchingSplitView()) { QKeyEvent* keyEvent = static_cast<QKeyEvent*>(event); if (keyEvent->key() == Qt::Key_Tab && keyEvent->modifiers() == Qt::NoModifier) { @@ -1337,6 +1339,20 @@ QUrl DolphinView::openItemAsFolderUrl(const KFileItem& item, const bool browseTh return QUrl(); } +void DolphinView::resetZoomLevel() +{ + ViewModeSettings::ViewMode mode; + + switch (m_mode) { + case IconsView: mode = ViewModeSettings::IconsMode; break; + case CompactView: mode = ViewModeSettings::CompactMode; break; + case DetailsView: mode = ViewModeSettings::DetailsMode; break; + } + const ViewModeSettings settings(mode); + const QSize iconSize = QSize(settings.iconSize(), settings.iconSize()); + setZoomLevel(ZoomLevelInfo::zoomLevelForIconSize(iconSize)); +} + void DolphinView::observeCreatedItem(const QUrl& url) { if (m_active) { @@ -1414,11 +1430,11 @@ void DolphinView::updateViewState() } } -void DolphinView::hideToolTip() +void DolphinView::hideToolTip(const ToolTipManager::HideBehavior behavior) { #ifdef HAVE_BALOO if (GeneralSettings::showToolTips()) { - m_toolTipManager->hideToolTip(); + m_toolTipManager->hideToolTip(behavior); } #endif } diff --git a/src/views/dolphinview.h b/src/views/dolphinview.h index 7be2eed2d..ba38d3234 100644 --- a/src/views/dolphinview.h +++ b/src/views/dolphinview.h @@ -23,6 +23,7 @@ #include "dolphintabwidget.h" #include "dolphin_export.h" +#include "tooltips/tooltipmanager.h" #include <KFileItem> #include <KIO/Job> @@ -195,6 +196,11 @@ public: void setZoomLevel(int level); int zoomLevel() const; + /** + * Resets the view's icon size to the default value + */ + void resetZoomLevel(); + void setSortRole(const QByteArray& role); QByteArray sortRole() const; @@ -697,8 +703,6 @@ private slots: */ void updateViewState(); - void hideToolTip(); - /** * Calculates the number of currently shown files into * \a fileCount and the number of folders into \a folderCount. @@ -734,6 +738,11 @@ private: void applyModeToView(); /** + * Hides tooltip displayed over element. + */ + void hideToolTip(const ToolTipManager::HideBehavior behavior = ToolTipManager::HideBehavior::Later); + + /** * Helper method for DolphinView::paste() and DolphinView::pasteIntoFolder(). * Pastes the clipboard data into the URL \a url. */ diff --git a/src/views/dolphinviewactionhandler.cpp b/src/views/dolphinviewactionhandler.cpp index a79dacb1f..25b7f82cb 100644 --- a/src/views/dolphinviewactionhandler.cpp +++ b/src/views/dolphinviewactionhandler.cpp @@ -163,7 +163,7 @@ void DolphinViewActionHandler::createActions() "</interface> option is enabled.</para>")); compactAction->setWhatsThis(xi18nc("@info:whatsthis Compact view mode", "<para>This switches to a compact view mode that lists the folders " - "and files in columns with the names beside the icons. <para></para>" + "and files in columns with the names beside the icons.</para><para>" "This helps to keep the overview in folders with many items.</para>")); detailsAction->setWhatsThis(xi18nc("@info:whatsthis Details view mode", "<para>This switches to a list view mode that focuses on folder " @@ -188,13 +188,21 @@ void DolphinViewActionHandler::createActions() m_actionCollection); zoomInAction->setWhatsThis(i18nc("@info:whatsthis zoom in", "This increases the icon size.")); + QAction* zoomResetAction = m_actionCollection->addAction(QStringLiteral("view_zoom_reset")); + zoomResetAction->setText(i18nc("@action:inmenu View", "Reset Zoom Level")); + zoomResetAction->setToolTip(i18n("Zoom To Default")); + zoomResetAction->setWhatsThis(i18nc("@info:whatsthis zoom reset", "This resets the icon size to default.")); + zoomResetAction->setIcon(QIcon::fromTheme(QStringLiteral("zoom-original"))); + m_actionCollection->setDefaultShortcuts(zoomResetAction, {Qt::CTRL + Qt::Key_0}); + connect(zoomResetAction, &QAction::triggered, this, &DolphinViewActionHandler::zoomReset); + QAction* zoomOutAction = KStandardAction::zoomOut(this, &DolphinViewActionHandler::zoomOut, m_actionCollection); - zoomOutAction->setWhatsThis(i18nc("@info:whatsthis zoom in", "This reduces the icon size.")); + zoomOutAction->setWhatsThis(i18nc("@info:whatsthis zoom out", "This reduces the icon size.")); KToggleAction* showPreview = m_actionCollection->add<KToggleAction>(QStringLiteral("show_preview")); - showPreview->setText(i18nc("@action:intoolbar", "Preview")); + showPreview->setText(i18nc("@action:intoolbar", "Show Previews")); 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 " @@ -245,7 +253,8 @@ void DolphinViewActionHandler::createActions() QActionGroup* visibleRolesGroup = createFileItemRolesActionGroup(QStringLiteral("show_")); KActionMenu* visibleRolesMenu = m_actionCollection->add<KActionMenu>(QStringLiteral("additional_info")); - visibleRolesMenu->setText(i18nc("@action:inmenu View", "Additional Information")); + visibleRolesMenu->setText(i18nc("@action:inmenu View", "Show Additional Information")); + visibleRolesMenu->setIcon(QIcon::fromTheme(QStringLiteral("documentinfo"))); visibleRolesMenu->setDelayed(false); foreach (QAction* action, visibleRolesGroup->actions()) { @@ -259,7 +268,7 @@ void DolphinViewActionHandler::createActions() connect(showInGroups, &KToggleAction::triggered, this, &DolphinViewActionHandler::toggleGroupedSorting); KToggleAction* showHiddenFiles = m_actionCollection->add<KToggleAction>(QStringLiteral("show_hidden_files")); - showHiddenFiles->setText(i18nc("@action:inmenu View", "Hidden Files")); + showHiddenFiles->setText(i18nc("@action:inmenu View", "Show Hidden Files")); showHiddenFiles->setToolTip(i18nc("@info", "Visibility of hidden files and folders")); showHiddenFiles->setWhatsThis(xi18nc("@info:whatsthis", "<para>When " "this is enabled <emphasis>hidden</emphasis> files and folders " @@ -272,6 +281,7 @@ void DolphinViewActionHandler::createActions() QAction* adjustViewProps = m_actionCollection->addAction(QStringLiteral("view_properties")); adjustViewProps->setText(i18nc("@action:inmenu View", "Adjust View Properties...")); + adjustViewProps->setIcon(QIcon::fromTheme(QStringLiteral("view-choose"))); 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); @@ -280,7 +290,7 @@ void DolphinViewActionHandler::createActions() QActionGroup* DolphinViewActionHandler::createFileItemRolesActionGroup(const QString& groupPrefix) { const bool isSortGroup = (groupPrefix == QLatin1String("sort_by_")); - Q_ASSERT(isSortGroup || (!isSortGroup && groupPrefix == QLatin1String("show_"))); + Q_ASSERT(isSortGroup || groupPrefix == QLatin1String("show_")); QActionGroup* rolesActionGroup = new QActionGroup(m_actionCollection); rolesActionGroup->setExclusive(isSortGroup); @@ -391,7 +401,7 @@ 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. + // the 'Zoom In', 'Zoom Out' and 'Zoom Reset' actions must be adapted. updateViewActions(); } @@ -454,6 +464,12 @@ void DolphinViewActionHandler::zoomOut() updateViewActions(); } +void DolphinViewActionHandler::zoomReset() +{ + m_currentView->resetZoomLevel(); + updateViewActions(); +} + void DolphinViewActionHandler::toggleSortFoldersFirst() { const bool sortFirst = m_currentView->sortFoldersFirst(); @@ -535,11 +551,11 @@ void DolphinViewActionHandler::slotHiddenFilesShownChanged(bool 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) @@ -639,7 +655,6 @@ void DolphinViewActionHandler::slotSortTriggered(QAction* action) // actions and the sub-menu-actions. If an action gets checked, it must // be assured that all other actions get unchecked, except the ascending/ // descending actions - QAction* sortByMenu = m_actionCollection->action(QStringLiteral("sort")); for (QAction *groupAction : qAsConst(m_sortByActions)) { KActionMenu* actionMenu = qobject_cast<KActionMenu*>(groupAction); if (actionMenu) { diff --git a/src/views/dolphinviewactionhandler.h b/src/views/dolphinviewactionhandler.h index 7d675b7d4..f931b3b9c 100644 --- a/src/views/dolphinviewactionhandler.h +++ b/src/views/dolphinviewactionhandler.h @@ -131,6 +131,9 @@ private Q_SLOTS: /** Decreases the size of the current set view mode. */ void zoomOut(); + + /** Resets the size of the current set view mode to default. */ + void zoomReset(); /** Switches between a separate sorting and a mixed sorting of files and folders. */ void toggleSortFoldersFirst(); diff --git a/src/views/renamedialog.cpp b/src/views/renamedialog.cpp index 5d329c3ce..96068564d 100644 --- a/src/views/renamedialog.cpp +++ b/src/views/renamedialog.cpp @@ -188,7 +188,7 @@ void RenameDialog::slotAccepted() void RenameDialog::slotTextChanged(const QString& newName) { - bool enable = !newName.isEmpty() && (newName != QLatin1String("..")) && (newName != QLatin1String(".")); + bool enable = !newName.isEmpty() && (newName != QLatin1String("..")) && (newName != QLatin1Char('.')); if (enable && !m_renameOneItem) { const int count = newName.count(QLatin1Char('#')); if (count == 0) { diff --git a/src/views/tooltips/tooltipmanager.cpp b/src/views/tooltips/tooltipmanager.cpp index aae97458c..eaa785987 100644 --- a/src/views/tooltips/tooltipmanager.cpp +++ b/src/views/tooltips/tooltipmanager.cpp @@ -104,7 +104,7 @@ void ToolTipManager::showToolTip(const KFileItem& item, const QRectF& itemRect, Q_ASSERT(!m_metaDataRequested); } -void ToolTipManager::hideToolTip() +void ToolTipManager::hideToolTip(const HideBehavior behavior) { if (m_appliedWaitCursor) { QApplication::restoreOverrideCursor(); @@ -116,7 +116,14 @@ void ToolTipManager::hideToolTip() m_showToolTipTimer->stop(); m_contentRetrievalTimer->stop(); if (m_tooltipWidget) { - m_tooltipWidget->hideLater(); + switch (behavior) { + case HideBehavior::Instantly: + m_tooltipWidget->hide(); + break; + case HideBehavior::Later: + m_tooltipWidget->hideLater(); + break; + } } } diff --git a/src/views/tooltips/tooltipmanager.h b/src/views/tooltips/tooltipmanager.h index 10f88ad76..c09a40d31 100644 --- a/src/views/tooltips/tooltipmanager.h +++ b/src/views/tooltips/tooltipmanager.h @@ -42,6 +42,11 @@ class ToolTipManager : public QObject Q_OBJECT public: + enum class HideBehavior { + Instantly, + Later + }; + explicit ToolTipManager(QWidget* parent); ~ToolTipManager() override; @@ -56,7 +61,7 @@ public: /** * Hides the currently shown tooltip. */ - void hideToolTip(); + void hideToolTip(const HideBehavior behavior = HideBehavior::Later); signals: /** diff --git a/src/views/versioncontrol/fileviewversioncontrolplugin.desktop b/src/views/versioncontrol/fileviewversioncontrolplugin.desktop index 72c02b735..a3b45fc42 100644 --- a/src/views/versioncontrol/fileviewversioncontrolplugin.desktop +++ b/src/views/versioncontrol/fileviewversioncontrolplugin.desktop @@ -23,7 +23,7 @@ Comment[id]=Plugin Kendali Versi untuk Tampilan File Comment[it]=Estensione di controllo delle versioni per le viste dei file Comment[ja]=ファイルビューのためのバージョン管理プラグイン Comment[ko]=파일 보기를 위한 버전 관리 플러그인 -Comment[lt]=Versijų kontrolės papildinys failų tvarkyklėms +Comment[lt]=Failo rodinių versijų tvarkymo papildinys Comment[ml]=ഫയല് അവതരണദിശകൾക്കുള്ള പതിപ്പ് നിയന്ത്രണ സംയോജകം Comment[nb]=Versjonskontrollmodul for filvisninger Comment[nl]=Plugin voor versiecontrole op bestandoverzichten diff --git a/src/views/viewproperties.cpp b/src/views/viewproperties.cpp index eddc7225d..e5f3a82c4 100644 --- a/src/views/viewproperties.cpp +++ b/src/views/viewproperties.cpp @@ -26,6 +26,8 @@ #include <QCryptographicHash> +#include <KFileItem> + namespace { const int AdditionalInfoViewPropertiesVersion = 1; const int NameRolePropertiesVersion = 2; @@ -57,7 +59,7 @@ ViewProperties::ViewProperties(const QUrl& url) : // we store the properties information in a local file. if (useGlobalViewProps) { m_filePath = destinationDir(QStringLiteral("global")); - } else if (url.scheme().contains(QStringLiteral("search"))) { + } else if (url.scheme().contains(QLatin1String("search"))) { m_filePath = destinationDir(QStringLiteral("search/")) + directoryHashForUrl(url); useDetailsViewWithPath = true; } else if (url.scheme() == QLatin1String("trash")) { @@ -71,6 +73,11 @@ ViewProperties::ViewProperties(const QUrl& url) : bool useDestinationDir = !isPartOfHome(m_filePath); if (!useDestinationDir) { + const KFileItem fileItem(url); + useDestinationDir = fileItem.isSlow(); + } + + if (!useDestinationDir) { const QFileInfo dirInfo(m_filePath); const QFileInfo fileInfo(m_filePath + QDir::separator() + ViewPropertiesFileName); useDestinationDir = !dirInfo.isWritable() || (dirInfo.size() > 0 && fileInfo.exists() && !(fileInfo.isReadable() && fileInfo.isWritable())); |
