KIO

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

KDE's Doxygen guidelines are available online.