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