foldermodel.cpp 50.1 KB
Newer Older
1
/***************************************************************************
2
 *   Copyright (C) 2006 David Faure <faure@kde.org>                        *
3
 *   Copyright (C) 2008 Fredrik Höglund <fredrik@kde.org>                  *
4
 *   Copyright (C) 2008 Rafael Fernández López <ereslibre@kde.org>           *
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
 *   Copyright (C) 2011 Marco Martin <mart@kde.org>                        *
 *   Copyright (C) 2014 by Eike Hein <hein@kde.org>                        *
 *                                                                         *
 *   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.,                                       *
 *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA .        *
 ***************************************************************************/

#include "foldermodel.h"
25
26
#include "itemviewadapter.h"
#include "positioner.h"
27
28
29
30

#include <QApplication>
#include <QClipboard>
#include <QCollator>
31
32
33
#include <QDesktopWidget>
#include <QDrag>
#include <QImage>
34
#include <QItemSelectionModel>
35
#include <QMenu>
36
#include <QMimeData>
37
#include <QMimeDatabase>
38
39
40
41
#include <QPainter>
#include <QPixmap>
#include <QQuickItem>
#include <QQuickWindow>
42
43
#include <qplatformdefs.h>

44
#include <KDirWatch>
45
#include <KIO/DropJob>
46
47
#include <KAuthorized>
#include <KConfigGroup>
48
#include <KFileCopyToMenu>
49
50
#include <KFileItemActions>
#include <KFileItemListProperties>
51
#include <KNewFileMenu>
52
#include <KIO/DeleteJob>
53
#include <KIO/EmptyTrashJob>
54
#include <KIO/FileUndoManager>
55
#include <KIO/JobUiDelegate>
56
#include <KIO/Paste>
57
#include <KIO/PasteJob>
58
#include <KIO/RestoreJob>
59
#include <KLocalizedString>
60
#include <KPropertiesDialog>
61
#include <KSharedConfig>
62
#include <KShell>
63
#include <kio_version.h>
64

65
66
#include <KCoreDirLister>
#include <KDirLister>
67
68
#include <KDesktopFile>
#include <KDirModel>
69
#include <KIO/CopyJob>
70
#include <KIO/Job>
71
#include <KProtocolInfo>
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
#include <KRun>

#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>

DirLister::DirLister(QObject *parent) : KDirLister(parent)
{
}

DirLister:: ~DirLister()
{
}

void DirLister::handleError(KIO::Job *job)
{
    if (!autoErrorHandlingEnabled()) {
        emit error(job->errorString());
        return;
    }

    KDirLister::handleError(job);
}

FolderModel::FolderModel(QObject *parent) : QSortFilterProxyModel(parent),
97
    m_dirWatch(nullptr),
98
    m_dragInProgress(false),
99
    m_urlChangedWhileDragging(false),
100
101
102
103
    m_previewGenerator(0),
    m_viewAdapter(0),
    m_actionCollection(this),
    m_newMenu(0),
104
    m_fileItemActions(0),
105
    m_usedByContainment(false),
106
    m_locked(true),
Eike Hein's avatar
Eike Hein committed
107
    m_sortMode(0),
108
109
110
111
112
113
114
    m_sortDesc(false),
    m_sortDirsFirst(true),
    m_parseDesktopFiles(false),
    m_previews(false),
    m_filterMode(NoFilter),
    m_filterPatternMatchAll(true)
{
115
116
    //needed to pass the job around with qml
    qmlRegisterType<KIO::DropJob>();
117
118
119
    DirLister *dirLister = new DirLister(this);
    dirLister->setDelayedMimeTypes(true);
    dirLister->setAutoErrorHandlingEnabled(false, 0);
120
121
    connect(dirLister, &DirLister::error, this, &FolderModel::dirListFailed);
    connect(dirLister, &KCoreDirLister::itemsDeleted, this, &FolderModel::evictFromIsDirCache);
122
123
124

    connect(dirLister, &KCoreDirLister::started, this, std::bind(&FolderModel::setStatus, this, Status::Listing));

125
    void (KCoreDirLister::*myCompletedSignal)() = &KCoreDirLister::completed;
126
127
128
129
130
    QObject::connect(dirLister, myCompletedSignal, this, [this] {
        setStatus(Status::Ready);
        emit listingCompleted();
    });

131
    void (KCoreDirLister::*myCanceledSignal)() = &KCoreDirLister::canceled;
132
133
134
135
    QObject::connect(dirLister, myCanceledSignal, this, [this] {
        setStatus(Status::Canceled);
        emit listingCanceled();
    });
136
137
138

    m_dirModel = new KDirModel(this);
    m_dirModel->setDirLister(dirLister);
Eike Hein's avatar
Eike Hein committed
139
    m_dirModel->setDropsAllowed(KDirModel::DropOnDirectory | KDirModel::DropOnLocalExecutable);
140
141
142
143
144
145
146
147
148
149
150

    m_selectionModel = new QItemSelectionModel(this, this);
    connect(m_selectionModel, SIGNAL(selectionChanged(QItemSelection,QItemSelection)),
            this, SLOT(selectionChanged(QItemSelection,QItemSelection)));

    setSourceModel(m_dirModel);

    setSortLocaleAware(true);
    setFilterCaseSensitivity(Qt::CaseInsensitive);
    setDynamicSortFilter(true);

Eike Hein's avatar
Eike Hein committed
151
    sort(m_sortMode, m_sortDesc ? Qt::DescendingOrder : Qt::AscendingOrder);
152

153
154
155
156
157
158
159
160
161
    createActions();
}

FolderModel::~FolderModel()
{
}

QHash< int, QByteArray > FolderModel::roleNames() const
{
162
163
164
165
166
167
    return staticRoleNames();
}

QHash< int, QByteArray > FolderModel::staticRoleNames()
{
    QHash<int, QByteArray> roleNames;
168
169
    roleNames[Qt::DisplayRole] = "display";
    roleNames[Qt::DecorationRole] = "decoration";
170
    roleNames[BlankRole] = "blank";
171
    roleNames[OverlaysRole] = "overlays";
172
173
    roleNames[SelectedRole] = "selected";
    roleNames[IsDirRole] = "isDir";
174
    roleNames[IsLinkRole] = "isLink";
175
    roleNames[IsHiddenRole] = "isHidden";
176
    roleNames[UrlRole] = "url";
177
    roleNames[LinkDestinationUrl] = "linkDestinationUrl";
178
179
    roleNames[SizeRole] = "size";
    roleNames[TypeRole] = "type";
180
181
182
183
184
185

    return roleNames;
}

