dolphinviewactionhandler.cpp 36.6 KB
Newer Older
1
2
3
4
5
6
/*
 * SPDX-FileCopyrightText: 2008 David Faure <faure@kde.org>
 * SPDX-FileCopyrightText: 2012 Peter Penz <peter.penz19@gmail.com>
 *
 * SPDX-License-Identifier: GPL-2.0-or-later
 */
7
8
9

#include "dolphinviewactionhandler.h"

Roman Inflianskas's avatar
Roman Inflianskas committed
10
11
#include "dolphindebug.h"
#include "kitemviews/kfileitemmodel.h"
12
#include "settings/viewpropertiesdialog.h"
13
#include "views/zoomlevelinfo.h"
14
#include "kconfig_version.h"
15

Roman Inflianskas's avatar
Roman Inflianskas committed
16
17
18
#ifdef HAVE_BALOO
#include <Baloo/IndexerConfig>
#endif
Peter Penz's avatar
Peter Penz committed
19
20
#include <KActionCollection>
#include <KActionMenu>
21
#include <KFileItemListProperties>
Laurent Montel's avatar
Laurent Montel committed
22
#include <KLocalizedString>
23
#include <KNewFileMenu>
Peter Penz's avatar
Peter Penz committed
24
#include <KPropertiesDialog>
25
#include <KProtocolManager>
Nicolas Fella's avatar
Nicolas Fella committed
26
27

#include <QActionGroup>
Roman Inflianskas's avatar
Roman Inflianskas committed
28
29
#include <QMenu>
#include <QPointer>
Vishesh Handa's avatar
Vishesh Handa committed
30

31
32
33
DolphinViewActionHandler::DolphinViewActionHandler(KActionCollection* collection, QObject* parent) :
    QObject(parent),
    m_actionCollection(collection),
Kevin Funk's avatar
Kevin Funk committed
34
    m_currentView(nullptr),
35
36
    m_sortByActions(),
    m_visibleRoles()
37
38
39
40
41
42
43
44
45
{
    Q_ASSERT(m_actionCollection);
    createActions();
}

void DolphinViewActionHandler::setCurrentView(DolphinView* view)
{
    Q_ASSERT(view);

46
    if (m_currentView) {
Kevin Funk's avatar
Kevin Funk committed
47
        disconnect(m_currentView, nullptr, this, nullptr);
48
    }
49
50
51

    m_currentView = view;

52
53
54
55
56
57
58
59
    connect(view, &DolphinView::modeChanged,
            this, &DolphinViewActionHandler::updateViewActions);
    connect(view, &DolphinView::previewsShownChanged,
            this, &DolphinViewActionHandler::slotPreviewsShownChanged);
    connect(view, &DolphinView::sortOrderChanged,
            this, &DolphinViewActionHandler::slotSortOrderChanged);
    connect(view, &DolphinView::sortFoldersFirstChanged,
            this, &DolphinViewActionHandler::slotSortFoldersFirstChanged);
60
61
    connect(view, &DolphinView::sortHiddenLastChanged,
            this, &DolphinViewActionHandler::slotSortHiddenLastChanged);
62
63
64
65
66
67
68
69
70
71
72
73
    connect(view, &DolphinView::visibleRolesChanged,
            this, &DolphinViewActionHandler::slotVisibleRolesChanged);
    connect(view, &DolphinView::groupedSortingChanged,
            this, &DolphinViewActionHandler::slotGroupedSortingChanged);
    connect(view, &DolphinView::hiddenFilesShownChanged,
            this, &DolphinViewActionHandler::slotHiddenFilesShownChanged);
    connect(view, &DolphinView::sortRoleChanged,
            this, &DolphinViewActionHandler::slotSortRoleChanged);
    connect(view, &DolphinView::zoomLevelChanged,
            this, &DolphinViewActionHandler::slotZoomLevelChanged);
    connect(view, &DolphinView::writeStateChanged,
            this, &DolphinViewActionHandler::slotWriteStateChanged);
74
75
76
    connect(view, &DolphinView::selectionChanged,
            this, &DolphinViewActionHandler::slotSelectionChanged);
    slotSelectionChanged(m_currentView->selectedItems());
77
78
}

Rahman Duran's avatar
Rahman Duran committed
79
80
81
82
83
DolphinView* DolphinViewActionHandler::currentView()
{
    return m_currentView;
}

