dolphinview.cpp 62.2 KB
Newer Older
1
/***************************************************************************
2
 *   Copyright (C) 2006-2009 by Peter Penz <peter.penz19@gmail.com>        *
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
 *   Copyright (C) 2006 by Gregor Kališnik <gregor@podnapisi.net>          *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
18
 *   51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA            *
19
20
21
22
 ***************************************************************************/

#include "dolphinview.h"

23
#include "dolphin_detailsmodesettings.h"
24
#include "dolphin_generalsettings.h"
25
#include "dolphinitemlistview.h"
Roman Inflianskas's avatar
Roman Inflianskas committed
26
#include "dolphinnewfilemenuobserver.h"
Peter Penz's avatar
Peter Penz committed
27
#include "draganddrophelper.h"
Roman Inflianskas's avatar
Roman Inflianskas committed
28
29
30
31
32
33
#include "kitemviews/kfileitemlistview.h"
#include "kitemviews/kfileitemmodel.h"
#include "kitemviews/kitemlistcontainer.h"
#include "kitemviews/kitemlistcontroller.h"
#include "kitemviews/kitemlistheader.h"
#include "kitemviews/kitemlistselectionmanager.h"
34
#include "versioncontrol/versioncontrolobserver.h"
35
#include "viewproperties.h"
36
#include "views/tooltips/tooltipmanager.h"
37
#include "zoomlevelinfo.h"
38

Vishesh Handa's avatar
Vishesh Handa committed
39
#ifdef HAVE_BALOO
Roman Inflianskas's avatar
Roman Inflianskas committed
40
#include <Baloo/IndexerConfig>
41
#endif
Roman Inflianskas's avatar
Roman Inflianskas committed
42
43
44
45
#include <KColorScheme>
#include <KDesktopFile>
#include <KDirModel>
#include <KFileItemListProperties>
46
#include <KFormat>
Roman Inflianskas's avatar
Roman Inflianskas committed
47
48
49
50
51
52
53
#include <KIO/CopyJob>
#include <KIO/DeleteJob>
#include <KIO/DropJob>
#include <KIO/JobUiDelegate>
#include <KIO/Paste>
#include <KIO/PasteJob>
#include <KIO/PreviewJob>
54
#include <KIO/RenameFileDialog>
Roman Inflianskas's avatar
Roman Inflianskas committed
55
56
57
58
59
60
61
62
63
64
65
#include <KJobWidgets>
#include <KLocalizedString>
#include <KMessageBox>
#include <KProtocolManager>

#include <QAbstractItemView>
#include <QApplication>
#include <QClipboard>
#include <QDropEvent>
#include <QGraphicsSceneDragDropEvent>
#include <QMenu>
Nate Graham's avatar
Nate Graham committed
66
#include <QMimeDatabase>
Roman Inflianskas's avatar
Roman Inflianskas committed
67
68
69
#include <QPixmapCache>
#include <QPointer>
#include <QScrollBar>
70
#include <QSize>
Roman Inflianskas's avatar
Roman Inflianskas committed
71
72
#include <QTimer>
#include <QVBoxLayout>
73

74
DolphinView::DolphinView(const QUrl& url, QWidget* parent) :
75
    QWidget(parent),
76
    m_active(true),
77
    m_tabsForFiles(false),
78
    m_assureVisibleCurrentIndex(false),
79
    m_isFolderWritable(true),
80
    m_dragging(false),
81
    m_url(url),
82
    m_viewPropertiesContext(),
83
    m_mode(DolphinView::IconsView),
84
    m_visibleRoles(),
Kevin Funk's avatar
Kevin Funk committed
85
86
87
88
89
90
    m_topLayout(nullptr),
    m_model(nullptr),
    m_view(nullptr),
    m_container(nullptr),
    m_toolTipManager(nullptr),
    m_selectionChangedTimer(nullptr),
91
    m_currentItemUrl(),
92
    m_scrollToCurrentItem(false),
93
    m_restoredContentsPosition(),
94
    m_selectedUrls(),
95
    m_clearSelectionBeforeSelectingNewItems(false),
96
    m_markFirstNewlySelectedItemAsCurrent(false),
Kevin Funk's avatar
Kevin Funk committed
97
    m_versionControlObserver(nullptr),
98
    m_twoClicksRenamingTimer(nullptr)