QString FolderModel::url() const
{
186
    return m_url;
187
188
}

189
void FolderModel::setUrl(const QString& url)
190
{
191
    const QUrl &resolvedUrl = resolve(url);
192

193
194
    if (url == m_url) {
        m_dirModel->dirLister()->updateDirectory(resolvedUrl);
195
196
197
198
        return;
    }

    beginResetModel();
199
    m_url = url;
200
    m_isDirCache.clear();
201
    m_dirModel->dirLister()->openUrl(resolvedUrl);
202
    clearDragImages();
203
    m_dragIndexes.clear();
204
205
206
    endResetModel();

    emit urlChanged();
207
    emit resolvedUrlChanged();
208
209
210

    m_errorString.clear();
    emit errorStringChanged();
211
212
213
214
215

    if (m_dirWatch) {
        delete m_dirWatch;
    }

216
    if (resolvedUrl.isValid()) {
217
218
219
220
221
222
        m_dirWatch = new KDirWatch(this);
        connect(m_dirWatch, &KDirWatch::created, this, &FolderModel::iconNameChanged);
        connect(m_dirWatch, &KDirWatch::dirty, this, &FolderModel::iconNameChanged);
        m_dirWatch->addFile(resolvedUrl.toLocalFile() + QLatin1String("/.directory"));
    }

223
224
225
226
    if (m_dragInProgress) {
        m_urlChangedWhileDragging = true;
    }

227
    emit iconNameChanged();
228
229
}

230
231
232
233
234
QUrl FolderModel::resolvedUrl() const
{
    return m_dirModel->dirLister()->url();
}

235
236
237
238
239
240
241
242
243
244
245
246
247
QUrl FolderModel::resolve(const QString& url)
{
    QUrl resolvedUrl;

    if (url.startsWith('~')) {
        resolvedUrl = QUrl::fromLocalFile(KShell::tildeExpand(url));
    } else {
        resolvedUrl = QUrl::fromUserInput(url);
    }

    return resolvedUrl;
}

248
249
250
251
252
253
254
255
256
257
258
QString FolderModel::iconName() const
{
    const KFileItem rootItem(m_dirModel->dirLister()->url());

    if (!rootItem.isFinalIconKnown()) {
        rootItem.determineMimeType();
    }

    return rootItem.iconName();
}

259
260
261
262
263
264
265
266
267
268
269
270
271
FolderModel::Status FolderModel::status() const
{
    return m_status;
}

void FolderModel::setStatus(Status status)
{
    if (m_status != status) {
        m_status = status;
        emit statusChanged();
    }
}

272
273
274
275
276
QString FolderModel::errorString() const
{
    return m_errorString;
}

277
278
279
280
281
bool FolderModel::dragging() const
{
    return m_dragInProgress;
}

282
283
284
285
286
287
288
289
290
291
bool FolderModel::usedByContainment() const
{
    return m_usedByContainment;
}

void FolderModel::setUsedByContainment(bool used)
{
    if (m_usedByContainment != used) {
        m_usedByContainment = used;

292
        QAction *action = m_actionCollection.action(QStringLiteral("refresh"));
293
294
295

        if (action) {
            action->setText(m_usedByContainment ? i18n("&Refresh Desktop") : i18n("&Refresh View"));
296
            action->setIcon(m_usedByContainment ? QIcon::fromTheme(QStringLiteral("user-desktop")) : QIcon::fromTheme(QStringLiteral("view-refresh")));
297
298
        }

299
300
301
302
        emit usedByContainmentChanged();
    }
}

303
304
305
306
307
308
309
310
311
312
313
314
315
316
bool FolderModel::locked() const
{
    return m_locked;
}

void FolderModel::setLocked(bool locked)
{
    if (m_locked != locked) {
        m_locked = locked;

        emit lockedChanged();
    }
}

317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
void FolderModel::dirListFailed(const QString& error)
{
    m_errorString = error;
    emit errorStringChanged();
}

int FolderModel::sortMode() const
{
    return m_sortMode;
}

void FolderModel::setSortMode(int mode)
{
    if (m_sortMode != mode) {
        m_sortMode = mode;

Eike Hein's avatar
Eike Hein committed
333
        if (mode == -1 /* Unsorted */) {
334
335
336
            setDynamicSortFilter(false);
        } else {
            invalidate();
Eike Hein's avatar
Eike Hein committed
337
338
            sort(m_sortMode, m_sortDesc ? Qt::DescendingOrder : Qt::AscendingOrder);
            setDynamicSortFilter(true);
339
        }
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354

        emit sortModeChanged();
    }
}

bool FolderModel::sortDesc() const
{
    return m_sortDesc;
}

void FolderModel::setSortDesc(bool desc)
{
    if (m_sortDesc != desc) {
        m_sortDesc = desc;

Eike Hein's avatar
Eike Hein committed
355
        if (m_sortMode != -1 /* Unsorted */) {
356
            invalidate();
Eike Hein's avatar
Eike Hein committed
357
            sort(m_sortMode, m_sortDesc ? Qt::DescendingOrder : Qt::AscendingOrder);
358
        }
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373

        emit sortDescChanged();
    }
}

bool FolderModel::sortDirsFirst() const
{
    return m_sortDirsFirst;
}

void FolderModel::setSortDirsFirst(bool enable)
{
    if (m_sortDirsFirst != enable) {
        m_sortDirsFirst = enable;

Eike Hein's avatar
Eike Hein committed
374
        if (m_sortMode != -1 /* Unsorted */) {
375
            invalidate();
Eike Hein's avatar
Eike Hein committed
376
            sort(m_sortMode, m_sortDesc ? Qt::DescendingOrder : Qt::AscendingOrder);
377
        }
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483

        emit sortDirsFirstChanged();
    }
}

bool FolderModel::parseDesktopFiles() const
{
    return m_parseDesktopFiles;
}