84
85
86
void DolphinViewActionHandler::createActions()
{
    // This action doesn't appear in the GUI, it's for the shortcut only.
87
    // KNewFileMenu takes care of the GUI stuff.
88
    QAction* newDirAction = m_actionCollection->addAction(QStringLiteral("create_dir"));
89
    newDirAction->setText(i18nc("@action", "Create Folder..."));
90
91
    m_actionCollection->setDefaultShortcuts(newDirAction, KStandardShortcut::createFolder());
    newDirAction->setIcon(QIcon::fromTheme(QStringLiteral("folder-new")));
92
    newDirAction->setEnabled(false);    // Will be enabled in slotWriteStateChanged(bool) if the current URL is writable
93
    connect(newDirAction, &QAction::triggered, this, &DolphinViewActionHandler::createDirectoryTriggered);
94

95
    // File menu
96

97
98
99
100
    auto renameAction = KStandardAction::renameFile(this, &DolphinViewActionHandler::slotRename, m_actionCollection);
    renameAction->setWhatsThis(xi18nc("@info:whatsthis", "This renames the "
        "items in your current selection.<nl/>Renaming multiple items "
        "at once amounts to their new names differing only in a number."));
101

102
103
104
105
106
107
    auto trashAction = KStandardAction::moveToTrash(this, &DolphinViewActionHandler::slotTrashActivated, m_actionCollection);
    auto trashShortcuts = trashAction->shortcuts();
    if (!trashShortcuts.contains(QKeySequence::Delete)) {
        trashShortcuts.append(QKeySequence::Delete);
        m_actionCollection->setDefaultShortcuts(trashAction, trashShortcuts);
    }
108
109
110
111
    trashAction->setWhatsThis(xi18nc("@info:whatsthis", "This moves the "
        "items in your current selection to the <filename>Trash"
        "</filename>.<nl/>The trash is a temporary storage where "
        "items can be deleted from if disk space is needed."));
112

113
114
115
116
117
118
    auto deleteAction = KStandardAction::deleteFile(this, &DolphinViewActionHandler::slotDeleteItems, m_actionCollection);
    auto deleteShortcuts = deleteAction->shortcuts();
    if (!deleteShortcuts.contains(Qt::SHIFT | Qt::Key_Delete)) {
        deleteShortcuts.append(Qt::SHIFT | Qt::Key_Delete);
        m_actionCollection->setDefaultShortcuts(deleteAction, deleteShortcuts);
    }
119
120
121
    deleteAction->setWhatsThis(xi18nc("@info:whatsthis", "This deletes "
        "the items in your current selection completely. They can "
        "not be recovered by normal means."));
122

123
    // This action is useful for being enabled when KStandardAction::MoveToTrash should be
124
    // disabled and KStandardAction::DeleteFile is enabled (e.g. non-local files), so that Key_Del
125
126
    // can be used for deleting the file (#76016). It needs to be a separate action
    // so that the Edit menu isn't affected.
127
    QAction* deleteWithTrashShortcut = m_actionCollection->addAction(QStringLiteral("delete_shortcut"));
128
    // The descriptive text is just for the shortcuts editor.
129
    deleteWithTrashShortcut->setText(i18nc("@action \"Move to Trash\" for non-local files, etc.", "Delete (using shortcut for Trash)"));
130
    m_actionCollection->setDefaultShortcuts(deleteWithTrashShortcut, KStandardShortcut::moveToTrash());
131
    deleteWithTrashShortcut->setEnabled(false);
132
    connect(deleteWithTrashShortcut, &QAction::triggered, this, &DolphinViewActionHandler::slotDeleteItems);
133

Nate Graham's avatar
Nate Graham committed
134
135
136
137
138
139
140
    QAction* duplicateAction = m_actionCollection->addAction(QStringLiteral("duplicate"));
    duplicateAction->setText(i18nc("@action:inmenu File", "Duplicate Here"));
    duplicateAction->setIcon(QIcon::fromTheme(QStringLiteral("edit-duplicate")));
    m_actionCollection->setDefaultShortcut(duplicateAction, Qt::CTRL | Qt::Key_D);
    duplicateAction->setEnabled(false);
    connect(duplicateAction, &QAction::triggered, this, &DolphinViewActionHandler::slotDuplicate);

141
    QAction *propertiesAction = m_actionCollection->addAction( QStringLiteral("properties") );
142
    // Well, it's the File menu in dolphinmainwindow and the Edit menu in dolphinpart... :)
143
    propertiesAction->setText( i18nc("@action:inmenu File", "Properties") );
144
145
146
147
148
149
    propertiesAction->setWhatsThis(xi18nc("@info:whatsthis properties",
        "This shows a complete list of properties of the currently "
        "selected items in a new window.<nl/>If nothing is selected the "
        "window will be about the currently viewed folder instead.<nl/>"
        "You can configure advanced options there like managing "
        "read- and write-permissions."));
150
    propertiesAction->setIcon(QIcon::fromTheme(QStringLiteral("document-properties")));
151
    m_actionCollection->setDefaultShortcuts(propertiesAction, {Qt::ALT | Qt::Key_Return, Qt::ALT | Qt::Key_Enter});
152
    connect(propertiesAction, &QAction::triggered, this, &DolphinViewActionHandler::slotProperties);
153

Yann Holme-Nielsen's avatar
Yann Holme-Nielsen committed
154
    QAction *copyPathAction = m_actionCollection->addAction( QStringLiteral("copy_location") );
Nikita Churaev's avatar
Nikita Churaev committed
155
    copyPathAction->setText(i18nc("@action:incontextmenu", "Copy Location"));
Yann Holme-Nielsen's avatar
Yann Holme-Nielsen committed
156
157
158
159
160
    copyPathAction->setWhatsThis(i18nc("@info:whatsthis copy_location",
                                          "This will copy the path of the first selected item into the clipboard."
                                ));

    copyPathAction->setIcon(QIcon::fromTheme(QStringLiteral("edit-copy")));
161
    m_actionCollection->setDefaultShortcuts(copyPathAction, {Qt::CTRL | Qt::ALT | Qt::Key_C});
Yann Holme-Nielsen's avatar
Yann Holme-Nielsen committed
162
163
    connect(copyPathAction, &QAction::triggered, this, &DolphinViewActionHandler::slotCopyPath);

164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
    // This menu makes sure that users who don't know how to open a context menu and haven't
    // figured out how to enable the menu bar can still perform basic file manipulation.
    // This only works if they know how to select a file.
    // The text when nothing is selected at least implies that a selection can /somehow/ be made.
    // This menu is by default only used in the hamburger menu but created here so users can put
    // it on their toolbar.
    KActionMenu *basicActionsMenu = m_actionCollection->add<KActionMenu>(QStringLiteral("basic_actions"), this);
    // The text is set later depending on the selection in the currently active view.
    basicActionsMenu->setPopupMode(QToolButton::InstantPopup);
    basicActionsMenu->addAction(m_actionCollection->action(KStandardAction::name(KStandardAction::Cut)));
    basicActionsMenu->addAction(m_actionCollection->action(KStandardAction::name(KStandardAction::Copy)));
    basicActionsMenu->addAction(m_actionCollection->action(KStandardAction::name(KStandardAction::Paste)));
    basicActionsMenu->addSeparator();
    basicActionsMenu->addAction(m_actionCollection->action(KStandardAction::name(KStandardAction::RenameFile)));
    basicActionsMenu->addAction(m_actionCollection->action(KStandardAction::name(KStandardAction::MoveToTrash)));
    basicActionsMenu->addSeparator();
    basicActionsMenu->addAction(m_actionCollection->action(QStringLiteral("properties")));
    basicActionsMenu->addSeparator(); // We add one more separator because we sometimes add contextual
                                      // actions in slotSelectionChanged() after the static ones above.
Yann Holme-Nielsen's avatar
Yann Holme-Nielsen committed
183

184
    // View menu
185
    KToggleAction* iconsAction = iconsModeAction();
186
    KToggleAction* compactAction = compactModeAction();
187
188
    KToggleAction* detailsAction = detailsModeAction();

189
190
191
192
193
194
195
196
197
    iconsAction->setWhatsThis(xi18nc("@info:whatsthis Icons view mode",
        "<para>This switches to a view mode that focuses on the folder "
        "and file icons. This mode makes it easy to distinguish folders "
        "from files and to detect items with distinctive <emphasis>"
        "file types</emphasis>.</para><para> This mode is handy to "
        "browse through pictures when the <interface>Preview"
        "</interface> option is enabled.</para>"));
    compactAction->setWhatsThis(xi18nc("@info:whatsthis Compact view mode",
        "<para>This switches to a compact view mode that lists the folders "
198
        "and files in columns with the names beside the icons.</para><para>"
199
200
201
202
203
204
205
206
207
208
209
        "This helps to keep the overview in folders with many items.</para>"));
    detailsAction->setWhatsThis(xi18nc("@info:whatsthis Details view mode",
        "<para>This switches to a list view mode that focuses on folder "
        "and file details.</para><para>Click on a detail in the column "
        "header to sort the items by it. Click again to sort the other "
        "way around. To select which details should be displayed click "
        "the header with the right mouse button.</para><para>You can "
        "view the contents of a folder without leaving the current "
        "location by clicking to the left of it. This way you can view "
        "the contents of multiple folders in the same list.</para>"));

210
    KSelectAction* viewModeActions = m_actionCollection->add<KSelectAction>(QStringLiteral("view_mode"));
211
212
    viewModeActions->setText(i18nc("@action:intoolbar", "View Mode"));
    viewModeActions->addAction(iconsAction);
213
    viewModeActions->addAction(compactAction);
214
215
    viewModeActions->addAction(detailsAction);
    viewModeActions->setToolBarMode(KSelectAction::MenuMode);
216
    connect(viewModeActions, QOverload<QAction*>::of(&KSelectAction::triggered), this, &DolphinViewActionHandler::slotViewModeActionTriggered);
217

218
    QAction* zoomInAction = KStandardAction::zoomIn(this,
219
                            &DolphinViewActionHandler::zoomIn,
220
                            m_actionCollection);
221
    zoomInAction->setWhatsThis(i18nc("@info:whatsthis zoom in", "This increases the icon size."));
222

223
224
225
226
227
    QAction* zoomResetAction = m_actionCollection->addAction(QStringLiteral("view_zoom_reset"));
    zoomResetAction->setText(i18nc("@action:inmenu View", "Reset Zoom Level"));
    zoomResetAction->setToolTip(i18n("Zoom To Default"));
    zoomResetAction->setWhatsThis(i18nc("@info:whatsthis zoom reset", "This resets the icon size to default."));
    zoomResetAction->setIcon(QIcon::fromTheme(QStringLiteral("zoom-original")));
228
    m_actionCollection->setDefaultShortcuts(zoomResetAction, {Qt::CTRL | Qt::Key_0});
229
230
    connect(zoomResetAction, &QAction::triggered, this, &DolphinViewActionHandler::zoomReset);

231
    QAction* zoomOutAction = KStandardAction::zoomOut(this,
232
                             &DolphinViewActionHandler::zoomOut,
233
                             m_actionCollection);
234
    zoomOutAction->setWhatsThis(i18nc("@info:whatsthis zoom out", "This reduces the icon size."));
235

236
237
238
239
240
241
242
243
    KActionMenu* zoomMenu = m_actionCollection->add<KActionMenu>(QStringLiteral("zoom"));
    zoomMenu->setText(i18nc("@action:inmenu menu of zoom actions", "Zoom"));
    zoomMenu->setIcon(QIcon::fromTheme(QStringLiteral("zoom")));
    zoomMenu->setPopupMode(QToolButton::InstantPopup);
    zoomMenu->addAction(zoomInAction);
    zoomMenu->addAction(zoomResetAction);
    zoomMenu->addAction(zoomOutAction);

244
    KToggleAction* showPreview = m_actionCollection->add<KToggleAction>(QStringLiteral("show_preview"));
245
    showPreview->setText(i18nc("@action:intoolbar", "Show Previews"));
246
    showPreview->setToolTip(i18nc("@info", "Show preview of files and folders"));
247
248
249
250
    showPreview->setWhatsThis(xi18nc("@info:whatsthis", "When this is "
        "enabled, the icons are based on the actual file or folder "
        "contents.<nl/>For example the icons of images become scaled "
        "down versions of the images."));
251
    showPreview->setIcon(QIcon::fromTheme(QStringLiteral("view-preview")));
252
    connect(showPreview, &KToggleAction::triggered, this, &DolphinViewActionHandler::togglePreview);
253

254
    KToggleAction* sortFoldersFirst = m_actionCollection->add<KToggleAction>(QStringLiteral("folders_first"));
255
    sortFoldersFirst->setText(i18nc("@action:inmenu Sort", "Folders First"));
256
    connect(sortFoldersFirst, &KToggleAction::triggered, this, &DolphinViewActionHandler::toggleSortFoldersFirst);
257

258
259
260
261
    KToggleAction* sortHiddenLast = m_actionCollection->add<KToggleAction>(QStringLiteral("hidden_last"));
    sortHiddenLast->setText(i18nc("@action:inmenu Sort", "Hidden Files Last"));
    connect(sortHiddenLast, &KToggleAction::triggered, this, &DolphinViewActionHandler::toggleSortHiddenLast);

262
    // View -> Sort By
263
    QActionGroup* sortByActionGroup = createFileItemRolesActionGroup(QStringLiteral("sort_by_"));
264

265
    KActionMenu* sortByActionMenu = m_actionCollection->add<KActionMenu>(QStringLiteral("sort"));
266
    sortByActionMenu->setIcon(QIcon::fromTheme(QStringLiteral("view-sort")));
267
    sortByActionMenu->setText(i18nc("@action:inmenu View", "Sort By"));
268
    sortByActionMenu->setPopupMode(QToolButton::InstantPopup);
269

Alexander Lohnau's avatar
Alexander Lohnau committed
270
271
    const auto sortByActionGroupActions = sortByActionGroup->actions();
    for (QAction* action : sortByActionGroupActions) {
272
273
        sortByActionMenu->addAction(action);
    }
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293

    sortByActionMenu->addSeparator();

    QActionGroup* group = new QActionGroup(sortByActionMenu);
    group->setExclusive(true);

    KToggleAction* ascendingAction = m_actionCollection->add<KToggleAction>(QStringLiteral("ascending"));
    ascendingAction->setActionGroup(group);
    connect(ascendingAction, &QAction::triggered, this, [this] {
        m_currentView->setSortOrder(Qt::AscendingOrder);
    });

    KToggleAction* descendingAction = m_actionCollection->add<KToggleAction>(QStringLiteral("descending"));
    descendingAction->setActionGroup(group);
    connect(descendingAction, &QAction::triggered, this, [this] {
        m_currentView->setSortOrder(Qt::DescendingOrder);
    });

    sortByActionMenu->addAction(ascendingAction);
    sortByActionMenu->addAction(descendingAction);
294
295
    sortByActionMenu->addSeparator();
    sortByActionMenu->addAction(sortFoldersFirst);
296
    sortByActionMenu->addAction(sortHiddenLast);
297
298

    // View -> Additional Information
299
    QActionGroup* visibleRolesGroup = createFileItemRolesActionGroup(QStringLiteral("show_"));
300

301
    KActionMenu* visibleRolesMenu = m_actionCollection->add<KActionMenu>(QStringLiteral("additional_info"));
302
303
    visibleRolesMenu->setText(i18nc("@action:inmenu View", "Show Additional Information"));
    visibleRolesMenu->setIcon(QIcon::fromTheme(QStringLiteral("documentinfo")));
304
    visibleRolesMenu->setPopupMode(QToolButton::InstantPopup);
305

Alexander Lohnau's avatar
Alexander Lohnau committed
306
307
    const auto visibleRolesGroupActions = visibleRolesGroup->actions();
    for (QAction* action : visibleRolesGroupActions) {
308
        visibleRolesMenu->addAction(action);
309
    }
310

311
312
    KToggleAction* showInGroups = m_actionCollection->add<KToggleAction>(QStringLiteral("show_in_groups"));
    showInGroups->setIcon(QIcon::fromTheme(QStringLiteral("view-group")));
313
    showInGroups->setText(i18nc("@action:inmenu View", "Show in Groups"));
314
    showInGroups->setWhatsThis(i18nc("@info:whatsthis", "This groups files and folders by their first letter."));
315
    connect(showInGroups, &KToggleAction::triggered, this, &DolphinViewActionHandler::toggleGroupedSorting);
316

317
    KToggleAction* showHiddenFiles = m_actionCollection->add<KToggleAction>(QStringLiteral("show_hidden_files"));
318
    showHiddenFiles->setIcon(QIcon::fromTheme(QStringLiteral("view-visible")));
319
    showHiddenFiles->setText(i18nc("@action:inmenu View", "Show Hidden Files"));
320
321
322
323
324
325
    showHiddenFiles->setWhatsThis(xi18nc("@info:whatsthis", "<para>When "
        "this is enabled <emphasis>hidden</emphasis> files and folders "
        "are visible. They will be displayed semi-transparent.</para>"
        "<para>Hidden items only differ from other ones in that their "
        "name starts with a \".\". In general there is no need for "
        "users to access them which is why they are hidden.</para>"));
326
    m_actionCollection->setDefaultShortcuts(showHiddenFiles, KStandardShortcut::showHideHiddenFiles());
327
    connect(showHiddenFiles, &KToggleAction::triggered, this, &DolphinViewActionHandler::toggleShowHiddenFiles);
328

329
    QAction* adjustViewProps = m_actionCollection->addAction(QStringLiteral("view_properties"));
330
    adjustViewProps->setText(i18nc("@action:inmenu View", "Adjust View Display Style..."));
331
    adjustViewProps->setIcon(QIcon::fromTheme(QStringLiteral("view-choose")));
332
333
    adjustViewProps->setWhatsThis(i18nc("@info:whatsthis", "This opens a window "
        "in which all folder view properties can be adjusted."));
334
    connect(adjustViewProps, &QAction::triggered, this, &DolphinViewActionHandler::slotAdjustViewProperties);
335
336
}