99
{
100
    m_topLayout = new QVBoxLayout(this);
101
    m_topLayout->setSpacing(0);
Laurent Montel's avatar
Laurent Montel committed
102
    m_topLayout->setContentsMargins(0, 0, 0, 0);
103

104
105
106
    // When a new item has been created by the "Create New..." menu, the item should
    // get selected and it must be assured that the item will get visible. As the
    // creation is done asynchronously, several signals must be checked:
107
108
    connect(&DolphinNewFileMenuObserver::instance(), &DolphinNewFileMenuObserver::itemCreated,
            this, &DolphinView::observeCreatedItem);
109

110
111
112
    m_selectionChangedTimer = new QTimer(this);
    m_selectionChangedTimer->setSingleShot(true);
    m_selectionChangedTimer->setInterval(300);
113
114
    connect(m_selectionChangedTimer, &QTimer::timeout,
            this, &DolphinView::emitSelectionChangedSignal);
115

116
117
118
    m_model = new KFileItemModel(this);
    m_view = new DolphinItemListView();
    m_view->setEnabledSelectionToggles(GeneralSettings::showSelectionToggle());
119
    m_view->setVisibleRoles({"text"});
120
121
122
123
124
125
    applyModeToView();

    KItemListController* controller = new KItemListController(m_model, m_view, this);
    const int delay = GeneralSettings::autoExpandFolders() ? 750 : -1;
    controller->setAutoActivationDelay(delay);

126
127
128
129
    // The EnlargeSmallPreviews setting can only be changed after the model
    // has been set in the view by KItemListController.
    m_view->setEnlargeSmallPreviews(GeneralSettings::enlargeSmallPreviews());

130
    m_container = new KItemListContainer(controller, this);
131
    m_container->installEventFilter(this);
Peter Penz's avatar
Peter Penz committed
132
    setFocusProxy(m_container);
133
134
    connect(m_container->horizontalScrollBar(), &QScrollBar::valueChanged, this, [=] { hideToolTip(); });
    connect(m_container->verticalScrollBar(), &QScrollBar::valueChanged, this, [=] { hideToolTip(); });
135

136
    controller->setSelectionBehavior(KItemListController::MultiSelection);
137
138
139
140
141
142
143
144
145
146
147
148
    connect(controller, &KItemListController::itemActivated, this, &DolphinView::slotItemActivated);
    connect(controller, &KItemListController::itemsActivated, this, &DolphinView::slotItemsActivated);
    connect(controller, &KItemListController::itemMiddleClicked, this, &DolphinView::slotItemMiddleClicked);
    connect(controller, &KItemListController::itemContextMenuRequested, this, &DolphinView::slotItemContextMenuRequested);
    connect(controller, &KItemListController::viewContextMenuRequested, this, &DolphinView::slotViewContextMenuRequested);
    connect(controller, &KItemListController::headerContextMenuRequested, this, &DolphinView::slotHeaderContextMenuRequested);
    connect(controller, &KItemListController::mouseButtonPressed, this, &DolphinView::slotMouseButtonPressed);
    connect(controller, &KItemListController::itemHovered, this, &DolphinView::slotItemHovered);
    connect(controller, &KItemListController::itemUnhovered, this, &DolphinView::slotItemUnhovered);
    connect(controller, &KItemListController::itemDropEvent, this, &DolphinView::slotItemDropEvent);
    connect(controller, &KItemListController::escapePressed, this, &DolphinView::stopLoading);
    connect(controller, &KItemListController::modelChanged, this, &DolphinView::slotModelChanged);
149
    connect(controller, &KItemListController::selectedItemTextPressed, this, &DolphinView::slotSelectedItemTextPressed);
150
151
152
153
154
155
156
157
158
159
160
161
162
163

    connect(m_model, &KFileItemModel::directoryLoadingStarted,       this, &DolphinView::slotDirectoryLoadingStarted);
    connect(m_model, &KFileItemModel::directoryLoadingCompleted,     this, &DolphinView::slotDirectoryLoadingCompleted);
    connect(m_model, &KFileItemModel::directoryLoadingCanceled,      this, &DolphinView::directoryLoadingCanceled);
    connect(m_model, &KFileItemModel::directoryLoadingProgress,   this, &DolphinView::directoryLoadingProgress);
    connect(m_model, &KFileItemModel::directorySortingProgress,   this, &DolphinView::directorySortingProgress);
    connect(m_model, &KFileItemModel::itemsChanged,
            this, &DolphinView::slotItemsChanged);
    connect(m_model, &KFileItemModel::itemsRemoved,    this, &DolphinView::itemCountChanged);
    connect(m_model, &KFileItemModel::itemsInserted,   this, &DolphinView::itemCountChanged);
    connect(m_model, &KFileItemModel::infoMessage,            this, &DolphinView::infoMessage);
    connect(m_model, &KFileItemModel::errorMessage,           this, &DolphinView::errorMessage);
    connect(m_model, &KFileItemModel::directoryRedirection, this, &DolphinView::slotDirectoryRedirection);
    connect(m_model, &KFileItemModel::urlIsFileError,            this, &DolphinView::urlIsFileError);
164
165

    m_view->installEventFilter(this);
166
167
168
169
170
171
172
173
    connect(m_view, &DolphinItemListView::sortOrderChanged,
            this, &DolphinView::slotSortOrderChangedByHeader);
    connect(m_view, &DolphinItemListView::sortRoleChanged,
            this, &DolphinView::slotSortRoleChangedByHeader);
    connect(m_view, &DolphinItemListView::visibleRolesChanged,
            this, &DolphinView::slotVisibleRolesChangedByHeader);
    connect(m_view, &DolphinItemListView::roleEditingCanceled,
            this, &DolphinView::slotRoleEditingCanceled);
174
175
    connect(m_view->header(), &KItemListHeader::columnWidthChangeFinished,
            this, &DolphinView::slotHeaderColumnWidthChangeFinished);
Peter Penz's avatar
Peter Penz committed
176

177
    KItemListSelectionManager* selectionManager = controller->selectionManager();
178
179
    connect(selectionManager, &KItemListSelectionManager::selectionChanged,
            this, &DolphinView::slotSelectionChanged);
180

181
#ifdef HAVE_BALOO
182
    m_toolTipManager = new ToolTipManager(this);
183
    connect(m_toolTipManager, &ToolTipManager::urlActivated, this, &DolphinView::urlActivated);
184
#endif
185

186
    m_versionControlObserver = new VersionControlObserver(this);
187
    m_versionControlObserver->setView(this);
188
    m_versionControlObserver->setModel(m_model);
189
190
191
    connect(m_versionControlObserver, &VersionControlObserver::infoMessage, this, &DolphinView::infoMessage);
    connect(m_versionControlObserver, &VersionControlObserver::errorMessage, this, &DolphinView::errorMessage);
    connect(m_versionControlObserver, &VersionControlObserver::operationCompletedMessage, this, &DolphinView::operationCompletedMessage);
192

193
194
195
196
    m_twoClicksRenamingTimer = new QTimer(this);
    m_twoClicksRenamingTimer->setSingleShot(true);
    connect(m_twoClicksRenamingTimer, &QTimer::timeout, this, &DolphinView::slotTwoClicksRenamingTimerTimeout);

197
    applyViewProperties();
198
199
200
    m_topLayout->addWidget(m_container);

    loadDirectory(url);
201
202
203
204
205
206
}