void FolderModel::setParseDesktopFiles(bool enable)
{
    if (m_parseDesktopFiles != enable) {
        m_parseDesktopFiles = enable;
        emit parseDesktopFilesChanged();
    }
}

QObject* FolderModel::viewAdapter() const
{
    return m_viewAdapter;
}

void FolderModel::setViewAdapter(QObject* adapter)
{
    if (m_viewAdapter != adapter) {
        KAbstractViewAdapter *abstractViewAdapter = dynamic_cast<KAbstractViewAdapter *>(adapter);

        m_viewAdapter = abstractViewAdapter;

        if (m_viewAdapter && !m_previewGenerator) {
            m_previewGenerator = new KFilePreviewGenerator(abstractViewAdapter, this);
            m_previewGenerator->setPreviewShown(m_previews);
            m_previewGenerator->setEnabledPlugins(m_previewPlugins);
        }

        emit viewAdapterChanged();
    }
}

bool FolderModel::previews() const
{
    return m_previews;
}

void FolderModel::setPreviews(bool previews)
{
    if (m_previews != previews) {
        m_previews = previews;

        if (m_previewGenerator) {
            m_previewGenerator->setPreviewShown(m_previews);
        }

        emit previewsChanged();
    }
}

QStringList FolderModel::previewPlugins() const
{
    return m_previewPlugins;
}

void FolderModel::setPreviewPlugins(const QStringList& previewPlugins)
{
    if (m_previewPlugins != previewPlugins) {
        m_previewPlugins = previewPlugins;

        if (m_previewGenerator) {
            m_previewGenerator->setPreviewShown(false);
            m_previewGenerator->setEnabledPlugins(m_previewPlugins);
            m_previewGenerator->setPreviewShown(true);
        }

        emit previewPluginsChanged();
    }
}

int FolderModel::filterMode() const
{
    return m_filterMode;
}

void FolderModel::setFilterMode(int filterMode)
{
    if (m_filterMode != (FilterMode)filterMode) {
        m_filterMode = (FilterMode)filterMode;

        invalidateFilter();

        emit filterModeChanged();
    }
}

QString FolderModel::filterPattern() const
{
    return m_filterPattern;
}

void FolderModel::setFilterPattern(const QString &pattern)
{
    if (m_filterPattern == pattern) {
        return;
    }

    m_filterPattern = pattern;
484
    m_filterPatternMatchAll = (pattern == QLatin1String("*"));
485
486
487
488
489
490
491
492
493
494
495

    const QStringList patterns = pattern.split(' ');
    m_regExps.clear();

    foreach (const QString &pattern, patterns) {
        QRegExp rx(pattern);
        rx.setPatternSyntax(QRegExp::Wildcard);
        rx.setCaseSensitivity(Qt::CaseInsensitive);
        m_regExps.append(rx);
    }

496
497
    invalidateFilter();

498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
    emit filterPatternChanged();
}

QStringList FolderModel::filterMimeTypes() const
{
    return m_mimeSet.toList();
}

void FolderModel::setFilterMimeTypes(const QStringList &mimeList)
{
    const QSet<QString> &set = QSet<QString>::fromList(mimeList);

    if (m_mimeSet != set) {

        m_mimeSet = set;

        invalidateFilter();

        emit filterMimeTypesChanged();
    }
}

520
521
522
523
524
KFileItem FolderModel::rootItem() const
{
    return m_dirModel->dirLister()->rootItem();
}

525
526
527
528
529
530
531
532
533
void FolderModel::up()
{
    const QUrl &up = KIO::upUrl(resolvedUrl());

    if (up.isValid()) {
        setUrl(up.toString());
    }
}

534
void FolderModel::cd(int row)
535
{
536
537
538
539
    if (row < 0) {
        return;
    }

540
541
542
543
544
545
546
547
548
549
550
551
552
553
    const QModelIndex idx = index(row, 0);
    bool isDir = data(idx, IsDirRole).toBool();

    if (isDir) {
        const KFileItem  item = itemForIndex(idx);
        if (m_parseDesktopFiles && item.isDesktopFile()) {
            const KDesktopFile file(item.targetUrl().path());
            if (file.readType() == QLatin1String("Link")) {
                setUrl(file.readUrl());
            }
        }
        else {
            setUrl(item.url().toString());
        }
554
555
    }
}
556

557
558
559
560
561
562
563
void FolderModel::run(int row)
{
    if (row < 0) {
        return;
    }

    KFileItem item = itemForIndex(index(row, 0));
564

565
566
567
568
    QUrl url(item.targetUrl());

    // FIXME TODO: This can go once we depend on a KIO w/ fe1f50caaf2.
    if (url.scheme().isEmpty()) {
569
        url.setScheme(QStringLiteral("file"));
570
    }
571

572
    KRun *run = new KRun(url, 0);
573
574
575
576
577
    // On desktop:/ we want to be able to run .desktop files right away,
    // otherwise ask for security reasons. We also don't use the targetUrl()
    // from above since we don't want the resolved /home/foo/Desktop URL.
    run->setShowScriptExecutionPrompt(item.url().scheme() != QLatin1String("desktop")
                                   || item.url().adjusted(QUrl::RemoveFilename).path() != QLatin1String("/"));
578
579
}

580
581
582
583
584
585
void FolderModel::runSelected()
{
    if (!m_selectionModel->hasSelection()) {
        return;
    }

586
587
588
589
590
591
592
    if (m_selectionModel->selectedIndexes().count() == 1) {
        run(m_selectionModel->selectedIndexes().constFirst().row());
        return;
    }

    KFileItemActions fileItemActions(this);
    KFileItemList items;
593

594
    foreach (const QModelIndex &index, m_selectionModel->selectedIndexes()) {
595
        // Skip over directories.
596
597
        if (!index.data(IsDirRole).toBool()) {
            items << itemForIndex(index);
598
        }
599
    }
600
601

    fileItemActions.runPreferredApplications(items, QString());
602
603
}

604
605
void FolderModel::rename(int row, const QString& name)
{
606
607
608
609
    if (row < 0) {
        return;
    }

610
611
612
613
    QModelIndex idx = index(row, 0);
    m_dirModel->setData(mapToSource(idx), name, Qt::EditRole);
}