337
QActionGroup* DolphinViewActionHandler::createFileItemRolesActionGroup(const QString& groupPrefix)
338
{
339
    const bool isSortGroup = (groupPrefix == QLatin1String("sort_by_"));
Antonio Prcela's avatar
Antonio Prcela committed
340
    Q_ASSERT(isSortGroup || groupPrefix == QLatin1String("show_"));
341
342
343
344

    QActionGroup* rolesActionGroup = new QActionGroup(m_actionCollection);
    rolesActionGroup->setExclusive(isSortGroup);
    if (isSortGroup) {
345
346
        connect(rolesActionGroup, &QActionGroup::triggered,
                this, &DolphinViewActionHandler::slotSortTriggered);
347
    } else {
348
349
        connect(rolesActionGroup, &QActionGroup::triggered,
                this, &DolphinViewActionHandler::toggleVisibleRole);
350
    }
351

352
    QString groupName;
Kevin Funk's avatar
Kevin Funk committed
353
354
    KActionMenu* groupMenu = nullptr;
    QActionGroup* groupMenuGroup = nullptr;
355

356
    bool indexingEnabled = false;
Vishesh Handa's avatar
Vishesh Handa committed
357
358
359
#ifdef HAVE_BALOO
    Baloo::IndexerConfig config;
    indexingEnabled = config.fileIndexingEnabled();
360
361
#endif

Peter Penz's avatar
Peter Penz committed
362
    const QList<KFileItemModel::RoleInfo> rolesInfo = KFileItemModel::rolesInformation();
Alexander Lohnau's avatar
Alexander Lohnau committed
363
    for (const KFileItemModel::RoleInfo& info : rolesInfo) {
364
365
        if (!isSortGroup && info.role == "text") {
            // It should not be possible to hide the "text" role
366
367
368
            continue;
        }

Kevin Funk's avatar
Kevin Funk committed
369
        KToggleAction* action = nullptr;
370
371
372
373
374
375
376
377
378
379
380
381
382
383
        const QString name = groupPrefix + info.role;
        if (info.group.isEmpty()) {
            action = m_actionCollection->add<KToggleAction>(name);
            action->setActionGroup(rolesActionGroup);
        } else {
            if (!groupMenu || info.group != groupName) {
                groupName = info.group;
                groupMenu = m_actionCollection->add<KActionMenu>(groupName);
                groupMenu->setText(groupName);
                groupMenu->setActionGroup(rolesActionGroup);

                groupMenuGroup = new QActionGroup(groupMenu);
                groupMenuGroup->setExclusive(isSortGroup);
                if (isSortGroup) {
384
385
                    connect(groupMenuGroup, &QActionGroup::triggered,
                            this, &DolphinViewActionHandler::slotSortTriggered);
386
                } else {
387
388
                    connect(groupMenuGroup, &QActionGroup::triggered,
                            this, &DolphinViewActionHandler::toggleVisibleRole);
389
390
391
392
393
394
395
                }
            }

            action = new KToggleAction(groupMenu);
            action->setActionGroup(groupMenuGroup);
            groupMenu->addAction(action);
        }
Peter Penz's avatar
Peter Penz committed
396
397
        action->setText(info.translation);
        action->setData(info.role);
398

Vishesh Handa's avatar
Vishesh Handa committed
399
400
        const bool enable = (!info.requiresBaloo && !info.requiresIndexer) ||
                            (info.requiresBaloo) ||
401
402
403
                            (info.requiresIndexer && indexingEnabled);
        action->setEnabled(enable);

404
405
406
407
408
        if (isSortGroup) {
            m_sortByActions.insert(info.role, action);
        } else {
            m_visibleRoles.insert(info.role, action);
        }
409
    }
410

411
    return rolesActionGroup;
412
413
}