DolphinView::~DolphinView()
{
}

207
QUrl DolphinView::url() const
208
{
209
    return m_url;
210
211
}

212
213
214
215
216
217
218
219
void DolphinView::setActive(bool active)
{
    if (active == m_active) {
        return;
    }

    m_active = active;

220
    updatePalette();
221
222

    if (active) {
Peter Penz's avatar
Peter Penz committed
223
        m_container->setFocus();
224
        emit activated();
225
        emit writeStateChanged(m_isFolderWritable);
226
227
228
    }
}

229
230
bool DolphinView::isActive() const
{
231
    return m_active;
232
233
234
235
}

void DolphinView::setMode(Mode mode)
{
236
    if (mode != m_mode) {
237
        ViewProperties props(viewPropertiesUrl());
238
        props.setViewMode(mode);
239

240
241
242
243
244
        // We pass the new ViewProperties to applyViewProperties, rather than
        // storing them on disk and letting applyViewProperties() read them
        // from there, to prevent that changing the view mode fails if the
        // .directory file is not writable (see bug 318534).
        applyViewProperties(props);
245
    }
246
247
248
249
250
251
252
}

DolphinView::Mode DolphinView::mode() const
{
    return m_mode;
}

253
254
255
256
257
258
void DolphinView::setPreviewsShown(bool show)
{
    if (previewsShown() == show) {
        return;
    }

259
    ViewProperties props(viewPropertiesUrl());
260
261
    props.setPreviewsShown(show);

262
    const int oldZoomLevel = m_view->zoomLevel();
263
    m_view->setPreviewsShown(show);
264
    emit previewsShownChanged(show);
265
266
267
268
269

    const int newZoomLevel = m_view->zoomLevel();
    if (newZoomLevel != oldZoomLevel) {
        emit zoomLevelChanged(newZoomLevel, oldZoomLevel);
    }
270
271
}

272
bool DolphinView::previewsShown() const
273
{
274
    return m_view->previewsShown();
275
276
}

277
278
void DolphinView::setHiddenFilesShown(bool show)
{
279
    if (m_model->showHiddenFiles() == show) {
280
281
282
283
284
        return;
    }

    const KFileItemList itemList = selectedItems();
    m_selectedUrls.clear();
Lukáš Tinkl's avatar
Lukáš Tinkl committed
285
    m_selectedUrls = itemList.urlList();
286

287
    ViewProperties props(viewPropertiesUrl());
288
289
    props.setHiddenFilesShown(show);

290
    m_model->setShowHiddenFiles(show);
291
292
293
    emit hiddenFilesShownChanged(show);
}

