File indexing completed on 2024-04-28 05:45:25

0001 /*
0002  * SPDX-FileCopyrightText: 2008 David Faure <faure@kde.org>
0003  * SPDX-FileCopyrightText: 2012 Peter Penz <peter.penz19@gmail.com>
0004  *
0005  * SPDX-License-Identifier: GPL-2.0-or-later
0006  */
0007 
0008 #include "dolphinviewactionhandler.h"
0009 
0010 #include "kitemviews/kfileitemlisttostring.h"
0011 #include "kitemviews/kfileitemmodel.h"
0012 #include "selectionmode/actiontexthelper.h"
0013 #include "settings/viewpropertiesdialog.h"
0014 #include "views/zoomlevelinfo.h"
0015 
0016 #if HAVE_BALOO
0017 #include <Baloo/IndexerConfig>
0018 #endif
0019 #include <KActionCollection>
0020 #include <KActionMenu>
0021 #include <KFileItemListProperties>
0022 #include <KLocalizedString>
0023 #include <KNewFileMenu>
0024 #include <KPropertiesDialog>
0025 #include <KProtocolManager>
0026 
0027 #include <QActionGroup>
0028 #include <QMenu>
0029 #include <QPointer>
0030 
0031 DolphinViewActionHandler::DolphinViewActionHandler(KActionCollection *collection, SelectionMode::ActionTextHelper *actionTextHelper, QObject *parent)
0032     : QObject(parent)
0033     , m_actionCollection(collection)
0034     , m_currentView(nullptr)
0035     , m_sortByActions()
0036     , m_visibleRoles()
0037 {
0038     Q_ASSERT(m_actionCollection);
0039     createActions(actionTextHelper);
0040 }
0041 
0042 void DolphinViewActionHandler::setCurrentView(DolphinView *view)
0043 {
0044     Q_ASSERT(view);
0045 
0046     if (m_currentView) {
0047         disconnect(m_currentView, nullptr, this, nullptr);
0048     }
0049 
0050     m_currentView = view;
0051 
0052     connect(view, &DolphinView::modeChanged, this, &DolphinViewActionHandler::updateViewActions);
0053     connect(view, &DolphinView::previewsShownChanged, this, &DolphinViewActionHandler::slotPreviewsShownChanged);
0054     connect(view, &DolphinView::sortOrderChanged, this, &DolphinViewActionHandler::slotSortOrderChanged);
0055     connect(view, &DolphinView::sortFoldersFirstChanged, this, &DolphinViewActionHandler::slotSortFoldersFirstChanged);
0056     connect(view, &DolphinView::sortHiddenLastChanged, this, &DolphinViewActionHandler::slotSortHiddenLastChanged);
0057     connect(view, &DolphinView::visibleRolesChanged, this, &DolphinViewActionHandler::slotVisibleRolesChanged);
0058     connect(view, &DolphinView::groupedSortingChanged, this, &DolphinViewActionHandler::slotGroupedSortingChanged);
0059     connect(view, &DolphinView::hiddenFilesShownChanged, this, &DolphinViewActionHandler::slotHiddenFilesShownChanged);
0060     connect(view, &DolphinView::sortRoleChanged, this, &DolphinViewActionHandler::slotSortRoleChanged);
0061     connect(view, &DolphinView::zoomLevelChanged, this, &DolphinViewActionHandler::slotZoomLevelChanged);
0062     connect(view, &DolphinView::writeStateChanged, this, &DolphinViewActionHandler::slotWriteStateChanged);
0063     slotWriteStateChanged(view->isFolderWritable());
0064     connect(view, &DolphinView::selectionModeChangeRequested, this, [this](bool enabled) {
0065         Q_EMIT selectionModeChangeTriggered(enabled);
0066     });
0067     connect(view, &DolphinView::selectionChanged, this, &DolphinViewActionHandler::slotSelectionChanged);
0068     slotSelectionChanged(m_currentView->selectedItems());
0069 }
0070 
0071 DolphinView *DolphinViewActionHandler::currentView()
0072 {
0073     return m_currentView;
0074 }
0075 
0076 void DolphinViewActionHandler::createActions(SelectionMode::ActionTextHelper *actionTextHelper)
0077 {
0078     // This action doesn't appear in the GUI, it's for the shortcut only.
0079     // KNewFileMenu takes care of the GUI stuff.
0080     QAction *newDirAction = m_actionCollection->addAction(QStringLiteral("create_dir"));
0081     newDirAction->setText(i18nc("@action", "Create Folder…"));
0082     m_actionCollection->setDefaultShortcuts(newDirAction, KStandardShortcut::createFolder());
0083     newDirAction->setIcon(QIcon::fromTheme(QStringLiteral("folder-new")));
0084     newDirAction->setEnabled(false); // Will be enabled in slotWriteStateChanged(bool) if the current URL is writable
0085     connect(newDirAction, &QAction::triggered, this, &DolphinViewActionHandler::createDirectoryTriggered);
0086 
0087     // File menu
0088 
0089     auto renameAction = KStandardAction::renameFile(this, &DolphinViewActionHandler::slotRename, m_actionCollection);
0090     renameAction->setWhatsThis(xi18nc("@info:whatsthis",
0091                                       "This renames the "
0092                                       "items in your current selection.<nl/>Renaming multiple items "
0093                                       "at once amounts to their new names differing only in a number."));
0094 
0095     auto trashAction = KStandardAction::moveToTrash(this, &DolphinViewActionHandler::slotTrashActivated, m_actionCollection);
0096     auto trashShortcuts = trashAction->shortcuts();
0097     trashAction->setAutoRepeat(false);
0098     if (!trashShortcuts.contains(QKeySequence::Delete)) {
0099         trashShortcuts.append(QKeySequence::Delete);
0100         m_actionCollection->setDefaultShortcuts(trashAction, trashShortcuts);
0101     }
0102     trashAction->setWhatsThis(xi18nc("@info:whatsthis",
0103                                      "This moves the "
0104                                      "items in your current selection to the <filename>Trash"
0105                                      "</filename>.<nl/>The trash is a temporary storage where "
0106                                      "items can be deleted from if disk space is needed."));
0107 
0108     auto deleteAction = KStandardAction::deleteFile(this, &DolphinViewActionHandler::slotDeleteItems, m_actionCollection);
0109     auto deleteShortcuts = deleteAction->shortcuts();
0110     deleteAction->setAutoRepeat(false);
0111     if (!deleteShortcuts.contains(Qt::SHIFT | Qt::Key_Delete)) {
0112         deleteShortcuts.append(Qt::SHIFT | Qt::Key_Delete);
0113         m_actionCollection->setDefaultShortcuts(deleteAction, deleteShortcuts);
0114     }
0115     deleteAction->setWhatsThis(xi18nc("@info:whatsthis",
0116                                       "This deletes "
0117                                       "the items in your current selection completely. They can "
0118                                       "not be recovered by normal means."));
0119 
0120     // This action is useful for being enabled when KStandardAction::MoveToTrash should be
0121     // disabled and KStandardAction::DeleteFile is enabled (e.g. non-local files), so that Key_Del
0122     // can be used for deleting the file (#76016). It needs to be a separate action
0123     // so that the Edit menu isn't affected.
0124     QAction *deleteWithTrashShortcut = m_actionCollection->addAction(QStringLiteral("delete_shortcut"));
0125     // The descriptive text is just for the shortcuts editor.
0126     deleteWithTrashShortcut->setText(i18nc("@action \"Move to Trash\" for non-local files, etc.", "Delete (using shortcut for Trash)"));
0127     m_actionCollection->setDefaultShortcuts(deleteWithTrashShortcut, KStandardShortcut::moveToTrash());
0128     deleteWithTrashShortcut->setEnabled(false);
0129     connect(deleteWithTrashShortcut, &QAction::triggered, this, &DolphinViewActionHandler::slotDeleteItems);
0130 
0131     QAction *duplicateAction = m_actionCollection->addAction(QStringLiteral("duplicate"));
0132     duplicateAction->setText(i18nc("@action:inmenu File", "Duplicate Here"));
0133     duplicateAction->setIcon(QIcon::fromTheme(QStringLiteral("edit-duplicate")));
0134     m_actionCollection->setDefaultShortcut(duplicateAction, Qt::CTRL | Qt::Key_D);
0135     duplicateAction->setEnabled(false);
0136     connect(duplicateAction, &QAction::triggered, this, &DolphinViewActionHandler::slotDuplicate);
0137 
0138     QAction *propertiesAction = m_actionCollection->addAction(QStringLiteral("properties"));
0139     // Well, it's the File menu in dolphinmainwindow and the Edit menu in dolphinpart... :)
0140     propertiesAction->setText(i18nc("@action:inmenu File", "Properties"));
0141     propertiesAction->setWhatsThis(xi18nc("@info:whatsthis properties",
0142                                           "This shows a complete list of properties of the currently "
0143                                           "selected items in a new window.<nl/>If nothing is selected the "
0144                                           "window will be about the currently viewed folder instead.<nl/>"
0145                                           "You can configure advanced options there like managing "
0146                                           "read- and write-permissions."));
0147     propertiesAction->setIcon(QIcon::fromTheme(QStringLiteral("document-properties")));
0148     m_actionCollection->setDefaultShortcuts(propertiesAction, {Qt::ALT | Qt::Key_Return, Qt::ALT | Qt::Key_Enter});
0149     connect(propertiesAction, &QAction::triggered, this, &DolphinViewActionHandler::slotProperties);
0150 
0151     QAction *copyPathAction = m_actionCollection->addAction(QStringLiteral("copy_location"));
0152     copyPathAction->setText(i18nc("@action:incontextmenu", "Copy Location"));
0153     copyPathAction->setWhatsThis(i18nc("@info:whatsthis copy_location", "This will copy the path of the first selected item into the clipboard."));
0154 
0155     copyPathAction->setIcon(QIcon::fromTheme(QStringLiteral("edit-copy-path")));
0156     m_actionCollection->setDefaultShortcuts(copyPathAction, {Qt::CTRL | Qt::ALT | Qt::Key_C});
0157     connect(copyPathAction, &QAction::triggered, this, &DolphinViewActionHandler::slotCopyPath);
0158 
0159     if (actionTextHelper) {
0160         // The "…" at the end make clear that they won't trigger their respective actions directly.
0161         actionTextHelper->registerTextWhenNothingIsSelected(trashAction, i18nc("@action:inmenu File", "Move to Trash…"));
0162         actionTextHelper->registerTextWhenNothingIsSelected(deleteAction, i18nc("@action:inmenu File", "Delete…"));
0163         actionTextHelper->registerTextWhenNothingIsSelected(duplicateAction, i18nc("@action:inmenu File", "Duplicate Here…"));
0164         actionTextHelper->registerTextWhenNothingIsSelected(copyPathAction, i18nc("@action:incontextmenu", "Copy Location…"));
0165     }
0166 
0167     // This menu makes sure that users who don't know how to open a context menu and haven't
0168     // figured out how to enable the menu bar can still perform basic file manipulation.
0169     // This only works if they know how to select a file.
0170     // The text when nothing is selected at least implies that a selection can /somehow/ be made.
0171     // This menu is by default only used in the hamburger menu but created here so users can put
0172     // it on their toolbar.
0173     KActionMenu *basicActionsMenu = m_actionCollection->add<KActionMenu>(QStringLiteral("basic_actions"), this);
0174     // The text is set later depending on the selection in the currently active view.
0175     basicActionsMenu->setPopupMode(QToolButton::InstantPopup);
0176     basicActionsMenu->addAction(m_actionCollection->action(KStandardAction::name(KStandardAction::Cut)));
0177     basicActionsMenu->addAction(m_actionCollection->action(KStandardAction::name(KStandardAction::Copy)));
0178     basicActionsMenu->addAction(m_actionCollection->action(KStandardAction::name(KStandardAction::Paste)));
0179     basicActionsMenu->addSeparator();
0180     basicActionsMenu->addAction(m_actionCollection->action(KStandardAction::name(KStandardAction::RenameFile)));
0181     basicActionsMenu->addAction(m_actionCollection->action(KStandardAction::name(KStandardAction::MoveToTrash)));
0182     basicActionsMenu->addSeparator();
0183     basicActionsMenu->addAction(m_actionCollection->action(QStringLiteral("properties")));
0184     basicActionsMenu->addSeparator(); // We add one more separator because we sometimes add contextual
0185                                       // actions in slotSelectionChanged() after the static ones above.
0186 
0187     // View menu
0188     KToggleAction *iconsAction = iconsModeAction();
0189     KToggleAction *compactAction = compactModeAction();
0190     KToggleAction *detailsAction = detailsModeAction();
0191 
0192     iconsAction->setWhatsThis(xi18nc("@info:whatsthis Icons view mode",
0193                                      "<para>This switches to a view mode that focuses on the folder "
0194                                      "and file icons. This mode makes it easy to distinguish folders "
0195                                      "from files and to detect items with distinctive <emphasis>"
0196                                      "file types</emphasis>.</para><para> This mode is handy to "
0197                                      "browse through pictures when the <interface>Preview"
0198                                      "</interface> option is enabled.</para>"));
0199     compactAction->setWhatsThis(xi18nc("@info:whatsthis Compact view mode",
0200                                        "<para>This switches to a compact view mode that lists the folders "
0201                                        "and files in columns with the names beside the icons.</para><para>"
0202                                        "This helps to keep the overview in folders with many items.</para>"));
0203     detailsAction->setWhatsThis(xi18nc("@info:whatsthis Details view mode",
0204                                        "<para>This switches to a list view mode that focuses on folder "
0205                                        "and file details.</para><para>Click on a detail in the column "
0206                                        "header to sort the items by it. Click again to sort the other "
0207                                        "way around. To select which details should be displayed click "
0208                                        "the header with the right mouse button.</para><para>You can "
0209                                        "view the contents of a folder without leaving the current "
0210                                        "location by clicking to the left of it. This way you can view "
0211                                        "the contents of multiple folders in the same list.</para>"));
0212 
0213     KSelectAction *viewModeActions = m_actionCollection->add<KSelectAction>(QStringLiteral("view_mode"));
0214     viewModeActions->setText(i18nc("@action:intoolbar", "View Mode"));
0215     viewModeActions->addAction(iconsAction);
0216     viewModeActions->addAction(compactAction);
0217     viewModeActions->addAction(detailsAction);
0218     viewModeActions->setToolBarMode(KSelectAction::MenuMode);
0219     connect(viewModeActions, &KSelectAction::actionTriggered, this, &DolphinViewActionHandler::slotViewModeActionTriggered);
0220 
0221     QAction *zoomInAction = KStandardAction::zoomIn(this, &DolphinViewActionHandler::zoomIn, m_actionCollection);
0222     zoomInAction->setWhatsThis(i18nc("@info:whatsthis zoom in", "This increases the icon size."));
0223 
0224     QAction *zoomResetAction = m_actionCollection->addAction(QStringLiteral("view_zoom_reset"));
0225     zoomResetAction->setText(i18nc("@action:inmenu View", "Reset Zoom Level"));
0226     zoomResetAction->setToolTip(i18n("Zoom To Default"));
0227     zoomResetAction->setWhatsThis(i18nc("@info:whatsthis zoom reset", "This resets the icon size to default."));
0228     zoomResetAction->setIcon(QIcon::fromTheme(QStringLiteral("zoom-original")));
0229     m_actionCollection->setDefaultShortcuts(zoomResetAction, {Qt::CTRL | Qt::Key_0});
0230     connect(zoomResetAction, &QAction::triggered, this, &DolphinViewActionHandler::zoomReset);
0231 
0232     QAction *zoomOutAction = KStandardAction::zoomOut(this, &DolphinViewActionHandler::zoomOut, m_actionCollection);
0233     zoomOutAction->setWhatsThis(i18nc("@info:whatsthis zoom out", "This reduces the icon size."));
0234 
0235     KActionMenu *zoomMenu = m_actionCollection->add<KActionMenu>(QStringLiteral("zoom"));
0236     zoomMenu->setText(i18nc("@action:inmenu menu of zoom actions", "Zoom"));
0237     zoomMenu->setIcon(QIcon::fromTheme(QStringLiteral("zoom")));
0238     zoomMenu->setPopupMode(QToolButton::InstantPopup);
0239     zoomMenu->addAction(zoomInAction);
0240     zoomMenu->addAction(zoomResetAction);
0241     zoomMenu->addAction(zoomOutAction);
0242 
0243     KToggleAction *showPreview = m_actionCollection->add<KToggleAction>(QStringLiteral("show_preview"));
0244     showPreview->setText(i18nc("@action:intoolbar", "Show Previews"));
0245     showPreview->setToolTip(i18nc("@info", "Show preview of files and folders"));
0246     showPreview->setWhatsThis(xi18nc("@info:whatsthis",
0247                                      "When this is "
0248                                      "enabled, the icons are based on the actual file or folder "
0249                                      "contents.<nl/>For example the icons of images become scaled "
0250                                      "down versions of the images."));
0251     showPreview->setIcon(QIcon::fromTheme(QStringLiteral("view-preview")));
0252     m_actionCollection->setDefaultShortcut(showPreview, QKeySequence(Qt::Key_F12));
0253     connect(showPreview, &KToggleAction::triggered, this, &DolphinViewActionHandler::togglePreview);
0254 
0255     KToggleAction *sortFoldersFirst = m_actionCollection->add<KToggleAction>(QStringLiteral("folders_first"));
0256     sortFoldersFirst->setText(i18nc("@action:inmenu Sort", "Folders First"));
0257     connect(sortFoldersFirst, &KToggleAction::triggered, this, &DolphinViewActionHandler::toggleSortFoldersFirst);
0258 
0259     KToggleAction *sortHiddenLast = m_actionCollection->add<KToggleAction>(QStringLiteral("hidden_last"));
0260     sortHiddenLast->setText(i18nc("@action:inmenu Sort", "Hidden Files Last"));
0261     connect(sortHiddenLast, &KToggleAction::triggered, this, &DolphinViewActionHandler::toggleSortHiddenLast);
0262 
0263     // View -> Sort By
0264     QActionGroup *sortByActionGroup = createFileItemRolesActionGroup(QStringLiteral("sort_by_"));
0265 
0266     KActionMenu *sortByActionMenu = m_actionCollection->add<KActionMenu>(QStringLiteral("sort"));
0267     sortByActionMenu->setIcon(QIcon::fromTheme(QStringLiteral("view-sort")));
0268     sortByActionMenu->setText(i18nc("@action:inmenu View", "Sort By"));
0269     sortByActionMenu->setPopupMode(QToolButton::InstantPopup);
0270 
0271     const auto sortByActionGroupActions = sortByActionGroup->actions();
0272     for (QAction *action : sortByActionGroupActions) {
0273         sortByActionMenu->addAction(action);
0274     }
0275 
0276     sortByActionMenu->addSeparator();
0277 
0278     QActionGroup *group = new QActionGroup(sortByActionMenu);
0279     group->setExclusive(true);
0280 
0281     KToggleAction *ascendingAction = m_actionCollection->add<KToggleAction>(QStringLiteral("ascending"));
0282     ascendingAction->setActionGroup(group);
0283     connect(ascendingAction, &QAction::triggered, this, [this] {
0284         m_currentView->setSortOrder(Qt::AscendingOrder);
0285     });
0286 
0287     KToggleAction *descendingAction = m_actionCollection->add<KToggleAction>(QStringLiteral("descending"));
0288     descendingAction->setActionGroup(group);
0289     connect(descendingAction, &QAction::triggered, this, [this] {
0290         m_currentView->setSortOrder(Qt::DescendingOrder);
0291     });
0292 
0293     sortByActionMenu->addAction(ascendingAction);
0294     sortByActionMenu->addAction(descendingAction);
0295     sortByActionMenu->addSeparator();
0296     sortByActionMenu->addAction(sortFoldersFirst);
0297     sortByActionMenu->addAction(sortHiddenLast);
0298 
0299     // View -> Additional Information
0300     QActionGroup *visibleRolesGroup = createFileItemRolesActionGroup(QStringLiteral("show_"));
0301 
0302     KActionMenu *visibleRolesMenu = m_actionCollection->add<KActionMenu>(QStringLiteral("additional_info"));
0303     visibleRolesMenu->setText(i18nc("@action:inmenu View", "Show Additional Information"));
0304     visibleRolesMenu->setIcon(QIcon::fromTheme(QStringLiteral("documentinfo")));
0305     visibleRolesMenu->setPopupMode(QToolButton::InstantPopup);
0306 
0307     const auto visibleRolesGroupActions = visibleRolesGroup->actions();
0308     for (QAction *action : visibleRolesGroupActions) {
0309         visibleRolesMenu->addAction(action);
0310     }
0311 
0312     KToggleAction *showInGroups = m_actionCollection->add<KToggleAction>(QStringLiteral("show_in_groups"));
0313     showInGroups->setIcon(QIcon::fromTheme(QStringLiteral("view-group")));
0314     showInGroups->setText(i18nc("@action:inmenu View", "Show in Groups"));
0315     showInGroups->setWhatsThis(i18nc("@info:whatsthis", "This groups files and folders by their first letter."));
0316     connect(showInGroups, &KToggleAction::triggered, this, &DolphinViewActionHandler::toggleGroupedSorting);
0317 
0318     KToggleAction *showHiddenFiles = m_actionCollection->add<KToggleAction>(QStringLiteral("show_hidden_files"));
0319     showHiddenFiles->setIcon(QIcon::fromTheme(QStringLiteral("view-visible")));
0320     showHiddenFiles->setText(i18nc("@action:inmenu View", "Show Hidden Files"));
0321     showHiddenFiles->setWhatsThis(xi18nc("@info:whatsthis",
0322                                          "<para>When "
0323                                          "this is enabled <emphasis>hidden</emphasis> files and folders "
0324                                          "are visible. They will be displayed semi-transparent.</para>"
0325                                          "<para>Hidden items only differ from other ones in that their "
0326                                          "name starts with a \".\". In general there is no need for "
0327                                          "users to access them which is why they are hidden.</para>"));
0328     m_actionCollection->setDefaultShortcuts(showHiddenFiles, KStandardShortcut::showHideHiddenFiles());
0329     connect(showHiddenFiles, &KToggleAction::triggered, this, &DolphinViewActionHandler::toggleShowHiddenFiles);
0330 
0331     QAction *adjustViewProps = m_actionCollection->addAction(QStringLiteral("view_properties"));
0332     adjustViewProps->setText(i18nc("@action:inmenu View", "Adjust View Display Style…"));
0333     adjustViewProps->setIcon(QIcon::fromTheme(QStringLiteral("view-choose")));
0334     adjustViewProps->setWhatsThis(i18nc("@info:whatsthis",
0335                                         "This opens a window "
0336                                         "in which all folder view properties can be adjusted."));
0337     connect(adjustViewProps, &QAction::triggered, this, &DolphinViewActionHandler::slotAdjustViewProperties);
0338 }
0339 
0340 QActionGroup *DolphinViewActionHandler::createFileItemRolesActionGroup(const QString &groupPrefix)
0341 {
0342     const bool isSortGroup = (groupPrefix == QLatin1String("sort_by_"));
0343     Q_ASSERT(isSortGroup || groupPrefix == QLatin1String("show_"));
0344 
0345     QActionGroup *rolesActionGroup = new QActionGroup(m_actionCollection);
0346     rolesActionGroup->setExclusive(isSortGroup);
0347     if (isSortGroup) {
0348         connect(rolesActionGroup, &QActionGroup::triggered, this, &DolphinViewActionHandler::slotSortTriggered);
0349     } else {
0350         connect(rolesActionGroup, &QActionGroup::triggered, this, &DolphinViewActionHandler::toggleVisibleRole);
0351     }
0352 
0353     QString groupName;
0354     KActionMenu *groupMenu = nullptr;
0355     QActionGroup *groupMenuGroup = nullptr;
0356 
0357     bool indexingEnabled = false;
0358 #if HAVE_BALOO
0359     Baloo::IndexerConfig config;
0360     indexingEnabled = config.fileIndexingEnabled();
0361 #endif
0362 
0363     const QList<KFileItemModel::RoleInfo> rolesInfo = KFileItemModel::rolesInformation();
0364     for (const KFileItemModel::RoleInfo &info : rolesInfo) {
0365         if (!isSortGroup && info.role == "text") {
0366             // It should not be possible to hide the "text" role
0367             continue;
0368         }
0369 
0370         KToggleAction *action = nullptr;
0371         const QString name = groupPrefix + info.role;
0372         if (info.group.isEmpty()) {
0373             action = m_actionCollection->add<KToggleAction>(name);
0374             action->setActionGroup(rolesActionGroup);
0375         } else {
0376             if (!groupMenu || info.group != groupName) {
0377                 groupName = info.group;
0378                 groupMenu = m_actionCollection->add<KActionMenu>(groupName);
0379                 groupMenu->setText(groupName);
0380                 groupMenu->setActionGroup(rolesActionGroup);
0381 
0382                 groupMenuGroup = new QActionGroup(groupMenu);
0383                 groupMenuGroup->setExclusive(isSortGroup);
0384                 if (isSortGroup) {
0385                     connect(groupMenuGroup, &QActionGroup::triggered, this, &DolphinViewActionHandler::slotSortTriggered);
0386                 } else {
0387                     connect(groupMenuGroup, &QActionGroup::triggered, this, &DolphinViewActionHandler::toggleVisibleRole);
0388                 }
0389             }
0390 
0391             action = new KToggleAction(groupMenu);
0392             action->setActionGroup(groupMenuGroup);
0393             groupMenu->addAction(action);
0394         }
0395         action->setText(info.translation);
0396         action->setData(info.role);
0397 
0398         const bool enable = (!info.requiresBaloo && !info.requiresIndexer) || (info.requiresBaloo) || (info.requiresIndexer && indexingEnabled);
0399         action->setEnabled(enable);
0400 
0401         if (isSortGroup) {
0402             m_sortByActions.insert(info.role, action);
0403         } else {
0404             m_visibleRoles.insert(info.role, action);
0405         }
0406     }
0407 
0408     return rolesActionGroup;
0409 }
0410 
0411 void DolphinViewActionHandler::slotViewModeActionTriggered(QAction *action)
0412 {
0413     const DolphinView::Mode mode = action->data().value<DolphinView::Mode>();
0414     m_currentView->setViewMode(mode);
0415 
0416     QAction *viewModeMenu = m_actionCollection->action(QStringLiteral("view_mode"));
0417     viewModeMenu->setIcon(action->icon());
0418 }
0419 
0420 void DolphinViewActionHandler::slotRename()
0421 {
0422     if (m_currentView->selectedItemsCount() == 0) {
0423         Q_EMIT selectionModeChangeTriggered(true, SelectionMode::BottomBar::Contents::RenameContents);
0424     } else {
0425         Q_EMIT actionBeingHandled();
0426         m_currentView->renameSelectedItems();
0427         // We don't exit selectionMode here because users might want to rename more items.
0428     }
0429 }
0430 
0431 void DolphinViewActionHandler::slotTrashActivated()
0432 {
0433     if (m_currentView->selectedItemsCount() == 0) {
0434         Q_EMIT selectionModeChangeTriggered(true, SelectionMode::BottomBar::Contents::MoveToTrashContents);
0435     } else {
0436         Q_EMIT actionBeingHandled();
0437         m_currentView->trashSelectedItems();
0438         Q_EMIT selectionModeChangeTriggered(false);
0439     }
0440 }
0441 
0442 void DolphinViewActionHandler::slotDeleteItems()
0443 {
0444     if (m_currentView->selectedItemsCount() == 0) {
0445         Q_EMIT selectionModeChangeTriggered(true, SelectionMode::BottomBar::Contents::DeleteContents);
0446     } else {
0447         Q_EMIT actionBeingHandled();
0448         m_currentView->deleteSelectedItems();
0449         Q_EMIT selectionModeChangeTriggered(false);
0450     }
0451 }
0452 
0453 void DolphinViewActionHandler::togglePreview(bool show)
0454 {
0455     Q_EMIT actionBeingHandled();
0456     m_currentView->setPreviewsShown(show);
0457 }
0458 
0459 void DolphinViewActionHandler::slotPreviewsShownChanged(bool shown)
0460 {
0461     Q_UNUSED(shown)
0462     // It is not enough to update the 'Show Preview' action, also
0463     // the 'Zoom In', 'Zoom Out' and 'Zoom Reset' actions must be adapted.
0464     updateViewActions();
0465 }
0466 
0467 QString DolphinViewActionHandler::currentViewModeActionName() const
0468 {
0469     switch (m_currentView->viewMode()) {
0470     case DolphinView::IconsView:
0471         return QStringLiteral("icons");
0472     case DolphinView::DetailsView:
0473         return QStringLiteral("details");
0474     case DolphinView::CompactView:
0475         return QStringLiteral("compact");
0476     default:
0477         Q_ASSERT(false);
0478         break;
0479     }
0480     return QString(); // can't happen
0481 }
0482 
0483 KActionCollection *DolphinViewActionHandler::actionCollection()
0484 {
0485     return m_actionCollection;
0486 }
0487 
0488 void DolphinViewActionHandler::updateViewActions()
0489 {
0490     QAction *viewModeAction = m_actionCollection->action(currentViewModeActionName());
0491     if (viewModeAction) {
0492         viewModeAction->setChecked(true);
0493 
0494         QAction *viewModeMenu = m_actionCollection->action(QStringLiteral("view_mode"));
0495         viewModeMenu->setIcon(viewModeAction->icon());
0496     }
0497 
0498     QAction *showPreviewAction = m_actionCollection->action(QStringLiteral("show_preview"));
0499     showPreviewAction->setChecked(m_currentView->previewsShown());
0500 
0501     slotSortOrderChanged(m_currentView->sortOrder());
0502     slotSortFoldersFirstChanged(m_currentView->sortFoldersFirst());
0503     slotSortHiddenLastChanged(m_currentView->sortHiddenLast());
0504     slotVisibleRolesChanged(m_currentView->visibleRoles(), QList<QByteArray>());
0505     slotGroupedSortingChanged(m_currentView->groupedSorting());
0506     slotSortRoleChanged(m_currentView->sortRole());
0507     slotZoomLevelChanged(m_currentView->zoomLevel(), -1);
0508 
0509     // Updates the "show_hidden_files" action state and icon
0510     slotHiddenFilesShownChanged(m_currentView->hiddenFilesShown());
0511 }
0512 
0513 void DolphinViewActionHandler::zoomIn()
0514 {
0515     const int level = m_currentView->zoomLevel();
0516     m_currentView->setZoomLevel(level + 1);
0517     updateViewActions();
0518 }
0519 
0520 void DolphinViewActionHandler::zoomOut()
0521 {
0522     const int level = m_currentView->zoomLevel();
0523     m_currentView->setZoomLevel(level - 1);
0524     updateViewActions();
0525 }
0526 
0527 void DolphinViewActionHandler::zoomReset()
0528 {
0529     m_currentView->resetZoomLevel();
0530     updateViewActions();
0531 }
0532 
0533 void DolphinViewActionHandler::toggleSortFoldersFirst()
0534 {
0535     const bool sortFirst = m_currentView->sortFoldersFirst();
0536     m_currentView->setSortFoldersFirst(!sortFirst);
0537 }
0538 
0539 void DolphinViewActionHandler::toggleSortHiddenLast()
0540 {
0541     const bool sortHiddenLast = m_currentView->sortHiddenLast();
0542     m_currentView->setSortHiddenLast(!sortHiddenLast);
0543 }
0544 
0545 void DolphinViewActionHandler::slotSortOrderChanged(Qt::SortOrder order)
0546 {
0547     QAction *descending = m_actionCollection->action(QStringLiteral("descending"));
0548     QAction *ascending = m_actionCollection->action(QStringLiteral("ascending"));
0549     const bool sortDescending = (order == Qt::DescendingOrder);
0550     descending->setChecked(sortDescending);
0551     ascending->setChecked(!sortDescending);
0552 }
0553 
0554 void DolphinViewActionHandler::slotSortFoldersFirstChanged(bool foldersFirst)
0555 {
0556     m_actionCollection->action(QStringLiteral("folders_first"))->setChecked(foldersFirst);
0557 }
0558 
0559 void DolphinViewActionHandler::slotSortHiddenLastChanged(bool hiddenLast)
0560 {
0561     m_actionCollection->action(QStringLiteral("hidden_last"))->setChecked(hiddenLast);
0562 }
0563 
0564 void DolphinViewActionHandler::toggleVisibleRole(QAction *action)
0565 {
0566     Q_EMIT actionBeingHandled();
0567 
0568     const QByteArray toggledRole = action->data().toByteArray();
0569 
0570     QList<QByteArray> roles = m_currentView->visibleRoles();
0571 
0572     const bool show = action->isChecked();
0573 
0574     const int index = roles.indexOf(toggledRole);
0575     const bool containsInfo = (index >= 0);
0576     if (show && !containsInfo) {
0577         roles.append(toggledRole);
0578         m_currentView->setVisibleRoles(roles);
0579     } else if (!show && containsInfo) {
0580         roles.removeAt(index);
0581         m_currentView->setVisibleRoles(roles);
0582         Q_ASSERT(roles.indexOf(toggledRole) < 0);
0583     }
0584 }
0585 
0586 void DolphinViewActionHandler::slotVisibleRolesChanged(const QList<QByteArray> &current, const QList<QByteArray> &previous)
0587 {
0588     Q_UNUSED(previous)
0589 
0590     const auto checkedRoles = QSet<QByteArray>(current.constBegin(), current.constEnd());
0591     QHashIterator<QByteArray, KToggleAction *> it(m_visibleRoles);
0592     while (it.hasNext()) {
0593         it.next();
0594         const QByteArray &role = it.key();
0595         KToggleAction *action = it.value();
0596         action->setChecked(checkedRoles.contains(role));
0597     }
0598 }
0599 
0600 void DolphinViewActionHandler::toggleGroupedSorting(bool grouped)
0601 {
0602     m_currentView->setGroupedSorting(grouped);
0603 }
0604 
0605 void DolphinViewActionHandler::slotGroupedSortingChanged(bool groupedSorting)
0606 {
0607     QAction *showInGroupsAction = m_actionCollection->action(QStringLiteral("show_in_groups"));
0608     showInGroupsAction->setChecked(groupedSorting);
0609 }
0610 
0611 void DolphinViewActionHandler::toggleShowHiddenFiles(bool show)
0612 {
0613     Q_EMIT actionBeingHandled();
0614     m_currentView->setHiddenFilesShown(show);
0615 }
0616 
0617 void DolphinViewActionHandler::slotHiddenFilesShownChanged(bool shown)
0618 {
0619     QAction *showHiddenFilesAction = m_actionCollection->action(QStringLiteral("show_hidden_files"));
0620     showHiddenFilesAction->setChecked(shown);
0621 }
0622 
0623 void DolphinViewActionHandler::slotWriteStateChanged(bool isFolderWritable)
0624 {
0625     m_actionCollection->action(QStringLiteral("create_dir"))->setEnabled(isFolderWritable && KProtocolManager::supportsMakeDir(currentView()->url()));
0626 }
0627 
0628 KToggleAction *DolphinViewActionHandler::iconsModeAction()
0629 {
0630     KToggleAction *iconsView = m_actionCollection->add<KToggleAction>(QStringLiteral("icons"));
0631     iconsView->setText(i18nc("@action:inmenu View Mode", "Icons"));
0632     iconsView->setToolTip(i18nc("@info", "Icons view mode"));
0633     m_actionCollection->setDefaultShortcut(iconsView, Qt::CTRL | Qt::Key_1);
0634     iconsView->setIcon(QIcon::fromTheme(QStringLiteral("view-list-icons")));
0635     iconsView->setData(QVariant::fromValue(DolphinView::IconsView));
0636     return iconsView;
0637 }
0638 
0639 KToggleAction *DolphinViewActionHandler::compactModeAction()
0640 {
0641     KToggleAction *iconsView = m_actionCollection->add<KToggleAction>(QStringLiteral("compact"));
0642     iconsView->setText(i18nc("@action:inmenu View Mode", "Compact"));
0643     iconsView->setToolTip(i18nc("@info", "Compact view mode"));
0644     m_actionCollection->setDefaultShortcut(iconsView, Qt::CTRL | Qt::Key_2);
0645     iconsView->setIcon(QIcon::fromTheme(QStringLiteral("view-list-details"))); // TODO: discuss with Oxygen-team the wrong (?) name
0646     iconsView->setData(QVariant::fromValue(DolphinView::CompactView));
0647     return iconsView;
0648 }
0649 
0650 KToggleAction *DolphinViewActionHandler::detailsModeAction()
0651 {
0652     KToggleAction *detailsView = m_actionCollection->add<KToggleAction>(QStringLiteral("details"));
0653     detailsView->setText(i18nc("@action:inmenu View Mode", "Details"));
0654     detailsView->setToolTip(i18nc("@info", "Details view mode"));
0655     m_actionCollection->setDefaultShortcut(detailsView, Qt::CTRL | Qt::Key_3);
0656     detailsView->setIcon(QIcon::fromTheme(QStringLiteral("view-list-tree")));
0657     detailsView->setData(QVariant::fromValue(DolphinView::DetailsView));
0658     return detailsView;
0659 }
0660 
0661 void DolphinViewActionHandler::slotSortRoleChanged(const QByteArray &role)
0662 {
0663     KToggleAction *action = m_sortByActions.value(role);
0664     if (action) {
0665         action->setChecked(true);
0666 
0667         if (!action->icon().isNull()) {
0668             QAction *sortByMenu = m_actionCollection->action(QStringLiteral("sort"));
0669             sortByMenu->setIcon(action->icon());
0670         }
0671     }
0672 
0673     QAction *descending = m_actionCollection->action(QStringLiteral("descending"));
0674     QAction *ascending = m_actionCollection->action(QStringLiteral("ascending"));
0675 
0676     if (role == "text" || role == "type" || role == "extension" || role == "tags" || role == "comment") {
0677         descending->setText(i18nc("Sort descending", "Z-A"));
0678         ascending->setText(i18nc("Sort ascending", "A-Z"));
0679     } else if (role == "size") {
0680         descending->setText(i18nc("Sort descending", "Largest First"));
0681         ascending->setText(i18nc("Sort ascending", "Smallest First"));
0682     } else if (role == "modificationtime" || role == "creationtime" || role == "accesstime") {
0683         descending->setText(i18nc("Sort descending", "Newest First"));
0684         ascending->setText(i18nc("Sort ascending", "Oldest First"));
0685     } else if (role == "rating") {
0686         descending->setText(i18nc("Sort descending", "Highest First"));
0687         ascending->setText(i18nc("Sort ascending", "Lowest First"));
0688     } else {
0689         descending->setText(i18nc("Sort descending", "Descending"));
0690         ascending->setText(i18nc("Sort ascending", "Ascending"));
0691     }
0692 
0693     slotSortOrderChanged(m_currentView->sortOrder());
0694 }
0695 
0696 void DolphinViewActionHandler::slotZoomLevelChanged(int current, int previous)
0697 {
0698     Q_UNUSED(previous)
0699 
0700     QAction *zoomInAction = m_actionCollection->action(KStandardAction::name(KStandardAction::ZoomIn));
0701     if (zoomInAction) {
0702         zoomInAction->setEnabled(current < ZoomLevelInfo::maximumLevel());
0703     }
0704 
0705     QAction *zoomOutAction = m_actionCollection->action(KStandardAction::name(KStandardAction::ZoomOut));
0706     if (zoomOutAction) {
0707         zoomOutAction->setEnabled(current > ZoomLevelInfo::minimumLevel());
0708     }
0709 }
0710 
0711 void DolphinViewActionHandler::slotSortTriggered(QAction *action)
0712 {
0713     // The radiobuttons of the "Sort By"-menu are split between the main-menu
0714     // and several sub-menus. Because of this they don't have a common
0715     // action-group that assures an exclusive toggle-state between the main-menu
0716     // actions and the sub-menu-actions. If an action gets checked, it must
0717     // be assured that all other actions get unchecked, except the ascending/
0718     // descending actions
0719     for (QAction *groupAction : std::as_const(m_sortByActions)) {
0720         KActionMenu *actionMenu = qobject_cast<KActionMenu *>(groupAction);
0721         if (actionMenu) {
0722             const auto actions = actionMenu->menu()->actions();
0723             for (QAction *subAction : actions) {
0724                 subAction->setChecked(false);
0725             }
0726         } else if (groupAction->actionGroup()) {
0727             groupAction->setChecked(false);
0728         }
0729     }
0730     action->setChecked(true);
0731 
0732     // Apply the activated sort-role to the view
0733     const QByteArray role = action->data().toByteArray();
0734     m_currentView->setSortRole(role);
0735 }
0736 
0737 void DolphinViewActionHandler::slotAdjustViewProperties()
0738 {
0739     Q_EMIT actionBeingHandled();
0740     QPointer<ViewPropertiesDialog> dialog = new ViewPropertiesDialog(m_currentView);
0741     dialog->exec();
0742     delete dialog;
0743 }
0744 
0745 void DolphinViewActionHandler::slotDuplicate()
0746 {
0747     if (m_currentView->selectedItemsCount() == 0) {
0748         Q_EMIT selectionModeChangeTriggered(true, SelectionMode::BottomBar::Contents::DuplicateContents);
0749     } else {
0750         Q_EMIT actionBeingHandled();
0751         m_currentView->duplicateSelectedItems();
0752         Q_EMIT selectionModeChangeTriggered(false);
0753     }
0754 }
0755 
0756 void DolphinViewActionHandler::slotProperties()
0757 {
0758     KPropertiesDialog *dialog = nullptr;
0759     const KFileItemList list = m_currentView->selectedItems();
0760     if (list.isEmpty()) {
0761         const QUrl url = m_currentView->url();
0762         dialog = new KPropertiesDialog(url, m_currentView);
0763     } else {
0764         dialog = new KPropertiesDialog(list, m_currentView);
0765     }
0766 
0767     dialog->setAttribute(Qt::WA_DeleteOnClose);
0768     dialog->show();
0769     dialog->raise();
0770     dialog->activateWindow();
0771 }
0772 
0773 void DolphinViewActionHandler::slotCopyPath()
0774 {
0775     if (m_currentView->selectedItemsCount() == 0) {
0776         Q_EMIT selectionModeChangeTriggered(true, SelectionMode::BottomBar::Contents::CopyLocationContents);
0777     } else {
0778         m_currentView->copyPathToClipboard();
0779         Q_EMIT selectionModeChangeTriggered(false);
0780     }
0781 }
0782 
0783 void DolphinViewActionHandler::slotSelectionChanged(const KFileItemList &selection)
0784 {
0785     QString basicActionsMenuText;
0786     if (selection.isEmpty()) {
0787         basicActionsMenuText = i18nc("@action:inmenu menu with actions like copy, paste, rename. The user's selection is empty when this text is shown.",
0788                                      "Actions for Current View");
0789     } else {
0790         // clang-format off
0791         QFontMetrics fontMetrics = QMenu().fontMetrics();
0792         // i18n: @action:inmenu menu with actions like copy, paste, rename.
0793         // %1 is a textual representation of the currently selected files or folders. This can be the name of
0794         // the file/files like "file1" or "file1, file2 and file3" or an aggregate like "8 Selected Folders".
0795         // If this sort of word puzzle can not be correctly translated in your language, translate it as "NULL" (without the quotes)
0796         // and a fallback will be used.
0797         basicActionsMenuText = i18n("Actions for %1", fileItemListToString(selection, fontMetrics.averageCharWidth() * 40, fontMetrics, ItemsState::Selected));
0798         // clang-format on
0799     }
0800 
0801     if (basicActionsMenuText == QStringLiteral("NULL")) {
0802         const KFileItemListProperties properties(selection);
0803         basicActionsMenuText = i18ncp("@action:inmenu menu with actions like copy, paste, rename. %1 is the amount of selected files/folders.",
0804                                       "Actions for One Selected Item",
0805                                       "Actions for %1 Selected Items",
0806                                       selection.count());
0807     }
0808 
0809     QAction *basicActionsMenu = m_actionCollection->action(QStringLiteral("basic_actions"));
0810     basicActionsMenu->setText(basicActionsMenuText);
0811 
0812     // Add or remove contextual actions
0813     while (!basicActionsMenu->menu()->actions().constLast()->isSeparator()) {
0814         basicActionsMenu->menu()->removeAction(basicActionsMenu->menu()->actions().last());
0815     }
0816     if (selection.count() == 1) {
0817         if (selection.first().isLink()) {
0818             basicActionsMenu->menu()->addAction(m_actionCollection->action(QStringLiteral("show_target")));
0819         }
0820         if (selection.first().isDir()) {
0821             basicActionsMenu->menu()->addAction(m_actionCollection->action(QStringLiteral("add_to_places")));
0822         }
0823     }
0824 }
0825 
0826 #include "moc_dolphinviewactionhandler.cpp"