target_sources(dolphinprivate PRIVATE
kitemviews/kfileitemlistview.cpp
kitemviews/kfileitemlistwidget.cpp
+ kitemviews/kfileitemlisttostring.cpp
kitemviews/kfileitemmodel.cpp
kitemviews/kfileitemmodelrolesupdater.cpp
kitemviews/kitemlistcontainer.cpp
--- /dev/null
+/*
+ This file is part of the KDE project
+ SPDX-FileCopyrightText: 2022 Felix Ernst <felixernst@zohomail.eu>
+
+ 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());
+ }
+}
--- /dev/null
+/*
+ This file is part of the KDE project
+ SPDX-FileCopyrightText: 2022 Felix Ernst <felixernst@zohomail.eu>
+
+ 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
#include "dolphinviewactionhandler.h"
#include "dolphindebug.h"
+#include "kitemviews/kfileitemlisttostring.h"
#include "kitemviews/kfileitemmodel.h"
#include "settings/viewpropertiesdialog.h"
#include "views/zoomlevelinfo.h"
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"));