614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
int FolderModel::fileExtensionBoundary(int row)
{
    const QModelIndex idx = index(row, 0);
    const QString &name = data(idx, Qt::DisplayRole).toString();

    int boundary = name.length();

    if (data(idx, IsDirRole).toBool()) {
        return boundary;
    }

    QMimeDatabase db;
    const QString &ext = db.suffixForFileName(name);

    if (ext.isEmpty()) {
        boundary = name.lastIndexOf(QLatin1Char('.'));

        if (boundary < 1) {
            boundary = name.length();
        }
    } else {
        boundary -= ext.length() + 1;
    }

    return boundary;
}

641
642
643
644
645
bool FolderModel::hasSelection()
{
    return m_selectionModel->hasSelection();
}

646
647
bool FolderModel::isSelected(int row)
{
648
649
650
651
    if (row < 0) {
        return false;
    }

652
653
654
    return m_selectionModel->isSelected(index(row, 0));
}

655
656
void FolderModel::setSelected(int row)
{
657
658
659
660
    if (row < 0) {
        return;
    }

661
662
663
    m_selectionModel->select(index(row, 0), QItemSelectionModel::Select);
}

664
665
666
667
668
669
670
671
672
void FolderModel::toggleSelected(int row)
{
    if (row < 0) {
        return;
    }

    m_selectionModel->select(index(row, 0), QItemSelectionModel::Toggle);
}

673
void FolderModel::setRangeSelected(int anchor, int to)
674
{
675
    if (anchor < 0 || to < 0) {
676
677
678
        return;
    }

679
    QItemSelection selection(index(anchor, 0), index(to, 0));
680
681
682
    m_selectionModel->select(selection, QItemSelectionModel::ClearAndSelect);
}

683
void FolderModel::updateSelection(const QVariantList &rows, bool toggle)
684
{
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
    QItemSelection newSelection;

    int iRow = -1;

    foreach (const QVariant &row, rows) {
        iRow = row.toInt();

        if (iRow < 0) {
            return;
        }

        const QModelIndex &idx = index(iRow, 0);
        newSelection.select(idx, idx);
    }

    if (toggle) {
        QItemSelection pinnedSelection = m_pinnedSelection;
        pinnedSelection.merge(newSelection, QItemSelectionModel::Toggle);
        m_selectionModel->select(pinnedSelection, QItemSelectionModel::ClearAndSelect);
    } else {
        m_selectionModel->select(newSelection, QItemSelectionModel::ClearAndSelect);
    }
707
708
709
710
}

void FolderModel::clearSelection()
{
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
    if (m_selectionModel->hasSelection()) {
        m_selectionModel->clear();
    }
}

void FolderModel::pinSelection()
{
    m_pinnedSelection = m_selectionModel->selection();
}

void FolderModel::unpinSelection()
{
    m_pinnedSelection = QItemSelection();
}

void FolderModel::addItemDragImage(int row, int x, int y, int width, int height, const QVariant &image)
{
    if (row < 0) {
        return;
    }

732
    delete m_dragImages.take(row);
733

734
    DragImage *dragImage = new DragImage();
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
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
    dragImage->row = row;
    dragImage->rect = QRect(x, y, width, height);
    dragImage->image = image.value<QImage>();
    dragImage->blank = false;

    m_dragImages.insert(row, dragImage);
}

void FolderModel::clearDragImages()
{
    if (!m_dragImages.isEmpty()) {
        foreach (DragImage *image, m_dragImages) {
            delete image;
        }

        m_dragImages.clear();
    }
}

void FolderModel::setDragHotSpotScrollOffset(int x, int y)
{
    m_dragHotSpotScrollOffset.setX(x);
    m_dragHotSpotScrollOffset.setY(y);
}

QPoint FolderModel::dragCursorOffset(int row)
{
    if (!m_dragImages.contains(row)) {
        return QPoint(-1, -1);
    }

    return m_dragImages.value(row)->cursorOffset;
}

void FolderModel::addDragImage(QDrag *drag, int x, int y)
{
    if (!drag || m_dragImages.isEmpty()) {
        return;
    }

    QRegion region;

    foreach (DragImage *image, m_dragImages) {
        image->blank = isBlank(image->row);
        image->rect.translate(-m_dragHotSpotScrollOffset.x(), -m_dragHotSpotScrollOffset.y());
        if (!image->blank && !image->image.isNull()) {
            region = region.united(image->rect);
        }
    }

    QRect rect = region.boundingRect();
    QPoint offset = rect.topLeft();
    rect.translate(-offset.x(), -offset.y());

    QImage dragImage(rect.size(), QImage::Format_RGBA8888);
    dragImage.fill(Qt::transparent);

    QPainter painter(&dragImage);

    QPoint pos;

    foreach (DragImage *image, m_dragImages) {
        if (!image->blank && !image->image.isNull()) {
            pos = image->rect.translated(-offset.x(), -offset.y()).topLeft();
Eike Hein's avatar
Eike Hein committed
799
800
            image->cursorOffset.setX(pos.x() - (x - offset.x()));
            image->cursorOffset.setY(pos.y() - (y - offset.y()));
801
802
803
804
805
806
807
808
809
810
811
812
813

            painter.drawImage(pos, image->image);
        }

        // FIXME HACK: Operate on copy.
        image->rect.translate(m_dragHotSpotScrollOffset.x(), m_dragHotSpotScrollOffset.y());
    }

    drag->setPixmap(QPixmap::fromImage(dragImage));
    drag->setHotSpot(QPoint(x - offset.x(), y - offset.y()));
}

void FolderModel::dragSelected(int x, int y)
814
{
Eike Hein's avatar
Eike Hein committed
815
816
817
818
819
    if (m_dragInProgress) {
        return;
    }

    m_dragInProgress = true;
820
821
    emit draggingChanged();
    m_urlChangedWhileDragging = false;
Eike Hein's avatar
Eike Hein committed
822

823
824
825
826
827
828
829
830
    // Avoid starting a drag synchronously in a mouse handler or interferes with
    // child event filtering in parent items (and thus e.g. press-and-hold hand-
    // ling in a containment).
    QMetaObject::invokeMethod(this, "dragSelectedInternal", Qt::QueuedConnection,
        Q_ARG(int, x),
        Q_ARG(int, y));
}