294
bool DolphinView::hiddenFilesShown() const
295
{
296
    return m_model->showHiddenFiles();
297
298
}

299
300
301
302
303
304
void DolphinView::setGroupedSorting(bool grouped)
{
    if (grouped == groupedSorting()) {
        return;
    }

305
    ViewProperties props(viewPropertiesUrl());
306
307
308
309
310
311
312
313
314
    props.setGroupedSorting(grouped);
    props.save();

    m_container->controller()->model()->setGroupedSorting(grouped);

    emit groupedSortingChanged(grouped);
}

bool DolphinView::groupedSorting() const
315
{
316
    return m_model->groupedSorting();
317
318
}

319
KFileItemList DolphinView::items() const
320
{
321
    KFileItemList list;
322
    const int itemCount = m_model->count();
323
324
325
    list.reserve(itemCount);

    for (int i = 0; i < itemCount; ++i) {
326
        list.append(m_model->fileItem(i));
327
328
329
330
331
332
333
    }

    return list;
}

int DolphinView::itemsCount() const
{
334
    return m_model->count();
335
336
}

337
KFileItemList DolphinView::selectedItems() const
338
{
339
    const KItemListSelectionManager* selectionManager = m_container->controller()->selectionManager();
340

341
    KFileItemList selectedItems;
342
343
344
    const auto items = selectionManager->selectedItems();
    selectedItems.reserve(items.count());
    for (int index : items) {
345
        selectedItems.append(m_model->fileItem(index));
346
    }
347
    return selectedItems;
348
349
}

350
351
int DolphinView::selectedItemsCount() const
{
352
353
    const KItemListSelectionManager* selectionManager = m_container->controller()->selectionManager();
    return selectionManager->selectedItems().count();
354
355
}

356
void DolphinView::markUrlsAsSelected(const QList<QUrl>& urls)
357
{
358
    m_selectedUrls = urls;
359
360
}

361
void DolphinView::markUrlAsCurrent(const QUrl &url)
362
363
{
    m_currentItemUrl = url;
364
    m_scrollToCurrentItem = true;
365
366
}

367
void DolphinView::selectItems(const QRegExp& pattern, bool enabled)
368
{
369
370
371
372
373
    const KItemListSelectionManager::SelectionMode mode = enabled
                                                        ? KItemListSelectionManager::Select
                                                        : KItemListSelectionManager::Deselect;
    KItemListSelectionManager* selectionManager = m_container->controller()->selectionManager();

374
375
    for (int index = 0; index < m_model->count(); index++) {
        const KFileItem item = m_model->fileItem(index);
376
        if (pattern.exactMatch(item.text())) {
377
            // An alternative approach would be to store the matching items in a KItemSet and
378
            // select them in one go after the loop, but we'd need a new function
379
            // KItemListSelectionManager::setSelected(KItemSet, SelectionMode mode)
380
381
382
383
            // for that.
            selectionManager->setSelected(index, 1, mode);
        }
    }
384
385
}

386
void DolphinView::setZoomLevel(int level)
387
{
388
    const int oldZoomLevel = zoomLevel();
389
    m_view->setZoomLevel(level);
390
    if (zoomLevel() != oldZoomLevel) {
391
        hideToolTip();
392
        emit zoomLevelChanged(zoomLevel(), oldZoomLevel);
393
    }
394
395
}

396
int DolphinView::zoomLevel() const
397
{
398
    return m_view->zoomLevel();
399
400
}

401
void DolphinView::setSortRole(const QByteArray& role)
402
{
403
404
    if (role != sortRole()) {
        updateSortRole(role);
405
406
407
    }
}

408
QByteArray DolphinView::sortRole() const
409
{
410
411
    const KItemModelBase* model = m_container->controller()->model();
    return model->sortRole();
412
413
414
415
416
}

void DolphinView::setSortOrder(Qt::SortOrder order)
{
    if (sortOrder() != order) {
417
        updateSortOrder(order);
418
419
420
421
422
    }
}

Qt::SortOrder DolphinView::sortOrder() const
{
423
    return m_model->sortOrder();
424
425
}

426
427
428
429
430
431
432
433
434
void DolphinView::setSortFoldersFirst(bool foldersFirst)
{
    if (sortFoldersFirst() != foldersFirst) {
        updateSortFoldersFirst(foldersFirst);
    }
}

bool DolphinView::sortFoldersFirst() const
{
435
    return m_model->sortDirectoriesFirst();
436
437
}

