File indexing completed on 2024-09-08 03:38:59
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"