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

KDE's Doxygen guidelines are available online.