438
void DolphinView::setVisibleRoles(const QList<QByteArray>& roles)
439
{
440
    const QList<QByteArray> previousRoles = roles;
441

442
    ViewProperties props(viewPropertiesUrl());
443
    props.setVisibleRoles(roles);
444

445
    m_visibleRoles = roles;
446
    m_view->setVisibleRoles(roles);
447

448
    emit visibleRolesChanged(m_visibleRoles, previousRoles);
449
450
}

451
QList<QByteArray> DolphinView::visibleRoles() const
452
{
453
    return m_visibleRoles;
454
455
}

456
457
void DolphinView::reload()
{
458
459
460
    QByteArray viewState;
    QDataStream saveStream(&viewState, QIODevice::WriteOnly);
    saveState(saveStream);
461

462
    setUrl(url());
463
    loadDirectory(url(), true);
464
465
466

    QDataStream restoreStream(viewState);
    restoreState(restoreStream);
467
468
}

469
void DolphinView::readSettings()
470
{
471
    const int oldZoomLevel = m_view->zoomLevel();
472

Laurent Montel's avatar
Laurent Montel committed
473
    GeneralSettings::self()->load();
474
    m_view->readSettings();
475
    applyViewProperties();
476

477
478
479
480
    const int delay = GeneralSettings::autoExpandFolders() ? 750 : -1;
    m_container->controller()->setAutoActivationDelay(delay);

    const int newZoomLevel = m_view->zoomLevel();
481
482
483
    if (newZoomLevel != oldZoomLevel) {
        emit zoomLevelChanged(newZoomLevel, oldZoomLevel);
    }
484
485
}

486
487
void DolphinView::writeSettings()
{
Laurent Montel's avatar
Laurent Montel committed
488
    GeneralSettings::self()->save();
489
    m_view->writeSettings();
490
491
}

492
493
void DolphinView::setNameFilter(const QString& nameFilter)
{
494
    m_model->setNameFilter(nameFilter);
495
496
}

497
498
QString DolphinView::nameFilter() const
{
499
    return m_model->nameFilter();
500
501
}

502
503
504
505
506
507
508
509
510
511
void DolphinView::setMimeTypeFilters(const QStringList& filters)
{
    return m_model->setMimeTypeFilters(filters);
}

QStringList DolphinView::mimeTypeFilters() const
{
    return m_model->mimeTypeFilters();
}

512
QString DolphinView::statusBarText() const
513
{
514
515
516
517
    QString summary;
    QString foldersText;
    QString filesText;

518
519
520
    int folderCount = 0;
    int fileCount = 0;
    KIO::filesize_t totalFileSize = 0;
521

522
    if (m_container->controller()->selectionManager()->hasSelection()) {
523
        // Give a summary of the status of the selected files
524
        const KFileItemList list = selectedItems();
525
        foreach (const KFileItem& item, list) {
526
527
528
529
            if (item.isDir()) {
                ++folderCount;
            } else {
                ++fileCount;
530
                totalFileSize += item.size();
531
532
            }
        }
533

534
        if (folderCount + fileCount == 1) {
535
536
            // If only one item is selected, show info about it
            return list.first().getStatusBarInfo();
537
        } else {
538
539
540
            // At least 2 items are selected
            foldersText = i18ncp("@info:status", "1 Folder selected", "%1 Folders selected", folderCount);
            filesText = i18ncp("@info:status", "1 File selected", "%1 Files selected", fileCount);
541
        }
542
543
    } else {
        calculateItemCount(fileCount, folderCount, totalFileSize);
544
545
        foldersText = i18ncp("@info:status", "1 Folder", "%1 Folders", folderCount);
        filesText = i18ncp("@info:status", "1 File", "%1 Files", fileCount);
546
547
    }

548
549
    if (fileCount > 0 && folderCount > 0) {
        summary = i18nc("@info:status folders, files (size)", "%1, %2 (%3)",
550
                        foldersText, filesText,
Laurent Montel's avatar
Laurent Montel committed
551
                        KFormat().formatByteSize(totalFileSize));
552
    } else if (fileCount > 0) {
553
554
        summary = i18nc("@info:status files (size)", "%1 (%2)",
                        filesText,
Laurent Montel's avatar
Laurent Montel committed
555
                        KFormat().formatByteSize(totalFileSize));
556
557
    } else if (folderCount > 0) {
        summary = foldersText;
558
559
    } else {
        summary = i18nc("@info:status", "0 Folders, 0 Files");
560
561
562
    }

    return summary;
563
564
}

565
QList<QAction*> DolphinView::versionControlActions(const KFileItemList& items) const
566
{
567
568
569
    QList<QAction*> actions;

    if (items.isEmpty()) {
570
        const KFileItem item = m_model->rootItem();
571
572
573
        if (!item.isNull()) {
            actions = m_versionControlObserver->actions(KFileItemList() << item);
        }
574
    } else {
575
        actions = m_versionControlObserver->actions(items);
576
577
578
    }

    return actions;
579
580
}

