File indexing completed on 2024-04-21 03:55:17

0001 /*
0002     This file is part of the KDE libraries
0003     SPDX-FileCopyrightText: 1999, 2000 Stephan Kulow <coolo@kde.org>
0004     SPDX-FileCopyrightText: 1999, 2000, 2001, 2002, 2003 Carsten Pfeiffer <pfeiffer@kde.org>
0005 
0006     SPDX-License-Identifier: LGPL-2.0-or-later
0007 */
0008 
0009 #include <config-kiofilewidgets.h>
0010 #include <defaults-kfile.h> // ConfigGroup, DefaultShowHidden, DefaultDirsFirst, DefaultSortReversed
0011 
0012 #include "../utils_p.h"
0013 
0014 #include "kdirmodel.h"
0015 #include "kdiroperator.h"
0016 #include "kdiroperatordetailview_p.h"
0017 #include "kdiroperatoriconview_p.h"
0018 #include "kdirsortfilterproxymodel.h"
0019 #include "kfileitem.h"
0020 #include "kfilemetapreview_p.h"
0021 #include "knewfilemenu.h"
0022 #include "kpreviewwidgetbase.h"
0023 #include "statjob.h"
0024 #include <KCompletion>
0025 #include <KConfigGroup>
0026 #include <KDirLister>
0027 #include <KFileItemActions>
0028 #include <KFileItemListProperties>
0029 #include <KIO/OpenFileManagerWindowJob>
0030 #include <KIO/RenameFileDialog>
0031 #include <KIconLoader>
0032 #include <KJobWidgets>
0033 #include <KLocalizedString>
0034 #include <KMessageBox>
0035 #include <KProtocolManager>
0036 #include <KSharedConfig>
0037 #include <KStandardAction>
0038 #include <KUrlMimeData>
0039 #include <kfileitemdelegate.h>
0040 #include <kfilepreviewgenerator.h>
0041 #include <kio/copyjob.h>
0042 #include <kio/deletejob.h>
0043 #include <kio/deleteortrashjob.h>
0044 #include <kio/jobuidelegate.h>
0045 #include <kio/previewjob.h>
0046 #include <kpropertiesdialog.h>
0047 
0048 #include <QActionGroup>
0049 #include <QApplication>
0050 #include <QDebug>
0051 #include <QHeaderView>
0052 #include <QListView>
0053 #include <QMenu>
0054 #include <QMimeDatabase>
0055 #include <QProgressBar>
0056 #include <QRegularExpression>
0057 #include <QScrollBar>
0058 #include <QSplitter>
0059 #include <QStack>
0060 #include <QTimer>
0061 #include <QWheelEvent>
0062 
0063 #include <memory>
0064 
0065 template class QHash<QString, KFileItem>;
0066 
0067 // QDir::SortByMask is not only undocumented, it also omits QDir::Type which  is another
0068 // sorting mode.
0069 static const int QDirSortMask = QDir::SortByMask | QDir::Type;
0070 
0071 class KDirOperatorPrivate
0072 {
0073 public:
0074     explicit KDirOperatorPrivate(KDirOperator *qq)
0075         : q(qq)
0076     {
0077         m_iconSize = static_cast<int>(KIconLoader::SizeSmall);
0078     }
0079 
0080     ~KDirOperatorPrivate();
0081 
0082     enum InlinePreviewState {
0083         ForcedToFalse = 0,
0084         ForcedToTrue,
0085         NotForced,
0086     };
0087 
0088     // private methods
0089     bool checkPreviewInternal() const;
0090     bool openUrl(const QUrl &url, KDirLister::OpenUrlFlags flags = KDirLister::NoFlags);
0091     int sortColumn() const;
0092     Qt::SortOrder sortOrder() const;
0093     void updateSorting(QDir::SortFlags sort);
0094 
0095     bool isReadable(const QUrl &url);
0096     bool isSchemeSupported(const QString &scheme) const;
0097 
0098     QPoint progressBarPos() const;
0099 
0100     KFile::FileView allViews();
0101 
0102     QMetaObject::Connection m_connection;
0103 
0104     // A pair to store zoom settings for view kinds
0105     struct ZoomSettingsForView {
0106         QString name;
0107         int defaultValue;
0108     };
0109 
0110     // private slots
0111     void slotDetailedView();
0112     void slotSimpleView();
0113     void slotTreeView();
0114     void slotDetailedTreeView();
0115     void slotIconsView();
0116     void slotCompactView();
0117     void slotDetailsView();
0118     void slotToggleHidden(bool);
0119     void slotToggleAllowExpansion(bool);
0120     void togglePreview(bool);
0121     void toggleInlinePreviews(bool);
0122     void slotOpenFileManager();
0123     void slotSortByName();
0124     void slotSortBySize();
0125     void slotSortByDate();
0126     void slotSortByType();
0127     void slotSortReversed(bool doReverse);
0128     void slotToggleDirsFirst();
0129     void slotToggleIconsView();
0130     void slotToggleCompactView();
0131     void slotToggleDetailsView();
0132     void slotToggleIgnoreCase();
0133     void slotStarted();
0134     void slotProgress(int);
0135     void slotShowProgress();
0136     void slotIOFinished();
0137     void slotCanceled();
0138     void slotRedirected(const QUrl &);
0139     void slotProperties();
0140     void slotActivated(const QModelIndex &);
0141     void slotSelectionChanged();
0142     void openContextMenu(const QPoint &);
0143     void triggerPreview(const QModelIndex &);
0144     void showPreview();
0145     void slotSplitterMoved(int, int);
0146     void assureVisibleSelection();
0147     void synchronizeSortingState(int, Qt::SortOrder);
0148     void slotChangeDecorationPosition();
0149     void slotExpandToUrl(const QModelIndex &);
0150     void slotItemsChanged();
0151     void slotDirectoryCreated(const QUrl &);
0152     void slotAskUserDeleteResult(bool allowDelete, const QList<QUrl> &urls, KIO::AskUserActionInterface::DeletionType deletionType, QWidget *parent);
0153 
0154     int iconSizeForViewType(QAbstractItemView *itemView) const;
0155     void writeIconZoomSettingsIfNeeded();
0156     ZoomSettingsForView zoomSettingsForView() const;
0157 
0158     QList<QAction *> insertOpenWithActions();
0159 
0160     // private members
0161     KDirOperator *const q;
0162     QStack<QUrl *> m_backStack; ///< Contains all URLs you can reach with the back button.
0163     QStack<QUrl *> m_forwardStack; ///< Contains all URLs you can reach with the forward button.
0164 
0165     QModelIndex m_lastHoveredIndex;
0166 
0167     KDirLister *m_dirLister = nullptr;
0168     QUrl m_currUrl;
0169 
0170     KCompletion m_completion;
0171     KCompletion m_dirCompletion;
0172     QDir::SortFlags m_sorting;
0173     QStyleOptionViewItem::Position m_decorationPosition = QStyleOptionViewItem::Left;
0174 
0175     QSplitter *m_splitter = nullptr;
0176 
0177     QAbstractItemView *m_itemView = nullptr;
0178     KDirModel *m_dirModel = nullptr;
0179     KDirSortFilterProxyModel *m_proxyModel = nullptr;
0180 
0181     KFileItemList m_pendingMimeTypes;
0182 
0183     // the enum KFile::FileView as an int
0184     int m_viewKind;
0185     int m_defaultView;
0186 
0187     KFile::Modes m_mode;
0188     QProgressBar *m_progressBar = nullptr;
0189 
0190     KPreviewWidgetBase *m_preview = nullptr;
0191     QUrl m_previewUrl;
0192     int m_previewWidth = 0;
0193 
0194     bool m_completeListDirty = false;
0195     bool m_followNewDirectories = true;
0196     bool m_followSelectedDirectories = true;
0197     bool m_onlyDoubleClickSelectsFiles = !qApp->style()->styleHint(QStyle::SH_ItemView_ActivateItemOnSingleClick);
0198 
0199     QUrl m_lastUrl; // used for highlighting a directory on back/cdUp
0200 
0201     QTimer *m_progressDelayTimer = nullptr;
0202     KActionMenu *m_actionMenu = nullptr;
0203     KActionCollection *m_actionCollection = nullptr;
0204     KNewFileMenu *m_newFileMenu = nullptr;
0205     KConfigGroup *m_configGroup = nullptr;
0206     KFilePreviewGenerator *m_previewGenerator = nullptr;
0207     KActionMenu *m_decorationMenu = nullptr;
0208     KToggleAction *m_leftAction = nullptr;
0209     KFileItemActions *m_itemActions = nullptr;
0210 
0211     int m_dropOptions = 0;
0212     int m_iconSize = 0;
0213     InlinePreviewState m_inlinePreviewState = NotForced;
0214     bool m_dirHighlighting = true;
0215     bool m_showPreviews = false;
0216     bool m_shouldFetchForItems = false;
0217     bool m_isSaving = false;
0218     bool m_showOpenWithActions = false;
0219 
0220     QList<QUrl> m_itemsToBeSetAsCurrent;
0221     QStringList m_supportedSchemes;
0222 
0223     QHash<KDirOperator::Action, QAction *> m_actions;
0224 };
0225 
0226 KDirOperatorPrivate::~KDirOperatorPrivate()
0227 {
0228     if (m_itemView) {
0229         // fix libc++ crash: its unique_ptr implementation has already set 'd' to null
0230         // and the event filter will get a QEvent::Leave event if we don't remove it.
0231         m_itemView->removeEventFilter(q);
0232         m_itemView->viewport()->removeEventFilter(q);
0233     }
0234 
0235     delete m_itemView;
0236     m_itemView = nullptr;
0237 
0238     // TODO:
0239     // if (configGroup) {
0240     //     itemView->writeConfig(configGroup);
0241     // }
0242 
0243     qDeleteAll(m_backStack);
0244     qDeleteAll(m_forwardStack);
0245 
0246     // The parent KDirOperator will delete these
0247     m_preview = nullptr;
0248     m_proxyModel = nullptr;
0249     m_dirModel = nullptr;
0250     m_progressDelayTimer = nullptr;
0251 
0252     m_dirLister = nullptr; // deleted by KDirModel
0253 
0254     delete m_configGroup;
0255     m_configGroup = nullptr;
0256 }
0257 
0258 QPoint KDirOperatorPrivate::progressBarPos() const
0259 {
0260     const int frameWidth = q->style()->pixelMetric(QStyle::PM_DefaultFrameWidth);
0261     return QPoint(frameWidth, q->height() - m_progressBar->height() - frameWidth);
0262 }
0263 
0264 KDirOperator::KDirOperator(const QUrl &_url, QWidget *parent)
0265     : QWidget(parent)
0266     , d(new KDirOperatorPrivate(this))
0267 {
0268     d->m_splitter = new QSplitter(this);
0269     d->m_splitter->setChildrenCollapsible(false);
0270     connect(d->m_splitter, &QSplitter::splitterMoved, this, [this](int pos, int index) {
0271         d->slotSplitterMoved(pos, index);
0272     });
0273 
0274     d->m_preview = nullptr;
0275 
0276     d->m_mode = KFile::File;
0277     d->m_viewKind = KFile::Simple;
0278 
0279     if (_url.isEmpty()) { // no dir specified -> current dir
0280         QString strPath = QDir::currentPath();
0281         strPath.append(QLatin1Char('/'));
0282         d->m_currUrl = QUrl::fromLocalFile(strPath);
0283     } else {
0284         d->m_currUrl = _url;
0285         if (d->m_currUrl.scheme().isEmpty()) {
0286             d->m_currUrl.setScheme(QStringLiteral("file"));
0287         }
0288 
0289         // make sure we have a trailing slash!
0290         Utils::appendSlashToPath(d->m_currUrl);
0291     }
0292 
0293     // We set the direction of this widget to LTR, since even on RTL desktops
0294     // viewing directory listings in RTL mode makes people's head explode.
0295     // Is this the correct place? Maybe it should be in some lower level widgets...?
0296     setLayoutDirection(Qt::LeftToRight);
0297     setDirLister(new KDirLister());
0298 
0299     connect(&d->m_completion, &KCompletion::match, this, &KDirOperator::slotCompletionMatch);
0300 
0301     d->m_progressBar = new QProgressBar(this);
0302     d->m_progressBar->setObjectName(QStringLiteral("d->m_progressBar"));
0303     d->m_progressBar->setFormat(i18nc("Loading bar percent value", "%p%"));
0304     d->m_progressBar->adjustSize();
0305     const int frameWidth = style()->pixelMetric(QStyle::PM_DefaultFrameWidth);
0306     d->m_progressBar->move(frameWidth, height() - d->m_progressBar->height() - frameWidth);
0307 
0308     d->m_progressDelayTimer = new QTimer(this);
0309     d->m_progressDelayTimer->setObjectName(QStringLiteral("d->m_progressBar delay timer"));
0310     connect(d->m_progressDelayTimer, &QTimer::timeout, this, [this]() {
0311         d->slotShowProgress();
0312     });
0313 
0314     d->m_completeListDirty = false;
0315 
0316     // action stuff
0317     setupActions();
0318     setupMenu();
0319 
0320     d->m_sorting = QDir::NoSort; // so updateSorting() doesn't think nothing has changed
0321     d->updateSorting(QDir::Name | QDir::DirsFirst);
0322 
0323     setFocusPolicy(Qt::WheelFocus);
0324     setAcceptDrops(true);
0325 }
0326 
0327 KDirOperator::~KDirOperator()
0328 {
0329     resetCursor();
0330     disconnect(d->m_dirLister, nullptr, this, nullptr);
0331 }
0332 
0333 void KDirOperator::setSorting(QDir::SortFlags spec)
0334 {
0335     d->updateSorting(spec);
0336 }
0337 
0338 QDir::SortFlags KDirOperator::sorting() const
0339 {
0340     return d->m_sorting;
0341 }
0342 
0343 bool KDirOperator::isRoot() const
0344 {
0345 #ifdef Q_OS_WIN
0346     if (url().isLocalFile()) {
0347         const QString path = url().toLocalFile();
0348         if (path.length() == 3) {
0349             return (path[0].isLetter() && path[1] == QLatin1Char(':') && path[2] == QLatin1Char('/'));
0350         }
0351         return false;
0352     } else
0353 #endif
0354         return url().path() == QLatin1String("/");
0355 }
0356 
0357 KDirLister *KDirOperator::dirLister() const
0358 {
0359     return d->m_dirLister;
0360 }
0361 
0362 void KDirOperator::resetCursor()
0363 {
0364     if (qApp) {
0365         QApplication::restoreOverrideCursor();
0366     }
0367     d->m_progressBar->hide();
0368 }
0369 
0370 void KDirOperator::sortByName()
0371 {
0372     d->updateSorting((d->m_sorting & ~QDirSortMask) | QDir::Name);
0373 }
0374 
0375 void KDirOperator::sortBySize()
0376 {
0377     d->updateSorting((d->m_sorting & ~QDirSortMask) | QDir::Size);
0378 }
0379 
0380 void KDirOperator::sortByDate()
0381 {
0382     d->updateSorting((d->m_sorting & ~QDirSortMask) | QDir::Time);
0383 }
0384 
0385 void KDirOperator::sortByType()
0386 {
0387     d->updateSorting((d->m_sorting & ~QDirSortMask) | QDir::Type);
0388 }
0389 
0390 void KDirOperator::sortReversed()
0391 {
0392     // toggle it, hence the inversion of current state
0393     d->slotSortReversed(!(d->m_sorting & QDir::Reversed));
0394 }
0395 
0396 void KDirOperator::toggleDirsFirst()
0397 {
0398     d->slotToggleDirsFirst();
0399 }
0400 
0401 void KDirOperator::toggleIgnoreCase()
0402 {
0403     if (d->m_proxyModel != nullptr) {
0404         Qt::CaseSensitivity cs = d->m_proxyModel->sortCaseSensitivity();
0405         cs = (cs == Qt::CaseSensitive) ? Qt::CaseInsensitive : Qt::CaseSensitive;
0406         d->m_proxyModel->setSortCaseSensitivity(cs);
0407     }
0408 }
0409 
0410 void KDirOperator::updateSelectionDependentActions()
0411 {
0412     const bool hasSelection = (d->m_itemView != nullptr) && d->m_itemView->selectionModel()->hasSelection();
0413     action(KDirOperator::Rename)->setEnabled(hasSelection);
0414     action(KDirOperator::Trash)->setEnabled(hasSelection);
0415     action(KDirOperator::Delete)->setEnabled(hasSelection);
0416     action(KDirOperator::Properties)->setEnabled(hasSelection);
0417 }
0418 
0419 void KDirOperator::setPreviewWidget(KPreviewWidgetBase *w)
0420 {
0421     const bool showPreview = (w != nullptr);
0422     if (showPreview) {
0423         d->m_viewKind = (d->m_viewKind | KFile::PreviewContents);
0424     } else {
0425         d->m_viewKind = (d->m_viewKind & ~KFile::PreviewContents);
0426     }
0427 
0428     delete d->m_preview;
0429     d->m_preview = w;
0430 
0431     if (w) {
0432         d->m_splitter->addWidget(w);
0433     }
0434 
0435     KToggleAction *previewAction = static_cast<KToggleAction *>(action(ShowPreviewPanel));
0436     previewAction->setEnabled(showPreview);
0437     previewAction->setChecked(showPreview);
0438     setViewMode(static_cast<KFile::FileView>(d->m_viewKind));
0439 }
0440 
0441 KFileItemList KDirOperator::selectedItems() const
0442 {
0443     KFileItemList itemList;
0444     if (d->m_itemView == nullptr) {
0445         return itemList;
0446     }
0447 
0448     const QItemSelection selection = d->m_proxyModel->mapSelectionToSource(d->m_itemView->selectionModel()->selection());
0449 
0450     const QModelIndexList indexList = selection.indexes();
0451     for (const QModelIndex &index : indexList) {
0452         KFileItem item = d->m_dirModel->itemForIndex(index);
0453         if (!item.isNull()) {
0454             itemList.append(item);
0455         }
0456     }
0457 
0458     return itemList;
0459 }
0460 
0461 bool KDirOperator::isSelected(const KFileItem &item) const
0462 {
0463     if ((item.isNull()) || (d->m_itemView == nullptr)) {
0464         return false;
0465     }
0466 
0467     const QModelIndex dirIndex = d->m_dirModel->indexForItem(item);
0468     const QModelIndex proxyIndex = d->m_proxyModel->mapFromSource(dirIndex);
0469     return d->m_itemView->selectionModel()->isSelected(proxyIndex);
0470 }
0471 
0472 int KDirOperator::numDirs() const
0473 {
0474     return (d->m_dirLister == nullptr) ? 0 : d->m_dirLister->directories().count();
0475 }
0476 
0477 int KDirOperator::numFiles() const
0478 {
0479     return (d->m_dirLister == nullptr) ? 0 : d->m_dirLister->items().count() - numDirs();
0480 }
0481 
0482 KCompletion *KDirOperator::completionObject() const
0483 {
0484     return const_cast<KCompletion *>(&d->m_completion);
0485 }
0486 
0487 KCompletion *KDirOperator::dirCompletionObject() const
0488 {
0489     return const_cast<KCompletion *>(&d->m_dirCompletion);
0490 }
0491 
0492 QAction *KDirOperator::action(KDirOperator::Action action) const
0493 {
0494     return d->m_actions[action];
0495 }
0496 
0497 QList<QAction *> KDirOperator::allActions() const
0498 {
0499     return d->m_actions.values();
0500 }
0501 
0502 KFile::FileView KDirOperatorPrivate::allViews()
0503 {
0504     return static_cast<KFile::FileView>(KFile::Simple | KFile::Detail | KFile::Tree | KFile::DetailTree);
0505 }
0506 
0507 void KDirOperatorPrivate::slotDetailedView()
0508 {
0509     // save old zoom settings
0510     writeIconZoomSettingsIfNeeded();
0511 
0512     KFile::FileView view = static_cast<KFile::FileView>((m_viewKind & ~allViews()) | KFile::Detail);
0513     q->setViewMode(view);
0514 }
0515 
0516 void KDirOperatorPrivate::slotSimpleView()
0517 {
0518     // save old zoom settings
0519     writeIconZoomSettingsIfNeeded();
0520 
0521     KFile::FileView view = static_cast<KFile::FileView>((m_viewKind & ~allViews()) | KFile::Simple);
0522     q->setViewMode(view);
0523 }
0524 
0525 void KDirOperatorPrivate::slotTreeView()
0526 {
0527     // save old zoom settings
0528     writeIconZoomSettingsIfNeeded();
0529 
0530     KFile::FileView view = static_cast<KFile::FileView>((m_viewKind & ~allViews()) | KFile::Tree);
0531     q->setViewMode(view);
0532 }
0533 
0534 void KDirOperatorPrivate::slotDetailedTreeView()
0535 {
0536     // save old zoom settings
0537     writeIconZoomSettingsIfNeeded();
0538 
0539     KFile::FileView view = static_cast<KFile::FileView>((m_viewKind & ~allViews()) | KFile::DetailTree);
0540     q->setViewMode(view);
0541 }
0542 
0543 void KDirOperatorPrivate::slotToggleAllowExpansion(bool allow)
0544 {
0545     KFile::FileView view = KFile::Detail;
0546     if (allow) {
0547         view = KFile::DetailTree;
0548     }
0549     q->setViewMode(view);
0550 }
0551 
0552 void KDirOperatorPrivate::slotToggleHidden(bool show)
0553 {
0554     m_dirLister->setShowHiddenFiles(show);
0555     q->updateDir();
0556     assureVisibleSelection();
0557 }
0558 
0559 void KDirOperatorPrivate::togglePreview(bool on)
0560 {
0561     if (on) {
0562         m_viewKind |= KFile::PreviewContents;
0563         if (m_preview == nullptr) {
0564             m_preview = new KFileMetaPreview(q);
0565             q->action(KDirOperator::ShowPreviewPanel)->setChecked(true);
0566             m_splitter->addWidget(m_preview);
0567         }
0568 
0569         m_preview->show();
0570 
0571         auto assureVisFunc = [this]() {
0572             assureVisibleSelection();
0573         };
0574         QMetaObject::invokeMethod(q, assureVisFunc, Qt::QueuedConnection);
0575         if (m_itemView != nullptr) {
0576             const QModelIndex index = m_itemView->selectionModel()->currentIndex();
0577             if (index.isValid()) {
0578                 triggerPreview(index);
0579             }
0580         }
0581     } else if (m_preview != nullptr) {
0582         m_viewKind = m_viewKind & ~KFile::PreviewContents;
0583         m_preview->hide();
0584     }
0585 }
0586 
0587 void KDirOperatorPrivate::toggleInlinePreviews(bool show)
0588 {
0589     if (m_showPreviews == show) {
0590         return;
0591     }
0592 
0593     m_showPreviews = show;
0594 
0595     if (!m_previewGenerator) {
0596         return;
0597     }
0598 
0599     m_previewGenerator->setPreviewShown(show);
0600 }
0601 
0602 void KDirOperatorPrivate::slotOpenFileManager()
0603 {
0604     const KFileItemList list = q->selectedItems();
0605     if (list.isEmpty()) {
0606         KIO::highlightInFileManager({m_currUrl.adjusted(QUrl::StripTrailingSlash)});
0607     } else {
0608         KIO::highlightInFileManager(list.urlList());
0609     }
0610 }
0611 
0612 void KDirOperatorPrivate::slotSortByName()
0613 {
0614     q->sortByName();
0615 }
0616 
0617 void KDirOperatorPrivate::slotSortBySize()
0618 {
0619     q->sortBySize();
0620 }
0621 
0622 void KDirOperatorPrivate::slotSortByDate()
0623 {
0624     q->sortByDate();
0625 }
0626 
0627 void KDirOperatorPrivate::slotSortByType()
0628 {
0629     q->sortByType();
0630 }
0631 
0632 void KDirOperatorPrivate::slotSortReversed(bool doReverse)
0633 {
0634     QDir::SortFlags s = m_sorting & ~QDir::Reversed;
0635     if (doReverse) {
0636         s |= QDir::Reversed;
0637     }
0638     updateSorting(s);
0639 }
0640 
0641 void KDirOperatorPrivate::slotToggleDirsFirst()
0642 {
0643     QDir::SortFlags s = (m_sorting ^ QDir::DirsFirst);
0644     updateSorting(s);
0645 }
0646 
0647 void KDirOperatorPrivate::slotIconsView()
0648 {
0649     // save old zoom settings
0650     writeIconZoomSettingsIfNeeded();
0651 
0652     // Put the icons on top
0653     q->action(KDirOperator::DecorationAtTop)->setChecked(true);
0654     m_decorationPosition = QStyleOptionViewItem::Top;
0655 
0656     // Switch to simple view
0657     KFile::FileView fileView = static_cast<KFile::FileView>((m_viewKind & ~allViews()) | KFile::Simple);
0658     q->setViewMode(fileView);
0659 }
0660 
0661 void KDirOperatorPrivate::slotCompactView()
0662 {
0663     // save old zoom settings
0664     writeIconZoomSettingsIfNeeded();
0665 
0666     // Put the icons on the side
0667     q->action(KDirOperator::DecorationAtLeft)->setChecked(true);
0668     m_decorationPosition = QStyleOptionViewItem::Left;
0669 
0670     // Switch to simple view
0671     KFile::FileView fileView = static_cast<KFile::FileView>((m_viewKind & ~allViews()) | KFile::Simple);
0672     q->setViewMode(fileView);
0673 }
0674 
0675 void KDirOperatorPrivate::slotDetailsView()
0676 {
0677     // save old zoom settings
0678     writeIconZoomSettingsIfNeeded();
0679 
0680     KFile::FileView view;
0681     if (q->action(KDirOperator::AllowExpansionInDetailsView)->isChecked()) {
0682         view = static_cast<KFile::FileView>((m_viewKind & ~allViews()) | KFile::DetailTree);
0683     } else {
0684         view = static_cast<KFile::FileView>((m_viewKind & ~allViews()) | KFile::Detail);
0685     }
0686     q->setViewMode(view);
0687 }
0688 
0689 void KDirOperatorPrivate::slotToggleIgnoreCase()
0690 {
0691     // TODO: port to Qt4's QAbstractItemView
0692     /*if ( !d->fileView )
0693       return;
0694 
0695     QDir::SortFlags sorting = d->fileView->sorting();
0696     if ( !KFile::isSortCaseInsensitive( sorting ) )
0697         d->fileView->setSorting( sorting | QDir::IgnoreCase );
0698     else
0699         d->fileView->setSorting( sorting & ~QDir::IgnoreCase );
0700     d->sorting = d->fileView->sorting();*/
0701 }
0702 
0703 void KDirOperator::mkdir()
0704 {
0705     d->m_newFileMenu->setWorkingDirectory(url());
0706     d->m_newFileMenu->createDirectory();
0707 }
0708 
0709 KIO::DeleteJob *KDirOperator::del(const KFileItemList &items, QWidget *parent, bool ask, bool showProgress)
0710 {
0711     if (items.isEmpty()) {
0712         KMessageBox::information(parent, i18n("You did not select a file to delete."), i18n("Nothing to Delete"));
0713         return nullptr;
0714     }
0715 
0716     if (parent == nullptr) {
0717         parent = this;
0718     }
0719 
0720     const QList<QUrl> urls = items.urlList();
0721 
0722     bool doIt = !ask;
0723     if (ask) {
0724         KIO::JobUiDelegate uiDelegate;
0725         uiDelegate.setWindow(parent);
0726         doIt = uiDelegate.askDeleteConfirmation(urls, KIO::JobUiDelegate::Delete, KIO::JobUiDelegate::DefaultConfirmation);
0727     }
0728 
0729     if (doIt) {
0730         KIO::JobFlags flags = showProgress ? KIO::DefaultFlags : KIO::HideProgressInfo;
0731         KIO::DeleteJob *job = KIO::del(urls, flags);
0732         KJobWidgets::setWindow(job, this);
0733         job->uiDelegate()->setAutoErrorHandlingEnabled(true);
0734         return job;
0735     }
0736 
0737     return nullptr;
0738 }
0739 
0740 void KDirOperator::deleteSelected()
0741 {
0742     const QList<QUrl> urls = selectedItems().urlList();
0743     if (urls.isEmpty()) {
0744         KMessageBox::information(this, i18n("You did not select a file to delete."), i18n("Nothing to Delete"));
0745         return;
0746     }
0747 
0748     using Iface = KIO::AskUserActionInterface;
0749     auto *deleteJob = new KIO::DeleteOrTrashJob(urls, Iface::Delete, Iface::DefaultConfirmation, this);
0750     deleteJob->start();
0751 }
0752 
0753 KIO::CopyJob *KDirOperator::trash(const KFileItemList &items, QWidget *parent, bool ask, bool showProgress)
0754 {
0755     if (items.isEmpty()) {
0756         KMessageBox::information(parent, i18n("You did not select a file to trash."), i18n("Nothing to Trash"));
0757         return nullptr;
0758     }
0759 
0760     const QList<QUrl> urls = items.urlList();
0761 
0762     bool doIt = !ask;
0763     if (ask) {
0764         KIO::JobUiDelegate uiDelegate;
0765         uiDelegate.setWindow(parent);
0766         doIt = uiDelegate.askDeleteConfirmation(urls, KIO::JobUiDelegate::Trash, KIO::JobUiDelegate::DefaultConfirmation);
0767     }
0768 
0769     if (doIt) {
0770         KIO::JobFlags flags = showProgress ? KIO::DefaultFlags : KIO::HideProgressInfo;
0771         KIO::CopyJob *job = KIO::trash(urls, flags);
0772         KJobWidgets::setWindow(job, this);
0773         job->uiDelegate()->setAutoErrorHandlingEnabled(true);
0774         return job;
0775     }
0776 
0777     return nullptr;
0778 }
0779 
0780 KFilePreviewGenerator *KDirOperator::previewGenerator() const
0781 {
0782     return d->m_previewGenerator;
0783 }
0784 
0785 void KDirOperator::setInlinePreviewShown(bool show)
0786 {
0787     d->m_inlinePreviewState = show ? KDirOperatorPrivate::ForcedToTrue : KDirOperatorPrivate::ForcedToFalse;
0788 }
0789 
0790 bool KDirOperator::isInlinePreviewShown() const
0791 {
0792     return d->m_showPreviews;
0793 }
0794 
0795 int KDirOperator::iconSize() const
0796 {
0797     return d->m_iconSize;
0798 }
0799 
0800 void KDirOperator::setIsSaving(bool isSaving)
0801 {
0802     d->m_isSaving = isSaving;
0803 }
0804 
0805 bool KDirOperator::isSaving() const
0806 {
0807     return d->m_isSaving;
0808 }
0809 
0810 void KDirOperator::renameSelected()
0811 {
0812     if (d->m_itemView == nullptr) {
0813         return;
0814     }
0815 
0816     const KFileItemList items = selectedItems();
0817     if (items.isEmpty()) {
0818         return;
0819     }
0820 
0821     KIO::RenameFileDialog *dialog = new KIO::RenameFileDialog(items, this);
0822     connect(dialog, &KIO::RenameFileDialog::renamingFinished, this, [this](const QList<QUrl> &urls) {
0823         d->assureVisibleSelection();
0824         Q_EMIT renamingFinished(urls);
0825     });
0826 
0827     dialog->open();
0828 }
0829 
0830 void KDirOperator::trashSelected()
0831 {
0832     if (d->m_itemView == nullptr) {
0833         return;
0834     }
0835 
0836     if (QApplication::keyboardModifiers() & Qt::ShiftModifier) {
0837         deleteSelected();
0838         return;
0839     }
0840 
0841     const QList<QUrl> urls = selectedItems().urlList();
0842     if (urls.isEmpty()) {
0843         KMessageBox::information(this, i18n("You did not select a file to trash."), i18n("Nothing to Trash"));
0844         return;
0845     }
0846 
0847     using Iface = KIO::AskUserActionInterface;
0848     auto *trashJob = new KIO::DeleteOrTrashJob(urls, Iface::Trash, Iface::DefaultConfirmation, this);
0849     trashJob->start();
0850 }
0851 
0852 void KDirOperator::setIconSize(int value)
0853 {
0854     if (d->m_iconSize == value) {
0855         return;
0856     }
0857 
0858     int size = value;
0859     // Keep size range in sync with KFileWidgetPrivate::m_stdIconSizes
0860     size = std::min(512, size);
0861     size = std::max<int>(KIconLoader::SizeSmall, size);
0862 
0863     d->m_iconSize = size;
0864 
0865     if (!d->m_previewGenerator) {
0866         return;
0867     }
0868 
0869     d->m_itemView->setIconSize(QSize(size, size));
0870     d->m_previewGenerator->updateIcons();
0871 
0872     Q_EMIT currentIconSizeChanged(size);
0873 }
0874 
0875 void KDirOperator::close()
0876 {
0877     resetCursor();
0878     d->m_pendingMimeTypes.clear();
0879     d->m_completion.clear();
0880     d->m_dirCompletion.clear();
0881     d->m_completeListDirty = true;
0882     d->m_dirLister->stop();
0883 }
0884 
0885 void KDirOperator::setUrl(const QUrl &_newurl, bool clearforward)
0886 {
0887     QUrl newurl = _newurl.isValid() ? _newurl.adjusted(QUrl::NormalizePathSegments) : QUrl::fromLocalFile(QDir::homePath());
0888     Utils::appendSlashToPath(newurl);
0889 
0890     // already set
0891     if (newurl.matches(d->m_currUrl, QUrl::StripTrailingSlash)) {
0892         return;
0893     }
0894 
0895     if (!d->isSchemeSupported(newurl.scheme())) {
0896         return;
0897     }
0898 
0899     if (!d->isReadable(newurl)) {
0900         // maybe newurl is a file? check its parent directory
0901         newurl = newurl.adjusted(QUrl::StripTrailingSlash).adjusted(QUrl::RemoveFilename);
0902         if (newurl.matches(d->m_currUrl, QUrl::StripTrailingSlash)) {
0903             Q_EMIT urlEntered(newurl); // To remove the filename in pathCombo
0904             return; // parent is current dir, nothing to do (fixes #173454, too)
0905         }
0906         KIO::StatJob *job = KIO::stat(newurl);
0907         KJobWidgets::setWindow(job, this);
0908         bool res = job->exec();
0909 
0910         KIO::UDSEntry entry = job->statResult();
0911         KFileItem i(entry, newurl);
0912         if ((!res || !d->isReadable(newurl)) && i.isDir()) {
0913             resetCursor();
0914             KMessageBox::error(d->m_itemView,
0915                                i18n("The specified folder does not exist "
0916                                     "or was not readable."));
0917             return;
0918         } else if (!i.isDir()) {
0919             return;
0920         }
0921     }
0922 
0923     if (clearforward) {
0924         // autodelete should remove this one
0925         d->m_backStack.push(new QUrl(d->m_currUrl));
0926         qDeleteAll(d->m_forwardStack);
0927         d->m_forwardStack.clear();
0928     }
0929 
0930     d->m_currUrl = newurl;
0931 
0932     pathChanged();
0933     Q_EMIT urlEntered(newurl);
0934 
0935     // enable/disable actions
0936     QAction *forwardAction = action(KDirOperator::Forward);
0937     forwardAction->setEnabled(!d->m_forwardStack.isEmpty());
0938 
0939     QAction *backAction = action(KDirOperator::Back);
0940     backAction->setEnabled(!d->m_backStack.isEmpty());
0941 
0942     QAction *upAction = action(KDirOperator::Up);
0943     upAction->setEnabled(!isRoot());
0944 
0945     d->openUrl(newurl);
0946 }
0947 
0948 void KDirOperator::updateDir()
0949 {
0950     QApplication::setOverrideCursor(Qt::WaitCursor);
0951     d->m_dirLister->emitChanges();
0952     QApplication::restoreOverrideCursor();
0953 }
0954 
0955 void KDirOperator::rereadDir()
0956 {
0957     pathChanged();
0958     d->openUrl(d->m_currUrl, KDirLister::Reload);
0959 }
0960 
0961 bool KDirOperatorPrivate::isSchemeSupported(const QString &scheme) const
0962 {
0963     return m_supportedSchemes.isEmpty() || m_supportedSchemes.contains(scheme);
0964 }
0965 
0966 bool KDirOperatorPrivate::openUrl(const QUrl &url, KDirLister::OpenUrlFlags flags)
0967 {
0968     const bool result = KProtocolManager::supportsListing(url) && isSchemeSupported(url.scheme()) && m_dirLister->openUrl(url, flags);
0969     if (!result) { // in that case, neither completed() nor canceled() will be emitted by KDL
0970         slotCanceled();
0971     }
0972 
0973     return result;
0974 }
0975 
0976 int KDirOperatorPrivate::sortColumn() const
0977 {
0978     int column = KDirModel::Name;
0979     if (KFile::isSortByDate(m_sorting)) {
0980         column = KDirModel::ModifiedTime;
0981     } else if (KFile::isSortBySize(m_sorting)) {
0982         column = KDirModel::Size;
0983     } else if (KFile::isSortByType(m_sorting)) {
0984         column = KDirModel::Type;
0985     } else {
0986         Q_ASSERT(KFile::isSortByName(m_sorting));
0987     }
0988 
0989     return column;
0990 }
0991 
0992 Qt::SortOrder KDirOperatorPrivate::sortOrder() const
0993 {
0994     return (m_sorting & QDir::Reversed) ? Qt::DescendingOrder : Qt::AscendingOrder;
0995 }
0996 
0997 void KDirOperatorPrivate::updateSorting(QDir::SortFlags sort)
0998 {
0999     // qDebug() << "changing sort flags from"  << m_sorting << "to" << sort;
1000     if (sort == m_sorting) {
1001         return;
1002     }
1003 
1004     m_sorting = sort;
1005     q->updateSortActions();
1006 
1007     m_proxyModel->setSortFoldersFirst(m_sorting & QDir::DirsFirst);
1008     m_proxyModel->sort(sortColumn(), sortOrder());
1009 
1010     // TODO: The headers from QTreeView don't take care about a sorting
1011     // change of the proxy model hence they must be updated the manually.
1012     // This is done here by a qobject_cast, but it would be nicer to:
1013     // - provide a signal 'sortingChanged()'
1014     // - connect KDirOperatorDetailView() with this signal and update the
1015     //   header internally
1016     QTreeView *treeView = qobject_cast<QTreeView *>(m_itemView);
1017     if (treeView != nullptr) {
1018         QHeaderView *headerView = treeView->header();
1019         headerView->blockSignals(true);
1020         headerView->setSortIndicator(sortColumn(), sortOrder());
1021         headerView->blockSignals(false);
1022     }
1023 
1024     assureVisibleSelection();
1025 }
1026 
1027 // Protected
1028 void KDirOperator::pathChanged()
1029 {
1030     if (d->m_itemView == nullptr) {
1031         return;
1032     }
1033 
1034     d->m_pendingMimeTypes.clear();
1035     // d->fileView->clear(); TODO
1036     d->m_completion.clear();
1037     d->m_dirCompletion.clear();
1038 
1039     // it may be, that we weren't ready at this time
1040     QApplication::restoreOverrideCursor();
1041 
1042     // when KIO::Job emits finished, the slot will restore the cursor
1043     QApplication::setOverrideCursor(Qt::WaitCursor);
1044 
1045     if (!d->isReadable(d->m_currUrl)) {
1046         KMessageBox::error(d->m_itemView,
1047                            i18n("The specified folder does not exist "
1048                                 "or was not readable."));
1049         if (d->m_backStack.isEmpty()) {
1050             home();
1051         } else {
1052             back();
1053         }
1054     }
1055 }
1056 
1057 void KDirOperatorPrivate::slotRedirected(const QUrl &newURL)
1058 {
1059     m_currUrl = newURL;
1060     m_pendingMimeTypes.clear();
1061     m_completion.clear();
1062     m_dirCompletion.clear();
1063     m_completeListDirty = true;
1064     Q_EMIT q->urlEntered(newURL);
1065 }
1066 
1067 // Code pinched from kfm then hacked
1068 void KDirOperator::back()
1069 {
1070     if (d->m_backStack.isEmpty()) {
1071         return;
1072     }
1073 
1074     d->m_forwardStack.push(new QUrl(d->m_currUrl));
1075 
1076     QUrl *s = d->m_backStack.pop();
1077     const QUrl newUrl = *s;
1078     delete s;
1079 
1080     if (d->m_dirHighlighting) {
1081         const QUrl _url = newUrl.adjusted(QUrl::StripTrailingSlash).adjusted(QUrl::RemoveFilename | QUrl::StripTrailingSlash);
1082 
1083         if (_url == d->m_currUrl.adjusted(QUrl::StripTrailingSlash) && !d->m_backStack.isEmpty()) {
1084             // e.g. started in a/b/c, cdUp() twice to "a", then back(), we highlight "c"
1085             d->m_lastUrl = *(d->m_backStack.top());
1086         } else {
1087             d->m_lastUrl = d->m_currUrl;
1088         }
1089     }
1090 
1091     setUrl(newUrl, false);
1092 }
1093 
1094 // Code pinched from kfm then hacked
1095 void KDirOperator::forward()
1096 {
1097     if (d->m_forwardStack.isEmpty()) {
1098         return;
1099     }
1100 
1101     d->m_backStack.push(new QUrl(d->m_currUrl));
1102 
1103     QUrl *s = d->m_forwardStack.pop();
1104     setUrl(*s, false);
1105     delete s;
1106 }
1107 
1108 QUrl KDirOperator::url() const
1109 {
1110     return d->m_currUrl;
1111 }
1112 
1113 void KDirOperator::cdUp()
1114 {
1115     // Allow /d/c// to go up to /d/ instead of /d/c/
1116     QUrl tmp(d->m_currUrl.adjusted(QUrl::NormalizePathSegments));
1117 
1118     if (d->m_dirHighlighting) {
1119         d->m_lastUrl = d->m_currUrl;
1120     }
1121 
1122     setUrl(tmp.resolved(QUrl(QStringLiteral(".."))), true);
1123 }
1124 
1125 void KDirOperator::home()
1126 {
1127     setUrl(QUrl::fromLocalFile(QDir::homePath()), true);
1128 }
1129 
1130 void KDirOperator::clearFilter()
1131 {
1132     d->m_dirLister->setNameFilter(QString());
1133     d->m_dirLister->clearMimeFilter();
1134     checkPreviewSupport();
1135 }
1136 
1137 void KDirOperator::setNameFilter(const QString &filter)
1138 {
1139     d->m_dirLister->setNameFilter(filter);
1140     checkPreviewSupport();
1141 }
1142 
1143 QString KDirOperator::nameFilter() const
1144 {
1145     return d->m_dirLister->nameFilter();
1146 }
1147 
1148 void KDirOperator::setMimeFilter(const QStringList &mimetypes)
1149 {
1150     d->m_dirLister->setMimeFilter(mimetypes);
1151     checkPreviewSupport();
1152 }
1153 
1154 QStringList KDirOperator::mimeFilter() const
1155 {
1156     return d->m_dirLister->mimeFilters();
1157 }
1158 
1159 void KDirOperator::setNewFileMenuSupportedMimeTypes(const QStringList &mimeTypes)
1160 {
1161     d->m_newFileMenu->setSupportedMimeTypes(mimeTypes);
1162 }
1163 
1164 QStringList KDirOperator::newFileMenuSupportedMimeTypes() const
1165 {
1166     return d->m_newFileMenu->supportedMimeTypes();
1167 }
1168 
1169 void KDirOperator::setNewFileMenuSelectDirWhenAlreadyExist(bool selectOnDirExists)
1170 {
1171     d->m_newFileMenu->setSelectDirWhenAlreadyExist(selectOnDirExists);
1172 }
1173 
1174 bool KDirOperator::checkPreviewSupport()
1175 {
1176     KToggleAction *previewAction = static_cast<KToggleAction *>(action(KDirOperator::ShowPreviewPanel));
1177 
1178     bool hasPreviewSupport = false;
1179     KConfigGroup cg(KSharedConfig::openConfig(), ConfigGroup);
1180     if (cg.readEntry("Show Default Preview", true)) {
1181         hasPreviewSupport = d->checkPreviewInternal();
1182     }
1183 
1184     previewAction->setEnabled(hasPreviewSupport);
1185     return hasPreviewSupport;
1186 }
1187 
1188 void KDirOperator::activatedMenu(const KFileItem &item, const QPoint &pos)
1189 {
1190     updateSelectionDependentActions();
1191 
1192     d->m_newFileMenu->setWorkingDirectory(item.url());
1193     d->m_newFileMenu->checkUpToDate();
1194 
1195     action(KDirOperator::New)->setEnabled(item.isDir());
1196 
1197     Q_EMIT contextMenuAboutToShow(item, d->m_actionMenu->menu());
1198 
1199     // Must be right before we call QMenu::exec(), otherwise we might remove
1200     // other non-related actions along with the open-with ones
1201     const QList<QAction *> openWithActions = d->insertOpenWithActions();
1202 
1203     d->m_actionMenu->menu()->exec(pos);
1204 
1205     // Remove the open-with actions, otherwise they would accumulate in the menu
1206     for (auto *action : openWithActions) {
1207         d->m_actionMenu->menu()->removeAction(action);
1208     }
1209 }
1210 
1211 QList<QAction *> KDirOperatorPrivate::insertOpenWithActions()
1212 {
1213     if (!m_showOpenWithActions) {
1214         return {};
1215     }
1216 
1217     const QList<QAction *> beforeOpenWith = m_actionMenu->menu()->actions();
1218 
1219     const KFileItemList items = q->selectedItems();
1220     if (!items.isEmpty()) {
1221         m_itemActions->setItemListProperties(KFileItemListProperties(items));
1222         const QList<QAction *> actions = m_actionMenu->menu()->actions();
1223         QAction *before = !actions.isEmpty() ? actions.at(0) : nullptr;
1224         m_itemActions->insertOpenWithActionsTo(before, m_actionMenu->menu(), QStringList());
1225     }
1226 
1227     // Get the list of the added open-with actions
1228     QList<QAction *> toRemove = m_actionMenu->menu()->actions();
1229     auto it = std::remove_if(toRemove.begin(), toRemove.end(), [beforeOpenWith](QAction *a) {
1230         return beforeOpenWith.contains(a);
1231     });
1232     toRemove.erase(it, toRemove.end());
1233 
1234     return toRemove;
1235 }
1236 
1237 void KDirOperator::showOpenWithActions(bool enable)
1238 {
1239     d->m_showOpenWithActions = enable;
1240 }
1241 
1242 void KDirOperator::changeEvent(QEvent *event)
1243 {
1244     QWidget::changeEvent(event);
1245 }
1246 
1247 bool KDirOperator::eventFilter(QObject *watched, QEvent *event)
1248 {
1249     // If we are not hovering any items, check if there is a current index
1250     // set. In that case, we show the preview of that item.
1251     switch (event->type()) {
1252     case QEvent::MouseMove: {
1253         if (d->m_preview && !d->m_preview->isHidden()) {
1254             const QModelIndex hoveredIndex = d->m_itemView->indexAt(d->m_itemView->viewport()->mapFromGlobal(QCursor::pos()));
1255 
1256             if (d->m_lastHoveredIndex == hoveredIndex) {
1257                 return QWidget::eventFilter(watched, event);
1258             }
1259 
1260             d->m_lastHoveredIndex = hoveredIndex;
1261 
1262             const QModelIndex currentIndex = d->m_itemView->selectionModel() ? d->m_itemView->selectionModel()->currentIndex() : QModelIndex();
1263 
1264             if (!hoveredIndex.isValid() && currentIndex.isValid() && (d->m_lastHoveredIndex != currentIndex)) {
1265                 const KFileItem item = d->m_itemView->model()->data(currentIndex, KDirModel::FileItemRole).value<KFileItem>();
1266                 if (!item.isNull()) {
1267                     d->m_preview->showPreview(item.url());
1268                 }
1269             }
1270         }
1271         break;
1272     }
1273     case QEvent::MouseButtonRelease: {
1274         if (d->m_preview != nullptr && !d->m_preview->isHidden()) {
1275             const QModelIndex hoveredIndex = d->m_itemView->indexAt(d->m_itemView->viewport()->mapFromGlobal(QCursor::pos()));
1276             const QModelIndex focusedIndex = d->m_itemView->selectionModel() ? d->m_itemView->selectionModel()->currentIndex() : QModelIndex();
1277 
1278             if (((!focusedIndex.isValid()) || !d->m_itemView->selectionModel()->isSelected(focusedIndex)) && (!hoveredIndex.isValid())) {
1279                 d->m_preview->clearPreview();
1280             }
1281         }
1282         QMouseEvent *mouseEvent = static_cast<QMouseEvent *>(event);
1283         if (mouseEvent) {
1284             switch (mouseEvent->button()) {
1285             case Qt::BackButton:
1286                 back();
1287                 break;
1288             case Qt::ForwardButton:
1289                 forward();
1290                 break;
1291             default:
1292                 break;
1293             }
1294         }
1295         break;
1296     }
1297     case QEvent::Wheel: {
1298         QWheelEvent *evt = static_cast<QWheelEvent *>(event);
1299         if (evt->modifiers() & Qt::ControlModifier) {
1300             if (evt->angleDelta().y() > 0) {
1301                 setIconSize(d->m_iconSize + 10);
1302             } else {
1303                 setIconSize(d->m_iconSize - 10);
1304             }
1305             return true;
1306         }
1307         break;
1308     }
1309     case QEvent::DragEnter: {
1310         // Accepts drops of one file or folder only
1311         QDragEnterEvent *evt = static_cast<QDragEnterEvent *>(event);
1312         const QList<QUrl> urls = KUrlMimeData::urlsFromMimeData(evt->mimeData(), KUrlMimeData::PreferLocalUrls);
1313 
1314         // only one file/folder can be dropped at the moment
1315         if (urls.size() != 1) {
1316             evt->ignore();
1317 
1318         } else {
1319             // MIME type filtering
1320             bool mimeFilterPass = true;
1321             const QStringList mimeFilters = d->m_dirLister->mimeFilters();
1322 
1323             if (mimeFilters.size() > 1) {
1324                 mimeFilterPass = false;
1325                 const QUrl &url = urls.constFirst();
1326 
1327                 QMimeDatabase mimeDataBase;
1328                 QMimeType fileMimeType = mimeDataBase.mimeTypeForUrl(url);
1329 
1330                 QRegularExpression regex;
1331                 for (const auto &mimeFilter : mimeFilters) {
1332                     regex.setPattern(mimeFilter);
1333                     if (regex.match(fileMimeType.name()).hasMatch()) { // matches!
1334                         mimeFilterPass = true;
1335                         break;
1336                     }
1337                 }
1338             }
1339 
1340             event->setAccepted(mimeFilterPass);
1341         }
1342 
1343         return true;
1344     }
1345     case QEvent::Drop: {
1346         QDropEvent *evt = static_cast<QDropEvent *>(event);
1347         const QList<QUrl> urls = KUrlMimeData::urlsFromMimeData(evt->mimeData(), KUrlMimeData::PreferLocalUrls);
1348 
1349         const QUrl &url = urls.constFirst();
1350 
1351         // stat the url to get details
1352         KIO::StatJob *job = KIO::stat(url, KIO::HideProgressInfo);
1353         job->exec();
1354 
1355         setFocus();
1356 
1357         const KIO::UDSEntry entry = job->statResult();
1358 
1359         if (entry.isDir()) {
1360             // if this was a directory
1361             setUrl(url, false);
1362         } else {
1363             // if the current url is not known
1364             if (d->m_dirLister->findByUrl(url).isNull()) {
1365                 setUrl(url.adjusted(QUrl::RemoveFilename), false);
1366 
1367                 // Will set the current item once loading has finished
1368                 auto urlSetterClosure = [this, url]() {
1369                     setCurrentItem(url);
1370                     QObject::disconnect(d->m_connection);
1371                 };
1372                 d->m_connection = connect(this, &KDirOperator::finishedLoading, this, urlSetterClosure);
1373             } else {
1374                 setCurrentItem(url);
1375             }
1376         }
1377         evt->accept();
1378         return true;
1379     }
1380     case QEvent::KeyPress: {
1381         QKeyEvent *evt = static_cast<QKeyEvent *>(event);
1382         if (evt->key() == Qt::Key_Return || evt->key() == Qt::Key_Enter) {
1383             // when no elements are selected and Return/Enter is pressed
1384             // emit keyEnterReturnPressed
1385             // let activated event be emitted by subsequent QAbstractItemView::keyPress otherwise
1386             if (!d->m_itemView->currentIndex().isValid()) {
1387                 Q_EMIT keyEnterReturnPressed();
1388                 evt->accept();
1389                 return true;
1390             }
1391         }
1392         break;
1393     }
1394     case QEvent::Resize: {
1395         /* clang-format off */
1396         if (watched == d->m_itemView->viewport()
1397             && d->m_itemView->horizontalScrollBar()
1398             && d->m_progressBar->parent() == this /* it could have been reparented to a statusbar */) { /* clang-format on */
1399             if (d->m_itemView->horizontalScrollBar()->isVisible()) {
1400                 // Show the progress bar above the horizontal scrollbar that may be visible
1401                 // in compact view
1402                 QPoint progressBarPos = d->progressBarPos();
1403                 progressBarPos.ry() -= d->m_itemView->horizontalScrollBar()->height();
1404                 d->m_progressBar->move(progressBarPos);
1405             } else {
1406                 d->m_progressBar->move(d->progressBarPos());
1407             }
1408         }
1409         break;
1410     }
1411     default:
1412         break;
1413     }
1414 
1415     return QWidget::eventFilter(watched, event);
1416 }
1417 
1418 bool KDirOperatorPrivate::checkPreviewInternal() const
1419 {
1420     const QStringList supported = KIO::PreviewJob::supportedMimeTypes();
1421     // no preview support for directories?
1422     if (q->dirOnlyMode() && !supported.contains(QLatin1String("inode/directory"))) {
1423         return false;
1424     }
1425 
1426     const QStringList mimeTypes = m_dirLister->mimeFilters();
1427     const QStringList nameFilters = m_dirLister->nameFilter().split(QLatin1Char(' '), Qt::SkipEmptyParts);
1428 
1429     if (mimeTypes.isEmpty() && nameFilters.isEmpty() && !supported.isEmpty()) {
1430         return true;
1431     } else {
1432         QMimeDatabase db;
1433         QRegularExpression re;
1434 
1435         if (!mimeTypes.isEmpty()) {
1436             for (const QString &str : supported) {
1437                 // wildcard matching because the "mimetype" can be "image/*"
1438                 re.setPattern(QRegularExpression::wildcardToRegularExpression(str));
1439 
1440                 if (mimeTypes.indexOf(re) != -1) { // matches! -> we want previews
1441                     return true;
1442                 }
1443             }
1444         }
1445 
1446         if (!nameFilters.isEmpty()) {
1447             // find the MIME types of all the filter-patterns
1448             for (const QString &filter : nameFilters) {
1449                 if (filter == QLatin1Char('*')) {
1450                     return true;
1451                 }
1452 
1453                 const QMimeType mt = db.mimeTypeForFile(filter, QMimeDatabase::MatchExtension /*fast mode, no file contents exist*/);
1454                 if (!mt.isValid()) {
1455                     continue;
1456                 }
1457                 const QString mime = mt.name();
1458 
1459                 for (const QString &str : supported) {
1460                     // the "mimetypes" we get from the PreviewJob can be "image/*"
1461                     // so we need to check in wildcard mode
1462                     re.setPattern(QRegularExpression::wildcardToRegularExpression(str));
1463                     if (re.match(mime).hasMatch()) {
1464                         return true;
1465                     }
1466                 }
1467             }
1468         }
1469     }
1470 
1471     return false;
1472 }
1473 
1474 QAbstractItemView *KDirOperator::createView(QWidget *parent, KFile::FileView viewKind)
1475 {
1476     QAbstractItemView *itemView = nullptr;
1477     if (KFile::isDetailView(viewKind) || KFile::isTreeView(viewKind) || KFile::isDetailTreeView(viewKind)) {
1478         KDirOperatorDetailView *detailView = new KDirOperatorDetailView(parent);
1479         detailView->setViewMode(viewKind);
1480         itemView = detailView;
1481     } else {
1482         itemView = new KDirOperatorIconView(parent, decorationPosition());
1483     }
1484 
1485     return itemView;
1486 }
1487 
1488 void KDirOperator::setAcceptDrops(bool acceptsDrops)
1489 {
1490     QWidget::setAcceptDrops(acceptsDrops);
1491     if (view()) {
1492         view()->setAcceptDrops(acceptsDrops);
1493         if (acceptsDrops) {
1494             view()->installEventFilter(this);
1495         } else {
1496             view()->removeEventFilter(this);
1497         }
1498     }
1499 }
1500 
1501 void KDirOperator::setDropOptions(int options)
1502 {
1503     d->m_dropOptions = options;
1504     // TODO:
1505     // if (d->fileView)
1506     //   d->fileView->setDropOptions(options);
1507 }
1508 
1509 void KDirOperator::setViewMode(KFile::FileView viewKind)
1510 {
1511     bool preview = (KFile::isPreviewInfo(viewKind) || KFile::isPreviewContents(viewKind));
1512 
1513     if (viewKind == KFile::Default) {
1514         if (KFile::isDetailView((KFile::FileView)d->m_defaultView)) {
1515             viewKind = KFile::Detail;
1516         } else if (KFile::isTreeView((KFile::FileView)d->m_defaultView)) {
1517             viewKind = KFile::Tree;
1518         } else if (KFile::isDetailTreeView((KFile::FileView)d->m_defaultView)) {
1519             viewKind = KFile::DetailTree;
1520         } else {
1521             viewKind = KFile::Simple;
1522         }
1523 
1524         const KFile::FileView defaultViewKind = static_cast<KFile::FileView>(d->m_defaultView);
1525         preview = (KFile::isPreviewInfo(defaultViewKind) || KFile::isPreviewContents(defaultViewKind)) && action(KDirOperator::ShowPreviewPanel)->isEnabled();
1526     }
1527 
1528     d->m_viewKind = static_cast<int>(viewKind);
1529     viewKind = static_cast<KFile::FileView>(d->m_viewKind);
1530 
1531     QAbstractItemView *newView = createView(this, viewKind);
1532     setViewInternal(newView);
1533 
1534     if (acceptDrops()) {
1535         newView->setAcceptDrops(true);
1536         newView->installEventFilter(this);
1537     }
1538 
1539     d->togglePreview(preview);
1540 }
1541 
1542 KFile::FileView KDirOperator::viewMode() const
1543 {
1544     return static_cast<KFile::FileView>(d->m_viewKind);
1545 }
1546 
1547 QAbstractItemView *KDirOperator::view() const
1548 {
1549     return d->m_itemView;
1550 }
1551 
1552 KFile::Modes KDirOperator::mode() const
1553 {
1554     return d->m_mode;
1555 }
1556 
1557 void KDirOperator::setMode(KFile::Modes mode)
1558 {
1559     if (d->m_mode == mode) {
1560         return;
1561     }
1562 
1563     const bool isDirOnlyChanged = dirOnlyMode(d->m_mode) != dirOnlyMode(mode);
1564 
1565     d->m_mode = mode;
1566 
1567     d->m_dirLister->setDirOnlyMode(dirOnlyMode());
1568 
1569     // When KFile::Directory mode changes, we need to update the dir,
1570     // otherwise initially we would be showing dirs and files even when
1571     // dirOnlyMode() is true
1572     if (isDirOnlyChanged) {
1573         updateDir();
1574     }
1575 
1576     // reset the view with the different mode
1577     if (d->m_itemView != nullptr) {
1578         setViewMode(static_cast<KFile::FileView>(d->m_viewKind));
1579     }
1580 }
1581 
1582 void KDirOperator::setViewInternal(QAbstractItemView *view)
1583 {
1584     if (view == d->m_itemView) {
1585         return;
1586     }
1587 
1588     // TODO: do a real timer and restart it after that
1589     d->m_pendingMimeTypes.clear();
1590     const bool listDir = (d->m_itemView == nullptr);
1591 
1592     if (d->m_mode & KFile::Files) {
1593         view->setSelectionMode(QAbstractItemView::ExtendedSelection);
1594     } else {
1595         view->setSelectionMode(QAbstractItemView::SingleSelection);
1596     }
1597 
1598     QItemSelectionModel *selectionModel = nullptr;
1599     if ((d->m_itemView != nullptr) && d->m_itemView->selectionModel()->hasSelection()) {
1600         // remember the selection of the current item view and apply this selection
1601         // to the new view later
1602         const QItemSelection selection = d->m_itemView->selectionModel()->selection();
1603         selectionModel = new QItemSelectionModel(d->m_proxyModel, this);
1604         selectionModel->select(selection, QItemSelectionModel::Select);
1605     }
1606 
1607     setFocusProxy(nullptr);
1608     delete d->m_itemView;
1609     d->m_itemView = view;
1610     d->m_itemView->setModel(d->m_proxyModel);
1611     setFocusProxy(d->m_itemView);
1612 
1613     d->m_itemView->viewport()->setObjectName(QStringLiteral("d->itemview_viewport"));
1614     view->viewport()->installEventFilter(this);
1615 
1616     KFileItemDelegate *delegate = new KFileItemDelegate(d->m_itemView);
1617     d->m_itemView->setItemDelegate(delegate);
1618     d->m_itemView->viewport()->setAttribute(Qt::WA_Hover);
1619     d->m_itemView->setContextMenuPolicy(Qt::CustomContextMenu);
1620     d->m_itemView->setMouseTracking(true);
1621     // d->itemView->setDropOptions(d->dropOptions);
1622 
1623     // first push our settings to the view, then listen for changes from the view
1624     QTreeView *treeView = qobject_cast<QTreeView *>(d->m_itemView);
1625     if (treeView) {
1626         QHeaderView *headerView = treeView->header();
1627         headerView->setSortIndicator(d->sortColumn(), d->sortOrder());
1628         connect(headerView, &QHeaderView::sortIndicatorChanged, this, [this](int logicalIndex, Qt::SortOrder order) {
1629             d->synchronizeSortingState(logicalIndex, order);
1630         });
1631     }
1632 
1633     connect(d->m_itemView, &QAbstractItemView::activated, this, [this](QModelIndex index) {
1634         d->slotActivated(index);
1635     });
1636 
1637     connect(d->m_itemView, &QAbstractItemView::customContextMenuRequested, this, [this](QPoint pos) {
1638         d->openContextMenu(pos);
1639     });
1640 
1641     connect(d->m_itemView, &QAbstractItemView::entered, this, [this](QModelIndex index) {
1642         d->triggerPreview(index);
1643     });
1644 
1645     d->m_splitter->insertWidget(0, d->m_itemView);
1646 
1647     d->m_splitter->resize(size());
1648     d->m_itemView->show();
1649 
1650     if (listDir) {
1651         QApplication::setOverrideCursor(Qt::WaitCursor);
1652         d->openUrl(d->m_currUrl);
1653     }
1654 
1655     if (selectionModel != nullptr) {
1656         d->m_itemView->setSelectionModel(selectionModel);
1657         auto assureVisFunc = [this]() {
1658             d->assureVisibleSelection();
1659         };
1660         QMetaObject::invokeMethod(this, assureVisFunc, Qt::QueuedConnection);
1661     }
1662 
1663     connect(d->m_itemView->selectionModel(), &QItemSelectionModel::currentChanged, this, [this](QModelIndex index) {
1664         d->triggerPreview(index);
1665     });
1666     connect(d->m_itemView->selectionModel(), &QItemSelectionModel::selectionChanged, this, [this]() {
1667         d->slotSelectionChanged();
1668     });
1669 
1670     // if we cannot cast it to a QListView, disable the "Icon Position" menu. Note that this check
1671     // needs to be done here, and not in createView, since we can be set an external view
1672     d->m_decorationMenu->setEnabled(qobject_cast<QListView *>(d->m_itemView));
1673 
1674     d->m_shouldFetchForItems = qobject_cast<QTreeView *>(view);
1675     if (d->m_shouldFetchForItems) {
1676         connect(d->m_dirModel, &KDirModel::expand, this, [this](QModelIndex index) {
1677             d->slotExpandToUrl(index);
1678         });
1679     } else {
1680         d->m_itemsToBeSetAsCurrent.clear();
1681     }
1682 
1683     const bool previewForcedToTrue = d->m_inlinePreviewState == KDirOperatorPrivate::ForcedToTrue;
1684     const bool previewShown = d->m_inlinePreviewState == KDirOperatorPrivate::NotForced ? d->m_showPreviews : previewForcedToTrue;
1685     d->m_previewGenerator = new KFilePreviewGenerator(d->m_itemView);
1686     d->m_itemView->setIconSize(previewForcedToTrue ? QSize(KIconLoader::SizeHuge, KIconLoader::SizeHuge) : QSize(d->m_iconSize, d->m_iconSize));
1687     d->m_previewGenerator->setPreviewShown(previewShown);
1688     action(KDirOperator::ShowPreview)->setChecked(previewShown);
1689 
1690     // ensure we change everything needed
1691     d->slotChangeDecorationPosition();
1692     updateViewActions();
1693 
1694     Q_EMIT viewChanged(view);
1695 
1696     const int zoom = previewForcedToTrue ? KIconLoader::SizeHuge : d->iconSizeForViewType(view);
1697 
1698     // this will make d->m_iconSize be updated, since setIconSize slot will be called
1699     Q_EMIT currentIconSizeChanged(zoom);
1700 }
1701 
1702 void KDirOperator::setDirLister(KDirLister *lister)
1703 {
1704     if (lister == d->m_dirLister) { // sanity check
1705         return;
1706     }
1707 
1708     delete d->m_dirModel;
1709     d->m_dirModel = nullptr;
1710 
1711     delete d->m_proxyModel;
1712     d->m_proxyModel = nullptr;
1713 
1714     // delete d->m_dirLister; // deleted by KDirModel already, which took ownership
1715     d->m_dirLister = lister;
1716 
1717     d->m_dirModel = new KDirModel(this);
1718     d->m_dirModel->setDirLister(d->m_dirLister);
1719     d->m_dirModel->setDropsAllowed(KDirModel::DropOnDirectory);
1720 
1721     d->m_shouldFetchForItems = qobject_cast<QTreeView *>(d->m_itemView);
1722     if (d->m_shouldFetchForItems) {
1723         connect(d->m_dirModel, &KDirModel::expand, this, [this](QModelIndex index) {
1724             d->slotExpandToUrl(index);
1725         });
1726     } else {
1727         d->m_itemsToBeSetAsCurrent.clear();
1728     }
1729 
1730     d->m_proxyModel = new KDirSortFilterProxyModel(this);
1731     d->m_proxyModel->setSourceModel(d->m_dirModel);
1732 
1733     d->m_dirLister->setDelayedMimeTypes(true);
1734 
1735     QWidget *mainWidget = topLevelWidget();
1736     d->m_dirLister->setMainWindow(mainWidget);
1737     // qDebug() << "mainWidget=" << mainWidget;
1738 
1739     connect(d->m_dirLister, &KCoreDirLister::percent, this, [this](int percent) {
1740         d->slotProgress(percent);
1741     });
1742     connect(d->m_dirLister, &KCoreDirLister::started, this, [this]() {
1743         d->slotStarted();
1744     });
1745     connect(d->m_dirLister, qOverload<>(&KCoreDirLister::completed), this, [this]() {
1746         d->slotIOFinished();
1747     });
1748     connect(d->m_dirLister, qOverload<>(&KCoreDirLister::canceled), this, [this]() {
1749         d->slotCanceled();
1750     });
1751     connect(d->m_dirLister, &KCoreDirLister::jobError, this, [this]() {
1752         d->slotIOFinished();
1753     });
1754     connect(d->m_dirLister, &KCoreDirLister::redirection, this, [this](const QUrl &, const QUrl &newUrl) {
1755         d->slotRedirected(newUrl);
1756     });
1757     connect(d->m_dirLister, &KCoreDirLister::newItems, this, [this]() {
1758         d->slotItemsChanged();
1759     });
1760     connect(d->m_dirLister, &KCoreDirLister::itemsDeleted, this, [this]() {
1761         d->slotItemsChanged();
1762     });
1763     connect(d->m_dirLister, qOverload<>(&KCoreDirLister::clear), this, [this]() {
1764         d->slotItemsChanged();
1765     });
1766 }
1767 
1768 void KDirOperator::selectDir(const KFileItem &item)
1769 {
1770     setUrl(item.targetUrl(), true);
1771 }
1772 
1773 void KDirOperator::selectFile(const KFileItem &item)
1774 {
1775     QApplication::restoreOverrideCursor();
1776 
1777     Q_EMIT fileSelected(item);
1778 }
1779 
1780 void KDirOperator::highlightFile(const KFileItem &item)
1781 {
1782     if ((d->m_preview != nullptr && !d->m_preview->isHidden()) && !item.isNull()) {
1783         d->m_preview->showPreview(item.url());
1784     }
1785 
1786     Q_EMIT fileHighlighted(item);
1787 }
1788 
1789 void KDirOperator::setCurrentItem(const QUrl &url)
1790 {
1791     // qDebug();
1792 
1793     KFileItem item = d->m_dirLister->findByUrl(url);
1794     if (d->m_shouldFetchForItems && item.isNull()) {
1795         d->m_itemsToBeSetAsCurrent << url;
1796 
1797         if (d->m_viewKind == KFile::DetailTree) {
1798             d->m_dirModel->expandToUrl(url);
1799         }
1800 
1801         return;
1802     }
1803 
1804     setCurrentItem(item);
1805 }
1806 
1807 void KDirOperator::setCurrentItem(const KFileItem &item)
1808 {
1809     // qDebug();
1810 
1811     if (!d->m_itemView) {
1812         return;
1813     }
1814 
1815     QItemSelectionModel *selModel = d->m_itemView->selectionModel();
1816     if (selModel) {
1817         selModel->clear();
1818         if (!item.isNull()) {
1819             const QModelIndex dirIndex = d->m_dirModel->indexForItem(item);
1820             const QModelIndex proxyIndex = d->m_proxyModel->mapFromSource(dirIndex);
1821             selModel->setCurrentIndex(proxyIndex, QItemSelectionModel::Select);
1822         }
1823     }
1824 }
1825 
1826 void KDirOperator::setCurrentItems(const QList<QUrl> &urls)
1827 {
1828     // qDebug();
1829 
1830     if (!d->m_itemView) {
1831         return;
1832     }
1833 
1834     KFileItemList itemList;
1835     for (const QUrl &url : urls) {
1836         KFileItem item = d->m_dirLister->findByUrl(url);
1837         if (d->m_shouldFetchForItems && item.isNull()) {
1838             d->m_itemsToBeSetAsCurrent << url;
1839 
1840             if (d->m_viewKind == KFile::DetailTree) {
1841                 d->m_dirModel->expandToUrl(url);
1842             }
1843 
1844             continue;
1845         }
1846 
1847         itemList << item;
1848     }
1849 
1850     setCurrentItems(itemList);
1851 }
1852 
1853 void KDirOperator::setCurrentItems(const KFileItemList &items)
1854 {
1855     // qDebug();
1856 
1857     if (d->m_itemView == nullptr) {
1858         return;
1859     }
1860 
1861     QItemSelectionModel *selModel = d->m_itemView->selectionModel();
1862     if (selModel) {
1863         selModel->clear();
1864         QModelIndex proxyIndex;
1865         for (const KFileItem &item : items) {
1866             if (!item.isNull()) {
1867                 const QModelIndex dirIndex = d->m_dirModel->indexForItem(item);
1868                 proxyIndex = d->m_proxyModel->mapFromSource(dirIndex);
1869                 selModel->select(proxyIndex, QItemSelectionModel::Select);
1870             }
1871         }
1872         if (proxyIndex.isValid()) {
1873             selModel->setCurrentIndex(proxyIndex, QItemSelectionModel::NoUpdate);
1874         }
1875     }
1876 }
1877 
1878 QString KDirOperator::makeCompletion(const QString &string)
1879 {
1880     if (string.isEmpty()) {
1881         d->m_itemView->selectionModel()->clear();
1882         return QString();
1883     }
1884 
1885     prepareCompletionObjects();
1886     return d->m_completion.makeCompletion(string);
1887 }
1888 
1889 QString KDirOperator::makeDirCompletion(const QString &string)
1890 {
1891     if (string.isEmpty()) {
1892         d->m_itemView->selectionModel()->clear();
1893         return QString();
1894     }
1895 
1896     prepareCompletionObjects();
1897     return d->m_dirCompletion.makeCompletion(string);
1898 }
1899 
1900 void KDirOperator::prepareCompletionObjects()
1901 {
1902     if (d->m_itemView == nullptr) {
1903         return;
1904     }
1905 
1906     if (d->m_completeListDirty) { // create the list of all possible completions
1907         const KFileItemList itemList = d->m_dirLister->items();
1908         for (const KFileItem &item : itemList) {
1909             d->m_completion.addItem(item.name());
1910             if (item.isDir()) {
1911                 d->m_dirCompletion.addItem(item.name());
1912             }
1913         }
1914         d->m_completeListDirty = false;
1915     }
1916 }
1917 
1918 void KDirOperator::slotCompletionMatch(const QString &match)
1919 {
1920     QUrl url(match);
1921     if (url.isRelative()) {
1922         url = d->m_currUrl.resolved(url);
1923     }
1924     setCurrentItem(url);
1925     Q_EMIT completion(match);
1926 }
1927 
1928 void KDirOperator::setupActions()
1929 {
1930     d->m_actionMenu = new KActionMenu(i18n("Menu"), this);
1931     d->m_actions[PopupMenu] = d->m_actionMenu;
1932 
1933     QAction *upAction = KStandardAction::create(KStandardAction::Up, this, SLOT(cdUp()), this);
1934     d->m_actions[Up] = upAction;
1935     upAction->setText(i18n("Parent Folder"));
1936 
1937     QAction *backAction = KStandardAction::create(KStandardAction::Back, this, SLOT(back()), this);
1938     d->m_actions[Back] = backAction;
1939     auto backShortcuts = backAction->shortcuts();
1940     backShortcuts << Qt::Key_Backspace;
1941     backAction->setShortcuts(backShortcuts);
1942     backAction->setToolTip(i18nc("@info", "Go back"));
1943 
1944     QAction *forwardAction = KStandardAction::create(KStandardAction::Forward, this, SLOT(forward()), this);
1945     d->m_actions[Forward] = forwardAction;
1946     forwardAction->setToolTip(i18nc("@info", "Go forward"));
1947 
1948     QAction *homeAction = KStandardAction::create(KStandardAction::Home, this, SLOT(home()), this);
1949     d->m_actions[Home] = homeAction;
1950     homeAction->setText(i18n("Home Folder"));
1951 
1952     QAction *reloadAction = KStandardAction::create(KStandardAction::Redisplay, this, SLOT(rereadDir()), this);
1953     d->m_actions[Reload] = reloadAction;
1954     reloadAction->setText(i18n("Reload"));
1955     reloadAction->setShortcuts(KStandardShortcut::shortcut(KStandardShortcut::Reload));
1956 
1957     QAction *mkdirAction = new QAction(i18n("New Folder..."), this);
1958     d->m_actions[NewFolder] = mkdirAction;
1959     mkdirAction->setIcon(QIcon::fromTheme(QStringLiteral("folder-new")));
1960     connect(mkdirAction, &QAction::triggered, this, [this]() {
1961         mkdir();
1962     });
1963 
1964     QAction *rename = KStandardAction::renameFile(this, &KDirOperator::renameSelected, this);
1965     d->m_actions[Rename] = rename;
1966 
1967     QAction *trash = new QAction(i18n("Move to Trash"), this);
1968     d->m_actions[Trash] = trash;
1969     trash->setIcon(QIcon::fromTheme(QStringLiteral("user-trash")));
1970     trash->setShortcut(Qt::Key_Delete);
1971     connect(trash, &QAction::triggered, this, &KDirOperator::trashSelected);
1972 
1973     QAction *action = new QAction(i18n("Delete"), this);
1974     d->m_actions[Delete] = action;
1975     action->setIcon(QIcon::fromTheme(QStringLiteral("edit-delete")));
1976     action->setShortcut(Qt::SHIFT | Qt::Key_Delete);
1977     connect(action, &QAction::triggered, this, &KDirOperator::deleteSelected);
1978 
1979     // the sort menu actions
1980     KActionMenu *sortMenu = new KActionMenu(i18n("Sorting"), this);
1981     d->m_actions[SortMenu] = sortMenu;
1982     sortMenu->setIcon(QIcon::fromTheme(QStringLiteral("view-sort")));
1983     sortMenu->setPopupMode(QToolButton::InstantPopup);
1984 
1985     KToggleAction *byNameAction = new KToggleAction(i18n("Sort by Name"), this);
1986     d->m_actions[SortByName] = byNameAction;
1987     connect(byNameAction, &QAction::triggered, this, [this]() {
1988         d->slotSortByName();
1989     });
1990 
1991     KToggleAction *bySizeAction = new KToggleAction(i18n("Sort by Size"), this);
1992     d->m_actions[SortBySize] = bySizeAction;
1993     connect(bySizeAction, &QAction::triggered, this, [this]() {
1994         d->slotSortBySize();
1995     });
1996 
1997     KToggleAction *byDateAction = new KToggleAction(i18n("Sort by Date"), this);
1998     d->m_actions[SortByDate] = byDateAction;
1999     connect(byDateAction, &QAction::triggered, this, [this]() {
2000         d->slotSortByDate();
2001     });
2002 
2003     KToggleAction *byTypeAction = new KToggleAction(i18n("Sort by Type"), this);
2004     d->m_actions[SortByType] = byTypeAction;
2005     connect(byTypeAction, &QAction::triggered, this, [this]() {
2006         d->slotSortByType();
2007     });
2008 
2009     QActionGroup *sortOrderGroup = new QActionGroup(this);
2010     sortOrderGroup->setExclusive(true);
2011 
2012     KToggleAction *ascendingAction = new KToggleAction(i18n("Ascending"), this);
2013     d->m_actions[SortAscending] = ascendingAction;
2014     ascendingAction->setActionGroup(sortOrderGroup);
2015     connect(ascendingAction, &QAction::triggered, this, [this]() {
2016         this->d->slotSortReversed(false);
2017     });
2018 
2019     KToggleAction *descendingAction = new KToggleAction(i18n("Descending"), this);
2020     d->m_actions[SortDescending] = descendingAction;
2021     descendingAction->setActionGroup(sortOrderGroup);
2022     connect(descendingAction, &QAction::triggered, this, [this]() {
2023         this->d->slotSortReversed(true);
2024     });
2025 
2026     KToggleAction *dirsFirstAction = new KToggleAction(i18n("Folders First"), this);
2027     d->m_actions[SortFoldersFirst] = dirsFirstAction;
2028     connect(dirsFirstAction, &QAction::triggered, this, [this]() {
2029         d->slotToggleDirsFirst();
2030     });
2031 
2032     KToggleAction *hiddenFilesLastAction = new KToggleAction(i18n("Hidden Files Last"), this);
2033     d->m_actions[SortHiddenFilesLast] = hiddenFilesLastAction;
2034     connect(hiddenFilesLastAction, &QAction::toggled, this, [this](bool checked) {
2035         d->m_proxyModel->setSortHiddenFilesLast(checked);
2036     });
2037 
2038     // View modes that match those of Dolphin
2039     KToggleAction *iconsViewAction = new KToggleAction(i18n("Icons View"), this);
2040     d->m_actions[ViewIconsView] = iconsViewAction;
2041     iconsViewAction->setIcon(QIcon::fromTheme(QStringLiteral("view-list-icons")));
2042     connect(iconsViewAction, &QAction::triggered, this, [this]() {
2043         d->slotIconsView();
2044     });
2045 
2046     KToggleAction *compactViewAction = new KToggleAction(i18n("Compact View"), this);
2047     d->m_actions[ViewCompactView] = compactViewAction;
2048     compactViewAction->setIcon(QIcon::fromTheme(QStringLiteral("view-list-details")));
2049     connect(compactViewAction, &QAction::triggered, this, [this]() {
2050         d->slotCompactView();
2051     });
2052 
2053     KToggleAction *detailsViewAction = new KToggleAction(i18n("Details View"), this);
2054     d->m_actions[ViewDetailsView] = detailsViewAction;
2055     detailsViewAction->setIcon(QIcon::fromTheme(QStringLiteral("view-list-tree")));
2056     connect(detailsViewAction, &QAction::triggered, this, [this]() {
2057         d->slotDetailsView();
2058     });
2059 
2060     QActionGroup *viewModeGroup = new QActionGroup(this);
2061     viewModeGroup->setExclusive(true);
2062     iconsViewAction->setActionGroup(viewModeGroup);
2063     compactViewAction->setActionGroup(viewModeGroup);
2064     detailsViewAction->setActionGroup(viewModeGroup);
2065 
2066     QActionGroup *sortGroup = new QActionGroup(this);
2067     byNameAction->setActionGroup(sortGroup);
2068     bySizeAction->setActionGroup(sortGroup);
2069     byDateAction->setActionGroup(sortGroup);
2070     byTypeAction->setActionGroup(sortGroup);
2071 
2072     d->m_decorationMenu = new KActionMenu(i18n("Icon Position"), this);
2073     d->m_actions[DecorationMenu] = d->m_decorationMenu;
2074 
2075     d->m_leftAction = new KToggleAction(i18n("Next to File Name"), this);
2076     d->m_actions[DecorationAtLeft] = d->m_leftAction;
2077     connect(d->m_leftAction, &QAction::triggered, this, [this]() {
2078         d->slotChangeDecorationPosition();
2079     });
2080 
2081     KToggleAction *topAction = new KToggleAction(i18n("Above File Name"), this);
2082     d->m_actions[DecorationAtTop] = topAction;
2083     connect(topAction, &QAction::triggered, this, [this]() {
2084         d->slotChangeDecorationPosition();
2085     });
2086 
2087     d->m_decorationMenu->addAction(d->m_leftAction);
2088     d->m_decorationMenu->addAction(topAction);
2089 
2090     QActionGroup *decorationGroup = new QActionGroup(this);
2091     decorationGroup->setExclusive(true);
2092     d->m_leftAction->setActionGroup(decorationGroup);
2093     topAction->setActionGroup(decorationGroup);
2094 
2095     KToggleAction *shortAction = new KToggleAction(i18n("Short View"), this);
2096     d->m_actions[ShortView] = shortAction;
2097     shortAction->setIcon(QIcon::fromTheme(QStringLiteral("view-list-icons")));
2098     connect(shortAction, &QAction::triggered, this, [this]() {
2099         d->slotSimpleView();
2100     });
2101 
2102     KToggleAction *detailedAction = new KToggleAction(i18n("Detailed View"), this);
2103     d->m_actions[DetailedView] = detailedAction;
2104     detailedAction->setIcon(QIcon::fromTheme(QStringLiteral("view-list-details")));
2105     connect(detailedAction, &QAction::triggered, this, [this]() {
2106         d->slotDetailedView();
2107     });
2108 
2109     KToggleAction *treeAction = new KToggleAction(i18n("Tree View"), this);
2110     d->m_actions[TreeView] = treeAction;
2111     treeAction->setIcon(QIcon::fromTheme(QStringLiteral("view-list-tree")));
2112     connect(treeAction, &QAction::triggered, this, [this]() {
2113         d->slotTreeView();
2114     });
2115 
2116     KToggleAction *detailedTreeAction = new KToggleAction(i18n("Detailed Tree View"), this);
2117     d->m_actions[DetailedTreeView] = detailedTreeAction;
2118     detailedTreeAction->setIcon(QIcon::fromTheme(QStringLiteral("view-list-tree")));
2119     connect(detailedTreeAction, &QAction::triggered, this, [this]() {
2120         d->slotDetailedTreeView();
2121     });
2122 
2123     QActionGroup *viewGroup = new QActionGroup(this);
2124     shortAction->setActionGroup(viewGroup);
2125     detailedAction->setActionGroup(viewGroup);
2126     treeAction->setActionGroup(viewGroup);
2127     detailedTreeAction->setActionGroup(viewGroup);
2128 
2129     KToggleAction *allowExpansionAction = new KToggleAction(i18n("Allow Expansion in Details View"), this);
2130     d->m_actions[AllowExpansionInDetailsView] = allowExpansionAction;
2131     connect(allowExpansionAction, &QAction::toggled, this, [this](bool allow) {
2132         d->slotToggleAllowExpansion(allow);
2133     });
2134 
2135     KToggleAction *showHiddenAction = new KToggleAction(i18n("Show Hidden Files"), this);
2136     d->m_actions[ShowHiddenFiles] = showHiddenAction;
2137     showHiddenAction->setShortcuts(KStandardShortcut::showHideHiddenFiles());
2138     connect(showHiddenAction, &QAction::toggled, this, [this](bool show) {
2139         d->slotToggleHidden(show);
2140     });
2141 
2142     KToggleAction *previewAction = new KToggleAction(i18n("Show Preview Panel"), this);
2143     d->m_actions[ShowPreviewPanel] = previewAction;
2144     previewAction->setShortcut(Qt::Key_F11);
2145     connect(previewAction, &QAction::toggled, this, [this](bool enable) {
2146         d->togglePreview(enable);
2147     });
2148 
2149     KToggleAction *inlinePreview = new KToggleAction(QIcon::fromTheme(QStringLiteral("view-preview")), i18n("Show Preview"), this);
2150     d->m_actions[ShowPreview] = inlinePreview;
2151     inlinePreview->setShortcut(Qt::Key_F12);
2152     connect(inlinePreview, &QAction::toggled, this, [this](bool enable) {
2153         d->toggleInlinePreviews(enable);
2154     });
2155 
2156     QAction *fileManager = new QAction(i18n("Open Containing Folder"), this);
2157     d->m_actions[OpenContainingFolder] = fileManager;
2158     fileManager->setIcon(QIcon::fromTheme(QStringLiteral("system-file-manager")));
2159     connect(fileManager, &QAction::triggered, this, [this]() {
2160         d->slotOpenFileManager();
2161     });
2162 
2163     action = new QAction(i18n("Properties"), this);
2164     d->m_actions[Properties] = action;
2165     action->setIcon(QIcon::fromTheme(QStringLiteral("document-properties")));
2166     action->setShortcut(Qt::ALT | Qt::Key_Return);
2167     connect(action, &QAction::triggered, this, [this]() {
2168         d->slotProperties();
2169     });
2170 
2171     // the view menu actions
2172     KActionMenu *viewMenu = new KActionMenu(i18n("&View Mode"), this);
2173     d->m_actions[ViewModeMenu] = viewMenu;
2174     viewMenu->setIcon(QIcon::fromTheme(QStringLiteral("view-list-tree")));
2175     viewMenu->addAction(shortAction);
2176     viewMenu->addAction(detailedAction);
2177     // Comment following lines to hide the extra two modes
2178     viewMenu->addAction(treeAction);
2179     viewMenu->addAction(detailedTreeAction);
2180     // TODO: QAbstractItemView does not offer an action collection. Provide
2181     // an interface to add a custom action collection.
2182 
2183     d->m_itemActions = new KFileItemActions(this);
2184 
2185     d->m_newFileMenu = new KNewFileMenu(this);
2186     d->m_actions[KDirOperator::New] = d->m_newFileMenu;
2187     connect(d->m_newFileMenu, &KNewFileMenu::directoryCreated, this, [this](const QUrl &url) {
2188         d->slotDirectoryCreated(url);
2189     });
2190     connect(d->m_newFileMenu, &KNewFileMenu::selectExistingDir, this, [this](const QUrl &url) {
2191         setCurrentItem(url);
2192     });
2193 
2194     const QList<QAction *> list = d->m_actions.values();
2195     for (QAction *action : list) {
2196         addAction(action);
2197         action->setShortcutContext(Qt::WidgetWithChildrenShortcut);
2198     }
2199 }
2200 
2201 void KDirOperator::setupMenu()
2202 {
2203     setupMenu(SortActions | ViewActions | FileActions);
2204 }
2205 
2206 void KDirOperator::setupMenu(int whichActions)
2207 {
2208     // first fill the submenus (sort and view)
2209     KActionMenu *sortMenu = static_cast<KActionMenu *>(action(KDirOperator::SortMenu));
2210     sortMenu->menu()->clear();
2211     sortMenu->addAction(action(KDirOperator::SortByName));
2212     sortMenu->addAction(action(KDirOperator::SortBySize));
2213     sortMenu->addAction(action(KDirOperator::SortByDate));
2214     sortMenu->addAction(action(KDirOperator::SortByType));
2215     sortMenu->addSeparator();
2216     sortMenu->addAction(action(KDirOperator::SortAscending));
2217     sortMenu->addAction(action(KDirOperator::SortDescending));
2218     sortMenu->addSeparator();
2219     sortMenu->addAction(action(KDirOperator::SortFoldersFirst));
2220     sortMenu->addAction(action(KDirOperator::SortHiddenFilesLast));
2221 
2222     // now plug everything into the popupmenu
2223     d->m_actionMenu->menu()->clear();
2224     if (whichActions & NavActions) {
2225         d->m_actionMenu->addAction(action(KDirOperator::Up));
2226         d->m_actionMenu->addAction(action(KDirOperator::Back));
2227         d->m_actionMenu->addAction(action(KDirOperator::Forward));
2228         d->m_actionMenu->addAction(action(KDirOperator::Home));
2229         d->m_actionMenu->addSeparator();
2230     }
2231 
2232     if (whichActions & FileActions) {
2233         d->m_actionMenu->addAction(action(KDirOperator::New));
2234 
2235         d->m_actionMenu->addAction(action(KDirOperator::Rename));
2236         action(KDirOperator::Rename)->setEnabled(KProtocolManager::supportsMoving(d->m_currUrl));
2237 
2238         if (d->m_currUrl.isLocalFile() && !(QApplication::keyboardModifiers() & Qt::ShiftModifier)) {
2239             d->m_actionMenu->addAction(action(KDirOperator::Trash));
2240         }
2241         KConfigGroup cg(KSharedConfig::openConfig(), QStringLiteral("KDE"));
2242         const bool del = !d->m_currUrl.isLocalFile() || (QApplication::keyboardModifiers() & Qt::ShiftModifier) || cg.readEntry("ShowDeleteCommand", false);
2243         if (del) {
2244             d->m_actionMenu->addAction(action(KDirOperator::Delete));
2245         }
2246         d->m_actionMenu->addSeparator();
2247     }
2248 
2249     if (whichActions & SortActions) {
2250         d->m_actionMenu->addAction(sortMenu);
2251         if (!(whichActions & ViewActions)) {
2252             d->m_actionMenu->addSeparator();
2253         }
2254     }
2255 
2256     if (whichActions & ViewActions) {
2257         d->m_actionMenu->addAction(action(KDirOperator::ViewModeMenu));
2258         d->m_actionMenu->addAction(action(KDirOperator::Reload));
2259         d->m_actionMenu->addSeparator();
2260     }
2261 
2262     if (whichActions & FileActions) {
2263         d->m_actionMenu->addAction(action(KDirOperator::OpenContainingFolder));
2264         d->m_actionMenu->addAction(action(KDirOperator::Properties));
2265     }
2266 }
2267 
2268 void KDirOperator::updateSortActions()
2269 {
2270     QAction *ascending = action(KDirOperator::SortAscending);
2271     QAction *descending = action(KDirOperator::SortDescending);
2272 
2273     if (KFile::isSortByName(d->m_sorting)) {
2274         action(KDirOperator::SortByName)->setChecked(true);
2275         descending->setText(i18nc("Sort descending", "Z-A"));
2276         ascending->setText(i18nc("Sort ascending", "A-Z"));
2277     } else if (KFile::isSortByDate(d->m_sorting)) {
2278         action(KDirOperator::SortByDate)->setChecked(true);
2279         descending->setText(i18nc("Sort descending", "Newest First"));
2280         ascending->setText(i18nc("Sort ascending", "Oldest First"));
2281     } else if (KFile::isSortBySize(d->m_sorting)) {
2282         action(KDirOperator::SortBySize)->setChecked(true);
2283         descending->setText(i18nc("Sort descending", "Largest First"));
2284         ascending->setText(i18nc("Sort ascending", "Smallest First"));
2285     } else if (KFile::isSortByType(d->m_sorting)) {
2286         action(KDirOperator::SortByType)->setChecked(true);
2287         descending->setText(i18nc("Sort descending", "Z-A"));
2288         ascending->setText(i18nc("Sort ascending", "A-Z"));
2289     }
2290     ascending->setChecked(!(d->m_sorting & QDir::Reversed));
2291     descending->setChecked(d->m_sorting & QDir::Reversed);
2292     action(KDirOperator::SortFoldersFirst)->setChecked(d->m_sorting & QDir::DirsFirst);
2293 }
2294 
2295 void KDirOperator::updateViewActions()
2296 {
2297     KFile::FileView fv = static_cast<KFile::FileView>(d->m_viewKind);
2298 
2299     // QAction *separateDirs = d->actionCollection->action("separate dirs");
2300     // separateDirs->setChecked(KFile::isSeparateDirs(fv) &&
2301     //                         separateDirs->isEnabled());
2302 
2303     action(KDirOperator::ShortView)->setChecked(KFile::isSimpleView(fv));
2304     action(KDirOperator::DetailedView)->setChecked(KFile::isDetailView(fv));
2305     action(KDirOperator::TreeView)->setChecked(KFile::isTreeView(fv));
2306     action(KDirOperator::DetailedTreeView)->setChecked(KFile::isDetailTreeView(fv));
2307 
2308     // dolphin style views
2309     action(KDirOperator::ViewIconsView)->setChecked(KFile::isSimpleView(fv) && d->m_decorationPosition == QStyleOptionViewItem::Top);
2310     action(KDirOperator::ViewCompactView)->setChecked(KFile::isSimpleView(fv) && d->m_decorationPosition == QStyleOptionViewItem::Left);
2311     action(KDirOperator::ViewDetailsView)->setChecked(KFile::isDetailTreeView(fv) || KFile::isDetailView(fv));
2312 }
2313 
2314 void KDirOperator::readConfig(const KConfigGroup &configGroup)
2315 {
2316     d->m_defaultView = 0;
2317     QString viewStyle = configGroup.readEntry("View Style", "DetailTree");
2318     if (viewStyle == QLatin1String("Detail")) {
2319         d->m_defaultView |= KFile::Detail;
2320     } else if (viewStyle == QLatin1String("Tree")) {
2321         d->m_defaultView |= KFile::Tree;
2322     } else if (viewStyle == QLatin1String("DetailTree")) {
2323         d->m_defaultView |= KFile::DetailTree;
2324     } else {
2325         d->m_defaultView |= KFile::Simple;
2326     }
2327     // if (configGroup.readEntry(QLatin1String("Separate Directories"),
2328     //                          DefaultMixDirsAndFiles)) {
2329     //    d->defaultView |= KFile::SeparateDirs;
2330     //}
2331     if (configGroup.readEntry(QStringLiteral("Show Preview"), false)) {
2332         d->m_defaultView |= KFile::PreviewContents;
2333     }
2334 
2335     d->m_previewWidth = configGroup.readEntry(QStringLiteral("Preview Width"), 100);
2336 
2337     if (configGroup.readEntry(QStringLiteral("Show hidden files"), DefaultShowHidden)) {
2338         action(KDirOperator::ShowHiddenFiles)->setChecked(true);
2339         d->m_dirLister->setShowHiddenFiles(true);
2340     }
2341 
2342     if (configGroup.readEntry(QStringLiteral("Allow Expansion"), DefaultShowHidden)) {
2343         action(KDirOperator::AllowExpansionInDetailsView)->setChecked(true);
2344     }
2345 
2346     const bool hiddenFilesLast = configGroup.readEntry(QStringLiteral("Sort hidden files last"), DefaultHiddenFilesLast);
2347     action(KDirOperator::SortHiddenFilesLast)->setChecked(hiddenFilesLast);
2348 
2349     QDir::SortFlags sorting = QDir::Name;
2350     if (configGroup.readEntry(QStringLiteral("Sort directories first"), DefaultDirsFirst)) {
2351         sorting |= QDir::DirsFirst;
2352     }
2353     QString name = QStringLiteral("Name");
2354     QString sortBy = configGroup.readEntry(QStringLiteral("Sort by"), name);
2355     if (sortBy == name) {
2356         sorting |= QDir::Name;
2357     } else if (sortBy == QLatin1String("Size")) {
2358         sorting |= QDir::Size;
2359     } else if (sortBy == QLatin1String("Date")) {
2360         sorting |= QDir::Time;
2361     } else if (sortBy == QLatin1String("Type")) {
2362         sorting |= QDir::Type;
2363     }
2364     if (configGroup.readEntry(QStringLiteral("Sort reversed"), DefaultSortReversed)) {
2365         sorting |= QDir::Reversed;
2366     }
2367     d->updateSorting(sorting);
2368 
2369     if (d->m_inlinePreviewState == KDirOperatorPrivate::NotForced) {
2370         d->m_showPreviews = configGroup.readEntry(QStringLiteral("Show Inline Previews"), true);
2371     }
2372     QStyleOptionViewItem::Position pos =
2373         (QStyleOptionViewItem::Position)configGroup.readEntry(QStringLiteral("Decoration position"), (int)QStyleOptionViewItem::Top);
2374     setDecorationPosition(pos);
2375 }
2376 
2377 void KDirOperator::writeConfig(KConfigGroup &configGroup)
2378 {
2379     QString sortBy = QStringLiteral("Name");
2380     if (KFile::isSortBySize(d->m_sorting)) {
2381         sortBy = QStringLiteral("Size");
2382     } else if (KFile::isSortByDate(d->m_sorting)) {
2383         sortBy = QStringLiteral("Date");
2384     } else if (KFile::isSortByType(d->m_sorting)) {
2385         sortBy = QStringLiteral("Type");
2386     }
2387 
2388     configGroup.writeEntry(QStringLiteral("Sort by"), sortBy);
2389 
2390     configGroup.writeEntry(QStringLiteral("Sort reversed"), action(KDirOperator::SortDescending)->isChecked());
2391 
2392     configGroup.writeEntry(QStringLiteral("Sort directories first"), action(KDirOperator::SortFoldersFirst)->isChecked());
2393 
2394     const bool hiddenFilesLast = action(KDirOperator::SortHiddenFilesLast)->isChecked();
2395     configGroup.writeEntry(QStringLiteral("Sort hidden files last"), hiddenFilesLast);
2396 
2397     // don't save the preview when an application specific preview is in use.
2398     bool appSpecificPreview = false;
2399     if (d->m_preview) {
2400         KFileMetaPreview *tmp = dynamic_cast<KFileMetaPreview *>(d->m_preview);
2401         appSpecificPreview = (tmp == nullptr);
2402     }
2403 
2404     if (!appSpecificPreview) {
2405         KToggleAction *previewAction = static_cast<KToggleAction *>(action(KDirOperator::ShowPreviewPanel));
2406         if (previewAction->isEnabled()) {
2407             bool hasPreview = previewAction->isChecked();
2408             configGroup.writeEntry(QStringLiteral("Show Preview"), hasPreview);
2409 
2410             if (hasPreview) {
2411                 // remember the width of the preview widget
2412                 QList<int> sizes = d->m_splitter->sizes();
2413                 Q_ASSERT(sizes.count() == 2);
2414                 configGroup.writeEntry(QStringLiteral("Preview Width"), sizes[1]);
2415             }
2416         }
2417     }
2418 
2419     configGroup.writeEntry(QStringLiteral("Show hidden files"), action(KDirOperator::ShowHiddenFiles)->isChecked());
2420 
2421     configGroup.writeEntry(QStringLiteral("Allow Expansion"), action(KDirOperator::AllowExpansionInDetailsView)->isChecked());
2422 
2423     KFile::FileView fv = static_cast<KFile::FileView>(d->m_viewKind);
2424     QString style;
2425     if (KFile::isDetailView(fv)) {
2426         style = QStringLiteral("Detail");
2427     } else if (KFile::isSimpleView(fv)) {
2428         style = QStringLiteral("Simple");
2429     } else if (KFile::isTreeView(fv)) {
2430         style = QStringLiteral("Tree");
2431     } else if (KFile::isDetailTreeView(fv)) {
2432         style = QStringLiteral("DetailTree");
2433     }
2434     configGroup.writeEntry(QStringLiteral("View Style"), style);
2435 
2436     if (d->m_inlinePreviewState == KDirOperatorPrivate::NotForced) {
2437         configGroup.writeEntry(QStringLiteral("Show Inline Previews"), d->m_showPreviews);
2438         d->writeIconZoomSettingsIfNeeded();
2439     }
2440 
2441     configGroup.writeEntry(QStringLiteral("Decoration position"), (int)d->m_decorationPosition);
2442 }
2443 
2444 void KDirOperatorPrivate::writeIconZoomSettingsIfNeeded()
2445 {
2446     // must match behavior of iconSizeForViewType
2447     if (m_configGroup && m_itemView) {
2448         ZoomSettingsForView zoomSettings = zoomSettingsForView();
2449         m_configGroup->writeEntry(zoomSettings.name, m_iconSize);
2450     }
2451 }
2452 
2453 void KDirOperator::resizeEvent(QResizeEvent *)
2454 {
2455     // resize the m_splitter and assure that the width of
2456     // the preview widget is restored
2457     QList<int> sizes = d->m_splitter->sizes();
2458     const bool hasPreview = (sizes.count() == 2);
2459 
2460     d->m_splitter->resize(size());
2461     sizes = d->m_splitter->sizes();
2462 
2463     const bool restorePreviewWidth = hasPreview && (d->m_previewWidth != sizes[1]);
2464     if (restorePreviewWidth) {
2465         const int availableWidth = sizes[0] + sizes[1];
2466         sizes[0] = availableWidth - d->m_previewWidth;
2467         sizes[1] = d->m_previewWidth;
2468         d->m_splitter->setSizes(sizes);
2469     }
2470     if (hasPreview) {
2471         d->m_previewWidth = sizes[1];
2472     }
2473 
2474     if (d->m_progressBar->parent() == this) {
2475         // Might be reparented into a statusbar
2476         const int frameWidth = style()->pixelMetric(QStyle::PM_DefaultFrameWidth);
2477         d->m_progressBar->move(frameWidth, height() - d->m_progressBar->height() - frameWidth);
2478     }
2479 }
2480 
2481 void KDirOperator::setOnlyDoubleClickSelectsFiles(bool enable)
2482 {
2483     d->m_onlyDoubleClickSelectsFiles = enable;
2484     // TODO: port to QAbstractItemModel
2485     // if (d->itemView != 0) {
2486     //    d->itemView->setOnlyDoubleClickSelectsFiles(enable);
2487     //}
2488 }
2489 
2490 bool KDirOperator::onlyDoubleClickSelectsFiles() const
2491 {
2492     return d->m_onlyDoubleClickSelectsFiles;
2493 }
2494 
2495 void KDirOperator::setFollowNewDirectories(bool enable)
2496 {
2497     d->m_followNewDirectories = enable;
2498 }
2499 
2500 bool KDirOperator::followNewDirectories() const
2501 {
2502     return d->m_followNewDirectories;
2503 }
2504 
2505 void KDirOperator::setFollowSelectedDirectories(bool enable)
2506 {
2507     d->m_followSelectedDirectories = enable;
2508 }
2509 
2510 bool KDirOperator::followSelectedDirectories() const
2511 {
2512     return d->m_followSelectedDirectories;
2513 }
2514 
2515 void KDirOperatorPrivate::slotStarted()
2516 {
2517     m_progressBar->setValue(0);
2518     // delay showing the progressbar for one second
2519     m_progressDelayTimer->setSingleShot(true);
2520     m_progressDelayTimer->start(1000);
2521 }
2522 
2523 void KDirOperatorPrivate::slotShowProgress()
2524 {
2525     m_progressBar->raise();
2526     m_progressBar->show();
2527 }
2528 
2529 void KDirOperatorPrivate::slotProgress(int percent)
2530 {
2531     m_progressBar->setValue(percent);
2532 }
2533 
2534 void KDirOperatorPrivate::slotIOFinished()
2535 {
2536     m_progressDelayTimer->stop();
2537     slotProgress(100);
2538     m_progressBar->hide();
2539     Q_EMIT q->finishedLoading();
2540     q->resetCursor();
2541 
2542     if (m_preview) {
2543         m_preview->clearPreview();
2544     }
2545 
2546     // m_lastUrl can be empty when e.g. kfilewidget is first opened
2547     if (!m_lastUrl.isEmpty() && m_dirHighlighting) {
2548         q->setCurrentItem(m_lastUrl);
2549     }
2550 }
2551 
2552 void KDirOperatorPrivate::slotCanceled()
2553 {
2554     Q_EMIT q->finishedLoading();
2555     q->resetCursor();
2556 }
2557 
2558 QProgressBar *KDirOperator::progressBar() const
2559 {
2560     return d->m_progressBar;
2561 }
2562 
2563 void KDirOperator::clearHistory()
2564 {
2565     qDeleteAll(d->m_backStack);
2566     d->m_backStack.clear();
2567     action(KDirOperator::Back)->setEnabled(false);
2568 
2569     qDeleteAll(d->m_forwardStack);
2570     d->m_forwardStack.clear();
2571     action(KDirOperator::Forward)->setEnabled(false);
2572 }
2573 
2574 void KDirOperator::setEnableDirHighlighting(bool enable)
2575 {
2576     d->m_dirHighlighting = enable;
2577 }
2578 
2579 bool KDirOperator::dirHighlighting() const
2580 {
2581     return d->m_dirHighlighting;
2582 }
2583 
2584 bool KDirOperator::dirOnlyMode() const
2585 {
2586     return dirOnlyMode(d->m_mode);
2587 }
2588 
2589 bool KDirOperator::dirOnlyMode(uint mode)
2590 {
2591     return ((mode & KFile::Directory) && (mode & (KFile::File | KFile::Files)) == 0);
2592 }
2593 
2594 void KDirOperatorPrivate::slotProperties()
2595 {
2596     if (m_itemView == nullptr) {
2597         return;
2598     }
2599 
2600     const KFileItemList list = q->selectedItems();
2601     if (!list.isEmpty()) {
2602         auto *dialog = new KPropertiesDialog(list, q);
2603         dialog->setAttribute(Qt::WA_DeleteOnClose);
2604         dialog->setModal(true);
2605         dialog->show();
2606     }
2607 }
2608 
2609 void KDirOperatorPrivate::slotActivated(const QModelIndex &index)
2610 {
2611     const QModelIndex dirIndex = m_proxyModel->mapToSource(index);
2612     KFileItem item = m_dirModel->itemForIndex(dirIndex);
2613 
2614     const Qt::KeyboardModifiers modifiers = QApplication::keyboardModifiers();
2615     if (item.isNull() || (modifiers & Qt::ShiftModifier) || (modifiers & Qt::ControlModifier)) {
2616         return;
2617     }
2618 
2619     if (item.isDir()) {
2620         // Only allow disabling following selected directories on Tree and
2621         // DetailTree views as selected directories in these views still expand
2622         // when selected. For other views, disabling following selected
2623         // directories would make selecting a directory a noop which is
2624         // unintuitive.
2625         if (m_followSelectedDirectories || (m_viewKind != KFile::Tree && m_viewKind != KFile::DetailTree)) {
2626             q->selectDir(item);
2627         }
2628     } else {
2629         q->selectFile(item);
2630     }
2631 }
2632 
2633 void KDirOperatorPrivate::slotSelectionChanged()
2634 {
2635     if (m_itemView == nullptr) {
2636         return;
2637     }
2638 
2639     // In the multiselection mode each selection change is indicated by
2640     // emitting a null item. Also when the selection has been cleared, a
2641     // null item must be emitted.
2642     const bool multiSelectionMode = (m_itemView->selectionMode() == QAbstractItemView::ExtendedSelection);
2643     const bool hasSelection = m_itemView->selectionModel()->hasSelection();
2644     if (multiSelectionMode || !hasSelection) {
2645         KFileItem nullItem;
2646         q->highlightFile(nullItem);
2647     } else {
2648         const KFileItem selectedItem = q->selectedItems().constFirst();
2649         q->highlightFile(selectedItem);
2650     }
2651 }
2652 
2653 void KDirOperatorPrivate::openContextMenu(const QPoint &pos)
2654 {
2655     const QModelIndex proxyIndex = m_itemView->indexAt(pos);
2656     const QModelIndex dirIndex = m_proxyModel->mapToSource(proxyIndex);
2657     KFileItem item = m_dirModel->itemForIndex(dirIndex);
2658 
2659     if (item.isNull()) {
2660         return;
2661     }
2662 
2663     q->activatedMenu(item, QCursor::pos());
2664 }
2665 
2666 void KDirOperatorPrivate::triggerPreview(const QModelIndex &index)
2667 {
2668     if ((m_preview != nullptr && !m_preview->isHidden()) && index.isValid() && (index.column() == KDirModel::Name)) {
2669         const QModelIndex dirIndex = m_proxyModel->mapToSource(index);
2670         const KFileItem item = m_dirModel->itemForIndex(dirIndex);
2671 
2672         if (item.isNull()) {
2673             return;
2674         }
2675 
2676         if (!item.isDir()) {
2677             m_previewUrl = item.url();
2678             showPreview();
2679         } else {
2680             m_preview->clearPreview();
2681         }
2682     }
2683 }
2684 
2685 void KDirOperatorPrivate::showPreview()
2686 {
2687     if (m_preview != nullptr) {
2688         m_preview->showPreview(m_previewUrl);
2689     }
2690 }
2691 
2692 void KDirOperatorPrivate::slotSplitterMoved(int, int)
2693 {
2694     const QList<int> sizes = m_splitter->sizes();
2695     if (sizes.count() == 2) {
2696         // remember the width of the preview widget (see KDirOperator::resizeEvent())
2697         m_previewWidth = sizes[1];
2698     }
2699 }
2700 
2701 void KDirOperatorPrivate::assureVisibleSelection()
2702 {
2703     if (m_itemView == nullptr) {
2704         return;
2705     }
2706 
2707     QItemSelectionModel *selModel = m_itemView->selectionModel();
2708     if (selModel->hasSelection()) {
2709         const QModelIndex index = selModel->currentIndex();
2710         m_itemView->scrollTo(index, QAbstractItemView::EnsureVisible);
2711         triggerPreview(index);
2712     }
2713 }
2714 
2715 void KDirOperatorPrivate::synchronizeSortingState(int logicalIndex, Qt::SortOrder order)
2716 {
2717     QDir::SortFlags newSort = m_sorting & ~(QDirSortMask | QDir::Reversed);
2718 
2719     switch (logicalIndex) {
2720     case KDirModel::Name:
2721         newSort |= QDir::Name;
2722         break;
2723     case KDirModel::Size:
2724         newSort |= QDir::Size;
2725         break;
2726     case KDirModel::ModifiedTime:
2727         newSort |= QDir::Time;
2728         break;
2729     case KDirModel::Type:
2730         newSort |= QDir::Type;
2731         break;
2732     default:
2733         Q_ASSERT(false);
2734     }
2735 
2736     if (order == Qt::DescendingOrder) {
2737         newSort |= QDir::Reversed;
2738     }
2739 
2740     updateSorting(newSort);
2741 
2742     QMetaObject::invokeMethod(
2743         q,
2744         [this]() {
2745             assureVisibleSelection();
2746         },
2747         Qt::QueuedConnection);
2748 }
2749 
2750 void KDirOperatorPrivate::slotChangeDecorationPosition()
2751 {
2752     if (!m_itemView) {
2753         return;
2754     }
2755 
2756     KDirOperatorIconView *view = qobject_cast<KDirOperatorIconView *>(m_itemView);
2757 
2758     if (!view) {
2759         return;
2760     }
2761 
2762     const bool leftChecked = q->action(KDirOperator::DecorationAtLeft)->isChecked();
2763 
2764     if (leftChecked) {
2765         view->setDecorationPosition(QStyleOptionViewItem::Left);
2766     } else {
2767         view->setDecorationPosition(QStyleOptionViewItem::Top);
2768     }
2769 
2770     m_itemView->update();
2771 }
2772 
2773 void KDirOperatorPrivate::slotExpandToUrl(const QModelIndex &index)
2774 {
2775     QTreeView *treeView = qobject_cast<QTreeView *>(m_itemView);
2776 
2777     if (!treeView) {
2778         return;
2779     }
2780 
2781     const KFileItem item = m_dirModel->itemForIndex(index);
2782 
2783     if (item.isNull()) {
2784         return;
2785     }
2786 
2787     if (!item.isDir()) {
2788         const QModelIndex proxyIndex = m_proxyModel->mapFromSource(index);
2789 
2790         QList<QUrl>::Iterator it = m_itemsToBeSetAsCurrent.begin();
2791         while (it != m_itemsToBeSetAsCurrent.end()) {
2792             const QUrl url = *it;
2793             if (url.matches(item.url(), QUrl::StripTrailingSlash) || url.isParentOf(item.url())) {
2794                 const KFileItem _item = m_dirLister->findByUrl(url);
2795                 if (!_item.isNull() && _item.isDir()) {
2796                     const QModelIndex _index = m_dirModel->indexForItem(_item);
2797                     const QModelIndex _proxyIndex = m_proxyModel->mapFromSource(_index);
2798                     treeView->expand(_proxyIndex);
2799 
2800                     // if we have expanded the last parent of this item, select it
2801                     if (item.url().adjusted(QUrl::RemoveFilename | QUrl::StripTrailingSlash) == url.adjusted(QUrl::StripTrailingSlash)) {
2802                         treeView->selectionModel()->select(proxyIndex, QItemSelectionModel::Select);
2803                     }
2804                 }
2805                 it = m_itemsToBeSetAsCurrent.erase(it);
2806             } else {
2807                 ++it;
2808             }
2809         }
2810     } else if (!m_itemsToBeSetAsCurrent.contains(item.url())) {
2811         m_itemsToBeSetAsCurrent << item.url();
2812     }
2813 }
2814 
2815 void KDirOperatorPrivate::slotItemsChanged()
2816 {
2817     m_completeListDirty = true;
2818 }
2819 
2820 int KDirOperatorPrivate::iconSizeForViewType(QAbstractItemView *itemView) const
2821 {
2822     // must match behavior of writeIconZoomSettingsIfNeeded
2823     if (!itemView || !m_configGroup) {
2824         return 0;
2825     }
2826 
2827     ZoomSettingsForView zoomSettings = zoomSettingsForView();
2828     return m_configGroup->readEntry(zoomSettings.name, zoomSettings.defaultValue);
2829 }
2830 
2831 KDirOperatorPrivate::ZoomSettingsForView KDirOperatorPrivate::zoomSettingsForView() const
2832 {
2833     KFile::FileView fv = static_cast<KFile::FileView>(m_viewKind);
2834 
2835     KSharedConfigPtr config = KSharedConfig::openConfig();
2836     if (KFile::isSimpleView(fv)) {
2837         KConfigGroup cg(config, QStringLiteral("DesktopIcons"));
2838         const int desktopIconSize = cg.readEntry("Size", static_cast<int>(KIconLoader::SizeHuge));
2839         if (m_decorationPosition == QStyleOptionViewItem::Top) {
2840             // Simple view decoration above, aka Icons View
2841             return {QStringLiteral("iconViewIconSize"), desktopIconSize};
2842         } else {
2843             // Simple view decoration left, aka compact view
2844             return {QStringLiteral("listViewIconSize"), desktopIconSize};
2845         }
2846     }
2847 
2848     const int smallIconSize = static_cast<int>(KIconLoader::SizeSmall);
2849     if (KFile::isTreeView(fv)) {
2850         return {QStringLiteral("treeViewIconSize"), smallIconSize};
2851     } else {
2852         // DetailView and DetailTreeView
2853         return {QStringLiteral("detailViewIconSize"), smallIconSize};
2854     }
2855 }
2856 
2857 void KDirOperator::setViewConfig(KConfigGroup &configGroup)
2858 {
2859     delete d->m_configGroup;
2860     d->m_configGroup = new KConfigGroup(configGroup);
2861 }
2862 
2863 KConfigGroup *KDirOperator::viewConfigGroup() const
2864 {
2865     return d->m_configGroup;
2866 }
2867 
2868 void KDirOperator::setShowHiddenFiles(bool s)
2869 {
2870     action(KDirOperator::ShowHiddenFiles)->setChecked(s);
2871 }
2872 
2873 bool KDirOperator::showHiddenFiles() const
2874 {
2875     return action(KDirOperator::ShowHiddenFiles)->isChecked();
2876 }
2877 
2878 QStyleOptionViewItem::Position KDirOperator::decorationPosition() const
2879 {
2880     return d->m_decorationPosition;
2881 }
2882 
2883 void KDirOperator::setDecorationPosition(QStyleOptionViewItem::Position position)
2884 {
2885     d->m_decorationPosition = position;
2886     const bool decorationAtLeft = d->m_decorationPosition == QStyleOptionViewItem::Left;
2887     action(KDirOperator::DecorationAtLeft)->setChecked(decorationAtLeft);
2888     action(KDirOperator::DecorationAtTop)->setChecked(!decorationAtLeft);
2889 }
2890 
2891 bool KDirOperatorPrivate::isReadable(const QUrl &url)
2892 {
2893     if (!url.isLocalFile()) {
2894         return true; // what else can we say?
2895     }
2896     const QFileInfo fileInfo(url.toLocalFile());
2897 #ifdef Q_OS_WIN
2898     return fileInfo.isReadable() && fileInfo.isDir();
2899 #else
2900     return fileInfo.isReadable();
2901 #endif
2902 }
2903 
2904 void KDirOperatorPrivate::slotDirectoryCreated(const QUrl &url)
2905 {
2906     if (m_followNewDirectories) {
2907         q->setUrl(url, true);
2908     }
2909 }
2910 
2911 void KDirOperator::setSupportedSchemes(const QStringList &schemes)
2912 {
2913     d->m_supportedSchemes = schemes;
2914     rereadDir();
2915 }
2916 
2917 QStringList KDirOperator::supportedSchemes() const
2918 {
2919     return d->m_supportedSchemes;
2920 }
2921 
2922 #include "moc_kdiroperator.cpp"