414
415
416
417
void DolphinViewActionHandler::slotViewModeActionTriggered(QAction* action)
{
    const DolphinView::Mode mode = action->data().value<DolphinView::Mode>();
    m_currentView->setMode(mode);
418

419
    QAction* viewModeMenu = m_actionCollection->action(QStringLiteral("view_mode"));
Emmanuel Pescosta's avatar
Emmanuel Pescosta committed
420
    viewModeMenu->setIcon(action->icon());
421
422
}

423
424
void DolphinViewActionHandler::slotRename()
{
Alexander Lohnau's avatar
Alexander Lohnau committed
425
    Q_EMIT actionBeingHandled();
426
427
428
    m_currentView->renameSelectedItems();
}

429
void DolphinViewActionHandler::slotTrashActivated()
430
{
Alexander Lohnau's avatar
Alexander Lohnau committed
431
    Q_EMIT actionBeingHandled();
432
    m_currentView->trashSelectedItems();
433
434
435
436
}

void DolphinViewActionHandler::slotDeleteItems()
{
Alexander Lohnau's avatar
Alexander Lohnau committed
437
    Q_EMIT actionBeingHandled();
438
439
440
441
442
    m_currentView->deleteSelectedItems();
}

void DolphinViewActionHandler::togglePreview(bool show)
{
Alexander Lohnau's avatar
Alexander Lohnau committed
443
    Q_EMIT actionBeingHandled();
444
    m_currentView->setPreviewsShown(show);
445
446
}