Lukáš Tinkl's avatar
Lukáš Tinkl committed
581
void DolphinView::setUrl(const QUrl& url)
582
{
583
    if (url == m_url) {
584
585
        return;
    }
586

587
588
    clearSelection();

589
    m_url = url;
590

Peter Penz's avatar
Peter Penz committed
591
    hideToolTip();
592

593
594
    disconnect(m_view, &DolphinItemListView::roleEditingFinished,
               this, &DolphinView::slotRoleEditingFinished);
595

596
597
598
599
    // It is important to clear the items from the model before
    // applying the view properties, otherwise expensive operations
    // might be done on the existing items although they get cleared
    // anyhow afterwards by loadDirectory().
600
    m_model->clear();
601
    applyViewProperties();
602
    loadDirectory(url);
603

604
    emit urlChanged(url);
605
606
}

607
608
void DolphinView::selectAll()
{
609
    KItemListSelectionManager* selectionManager = m_container->controller()->selectionManager();
610
    selectionManager->setSelected(0, m_model->count());
611
612
613
614
}

void DolphinView::invertSelection()
{
615
    KItemListSelectionManager* selectionManager = m_container->controller()->selectionManager();
616
    selectionManager->setSelected(0, m_model->count(), KItemListSelectionManager::Toggle);
617
618
619
620
}

void DolphinView::clearSelection()
{
621
    m_selectedUrls.clear();
622
    m_container->controller()->selectionManager()->clearSelection();
623
624
}

625
void DolphinView::renameSelectedItems()
626
{
627
    const KFileItemList items = selectedItems();
628
629
630
631
632
633
634
    if (items.isEmpty()) {
        return;
    }

    if (items.count() == 1 && GeneralSettings::renameInline()) {
        const int index = m_model->index(items.first());
        m_view->editRole(index, "text");
635

636
637
        hideToolTip();

638
639
        connect(m_view, &DolphinItemListView::roleEditingFinished,
                this, &DolphinView::slotRoleEditingFinished);
640
    } else {
641
642
643
        KIO::RenameFileDialog* dialog = new KIO::RenameFileDialog(items, this);
        connect(dialog, &KIO::RenameFileDialog::renamingFinished,
                this, &DolphinView::slotRenameDialogRenamingFinished);
Emirald Mateli's avatar
Emirald Mateli committed
644

645
        dialog->open();
646
647
648
649
650
651
    }

    // Assure that the current index remains visible when KFileItemModel
    // will notify the view about changed items (which might result in
    // a changed sorting).
    m_assureVisibleCurrentIndex = true;
652
653
}

654
void DolphinView::trashSelectedItems()
655
{
Lukáš Tinkl's avatar
Lukáš Tinkl committed
656
    const QList<QUrl> list = simplifiedSelectedUrls();
657
658
659
660
    KIO::JobUiDelegate uiDelegate;
    uiDelegate.setWindow(window());
    if (uiDelegate.askDeleteConfirmation(list, KIO::JobUiDelegate::Trash, KIO::JobUiDelegate::DefaultConfirmation)) {
        KIO::Job* job = KIO::trash(list);
661
        KIO::FileUndoManager::self()->recordJob(KIO::FileUndoManager::Trash, list, QUrl(QStringLiteral("trash:/")), job);
662
663
664
665
        KJobWidgets::setWindow(job, this);
        connect(job, &KIO::Job::result,
                this, &DolphinView::slotTrashFileFinished);
    }
666
667
668
669
}

void DolphinView::deleteSelectedItems()
{
Lukáš Tinkl's avatar
Lukáš Tinkl committed
670
    const QList<QUrl> list = simplifiedSelectedUrls();
671

672
673
674
    KIO::JobUiDelegate uiDelegate;
    uiDelegate.setWindow(window());
    if (uiDelegate.askDeleteConfirmation(list, KIO::JobUiDelegate::Delete, KIO::JobUiDelegate::DefaultConfirmation)) {
675
        KIO::Job* job = KIO::del(list);
676
        KJobWidgets::setWindow(job, this);
677
678
        connect(job, &KIO::Job::result,
                this, &DolphinView::slotDeleteFileFinished);
679
    }
680
}
681

682
683
void DolphinView::cutSelectedItems()
{
684
    QMimeData* mimeData = selectionMimeData();
685
    KIO::setClipboardDataCut(mimeData, true);
686
    QApplication::clipboard()->setMimeData(mimeData);
687
688
}