Bhushan Shah's avatar
Bhushan Shah committed
831
void FolderModel::dragSelectedInternal(int x, int y)
832
833
{
    if (!m_viewAdapter || !m_selectionModel->hasSelection()) {
Eike Hein's avatar
Eike Hein committed
834
        m_dragInProgress = false;
835
        emit draggingChanged();
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
        return;
    }

    ItemViewAdapter *adapter = qobject_cast<ItemViewAdapter *>(m_viewAdapter);
    QQuickItem *item = qobject_cast<QQuickItem *>(adapter->adapterView());

    QDrag *drag = new QDrag(item);

    addDragImage(drag, x, y);

    m_dragIndexes = m_selectionModel->selectedIndexes();

    qSort(m_dragIndexes.begin(), m_dragIndexes.end());

    // TODO: Optimize to emit contiguous groups.
    emit dataChanged(m_dragIndexes.first(), m_dragIndexes.last(), QVector<int>() << BlankRole);

    QModelIndexList sourceDragIndexes;

    foreach (const QModelIndex &index, m_dragIndexes) {
        sourceDragIndexes.append(mapToSource(index));
    }

    drag->setMimeData(m_dirModel->mimeData(sourceDragIndexes));

861
862
863
864
865
866
    // Due to spring-loading (aka auto-expand), the URL might change
    // while the drag is in-flight - in that case we don't want to
    // unnecessarily emit dataChanged() for (possibly invalid) indices
    // after it ends.
    const QUrl currentUrl(m_dirModel->dirLister()->url());

867
868
    item->grabMouse();
    drag->exec(supportedDragActions());
869

870
871
    item->ungrabMouse();

872
873
874
875
876
877
878
879
880
881
882
    m_dragInProgress = false;
    emit draggingChanged();
    m_urlChangedWhileDragging = false;

    if (m_dirModel->dirLister()->url() == currentUrl) {
        const QModelIndex first(m_dragIndexes.first());
        const QModelIndex last(m_dragIndexes.last());
        m_dragIndexes.clear();
        // TODO: Optimize to emit contiguous groups.
        emit dataChanged(first, last, QVector<int>() << BlankRole);
    }
883
884
885
886
}

void FolderModel::drop(QQuickItem *target, QObject* dropEvent, int row)
{
887
    QMimeData *mimeData = qobject_cast<QMimeData *>(dropEvent->property("mimeData").value<QObject *>());
888
889
890
891
892

    if (!mimeData) {
        return;
    }

893
    if (m_dragInProgress && row == -1 && !m_urlChangedWhileDragging) {
894
895
896
897
        if (m_locked || mimeData->urls().isEmpty()) {
            return;
        }

Eike Hein's avatar
Eike Hein committed
898
        setSortMode(-1);
899
900
901
902
903
904
905

        emit move(dropEvent->property("x").toInt(), dropEvent->property("y").toInt(),
            mimeData->urls());

        return;
    }

Eike Hein's avatar
Eike Hein committed
906
    QModelIndex idx;
907
908
909
    KFileItem item;

    if (row > -1 && row < rowCount()) {
Eike Hein's avatar
Eike Hein committed
910
911
         idx = index(row, 0);
         item = itemForIndex(idx);
912
913
    }

914
915
    QUrl dropTargetUrl;

Kai Uwe Broulik's avatar
Kai Uwe Broulik committed
916
917
    // So we get to run mostLocalUrl() over the current URL.
    if (item.isNull()) {
918
        item = rootItem();
Kai Uwe Broulik's avatar
Kai Uwe Broulik committed
919
920
    }

921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
    if (item.isNull()) {
        dropTargetUrl = m_dirModel->dirLister()->url();
    } else if (m_parseDesktopFiles && item.isDesktopFile()) {
        const KDesktopFile file(item.targetUrl().path());

        if (file.readType() == QLatin1String("Link")) {
            dropTargetUrl = QUrl(file.readUrl());
        } else {
            dropTargetUrl = item.mostLocalUrl();
        }
    } else {
        dropTargetUrl = item.mostLocalUrl();
    }

    if (mimeData->hasFormat(QStringLiteral("application/x-kde-ark-dndextract-service")) &&
936
937
938
        mimeData->hasFormat(QStringLiteral("application/x-kde-ark-dndextract-path"))) {
        const QString remoteDBusClient = mimeData->data(QStringLiteral("application/x-kde-ark-dndextract-service"));
        const QString remoteDBusPath = mimeData->data(QStringLiteral("application/x-kde-ark-dndextract-path"));
939
940
941

        QDBusMessage message =
            QDBusMessage::createMethodCall(remoteDBusClient, remoteDBusPath,
942
943
                                            QStringLiteral("org.kde.ark.DndExtract"),
                                            QStringLiteral("extractSelectedFilesTo"));
944
        message.setArguments({dropTargetUrl.toDisplayString(QUrl::PreferLocalFile)});
945
946
947
948
949
950

        QDBusConnection::sessionBus().call(message);

        return;
    }

Eike Hein's avatar
Eike Hein committed
951
    if (idx.isValid() && !(flags(idx) & Qt::ItemIsDropEnabled)) {
952
953
954
        return;
    }

955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
    QPoint pos;
    pos.setX(dropEvent->property("x").toInt());
    pos.setY(dropEvent->property("y").toInt());

    pos = target->mapToScene(pos).toPoint();
    pos = target->window()->mapToGlobal(pos);

    Qt::DropAction proposedAction((Qt::DropAction)dropEvent->property("proposedAction").toInt());
    Qt::DropActions possibleActions(dropEvent->property("possibleActions").toInt());
    Qt::MouseButtons buttons(dropEvent->property("buttons").toInt());
    Qt::KeyboardModifiers modifiers(dropEvent->property("modifiers").toInt());

    QDropEvent ev(pos, possibleActions, mimeData, buttons, modifiers);
    ev.setDropAction(proposedAction);

970
    KIO::DropJob *dropJob = KIO::drop(&ev, dropTargetUrl);
971
    dropJob->uiDelegate()->setAutoErrorHandlingEnabled(true);
972
973
974
    const int x = dropEvent->property("x").toInt();
    const int y = dropEvent->property("y").toInt();

975
976
977
978
979
980
981
982
983
984
    // The QMimeData we extract from the DropArea's drop event is deleted as soon as this method
    // ends but we need to keep a copy for when popupMenuAboutToShow fires.
    QMimeData *mimeCopy = new QMimeData();
    for (const QString &format : mimeData->formats()) {
        mimeCopy->setData(format, mimeData->data(format));
    }

    connect(dropJob, static_cast<void(KIO::DropJob::*)(const KFileItemListProperties &)>(&KIO::DropJob::popupMenuAboutToShow), this, [this, mimeCopy, x, y, dropJob](const KFileItemListProperties &) {
        emit popupMenuAboutToShow(dropJob, mimeCopy, x, y);
        mimeCopy->deleteLater();
985
    });
986
987
}