447
void DolphinViewActionHandler::slotPreviewsShownChanged(bool shown)
448
{
449
    Q_UNUSED(shown)
450
    // It is not enough to update the 'Show Preview' action, also
451
    // the 'Zoom In', 'Zoom Out' and 'Zoom Reset' actions must be adapted.
452
453
454
    updateViewActions();
}

455
456
457
458
QString DolphinViewActionHandler::currentViewModeActionName() const
{
    switch (m_currentView->mode()) {
    case DolphinView::IconsView:
459
        return QStringLiteral("icons");
460
    case DolphinView::DetailsView:
461
        return QStringLiteral("details");
462
    case DolphinView::CompactView:
463
        return QStringLiteral("compact");
464
465
466
    default:
        Q_ASSERT(false);
        break;
467
468
469
470
    }
    return QString(); // can't happen
}

Rahman Duran's avatar
Rahman Duran committed
471
472
473
474
475
KActionCollection* DolphinViewActionHandler::actionCollection()
{
    return m_actionCollection;
}

476
477
void DolphinViewActionHandler::updateViewActions()
{
478
    QAction* viewModeAction = m_actionCollection->action(currentViewModeActionName());
479
    if (viewModeAction) {
480
        viewModeAction->setChecked(true);
481

482
        QAction* viewModeMenu = m_actionCollection->action(QStringLiteral("view_mode"));
Emmanuel Pescosta's avatar
Emmanuel Pescosta committed
483
        viewModeMenu->setIcon(viewModeAction->icon());
484
485
    }

486
    QAction* showPreviewAction = m_actionCollection->action(QStringLiteral("show_preview"));
487
    showPreviewAction->setChecked(m_currentView->previewsShown());
488
489

    slotSortOrderChanged(m_currentView->sortOrder());
490
    slotSortFoldersFirstChanged(m_currentView->sortFoldersFirst());
491
    slotSortHiddenLastChanged(m_currentView->sortHiddenLast());
492
    slotVisibleRolesChanged(m_currentView->visibleRoles(), QList<QByteArray>());
493
    slotGroupedSortingChanged(m_currentView->groupedSorting());
494
    slotSortRoleChanged(m_currentView->sortRole());
495
    slotZoomLevelChanged(m_currentView->zoomLevel(), -1);
496

Roman Gilg's avatar
Roman Gilg committed
497
498
    // Updates the "show_hidden_files" action state and icon
    slotHiddenFilesShownChanged(m_currentView->hiddenFilesShown());
499
500
501
502
}