689
void DolphinView::copySelectedItems()
690
{
691
    QMimeData* mimeData = selectionMimeData();
692
    QApplication::clipboard()->setMimeData(mimeData);
693
694
}

695
void DolphinView::paste()
696
{
697
698
    pasteToUrl(url());
}
699

700
701
702
703
704
void DolphinView::pasteIntoFolder()
{
    const KFileItemList items = selectedItems();
    if ((items.count() == 1) && items.first().isDir()) {
        pasteToUrl(items.first().url());
705
    }
706
}
707

Nate Graham's avatar
Nate Graham committed
708
709
710
711
712
713
714
715
716
717
718
719
720
721
void DolphinView::duplicateSelectedItems()
{
    const KFileItemList itemList = selectedItems();
    if (itemList.isEmpty()) {
        return;
    }

    const QMimeDatabase db;

    // Duplicate all selected items and append "copy" to the end of the file name
    // but before the filename extension, if present
    QList<QUrl> newSelection;
    for (const auto &item : itemList) {
        const QUrl originalURL  = item.url();
722
        const QString originalDirectoryPath = originalURL.adjusted(QUrl::RemoveFilename).path();
Nate Graham's avatar
Nate Graham committed
723
        const QString originalFileName = item.name();
724

Nate Graham's avatar
Nate Graham committed
725
726
727
728
729
730
        QString extension = db.suffixForFileName(originalFileName);

        QUrl duplicateURL = originalURL;

        // No extension; new filename is "<oldfilename> copy"
        if (extension.isEmpty()) {
731
            duplicateURL.setPath(originalDirectoryPath + i18nc("<filename> copy", "%1 copy", originalFileName));
Nate Graham's avatar
Nate Graham committed
732
733
734
735
736
737
738
739
        // There's an extension; new filename is "<oldfilename> copy.<extension>"
        } else {
            // Need to add a dot since QMimeDatabase::suffixForFileName() doesn't include it
            extension = QLatin1String(".") + extension;
            const QString originalFilenameWithoutExtension = originalFileName.chopped(extension.size());
            // Preserve file's original filename extension in case the casing differs
            // from what QMimeDatabase::suffixForFileName() returned
            const QString originalExtension = originalFileName.right(extension.size());
740
            duplicateURL.setPath(originalDirectoryPath + i18nc("<filename> copy", "%1 copy", originalFilenameWithoutExtension) + originalExtension);
Nate Graham's avatar
Nate Graham committed
741
742
743
744
745
746
747
748
749
750
        }

        KIO::CopyJob* job = KIO::copyAs(originalURL, duplicateURL, KIO::HideProgressInfo);
        KJobWidgets::setWindow(job, this);

        if (job) {
            newSelection << duplicateURL;
            KIO::FileUndoManager::self()->recordCopyJob(job);
        }
    }
Elvis Angelaccio's avatar
Elvis Angelaccio committed
751
752

    forceUrlsSelection(newSelection.first(), newSelection);
Nate Graham's avatar
Nate Graham committed
753
754
}

755
756
757
758
759
void DolphinView::stopLoading()
{
    m_model->cancelDirectoryLoading();
}

760
761
void DolphinView::updatePalette()
{
762
    QColor color = KColorScheme(isActiveWindow() ? QPalette::Active : QPalette::Inactive, KColorScheme::View).background().color();
763
764
765
766
767
768
769
770
771
772
773
774
775
776
    if (!m_active) {
        color.setAlpha(150);
    }

    QWidget* viewport = m_container->viewport();
    if (viewport) {
        QPalette palette;
        palette.setColor(viewport->backgroundRole(), color);
        viewport->setPalette(palette);
    }

    update();
}

777
778
779
780
781
782
void DolphinView::abortTwoClicksRenaming()
{
    m_twoClicksRenamingItemUrl.clear();
    m_twoClicksRenamingTimer->stop();
}

783
bool DolphinView::eventFilter(QObject* watched, QEvent* event)
784
{
785
    switch (event->type()) {
786
787
    case QEvent::PaletteChange:
        updatePalette();
788
        QPixmapCache::clear();
789
790
        break;

791
792
793
794
795
    case QEvent::WindowActivate:
    case QEvent::WindowDeactivate:
        updatePalette();
        break;

796
    case QEvent::KeyPress:
797
        hideToolTip(ToolTipManager::HideBehavior::Instantly);
798
799
800
        if (GeneralSettings::useTabForSwitchingSplitView()) {
            QKeyEvent* keyEvent = static_cast<QKeyEvent*>(event);
            if (keyEvent->key() == Qt::Key_Tab && keyEvent->modifiers() == Qt::NoModifier) {
Elvis Angelaccio's avatar
Elvis Angelaccio committed
801
                emit toggleActiveViewRequested();
802
803
804
805
                return true;
            }
        }
        break;
806
807
808
809
810
811
    case QEvent::FocusIn:
        if (watched == m_container) {
            setActive(true);
        }
        break;

812
    case QEvent::GraphicsSceneDragEnter:
813
        if (watched == m_view) {
814
            m_dragging = true;
815
            abortTwoClicksRenaming();
816
817
818
819
        }
        break;

    case QEvent::GraphicsSceneDragLeave:
820
        if (watched == m_view) {
821
822
823
824
825
            m_dragging = false;
        }
        break;

    case QEvent::GraphicsSceneDrop:
826
        if (watched == m_view) {
827
828
            m_dragging = false;
        }
829
830
831
832
833
    default:
        break;
    }

    return QWidget::eventFilter(watched, event);
834
}
835