988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
void FolderModel::dropCwd(QObject* dropEvent)
{
    QMimeData *mimeData = qobject_cast<QMimeData *>(dropEvent->property("mimeData").value<QObject *>());

    if (!mimeData) {
        return;
    }

    if (mimeData->hasFormat(QStringLiteral("application/x-kde-ark-dndextract-service")) &&
        mimeData->hasFormat(QStringLiteral("application/x-kde-ark-dndextract-path"))) {
        const QString remoteDBusClient = mimeData->data(QStringLiteral("application/x-kde-ark-dndextract-service"));
        const QString remoteDBusPath = mimeData->data(QStringLiteral("application/x-kde-ark-dndextract-path"));

        QDBusMessage message =
            QDBusMessage::createMethodCall(remoteDBusClient, remoteDBusPath,
                                            QStringLiteral("org.kde.ark.DndExtract"),
                                            QStringLiteral("extractSelectedFilesTo"));
        message.setArguments(QVariantList() << m_dirModel->dirLister()->url().adjusted(QUrl::PreferLocalFile).toString());

        QDBusConnection::sessionBus().call(message);
    } else {
        Qt::DropAction proposedAction((Qt::DropAction)dropEvent->property("proposedAction").toInt());
        Qt::DropActions possibleActions(dropEvent->property("possibleActions").toInt());
        Qt::MouseButtons buttons(dropEvent->property("buttons").toInt());
        Qt::KeyboardModifiers modifiers(dropEvent->property("modifiers").toInt());

        QDropEvent ev(QPoint(), possibleActions, mimeData, buttons, modifiers);
        ev.setDropAction(proposedAction);

        KIO::DropJob *dropJob = KIO::drop(&ev, m_dirModel->dirLister()->url().adjusted(QUrl::PreferLocalFile));
1018
        dropJob->uiDelegate()->setAutoErrorHandlingEnabled(true);
1019
1020
1021
    }
}

1022
1023
1024
1025
1026
1027
void FolderModel::selectionChanged(QItemSelection selected, QItemSelection deselected)
{
    QModelIndexList indices = selected.indexes();
    indices.append(deselected.indexes());

    QVector<int> roles;
1028
    roles.append(SelectedRole);
1029
1030
1031
1032

    foreach(const QModelIndex index, indices) {
        emit dataChanged(index, index, roles);
    }
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053

    if (!m_selectionModel->hasSelection()) {
        clearDragImages();
    } else {
        foreach (const QModelIndex &idx, deselected.indexes()) {
            if (m_dragImages.contains(idx.row())) {
                DragImage *image = m_dragImages.value(idx.row());
                delete image;
                m_dragImages.remove(idx.row());
            }
        }
    }
}

bool FolderModel::isBlank(int row) const
{
    if (row < 0) {
        return true;
    }

    return data(index(row, 0), BlankRole).toBool();
1054
1055
1056
1057
1058
1059
1060
1061
}

QVariant FolderModel::data(const QModelIndex& index, int role) const
{
    if (!index.isValid()) {
        return QVariant();
    }

1062
1063
    if (role == BlankRole) {
        return m_dragIndexes.contains(index);
1064
1065
1066
    } else if (role == OverlaysRole) {
        const KFileItem item = itemForIndex(index);
        return item.overlays();
1067
    } else if (role == SelectedRole) {
1068
        return m_selectionModel->isSelected(index);
1069
    } else if (role == IsDirRole) {
1070
1071
1072
1073
1074
1075
1076
        const QUrl &url = data(index, UrlRole).value<QUrl>();

        if (m_isDirCache.contains(url)) {
            return m_isDirCache[url];
        } else {
            return isDir(mapToSource(index), m_dirModel);
        }
1077
1078
1079
    } else if (role == IsLinkRole) {
        const KFileItem item = itemForIndex(index);
        return item.isLink();
1080
1081
1082
    } else if (role == IsHiddenRole) {
        const KFileItem item = itemForIndex(index);
        return item.isHidden();
1083
1084
    } else if (role == UrlRole) {
        return itemForIndex(index).url();
1085
1086
1087
1088
1089
1090
    } else if (role == LinkDestinationUrl) {
        const KFileItem item = itemForIndex(index);

        if (m_parseDesktopFiles && item.isDesktopFile()) {
            const KDesktopFile file(item.targetUrl().path());

1091
            if (file.readType() == QLatin1String("Link")) {
1092
1093
1094
1095
1096
                return file.readUrl();
            }
        }

        return item.url();
1097
    } else if (role == SizeRole) {
Eike Hein's avatar
Eike Hein committed
1098
1099
1100
1101
1102
        bool isDir = data(index, IsDirRole).toBool();

        if (!isDir) {
            return m_dirModel->data(mapToSource(QSortFilterProxyModel::index(index.row(), 1)), Qt::DisplayRole);
        }
1103
    } else if (role == TypeRole) {
1104
        return m_dirModel->data(mapToSource(QSortFilterProxyModel::index(index.row(), 6)), Qt::DisplayRole);
1105
1106
    } else if (role == FileNameRole) {
        return itemForIndex(index).url().fileName();
1107
1108
1109
1110
1111
    }

    return QSortFilterProxyModel::data(index, role);
}

1112
1113
1114
1115
1116
int FolderModel::indexForUrl(const QUrl& url) const
{
    return mapFromSource(m_dirModel->indexForUrl(url)).row();
}

1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
KFileItem FolderModel::itemForIndex(const QModelIndex &index) const
{
    return m_dirModel->itemForIndex(mapToSource(index));
}