void DolphinViewActionHandler::zoomIn()
{
503
504
    const int level = m_currentView->zoomLevel();
    m_currentView->setZoomLevel(level + 1);
505
506
507
508
509
    updateViewActions();
}

void DolphinViewActionHandler::zoomOut()
{
510
511
    const int level = m_currentView->zoomLevel();
    m_currentView->setZoomLevel(level - 1);
512
513
514
    updateViewActions();
}

515
516
517
518
519
520
void DolphinViewActionHandler::zoomReset()
{
    m_currentView->resetZoomLevel();
    updateViewActions();
}

521
522
void DolphinViewActionHandler::toggleSortFoldersFirst()
{
523
524
    const bool sortFirst = m_currentView->sortFoldersFirst();
    m_currentView->setSortFoldersFirst(!sortFirst);
525
526
}

527
528
529
530
531
532
void DolphinViewActionHandler::toggleSortHiddenLast()
{
    const bool sortHiddenLast = m_currentView->sortHiddenLast();
    m_currentView->setSortHiddenLast(!sortHiddenLast);
}

533
534
void DolphinViewActionHandler::slotSortOrderChanged(Qt::SortOrder order)
{
535
    QAction* descending = m_actionCollection->action(QStringLiteral("descending"));
536
    QAction* ascending = m_actionCollection->action(QStringLiteral("ascending"));
537
538
    const bool sortDescending = (order == Qt::DescendingOrder);
    descending->setChecked(sortDescending);
539
    ascending->setChecked(!sortDescending);
540
541
}

542
543
void DolphinViewActionHandler::slotSortFoldersFirstChanged(bool foldersFirst)
{
544
    m_actionCollection->action(QStringLiteral("folders_first"))->setChecked(foldersFirst);
545
546
}

547
548
549
550
551
void DolphinViewActionHandler::slotSortHiddenLastChanged(bool hiddenLast)
{
    m_actionCollection->action(QStringLiteral("hidden_last"))->setChecked(hiddenLast);
}

552
void DolphinViewActionHandler::toggleVisibleRole(QAction* action)
553
{
Alexander Lohnau's avatar
Alexander Lohnau committed
554
    Q_EMIT actionBeingHandled();
555

556
    const QByteArray toggledRole = action->data().toByteArray();
557

558
    QList<QByteArray> roles = m_currentView->visibleRoles();
559
560
561

    const bool show = action->isChecked();

562
    const int index = roles.indexOf(toggledRole);
563
564
    const bool containsInfo = (index >= 0);
    if (show && !containsInfo) {
565
566
        roles.append(toggledRole);
        m_currentView->setVisibleRoles(roles);
567
    } else if (!show && containsInfo) {
568
569
570
        roles.removeAt(index);
        m_currentView->setVisibleRoles(roles);
        Q_ASSERT(roles.indexOf(toggledRole) < 0);
571
    }
572
573
}

