1 /*
2     This file is part of the KDE libraries
3     SPDX-FileCopyrightText: 1999, 2000 Stephan Kulow <coolo@kde.org>
4     SPDX-FileCopyrightText: 1999, 2000, 2001, 2002, 2003 Carsten Pfeiffer <pfeiffer@kde.org>
5 
6     SPDX-License-Identifier: LGPL-2.0-or-later
7 */
8 
9 #include <config-kiofilewidgets.h>
10 #include <defaults-kfile.h> // ConfigGroup, DefaultShowHidden, DefaultDirsFirst, DefaultSortReversed
11 
12 #include "../pathhelpers_p.h"
13 #include "kdirmodel.h"
14 #include "kdiroperator.h"
15 #include "kdiroperatordetailview_p.h"
16 #include "kdiroperatoriconview_p.h"
17 #include "kdirsortfilterproxymodel.h"
18 #include "kfileitem.h"
19 #include "kfilemetapreview_p.h"
20 #include "knewfilemenu.h"
21 #include "kpreviewwidgetbase.h"
22 #include <KActionCollection>
23 #include <KConfigGroup>
24 #include <KFileItemActions>
25 #include <KFileItemListProperties>
26 #include <KIO/OpenFileManagerWindowJob>
27 #include <KIO/RenameFileDialog>
28 #include <KIconLoader>
29 #include <KJobWidgets>
30 #include <KLocalizedString>
31 #include <KMessageBox>
32 #include <KProtocolManager>
33 #include <KSharedConfig>
34 #include <KUrlMimeData>
35 #include <kfileitemdelegate.h>
36 #include <kfilepreviewgenerator.h>
37 #include <kio/copyjob.h>
38 #include <kio/deletejob.h>
39 #include <kio/jobuidelegate.h>
40 #include <kio/previewjob.h>
41 #include <kpropertiesdialog.h>
42 #include <widgetsaskuseractionhandler.h>
43 
44 #include <QActionGroup>
45 #include <QApplication>
46 #include <QDebug>
47 #include <QHeaderView>
48 #include <QListView>
49 #include <QMenu>
50 #include <QMimeDatabase>
51 #include <QProgressBar>
52 #include <QRegularExpression>
53 #include <QScrollBar>
54 #include <QSplitter>
55 #include <QTimer>
56 #include <QWheelEvent>
57 
58 #include <memory>
59 
60 template class QHash<QString, KFileItem>;
61 
62 // QDir::SortByMask is not only undocumented, it also omits QDir::Type which  is another
63 // sorting mode.
64 static const int QDirSortMask = QDir::SortByMask | QDir::Type;
65 
66 class KDirOperatorPrivate
67 {
68 public:
KDirOperatorPrivate(KDirOperator * qq)69     explicit KDirOperatorPrivate(KDirOperator *qq)
70         : q(qq)
71     {
72         KConfigGroup cg(KSharedConfig::openConfig(), "SmallIcons");
73         m_iconSize = cg.readEntry("Size", static_cast<int>(KIconLoader::SizeSmall));
74     }
75 
76     ~KDirOperatorPrivate();
77 
78     enum InlinePreviewState {
79         ForcedToFalse = 0,
80         ForcedToTrue,
81         NotForced,
82     };
83 
84     // private methods
85     bool checkPreviewInternal() const;
86     bool openUrl(const QUrl &url, KDirLister::OpenUrlFlags flags = KDirLister::NoFlags);
87     int sortColumn() const;
88     Qt::SortOrder sortOrder() const;
89     void updateSorting(QDir::SortFlags sort);
90     KIO::WidgetsAskUserActionHandler *askUserHandler();
91 
92     bool isReadable(const QUrl &url);
93     bool isSchemeSupported(const QString &scheme) const;
94 
95     QPoint progressBarPos() const;
96 
97     KFile::FileView allViews();
98 
99     QMetaObject::Connection m_connection;
100 
101     // A pair to store zoom settings for view kinds
102     struct ZoomSettingsForView {
103         QString name;
104         int defaultValue;
105     };
106 
107     // private slots
108     void slotDetailedView();
109     void slotSimpleView();
110     void slotTreeView();
111     void slotDetailedTreeView();
112     void slotIconsView();
113     void slotCompactView();
114     void slotDetailsView();
115     void slotToggleHidden(bool);
116     void slotToggleAllowExpansion(bool);
117     void togglePreview(bool);
118     void toggleInlinePreviews(bool);
119     void slotOpenFileManager();
120     void slotSortByName();
121     void slotSortBySize();
122     void slotSortByDate();
123     void slotSortByType();
124     void slotSortReversed(bool doReverse);
125     void slotToggleDirsFirst();
126     void slotToggleIconsView();
127     void slotToggleCompactView();
128     void slotToggleDetailsView();
129     void slotToggleIgnoreCase();
130     void slotStarted();
131     void slotProgress(int);
132     void slotShowProgress();
133     void slotIOFinished();
134     void slotCanceled();
135     void slotRedirected(const QUrl &);
136     void slotProperties();
137     void slotActivated(const QModelIndex &);
138     void slotSelectionChanged();
139     void openContextMenu(const QPoint &);
140     void triggerPreview(const QModelIndex &);
141     void showPreview();
142     void slotSplitterMoved(int, int);
143     void assureVisibleSelection();
144     void synchronizeSortingState(int, Qt::SortOrder);
145     void slotChangeDecorationPosition();
146     void slotExpandToUrl(const QModelIndex &);
147     void slotItemsChanged();
148     void slotDirectoryCreated(const QUrl &);
149     void slotAskUserDeleteResult(bool allowDelete, const QList<QUrl> &urls, KIO::AskUserActionInterface::DeletionType deletionType, QWidget *parent);
150 
151     int iconSizeForViewType(QAbstractItemView *itemView) const;
152     void writeIconZoomSettingsIfNeeded();
153     ZoomSettingsForView zoomSettingsForView() const;
154 
155     QList<QAction *> insertOpenWithActions();
156 
157     // private members
158     KDirOperator *const q;
159     QStack<QUrl *> m_backStack; ///< Contains all URLs you can reach with the back button.
160     QStack<QUrl *> m_forwardStack; ///< Contains all URLs you can reach with the forward button.
161 
162     QModelIndex m_lastHoveredIndex;
163 
164     KDirLister *m_dirLister = nullptr;
165     QUrl m_currUrl;
166 
167     KCompletion m_completion;
168     KCompletion m_dirCompletion;
169     QDir::SortFlags m_sorting;
170     QStyleOptionViewItem::Position m_decorationPosition = QStyleOptionViewItem::Left;
171 
172     QSplitter *m_splitter = nullptr;
173 
174     QAbstractItemView *m_itemView = nullptr;
175     KDirModel *m_dirModel = nullptr;
176     KDirSortFilterProxyModel *m_proxyModel = nullptr;
177 
178     KFileItemList m_pendingMimeTypes;
179 
180     // the enum KFile::FileView as an int
181     int m_viewKind;
182     int m_defaultView;
183 
184     KFile::Modes m_mode;
185     QProgressBar *m_progressBar = nullptr;
186 
187     KPreviewWidgetBase *m_preview = nullptr;
188     QUrl m_previewUrl;
189     int m_previewWidth = 0;
190 
191     bool m_completeListDirty = false;
192     bool m_followNewDirectories = true;
193     bool m_followSelectedDirectories = true;
194     bool m_onlyDoubleClickSelectsFiles = !qApp->style()->styleHint(QStyle::SH_ItemView_ActivateItemOnSingleClick);
195 
196     QUrl m_lastUrl; // used for highlighting a directory on back/cdUp
197 
198     QTimer *m_progressDelayTimer = nullptr;
199     KActionMenu *m_actionMenu = nullptr;
200     KActionCollection *m_actionCollection = nullptr;
201     KNewFileMenu *m_newFileMenu = nullptr;
202     KConfigGroup *m_configGroup = nullptr;
203     KFilePreviewGenerator *m_previewGenerator = nullptr;
204     KActionMenu *m_decorationMenu = nullptr;
205     KToggleAction *m_leftAction = nullptr;
206     KFileItemActions *m_itemActions = nullptr;
207 
208     int m_dropOptions = 0;
209     int m_iconSize = 0;
210     InlinePreviewState m_inlinePreviewState = NotForced;
211     bool m_dirHighlighting = true;
212     bool m_showPreviews = false;
213     bool m_shouldFetchForItems = false;
214     bool m_isSaving = false;
215     bool m_showOpenWithActions = false;
216 
217     QList<QUrl> m_itemsToBeSetAsCurrent;
218     QStringList m_supportedSchemes;
219 
220     std::unique_ptr<KIO::WidgetsAskUserActionHandler> m_askUserHandler;
221 };
222 
~KDirOperatorPrivate()223 KDirOperatorPrivate::~KDirOperatorPrivate()
224 {
225     if (m_itemView) {
226         // fix libc++ crash: its unique_ptr implementation has already set 'd' to null
227         // and the event filter will get a QEvent::Leave event if we don't remove it.
228         m_itemView->removeEventFilter(q);
229         m_itemView->viewport()->removeEventFilter(q);
230     }
231 
232     delete m_itemView;
233     m_itemView = nullptr;
234 
235     // TODO:
236     // if (configGroup) {
237     //     itemView->writeConfig(configGroup);
238     // }
239 
240     qDeleteAll(m_backStack);
241     qDeleteAll(m_forwardStack);
242 
243     // The parent KDirOperator will delete these
244     m_preview = nullptr;
245     m_proxyModel = nullptr;
246     m_dirModel = nullptr;
247     m_progressDelayTimer = nullptr;
248 
249     m_dirLister = nullptr; // deleted by KDirModel
250 
251     delete m_configGroup;
252     m_configGroup = nullptr;
253 }
254 
progressBarPos() const255 QPoint KDirOperatorPrivate::progressBarPos() const
256 {
257     const int frameWidth = q->style()->pixelMetric(QStyle::PM_DefaultFrameWidth);
258     return QPoint(frameWidth, q->height() - m_progressBar->height() - frameWidth);
259 }
260 
KDirOperator(const QUrl & _url,QWidget * parent)261 KDirOperator::KDirOperator(const QUrl &_url, QWidget *parent)
262     : QWidget(parent)
263     , d(new KDirOperatorPrivate(this))
264 {
265     d->m_splitter = new QSplitter(this);
266     d->m_splitter->setChildrenCollapsible(false);
267     connect(d->m_splitter, &QSplitter::splitterMoved, this, [this](int pos, int index) {
268         d->slotSplitterMoved(pos, index);
269     });
270 
271     d->m_preview = nullptr;
272 
273     d->m_mode = KFile::File;
274     d->m_viewKind = KFile::Simple;
275 
276     if (_url.isEmpty()) { // no dir specified -> current dir
277         QString strPath = QDir::currentPath();
278         strPath.append(QLatin1Char('/'));
279         d->m_currUrl = QUrl::fromLocalFile(strPath);
280     } else {
281         d->m_currUrl = _url;
282         if (d->m_currUrl.scheme().isEmpty()) {
283             d->m_currUrl.setScheme(QStringLiteral("file"));
284         }
285 
286         QString path = d->m_currUrl.path();
287         if (!path.endsWith(QLatin1Char('/'))) {
288             path.append(QLatin1Char('/')); // make sure we have a trailing slash!
289         }
290         d->m_currUrl.setPath(path);
291     }
292 
293     // We set the direction of this widget to LTR, since even on RTL desktops
294     // viewing directory listings in RTL mode makes people's head explode.
295     // Is this the correct place? Maybe it should be in some lower level widgets...?
296     setLayoutDirection(Qt::LeftToRight);
297     setDirLister(new KDirLister());
298 
299     connect(&d->m_completion, &KCompletion::match, this, &KDirOperator::slotCompletionMatch);
300 
301     d->m_progressBar = new QProgressBar(this);
302     d->m_progressBar->setObjectName(QStringLiteral("d->m_progressBar"));
303     d->m_progressBar->adjustSize();
304     const int frameWidth = style()->pixelMetric(QStyle::PM_DefaultFrameWidth);
305     d->m_progressBar->move(frameWidth, height() - d->m_progressBar->height() - frameWidth);
306 
307     d->m_progressDelayTimer = new QTimer(this);
308     d->m_progressDelayTimer->setObjectName(QStringLiteral("d->m_progressBar delay timer"));
309     connect(d->m_progressDelayTimer, &QTimer::timeout, this, [this]() {
310         d->slotShowProgress();
311     });
312 
313     d->m_completeListDirty = false;
314 
315     // action stuff
316     setupActions();
317     setupMenu();
318 
319     d->m_sorting = QDir::NoSort; // so updateSorting() doesn't think nothing has changed
320     d->updateSorting(QDir::Name | QDir::DirsFirst);
321 
322     setFocusPolicy(Qt::WheelFocus);
323     setAcceptDrops(true);
324 }
325 
~KDirOperator()326 KDirOperator::~KDirOperator()
327 {
328     resetCursor();
329     disconnect(d->m_dirLister, nullptr, this, nullptr);
330 }
331 
setSorting(QDir::SortFlags spec)332 void KDirOperator::setSorting(QDir::SortFlags spec)
333 {
334     d->updateSorting(spec);
335 }
336 
sorting() const337 QDir::SortFlags KDirOperator::sorting() const
338 {
339     return d->m_sorting;
340 }
341 
isRoot() const342 bool KDirOperator::isRoot() const
343 {
344 #ifdef Q_OS_WIN
345     if (url().isLocalFile()) {
346         const QString path = url().toLocalFile();
347         if (path.length() == 3) {
348             return (path[0].isLetter() && path[1] == QLatin1Char(':') && path[2] == QLatin1Char('/'));
349         }
350         return false;
351     } else
352 #endif
353         return url().path() == QLatin1String("/");
354 }
355 
dirLister() const356 KDirLister *KDirOperator::dirLister() const
357 {
358     return d->m_dirLister;
359 }
360 
resetCursor()361 void KDirOperator::resetCursor()
362 {
363     if (qApp) {
364         QApplication::restoreOverrideCursor();
365     }
366     d->m_progressBar->hide();
367 }
368 
sortByName()369 void KDirOperator::sortByName()
370 {
371     d->updateSorting((d->m_sorting & ~QDirSortMask) | QDir::Name);
372 }
373 
sortBySize()374 void KDirOperator::sortBySize()
375 {
376     d->updateSorting((d->m_sorting & ~QDirSortMask) | QDir::Size);
377 }
378 
sortByDate()379 void KDirOperator::sortByDate()
380 {
381     d->updateSorting((d->m_sorting & ~QDirSortMask) | QDir::Time);
382 }
383 
sortByType()384 void KDirOperator::sortByType()
385 {
386     d->updateSorting((d->m_sorting & ~QDirSortMask) | QDir::Type);
387 }
388 
sortReversed()389 void KDirOperator::sortReversed()
390 {
391     // toggle it, hence the inversion of current state
392     d->slotSortReversed(!(d->m_sorting & QDir::Reversed));
393 }
394 
toggleDirsFirst()395 void KDirOperator::toggleDirsFirst()
396 {
397     d->slotToggleDirsFirst();
398 }
399 
toggleIgnoreCase()400 void KDirOperator::toggleIgnoreCase()
401 {
402     if (d->m_proxyModel != nullptr) {
403         Qt::CaseSensitivity cs = d->m_proxyModel->sortCaseSensitivity();
404         cs = (cs == Qt::CaseSensitive) ? Qt::CaseInsensitive : Qt::CaseSensitive;
405         d->m_proxyModel->setSortCaseSensitivity(cs);
406     }
407 }
408 
updateSelectionDependentActions()409 void KDirOperator::updateSelectionDependentActions()
410 {
411     const bool hasSelection = (d->m_itemView != nullptr) && d->m_itemView->selectionModel()->hasSelection();
412     d->m_actionCollection->action(QStringLiteral("rename"))->setEnabled(hasSelection);
413     d->m_actionCollection->action(QStringLiteral("trash"))->setEnabled(hasSelection);
414     d->m_actionCollection->action(QStringLiteral("delete"))->setEnabled(hasSelection);
415     d->m_actionCollection->action(QStringLiteral("properties"))->setEnabled(hasSelection);
416 }
417 
setPreviewWidget(KPreviewWidgetBase * w)418 void KDirOperator::setPreviewWidget(KPreviewWidgetBase *w)
419 {
420     const bool showPreview = (w != nullptr);
421     if (showPreview) {
422         d->m_viewKind = (d->m_viewKind | KFile::PreviewContents);
423     } else {
424         d->m_viewKind = (d->m_viewKind & ~KFile::PreviewContents);
425     }
426 
427     delete d->m_preview;
428     d->m_preview = w;
429 
430     if (w) {
431         d->m_splitter->addWidget(w);
432     }
433 
434     KToggleAction *previewAction = static_cast<KToggleAction *>(d->m_actionCollection->action(QStringLiteral("preview")));
435     previewAction->setEnabled(showPreview);
436     previewAction->setChecked(showPreview);
437     setView(static_cast<KFile::FileView>(d->m_viewKind));
438 }
439 
selectedItems() const440 KFileItemList KDirOperator::selectedItems() const
441 {
442     KFileItemList itemList;
443     if (d->m_itemView == nullptr) {
444         return itemList;
445     }
446 
447     const QItemSelection selection = d->m_proxyModel->mapSelectionToSource(d->m_itemView->selectionModel()->selection());
448 
449     const QModelIndexList indexList = selection.indexes();
450     for (const QModelIndex &index : indexList) {
451         KFileItem item = d->m_dirModel->itemForIndex(index);
452         if (!item.isNull()) {
453             itemList.append(item);
454         }
455     }
456 
457     return itemList;
458 }
459 
isSelected(const KFileItem & item) const460 bool KDirOperator::isSelected(const KFileItem &item) const
461 {
462     if ((item.isNull()) || (d->m_itemView == nullptr)) {
463         return false;
464     }
465 
466     const QModelIndex dirIndex = d->m_dirModel->indexForItem(item);
467     const QModelIndex proxyIndex = d->m_proxyModel->mapFromSource(dirIndex);
468     return d->m_itemView->selectionModel()->isSelected(proxyIndex);
469 }
470 
numDirs() const471 int KDirOperator::numDirs() const
472 {
473     return (d->m_dirLister == nullptr) ? 0 : d->m_dirLister->directories().count();
474 }
475 
numFiles() const476 int KDirOperator::numFiles() const
477 {
478     return (d->m_dirLister == nullptr) ? 0 : d->m_dirLister->items().count() - numDirs();
479 }
480 
completionObject() const481 KCompletion *KDirOperator::completionObject() const
482 {
483     return const_cast<KCompletion *>(&d->m_completion);
484 }
485 
dirCompletionObject() const486 KCompletion *KDirOperator::dirCompletionObject() const
487 {
488     return const_cast<KCompletion *>(&d->m_dirCompletion);
489 }
490 
actionCollection() const491 KActionCollection *KDirOperator::actionCollection() const
492 {
493     return d->m_actionCollection;
494 }
495 
allViews()496 KFile::FileView KDirOperatorPrivate::allViews()
497 {
498     return static_cast<KFile::FileView>(KFile::Simple | KFile::Detail | KFile::Tree | KFile::DetailTree);
499 }
500 
slotDetailedView()501 void KDirOperatorPrivate::slotDetailedView()
502 {
503     // save old zoom settings
504     writeIconZoomSettingsIfNeeded();
505 
506     KFile::FileView view = static_cast<KFile::FileView>((m_viewKind & ~allViews()) | KFile::Detail);
507     q->setView(view);
508 }
509 
slotSimpleView()510 void KDirOperatorPrivate::slotSimpleView()
511 {
512     // save old zoom settings
513     writeIconZoomSettingsIfNeeded();
514 
515     KFile::FileView view = static_cast<KFile::FileView>((m_viewKind & ~allViews()) | KFile::Simple);
516     q->setView(view);
517 }
518 
slotTreeView()519 void KDirOperatorPrivate::slotTreeView()
520 {
521     // save old zoom settings
522     writeIconZoomSettingsIfNeeded();
523 
524     KFile::FileView view = static_cast<KFile::FileView>((m_viewKind & ~allViews()) | KFile::Tree);
525     q->setView(view);
526 }
527 
slotDetailedTreeView()528 void KDirOperatorPrivate::slotDetailedTreeView()
529 {
530     // save old zoom settings
531     writeIconZoomSettingsIfNeeded();
532 
533     KFile::FileView view = static_cast<KFile::FileView>((m_viewKind & ~allViews()) | KFile::DetailTree);
534     q->setView(view);
535 }
536 
slotToggleAllowExpansion(bool allow)537 void KDirOperatorPrivate::slotToggleAllowExpansion(bool allow)
538 {
539     KFile::FileView view = KFile::Detail;
540     if (allow) {
541         view = KFile::DetailTree;
542     }
543     q->setView(view);
544 }
545 
slotToggleHidden(bool show)546 void KDirOperatorPrivate::slotToggleHidden(bool show)
547 {
548     m_dirLister->setShowingDotFiles(show);
549     q->updateDir();
550     assureVisibleSelection();
551 }
552 
togglePreview(bool on)553 void KDirOperatorPrivate::togglePreview(bool on)
554 {
555     if (on) {
556         m_viewKind |= KFile::PreviewContents;
557         if (m_preview == nullptr) {
558             m_preview = new KFileMetaPreview(q);
559             m_actionCollection->action(QStringLiteral("preview"))->setChecked(true);
560             m_splitter->addWidget(m_preview);
561         }
562 
563         m_preview->show();
564 
565         QMetaObject::invokeMethod(
566             q,
567             [this]() {
568                 assureVisibleSelection();
569             },
570             Qt::QueuedConnection);
571         if (m_itemView != nullptr) {
572             const QModelIndex index = m_itemView->selectionModel()->currentIndex();
573             if (index.isValid()) {
574                 triggerPreview(index);
575             }
576         }
577     } else if (m_preview != nullptr) {
578         m_viewKind = m_viewKind & ~KFile::PreviewContents;
579         m_preview->hide();
580     }
581 }
582 
toggleInlinePreviews(bool show)583 void KDirOperatorPrivate::toggleInlinePreviews(bool show)
584 {
585     if (m_showPreviews == show) {
586         return;
587     }
588 
589     m_showPreviews = show;
590 
591     if (!m_previewGenerator) {
592         return;
593     }
594 
595     m_previewGenerator->setPreviewShown(show);
596 }
597 
slotOpenFileManager()598 void KDirOperatorPrivate::slotOpenFileManager()
599 {
600     const KFileItemList list = q->selectedItems();
601     if (list.isEmpty()) {
602         KIO::highlightInFileManager({m_currUrl.adjusted(QUrl::StripTrailingSlash)});
603     } else {
604         KIO::highlightInFileManager(list.urlList());
605     }
606 }
607 
slotSortByName()608 void KDirOperatorPrivate::slotSortByName()
609 {
610     q->sortByName();
611 }
612 
slotSortBySize()613 void KDirOperatorPrivate::slotSortBySize()
614 {
615     q->sortBySize();
616 }
617 
slotSortByDate()618 void KDirOperatorPrivate::slotSortByDate()
619 {
620     q->sortByDate();
621 }
622 
slotSortByType()623 void KDirOperatorPrivate::slotSortByType()
624 {
625     q->sortByType();
626 }
627 
slotSortReversed(bool doReverse)628 void KDirOperatorPrivate::slotSortReversed(bool doReverse)
629 {
630     QDir::SortFlags s = m_sorting & ~QDir::Reversed;
631     if (doReverse) {
632         s |= QDir::Reversed;
633     }
634     updateSorting(s);
635 }
636 
slotToggleDirsFirst()637 void KDirOperatorPrivate::slotToggleDirsFirst()
638 {
639     QDir::SortFlags s = (m_sorting ^ QDir::DirsFirst);
640     updateSorting(s);
641 }
642 
slotIconsView()643 void KDirOperatorPrivate::slotIconsView()
644 {
645     // save old zoom settings
646     writeIconZoomSettingsIfNeeded();
647 
648     // Put the icons on top
649     m_actionCollection->action(QStringLiteral("decorationAtTop"))->setChecked(true);
650     m_decorationPosition = QStyleOptionViewItem::Top;
651 
652     // Switch to simple view
653     KFile::FileView fileView = static_cast<KFile::FileView>((m_viewKind & ~allViews()) | KFile::Simple);
654     q->setView(fileView);
655 }
656 
slotCompactView()657 void KDirOperatorPrivate::slotCompactView()
658 {
659     // save old zoom settings
660     writeIconZoomSettingsIfNeeded();
661 
662     // Put the icons on the side
663     m_actionCollection->action(QStringLiteral("decorationAtLeft"))->setChecked(true);
664     m_decorationPosition = QStyleOptionViewItem::Left;
665 
666     // Switch to simple view
667     KFile::FileView fileView = static_cast<KFile::FileView>((m_viewKind & ~allViews()) | KFile::Simple);
668     q->setView(fileView);
669 }
670 
slotDetailsView()671 void KDirOperatorPrivate::slotDetailsView()
672 {
673     // save old zoom settings
674     writeIconZoomSettingsIfNeeded();
675 
676     KFile::FileView view;
677     if (m_actionCollection->action(QStringLiteral("allow expansion"))->isChecked()) {
678         view = static_cast<KFile::FileView>((m_viewKind & ~allViews()) | KFile::DetailTree);
679     } else {
680         view = static_cast<KFile::FileView>((m_viewKind & ~allViews()) | KFile::Detail);
681     }
682     q->setView(view);
683 }
684 
slotToggleIgnoreCase()685 void KDirOperatorPrivate::slotToggleIgnoreCase()
686 {
687     // TODO: port to Qt4's QAbstractItemView
688     /*if ( !d->fileView )
689       return;
690 
691     QDir::SortFlags sorting = d->fileView->sorting();
692     if ( !KFile::isSortCaseInsensitive( sorting ) )
693         d->fileView->setSorting( sorting | QDir::IgnoreCase );
694     else
695         d->fileView->setSorting( sorting & ~QDir::IgnoreCase );
696     d->sorting = d->fileView->sorting();*/
697 }
698 
mkdir()699 void KDirOperator::mkdir()
700 {
701     d->m_newFileMenu->setPopupFiles(QList<QUrl>{url()});
702     d->m_newFileMenu->setViewShowsHiddenFiles(showHiddenFiles());
703     d->m_newFileMenu->createDirectory();
704 }
705 
706 #if KIOFILEWIDGETS_BUILD_DEPRECATED_SINCE(5, 78)
mkdir(const QString & directory,bool enterDirectory)707 bool KDirOperator::mkdir(const QString &directory, bool enterDirectory)
708 {
709     // Creates "directory", relative to the current directory (d->currUrl).
710     // The given path may contain any number directories, existent or not.
711     // They will all be created, if possible.
712 
713     // TODO: very similar to KDirSelectDialog::Private::slotMkdir
714 
715     bool writeOk = false;
716     bool exists = false;
717     QUrl folderurl(d->m_currUrl);
718 
719     const QStringList dirs = directory.split(QLatin1Char('/'), Qt::SkipEmptyParts);
720     QStringList::ConstIterator it = dirs.begin();
721 
722     for (; it != dirs.end(); ++it) {
723         folderurl.setPath(concatPaths(folderurl.path(), *it));
724         if (folderurl.isLocalFile()) {
725             exists = QFile::exists(folderurl.toLocalFile());
726         } else {
727             KIO::StatJob *job = KIO::stat(folderurl);
728             KJobWidgets::setWindow(job, this);
729             job->setDetails(KIO::StatNoDetails); // We only want to know if it exists
730             job->setSide(KIO::StatJob::DestinationSide);
731             exists = job->exec();
732         }
733 
734         if (!exists) {
735             KIO::Job *job = KIO::mkdir(folderurl);
736             KJobWidgets::setWindow(job, this);
737             writeOk = job->exec();
738         }
739     }
740 
741     if (exists) { // url was already existent
742         KMessageBox::sorry(d->m_itemView, i18n("A file or folder named %1 already exists.", folderurl.toDisplayString(QUrl::PreferLocalFile)));
743     } else if (!writeOk) {
744         KMessageBox::sorry(d->m_itemView,
745                            i18n("You do not have permission to "
746                                 "create that folder."));
747     } else if (enterDirectory) {
748         setUrl(folderurl, true);
749     }
750 
751     return writeOk;
752 }
753 #endif
754 
del(const KFileItemList & items,QWidget * parent,bool ask,bool showProgress)755 KIO::DeleteJob *KDirOperator::del(const KFileItemList &items, QWidget *parent, bool ask, bool showProgress)
756 {
757     if (items.isEmpty()) {
758         KMessageBox::information(parent, i18n("You did not select a file to delete."), i18n("Nothing to Delete"));
759         return nullptr;
760     }
761 
762     if (parent == nullptr) {
763         parent = this;
764     }
765 
766     const QList<QUrl> urls = items.urlList();
767 
768     bool doIt = !ask;
769     if (ask) {
770         KIO::JobUiDelegate uiDelegate;
771         uiDelegate.setWindow(parent);
772         doIt = uiDelegate.askDeleteConfirmation(urls, KIO::JobUiDelegate::Delete, KIO::JobUiDelegate::DefaultConfirmation);
773     }
774 
775     if (doIt) {
776         KIO::JobFlags flags = showProgress ? KIO::DefaultFlags : KIO::HideProgressInfo;
777         KIO::DeleteJob *job = KIO::del(urls, flags);
778         KJobWidgets::setWindow(job, this);
779         job->uiDelegate()->setAutoErrorHandlingEnabled(true);
780         return job;
781     }
782 
783     return nullptr;
784 }
785 
askUserHandler()786 KIO::WidgetsAskUserActionHandler *KDirOperatorPrivate::askUserHandler()
787 {
788     if (m_askUserHandler) {
789         return m_askUserHandler.get();
790     }
791 
792     m_askUserHandler.reset(new KIO::WidgetsAskUserActionHandler{});
793     QObject::connect(m_askUserHandler.get(),
794                      &KIO::WidgetsAskUserActionHandler::askUserDeleteResult,
795                      q,
796                      [this](bool allowDelete, const QList<QUrl> &urls, KIO::AskUserActionInterface::DeletionType deletionType, QWidget *parentWidget) {
797                          slotAskUserDeleteResult(allowDelete, urls, deletionType, parentWidget);
798                      });
799 
800     return m_askUserHandler.get();
801 }
802 
deleteSelected()803 void KDirOperator::deleteSelected()
804 {
805     const QList<QUrl> urls = selectedItems().urlList();
806     if (urls.isEmpty()) {
807         KMessageBox::information(this, i18n("You did not select a file to delete."), i18n("Nothing to Delete"));
808         return;
809     }
810 
811     d->askUserHandler()->askUserDelete(urls, KIO::AskUserActionInterface::Delete, KIO::AskUserActionInterface::DefaultConfirmation, this);
812 }
813 
trash(const KFileItemList & items,QWidget * parent,bool ask,bool showProgress)814 KIO::CopyJob *KDirOperator::trash(const KFileItemList &items, QWidget *parent, bool ask, bool showProgress)
815 {
816     if (items.isEmpty()) {
817         KMessageBox::information(parent, i18n("You did not select a file to trash."), i18n("Nothing to Trash"));
818         return nullptr;
819     }
820 
821     const QList<QUrl> urls = items.urlList();
822 
823     bool doIt = !ask;
824     if (ask) {
825         KIO::JobUiDelegate uiDelegate;
826         uiDelegate.setWindow(parent);
827         doIt = uiDelegate.askDeleteConfirmation(urls, KIO::JobUiDelegate::Trash, KIO::JobUiDelegate::DefaultConfirmation);
828     }
829 
830     if (doIt) {
831         KIO::JobFlags flags = showProgress ? KIO::DefaultFlags : KIO::HideProgressInfo;
832         KIO::CopyJob *job = KIO::trash(urls, flags);
833         KJobWidgets::setWindow(job, this);
834         job->uiDelegate()->setAutoErrorHandlingEnabled(true);
835         return job;
836     }
837 
838     return nullptr;
839 }
840 
previewGenerator() const841 KFilePreviewGenerator *KDirOperator::previewGenerator() const
842 {
843     return d->m_previewGenerator;
844 }
845 
setInlinePreviewShown(bool show)846 void KDirOperator::setInlinePreviewShown(bool show)
847 {
848     d->m_inlinePreviewState = show ? KDirOperatorPrivate::ForcedToTrue : KDirOperatorPrivate::ForcedToFalse;
849 }
850 
isInlinePreviewShown() const851 bool KDirOperator::isInlinePreviewShown() const
852 {
853     return d->m_showPreviews;
854 }
855 
856 #if KIOFILEWIDGETS_BUILD_DEPRECATED_SINCE(5, 76)
iconsZoom() const857 int KDirOperator::iconsZoom() const
858 {
859     const int stepSize = (KIconLoader::SizeEnormous - KIconLoader::SizeSmall) / 100;
860     const int zoom = (d->m_iconSize / stepSize) - KIconLoader::SizeSmall;
861     return zoom;
862 }
863 #endif
864 
iconSize() const865 int KDirOperator::iconSize() const
866 {
867     return d->m_iconSize;
868 }
869 
setIsSaving(bool isSaving)870 void KDirOperator::setIsSaving(bool isSaving)
871 {
872     d->m_isSaving = isSaving;
873 }
874 
isSaving() const875 bool KDirOperator::isSaving() const
876 {
877     return d->m_isSaving;
878 }
879 
renameSelected()880 void KDirOperator::renameSelected()
881 {
882     if (d->m_itemView == nullptr) {
883         return;
884     }
885 
886     const KFileItemList items = selectedItems();
887     if (items.isEmpty()) {
888         return;
889     }
890 
891     KIO::RenameFileDialog *dialog = new KIO::RenameFileDialog(items, this);
892     connect(dialog, &KIO::RenameFileDialog::renamingFinished, this, [this]() {
893         d->assureVisibleSelection();
894     });
895 
896     dialog->open();
897 }
898 
trashSelected()899 void KDirOperator::trashSelected()
900 {
901     if (d->m_itemView == nullptr) {
902         return;
903     }
904 
905     if (QApplication::keyboardModifiers() & Qt::ShiftModifier) {
906         deleteSelected();
907         return;
908     }
909 
910     const QList<QUrl> urls = selectedItems().urlList();
911     if (urls.isEmpty()) {
912         KMessageBox::information(this, i18n("You did not select a file to trash."), i18n("Nothing to Trash"));
913         return;
914     }
915 
916     d->askUserHandler()->askUserDelete(urls, KIO::AskUserActionInterface::Trash, KIO::AskUserActionInterface::DefaultConfirmation, this);
917 }
918 
slotAskUserDeleteResult(bool allowDelete,const QList<QUrl> & urls,KIO::AskUserActionInterface::DeletionType deletionType,QWidget * parentWidget)919 void KDirOperatorPrivate::slotAskUserDeleteResult(bool allowDelete,
920                                                   const QList<QUrl> &urls,
921                                                   KIO::AskUserActionInterface::DeletionType deletionType,
922                                                   QWidget *parentWidget)
923 {
924     if (parentWidget != q || !allowDelete) {
925         return;
926     }
927 
928     KIO::Job *job = nullptr;
929     if (deletionType == KIO::AskUserActionInterface::Trash) {
930         job = KIO::trash(urls);
931     } else if (deletionType == KIO::AskUserActionInterface::Delete) {
932         job = KIO::del(urls, KIO::DefaultFlags);
933     }
934 
935     if (!job) {
936         return;
937     }
938     KJobWidgets::setWindow(job, q);
939     job->uiDelegate()->setAutoErrorHandlingEnabled(true);
940 }
941 
942 #if KIOFILEWIDGETS_BUILD_DEPRECATED_SINCE(5, 76)
setIconsZoom(int _value)943 void KDirOperator::setIconsZoom(int _value)
944 {
945     int value = _value;
946     value = qMin(100, value);
947     value = qMax(0, value);
948     const int stepSize = (KIconLoader::SizeEnormous - KIconLoader::SizeSmall) / 100;
949     const int val = (value * stepSize) + KIconLoader::SizeSmall;
950     setIconSize(val);
951 }
952 #endif
953 
setIconSize(int value)954 void KDirOperator::setIconSize(int value)
955 {
956     if (d->m_iconSize == value) {
957         return;
958     }
959 
960     int size = value;
961     size = qMin(static_cast<int>(KIconLoader::SizeEnormous), size);
962     size = qMax(static_cast<int>(KIconLoader::SizeSmall), size);
963 
964     d->m_iconSize = size;
965 
966     if (!d->m_previewGenerator) {
967         return;
968     }
969 
970     d->m_itemView->setIconSize(QSize(size, size));
971     d->m_previewGenerator->updateIcons();
972 
973     Q_EMIT currentIconSizeChanged(size);
974 }
975 
close()976 void KDirOperator::close()
977 {
978     resetCursor();
979     d->m_pendingMimeTypes.clear();
980     d->m_completion.clear();
981     d->m_dirCompletion.clear();
982     d->m_completeListDirty = true;
983     d->m_dirLister->stop();
984 }
985 
setUrl(const QUrl & _newurl,bool clearforward)986 void KDirOperator::setUrl(const QUrl &_newurl, bool clearforward)
987 {
988     QUrl newurl;
989 
990     if (!_newurl.isValid()) {
991         newurl = QUrl::fromLocalFile(QDir::homePath());
992     } else {
993         newurl = _newurl.adjusted(QUrl::NormalizePathSegments);
994     }
995 
996     if (!newurl.path().isEmpty() && !newurl.path().endsWith(QLatin1Char('/'))) {
997         newurl.setPath(newurl.path() + QLatin1Char('/'));
998     }
999 
1000     // already set
1001     if (newurl.matches(d->m_currUrl, QUrl::StripTrailingSlash)) {
1002         return;
1003     }
1004 
1005     if (!d->isSchemeSupported(newurl.scheme())) {
1006         return;
1007     }
1008 
1009     if (!d->isReadable(newurl)) {
1010         // maybe newurl is a file? check its parent directory
1011         newurl = newurl.adjusted(QUrl::RemoveFilename | QUrl::StripTrailingSlash);
1012         if (newurl.matches(d->m_currUrl, QUrl::StripTrailingSlash)) {
1013             return; // parent is current dir, nothing to do (fixes #173454, too)
1014         }
1015         KIO::StatJob *job = KIO::stat(newurl);
1016         KJobWidgets::setWindow(job, this);
1017         bool res = job->exec();
1018 
1019         KIO::UDSEntry entry = job->statResult();
1020         KFileItem i(entry, newurl);
1021         if ((!res || !d->isReadable(newurl)) && i.isDir()) {
1022             resetCursor();
1023             KMessageBox::error(d->m_itemView,
1024                                i18n("The specified folder does not exist "
1025                                     "or was not readable."));
1026             return;
1027         } else if (!i.isDir()) {
1028             return;
1029         }
1030     }
1031 
1032     if (clearforward) {
1033         // autodelete should remove this one
1034         d->m_backStack.push(new QUrl(d->m_currUrl));
1035         qDeleteAll(d->m_forwardStack);
1036         d->m_forwardStack.clear();
1037     }
1038 
1039     d->m_currUrl = newurl;
1040 
1041     pathChanged();
1042     Q_EMIT urlEntered(newurl);
1043 
1044     // enable/disable actions
1045     QAction *forwardAction = d->m_actionCollection->action(QStringLiteral("forward"));
1046     forwardAction->setEnabled(!d->m_forwardStack.isEmpty());
1047 
1048     QAction *backAction = d->m_actionCollection->action(QStringLiteral("back"));
1049     backAction->setEnabled(!d->m_backStack.isEmpty());
1050 
1051     QAction *upAction = d->m_actionCollection->action(QStringLiteral("up"));
1052     upAction->setEnabled(!isRoot());
1053 
1054     d->openUrl(newurl);
1055 }
1056 
updateDir()1057 void KDirOperator::updateDir()
1058 {
1059     QApplication::setOverrideCursor(Qt::WaitCursor);
1060     d->m_dirLister->emitChanges();
1061     QApplication::restoreOverrideCursor();
1062 }
1063 
rereadDir()1064 void KDirOperator::rereadDir()
1065 {
1066     pathChanged();
1067     d->openUrl(d->m_currUrl, KDirLister::Reload);
1068 }
1069 
isSchemeSupported(const QString & scheme) const1070 bool KDirOperatorPrivate::isSchemeSupported(const QString &scheme) const
1071 {
1072     return m_supportedSchemes.isEmpty() || m_supportedSchemes.contains(scheme);
1073 }
1074 
openUrl(const QUrl & url,KDirLister::OpenUrlFlags flags)1075 bool KDirOperatorPrivate::openUrl(const QUrl &url, KDirLister::OpenUrlFlags flags)
1076 {
1077     const bool result = KProtocolManager::supportsListing(url) && isSchemeSupported(url.scheme()) && m_dirLister->openUrl(url, flags);
1078     if (!result) { // in that case, neither completed() nor canceled() will be emitted by KDL
1079         slotCanceled();
1080     }
1081 
1082     return result;
1083 }
1084 
sortColumn() const1085 int KDirOperatorPrivate::sortColumn() const
1086 {
1087     int column = KDirModel::Name;
1088     if (KFile::isSortByDate(m_sorting)) {
1089         column = KDirModel::ModifiedTime;
1090     } else if (KFile::isSortBySize(m_sorting)) {
1091         column = KDirModel::Size;
1092     } else if (KFile::isSortByType(m_sorting)) {
1093         column = KDirModel::Type;
1094     } else {
1095         Q_ASSERT(KFile::isSortByName(m_sorting));
1096     }
1097 
1098     return column;
1099 }
1100 
sortOrder() const1101 Qt::SortOrder KDirOperatorPrivate::sortOrder() const
1102 {
1103     return (m_sorting & QDir::Reversed) ? Qt::DescendingOrder : Qt::AscendingOrder;
1104 }
1105 
updateSorting(QDir::SortFlags sort)1106 void KDirOperatorPrivate::updateSorting(QDir::SortFlags sort)
1107 {
1108     // qDebug() << "changing sort flags from"  << m_sorting << "to" << sort;
1109     if (sort == m_sorting) {
1110         return;
1111     }
1112 
1113     if ((m_sorting ^ sort) & QDir::DirsFirst) {
1114         // The "Folders First" setting has been changed.
1115         // We need to make sure that the files and folders are really re-sorted.
1116         // Without the following intermediate "fake resorting",
1117         // QSortFilterProxyModel::sort(int column, Qt::SortOrder order)
1118         // would do nothing because neither the column nor the sort order have been changed.
1119         Qt::SortOrder tmpSortOrder = (sortOrder() == Qt::AscendingOrder ? Qt::DescendingOrder : Qt::AscendingOrder);
1120         m_proxyModel->sort(sortOrder(), tmpSortOrder);
1121         m_proxyModel->setSortFoldersFirst(sort & QDir::DirsFirst);
1122     }
1123 
1124     m_sorting = sort;
1125     q->updateSortActions();
1126     m_proxyModel->sort(sortColumn(), sortOrder());
1127 
1128     // TODO: The headers from QTreeView don't take care about a sorting
1129     // change of the proxy model hence they must be updated the manually.
1130     // This is done here by a qobject_cast, but it would be nicer to:
1131     // - provide a signal 'sortingChanged()'
1132     // - connect KDirOperatorDetailView() with this signal and update the
1133     //   header internally
1134     QTreeView *treeView = qobject_cast<QTreeView *>(m_itemView);
1135     if (treeView != nullptr) {
1136         QHeaderView *headerView = treeView->header();
1137         headerView->blockSignals(true);
1138         headerView->setSortIndicator(sortColumn(), sortOrder());
1139         headerView->blockSignals(false);
1140     }
1141 
1142     assureVisibleSelection();
1143 }
1144 
1145 // Protected
pathChanged()1146 void KDirOperator::pathChanged()
1147 {
1148     if (d->m_itemView == nullptr) {
1149         return;
1150     }
1151 
1152     d->m_pendingMimeTypes.clear();
1153     // d->fileView->clear(); TODO
1154     d->m_completion.clear();
1155     d->m_dirCompletion.clear();
1156 
1157     // it may be, that we weren't ready at this time
1158     QApplication::restoreOverrideCursor();
1159 
1160     // when KIO::Job emits finished, the slot will restore the cursor
1161     QApplication::setOverrideCursor(Qt::WaitCursor);
1162 
1163     if (!d->isReadable(d->m_currUrl)) {
1164         KMessageBox::error(d->m_itemView,
1165                            i18n("The specified folder does not exist "
1166                                 "or was not readable."));
1167         if (d->m_backStack.isEmpty()) {
1168             home();
1169         } else {
1170             back();
1171         }
1172     }
1173 }
1174 
slotRedirected(const QUrl & newURL)1175 void KDirOperatorPrivate::slotRedirected(const QUrl &newURL)
1176 {
1177     m_currUrl = newURL;
1178     m_pendingMimeTypes.clear();
1179     m_completion.clear();
1180     m_dirCompletion.clear();
1181     m_completeListDirty = true;
1182     Q_EMIT q->urlEntered(newURL);
1183 }
1184 
1185 // Code pinched from kfm then hacked
back()1186 void KDirOperator::back()
1187 {
1188     if (d->m_backStack.isEmpty()) {
1189         return;
1190     }
1191 
1192     d->m_forwardStack.push(new QUrl(d->m_currUrl));
1193 
1194     QUrl *s = d->m_backStack.pop();
1195     const QUrl newUrl = *s;
1196     delete s;
1197 
1198     if (d->m_dirHighlighting) {
1199         const QUrl _url = newUrl.adjusted(QUrl::StripTrailingSlash).adjusted(QUrl::RemoveFilename | QUrl::StripTrailingSlash);
1200 
1201         if (_url == d->m_currUrl.adjusted(QUrl::StripTrailingSlash) && !d->m_backStack.isEmpty()) {
1202             // e.g. started in a/b/c, cdUp() twice to "a", then back(), we highlight "c"
1203             d->m_lastUrl = *(d->m_backStack.top());
1204         } else {
1205             d->m_lastUrl = d->m_currUrl;
1206         }
1207     }
1208 
1209     setUrl(newUrl, false);
1210 }
1211 
1212 // Code pinched from kfm then hacked
forward()1213 void KDirOperator::forward()
1214 {
1215     if (d->m_forwardStack.isEmpty()) {
1216         return;
1217     }
1218 
1219     d->m_backStack.push(new QUrl(d->m_currUrl));
1220 
1221     QUrl *s = d->m_forwardStack.pop();
1222     setUrl(*s, false);
1223     delete s;
1224 }
1225 
url() const1226 QUrl KDirOperator::url() const
1227 {
1228     return d->m_currUrl;
1229 }
1230 
cdUp()1231 void KDirOperator::cdUp()
1232 {
1233     // Allow /d/c// to go up to /d/ instead of /d/c/
1234     QUrl tmp(d->m_currUrl.adjusted(QUrl::NormalizePathSegments));
1235 
1236     if (d->m_dirHighlighting) {
1237         d->m_lastUrl = d->m_currUrl;
1238     }
1239 
1240     setUrl(tmp.resolved(QUrl(QStringLiteral(".."))), true);
1241 }
1242 
home()1243 void KDirOperator::home()
1244 {
1245     setUrl(QUrl::fromLocalFile(QDir::homePath()), true);
1246 }
1247 
clearFilter()1248 void KDirOperator::clearFilter()
1249 {
1250     d->m_dirLister->setNameFilter(QString());
1251     d->m_dirLister->clearMimeFilter();
1252     checkPreviewSupport();
1253 }
1254 
setNameFilter(const QString & filter)1255 void KDirOperator::setNameFilter(const QString &filter)
1256 {
1257     d->m_dirLister->setNameFilter(filter);
1258     checkPreviewSupport();
1259 }
1260 
nameFilter() const1261 QString KDirOperator::nameFilter() const
1262 {
1263     return d->m_dirLister->nameFilter();
1264 }
1265 
setMimeFilter(const QStringList & mimetypes)1266 void KDirOperator::setMimeFilter(const QStringList &mimetypes)
1267 {
1268     d->m_dirLister->setMimeFilter(mimetypes);
1269     checkPreviewSupport();
1270 }
1271 
mimeFilter() const1272 QStringList KDirOperator::mimeFilter() const
1273 {
1274     return d->m_dirLister->mimeFilters();
1275 }
1276 
setNewFileMenuSupportedMimeTypes(const QStringList & mimeTypes)1277 void KDirOperator::setNewFileMenuSupportedMimeTypes(const QStringList &mimeTypes)
1278 {
1279     d->m_newFileMenu->setSupportedMimeTypes(mimeTypes);
1280 }
1281 
newFileMenuSupportedMimeTypes() const1282 QStringList KDirOperator::newFileMenuSupportedMimeTypes() const
1283 {
1284     return d->m_newFileMenu->supportedMimeTypes();
1285 }
1286 
setNewFileMenuSelectDirWhenAlreadyExist(bool selectOnDirExists)1287 void KDirOperator::setNewFileMenuSelectDirWhenAlreadyExist(bool selectOnDirExists)
1288 {
1289     d->m_newFileMenu->setSelectDirWhenAlreadyExist(selectOnDirExists);
1290 }
1291 
checkPreviewSupport()1292 bool KDirOperator::checkPreviewSupport()
1293 {
1294     KToggleAction *previewAction = static_cast<KToggleAction *>(d->m_actionCollection->action(QStringLiteral("preview")));
1295 
1296     bool hasPreviewSupport = false;
1297     KConfigGroup cg(KSharedConfig::openConfig(), ConfigGroup);
1298     if (cg.readEntry("Show Default Preview", true)) {
1299         hasPreviewSupport = d->checkPreviewInternal();
1300     }
1301 
1302     previewAction->setEnabled(hasPreviewSupport);
1303     return hasPreviewSupport;
1304 }
1305 
activatedMenu(const KFileItem & item,const QPoint & pos)1306 void KDirOperator::activatedMenu(const KFileItem &item, const QPoint &pos)
1307 {
1308     updateSelectionDependentActions();
1309 
1310     d->m_newFileMenu->setPopupFiles(QList<QUrl>{item.url()});
1311     d->m_newFileMenu->setViewShowsHiddenFiles(showHiddenFiles());
1312     d->m_newFileMenu->checkUpToDate();
1313 
1314     d->m_actionCollection->action(QStringLiteral("new"))->setEnabled(item.isDir());
1315 
1316     Q_EMIT contextMenuAboutToShow(item, d->m_actionMenu->menu());
1317 
1318     // Must be right before we call QMenu::exec(), otherwise we might remove
1319     // other non-related actions along with the open-with ones
1320     const QList<QAction *> openWithActions = d->insertOpenWithActions();
1321 
1322     d->m_actionMenu->menu()->exec(pos);
1323 
1324     // Remove the open-with actions, otherwise they would accumulate in the menu
1325     for (auto *action : openWithActions) {
1326         d->m_actionMenu->menu()->removeAction(action);
1327     }
1328 }
1329 
insertOpenWithActions()1330 QList<QAction *> KDirOperatorPrivate::insertOpenWithActions()
1331 {
1332     if (!m_showOpenWithActions) {
1333         return {};
1334     }
1335 
1336     const QList<QAction *> beforeOpenWith = m_actionMenu->menu()->actions();
1337 
1338     const KFileItemList items = q->selectedItems();
1339     if (!items.isEmpty()) {
1340         m_itemActions->setItemListProperties(KFileItemListProperties(items));
1341         const QList<QAction *> actions = m_actionMenu->menu()->actions();
1342         QAction *before = !actions.isEmpty() ? actions.at(0) : nullptr;
1343         m_itemActions->insertOpenWithActionsTo(before, m_actionMenu->menu(), QStringList());
1344     }
1345 
1346     // Get the list of the added open-with actions
1347     QList<QAction *> toRemove = m_actionMenu->menu()->actions();
1348     auto it = std::remove_if(toRemove.begin(), toRemove.end(), [beforeOpenWith](QAction *a) {
1349         return beforeOpenWith.contains(a);
1350     });
1351     toRemove.erase(it, toRemove.end());
1352 
1353     return toRemove;
1354 }
1355 
showOpenWithActions(bool enable)1356 void KDirOperator::showOpenWithActions(bool enable)
1357 {
1358     d->m_showOpenWithActions = enable;
1359 }
1360 
changeEvent(QEvent * event)1361 void KDirOperator::changeEvent(QEvent *event)
1362 {
1363     QWidget::changeEvent(event);
1364 }
1365 
eventFilter(QObject * watched,QEvent * event)1366 bool KDirOperator::eventFilter(QObject *watched, QEvent *event)
1367 {
1368     // If we are not hovering any items, check if there is a current index
1369     // set. In that case, we show the preview of that item.
1370     switch (event->type()) {
1371     case QEvent::MouseMove: {
1372         if (d->m_preview && !d->m_preview->isHidden()) {
1373             const QModelIndex hoveredIndex = d->m_itemView->indexAt(d->m_itemView->viewport()->mapFromGlobal(QCursor::pos()));
1374 
1375             if (d->m_lastHoveredIndex == hoveredIndex) {
1376                 return QWidget::eventFilter(watched, event);
1377             }
1378 
1379             d->m_lastHoveredIndex = hoveredIndex;
1380 
1381             const QModelIndex currentIndex = d->m_itemView->selectionModel() ? d->m_itemView->selectionModel()->currentIndex() : QModelIndex();
1382 
1383             if (!hoveredIndex.isValid() && currentIndex.isValid() && (d->m_lastHoveredIndex != currentIndex)) {
1384                 const KFileItem item = d->m_itemView->model()->data(currentIndex, KDirModel::FileItemRole).value<KFileItem>();
1385                 if (!item.isNull()) {
1386                     d->m_preview->showPreview(item.url());
1387                 }
1388             }
1389         }
1390         break;
1391     }
1392     case QEvent::Leave: {
1393         if (d->m_preview && !d->m_preview->isHidden()) {
1394             // when mouse leaves the view, show preview of selected file
1395             const QModelIndex currentIndex = d->m_itemView->selectionModel() ? d->m_itemView->selectionModel()->currentIndex() : QModelIndex();
1396             if (currentIndex.isValid()) {
1397                 const KFileItem item = d->m_itemView->model()->data(currentIndex, KDirModel::FileItemRole).value<KFileItem>();
1398                 if (!item.isNull()) {
1399                     d->m_preview->showPreview(item.url());
1400                 }
1401             }
1402         }
1403         break;
1404     }
1405     case QEvent::MouseButtonRelease: {
1406         if (d->m_preview != nullptr && !d->m_preview->isHidden()) {
1407             const QModelIndex hoveredIndex = d->m_itemView->indexAt(d->m_itemView->viewport()->mapFromGlobal(QCursor::pos()));
1408             const QModelIndex focusedIndex = d->m_itemView->selectionModel() ? d->m_itemView->selectionModel()->currentIndex() : QModelIndex();
1409 
1410             if (((!focusedIndex.isValid()) || !d->m_itemView->selectionModel()->isSelected(focusedIndex)) && (!hoveredIndex.isValid())) {
1411                 d->m_preview->clearPreview();
1412             }
1413         }
1414         QMouseEvent *mouseEvent = static_cast<QMouseEvent *>(event);
1415         if (mouseEvent) {
1416             switch (mouseEvent->button()) {
1417             case Qt::BackButton:
1418                 back();
1419                 break;
1420             case Qt::ForwardButton:
1421                 forward();
1422                 break;
1423             default:
1424                 break;
1425             }
1426         }
1427         break;
1428     }
1429     case QEvent::Wheel: {
1430         QWheelEvent *evt = static_cast<QWheelEvent *>(event);
1431         if (evt->modifiers() & Qt::ControlModifier) {
1432             if (evt->angleDelta().y() > 0) {
1433                 setIconSize(d->m_iconSize + 10);
1434             } else {
1435                 setIconSize(d->m_iconSize - 10);
1436             }
1437             return true;
1438         }
1439         break;
1440     }
1441     case QEvent::DragEnter: {
1442         // Accepts drops of one file or folder only
1443         QDragEnterEvent *evt = static_cast<QDragEnterEvent *>(event);
1444         const QList<QUrl> urls = KUrlMimeData::urlsFromMimeData(evt->mimeData(), KUrlMimeData::DecodeOptions::PreferLocalUrls);
1445 
1446         // only one file/folder can be dropped at the moment
1447         if (urls.size() != 1) {
1448             evt->ignore();
1449 
1450         } else {
1451             // MIME type filtering
1452             bool mimeFilterPass = true;
1453             const QStringList mimeFilters = d->m_dirLister->mimeFilters();
1454 
1455             if (mimeFilters.size() > 1) {
1456                 mimeFilterPass = false;
1457                 const QUrl &url = urls.constFirst();
1458 
1459                 QMimeDatabase mimeDataBase;
1460                 QMimeType fileMimeType = mimeDataBase.mimeTypeForUrl(url);
1461 
1462                 QRegularExpression regex;
1463                 for (const auto &mimeFilter : mimeFilters) {
1464                     regex.setPattern(mimeFilter);
1465                     if (regex.match(fileMimeType.name()).hasMatch()) { // matches!
1466                         mimeFilterPass = true;
1467                         break;
1468                     }
1469                 }
1470             }
1471 
1472             event->setAccepted(mimeFilterPass);
1473         }
1474 
1475         return true;
1476     }
1477     case QEvent::Drop: {
1478         QDropEvent *evt = static_cast<QDropEvent *>(event);
1479         const QList<QUrl> urls = KUrlMimeData::urlsFromMimeData(evt->mimeData(), KUrlMimeData::DecodeOptions::PreferLocalUrls);
1480 
1481         const QUrl &url = urls.constFirst();
1482 
1483         // stat the url to get details
1484         KIO::StatJob *job = KIO::stat(url, KIO::HideProgressInfo);
1485         job->exec();
1486 
1487         setFocus();
1488 
1489         const KIO::UDSEntry entry = job->statResult();
1490 
1491         if (entry.isDir()) {
1492             // if this was a directory
1493             setUrl(url, false);
1494         } else {
1495             // if the current url is not known
1496             if (d->m_dirLister->findByUrl(url).isNull()) {
1497                 setUrl(url.adjusted(QUrl::RemoveFilename), false);
1498 
1499                 // Will set the current item once loading has finished
1500                 auto urlSetterClosure = [this, url]() {
1501                     setCurrentItem(url);
1502                     QObject::disconnect(d->m_connection);
1503                 };
1504                 d->m_connection = connect(this, &KDirOperator::finishedLoading, this, urlSetterClosure);
1505             } else {
1506                 setCurrentItem(url);
1507             }
1508         }
1509         evt->accept();
1510         return true;
1511     }
1512     case QEvent::KeyPress: {
1513         QKeyEvent *evt = static_cast<QKeyEvent *>(event);
1514         if (evt->key() == Qt::Key_Return || evt->key() == Qt::Key_Enter) {
1515             // when no elements are selected and Return/Enter is pressed
1516             // emit keyEnterReturnPressed
1517             // let activated event be emitted by subsequent QAbstractItemView::keyPress otherwise
1518             if (!d->m_itemView->currentIndex().isValid()) {
1519                 Q_EMIT keyEnterReturnPressed();
1520                 evt->accept();
1521                 return true;
1522             }
1523         }
1524         break;
1525     }
1526     case QEvent::Resize: {
1527         /* clang-format off */
1528         if (watched == d->m_itemView->viewport()
1529             && d->m_itemView->horizontalScrollBar()
1530             && d->m_progressBar->parent() == this /* it could have been reparented to a statusbar */) { /* clang-format on */
1531             if (d->m_itemView->horizontalScrollBar()->isVisible()) {
1532                 // Show the progress bar above the horizontal scrollbar that may be visible
1533                 // in compact view
1534                 QPoint progressBarPos = d->progressBarPos();
1535                 progressBarPos.ry() -= d->m_itemView->horizontalScrollBar()->height();
1536                 d->m_progressBar->move(progressBarPos);
1537             } else {
1538                 d->m_progressBar->move(d->progressBarPos());
1539             }
1540         }
1541         break;
1542     }
1543     default:
1544         break;
1545     }
1546 
1547     return QWidget::eventFilter(watched, event);
1548 }
1549 
checkPreviewInternal() const1550 bool KDirOperatorPrivate::checkPreviewInternal() const
1551 {
1552     const QStringList supported = KIO::PreviewJob::supportedMimeTypes();
1553     // no preview support for directories?
1554     if (q->dirOnlyMode() && supported.indexOf(QLatin1String("inode/directory")) == -1) {
1555         return false;
1556     }
1557 
1558     const QStringList mimeTypes = m_dirLister->mimeFilters();
1559     const QStringList nameFilters = m_dirLister->nameFilter().split(QLatin1Char(' '), Qt::SkipEmptyParts);
1560 
1561     if (mimeTypes.isEmpty() && nameFilters.isEmpty() && !supported.isEmpty()) {
1562         return true;
1563     } else {
1564         QMimeDatabase db;
1565         QRegularExpression re;
1566 
1567         if (!mimeTypes.isEmpty()) {
1568             for (const QString &str : supported) {
1569                 // wildcard matching because the "mimetype" can be "image/*"
1570                 re.setPattern(QRegularExpression::wildcardToRegularExpression(str));
1571 
1572                 if (mimeTypes.indexOf(re) != -1) { // matches! -> we want previews
1573                     return true;
1574                 }
1575             }
1576         }
1577 
1578         if (!nameFilters.isEmpty()) {
1579             // find the MIME types of all the filter-patterns
1580             for (const QString &filter : nameFilters) {
1581                 if (filter == QLatin1Char('*')) {
1582                     return true;
1583                 }
1584 
1585                 const QMimeType mt = db.mimeTypeForFile(filter, QMimeDatabase::MatchExtension /*fast mode, no file contents exist*/);
1586                 if (!mt.isValid()) {
1587                     continue;
1588                 }
1589                 const QString mime = mt.name();
1590 
1591                 for (const QString &str : supported) {
1592                     // the "mimetypes" we get from the PreviewJob can be "image/*"
1593                     // so we need to check in wildcard mode
1594                     re.setPattern(QRegularExpression::wildcardToRegularExpression(str));
1595                     if (re.match(mime).hasMatch()) {
1596                         return true;
1597                     }
1598                 }
1599             }
1600         }
1601     }
1602 
1603     return false;
1604 }
1605 
createView(QWidget * parent,KFile::FileView viewKind)1606 QAbstractItemView *KDirOperator::createView(QWidget *parent, KFile::FileView viewKind)
1607 {
1608     QAbstractItemView *itemView = nullptr;
1609     if (KFile::isDetailView(viewKind) || KFile::isTreeView(viewKind) || KFile::isDetailTreeView(viewKind)) {
1610         KDirOperatorDetailView *detailView = new KDirOperatorDetailView(parent);
1611         detailView->setViewMode(viewKind);
1612         itemView = detailView;
1613     } else {
1614         itemView = new KDirOperatorIconView(parent, decorationPosition());
1615     }
1616 
1617     return itemView;
1618 }
1619 
setAcceptDrops(bool acceptsDrops)1620 void KDirOperator::setAcceptDrops(bool acceptsDrops)
1621 {
1622     QWidget::setAcceptDrops(acceptsDrops);
1623     if (view()) {
1624         view()->setAcceptDrops(acceptsDrops);
1625         if (acceptsDrops) {
1626             view()->installEventFilter(this);
1627         } else {
1628             view()->removeEventFilter(this);
1629         }
1630     }
1631 }
1632 
setDropOptions(int options)1633 void KDirOperator::setDropOptions(int options)
1634 {
1635     d->m_dropOptions = options;
1636     // TODO:
1637     // if (d->fileView)
1638     //   d->fileView->setDropOptions(options);
1639 }
1640 
setView(KFile::FileView viewKind)1641 void KDirOperator::setView(KFile::FileView viewKind)
1642 {
1643     bool preview = (KFile::isPreviewInfo(viewKind) || KFile::isPreviewContents(viewKind));
1644 
1645     if (viewKind == KFile::Default) {
1646         if (KFile::isDetailView((KFile::FileView)d->m_defaultView)) {
1647             viewKind = KFile::Detail;
1648         } else if (KFile::isTreeView((KFile::FileView)d->m_defaultView)) {
1649             viewKind = KFile::Tree;
1650         } else if (KFile::isDetailTreeView((KFile::FileView)d->m_defaultView)) {
1651             viewKind = KFile::DetailTree;
1652         } else {
1653             viewKind = KFile::Simple;
1654         }
1655 
1656         const KFile::FileView defaultViewKind = static_cast<KFile::FileView>(d->m_defaultView);
1657         preview = (KFile::isPreviewInfo(defaultViewKind) || KFile::isPreviewContents(defaultViewKind))
1658             && d->m_actionCollection->action(QStringLiteral("preview"))->isEnabled();
1659     }
1660 
1661     d->m_viewKind = static_cast<int>(viewKind);
1662     viewKind = static_cast<KFile::FileView>(d->m_viewKind);
1663 
1664     QAbstractItemView *newView = createView(this, viewKind);
1665     setView(newView);
1666 
1667     if (acceptDrops()) {
1668         newView->setAcceptDrops(true);
1669         newView->installEventFilter(this);
1670     }
1671 
1672     d->togglePreview(preview);
1673 }
1674 
viewMode() const1675 KFile::FileView KDirOperator::viewMode() const
1676 {
1677     return static_cast<KFile::FileView>(d->m_viewKind);
1678 }
1679 
view() const1680 QAbstractItemView *KDirOperator::view() const
1681 {
1682     return d->m_itemView;
1683 }
1684 
mode() const1685 KFile::Modes KDirOperator::mode() const
1686 {
1687     return d->m_mode;
1688 }
1689 
setMode(KFile::Modes mode)1690 void KDirOperator::setMode(KFile::Modes mode)
1691 {
1692     if (d->m_mode == mode) {
1693         return;
1694     }
1695 
1696     d->m_mode = mode;
1697 
1698     d->m_dirLister->setDirOnlyMode(dirOnlyMode());
1699 
1700     // reset the view with the different mode
1701     if (d->m_itemView != nullptr) {
1702         setView(static_cast<KFile::FileView>(d->m_viewKind));
1703     }
1704 }
1705 
setView(QAbstractItemView * view)1706 void KDirOperator::setView(QAbstractItemView *view)
1707 {
1708     if (view == d->m_itemView) {
1709         return;
1710     }
1711 
1712     // TODO: do a real timer and restart it after that
1713     d->m_pendingMimeTypes.clear();
1714     const bool listDir = (d->m_itemView == nullptr);
1715 
1716     if (d->m_mode & KFile::Files) {
1717         view->setSelectionMode(QAbstractItemView::ExtendedSelection);
1718     } else {
1719         view->setSelectionMode(QAbstractItemView::SingleSelection);
1720     }
1721 
1722     QItemSelectionModel *selectionModel = nullptr;
1723     if ((d->m_itemView != nullptr) && d->m_itemView->selectionModel()->hasSelection()) {
1724         // remember the selection of the current item view and apply this selection
1725         // to the new view later
1726         const QItemSelection selection = d->m_itemView->selectionModel()->selection();
1727         selectionModel = new QItemSelectionModel(d->m_proxyModel, this);
1728         selectionModel->select(selection, QItemSelectionModel::Select);
1729     }
1730 
1731     setFocusProxy(nullptr);
1732     delete d->m_itemView;
1733     d->m_itemView = view;
1734     d->m_itemView->setModel(d->m_proxyModel);
1735     setFocusProxy(d->m_itemView);
1736 
1737     d->m_itemView->viewport()->setObjectName(QStringLiteral("d->itemview_viewport"));
1738     view->viewport()->installEventFilter(this);
1739 
1740     KFileItemDelegate *delegate = new KFileItemDelegate(d->m_itemView);
1741     d->m_itemView->setItemDelegate(delegate);
1742     d->m_itemView->viewport()->setAttribute(Qt::WA_Hover);
1743     d->m_itemView->setContextMenuPolicy(Qt::CustomContextMenu);
1744     d->m_itemView->setMouseTracking(true);
1745     // d->itemView->setDropOptions(d->dropOptions);
1746 
1747     // first push our settings to the view, then listen for changes from the view
1748     QTreeView *treeView = qobject_cast<QTreeView *>(d->m_itemView);
1749     if (treeView) {
1750         QHeaderView *headerView = treeView->header();
1751         headerView->setSortIndicator(d->sortColumn(), d->sortOrder());
1752         connect(headerView, &QHeaderView::sortIndicatorChanged, this, [this](int logicalIndex, Qt::SortOrder order) {
1753             d->synchronizeSortingState(logicalIndex, order);
1754         });
1755     }
1756 
1757     connect(d->m_itemView, &QAbstractItemView::activated, this, [this](QModelIndex index) {
1758         d->slotActivated(index);
1759     });
1760 
1761     connect(d->m_itemView, &QAbstractItemView::customContextMenuRequested, this, [this](QPoint pos) {
1762         d->openContextMenu(pos);
1763     });
1764 
1765     connect(d->m_itemView, &QAbstractItemView::entered, this, [this](QModelIndex index) {
1766         d->triggerPreview(index);
1767     });
1768 
1769     d->m_splitter->insertWidget(0, d->m_itemView);
1770 
1771     d->m_splitter->resize(size());
1772     d->m_itemView->show();
1773 
1774     if (listDir) {
1775         QApplication::setOverrideCursor(Qt::WaitCursor);
1776         d->openUrl(d->m_currUrl);
1777     }
1778 
1779     if (selectionModel != nullptr) {
1780         d->m_itemView->setSelectionModel(selectionModel);
1781         QMetaObject::invokeMethod(
1782             this,
1783             [this]() {
1784                 d->assureVisibleSelection();
1785             },
1786             Qt::QueuedConnection);
1787     }
1788 
1789     connect(d->m_itemView->selectionModel(), &QItemSelectionModel::currentChanged, this, [this](QModelIndex index) {
1790         d->triggerPreview(index);
1791     });
1792     connect(d->m_itemView->selectionModel(), &QItemSelectionModel::selectionChanged, this, [this]() {
1793         d->slotSelectionChanged();
1794     });
1795 
1796     // if we cannot cast it to a QListView, disable the "Icon Position" menu. Note that this check
1797     // needs to be done here, and not in createView, since we can be set an external view
1798     d->m_decorationMenu->setEnabled(qobject_cast<QListView *>(d->m_itemView));
1799 
1800     d->m_shouldFetchForItems = qobject_cast<QTreeView *>(view);
1801     if (d->m_shouldFetchForItems) {
1802         connect(d->m_dirModel, &KDirModel::expand, this, [this](QModelIndex index) {
1803             d->slotExpandToUrl(index);
1804         });
1805     } else {
1806         d->m_itemsToBeSetAsCurrent.clear();
1807     }
1808 
1809     const bool previewForcedToTrue = d->m_inlinePreviewState == KDirOperatorPrivate::ForcedToTrue;
1810     const bool previewShown = d->m_inlinePreviewState == KDirOperatorPrivate::NotForced ? d->m_showPreviews : previewForcedToTrue;
1811     d->m_previewGenerator = new KFilePreviewGenerator(d->m_itemView);
1812     d->m_itemView->setIconSize(previewForcedToTrue ? QSize(KIconLoader::SizeHuge, KIconLoader::SizeHuge) : QSize(d->m_iconSize, d->m_iconSize));
1813     d->m_previewGenerator->setPreviewShown(previewShown);
1814     d->m_actionCollection->action(QStringLiteral("inline preview"))->setChecked(previewShown);
1815 
1816     // ensure we change everything needed
1817     d->slotChangeDecorationPosition();
1818     updateViewActions();
1819 
1820     Q_EMIT viewChanged(view);
1821 
1822     const int zoom = previewForcedToTrue ? KIconLoader::SizeHuge : d->iconSizeForViewType(view);
1823 
1824     // this will make d->m_iconSize be updated, since setIconSize slot will be called
1825     Q_EMIT currentIconSizeChanged(zoom);
1826 }
1827 
setDirLister(KDirLister * lister)1828 void KDirOperator::setDirLister(KDirLister *lister)
1829 {
1830     if (lister == d->m_dirLister) { // sanity check
1831         return;
1832     }
1833 
1834     delete d->m_dirModel;
1835     d->m_dirModel = nullptr;
1836 
1837     delete d->m_proxyModel;
1838     d->m_proxyModel = nullptr;
1839 
1840     // delete d->m_dirLister; // deleted by KDirModel already, which took ownership
1841     d->m_dirLister = lister;
1842 
1843     d->m_dirModel = new KDirModel(this);
1844     d->m_dirModel->setDirLister(d->m_dirLister);
1845     d->m_dirModel->setDropsAllowed(KDirModel::DropOnDirectory);
1846 
1847     d->m_shouldFetchForItems = qobject_cast<QTreeView *>(d->m_itemView);
1848     if (d->m_shouldFetchForItems) {
1849         connect(d->m_dirModel, &KDirModel::expand, this, [this](QModelIndex index) {
1850             d->slotExpandToUrl(index);
1851         });
1852     } else {
1853         d->m_itemsToBeSetAsCurrent.clear();
1854     }
1855 
1856     d->m_proxyModel = new KDirSortFilterProxyModel(this);
1857     d->m_proxyModel->setSourceModel(d->m_dirModel);
1858 
1859     d->m_dirLister->setDelayedMimeTypes(true);
1860 
1861     QWidget *mainWidget = topLevelWidget();
1862     d->m_dirLister->setMainWindow(mainWidget);
1863     // qDebug() << "mainWidget=" << mainWidget;
1864 
1865     connect(d->m_dirLister, &KCoreDirLister::percent, this, [this](int percent) {
1866         d->slotProgress(percent);
1867     });
1868     connect(d->m_dirLister, &KCoreDirLister::started, this, [this]() {
1869         d->slotStarted();
1870     });
1871     connect(d->m_dirLister, qOverload<>(&KCoreDirLister::completed), this, [this]() {
1872         d->slotIOFinished();
1873     });
1874     connect(d->m_dirLister, qOverload<>(&KCoreDirLister::canceled), this, [this]() {
1875         d->slotCanceled();
1876     });
1877     connect(d->m_dirLister, qOverload<const QUrl &, const QUrl &>(&KCoreDirLister::redirection), this, [this](const QUrl &, const QUrl &newUrl) {
1878         d->slotRedirected(newUrl);
1879     });
1880     connect(d->m_dirLister, &KCoreDirLister::newItems, this, [this]() {
1881         d->slotItemsChanged();
1882     });
1883     connect(d->m_dirLister, &KCoreDirLister::itemsDeleted, this, [this]() {
1884         d->slotItemsChanged();
1885     });
1886     connect(d->m_dirLister, &KCoreDirLister::itemsFilteredByMime, this, [this]() {
1887         d->slotItemsChanged();
1888     });
1889     connect(d->m_dirLister, qOverload<>(&KCoreDirLister::clear), this, [this]() {
1890         d->slotItemsChanged();
1891     });
1892 }
1893 
selectDir(const KFileItem & item)1894 void KDirOperator::selectDir(const KFileItem &item)
1895 {
1896     setUrl(item.targetUrl(), true);
1897 }
1898 
selectFile(const KFileItem & item)1899 void KDirOperator::selectFile(const KFileItem &item)
1900 {
1901     QApplication::restoreOverrideCursor();
1902 
1903     Q_EMIT fileSelected(item);
1904 }
1905 
highlightFile(const KFileItem & item)1906 void KDirOperator::highlightFile(const KFileItem &item)
1907 {
1908     if ((d->m_preview != nullptr && !d->m_preview->isHidden()) && !item.isNull()) {
1909         d->m_preview->showPreview(item.url());
1910     }
1911 
1912     Q_EMIT fileHighlighted(item);
1913 }
1914 
setCurrentItem(const QUrl & url)1915 void KDirOperator::setCurrentItem(const QUrl &url)
1916 {
1917     // qDebug();
1918 
1919     KFileItem item = d->m_dirLister->findByUrl(url);
1920     if (d->m_shouldFetchForItems && item.isNull()) {
1921         d->m_itemsToBeSetAsCurrent << url;
1922 
1923         if (d->m_viewKind == KFile::DetailTree) {
1924             d->m_dirModel->expandToUrl(url);
1925         }
1926 
1927         return;
1928     }
1929 
1930     setCurrentItem(item);
1931 }
1932 
setCurrentItem(const KFileItem & item)1933 void KDirOperator::setCurrentItem(const KFileItem &item)
1934 {
1935     // qDebug();
1936 
1937     if (!d->m_itemView) {
1938         return;
1939     }
1940 
1941     QItemSelectionModel *selModel = d->m_itemView->selectionModel();
1942     if (selModel) {
1943         selModel->clear();
1944         if (!item.isNull()) {
1945             const QModelIndex dirIndex = d->m_dirModel->indexForItem(item);
1946             const QModelIndex proxyIndex = d->m_proxyModel->mapFromSource(dirIndex);
1947             selModel->setCurrentIndex(proxyIndex, QItemSelectionModel::Select);
1948         }
1949     }
1950 }
1951 
setCurrentItems(const QList<QUrl> & urls)1952 void KDirOperator::setCurrentItems(const QList<QUrl> &urls)
1953 {
1954     // qDebug();
1955 
1956     if (!d->m_itemView) {
1957         return;
1958     }
1959 
1960     KFileItemList itemList;
1961     for (const QUrl &url : urls) {
1962         KFileItem item = d->m_dirLister->findByUrl(url);
1963         if (d->m_shouldFetchForItems && item.isNull()) {
1964             d->m_itemsToBeSetAsCurrent << url;
1965 
1966             if (d->m_viewKind == KFile::DetailTree) {
1967                 d->m_dirModel->expandToUrl(url);
1968             }
1969 
1970             continue;
1971         }
1972 
1973         itemList << item;
1974     }
1975 
1976     setCurrentItems(itemList);
1977 }
1978 
setCurrentItems(const KFileItemList & items)1979 void KDirOperator::setCurrentItems(const KFileItemList &items)
1980 {
1981     // qDebug();
1982 
1983     if (d->m_itemView == nullptr) {
1984         return;
1985     }
1986 
1987     QItemSelectionModel *selModel = d->m_itemView->selectionModel();
1988     if (selModel) {
1989         selModel->clear();
1990         QModelIndex proxyIndex;
1991         for (const KFileItem &item : items) {
1992             if (!item.isNull()) {
1993                 const QModelIndex dirIndex = d->m_dirModel->indexForItem(item);
1994                 proxyIndex = d->m_proxyModel->mapFromSource(dirIndex);
1995                 selModel->select(proxyIndex, QItemSelectionModel::Select);
1996             }
1997         }
1998         if (proxyIndex.isValid()) {
1999             selModel->setCurrentIndex(proxyIndex, QItemSelectionModel::NoUpdate);
2000         }
2001     }
2002 }
2003 
makeCompletion(const QString & string)2004 QString KDirOperator::makeCompletion(const QString &string)
2005 {
2006     if (string.isEmpty()) {
2007         d->m_itemView->selectionModel()->clear();
2008         return QString();
2009     }
2010 
2011     prepareCompletionObjects();
2012     return d->m_completion.makeCompletion(string);
2013 }
2014 
makeDirCompletion(const QString & string)2015 QString KDirOperator::makeDirCompletion(const QString &string)
2016 {
2017     if (string.isEmpty()) {
2018         d->m_itemView->selectionModel()->clear();
2019         return QString();
2020     }
2021 
2022     prepareCompletionObjects();
2023     return d->m_dirCompletion.makeCompletion(string);
2024 }
2025 
prepareCompletionObjects()2026 void KDirOperator::prepareCompletionObjects()
2027 {
2028     if (d->m_itemView == nullptr) {
2029         return;
2030     }
2031 
2032     if (d->m_completeListDirty) { // create the list of all possible completions
2033         const KFileItemList itemList = d->m_dirLister->items();
2034         for (const KFileItem &item : itemList) {
2035             d->m_completion.addItem(item.name());
2036             if (item.isDir()) {
2037                 d->m_dirCompletion.addItem(item.name());
2038             }
2039         }
2040         d->m_completeListDirty = false;
2041     }
2042 }
2043 
slotCompletionMatch(const QString & match)2044 void KDirOperator::slotCompletionMatch(const QString &match)
2045 {
2046     QUrl url(match);
2047     if (url.isRelative()) {
2048         url = d->m_currUrl.resolved(url);
2049     }
2050     setCurrentItem(url);
2051     Q_EMIT completion(match);
2052 }
2053 
setupActions()2054 void KDirOperator::setupActions()
2055 {
2056     d->m_actionCollection = new KActionCollection(this);
2057     d->m_actionCollection->setObjectName(QStringLiteral("KDirOperator::actionCollection"));
2058 
2059     d->m_actionMenu = new KActionMenu(i18n("Menu"), this);
2060     d->m_actionCollection->addAction(QStringLiteral("popupMenu"), d->m_actionMenu);
2061 
2062     QAction *upAction = d->m_actionCollection->addAction(KStandardAction::Up, QStringLiteral("up"), this, SLOT(cdUp()));
2063     upAction->setText(i18n("Parent Folder"));
2064 
2065     QAction *backAction = d->m_actionCollection->addAction(KStandardAction::Back, QStringLiteral("back"), this, SLOT(back()));
2066     backAction->setShortcut(Qt::Key_Backspace);
2067     backAction->setToolTip(i18nc("@info", "Go back"));
2068 
2069     QAction *forwardAction = d->m_actionCollection->addAction(KStandardAction::Forward, QStringLiteral("forward"), this, SLOT(forward()));
2070     forwardAction->setToolTip(i18nc("@info", "Go forward"));
2071 
2072     QAction *homeAction = d->m_actionCollection->addAction(KStandardAction::Home, QStringLiteral("home"), this, SLOT(home()));
2073     homeAction->setText(i18n("Home Folder"));
2074 
2075     QAction *reloadAction = d->m_actionCollection->addAction(KStandardAction::Redisplay, QStringLiteral("reload"), this, SLOT(rereadDir()));
2076     reloadAction->setText(i18n("Reload"));
2077     reloadAction->setShortcuts(KStandardShortcut::shortcut(KStandardShortcut::Reload));
2078 
2079     QAction *mkdirAction = new QAction(i18n("New Folder..."), this);
2080     d->m_actionCollection->addAction(QStringLiteral("mkdir"), mkdirAction);
2081     mkdirAction->setIcon(QIcon::fromTheme(QStringLiteral("folder-new")));
2082     connect(mkdirAction, &QAction::triggered, this, [this]() {
2083         mkdir();
2084     });
2085 
2086     QAction *rename = KStandardAction::renameFile(this, &KDirOperator::renameSelected, this);
2087     d->m_actionCollection->addAction(QStringLiteral("rename"), rename);
2088 
2089     QAction *trash = new QAction(i18n("Move to Trash"), this);
2090     d->m_actionCollection->addAction(QStringLiteral("trash"), trash);
2091     trash->setIcon(QIcon::fromTheme(QStringLiteral("user-trash")));
2092     trash->setShortcut(Qt::Key_Delete);
2093     connect(trash, &QAction::triggered, this, &KDirOperator::trashSelected);
2094 
2095     QAction *action = new QAction(i18n("Delete"), this);
2096     d->m_actionCollection->addAction(QStringLiteral("delete"), action);
2097     action->setIcon(QIcon::fromTheme(QStringLiteral("edit-delete")));
2098     action->setShortcut(Qt::SHIFT | Qt::Key_Delete);
2099     connect(action, &QAction::triggered, this, &KDirOperator::deleteSelected);
2100 
2101     // the sort menu actions
2102     KActionMenu *sortMenu = new KActionMenu(i18n("Sorting"), this);
2103     sortMenu->setIcon(QIcon::fromTheme(QStringLiteral("view-sort")));
2104     sortMenu->setPopupMode(QToolButton::InstantPopup);
2105     d->m_actionCollection->addAction(QStringLiteral("sorting menu"), sortMenu);
2106 
2107     KToggleAction *byNameAction = new KToggleAction(i18n("Sort by Name"), this);
2108     d->m_actionCollection->addAction(QStringLiteral("by name"), byNameAction);
2109     connect(byNameAction, &QAction::triggered, this, [this]() {
2110         d->slotSortByName();
2111     });
2112 
2113     KToggleAction *bySizeAction = new KToggleAction(i18n("Sort by Size"), this);
2114     d->m_actionCollection->addAction(QStringLiteral("by size"), bySizeAction);
2115     connect(bySizeAction, &QAction::triggered, this, [this]() {
2116         d->slotSortBySize();
2117     });
2118 
2119     KToggleAction *byDateAction = new KToggleAction(i18n("Sort by Date"), this);
2120     d->m_actionCollection->addAction(QStringLiteral("by date"), byDateAction);
2121     connect(byDateAction, &QAction::triggered, this, [this]() {
2122         d->slotSortByDate();
2123     });
2124 
2125     KToggleAction *byTypeAction = new KToggleAction(i18n("Sort by Type"), this);
2126     d->m_actionCollection->addAction(QStringLiteral("by type"), byTypeAction);
2127     connect(byTypeAction, &QAction::triggered, this, [this]() {
2128         d->slotSortByType();
2129     });
2130 
2131     QActionGroup *sortOrderGroup = new QActionGroup(this);
2132     sortOrderGroup->setExclusive(true);
2133 
2134     KToggleAction *ascendingAction = new KToggleAction(i18n("Ascending"), this);
2135     d->m_actionCollection->addAction(QStringLiteral("ascending"), ascendingAction);
2136     ascendingAction->setActionGroup(sortOrderGroup);
2137     connect(ascendingAction, &QAction::triggered, this, [this]() {
2138         this->d->slotSortReversed(false);
2139     });
2140 
2141     KToggleAction *descendingAction = new KToggleAction(i18n("Descending"), this);
2142     d->m_actionCollection->addAction(QStringLiteral("descending"), descendingAction);
2143     descendingAction->setActionGroup(sortOrderGroup);
2144     connect(descendingAction, &QAction::triggered, this, [this]() {
2145         this->d->slotSortReversed(true);
2146     });
2147 
2148     KToggleAction *dirsFirstAction = new KToggleAction(i18n("Folders First"), this);
2149     d->m_actionCollection->addAction(QStringLiteral("dirs first"), dirsFirstAction);
2150     connect(dirsFirstAction, &QAction::triggered, this, [this]() {
2151         d->slotToggleDirsFirst();
2152     });
2153 
2154     // View modes that match those of Dolphin
2155     KToggleAction *iconsViewAction = new KToggleAction(i18n("Icons View"), this);
2156     iconsViewAction->setIcon(QIcon::fromTheme(QStringLiteral("view-list-icons")));
2157     d->m_actionCollection->addAction(QStringLiteral("icons view"), iconsViewAction);
2158     connect(iconsViewAction, &QAction::triggered, this, [this]() {
2159         d->slotIconsView();
2160     });
2161 
2162     KToggleAction *compactViewAction = new KToggleAction(i18n("Compact View"), this);
2163     compactViewAction->setIcon(QIcon::fromTheme(QStringLiteral("view-list-details")));
2164     d->m_actionCollection->addAction(QStringLiteral("compact view"), compactViewAction);
2165     connect(compactViewAction, &QAction::triggered, this, [this]() {
2166         d->slotCompactView();
2167     });
2168 
2169     KToggleAction *detailsViewAction = new KToggleAction(i18n("Details View"), this);
2170     detailsViewAction->setIcon(QIcon::fromTheme(QStringLiteral("view-list-tree")));
2171     d->m_actionCollection->addAction(QStringLiteral("details view"), detailsViewAction);
2172     connect(detailsViewAction, &QAction::triggered, this, [this]() {
2173         d->slotDetailsView();
2174     });
2175 
2176     QActionGroup *viewModeGroup = new QActionGroup(this);
2177     viewModeGroup->setExclusive(true);
2178     iconsViewAction->setActionGroup(viewModeGroup);
2179     compactViewAction->setActionGroup(viewModeGroup);
2180     detailsViewAction->setActionGroup(viewModeGroup);
2181 
2182     QActionGroup *sortGroup = new QActionGroup(this);
2183     byNameAction->setActionGroup(sortGroup);
2184     bySizeAction->setActionGroup(sortGroup);
2185     byDateAction->setActionGroup(sortGroup);
2186     byTypeAction->setActionGroup(sortGroup);
2187 
2188     d->m_decorationMenu = new KActionMenu(i18n("Icon Position"), this);
2189     d->m_actionCollection->addAction(QStringLiteral("decoration menu"), d->m_decorationMenu);
2190 
2191     d->m_leftAction = new KToggleAction(i18n("Next to File Name"), this);
2192     d->m_actionCollection->addAction(QStringLiteral("decorationAtLeft"), d->m_leftAction);
2193     connect(d->m_leftAction, &QAction::triggered, this, [this]() {
2194         d->slotChangeDecorationPosition();
2195     });
2196 
2197     KToggleAction *topAction = new KToggleAction(i18n("Above File Name"), this);
2198     d->m_actionCollection->addAction(QStringLiteral("decorationAtTop"), topAction);
2199     connect(topAction, &QAction::triggered, this, [this]() {
2200         d->slotChangeDecorationPosition();
2201     });
2202 
2203     d->m_decorationMenu->addAction(d->m_leftAction);
2204     d->m_decorationMenu->addAction(topAction);
2205 
2206     QActionGroup *decorationGroup = new QActionGroup(this);
2207     decorationGroup->setExclusive(true);
2208     d->m_leftAction->setActionGroup(decorationGroup);
2209     topAction->setActionGroup(decorationGroup);
2210 
2211     KToggleAction *shortAction = new KToggleAction(i18n("Short View"), this);
2212     d->m_actionCollection->addAction(QStringLiteral("short view"), shortAction);
2213     shortAction->setIcon(QIcon::fromTheme(QStringLiteral("view-list-icons")));
2214     connect(shortAction, &QAction::triggered, this, [this]() {
2215         d->slotSimpleView();
2216     });
2217 
2218     KToggleAction *detailedAction = new KToggleAction(i18n("Detailed View"), this);
2219     d->m_actionCollection->addAction(QStringLiteral("detailed view"), detailedAction);
2220     detailedAction->setIcon(QIcon::fromTheme(QStringLiteral("view-list-details")));
2221     connect(detailedAction, &QAction::triggered, this, [this]() {
2222         d->slotDetailedView();
2223     });
2224 
2225     KToggleAction *treeAction = new KToggleAction(i18n("Tree View"), this);
2226     d->m_actionCollection->addAction(QStringLiteral("tree view"), treeAction);
2227     treeAction->setIcon(QIcon::fromTheme(QStringLiteral("view-list-tree")));
2228     connect(treeAction, &QAction::triggered, this, [this]() {
2229         d->slotTreeView();
2230     });
2231 
2232     KToggleAction *detailedTreeAction = new KToggleAction(i18n("Detailed Tree View"), this);
2233     d->m_actionCollection->addAction(QStringLiteral("detailed tree view"), detailedTreeAction);
2234     detailedTreeAction->setIcon(QIcon::fromTheme(QStringLiteral("view-list-tree")));
2235     connect(detailedTreeAction, &QAction::triggered, this, [this]() {
2236         d->slotDetailedTreeView();
2237     });
2238 
2239     QActionGroup *viewGroup = new QActionGroup(this);
2240     shortAction->setActionGroup(viewGroup);
2241     detailedAction->setActionGroup(viewGroup);
2242     treeAction->setActionGroup(viewGroup);
2243     detailedTreeAction->setActionGroup(viewGroup);
2244 
2245     KToggleAction *allowExpansionAction = new KToggleAction(i18n("Allow Expansion in Details View"), this);
2246     d->m_actionCollection->addAction(QStringLiteral("allow expansion"), allowExpansionAction);
2247     connect(allowExpansionAction, &QAction::toggled, this, [this](bool allow) {
2248         d->slotToggleAllowExpansion(allow);
2249     });
2250 
2251     KToggleAction *showHiddenAction = new KToggleAction(i18n("Show Hidden Files"), this);
2252     d->m_actionCollection->addAction(QStringLiteral("show hidden"), showHiddenAction);
2253     showHiddenAction->setShortcuts(KStandardShortcut::showHideHiddenFiles());
2254     connect(showHiddenAction, &QAction::toggled, this, [this](bool show) {
2255         d->slotToggleHidden(show);
2256     });
2257 
2258     KToggleAction *previewAction = new KToggleAction(i18n("Show Preview Panel"), this);
2259     d->m_actionCollection->addAction(QStringLiteral("preview"), previewAction);
2260     previewAction->setShortcut(Qt::Key_F11);
2261     connect(previewAction, &QAction::toggled, this, [this](bool enable) {
2262         d->togglePreview(enable);
2263     });
2264 
2265     KToggleAction *inlinePreview = new KToggleAction(QIcon::fromTheme(QStringLiteral("view-preview")), i18n("Show Preview"), this);
2266     d->m_actionCollection->addAction(QStringLiteral("inline preview"), inlinePreview);
2267     inlinePreview->setShortcut(Qt::Key_F12);
2268     connect(inlinePreview, &QAction::toggled, this, [this](bool enable) {
2269         d->toggleInlinePreviews(enable);
2270     });
2271 
2272     QAction *fileManager = new QAction(i18n("Open Containing Folder"), this);
2273     d->m_actionCollection->addAction(QStringLiteral("file manager"), fileManager);
2274     fileManager->setIcon(QIcon::fromTheme(QStringLiteral("system-file-manager")));
2275     connect(fileManager, &QAction::triggered, this, [this]() {
2276         d->slotOpenFileManager();
2277     });
2278 
2279     action = new QAction(i18n("Properties"), this);
2280     d->m_actionCollection->addAction(QStringLiteral("properties"), action);
2281     action->setIcon(QIcon::fromTheme(QStringLiteral("document-properties")));
2282     action->setShortcut(Qt::ALT | Qt::Key_Return);
2283     connect(action, &QAction::triggered, this, [this]() {
2284         d->slotProperties();
2285     });
2286 
2287     // the view menu actions
2288     KActionMenu *viewMenu = new KActionMenu(i18n("&View"), this);
2289     d->m_actionCollection->addAction(QStringLiteral("view menu"), viewMenu);
2290     viewMenu->addAction(shortAction);
2291     viewMenu->addAction(detailedAction);
2292     // Comment following lines to hide the extra two modes
2293     viewMenu->addAction(treeAction);
2294     viewMenu->addAction(detailedTreeAction);
2295     // TODO: QAbstractItemView does not offer an action collection. Provide
2296     // an interface to add a custom action collection.
2297 
2298     d->m_itemActions = new KFileItemActions(this);
2299 
2300     d->m_newFileMenu = new KNewFileMenu(d->m_actionCollection, QStringLiteral("new"), this);
2301     connect(d->m_newFileMenu, &KNewFileMenu::directoryCreated, this, [this](const QUrl &url) {
2302         d->slotDirectoryCreated(url);
2303     });
2304     connect(d->m_newFileMenu, &KNewFileMenu::selectExistingDir, this, [this](const QUrl &url) {
2305         setCurrentItem(url);
2306     });
2307 
2308     d->m_actionCollection->addAssociatedWidget(this);
2309     const QList<QAction *> list = d->m_actionCollection->actions();
2310     for (QAction *action : list) {
2311         action->setShortcutContext(Qt::WidgetWithChildrenShortcut);
2312     }
2313 }
2314 
setupMenu()2315 void KDirOperator::setupMenu()
2316 {
2317     setupMenu(SortActions | ViewActions | FileActions);
2318 }
2319 
setupMenu(int whichActions)2320 void KDirOperator::setupMenu(int whichActions)
2321 {
2322     // first fill the submenus (sort and view)
2323     KActionMenu *sortMenu = static_cast<KActionMenu *>(d->m_actionCollection->action(QStringLiteral("sorting menu")));
2324     sortMenu->menu()->clear();
2325     sortMenu->addAction(d->m_actionCollection->action(QStringLiteral("by name")));
2326     sortMenu->addAction(d->m_actionCollection->action(QStringLiteral("by size")));
2327     sortMenu->addAction(d->m_actionCollection->action(QStringLiteral("by date")));
2328     sortMenu->addAction(d->m_actionCollection->action(QStringLiteral("by type")));
2329     sortMenu->addSeparator();
2330     sortMenu->addAction(d->m_actionCollection->action(QStringLiteral("ascending")));
2331     sortMenu->addAction(d->m_actionCollection->action(QStringLiteral("descending")));
2332     sortMenu->addSeparator();
2333     sortMenu->addAction(d->m_actionCollection->action(QStringLiteral("dirs first")));
2334 
2335     // now plug everything into the popupmenu
2336     d->m_actionMenu->menu()->clear();
2337     if (whichActions & NavActions) {
2338         d->m_actionMenu->addAction(d->m_actionCollection->action(QStringLiteral("up")));
2339         d->m_actionMenu->addAction(d->m_actionCollection->action(QStringLiteral("back")));
2340         d->m_actionMenu->addAction(d->m_actionCollection->action(QStringLiteral("forward")));
2341         d->m_actionMenu->addAction(d->m_actionCollection->action(QStringLiteral("home")));
2342         d->m_actionMenu->addSeparator();
2343     }
2344 
2345     if (whichActions & FileActions) {
2346         d->m_actionMenu->addAction(d->m_actionCollection->action(QStringLiteral("new")));
2347 
2348         d->m_actionMenu->addAction(d->m_actionCollection->action(QStringLiteral("rename")));
2349         d->m_actionCollection->action(QStringLiteral("rename"))->setEnabled(KProtocolManager::supportsMoving(d->m_currUrl));
2350 
2351         if (d->m_currUrl.isLocalFile() && !(QApplication::keyboardModifiers() & Qt::ShiftModifier)) {
2352             d->m_actionMenu->addAction(d->m_actionCollection->action(QStringLiteral("trash")));
2353         }
2354         KConfigGroup cg(KSharedConfig::openConfig(), QStringLiteral("KDE"));
2355         const bool del = !d->m_currUrl.isLocalFile() || (QApplication::keyboardModifiers() & Qt::ShiftModifier) || cg.readEntry("ShowDeleteCommand", false);
2356         if (del) {
2357             d->m_actionMenu->addAction(d->m_actionCollection->action(QStringLiteral("delete")));
2358         }
2359         d->m_actionMenu->addSeparator();
2360     }
2361 
2362     if (whichActions & SortActions) {
2363         d->m_actionMenu->addAction(sortMenu);
2364         if (!(whichActions & ViewActions)) {
2365             d->m_actionMenu->addSeparator();
2366         }
2367     }
2368 
2369     if (whichActions & ViewActions) {
2370         d->m_actionMenu->addAction(d->m_actionCollection->action(QStringLiteral("view menu")));
2371         d->m_actionMenu->addAction(d->m_actionCollection->action(QStringLiteral("reload")));
2372         d->m_actionMenu->addSeparator();
2373     }
2374 
2375     if (whichActions & FileActions) {
2376         d->m_actionMenu->addAction(d->m_actionCollection->action(QStringLiteral("file manager")));
2377         d->m_actionMenu->addAction(d->m_actionCollection->action(QStringLiteral("properties")));
2378     }
2379 }
2380 
updateSortActions()2381 void KDirOperator::updateSortActions()
2382 {
2383     QAction *ascending = d->m_actionCollection->action(QStringLiteral("ascending"));
2384     QAction *descending = d->m_actionCollection->action(QStringLiteral("descending"));
2385 
2386     if (KFile::isSortByName(d->m_sorting)) {
2387         d->m_actionCollection->action(QStringLiteral("by name"))->setChecked(true);
2388         descending->setText(i18nc("Sort descending", "Z-A"));
2389         ascending->setText(i18nc("Sort ascending", "A-Z"));
2390     } else if (KFile::isSortByDate(d->m_sorting)) {
2391         d->m_actionCollection->action(QStringLiteral("by date"))->setChecked(true);
2392         descending->setText(i18nc("Sort descending", "Newest First"));
2393         ascending->setText(i18nc("Sort ascending", "Oldest First"));
2394     } else if (KFile::isSortBySize(d->m_sorting)) {
2395         d->m_actionCollection->action(QStringLiteral("by size"))->setChecked(true);
2396         descending->setText(i18nc("Sort descending", "Largest First"));
2397         ascending->setText(i18nc("Sort ascending", "Smallest First"));
2398     } else if (KFile::isSortByType(d->m_sorting)) {
2399         d->m_actionCollection->action(QStringLiteral("by type"))->setChecked(true);
2400         descending->setText(i18nc("Sort descending", "Z-A"));
2401         ascending->setText(i18nc("Sort ascending", "A-Z"));
2402     }
2403     ascending->setChecked(!(d->m_sorting & QDir::Reversed));
2404     descending->setChecked(d->m_sorting & QDir::Reversed);
2405     d->m_actionCollection->action(QStringLiteral("dirs first"))->setChecked(d->m_sorting & QDir::DirsFirst);
2406 }
2407 
updateViewActions()2408 void KDirOperator::updateViewActions()
2409 {
2410     KFile::FileView fv = static_cast<KFile::FileView>(d->m_viewKind);
2411 
2412     // QAction *separateDirs = d->actionCollection->action("separate dirs");
2413     // separateDirs->setChecked(KFile::isSeparateDirs(fv) &&
2414     //                         separateDirs->isEnabled());
2415 
2416     d->m_actionCollection->action(QStringLiteral("short view"))->setChecked(KFile::isSimpleView(fv));
2417     d->m_actionCollection->action(QStringLiteral("detailed view"))->setChecked(KFile::isDetailView(fv));
2418     d->m_actionCollection->action(QStringLiteral("tree view"))->setChecked(KFile::isTreeView(fv));
2419     d->m_actionCollection->action(QStringLiteral("detailed tree view"))->setChecked(KFile::isDetailTreeView(fv));
2420 
2421     // dolphin style views
2422     d->m_actionCollection->action(QStringLiteral("icons view"))->setChecked(KFile::isSimpleView(fv) && d->m_decorationPosition == QStyleOptionViewItem::Top);
2423     d->m_actionCollection->action(QStringLiteral("compact view"))->setChecked(KFile::isSimpleView(fv) && d->m_decorationPosition == QStyleOptionViewItem::Left);
2424     d->m_actionCollection->action(QStringLiteral("details view"))->setChecked(KFile::isDetailTreeView(fv) || KFile::isDetailView(fv));
2425 }
2426 
readConfig(const KConfigGroup & configGroup)2427 void KDirOperator::readConfig(const KConfigGroup &configGroup)
2428 {
2429     d->m_defaultView = 0;
2430     QString viewStyle = configGroup.readEntry("View Style", "DetailTree");
2431     if (viewStyle == QLatin1String("Detail")) {
2432         d->m_defaultView |= KFile::Detail;
2433     } else if (viewStyle == QLatin1String("Tree")) {
2434         d->m_defaultView |= KFile::Tree;
2435     } else if (viewStyle == QLatin1String("DetailTree")) {
2436         d->m_defaultView |= KFile::DetailTree;
2437     } else {
2438         d->m_defaultView |= KFile::Simple;
2439     }
2440     // if (configGroup.readEntry(QLatin1String("Separate Directories"),
2441     //                          DefaultMixDirsAndFiles)) {
2442     //    d->defaultView |= KFile::SeparateDirs;
2443     //}
2444     if (configGroup.readEntry(QStringLiteral("Show Preview"), false)) {
2445         d->m_defaultView |= KFile::PreviewContents;
2446     }
2447 
2448     d->m_previewWidth = configGroup.readEntry(QStringLiteral("Preview Width"), 100);
2449 
2450     if (configGroup.readEntry(QStringLiteral("Show hidden files"), DefaultShowHidden)) {
2451         d->m_actionCollection->action(QStringLiteral("show hidden"))->setChecked(true);
2452         d->m_dirLister->setShowingDotFiles(true);
2453     }
2454 
2455     if (configGroup.readEntry(QStringLiteral("Allow Expansion"), DefaultShowHidden)) {
2456         d->m_actionCollection->action(QStringLiteral("allow expansion"))->setChecked(true);
2457     }
2458 
2459     QDir::SortFlags sorting = QDir::Name;
2460     if (configGroup.readEntry(QStringLiteral("Sort directories first"), DefaultDirsFirst)) {
2461         sorting |= QDir::DirsFirst;
2462     }
2463     QString name = QStringLiteral("Name");
2464     QString sortBy = configGroup.readEntry(QStringLiteral("Sort by"), name);
2465     if (sortBy == name) {
2466         sorting |= QDir::Name;
2467     } else if (sortBy == QLatin1String("Size")) {
2468         sorting |= QDir::Size;
2469     } else if (sortBy == QLatin1String("Date")) {
2470         sorting |= QDir::Time;
2471     } else if (sortBy == QLatin1String("Type")) {
2472         sorting |= QDir::Type;
2473     }
2474     if (configGroup.readEntry(QStringLiteral("Sort reversed"), DefaultSortReversed)) {
2475         sorting |= QDir::Reversed;
2476     }
2477     d->updateSorting(sorting);
2478 
2479     if (d->m_inlinePreviewState == KDirOperatorPrivate::NotForced) {
2480         d->m_showPreviews = configGroup.readEntry(QStringLiteral("Show Inline Previews"), true);
2481     }
2482     QStyleOptionViewItem::Position pos =
2483         (QStyleOptionViewItem::Position)configGroup.readEntry(QStringLiteral("Decoration position"), (int)QStyleOptionViewItem::Top);
2484     setDecorationPosition(pos);
2485 }
2486 
writeConfig(KConfigGroup & configGroup)2487 void KDirOperator::writeConfig(KConfigGroup &configGroup)
2488 {
2489     QString sortBy = QStringLiteral("Name");
2490     if (KFile::isSortBySize(d->m_sorting)) {
2491         sortBy = QStringLiteral("Size");
2492     } else if (KFile::isSortByDate(d->m_sorting)) {
2493         sortBy = QStringLiteral("Date");
2494     } else if (KFile::isSortByType(d->m_sorting)) {
2495         sortBy = QStringLiteral("Type");
2496     }
2497 
2498     configGroup.writeEntry(QStringLiteral("Sort by"), sortBy);
2499 
2500     configGroup.writeEntry(QStringLiteral("Sort reversed"), d->m_actionCollection->action(QStringLiteral("descending"))->isChecked());
2501 
2502     configGroup.writeEntry(QStringLiteral("Sort directories first"), d->m_actionCollection->action(QStringLiteral("dirs first"))->isChecked());
2503 
2504     // don't save the preview when an application specific preview is in use.
2505     bool appSpecificPreview = false;
2506     if (d->m_preview) {
2507         KFileMetaPreview *tmp = dynamic_cast<KFileMetaPreview *>(d->m_preview);
2508         appSpecificPreview = (tmp == nullptr);
2509     }
2510 
2511     if (!appSpecificPreview) {
2512         KToggleAction *previewAction = static_cast<KToggleAction *>(d->m_actionCollection->action(QStringLiteral("preview")));
2513         if (previewAction->isEnabled()) {
2514             bool hasPreview = previewAction->isChecked();
2515             configGroup.writeEntry(QStringLiteral("Show Preview"), hasPreview);
2516 
2517             if (hasPreview) {
2518                 // remember the width of the preview widget
2519                 QList<int> sizes = d->m_splitter->sizes();
2520                 Q_ASSERT(sizes.count() == 2);
2521                 configGroup.writeEntry(QStringLiteral("Preview Width"), sizes[1]);
2522             }
2523         }
2524     }
2525 
2526     configGroup.writeEntry(QStringLiteral("Show hidden files"), d->m_actionCollection->action(QStringLiteral("show hidden"))->isChecked());
2527 
2528     configGroup.writeEntry(QStringLiteral("Allow Expansion"), d->m_actionCollection->action(QStringLiteral("allow expansion"))->isChecked());
2529 
2530     KFile::FileView fv = static_cast<KFile::FileView>(d->m_viewKind);
2531     QString style;
2532     if (KFile::isDetailView(fv)) {
2533         style = QStringLiteral("Detail");
2534     } else if (KFile::isSimpleView(fv)) {
2535         style = QStringLiteral("Simple");
2536     } else if (KFile::isTreeView(fv)) {
2537         style = QStringLiteral("Tree");
2538     } else if (KFile::isDetailTreeView(fv)) {
2539         style = QStringLiteral("DetailTree");
2540     }
2541     configGroup.writeEntry(QStringLiteral("View Style"), style);
2542 
2543     if (d->m_inlinePreviewState == KDirOperatorPrivate::NotForced) {
2544         configGroup.writeEntry(QStringLiteral("Show Inline Previews"), d->m_showPreviews);
2545         d->writeIconZoomSettingsIfNeeded();
2546     }
2547 
2548     configGroup.writeEntry(QStringLiteral("Decoration position"), (int)d->m_decorationPosition);
2549 }
2550 
writeIconZoomSettingsIfNeeded()2551 void KDirOperatorPrivate::writeIconZoomSettingsIfNeeded()
2552 {
2553     // must match behavior of iconSizeForViewType
2554     if (m_configGroup && m_itemView) {
2555         ZoomSettingsForView zoomSettings = zoomSettingsForView();
2556         m_configGroup->writeEntry(zoomSettings.name, m_iconSize);
2557     }
2558 }
2559 
resizeEvent(QResizeEvent *)2560 void KDirOperator::resizeEvent(QResizeEvent *)
2561 {
2562     // resize the m_splitter and assure that the width of
2563     // the preview widget is restored
2564     QList<int> sizes = d->m_splitter->sizes();
2565     const bool hasPreview = (sizes.count() == 2);
2566 
2567     d->m_splitter->resize(size());
2568     sizes = d->m_splitter->sizes();
2569 
2570     const bool restorePreviewWidth = hasPreview && (d->m_previewWidth != sizes[1]);
2571     if (restorePreviewWidth) {
2572         const int availableWidth = sizes[0] + sizes[1];
2573         sizes[0] = availableWidth - d->m_previewWidth;
2574         sizes[1] = d->m_previewWidth;
2575         d->m_splitter->setSizes(sizes);
2576     }
2577     if (hasPreview) {
2578         d->m_previewWidth = sizes[1];
2579     }
2580 
2581     if (d->m_progressBar->parent() == this) {
2582         // Might be reparented into a statusbar
2583         const int frameWidth = style()->pixelMetric(QStyle::PM_DefaultFrameWidth);
2584         d->m_progressBar->move(frameWidth, height() - d->m_progressBar->height() - frameWidth);
2585     }
2586 }
2587 
keyPressEvent(QKeyEvent * e)2588 void KDirOperator::keyPressEvent(QKeyEvent *e) // TODO KF6 remove
2589 {
2590     QWidget::keyPressEvent(e);
2591 }
2592 
setOnlyDoubleClickSelectsFiles(bool enable)2593 void KDirOperator::setOnlyDoubleClickSelectsFiles(bool enable)
2594 {
2595     d->m_onlyDoubleClickSelectsFiles = enable;
2596     // TODO: port to QAbstractItemModel
2597     // if (d->itemView != 0) {
2598     //    d->itemView->setOnlyDoubleClickSelectsFiles(enable);
2599     //}
2600 }
2601 
onlyDoubleClickSelectsFiles() const2602 bool KDirOperator::onlyDoubleClickSelectsFiles() const
2603 {
2604     return d->m_onlyDoubleClickSelectsFiles;
2605 }
2606 
setFollowNewDirectories(bool enable)2607 void KDirOperator::setFollowNewDirectories(bool enable)
2608 {
2609     d->m_followNewDirectories = enable;
2610 }
2611 
followNewDirectories() const2612 bool KDirOperator::followNewDirectories() const
2613 {
2614     return d->m_followNewDirectories;
2615 }
2616 
setFollowSelectedDirectories(bool enable)2617 void KDirOperator::setFollowSelectedDirectories(bool enable)
2618 {
2619     d->m_followSelectedDirectories = enable;
2620 }
2621 
followSelectedDirectories() const2622 bool KDirOperator::followSelectedDirectories() const
2623 {
2624     return d->m_followSelectedDirectories;
2625 }
2626 
slotStarted()2627 void KDirOperatorPrivate::slotStarted()
2628 {
2629     m_progressBar->setValue(0);
2630     // delay showing the progressbar for one second
2631     m_progressDelayTimer->setSingleShot(true);
2632     m_progressDelayTimer->start(1000);
2633 }
2634 
slotShowProgress()2635 void KDirOperatorPrivate::slotShowProgress()
2636 {
2637     m_progressBar->raise();
2638     m_progressBar->show();
2639 }
2640 
slotProgress(int percent)2641 void KDirOperatorPrivate::slotProgress(int percent)
2642 {
2643     m_progressBar->setValue(percent);
2644 }
2645 
slotIOFinished()2646 void KDirOperatorPrivate::slotIOFinished()
2647 {
2648     m_progressDelayTimer->stop();
2649     slotProgress(100);
2650     m_progressBar->hide();
2651     Q_EMIT q->finishedLoading();
2652     q->resetCursor();
2653 
2654     if (m_preview) {
2655         m_preview->clearPreview();
2656     }
2657 
2658     // m_lastUrl can be empty when e.g. kfilewidget is first opened
2659     if (!m_lastUrl.isEmpty() && m_dirHighlighting) {
2660         q->setCurrentItem(m_lastUrl);
2661     }
2662 }
2663 
slotCanceled()2664 void KDirOperatorPrivate::slotCanceled()
2665 {
2666     Q_EMIT q->finishedLoading();
2667     q->resetCursor();
2668 }
2669 
progressBar() const2670 QProgressBar *KDirOperator::progressBar() const
2671 {
2672     return d->m_progressBar;
2673 }
2674 
clearHistory()2675 void KDirOperator::clearHistory()
2676 {
2677     qDeleteAll(d->m_backStack);
2678     d->m_backStack.clear();
2679     d->m_actionCollection->action(QStringLiteral("back"))->setEnabled(false);
2680 
2681     qDeleteAll(d->m_forwardStack);
2682     d->m_forwardStack.clear();
2683     d->m_actionCollection->action(QStringLiteral("forward"))->setEnabled(false);
2684 }
2685 
setEnableDirHighlighting(bool enable)2686 void KDirOperator::setEnableDirHighlighting(bool enable)
2687 {
2688     d->m_dirHighlighting = enable;
2689 }
2690 
dirHighlighting() const2691 bool KDirOperator::dirHighlighting() const
2692 {
2693     return d->m_dirHighlighting;
2694 }
2695 
dirOnlyMode() const2696 bool KDirOperator::dirOnlyMode() const
2697 {
2698     return dirOnlyMode(d->m_mode);
2699 }
2700 
dirOnlyMode(uint mode)2701 bool KDirOperator::dirOnlyMode(uint mode)
2702 {
2703     return ((mode & KFile::Directory) && (mode & (KFile::File | KFile::Files)) == 0);
2704 }
2705 
slotProperties()2706 void KDirOperatorPrivate::slotProperties()
2707 {
2708     if (m_itemView == nullptr) {
2709         return;
2710     }
2711 
2712     const KFileItemList list = q->selectedItems();
2713     if (!list.isEmpty()) {
2714         auto *dialog = new KPropertiesDialog(list, q);
2715         dialog->setAttribute(Qt::WA_DeleteOnClose);
2716         dialog->setModal(true);
2717         dialog->show();
2718     }
2719 }
2720 
slotActivated(const QModelIndex & index)2721 void KDirOperatorPrivate::slotActivated(const QModelIndex &index)
2722 {
2723     const QModelIndex dirIndex = m_proxyModel->mapToSource(index);
2724     KFileItem item = m_dirModel->itemForIndex(dirIndex);
2725 
2726     const Qt::KeyboardModifiers modifiers = QApplication::keyboardModifiers();
2727     if (item.isNull() || (modifiers & Qt::ShiftModifier) || (modifiers & Qt::ControlModifier)) {
2728         return;
2729     }
2730 
2731     if (item.isDir()) {
2732         // Only allow disabling following selected directories on Tree and
2733         // DetailTree views as selected directories in these views still expand
2734         // when selected. For other views, disabling following selected
2735         // directories would make selecting a directory a noop which is
2736         // unintuitive.
2737         if (m_followSelectedDirectories || (m_viewKind != KFile::Tree && m_viewKind != KFile::DetailTree)) {
2738             q->selectDir(item);
2739         }
2740     } else {
2741         q->selectFile(item);
2742     }
2743 }
2744 
slotSelectionChanged()2745 void KDirOperatorPrivate::slotSelectionChanged()
2746 {
2747     if (m_itemView == nullptr) {
2748         return;
2749     }
2750 
2751     // In the multiselection mode each selection change is indicated by
2752     // emitting a null item. Also when the selection has been cleared, a
2753     // null item must be emitted.
2754     const bool multiSelectionMode = (m_itemView->selectionMode() == QAbstractItemView::ExtendedSelection);
2755     const bool hasSelection = m_itemView->selectionModel()->hasSelection();
2756     if (multiSelectionMode || !hasSelection) {
2757         KFileItem nullItem;
2758         q->highlightFile(nullItem);
2759     } else {
2760         const KFileItem selectedItem = q->selectedItems().constFirst();
2761         q->highlightFile(selectedItem);
2762     }
2763 }
2764 
openContextMenu(const QPoint & pos)2765 void KDirOperatorPrivate::openContextMenu(const QPoint &pos)
2766 {
2767     const QModelIndex proxyIndex = m_itemView->indexAt(pos);
2768     const QModelIndex dirIndex = m_proxyModel->mapToSource(proxyIndex);
2769     KFileItem item = m_dirModel->itemForIndex(dirIndex);
2770 
2771     if (item.isNull()) {
2772         return;
2773     }
2774 
2775     q->activatedMenu(item, QCursor::pos());
2776 }
2777 
triggerPreview(const QModelIndex & index)2778 void KDirOperatorPrivate::triggerPreview(const QModelIndex &index)
2779 {
2780     if ((m_preview != nullptr && !m_preview->isHidden()) && index.isValid() && (index.column() == KDirModel::Name)) {
2781         const QModelIndex dirIndex = m_proxyModel->mapToSource(index);
2782         const KFileItem item = m_dirModel->itemForIndex(dirIndex);
2783 
2784         if (item.isNull()) {
2785             return;
2786         }
2787 
2788         if (!item.isDir()) {
2789             m_previewUrl = item.url();
2790             showPreview();
2791         } else {
2792             m_preview->clearPreview();
2793         }
2794     }
2795 }
2796 
showPreview()2797 void KDirOperatorPrivate::showPreview()
2798 {
2799     if (m_preview != nullptr) {
2800         m_preview->showPreview(m_previewUrl);
2801     }
2802 }
2803 
slotSplitterMoved(int,int)2804 void KDirOperatorPrivate::slotSplitterMoved(int, int)
2805 {
2806     const QList<int> sizes = m_splitter->sizes();
2807     if (sizes.count() == 2) {
2808         // remember the width of the preview widget (see KDirOperator::resizeEvent())
2809         m_previewWidth = sizes[1];
2810     }
2811 }
2812 
assureVisibleSelection()2813 void KDirOperatorPrivate::assureVisibleSelection()
2814 {
2815     if (m_itemView == nullptr) {
2816         return;
2817     }
2818 
2819     QItemSelectionModel *selModel = m_itemView->selectionModel();
2820     if (selModel->hasSelection()) {
2821         const QModelIndex index = selModel->currentIndex();
2822         m_itemView->scrollTo(index, QAbstractItemView::EnsureVisible);
2823         triggerPreview(index);
2824     }
2825 }
2826 
synchronizeSortingState(int logicalIndex,Qt::SortOrder order)2827 void KDirOperatorPrivate::synchronizeSortingState(int logicalIndex, Qt::SortOrder order)
2828 {
2829     QDir::SortFlags newSort = m_sorting & ~(QDirSortMask | QDir::Reversed);
2830 
2831     switch (logicalIndex) {
2832     case KDirModel::Name:
2833         newSort |= QDir::Name;
2834         break;
2835     case KDirModel::Size:
2836         newSort |= QDir::Size;
2837         break;
2838     case KDirModel::ModifiedTime:
2839         newSort |= QDir::Time;
2840         break;
2841     case KDirModel::Type:
2842         newSort |= QDir::Type;
2843         break;
2844     default:
2845         Q_ASSERT(false);
2846     }
2847 
2848     if (order == Qt::DescendingOrder) {
2849         newSort |= QDir::Reversed;
2850     }
2851 
2852     updateSorting(newSort);
2853 
2854     QMetaObject::invokeMethod(
2855         q,
2856         [this]() {
2857             assureVisibleSelection();
2858         },
2859         Qt::QueuedConnection);
2860 }
2861 
slotChangeDecorationPosition()2862 void KDirOperatorPrivate::slotChangeDecorationPosition()
2863 {
2864     if (!m_itemView) {
2865         return;
2866     }
2867 
2868     KDirOperatorIconView *view = qobject_cast<KDirOperatorIconView *>(m_itemView);
2869 
2870     if (!view) {
2871         return;
2872     }
2873 
2874     const bool leftChecked = m_actionCollection->action(QStringLiteral("decorationAtLeft"))->isChecked();
2875 
2876     if (leftChecked) {
2877         view->setDecorationPosition(QStyleOptionViewItem::Left);
2878     } else {
2879         view->setDecorationPosition(QStyleOptionViewItem::Top);
2880     }
2881 
2882     m_itemView->update();
2883 }
2884 
slotExpandToUrl(const QModelIndex & index)2885 void KDirOperatorPrivate::slotExpandToUrl(const QModelIndex &index)
2886 {
2887     QTreeView *treeView = qobject_cast<QTreeView *>(m_itemView);
2888 
2889     if (!treeView) {
2890         return;
2891     }
2892 
2893     const KFileItem item = m_dirModel->itemForIndex(index);
2894 
2895     if (item.isNull()) {
2896         return;
2897     }
2898 
2899     if (!item.isDir()) {
2900         const QModelIndex proxyIndex = m_proxyModel->mapFromSource(index);
2901 
2902         QList<QUrl>::Iterator it = m_itemsToBeSetAsCurrent.begin();
2903         while (it != m_itemsToBeSetAsCurrent.end()) {
2904             const QUrl url = *it;
2905             if (url.matches(item.url(), QUrl::StripTrailingSlash) || url.isParentOf(item.url())) {
2906                 const KFileItem _item = m_dirLister->findByUrl(url);
2907                 if (!_item.isNull() && _item.isDir()) {
2908                     const QModelIndex _index = m_dirModel->indexForItem(_item);
2909                     const QModelIndex _proxyIndex = m_proxyModel->mapFromSource(_index);
2910                     treeView->expand(_proxyIndex);
2911 
2912                     // if we have expanded the last parent of this item, select it
2913                     if (item.url().adjusted(QUrl::RemoveFilename | QUrl::StripTrailingSlash) == url.adjusted(QUrl::StripTrailingSlash)) {
2914                         treeView->selectionModel()->select(proxyIndex, QItemSelectionModel::Select);
2915                     }
2916                 }
2917                 it = m_itemsToBeSetAsCurrent.erase(it);
2918             } else {
2919                 ++it;
2920             }
2921         }
2922     } else if (!m_itemsToBeSetAsCurrent.contains(item.url())) {
2923         m_itemsToBeSetAsCurrent << item.url();
2924     }
2925 }
2926 
slotItemsChanged()2927 void KDirOperatorPrivate::slotItemsChanged()
2928 {
2929     m_completeListDirty = true;
2930 }
2931 
iconSizeForViewType(QAbstractItemView * itemView) const2932 int KDirOperatorPrivate::iconSizeForViewType(QAbstractItemView *itemView) const
2933 {
2934     // must match behavior of writeIconZoomSettingsIfNeeded
2935     if (!itemView || !m_configGroup) {
2936         return 0;
2937     }
2938 
2939     ZoomSettingsForView zoomSettings = zoomSettingsForView();
2940     return m_configGroup->readEntry(zoomSettings.name, zoomSettings.defaultValue);
2941 }
2942 
zoomSettingsForView() const2943 KDirOperatorPrivate::ZoomSettingsForView KDirOperatorPrivate::zoomSettingsForView() const
2944 {
2945     KFile::FileView fv = static_cast<KFile::FileView>(m_viewKind);
2946 
2947     KSharedConfigPtr config = KSharedConfig::openConfig();
2948     if (KFile::isSimpleView(fv)) {
2949         KConfigGroup cg(config, "DesktopIcons");
2950         const int desktopIconSize = cg.readEntry("Size", static_cast<int>(KIconLoader::SizeHuge));
2951         if (m_decorationPosition == QStyleOptionViewItem::Top) {
2952             // Simple view decoration above, aka Icons View
2953             return {QStringLiteral("iconViewIconSize"), desktopIconSize};
2954         } else {
2955             // Simple view decoration left, aka compact view
2956             return {QStringLiteral("listViewIconSize"), desktopIconSize};
2957         }
2958     }
2959 
2960     KConfigGroup cg(config, "SmallIcons");
2961     const int smallIconSize = cg.readEntry("Size", static_cast<int>(KIconLoader::SizeSmall));
2962     if (KFile::isTreeView(fv)) {
2963         return {QStringLiteral("treeViewIconSize"), smallIconSize};
2964     } else {
2965         // DetailView and DetailTreeView
2966         return {QStringLiteral("detailViewIconSize"), smallIconSize};
2967     }
2968 }
2969 
setViewConfig(KConfigGroup & configGroup)2970 void KDirOperator::setViewConfig(KConfigGroup &configGroup)
2971 {
2972     delete d->m_configGroup;
2973     d->m_configGroup = new KConfigGroup(configGroup);
2974 }
2975 
viewConfigGroup() const2976 KConfigGroup *KDirOperator::viewConfigGroup() const
2977 {
2978     return d->m_configGroup;
2979 }
2980 
setShowHiddenFiles(bool s)2981 void KDirOperator::setShowHiddenFiles(bool s)
2982 {
2983     d->m_actionCollection->action(QStringLiteral("show hidden"))->setChecked(s);
2984 }
2985 
showHiddenFiles() const2986 bool KDirOperator::showHiddenFiles() const
2987 {
2988     return d->m_actionCollection->action(QStringLiteral("show hidden"))->isChecked();
2989 }
2990 
decorationPosition() const2991 QStyleOptionViewItem::Position KDirOperator::decorationPosition() const
2992 {
2993     return d->m_decorationPosition;
2994 }
2995 
setDecorationPosition(QStyleOptionViewItem::Position position)2996 void KDirOperator::setDecorationPosition(QStyleOptionViewItem::Position position)
2997 {
2998     d->m_decorationPosition = position;
2999     const bool decorationAtLeft = d->m_decorationPosition == QStyleOptionViewItem::Left;
3000     d->m_actionCollection->action(QStringLiteral("decorationAtLeft"))->setChecked(decorationAtLeft);
3001     d->m_actionCollection->action(QStringLiteral("decorationAtTop"))->setChecked(!decorationAtLeft);
3002 }
3003 
isReadable(const QUrl & url)3004 bool KDirOperatorPrivate::isReadable(const QUrl &url)
3005 {
3006     if (!url.isLocalFile()) {
3007         return true; // what else can we say?
3008     }
3009     return QDir(url.toLocalFile()).isReadable();
3010 }
3011 
slotDirectoryCreated(const QUrl & url)3012 void KDirOperatorPrivate::slotDirectoryCreated(const QUrl &url)
3013 {
3014     if (m_followNewDirectories) {
3015         q->setUrl(url, true);
3016     }
3017 }
3018 
setSupportedSchemes(const QStringList & schemes)3019 void KDirOperator::setSupportedSchemes(const QStringList &schemes)
3020 {
3021     d->m_supportedSchemes = schemes;
3022     rereadDir();
3023 }
3024 
supportedSchemes() const3025 QStringList KDirOperator::supportedSchemes() const
3026 {
3027     return d->m_supportedSchemes;
3028 }
3029 
3030 #include "moc_kdiroperator.cpp"
3031