bool FolderModel::isDir(const QModelIndex &index, const KDirModel *dirModel) const
{
    KFileItem item = dirModel->itemForIndex(index);
    if (item.isDir()) {
        return true;
    }

    if (m_parseDesktopFiles && item.isDesktopFile()) {
        // Check if the desktop file is a link to a directory
        KDesktopFile file(item.targetUrl().path());
1132

1133
        if (file.readType() == QLatin1String("Link")) {
1134
            const QUrl url(file.readUrl());
1135

1136
            if (!m_isDirCache.contains(item.url()) && KProtocolInfo::protocolClass(url.scheme()) == QStringLiteral(":local")) {
1137
1138
1139
1140
                KIO::StatJob *job = KIO::stat(url, KIO::HideProgressInfo);
                job->setProperty("org.kde.plasma.folder_url", item.url());
                job->setSide(KIO::StatJob::SourceSide);
                job->setDetails(0);
1141
                connect(job, &KJob::result, this, &FolderModel::statResult);
1142
1143
1144
1145
1146
1147
1148
            }
        }
    }

    return false;
}

1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
void FolderModel::statResult(KJob *job)
{
    KIO::StatJob *statJob = static_cast<KIO::StatJob*>(job);

    const QUrl &url = statJob->property("org.kde.plasma.folder_url").value<QUrl>();
    const QModelIndex &idx = index(indexForUrl(url), 0);

    if (idx.isValid()) {
        m_isDirCache[url] = statJob->statResult().isDir();

        emit dataChanged(idx, idx, QVector<int>() << IsDirRole);
    }
}

Eike Hein's avatar
Eike Hein committed
1163
void FolderModel::evictFromIsDirCache(const KFileItemList& items)
1164
{
Eike Hein's avatar
Eike Hein committed
1165
1166
1167
    foreach (const KFileItem &item, items) {
        m_isDirCache.remove(item.url());
    }
1168
1169
}

1170
1171
1172
1173
1174
1175
1176
bool FolderModel::lessThan(const QModelIndex &left, const QModelIndex &right) const
{
    const KDirModel *dirModel = static_cast<KDirModel*>(sourceModel());

    if (m_sortDirsFirst || left.column() == KDirModel::Size) {
        bool leftIsDir = isDir(left, dirModel);
        bool rightIsDir = isDir(right, dirModel);
Eike Hein's avatar
Eike Hein committed
1177

1178
        if (leftIsDir && !rightIsDir) {
Eike Hein's avatar
Eike Hein committed
1179
            return (sortOrder() == Qt::AscendingOrder);
1180
        }
Eike Hein's avatar
Eike Hein committed
1181

1182
        if (!leftIsDir && rightIsDir) {
Eike Hein's avatar
Eike Hein committed
1183
            return (sortOrder() == Qt::DescendingOrder);
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
        }
    }

    const KFileItem leftItem = dirModel->data(left, KDirModel::FileItemRole).value<KFileItem>();
    const KFileItem rightItem = dirModel->data(right, KDirModel::FileItemRole).value<KFileItem>();
    const int column = left.column();
    int result = 0;

    switch (column) {
        case KDirModel::Size: {
Eike Hein's avatar
Eike Hein committed
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
                if (isDir(left, dirModel) && isDir(right, dirModel)) {
                    const int leftChildCount = dirModel->data(left, KDirModel::ChildCountRole).toInt();
                    const int rightChildCount = dirModel->data(right, KDirModel::ChildCountRole).toInt();
                    if (leftChildCount < rightChildCount)
                        result = -1;
                    else if (leftChildCount > rightChildCount)
                        result = +1;
                } else {
                    const KIO::filesize_t leftSize = leftItem.size();
                    const KIO::filesize_t rightSize = rightItem.size();
                    if (leftSize < rightSize)
                        result = -1;
                    else if (leftSize > rightSize)
                        result = +1;
                }

                break;
            }
        case KDirModel::ModifiedTime: {
                const QDateTime leftTime = leftItem.time(KFileItem::ModificationTime);
                const QDateTime rightTime = rightItem.time(KFileItem::ModificationTime);
                if (leftTime < rightTime)
1216
                    result = -1;
Eike Hein's avatar
Eike Hein committed
1217
                else if (leftTime > rightTime)
1218
                    result = +1;
Eike Hein's avatar
Eike Hein committed
1219
1220

                break;
1221
1222
1223
1224
1225
            }
        case KDirModel::Type:
            result = QString::compare(dirModel->data(left, Qt::DisplayRole).toString(),
                                      dirModel->data(right, Qt::DisplayRole).toString());
            break;
Eike Hein's avatar
Eike Hein committed
1226
1227
1228

        default:
            break;
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
    }

    if (result != 0)
        return result < 0;

    QCollator collator;

    result = collator.compare(leftItem.text(), rightItem.text());

    if (result != 0)
        return result < 0;

    result = collator.compare(leftItem.name(), rightItem.name());

    if (result != 0)
        return result < 0;

    return QString::compare(leftItem.url().url(), rightItem.url().url(), Qt::CaseSensitive);
}

1249
1250
1251
1252
1253
Qt::DropActions FolderModel::supportedDragActions() const
{
    return Qt::CopyAction | Qt::MoveAction | Qt::LinkAction;
}

1254
1255
1256
1257
1258
inline bool FolderModel::matchMimeType(const KFileItem &item) const
{
    if (m_mimeSet.isEmpty()) {
        return false;
    }
Eike Hein's avatar
Eike Hein committed
1259
1260
1261
1262
1263

    if (m_mimeSet.contains(QStringLiteral("all/all")) || m_mimeSet.contains(QStringLiteral("all/allfiles"))) {
        return true;
    }

1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
    const QString mimeType = item.determineMimeType().name();
    return m_mimeSet.contains(mimeType);
}

inline bool FolderModel::matchPattern(const KFileItem &item) const
{
    if (m_filterPatternMatchAll) {
        return true;
    }

    const QString name = item.name();
    QListIterator<QRegExp> i(m_regExps);
    while (i.hasNext()) {
        if (i.next().exactMatch(name)) {
            return true;
        }
    }

    return false;
}