574
575
void DolphinViewActionHandler::slotVisibleRolesChanged(const QList<QByteArray>& current,
                                                       const QList<QByteArray>& previous)
576
{
577
    Q_UNUSED(previous)
578

579
    const auto checkedRoles = QSet<QByteArray>(current.constBegin(), current.constEnd());
580
581
582
583
584
585
    QHashIterator<QByteArray, KToggleAction*> it(m_visibleRoles);
    while (it.hasNext()) {
        it.next();
        const QByteArray& role = it.key();
        KToggleAction* action = it.value();
        action->setChecked(checkedRoles.contains(role));
586
    }
587
588
}

589
void DolphinViewActionHandler::toggleGroupedSorting(bool grouped)
590
{
591
    m_currentView->setGroupedSorting(grouped);
592
593
}

594
void DolphinViewActionHandler::slotGroupedSortingChanged(bool groupedSorting)
595
{
596
    QAction* showInGroupsAction = m_actionCollection->action(QStringLiteral("show_in_groups"));
597
    showInGroupsAction->setChecked(groupedSorting);
598
599
600
601
}

void DolphinViewActionHandler::toggleShowHiddenFiles(bool show)
{
Alexander Lohnau's avatar
Alexander Lohnau committed
602
    Q_EMIT actionBeingHandled();
603
    m_currentView->setHiddenFilesShown(show);
604
605
}

606
void DolphinViewActionHandler::slotHiddenFilesShownChanged(bool shown)
607
{
608
    QAction* showHiddenFilesAction = m_actionCollection->action(QStringLiteral("show_hidden_files"));
609
    showHiddenFilesAction->setChecked(shown);
610
611
}

612
613
void DolphinViewActionHandler::slotWriteStateChanged(bool isFolderWritable)
{
614
615
    m_actionCollection->action(QStringLiteral("create_dir"))->setEnabled(isFolderWritable &&
                                                                         KProtocolManager::supportsMakeDir(currentView()->url()));
616
617
}

618
619
KToggleAction* DolphinViewActionHandler::iconsModeAction()
{
620
    KToggleAction* iconsView = m_actionCollection->add<KToggleAction>(QStringLiteral("icons"));
621
    iconsView->setText(i18nc("@action:inmenu View Mode", "Icons"));
622
    iconsView->setToolTip(i18nc("@info", "Icons view mode"));
623
    m_actionCollection->setDefaultShortcut(iconsView, Qt::CTRL | Qt::Key_1);
624
    iconsView->setIcon(QIcon::fromTheme(QStringLiteral("view-list-icons")));
625
626
627
628
    iconsView->setData(QVariant::fromValue(DolphinView::IconsView));
    return iconsView;
}

629
630
KToggleAction* DolphinViewActionHandler::compactModeAction()
{
631
    KToggleAction* iconsView = m_actionCollection->add<KToggleAction>(QStringLiteral("compact"));
632
633
    iconsView->setText(i18nc("@action:inmenu View Mode", "Compact"));
    iconsView->setToolTip(i18nc("@info", "Compact view mode"));
634
    m_actionCollection->setDefaultShortcut(iconsView, Qt::CTRL | Qt::Key_2);
635
    iconsView->setIcon(QIcon::fromTheme(QStringLiteral("view-list-details"))); // TODO: discuss with Oxygen-team the wrong (?) name
636
637
638
639
    iconsView->setData(QVariant::fromValue(DolphinView::CompactView));
    return iconsView;
}

640
641
KToggleAction* DolphinViewActionHandler::detailsModeAction()
{
642
    KToggleAction* detailsView = m_actionCollection->add<KToggleAction>(QStringLiteral("details"));
643
    detailsView->setText(i18nc("@action:inmenu View Mode", "Details"));
644
    detailsView->setToolTip(i18nc("@info", "Details view mode"));
645
    m_actionCollection->setDefaultShortcut(detailsView, Qt::CTRL | Qt::Key_3);
646
    detailsView->setIcon(QIcon::fromTheme(QStringLiteral("view-list-tree")));
647
648
649
650
    detailsView->setData(QVariant::fromValue(DolphinView::DetailsView));
    return detailsView;
}

651
void DolphinViewActionHandler::slotSortRoleChanged(const QByteArray& role)
652
{
653
    KToggleAction* action = m_sortByActions.value(role);
654
    if (action) {
655
        action->setChecked(true);
656

657
        if (!action->icon().isNull()) {
658
            QAction* sortByMenu = m_actionCollection->action(QStringLiteral("sort"));
Emmanuel Pescosta's avatar
Emmanuel Pescosta committed
659
            sortByMenu->setIcon(action->icon());
660
        }
661
    }
662
663
664
665
666
667
668
669

    QAction* descending = m_actionCollection->action(QStringLiteral("descending"));
    QAction* ascending = m_actionCollection->action(QStringLiteral("ascending"));

    if (role == "text" || role == "type" || role == "tags" || role == "comment") {
        descending->setText(i18nc("Sort descending", "Z-A"));
        ascending->setText(i18nc("Sort ascending", "A-Z"));
    } else if (role == "size") {
Nikita Churaev's avatar
Nikita Churaev committed
670
671
        descending->setText(i18nc("Sort descending", "Largest First"));
        ascending->setText(i18nc("Sort ascending", "Smallest First"));
672
    } else if (role == "modificationtime" || role == "creationtime" || role == "accesstime") {
Nikita Churaev's avatar
Nikita Churaev committed
673
674
        descending->setText(i18nc("Sort descending", "Newest First"));
        ascending->setText(i18nc("Sort ascending", "Oldest First"));
675
    } else if (role == "rating") {
Nikita Churaev's avatar
Nikita Churaev committed
676
677
        descending->setText(i18nc("Sort descending", "Highest First"));
        ascending->setText(i18nc("Sort ascending", "Lowest First"));
678
679
680
681
682
683
    } else {
        descending->setText(i18nc("Sort descending", "Descending"));
        ascending->setText(i18nc("Sort ascending", "Ascending"));
    }

    slotSortOrderChanged(m_currentView->sortOrder());
684
685
}

