diff options
Diffstat (limited to 'src')
24 files changed, 299 insertions, 594 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 0c1bcb2b9..364f12a74 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -61,6 +61,7 @@ add_library(dolphinprivate SHARED) target_sources(dolphinprivate PRIVATE kitemviews/kfileitemlistview.cpp kitemviews/kfileitemlistwidget.cpp + kitemviews/kfileitemlisttostring.cpp kitemviews/kfileitemmodel.cpp kitemviews/kfileitemmodelrolesupdater.cpp kitemviews/kitemlistcontainer.cpp @@ -91,8 +92,6 @@ target_sources(dolphinprivate PRIVATE kitemviews/private/kitemlistviewanimation.cpp kitemviews/private/kitemlistviewlayouter.cpp kitemviews/private/kpixmapmodifier.cpp - kitemviews/private/ktwofingerswipe.cpp - kitemviews/private/ktwofingertap.cpp settings/applyviewpropsjob.cpp settings/viewmodes/viewmodesettings.cpp settings/viewpropertiesdialog.cpp @@ -158,6 +157,7 @@ target_link_libraries( KF5::NewStuff KF5::Parts KF5::WindowSystem + KF5::WidgetsAddons ) if(HAVE_BALOO) @@ -307,7 +307,7 @@ target_link_libraries(dolphinstatic KF5::KCMUtils KF5::DBusAddons KF5::Notifications - Phonon::phonon4qt5 + Phonon::phonon4qt${QT_MAJOR_VERSION} ) if (HAVE_KACTIVITIES) diff --git a/src/dolphincontextmenu.cpp b/src/dolphincontextmenu.cpp index 340af6bd0..65d841060 100644 --- a/src/dolphincontextmenu.cpp +++ b/src/dolphincontextmenu.cpp @@ -13,6 +13,7 @@ #include "dolphinplacesmodelsingleton.h" #include "dolphinremoveaction.h" #include "dolphinviewcontainer.h" +#include "global.h" #include "trash/dolphintrash.h" #include "views/dolphinview.h" #include "views/viewmodecontroller.h" @@ -38,34 +39,25 @@ #include <QMimeDatabase> DolphinContextMenu::DolphinContextMenu(DolphinMainWindow* parent, - const QPoint& pos, const KFileItem& fileInfo, + const KFileItemList &selectedItems, const QUrl& baseUrl, KFileItemActions *fileItemActions) : QMenu(parent), - m_pos(pos), m_mainWindow(parent), m_fileInfo(fileInfo), m_baseUrl(baseUrl), m_baseFileItem(nullptr), - m_selectedItems(), + m_selectedItems(selectedItems), m_selectedItemsProperties(nullptr), m_context(NoContext), m_copyToMenu(parent), - m_customActions(), - m_command(None), m_removeAction(nullptr), m_fileItemActions(fileItemActions) { - // The context menu either accesses the URLs of the selected items - // or the items itself. To increase the performance both lists are cached. - const DolphinView* view = m_mainWindow->activeViewContainer()->view(); - m_selectedItems = view->selectedItems(); - QApplication::instance()->installEventFilter(this); - static_cast<KHamburgerMenu *>(m_mainWindow->actionCollection()-> - action(QStringLiteral("hamburger_menu")))->addToMenu(this); + addAllActions(); } DolphinContextMenu::~DolphinContextMenu() @@ -76,13 +68,11 @@ DolphinContextMenu::~DolphinContextMenu() m_selectedItemsProperties = nullptr; } -void DolphinContextMenu::setCustomActions(const QList<QAction*>& actions) +void DolphinContextMenu::addAllActions() { - m_customActions = actions; -} + static_cast<KHamburgerMenu *>(m_mainWindow->actionCollection()-> + action(QStringLiteral("hamburger_menu")))->addToMenu(this); -DolphinContextMenu::Command DolphinContextMenu::open() -{ // get the context information const auto scheme = m_baseUrl.scheme(); if (scheme == QLatin1String("trash")) { @@ -101,17 +91,15 @@ DolphinContextMenu::Command DolphinContextMenu::open() // open the corresponding popup for the context if (m_context & TrashContext) { if (m_context & ItemContext) { - openTrashItemContextMenu(); + addTrashItemContextMenu(); } else { - openTrashContextMenu(); + addTrashContextMenu(); } } else if (m_context & ItemContext) { - openItemContextMenu(); + addItemContextMenu(); } else { - openViewportContextMenu(); + addViewportContextMenu(); } - - return m_command; } bool DolphinContextMenu::eventFilter(QObject* object, QEvent* event) @@ -133,39 +121,25 @@ bool DolphinContextMenu::eventFilter(QObject* object, QEvent* event) return false; } -void DolphinContextMenu::openTrashContextMenu() +void DolphinContextMenu::addTrashContextMenu() { Q_ASSERT(m_context & TrashContext); - QAction* emptyTrashAction = new QAction(QIcon::fromTheme(QStringLiteral("trash-empty")), i18nc("@action:inmenu", "Empty Trash"), this); + QAction *emptyTrashAction = addAction(QIcon::fromTheme(QStringLiteral("trash-empty")), i18nc("@action:inmenu", "Empty Trash"), [this](){ + Trash::empty(m_mainWindow); + }); emptyTrashAction->setEnabled(!Trash::isEmpty()); - addAction(emptyTrashAction); - - addCustomActions(); QAction* propertiesAction = m_mainWindow->actionCollection()->action(QStringLiteral("properties")); addAction(propertiesAction); - - if (exec(m_pos) == emptyTrashAction) { - Trash::empty(m_mainWindow); - } } -void DolphinContextMenu::openTrashItemContextMenu() +void DolphinContextMenu::addTrashItemContextMenu() { Q_ASSERT(m_context & TrashContext); Q_ASSERT(m_context & ItemContext); - QAction* restoreAction = new QAction(QIcon::fromTheme("restoration"), i18nc("@action:inmenu", "Restore"), m_mainWindow); - addAction(restoreAction); - - QAction* deleteAction = m_mainWindow->actionCollection()->action(KStandardAction::name(KStandardAction::DeleteFile)); - addAction(deleteAction); - - QAction* propertiesAction = m_mainWindow->actionCollection()->action(QStringLiteral("properties")); - addAction(propertiesAction); - - if (exec(m_pos) == restoreAction) { + addAction(QIcon::fromTheme("restoration"), i18nc("@action:inmenu", "Restore"), [this](){ QList<QUrl> selectedUrls; selectedUrls.reserve(m_selectedItems.count()); for (const KFileItem &item : qAsConst(m_selectedItems)) { @@ -175,7 +149,13 @@ void DolphinContextMenu::openTrashItemContextMenu() KIO::RestoreJob *job = KIO::restoreFromTrash(selectedUrls); KJobWidgets::setWindow(job, m_mainWindow); job->uiDelegate()->setAutoErrorHandlingEnabled(true); - } + }); + + QAction* deleteAction = m_mainWindow->actionCollection()->action(KStandardAction::name(KStandardAction::DeleteFile)); + addAction(deleteAction); + + QAction* propertiesAction = m_mainWindow->actionCollection()->action(QStringLiteral("properties")); + addAction(propertiesAction); } void DolphinContextMenu::addDirectoryItemContextMenu() @@ -194,7 +174,6 @@ void DolphinContextMenu::addDirectoryItemContextMenu() // set up 'Create New' menu DolphinNewFileMenu* newFileMenu = new DolphinNewFileMenu(m_mainWindow->actionCollection(), m_mainWindow); - const DolphinView* view = m_mainWindow->activeViewContainer()->view(); newFileMenu->checkUpToDate(); newFileMenu->setPopupFiles(QList<QUrl>() << m_fileInfo.url()); newFileMenu->setEnabled(selectedItemsProps.supportsWriting()); @@ -209,16 +188,12 @@ void DolphinContextMenu::addDirectoryItemContextMenu() addSeparator(); } -void DolphinContextMenu::openItemContextMenu() +void DolphinContextMenu::addItemContextMenu() { Q_ASSERT(!m_fileInfo.isNull()); - QAction* openParentAction = nullptr; - QAction* openParentInNewWindowAction = nullptr; - QAction* openParentInNewTabAction = nullptr; const KFileItemListProperties& selectedItemsProps = selectedItemsProperties(); - m_fileItemActions->setItemListProperties(selectedItemsProps); if (m_selectedItems.count() == 1) { @@ -228,23 +203,28 @@ void DolphinContextMenu::openItemContextMenu() } else if (m_context & TimelineContext || m_context & SearchContext) { addOpenWithActions(); - openParentAction = new QAction(QIcon::fromTheme(QStringLiteral("document-open-folder")), + addAction(QIcon::fromTheme(QStringLiteral("document-open-folder")), i18nc("@action:inmenu", "Open Path"), - this); - addAction(openParentAction); + [this](){ + m_mainWindow->changeUrl(KIO::upUrl(m_fileInfo.url())); + m_mainWindow->activeViewContainer()->view()->markUrlsAsSelected({m_fileInfo.url()}); + m_mainWindow->activeViewContainer()->view()->markUrlAsCurrent(m_fileInfo.url()); + }); - openParentInNewWindowAction = new QAction(QIcon::fromTheme(QStringLiteral("window-new")), + addAction(QIcon::fromTheme(QStringLiteral("window-new")), i18nc("@action:inmenu", "Open Path in New Window"), - this); - addAction(openParentInNewWindowAction); + [this](){ + Dolphin::openNewWindow({m_fileInfo.url()}, m_mainWindow, Dolphin::OpenNewWindowFlag::Select); + }); - openParentInNewTabAction = new QAction(QIcon::fromTheme(QStringLiteral("tab-new")), + addAction(QIcon::fromTheme(QStringLiteral("tab-new")), i18nc("@action:inmenu", "Open Path in New Tab"), - this); - addAction(openParentInNewTabAction); + [this](){ + m_mainWindow->openNewTab(KIO::upUrl(m_fileInfo.url())); + }); addSeparator(); } else { @@ -290,23 +270,10 @@ void DolphinContextMenu::openItemContextMenu() addSeparator(); QAction* propertiesAction = m_mainWindow->actionCollection()->action(QStringLiteral("properties")); addAction(propertiesAction); - - QAction* activatedAction = exec(m_pos); - if (activatedAction) { - if (activatedAction == openParentAction) { - m_command = OpenParentFolder; - } else if (activatedAction == openParentInNewWindowAction) { - m_command = OpenParentFolderInNewWindow; - } else if (activatedAction == openParentInNewTabAction) { - m_command = OpenParentFolderInNewTab; - } - } } -void DolphinContextMenu::openViewportContextMenu() +void DolphinContextMenu::addViewportContextMenu() { - const DolphinView* view = m_mainWindow->activeViewContainer()->view(); - const KFileItemListProperties baseUrlProperties(KFileItemList() << baseFileItem()); m_fileItemActions->setItemListProperties(baseUrlProperties); @@ -344,14 +311,11 @@ void DolphinContextMenu::openViewportContextMenu() } addAdditionalActions(baseUrlProperties); - addCustomActions(); addSeparator(); QAction* propertiesAction = m_mainWindow->actionCollection()->action(QStringLiteral("properties")); addAction(propertiesAction); - - exec(m_pos); } void DolphinContextMenu::insertDefaultItemActions(const KFileItemListProperties& properties) @@ -476,11 +440,6 @@ void DolphinContextMenu::addOpenWithActions() m_fileItemActions->insertOpenWithActionsTo(nullptr, this, QStringList{qApp->desktopFileName()}); } -void DolphinContextMenu::addCustomActions() -{ - addActions(m_customActions); -} - void DolphinContextMenu::addAdditionalActions(const KFileItemListProperties &props) { addSeparator(); diff --git a/src/dolphincontextmenu.h b/src/dolphincontextmenu.h index e033fca6e..627a6e3b8 100644 --- a/src/dolphincontextmenu.h +++ b/src/dolphincontextmenu.h @@ -37,60 +37,42 @@ class DolphinContextMenu : public QMenu Q_OBJECT public: - enum Command - { - None, - OpenParentFolder, - OpenParentFolderInNewWindow, - OpenParentFolderInNewTab - }; - /** * @parent Pointer to the main window the context menu * belongs to. - * @pos Position in screen coordinates. * @fileInfo Pointer to the file item the context menu * is applied. If 0 is passed, the context menu * is above the viewport. + * @selectedItems The selected items for which the context menu + * is opened. This list generally includes \a fileInfo. * @baseUrl Base URL of the viewport where the context menu * should be opened. */ DolphinContextMenu(DolphinMainWindow* parent, - const QPoint& pos, const KFileItem& fileInfo, + const KFileItemList &selectedItems, const QUrl& baseUrl, KFileItemActions *fileItemActions); ~DolphinContextMenu() override; - void setCustomActions(const QList<QAction*>& actions); - - /** - * Opens the context menu model and returns the requested - * command, that should be triggered by the caller. If - * Command::None has been returned, either the context-menu - * had been closed without executing an action or an - * already available action from the main-window has been - * executed. - */ - Command open(); - protected: bool eventFilter(QObject* object, QEvent* event) override; private: - void openTrashContextMenu(); - void openTrashItemContextMenu(); - void openItemContextMenu(); - void openViewportContextMenu(); - - void insertDefaultItemActions(const KFileItemListProperties&); - /** - * Adds the "Show menubar" action to the menu if the - * menubar is hidden. + * Adds all the actions and menus to this menu based on all given information. + * This method calls the other helper methods for adding actions + * based on the context given in the constructor. */ - void addShowMenuBarAction(); + void addAllActions(); + + void addTrashContextMenu(); + void addTrashItemContextMenu(); + void addItemContextMenu(); + void addViewportContextMenu(); + + void insertDefaultItemActions(const KFileItemListProperties&); bool placeExists(const QUrl& url) const; @@ -109,17 +91,11 @@ private: void addOpenWithActions(); /** - * Adds custom actions e.g. like the "[x] Expandable Folders"-action - * provided in the details view. - */ - void addCustomActions(); - -private: - /** * Add services, custom actions, plugins and version control items to the menu */ void addAdditionalActions(const KFileItemListProperties &props); +private: struct Entry { int type; @@ -139,7 +115,6 @@ private: SearchContext = 8, }; - QPoint m_pos; DolphinMainWindow* m_mainWindow; KFileItem m_fileInfo; @@ -152,9 +127,6 @@ private: int m_context; KFileCopyToMenu m_copyToMenu; - QList<QAction*> m_customActions; - - Command m_command; DolphinRemoveAction* m_removeAction; // Action that represents either 'Move To Trash' or 'Delete' void addDirectoryItemContextMenu(); diff --git a/src/dolphinmainwindow.cpp b/src/dolphinmainwindow.cpp index 166091ca7..589c2c185 100644 --- a/src/dolphinmainwindow.cpp +++ b/src/dolphinmainwindow.cpp @@ -749,13 +749,45 @@ void DolphinMainWindow::slotToolBarActionMiddleClicked(QAction *action) } } +QAction *DolphinMainWindow::urlNavigatorHistoryAction(const KUrlNavigator *urlNavigator, int historyIndex, QObject *parent) +{ + const QUrl url = urlNavigator->locationUrl(historyIndex); + + QString text = url.toDisplayString(QUrl::PreferLocalFile); + + if (!urlNavigator->showFullPath()) { + const KFilePlacesModel *placesModel = DolphinPlacesModelSingleton::instance().placesModel(); + + const QModelIndex closestIdx = placesModel->closestItem(url); + if (closestIdx.isValid()) { + const QUrl placeUrl = placesModel->url(closestIdx); + + text = placesModel->text(closestIdx); + + QString pathInsidePlace = url.path().mid(placeUrl.path().length()); + + if (!pathInsidePlace.isEmpty() && !pathInsidePlace.startsWith(QLatin1Char('/'))) { + pathInsidePlace.prepend(QLatin1Char('/')); + } + + if (pathInsidePlace != QLatin1Char('/')) { + text.append(pathInsidePlace); + } + } + } + + QAction *action = new QAction(QIcon::fromTheme(KIO::iconNameForUrl(url)), text, parent); + action->setData(historyIndex); + return action; +} + void DolphinMainWindow::slotAboutToShowBackPopupMenu() { const KUrlNavigator *urlNavigator = m_activeViewContainer->urlNavigatorInternalWithHistory(); int entries = 0; m_backAction->menu()->clear(); for (int i = urlNavigator->historyIndex() + 1; i < urlNavigator->historySize() && entries < MaxNumberOfNavigationentries; ++i, ++entries) { - QAction* action = new QAction(urlNavigator->locationUrl(i).toDisplayString(QUrl::PreferLocalFile), m_backAction->menu()); + QAction *action = urlNavigatorHistoryAction(urlNavigator, i, m_backAction->menu()); action->setData(i); m_backAction->menu()->addAction(action); } @@ -784,7 +816,7 @@ void DolphinMainWindow::slotAboutToShowForwardPopupMenu() int entries = 0; m_forwardAction->menu()->clear(); for (int i = urlNavigator->historyIndex() - 1; i >= 0 && entries < MaxNumberOfNavigationentries; --i, ++entries) { - QAction* action = new QAction(urlNavigator->locationUrl(i).toDisplayString(QUrl::PreferLocalFile), m_forwardAction->menu()); + QAction *action = urlNavigatorHistoryAction(urlNavigator, i, m_forwardAction->menu()); action->setData(i); m_forwardAction->menu()->addAction(action); } @@ -1146,32 +1178,11 @@ void DolphinMainWindow::slotWriteStateChanged(bool isFolderWritable) void DolphinMainWindow::openContextMenu(const QPoint& pos, const KFileItem& item, - const QUrl& url, - const QList<QAction*>& customActions) + const KFileItemList &selectedItems, + const QUrl& url) { - QPointer<DolphinContextMenu> contextMenu = new DolphinContextMenu(this, pos, item, url, &m_fileItemActions); - contextMenu.data()->setCustomActions(customActions); - const DolphinContextMenu::Command command = contextMenu.data()->open(); - - switch (command) { - case DolphinContextMenu::OpenParentFolder: - changeUrl(KIO::upUrl(item.url())); - m_activeViewContainer->view()->markUrlsAsSelected({item.url()}); - m_activeViewContainer->view()->markUrlAsCurrent(item.url()); - break; - - case DolphinContextMenu::OpenParentFolderInNewWindow: - Dolphin::openNewWindow({item.url()}, this, Dolphin::OpenNewWindowFlag::Select); - break; - - case DolphinContextMenu::OpenParentFolderInNewTab: - openNewTab(KIO::upUrl(item.url())); - break; - - case DolphinContextMenu::None: - default: - break; - } + QPointer<DolphinContextMenu> contextMenu = new DolphinContextMenu(this, item, selectedItems, url, &m_fileItemActions); + contextMenu.data()->exec(pos); // Delete the menu, unless it has been deleted in its own nested event loop already. if (contextMenu) { diff --git a/src/dolphinmainwindow.h b/src/dolphinmainwindow.h index 1a3ec4efb..17327f2de 100644 --- a/src/dolphinmainwindow.h +++ b/src/dolphinmainwindow.h @@ -475,14 +475,11 @@ private Q_SLOTS: * @pos Position in screen coordinates. * @item File item context. If item is null, the context menu * should be applied to \a url. + * @selectedItems The selected items for which the context menu + * is opened. This list generally includes \a item. * @url URL which contains \a item. - * @customActions Actions that should be added to the context menu, - * if the file item is null. */ - void openContextMenu(const QPoint& pos, - const KFileItem& item, - const QUrl& url, - const QList<QAction*>& customActions); + void openContextMenu(const QPoint& pos, const KFileItem& item, const KFileItemList &selectedItems, const QUrl& url); /** * Updates the menu that is by default at the right end of the toolbar. @@ -653,6 +650,9 @@ private: */ bool addHamburgerMenuToToolbar(); + /** Creates an action representing an item in the URL navigator history */ + static QAction *urlNavigatorHistoryAction(const KUrlNavigator *urlNavigator, int historyIndex, QObject *parent = nullptr); + private: /** * Implements a custom error handling for the undo manager. This diff --git a/src/dolphinpart.cpp b/src/dolphinpart.cpp index 059508778..0f10a7769 100644 --- a/src/dolphinpart.cpp +++ b/src/dolphinpart.cpp @@ -385,8 +385,8 @@ void DolphinPart::createNewWindow(const QUrl& url) void DolphinPart::slotOpenContextMenu(const QPoint& pos, const KFileItem& _item, - const QUrl &, - const QList<QAction*>& customActions) + const KFileItemList &selectedItems, + const QUrl &) { KParts::BrowserExtension::PopupFlags popupFlags = KParts::BrowserExtension::DefaultPopupItems | KParts::BrowserExtension::ShowProperties @@ -402,13 +402,11 @@ void DolphinPart::slotOpenContextMenu(const QPoint& pos, item.setUrl(url()); // ensure we use the view url, not the canonical path (#213799) } - // TODO: We should change the signature of the slots (and signals) for being able - // to tell for which items we want a popup. KFileItemList items; - if (m_view->selectedItems().isEmpty()) { + if (selectedItems.isEmpty()) { items.append(item); } else { - items = m_view->selectedItems(); + items = selectedItems; } KFileItemListProperties capabilities(items); @@ -416,7 +414,6 @@ void DolphinPart::slotOpenContextMenu(const QPoint& pos, KParts::BrowserExtension::ActionGroupMap actionGroups; QList<QAction *> editActions; editActions += m_view->versionControlActions(m_view->selectedItems()); - editActions += customActions; if (!_item.isNull()) { // only for context menu on one or more items const bool supportsMoving = capabilities.supportsMoving(); diff --git a/src/dolphinpart.h b/src/dolphinpart.h index 25d76950c..a49603ca8 100644 --- a/src/dolphinpart.h +++ b/src/dolphinpart.h @@ -123,14 +123,11 @@ private Q_SLOTS: * @pos Position in screen coordinates. * @item File item context. If item is null, the context menu * should be applied to \a url. + * @selectedItems The selected items for which the context menu + * is opened. This list generally includes \a item. * @url URL which contains \a item. - * @customActions Actions that should be added to the context menu, - * if the file item is null. */ - void slotOpenContextMenu(const QPoint& pos, - const KFileItem& item, - const QUrl& url, - const QList<QAction*>& customActions); + void slotOpenContextMenu(const QPoint &pos, const KFileItem &_item, const KFileItemList &selectedItems, const QUrl &); /** * Informs the host that we are opening \a url (e.g. after a redirection diff --git a/src/kitemviews/kfileitemlisttostring.cpp b/src/kitemviews/kfileitemlisttostring.cpp new file mode 100644 index 000000000..8e8f880e9 --- /dev/null +++ b/src/kitemviews/kfileitemlisttostring.cpp @@ -0,0 +1,82 @@ +/* + This file is part of the KDE project + SPDX-FileCopyrightText: 2022 Felix Ernst <[email protected]> + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ + +#include "kfileitemlisttostring.h" + +#include <KFileItem> +#include <KFileItemListProperties> +#include <KLocalizedString> + +#include <QFontMetrics> +#include <QString> + +QString fileItemListToString(KFileItemList items, int maximumTextWidth, QFontMetrics fontMetrics, ItemsState itemsState) +{ + QString text; + switch (items.count()) { + case 1: + text = i18nc("Textual representation of a file. %1 is the name of the file/folder.", + "\"%1\"", items.first().name()); + break; + case 2: + text = i18nc("Textual representation of two files. %1 and %2 are names of files/folders.", + "\"%1\" and \"%2\"", items.first().name(), items.last().name()); + break; + case 3: + text = i18nc("Textual representation of three files. %1, %2 and %3 are names of files/folders.", + "\"%1\", \"%2\" and \"%3\"", + items.first().name(), items.at(1).name(), items.last().name()); + break; + case 4: + text = i18nc("Textual representation of four files. %1, %2, %3 and %4 are names of files/folders.", + "\"%1\", \"%2\", \"%3\" and \"%4\"", + items.first().name(), items.at(1).name(), items.at(2).name(), items.last().name()); + break; + case 5: + text = i18nc("Textual representation of five files. %1, %2, %3, %4 and %5 are names of files/folders.", + "\"%1\", \"%2\", \"%3\", \"%4\" and \"%5\"", + items.first().name(), items.at(1).name(), items.at(2).name(), items.at(3).name(), items.last().name()); + break; + default: + text = QString(); + break; + } + + // At some point the added clarity from the text starts being less important than the text width. + if (!text.isEmpty() && fontMetrics.horizontalAdvance(text) <= maximumTextWidth) { + return text; + } + + const KFileItemListProperties properties(items); + if (itemsState == Selected) { + if (properties.isFile()) { + text = i18ncp("Textual representation of selected files. %1 is the number of files.", + "One Selected File", "%1 Selected Files", items.count()); + } else if (properties.isDirectory()) { + text = i18ncp("Textual representation of selected folders. %1 is the number of folders.", + "One Selected Folder", "%1 Selected Folders", items.count()); + } else { + text = i18ncp("Textual representation of selected fileitems. %1 is the number of files/folders.", + "One Selected Item", "%1 Selected Items", items.count()); + } + + if (fontMetrics.horizontalAdvance(text) <= maximumTextWidth) { + return text; + } + } + + if (properties.isFile()) { + return i18ncp("Textual representation of files. %1 is the number of files.", + "One File", "%1 Files", items.count()); + } else if (properties.isDirectory()) { + return i18ncp("Textual representation of folders. %1 is the number of folders.", + "One Folder", "%1 Folders", items.count()); + } else { + return i18ncp("Textual representation of fileitems. %1 is the number of files/folders.", + "One Item", "%1 Items", items.count()); + } +} diff --git a/src/kitemviews/kfileitemlisttostring.h b/src/kitemviews/kfileitemlisttostring.h new file mode 100644 index 000000000..7eee0aec9 --- /dev/null +++ b/src/kitemviews/kfileitemlisttostring.h @@ -0,0 +1,56 @@ +/* + This file is part of the KDE project + SPDX-FileCopyrightText: 2022 Felix Ernst <[email protected]> + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ + +#ifndef KFILEITEMLISTTOSTRING_H +#define KFILEITEMLISTTOSTRING_H + +class KFileItemList; +class QFontMetrics; +class QString; + +enum ItemsState { + None, + Selected +}; + +/** + * @brief Generates a textual representation of the given list of KFileItems. + * + * This method is especially useful to be very explicit about which items will be affected by an action. + * For example can a label for a delete button be enriched to say "Permanantly Delete `picture1` and `picture2`" + * but also "Permanently Delete 7 Selected Folders" without requiring a huge amount of new translations for this. + * + * Unfortunately this doesn't always work. + * + * For some texts and some languages this wide range of words cannot be inserted into a text while staying + * grammatically correct. Because of this you will probably need to write a fallback for these languages. + * Something like this: + * \code + * // i18n: @action:inmenu menu with actions like copy, paste, rename. + * // %2 is a textual representation of the currently selected files or folders. This can be the name of + * // the file/files like "file1" or "file1, file2 and file3" or an aggregate like "8 Selected Folders". + * // If this sort of word puzzle can not be correctly translated in your language, translate it as "NULL" (without the quotes) + * // and a fallback will be used. + * auto buttonText = i18ncp("@action A more elaborate and clearly worded version of the Delete action", "Permanently Delete %2", "Permanently Delete %2", items.count(), fileItemListToString(items, fontMetrics.averageCharWidth() * 20, fontMetrics)); + * if (buttonText == QStringLiteral("NULL")) { + * buttonText = i18ncp("@action Delete button label. %1 is the number of items to be deleted.", + * "Permanently Delete %1 Item", "Permanently Delete %1 Items", items.count()) + * } + * \endcode + * (The i18n call should be completely in the line following the i18n: comment without any line breaks within the i18n call or the comment might not be correctly extracted. See: https://commits.kde.org/kxmlgui/a31135046e1b3335b5d7bbbe6aa9a883ce3284c1 ) + * + * @param items The KFileItemList for which a QString should be generated. + * @param maximumTextWidth The maximum width/horizontalAdvance the QString should have. Keep scaling in mind. + * @param fontMetrics the fontMetrics for the font that is used to calculate the maximumTextWidth. + * @param itemsState A state of the @p items that should be spelled out in the returned QString. + * @returns the names of the @p items separated by commas if that is below the @p maximumCharacterWidth. + * Otherwise returns something like "5 Files", "8 Selected Folders" or "60 Items" + * while being as specific as possible. + */ +QString fileItemListToString(KFileItemList items, int maximumTextWidth, QFontMetrics fontMetrics, ItemsState itemsState = ItemsState::None); + +#endif // KFILEITEMLISTTOSTRING_H diff --git a/src/kitemviews/kfileitemmodel.cpp b/src/kitemviews/kfileitemmodel.cpp index 6391d7d3f..052a71cbd 100644 --- a/src/kitemviews/kfileitemmodel.cpp +++ b/src/kitemviews/kfileitemmodel.cpp @@ -15,7 +15,7 @@ #include <KDirLister> #include <KIO/Job> -#include <KIO/kio_version.h> +#include <kio_version.h> #include <KLocalizedString> #include <KLazyLocalizedString> #include <KUrlMimeData> diff --git a/src/kitemviews/kitemlistcontroller.cpp b/src/kitemviews/kitemlistcontroller.cpp index 7ef37481d..ba4047dbe 100644 --- a/src/kitemviews/kitemlistcontroller.cpp +++ b/src/kitemviews/kitemlistcontroller.cpp @@ -13,10 +13,11 @@ #include "kitemlistview.h" #include "private/kitemlistkeyboardsearchmanager.h" #include "private/kitemlistrubberband.h" -#include "private/ktwofingerswipe.h" -#include "private/ktwofingertap.h" #include "views/draganddrophelper.h" +#include <KTwoFingerSwipe> +#include <KTwoFingerTap> + #include <QAccessible> #include <QApplication> #include <QDrag> diff --git a/src/kitemviews/kstandarditemlistwidget.cpp b/src/kitemviews/kstandarditemlistwidget.cpp index b531b8641..247581a4e 100644 --- a/src/kitemviews/kstandarditemlistwidget.cpp +++ b/src/kitemviews/kstandarditemlistwidget.cpp @@ -1196,7 +1196,11 @@ QString KStandardItemListWidget::elideRightKeepExtension(const QString &text, in QString ret = m_customizedFontMetrics.elidedText(text.chopped(extensionLength), Qt::ElideRight, elidingWidth - extensionWidth); +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) ret.append(text.rightRef(extensionLength)); +#else + ret.append(QStringView(text).right(extensionLength)); +#endif return ret; } } diff --git a/src/kitemviews/private/ktwofingerswipe.cpp b/src/kitemviews/private/ktwofingerswipe.cpp deleted file mode 100644 index 6d0e18e65..000000000 --- a/src/kitemviews/private/ktwofingerswipe.cpp +++ /dev/null @@ -1,139 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2020 Steffen Hartleib <[email protected]> - * - * SPDX-License-Identifier: GPL-2.0-or-later - */ - -// Self -#include "ktwofingerswipe.h" - -// Qt -#include <QTouchEvent> -#include <QLineF> - -KTwoFingerSwipeRecognizer::KTwoFingerSwipeRecognizer() : - QGestureRecognizer(), - m_touchBeginnTimestamp(0), - m_gestureAlreadyTriggered(false) -{ -} - -KTwoFingerSwipeRecognizer::~KTwoFingerSwipeRecognizer() -{ -} - -QGesture* KTwoFingerSwipeRecognizer::create(QObject*) -{ - return static_cast<QGesture*>(new KTwoFingerSwipe()); -} - -QGestureRecognizer::Result KTwoFingerSwipeRecognizer::recognize(QGesture* gesture, QObject* watched, QEvent* event) -{ - Q_UNUSED(watched) - - KTwoFingerSwipe* const kTwoFingerSwipe = static_cast<KTwoFingerSwipe*>(gesture); - const QTouchEvent* touchEvent = static_cast<const QTouchEvent*>(event); - - const int maxTimeFrameForSwipe = 90; - const int minDistanceForSwipe = 30; - - switch (event->type()) { - case QEvent::TouchBegin: { - m_touchBeginnTimestamp = touchEvent->timestamp(); - m_gestureAlreadyTriggered = false; - kTwoFingerSwipe->setHotSpot(touchEvent->touchPoints().first().startScreenPos()); - kTwoFingerSwipe->setPos(touchEvent->touchPoints().first().startPos()); - kTwoFingerSwipe->setScreenPos(touchEvent->touchPoints().first().startScreenPos()); - kTwoFingerSwipe->setScenePos(touchEvent->touchPoints().first().startScenePos()); - return MayBeGesture; - } - - case QEvent::TouchUpdate: { - const qint64 now = touchEvent->timestamp(); - const qint64 elapsedTime = now - m_touchBeginnTimestamp; - const QPointF distance = touchEvent->touchPoints().first().startPos() - touchEvent->touchPoints().first().pos(); - kTwoFingerSwipe->setHotSpot(touchEvent->touchPoints().first().startScreenPos()); - kTwoFingerSwipe->setPos(touchEvent->touchPoints().first().startPos()); - kTwoFingerSwipe->setScreenPos(touchEvent->touchPoints().first().startScreenPos()); - kTwoFingerSwipe->setScenePos(touchEvent->touchPoints().first().startScenePos()); - const QLineF ql = QLineF(touchEvent->touchPoints().first().startPos(), touchEvent->touchPoints().first().pos()); - kTwoFingerSwipe->setSwipeAngle(ql.angle()); - - if (touchEvent->touchPoints().size() > 2) { - return CancelGesture; - } - - if (touchEvent->touchPoints().size() == 2) { - if ((elapsedTime) > maxTimeFrameForSwipe) { - return CancelGesture; - } - - if (distance.manhattanLength() >= minDistanceForSwipe && - (elapsedTime) <= maxTimeFrameForSwipe && !m_gestureAlreadyTriggered) { - m_gestureAlreadyTriggered = true; - return FinishGesture; - } else if ((elapsedTime) <= maxTimeFrameForSwipe && !m_gestureAlreadyTriggered) { - return TriggerGesture; - } - } - break; - } - - default: - return Ignore; - } - return Ignore; -} - -KTwoFingerSwipe::KTwoFingerSwipe(QObject* parent) : - QGesture(parent), - m_pos(QPointF(-1, -1)), - m_screenPos(QPointF(-1, -1)), - m_scenePos(QPointF(-1, -1)), - m_swipeAngle(0.0) -{ -} - -KTwoFingerSwipe::~KTwoFingerSwipe() -{ -} - -QPointF KTwoFingerSwipe::pos() const -{ - return m_pos; -} - -void KTwoFingerSwipe::setPos(QPointF _pos) -{ - m_pos = _pos; -} - -QPointF KTwoFingerSwipe::screenPos() const -{ - return m_screenPos; -} - -void KTwoFingerSwipe::setScreenPos(QPointF _screenPos) -{ - m_screenPos = _screenPos; -} - -QPointF KTwoFingerSwipe::scenePos() const -{ - return m_scenePos; -} - -void KTwoFingerSwipe::setScenePos(QPointF _scenePos) -{ - m_scenePos = _scenePos; -} - -qreal KTwoFingerSwipe::swipeAngle() const -{ - return m_swipeAngle; -} - void KTwoFingerSwipe::setSwipeAngle(qreal _swipeAngle) -{ - m_swipeAngle = _swipeAngle; -} - diff --git a/src/kitemviews/private/ktwofingerswipe.h b/src/kitemviews/private/ktwofingerswipe.h deleted file mode 100644 index 6d64d8ff7..000000000 --- a/src/kitemviews/private/ktwofingerswipe.h +++ /dev/null @@ -1,54 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2020 Steffen Hartleib <[email protected]> - * - * SPDX-License-Identifier: GPL-2.0-or-later - */ - -#ifndef KTWOFINGERSWIPE_H -#define KTWOFINGERSWIPE_H - -#include "dolphin_export.h" -// Qt -#include <QGesture> -#include <QGestureRecognizer> - -class DOLPHIN_EXPORT KTwoFingerSwipe : public QGesture -{ - Q_OBJECT - Q_PROPERTY(QPointF pos READ pos WRITE setPos) - Q_PROPERTY(QPointF screenPos READ screenPos WRITE setScreenPos) - Q_PROPERTY(QPointF scenePos READ scenePos WRITE setScenePos) - Q_PROPERTY(qreal swipeAngle READ swipeAngle WRITE setSwipeAngle) -public: - explicit KTwoFingerSwipe(QObject* parent = nullptr); - ~KTwoFingerSwipe() override; - QPointF pos() const; - void setPos(QPointF pos); - QPointF screenPos() const; - void setScreenPos(QPointF screenPos); - QPointF scenePos() const; - void setScenePos(QPointF scenePos); - qreal swipeAngle() const; - void setSwipeAngle(qreal swipeAngle); -private: - QPointF m_pos; - QPointF m_screenPos; - QPointF m_scenePos; - qreal m_swipeAngle; -}; - -class DOLPHIN_EXPORT KTwoFingerSwipeRecognizer : public QGestureRecognizer -{ -public: - explicit KTwoFingerSwipeRecognizer(); - ~KTwoFingerSwipeRecognizer() override; - QGesture* create(QObject*) override; - Result recognize(QGesture*, QObject*, QEvent*) override; -private: - Q_DISABLE_COPY( KTwoFingerSwipeRecognizer ) - qint64 m_touchBeginnTimestamp; - bool m_gestureAlreadyTriggered; -}; - -#endif /* KTWOFINGERSWIPE_H */ - diff --git a/src/kitemviews/private/ktwofingertap.cpp b/src/kitemviews/private/ktwofingertap.cpp deleted file mode 100644 index edd9d1d35..000000000 --- a/src/kitemviews/private/ktwofingertap.cpp +++ /dev/null @@ -1,122 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2020 Steffen Hartleib <[email protected]> - * - * SPDX-License-Identifier: GPL-2.0-or-later - */ - -// Self -#include "ktwofingertap.h" - -// Qt -#include <QTouchEvent> -#include <QApplication> -#include <QGraphicsWidget> - -KTwoFingerTapRecognizer::KTwoFingerTapRecognizer() : - QGestureRecognizer(), - m_gestureTriggered(false) -{ -} - -KTwoFingerTapRecognizer::~KTwoFingerTapRecognizer() -{ -} - -QGesture* KTwoFingerTapRecognizer::create(QObject*) -{ - return static_cast<QGesture*>(new KTwoFingerTap()); -} - -QGestureRecognizer::Result KTwoFingerTapRecognizer::recognize(QGesture* gesture, QObject* watched, QEvent* event) -{ - if (qobject_cast<QGraphicsWidget*>(watched)) { - return Ignore; - } - - KTwoFingerTap* const kTwoFingerTap = static_cast<KTwoFingerTap*>(gesture); - const QTouchEvent* touchEvent = static_cast<const QTouchEvent*>(event); - - switch (event->type()) { - case QEvent::TouchBegin: { - kTwoFingerTap->setHotSpot(touchEvent->touchPoints().first().startScreenPos()); - kTwoFingerTap->setPos(touchEvent->touchPoints().first().startPos()); - kTwoFingerTap->setScreenPos(touchEvent->touchPoints().first().startScreenPos()); - kTwoFingerTap->setScenePos(touchEvent->touchPoints().first().startScenePos()); - m_gestureTriggered = false; - return MayBeGesture; - } - - case QEvent::TouchUpdate: { - - if (touchEvent->touchPoints().size() > 2) { - m_gestureTriggered = false; - return CancelGesture; - } - - if (touchEvent->touchPoints().size() == 2) { - if ((touchEvent->touchPoints().first().startPos() - touchEvent->touchPoints().first().pos()).manhattanLength() >= QApplication::startDragDistance()) { - m_gestureTriggered = false; - return CancelGesture; - } - if ((touchEvent->touchPoints().at(1).startPos() - touchEvent->touchPoints().at(1).pos()).manhattanLength() >= QApplication::startDragDistance()) { - m_gestureTriggered = false; - return CancelGesture; - } - if (touchEvent->touchPointStates() & Qt::TouchPointPressed) { - m_gestureTriggered = true; - } - if (touchEvent->touchPointStates() & Qt::TouchPointReleased && m_gestureTriggered) { - m_gestureTriggered = false; - return FinishGesture; - } - } - break; - } - - default: - return Ignore; - } - return Ignore; -} - -KTwoFingerTap::KTwoFingerTap(QObject* parent) : - QGesture(parent), - m_pos(QPointF(-1, -1)), - m_screenPos(QPointF(-1, -1)), - m_scenePos(QPointF(-1, -1)) -{ -} - -KTwoFingerTap::~KTwoFingerTap() -{ -} - -QPointF KTwoFingerTap::pos() const -{ - return m_pos; -} - -void KTwoFingerTap::setPos(QPointF _pos) -{ - m_pos = _pos; -} - -QPointF KTwoFingerTap::screenPos() const -{ - return m_screenPos; -} - -void KTwoFingerTap::setScreenPos(QPointF _screenPos) -{ - m_screenPos = _screenPos; -} - -QPointF KTwoFingerTap::scenePos() const -{ - return m_scenePos; -} - -void KTwoFingerTap::setScenePos(QPointF _scenePos) -{ - m_scenePos = _scenePos; -} diff --git a/src/kitemviews/private/ktwofingertap.h b/src/kitemviews/private/ktwofingertap.h deleted file mode 100644 index 6d409900b..000000000 --- a/src/kitemviews/private/ktwofingertap.h +++ /dev/null @@ -1,48 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2020 Steffen Hartleib <[email protected]> - * - * SPDX-License-Identifier: GPL-2.0-or-later - */ - -#ifndef KTWOFINGERTAP_H -#define KTWOFINGERTAP_H - -#include <dolphin_export.h> -// Qt -#include <QGesture> -#include <QGestureRecognizer> - -class DOLPHIN_EXPORT KTwoFingerTap : public QGesture -{ - Q_OBJECT - Q_PROPERTY(QPointF pos READ pos WRITE setPos) - Q_PROPERTY(QPointF screenPos READ screenPos WRITE setScreenPos) - Q_PROPERTY(QPointF scenePos READ scenePos WRITE setScenePos) -public: - explicit KTwoFingerTap(QObject* parent = nullptr); - ~KTwoFingerTap() override; - QPointF pos() const; - void setPos(QPointF pos); - QPointF screenPos() const; - void setScreenPos(QPointF screenPos); - QPointF scenePos() const; - void setScenePos(QPointF scenePos); -private: - QPointF m_pos; - QPointF m_screenPos; - QPointF m_scenePos; -}; - -class DOLPHIN_EXPORT KTwoFingerTapRecognizer : public QGestureRecognizer -{ -public: - explicit KTwoFingerTapRecognizer(); - ~KTwoFingerTapRecognizer() override; - QGesture* create(QObject*) override; - Result recognize(QGesture*, QObject*, QEvent*) override; -private: - Q_DISABLE_COPY(KTwoFingerTapRecognizer) - bool m_gestureTriggered; -}; - -#endif /* KTWOFINGERTAP_H */ diff --git a/src/org.kde.dolphin.desktop b/src/org.kde.dolphin.desktop index a6ab14067..d3a4e8680 100755 --- a/src/org.kde.dolphin.desktop +++ b/src/org.kde.dolphin.desktop @@ -135,10 +135,11 @@ Keywords[ko]=files;file management;file browsing;samba;network shares;Explorer;F Keywords[nl]=bestanden;bestandsbeheer;bladeren in bestanden;samba;netwerk-shares;verkenner;zoeksysteem; Keywords[nn]=filer;filhandsaming;filutforsking;samba;nettverksressursar;Explorer;Finder; Keywords[pl]=pliki;zarządzenie plikami;przeglądanie plików;samba;udziały sieciowe;Przeglądarka;Finder; +Keywords[pt]=ficheiros;gestão de ficheiros;navegação de ficheiros;samba;partilhas de rede;Explorador;Pesquisa; Keywords[pt_BR]=arquivo;gerenciamento de arquivos;navegação de arquivos;samba;compartilhamentos de rede;explorador;localizador; Keywords[ro]=fișiere;gestiune fișiere;răsfoire fișiere;samba;partajări de rețea;unități de rețea;Explorer;Finder; Keywords[ru]=files;file management;file browsing;samba;network shares;Explorer;Finder;файлы,управление файлами,просмотр файлов,сетевые папки -Keywords[sk]=súbory;správa súborov;prehliadanie súborov;samba;sieťové akcie;Prieskumník;Vyhľadávač; +Keywords[sk]=súbory;správa súborov;prehliadanie súborov;samba;zdieľanie v sieti;Prieskumník;Vyhľadávač; Keywords[sl]=datoteke;upravljanje z datotekami;brskanje po datotekah;samba;mrežni diski;Raziskovalec;Iskalec; Keywords[sv]=filer;filhantering;filbläddring;samba;delade nätverksresurser;Utforskare;Finder; Keywords[tr]=dosyalar;dosya yönetimi;dosya göz at;samba;ağ paylaşımları;Explorer;Finder; diff --git a/src/settings/contextmenu/servicemenuinstaller/servicemenuinstaller.cpp b/src/settings/contextmenu/servicemenuinstaller/servicemenuinstaller.cpp index 91da3d256..7f42ee3bd 100644 --- a/src/settings/contextmenu/servicemenuinstaller/servicemenuinstaller.cpp +++ b/src/settings/contextmenu/servicemenuinstaller/servicemenuinstaller.cpp @@ -50,7 +50,7 @@ Q_NORETURN void fail(const QString &str) QString getServiceMenusDir() { const QString dataLocation = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation); - return QDir(dataLocation).absoluteFilePath("kservices5/ServiceMenus"); + return QDir(dataLocation).absoluteFilePath("kio/servicemenus"); } #ifdef HAVE_PACKAGEKIT @@ -300,6 +300,8 @@ bool cmdInstall(const QString &archive, QString &errorText) errorText = i18n("Failed to copy .desktop file %1 to %2: %3", archive, dest, source.errorString()); return false; } + QFile destFile(dest); + destFile.setPermissions(destFile.permissions() | QFile::ExeOwner); } else { if (binaryPackages->contains(QMimeDatabase().mimeTypeForFile(archive).name())) { packageKit(PackageOperation::Install, archive); diff --git a/src/trash/dolphintrash.h b/src/trash/dolphintrash.h index 12f10f101..2ee85e89a 100644 --- a/src/trash/dolphintrash.h +++ b/src/trash/dolphintrash.h @@ -11,7 +11,7 @@ #include <QWidget> #include <KIO/EmptyTrashJob> -#include <KIOWidgets/KDirLister> +#include <KDirLister> class Trash: public QObject { diff --git a/src/views/dolphinview.cpp b/src/views/dolphinview.cpp index 5646fa982..e6aecff80 100644 --- a/src/views/dolphinview.cpp +++ b/src/views/dolphinview.cpp @@ -1058,12 +1058,12 @@ void DolphinView::slotItemContextMenuRequested(int index, const QPointF& pos) } const KFileItem item = m_model->fileItem(index); - Q_EMIT requestContextMenu(pos.toPoint(), item, url(), QList<QAction*>()); + Q_EMIT requestContextMenu(pos.toPoint(), item, selectedItems(), url()); } void DolphinView::slotViewContextMenuRequested(const QPointF& pos) { - Q_EMIT requestContextMenu(pos.toPoint(), KFileItem(), url(), QList<QAction*>()); + Q_EMIT requestContextMenu(pos.toPoint(), KFileItem(), selectedItems(), url()); } void DolphinView::slotHeaderContextMenuRequested(const QPointF& pos) diff --git a/src/views/dolphinview.h b/src/views/dolphinview.h index e93ca4fa0..b40be8936 100644 --- a/src/views/dolphinview.h +++ b/src/views/dolphinview.h @@ -510,13 +510,12 @@ Q_SIGNALS: /** * Is emitted if a context menu is requested for the item \a item, * which is part of \a url. If the item is null, the context menu - * for the URL should be shown and the custom actions \a customActions - * will be added. + * for the URL should be shown. */ void requestContextMenu(const QPoint& pos, const KFileItem& item, - const QUrl& url, - const QList<QAction*>& customActions); + const KFileItemList &selectedItems, + const QUrl& url); /** * Is emitted if an information message with the content \a msg diff --git a/src/views/dolphinviewactionhandler.cpp b/src/views/dolphinviewactionhandler.cpp index 2e524f8f2..c02d7856b 100644 --- a/src/views/dolphinviewactionhandler.cpp +++ b/src/views/dolphinviewactionhandler.cpp @@ -8,6 +8,7 @@ #include "dolphinviewactionhandler.h" #include "dolphindebug.h" +#include "kitemviews/kfileitemlisttostring.h" #include "kitemviews/kfileitemmodel.h" #include "settings/viewpropertiesdialog.h" #include "views/zoomlevelinfo.h" @@ -763,49 +764,25 @@ void DolphinViewActionHandler::slotCopyPath() void DolphinViewActionHandler::slotSelectionChanged(const KFileItemList& selection) { QString basicActionsMenuText; - switch (selection.count()) { - case 0: + if (selection.isEmpty()) { basicActionsMenuText = i18nc("@action:inmenu menu with actions like copy, paste, rename. The user's selection is empty when this text is shown.", "Actions for Current View"); - break; - case 1: - basicActionsMenuText = - i18nc("@action:inmenu menu with actions like copy, paste, rename. %1 is the name of the singular selected file/folder.", - "Actions for \"%1\"", selection.first().name()); - break; - case 2: - basicActionsMenuText = - i18nc("@action:inmenu menu with actions like copy, paste, rename. %1 and %2 are names of files/folders.", - "Actions for \"%1\" and \"%2\"", selection.first().name(), selection.last().name()); - break; - case 3: - basicActionsMenuText = - i18nc("@action:inmenu menu with actions like copy, paste, rename. %1, %2 and %3 are names of files/folders.", - "Actions for \"%1\", \"%2\" and \"%3\"", - selection.first().name(), selection.at(1).name(), selection.last().name()); - break; - default: - basicActionsMenuText = QString(); - break; + } else { + QFontMetrics fontMetrics = QMenu().fontMetrics(); + // i18n: @action:inmenu menu with actions like copy, paste, rename. + // %1 is a textual representation of the currently selected files or folders. This can be the name of + // the file/files like "file1" or "file1, file2 and file3" or an aggregate like "8 Selected Folders". + // If this sort of word puzzle can not be correctly translated in your language, translate it as "NULL" (without the quotes) + // and a fallback will be used. + basicActionsMenuText = i18n("Actions for %1", fileItemListToString(selection, fontMetrics.averageCharWidth() * 40, fontMetrics, ItemsState::Selected)); } - // At some point the added clarity from the text starts being less important than the menu width. - if (basicActionsMenuText.isEmpty() || basicActionsMenuText.length() > 40) { + if (basicActionsMenuText == QStringLiteral("NULL")) { const KFileItemListProperties properties(selection); - if (properties.isFile()) { - basicActionsMenuText = - i18ncp("@action:inmenu menu with actions like copy, paste, rename. %1 is the amount of selected files/folders.", - "Actions for One Selected File", "Actions for %1 Selected Files", selection.count()); - } else if (properties.isDirectory()) { - basicActionsMenuText = - i18ncp("@action:inmenu menu with actions like copy, paste, rename. %1 is the amount of selected files/folders.", - "Actions for One Selected Folder", "Actions for %1 Selected Folders", selection.count()); - } else { - basicActionsMenuText = - i18ncp("@action:inmenu menu with actions like copy, paste, rename. %1 is the amount of selected files/folders.", - "Actions for One Selected Item", "Actions for %1 Selected Items", selection.count()); - } + basicActionsMenuText = + i18ncp("@action:inmenu menu with actions like copy, paste, rename. %1 is the amount of selected files/folders.", + "Actions for One Selected Item", "Actions for %1 Selected Items", selection.count()); } QAction *basicActionsMenu = m_actionCollection->action(QStringLiteral("basic_actions")); diff --git a/src/views/tooltips/tooltipmanager.cpp b/src/views/tooltips/tooltipmanager.cpp index 54af9c94c..637261702 100644 --- a/src/views/tooltips/tooltipmanager.cpp +++ b/src/views/tooltips/tooltipmanager.cpp @@ -38,7 +38,6 @@ ToolTipManager::ToolTipManager(QWidget* parent) : m_showToolTipTimer(nullptr), m_contentRetrievalTimer(nullptr), m_transientParent(nullptr), - m_fileMetaDataWidget(nullptr), m_toolTipRequested(false), m_metaDataRequested(false), m_appliedWaitCursor(false), @@ -65,11 +64,14 @@ ToolTipManager::ToolTipManager(QWidget* parent) : ToolTipManager::~ToolTipManager() { + if (!m_fileMetaDatWidgetOwnershipTransferred) { + delete m_fileMetaDataWidget; + } } void ToolTipManager::showToolTip(const KFileItem& item, const QRectF& itemRect, QWindow *transientParent) { - hideToolTip(); + hideToolTip(HideBehavior::Instantly); m_itemRect = itemRect.toRect(); @@ -81,11 +83,11 @@ void ToolTipManager::showToolTip(const KFileItem& item, const QRectF& itemRect, // Only start the retrieving of the content, when the mouse has been over this // item for 200 milliseconds. This prevents a lot of useless preview jobs and // meta data retrieval, when passing rapidly over a lot of items. - m_fileMetaDataWidget.reset(new DolphinFileMetaDataWidget()); - connect(m_fileMetaDataWidget.data(), &DolphinFileMetaDataWidget::metaDataRequestFinished, - this, &ToolTipManager::slotMetaDataRequestFinished); - connect(m_fileMetaDataWidget.data(), &DolphinFileMetaDataWidget::urlActivated, - this, &ToolTipManager::urlActivated); + if (!m_fileMetaDataWidget) { + m_fileMetaDataWidget = new DolphinFileMetaDataWidget(); + connect(m_fileMetaDataWidget, &DolphinFileMetaDataWidget::metaDataRequestFinished, this, &ToolTipManager::slotMetaDataRequestFinished); + connect(m_fileMetaDataWidget, &DolphinFileMetaDataWidget::urlActivated, this, &ToolTipManager::urlActivated); + } m_contentRetrievalTimer->start(); m_showToolTipTimer->start(); @@ -220,7 +222,10 @@ void ToolTipManager::showToolTip() if (!m_tooltipWidget) { m_tooltipWidget.reset(new KToolTipWidget()); } - m_tooltipWidget->showBelow(m_itemRect, m_fileMetaDataWidget.data(), m_transientParent); + m_tooltipWidget->showBelow(m_itemRect, m_fileMetaDataWidget, m_transientParent); + // At this point KToolTipWidget adopted our parent-less metadata widget. + m_fileMetaDatWidgetOwnershipTransferred = true; + m_toolTipRequested = false; } diff --git a/src/views/tooltips/tooltipmanager.h b/src/views/tooltips/tooltipmanager.h index 3688e815c..c86c97f6b 100644 --- a/src/views/tooltips/tooltipmanager.h +++ b/src/views/tooltips/tooltipmanager.h @@ -77,7 +77,12 @@ private: QWindow* m_transientParent; QScopedPointer<KToolTipWidget> m_tooltipWidget; - QScopedPointer<DolphinFileMetaDataWidget> m_fileMetaDataWidget; + DolphinFileMetaDataWidget *m_fileMetaDataWidget = nullptr; + + /// Whether ownership of the metadata widget was transferred + /// over to the KToolTipWidget (i.e. we should not delete it + /// anymore) + bool m_fileMetaDatWidgetOwnershipTransferred = false; bool m_toolTipRequested; bool m_metaDataRequested; |