bool FolderModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const
{
    if (m_filterMode == NoFilter) {
        return true;
    }

    const KDirModel *dirModel = static_cast<KDirModel*>(sourceModel());
    const KFileItem item = dirModel->itemForIndex(dirModel->index(sourceRow, KDirModel::Name, sourceParent));

    if (m_filterMode == FilterShowMatches) {
        return (matchPattern(item) && matchMimeType(item));
    } else {
        return !(matchPattern(item) && matchMimeType(item));
    }
}

void FolderModel::createActions()
{
    KIO::FileUndoManager *manager = KIO::FileUndoManager::self();

    QAction *cut = KStandardAction::cut(this, SLOT(cut()), this);
    QAction *copy = KStandardAction::copy(this, SLOT(copy()), this);
1307

1308
1309
1310
1311
    QAction *undo = KStandardAction::undo(manager, SLOT(undo()), this);
    undo->setEnabled(manager->undoAvailable());
    undo->setShortcutContext(Qt::WidgetShortcut);
    connect(manager, SIGNAL(undoAvailable(bool)), undo, SLOT(setEnabled(bool)));
1312
    connect(manager, &KIO::FileUndoManager::undoTextChanged, this, &FolderModel::undoTextChanged);
1313
1314
1315
1316
1317

    QAction *paste = KStandardAction::paste(this, SLOT(paste()), this);
    QAction *pasteTo = KStandardAction::paste(this, SLOT(pasteTo()), this);

    QAction *reload = new QAction(i18n("&Reload"), this);
1318
    connect(reload, &QAction::triggered, this, &FolderModel::refresh);
1319

1320
1321
    QAction *refresh = new QAction(QIcon::fromTheme(QStringLiteral("view-refresh")), i18n("&Refresh View"), this);
    connect(refresh, &QAction::triggered, this, &FolderModel::refresh);
1322

1323
1324
    QAction *rename = new QAction(QIcon::fromTheme(QStringLiteral("edit-rename")), i18n("&Rename"), this);
    connect(rename, &QAction::triggered, this, &FolderModel::requestRename);
1325

1326
1327
    QAction *trash = new QAction(QIcon::fromTheme(QStringLiteral("user-trash")), i18n("&Move to Trash"), this);
    connect(trash, &QAction::triggered, this, &FolderModel::moveSelectedToTrash);
1328

1329
1330
    QAction *emptyTrash = new QAction(QIcon::fromTheme(QStringLiteral("trash-empty")), i18n("&Empty Trash Bin"), this);
    connect(emptyTrash, &QAction::triggered, this, &FolderModel::emptyTrashBin);
1331

1332
1333
1334
    QAction *restoreFromTrash = new QAction(i18nc("Restore from trash", "Restore"), this);
    connect(restoreFromTrash, &QAction::triggered, this, &FolderModel::restoreSelectedFromTrash);

1335
1336
    QAction *del = new QAction(QIcon::fromTheme(QStringLiteral("edit-delete")), i18n("&Delete"), this);
    connect(del, &QAction::triggered, this, &FolderModel::deleteSelected);
1337

1338
1339
1340
1341
    QAction *actOpen = new QAction(QIcon::fromTheme(QStringLiteral("window-new")), i18n("&Open"), this);
    connect(actOpen, &QAction::triggered, this, &FolderModel::openSelected);

    m_actionCollection.addAction(QStringLiteral("open"), actOpen);
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
    m_actionCollection.addAction(QStringLiteral("cut"), cut);
    m_actionCollection.addAction(QStringLiteral("undo"), undo);
    m_actionCollection.addAction(QStringLiteral("copy"), copy);
    m_actionCollection.addAction(QStringLiteral("paste"), paste);
    m_actionCollection.addAction(QStringLiteral("pasteto"), pasteTo);
    m_actionCollection.addAction(QStringLiteral("reload"), reload);
    m_actionCollection.addAction(QStringLiteral("refresh"), refresh);
    m_actionCollection.addAction(QStringLiteral("rename"), rename);
    m_actionCollection.addAction(QStringLiteral("trash"), trash);
    m_actionCollection.addAction(QStringLiteral("del"), del);
1352
    m_actionCollection.addAction(QStringLiteral("restoreFromTrash"), restoreFromTrash);
1353
    m_actionCollection.addAction(QStringLiteral("emptyTrash"), emptyTrash);
1354

1355
    m_newMenu = new KNewFileMenu(&m_actionCollection, QStringLiteral("newMenu"), QApplication::desktop());
1356
    m_newMenu->setModal(false);
1357
1358

    m_copyToMenu = new KFileCopyToMenu(Q_NULLPTR);
1359
}
1360

1361
1362
1363
QAction* FolderModel::action(const QString &name) const
{
    return m_actionCollection.action(name);
1364
1365
}

1366
QObject* FolderModel::newMenu() const
1367
{
1368
1369
    return m_newMenu->menu();
}
1370

1371
1372
1373
1374
1375
void FolderModel::updateActions()
{
    if (m_newMenu) {
        m_newMenu->checkUpToDate();
        m_newMenu->setPopupFiles(m_dirModel->dirLister()->url());
1376
1377
    }

1378
1379
    const bool isTrash = (resolvedUrl().scheme() == QLatin1String("trash"));

1380
    QAction *emptyTrash = m_actionCollection.action(QStringLiteral("emptyTrash"));
1381

1382
    if (emptyTrash) {
1383
        if (isTrash) {
1384
            emptyTrash->setVisible(true);
1385
            KConfig trashConfig(QStringLiteral("trashrc"), KConfig::SimpleConfig);
1386
1387
1388
            emptyTrash->setEnabled(!trashConfig.group("Status").readEntry("Empty", true));
        } else {
            emptyTrash->setVisible(false);
1389
1390
1391
        }
    }

1392
1393
1394
1395
    if (QAction *restoreFromTrash = m_actionCollection.action(QStringLiteral("restoreFromTrash"))) {
        restoreFromTrash->setVisible(isTrash);
    }

1396
    QAction *paste = m_actionCollection.action(QStringLiteral("paste"));
1397
1398

    if (paste) {
Eike Hein's avatar
Eike Hein committed
1399
        bool enable = false;
1400

Eike Hein's avatar
Eike Hein committed
1401
        const QString pasteText = KIO::pasteActionText(QApplication::clipboard()->mimeData(),
1402
            &enable, rootItem());
Eike Hein's avatar
Eike Hein committed