686
void DolphinViewActionHandler::slotZoomLevelChanged(int current, int previous)
687
{
688
    Q_UNUSED(previous)
689

690
    QAction* zoomInAction = m_actionCollection->action(KStandardAction::name(KStandardAction::ZoomIn));
691
    if (zoomInAction) {
692
        zoomInAction->setEnabled(current < ZoomLevelInfo::maximumLevel());
693
694
695
    }

    QAction* zoomOutAction = m_actionCollection->action(KStandardAction::name(KStandardAction::ZoomOut));
696
    if (zoomOutAction) {
697
        zoomOutAction->setEnabled(current > ZoomLevelInfo::minimumLevel());
698
699
700
    }
}

701
702
void DolphinViewActionHandler::slotSortTriggered(QAction* action)
{
703
704
705
706
    // The radiobuttons of the "Sort By"-menu are split between the main-menu
    // and several sub-menus. Because of this they don't have a common
    // action-group that assures an exclusive toggle-state between the main-menu
    // actions and the sub-menu-actions. If an action gets checked, it must
707
708
709
    // be assured that all other actions get unchecked, except the ascending/
    // descending actions
    for (QAction *groupAction : qAsConst(m_sortByActions)) {
710
711
        KActionMenu* actionMenu = qobject_cast<KActionMenu*>(groupAction);
        if (actionMenu) {
Alexander Lohnau's avatar
Alexander Lohnau committed
712
713
            const auto actions = actionMenu->menu()->actions();
            for (QAction* subAction : actions) {
714
715
716
717
718
719
720
721
722
                subAction->setChecked(false);
            }
        } else if (groupAction->actionGroup()) {
            groupAction->setChecked(false);
        }
    }
    action->setChecked(true);

    // Apply the activated sort-role to the view
723
724
    const QByteArray role = action->data().toByteArray();
    m_currentView->setSortRole(role);
725
}
726
727
728

void DolphinViewActionHandler::slotAdjustViewProperties()
{
Alexander Lohnau's avatar
Alexander Lohnau committed
729
    Q_EMIT actionBeingHandled();
730
731
732
    QPointer<ViewPropertiesDialog> dialog = new ViewPropertiesDialog(m_currentView);
    dialog->exec();
    delete dialog;
733
}
734

Nate Graham's avatar
Nate Graham committed
735
736
void DolphinViewActionHandler::slotDuplicate()
{
Alexander Lohnau's avatar
Alexander Lohnau committed
737
    Q_EMIT actionBeingHandled();
Nate Graham's avatar
Nate Graham committed
738
739
740
    m_currentView->duplicateSelectedItems();
}

741
742
void DolphinViewActionHandler::slotProperties()
{
Kevin Funk's avatar
Kevin Funk committed
743
    KPropertiesDialog* dialog = nullptr;
744
745
    const KFileItemList list = m_currentView->selectedItems();
    if (list.isEmpty()) {
Lukáš Tinkl's avatar
Lukáš Tinkl committed
746
        const QUrl url = m_currentView->url();
747
748
749
750
751
752
753
754
755
756
        dialog = new KPropertiesDialog(url, m_currentView);
    } else {
        dialog = new KPropertiesDialog(list, m_currentView);
    }

    dialog->setAttribute(Qt::WA_DeleteOnClose);
    dialog->show();
    dialog->raise();
    dialog->activateWindow();
}
Yann Holme-Nielsen's avatar
Yann Holme-Nielsen committed
757
758
759
760
761

void DolphinViewActionHandler::slotCopyPath()
{
    m_currentView->copyPathToClipboard();
}
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826

void DolphinViewActionHandler::slotSelectionChanged(const KFileItemList& selection)
{
    QString basicActionsMenuText;
    switch (selection.count()) {
    case 0:
        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;
    }

    // At some point the added clarity from the text starts being less important than the menu width.
    if (basicActionsMenuText.isEmpty() || basicActionsMenuText.length() > 40) {
        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());
        }
    }

    QAction *basicActionsMenu = m_actionCollection->action(QStringLiteral("basic_actions"));
    basicActionsMenu->setText(basicActionsMenuText);

    // Add or remove contextual actions
    while (!basicActionsMenu->menu()->actions().constLast()->isSeparator()) {
        basicActionsMenu->menu()->removeAction(basicActionsMenu->menu()->actions().last());
    }
    if (selection.count() == 1) {
        if (selection.first().isLink()) {
            basicActionsMenu->menu()->addAction(m_actionCollection->action(QStringLiteral("show_target")));
        }
        if (selection.first().isDir()) {
            basicActionsMenu->menu()->addAction(m_actionCollection->action(QStringLiteral("add_to_places")));
        }
    }
}