diff options
| -rw-r--r-- | src/CMakeLists.txt | 1 | ||||
| -rw-r--r-- | src/kitemviews/kfileitemlisttostring.cpp | 82 | ||||
| -rw-r--r-- | src/kitemviews/kfileitemlisttostring.h | 56 | ||||
| -rw-r--r-- | src/views/dolphinviewactionhandler.cpp | 51 |
4 files changed, 153 insertions, 37 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 7c3e5b72b..7f26ee3dc 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 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/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")); |
