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