836
837
838
839
840
841
842
843
844
845
846
847
848
void DolphinView::wheelEvent(QWheelEvent* event)
{
    if (event->modifiers().testFlag(Qt::ControlModifier)) {
        const int numDegrees = event->delta() / 8;
        const int numSteps = numDegrees / 15;

        setZoomLevel(zoomLevel() + numSteps);
        event->accept();
    } else {
        event->ignore();
    }
}

849
850
851
852
853
854
void DolphinView::hideEvent(QHideEvent* event)
{
    hideToolTip();
    QWidget::hideEvent(event);
}

855
856
857
bool DolphinView::event(QEvent* event)
{
    if (event->type() == QEvent::WindowDeactivate) {
858
859
860
861
862
        /* See Bug 297355
         * Dolphin leaves file preview tooltips open even when is not visible.
         *
         * Hide tool-tip when Dolphin loses focus.
         */
863
        hideToolTip();
864
        abortTwoClicksRenaming();
865
866
867
868
869
    }

    return QWidget::event(event);
}

870
void DolphinView::activate()
871
{
872
873
874
    setActive(true);
}

875
876
void DolphinView::slotItemActivated(int index)
{
877
878
    abortTwoClicksRenaming();

879
    const KFileItem item = m_model->fileItem(index);
880
881
882
883
    if (!item.isNull()) {
        emit itemActivated(item);
    }
}
884

885
void DolphinView::slotItemsActivated(const KItemSet& indexes)
886
887
888
{
    Q_ASSERT(indexes.count() >= 2);

889
890
    abortTwoClicksRenaming();

891
892
    if (indexes.count() > 5) {
        QString question = i18np("Are you sure you want to open 1 item?", "Are you sure you want to open %1 items?", indexes.count());
893
894
895
896
897
898
        const int answer = KMessageBox::warningYesNo(this, question);
        if (answer != KMessageBox::Yes) {
            return;
        }
    }

899
900
901
    KFileItemList items;
    items.reserve(indexes.count());

902
    for (int index : indexes) {
903
        KFileItem item = m_model->fileItem(index);
Lukáš Tinkl's avatar
Lukáš Tinkl committed
904
        const QUrl& url = openItemAsFolderUrl(item);
905

906
        if (!url.isEmpty()) { // Open folders in new tabs
907
            emit tabRequested(url, DolphinTabWidget::AfterLastTab);
908
        } else {
909
            items.append(item);
910
911
        }
    }
912
913
914
915
916
917

    if (items.count() == 1) {
        emit itemActivated(items.first());
    } else if (items.count() > 1) {
        emit itemsActivated(items);
    }
918
919
920
}

void DolphinView::slotItemMiddleClicked(int index)
921
{
922
    const KFileItem& item = m_model->fileItem(index);
Lukáš Tinkl's avatar
Lukáš Tinkl committed
923
    const QUrl& url = openItemAsFolderUrl(item);
924
    if (!url.isEmpty()) {
925
        emit tabRequested(url, DolphinTabWidget::AfterCurrentTab);
926
    } else if (isTabsForFilesEnabled()) {
927
        emit tabRequested(item.url(), DolphinTabWidget::AfterCurrentTab);
928
929
    }
}
930

Peter Penz's avatar
Peter Penz committed
931
void DolphinView::slotItemContextMenuRequested(int index, const QPointF& pos)
932
{
933
934
935
936
937
938
    // Force emit of a selection changed signal before we request the
    // context menu, to update the edit-actions first. (See Bug 294013)
    if (m_selectionChangedTimer->isActive()) {
        emitSelectionChangedSignal();
    }

939
    const KFileItem item = m_model->fileItem(index);
Peter Penz's avatar
Peter Penz committed
940
941
942
943
944
945
946
947
948
949
    emit requestContextMenu(pos.toPoint(), item, url(), QList<QAction*>());
}

void DolphinView::slotViewContextMenuRequested(const QPointF& pos)
{