File indexing completed on 2024-03-24 17:22:47
0001 /* 0002 * SPDX-FileCopyrightText: 2007 Peter Penz <peter.penz19@gmail.com> 0003 * 0004 * SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 #include "dolphinviewcontainer.h" 0008 0009 #include "dolphin_compactmodesettings.h" 0010 #include "dolphin_contentdisplaysettings.h" 0011 #include "dolphin_detailsmodesettings.h" 0012 #include "dolphin_generalsettings.h" 0013 #include "dolphin_iconsmodesettings.h" 0014 #include "dolphindebug.h" 0015 #include "dolphinplacesmodelsingleton.h" 0016 #include "filterbar/filterbar.h" 0017 #include "global.h" 0018 #include "search/dolphinsearchbox.h" 0019 #include "selectionmode/topbar.h" 0020 #include "statusbar/dolphinstatusbar.h" 0021 0022 #include <KActionCollection> 0023 #if HAVE_PLASMA_ACTIVITIES 0024 #include <PlasmaActivities/ResourceInstance> 0025 #endif 0026 #include <KApplicationTrader> 0027 #include <KFileItemActions> 0028 #include <KFilePlacesModel> 0029 #include <KIO/JobUiDelegateFactory> 0030 #include <KIO/OpenUrlJob> 0031 #include <KLocalizedString> 0032 #include <KMessageWidget> 0033 #include <KProtocolManager> 0034 #include <KShell> 0035 #include <kio_version.h> 0036 0037 #include <QApplication> 0038 #include <QDesktopServices> 0039 #include <QDropEvent> 0040 #include <QGridLayout> 0041 #include <QGuiApplication> 0042 #include <QRegularExpression> 0043 #include <QTimer> 0044 #include <QUrl> 0045 0046 // An overview of the widgets contained by this ViewContainer 0047 struct LayoutStructure { 0048 int searchBox = 0; 0049 int messageWidget = 1; 0050 int selectionModeTopBar = 2; 0051 int view = 3; 0052 int selectionModeBottomBar = 4; 0053 int filterBar = 5; 0054 int statusBar = 6; 0055 }; 0056 constexpr LayoutStructure positionFor; 0057 0058 DolphinViewContainer::DolphinViewContainer(const QUrl &url, QWidget *parent) 0059 : QWidget(parent) 0060 , m_topLayout(nullptr) 0061 , m_urlNavigator{new DolphinUrlNavigator(url)} 0062 , m_urlNavigatorConnected{nullptr} 0063 , m_searchBox(nullptr) 0064 , m_searchModeEnabled(false) 0065 , m_messageWidget(nullptr) 0066 , m_selectionModeTopBar{nullptr} 0067 , m_view(nullptr) 0068 , m_filterBar(nullptr) 0069 , m_selectionModeBottomBar{nullptr} 0070 , m_statusBar(nullptr) 0071 , m_statusBarTimer(nullptr) 0072 , m_statusBarTimestamp() 0073 , m_autoGrabFocus(true) 0074 { 0075 hide(); 0076 0077 m_topLayout = new QGridLayout(this); 0078 m_topLayout->setSpacing(0); 0079 m_topLayout->setContentsMargins(0, 0, 0, 0); 0080 0081 m_searchBox = new DolphinSearchBox(this); 0082 m_searchBox->hide(); 0083 connect(m_searchBox, &DolphinSearchBox::activated, this, &DolphinViewContainer::activate); 0084 connect(m_searchBox, &DolphinSearchBox::openRequest, this, &DolphinViewContainer::openSearchBox); 0085 connect(m_searchBox, &DolphinSearchBox::closeRequest, this, &DolphinViewContainer::closeSearchBox); 0086 connect(m_searchBox, &DolphinSearchBox::searchRequest, this, &DolphinViewContainer::startSearching); 0087 connect(m_searchBox, &DolphinSearchBox::focusViewRequest, this, &DolphinViewContainer::requestFocus); 0088 m_searchBox->setWhatsThis(xi18nc("@info:whatsthis findbar", 0089 "<para>This helps you find files and folders. Enter a <emphasis>" 0090 "search term</emphasis> and specify search settings with the " 0091 "buttons at the bottom:<list><item>Filename/Content: " 0092 "Does the item you are looking for contain the search terms " 0093 "within its filename or its contents?<nl/>The contents of images, " 0094 "audio files and videos will not be searched.</item><item>" 0095 "From Here/Everywhere: Do you want to search in this " 0096 "folder and its sub-folders or everywhere?</item><item>" 0097 "More Options: Click this to search by media type, access " 0098 "time or rating.</item><item>More Search Tools: Install other " 0099 "means to find an item.</item></list></para>")); 0100 0101 m_messageWidget = new KMessageWidget(this); 0102 m_messageWidget->setCloseButtonVisible(true); 0103 m_messageWidget->setPosition(KMessageWidget::Header); 0104 m_messageWidget->hide(); 0105 0106 #ifndef Q_OS_WIN 0107 if (getuid() == 0) { 0108 // We must be logged in as the root user; show a big scary warning 0109 showMessage(i18n("Running Dolphin as root can be dangerous. Please be careful."), Warning); 0110 } 0111 #endif 0112 0113 // Initialize filter bar 0114 m_filterBar = new FilterBar(this); 0115 m_filterBar->setVisible(GeneralSettings::filterBar()); 0116 0117 connect(m_filterBar, &FilterBar::filterChanged, this, &DolphinViewContainer::setNameFilter); 0118 connect(m_filterBar, &FilterBar::closeRequest, this, &DolphinViewContainer::closeFilterBar); 0119 connect(m_filterBar, &FilterBar::focusViewRequest, this, &DolphinViewContainer::requestFocus); 0120 0121 // Initialize the main view 0122 m_view = new DolphinView(url, this); 0123 connect(m_view, &DolphinView::urlChanged, m_filterBar, &FilterBar::clearIfUnlocked); 0124 connect(m_view, &DolphinView::urlChanged, m_messageWidget, &KMessageWidget::hide); 0125 // m_urlNavigator stays in sync with m_view's location changes and 0126 // keeps track of them so going back and forth in the history works. 0127 connect(m_view, &DolphinView::urlChanged, m_urlNavigator.get(), &DolphinUrlNavigator::setLocationUrl); 0128 connect(m_urlNavigator.get(), &DolphinUrlNavigator::urlChanged, this, &DolphinViewContainer::slotUrlNavigatorLocationChanged); 0129 connect(m_urlNavigator.get(), &DolphinUrlNavigator::urlAboutToBeChanged, this, &DolphinViewContainer::slotUrlNavigatorLocationAboutToBeChanged); 0130 connect(m_urlNavigator.get(), &DolphinUrlNavigator::urlSelectionRequested, this, &DolphinViewContainer::slotUrlSelectionRequested); 0131 connect(m_view, &DolphinView::writeStateChanged, this, &DolphinViewContainer::writeStateChanged); 0132 connect(m_view, &DolphinView::requestItemInfo, this, &DolphinViewContainer::showItemInfo); 0133 connect(m_view, &DolphinView::itemActivated, this, &DolphinViewContainer::slotItemActivated); 0134 connect(m_view, &DolphinView::fileMiddleClickActivated, this, &DolphinViewContainer::slotfileMiddleClickActivated); 0135 connect(m_view, &DolphinView::itemsActivated, this, &DolphinViewContainer::slotItemsActivated); 0136 connect(m_view, &DolphinView::redirection, this, &DolphinViewContainer::redirect); 0137 connect(m_view, &DolphinView::directoryLoadingStarted, this, &DolphinViewContainer::slotDirectoryLoadingStarted); 0138 connect(m_view, &DolphinView::directoryLoadingCompleted, this, &DolphinViewContainer::slotDirectoryLoadingCompleted); 0139 connect(m_view, &DolphinView::directoryLoadingCanceled, this, &DolphinViewContainer::slotDirectoryLoadingCanceled); 0140 connect(m_view, &DolphinView::itemCountChanged, this, &DolphinViewContainer::delayedStatusBarUpdate); 0141 connect(m_view, &DolphinView::directoryLoadingProgress, this, &DolphinViewContainer::updateDirectoryLoadingProgress); 0142 connect(m_view, &DolphinView::directorySortingProgress, this, &DolphinViewContainer::updateDirectorySortingProgress); 0143 connect(m_view, &DolphinView::selectionChanged, this, &DolphinViewContainer::delayedStatusBarUpdate); 0144 connect(m_view, &DolphinView::errorMessage, this, &DolphinViewContainer::showErrorMessage); 0145 connect(m_view, &DolphinView::urlIsFileError, this, &DolphinViewContainer::slotUrlIsFileError); 0146 connect(m_view, &DolphinView::activated, this, &DolphinViewContainer::activate); 0147 connect(m_view, &DolphinView::hiddenFilesShownChanged, this, &DolphinViewContainer::slotHiddenFilesShownChanged); 0148 connect(m_view, &DolphinView::sortHiddenLastChanged, this, &DolphinViewContainer::slotSortHiddenLastChanged); 0149 connect(m_view, &DolphinView::currentDirectoryRemoved, this, &DolphinViewContainer::slotCurrentDirectoryRemoved); 0150 0151 // Initialize status bar 0152 m_statusBar = new DolphinStatusBar(this); 0153 m_statusBar->setUrl(m_view->url()); 0154 m_statusBar->setZoomLevel(m_view->zoomLevel()); 0155 connect(m_view, &DolphinView::urlChanged, m_statusBar, &DolphinStatusBar::setUrl); 0156 connect(m_view, &DolphinView::zoomLevelChanged, m_statusBar, &DolphinStatusBar::setZoomLevel); 0157 connect(m_view, &DolphinView::infoMessage, m_statusBar, &DolphinStatusBar::setText); 0158 connect(m_view, &DolphinView::operationCompletedMessage, m_statusBar, &DolphinStatusBar::setText); 0159 connect(m_view, &DolphinView::statusBarTextChanged, m_statusBar, &DolphinStatusBar::setDefaultText); 0160 connect(m_view, &DolphinView::statusBarTextChanged, m_statusBar, &DolphinStatusBar::resetToDefaultText); 0161 connect(m_statusBar, &DolphinStatusBar::stopPressed, this, &DolphinViewContainer::stopDirectoryLoading); 0162 connect(m_statusBar, &DolphinStatusBar::zoomLevelChanged, this, &DolphinViewContainer::slotStatusBarZoomLevelChanged); 0163 0164 m_statusBarTimer = new QTimer(this); 0165 m_statusBarTimer->setSingleShot(true); 0166 m_statusBarTimer->setInterval(300); 0167 connect(m_statusBarTimer, &QTimer::timeout, this, &DolphinViewContainer::updateStatusBar); 0168 0169 KIO::FileUndoManager *undoManager = KIO::FileUndoManager::self(); 0170 connect(undoManager, &KIO::FileUndoManager::jobRecordingFinished, this, &DolphinViewContainer::delayedStatusBarUpdate); 0171 0172 m_topLayout->addWidget(m_searchBox, positionFor.searchBox, 0); 0173 m_topLayout->addWidget(m_messageWidget, positionFor.messageWidget, 0); 0174 m_topLayout->addWidget(m_view, positionFor.view, 0); 0175 m_topLayout->addWidget(m_filterBar, positionFor.filterBar, 0); 0176 m_topLayout->addWidget(m_statusBar, positionFor.statusBar, 0); 0177 0178 setSearchModeEnabled(isSearchUrl(url)); 0179 0180 // Update view as the ContentDisplaySettings change 0181 // this happens here and not in DolphinView as DolphinviewContainer and DolphinView are not in the same build target ATM 0182 connect(ContentDisplaySettings::self(), &KCoreConfigSkeleton::configChanged, m_view, &DolphinView::reload); 0183 0184 KFilePlacesModel *placesModel = DolphinPlacesModelSingleton::instance().placesModel(); 0185 connect(placesModel, &KFilePlacesModel::dataChanged, this, &DolphinViewContainer::slotPlacesModelChanged); 0186 connect(placesModel, &KFilePlacesModel::rowsInserted, this, &DolphinViewContainer::slotPlacesModelChanged); 0187 connect(placesModel, &KFilePlacesModel::rowsRemoved, this, &DolphinViewContainer::slotPlacesModelChanged); 0188 0189 connect(this, &DolphinViewContainer::searchModeEnabledChanged, this, &DolphinViewContainer::captionChanged); 0190 } 0191 0192 DolphinViewContainer::~DolphinViewContainer() 0193 { 0194 } 0195 0196 QUrl DolphinViewContainer::url() const 0197 { 0198 return m_view->url(); 0199 } 0200 0201 KFileItem DolphinViewContainer::rootItem() const 0202 { 0203 return m_view->rootItem(); 0204 } 0205 0206 void DolphinViewContainer::setActive(bool active) 0207 { 0208 m_searchBox->setActive(active); 0209 if (m_urlNavigatorConnected) { 0210 m_urlNavigatorConnected->setActive(active); 0211 } 0212 m_view->setActive(active); 0213 } 0214 0215 bool DolphinViewContainer::isActive() const 0216 { 0217 return m_view->isActive(); 0218 } 0219 0220 void DolphinViewContainer::setAutoGrabFocus(bool grab) 0221 { 0222 m_autoGrabFocus = grab; 0223 } 0224 0225 bool DolphinViewContainer::autoGrabFocus() const 0226 { 0227 return m_autoGrabFocus; 0228 } 0229 0230 QString DolphinViewContainer::currentSearchText() const 0231 { 0232 return m_searchBox->text(); 0233 } 0234 0235 const DolphinStatusBar *DolphinViewContainer::statusBar() const 0236 { 0237 return m_statusBar; 0238 } 0239 0240 DolphinStatusBar *DolphinViewContainer::statusBar() 0241 { 0242 return m_statusBar; 0243 } 0244 0245 const DolphinUrlNavigator *DolphinViewContainer::urlNavigator() const 0246 { 0247 return m_urlNavigatorConnected; 0248 } 0249 0250 DolphinUrlNavigator *DolphinViewContainer::urlNavigator() 0251 { 0252 return m_urlNavigatorConnected; 0253 } 0254 0255 const DolphinUrlNavigator *DolphinViewContainer::urlNavigatorInternalWithHistory() const 0256 { 0257 return m_urlNavigator.get(); 0258 } 0259 0260 DolphinUrlNavigator *DolphinViewContainer::urlNavigatorInternalWithHistory() 0261 { 0262 return m_urlNavigator.get(); 0263 } 0264 0265 const DolphinView *DolphinViewContainer::view() const 0266 { 0267 return m_view; 0268 } 0269 0270 DolphinView *DolphinViewContainer::view() 0271 { 0272 return m_view; 0273 } 0274 0275 void DolphinViewContainer::connectUrlNavigator(DolphinUrlNavigator *urlNavigator) 0276 { 0277 Q_CHECK_PTR(urlNavigator); 0278 Q_ASSERT(!m_urlNavigatorConnected); 0279 Q_ASSERT(m_urlNavigator.get() != urlNavigator); 0280 Q_CHECK_PTR(m_view); 0281 0282 urlNavigator->setLocationUrl(m_view->url()); 0283 urlNavigator->setShowHiddenFolders(m_view->hiddenFilesShown()); 0284 urlNavigator->setSortHiddenFoldersLast(m_view->sortHiddenLast()); 0285 if (m_urlNavigatorVisualState) { 0286 urlNavigator->setVisualState(*m_urlNavigatorVisualState.get()); 0287 m_urlNavigatorVisualState.reset(); 0288 } 0289 urlNavigator->setActive(isActive()); 0290 0291 // Url changes are still done via m_urlNavigator. 0292 connect(urlNavigator, &DolphinUrlNavigator::urlChanged, m_urlNavigator.get(), &DolphinUrlNavigator::setLocationUrl); 0293 connect(urlNavigator, &DolphinUrlNavigator::urlsDropped, this, [=](const QUrl &destination, QDropEvent *event) { 0294 m_view->dropUrls(destination, event, urlNavigator->dropWidget()); 0295 }); 0296 // Aside from these, only visual things need to be connected. 0297 connect(m_view, &DolphinView::urlChanged, urlNavigator, &DolphinUrlNavigator::setLocationUrl); 0298 connect(urlNavigator, &DolphinUrlNavigator::activated, this, &DolphinViewContainer::activate); 0299 0300 m_urlNavigatorConnected = urlNavigator; 0301 } 0302 0303 void DolphinViewContainer::disconnectUrlNavigator() 0304 { 0305 if (!m_urlNavigatorConnected) { 0306 return; 0307 } 0308 0309 disconnect(m_urlNavigatorConnected, &DolphinUrlNavigator::urlChanged, m_urlNavigator.get(), &DolphinUrlNavigator::setLocationUrl); 0310 disconnect(m_urlNavigatorConnected, &DolphinUrlNavigator::urlsDropped, this, nullptr); 0311 disconnect(m_view, &DolphinView::urlChanged, m_urlNavigatorConnected, &DolphinUrlNavigator::setLocationUrl); 0312 disconnect(m_urlNavigatorConnected, &DolphinUrlNavigator::activated, this, &DolphinViewContainer::activate); 0313 0314 m_urlNavigatorVisualState = m_urlNavigatorConnected->visualState(); 0315 m_urlNavigatorConnected = nullptr; 0316 } 0317 0318 void DolphinViewContainer::setSelectionModeEnabled(bool enabled, KActionCollection *actionCollection, SelectionMode::BottomBar::Contents bottomBarContents) 0319 { 0320 const bool wasEnabled = m_view->selectionMode(); 0321 m_view->setSelectionModeEnabled(enabled); 0322 0323 if (!enabled) { 0324 if (!wasEnabled) { 0325 return; // nothing to do here 0326 } 0327 Q_CHECK_PTR(m_selectionModeTopBar); // there is no point in disabling selectionMode when it wasn't even enabled once. 0328 Q_CHECK_PTR(m_selectionModeBottomBar); 0329 m_selectionModeTopBar->setVisible(false, WithAnimation); 0330 m_selectionModeBottomBar->setVisible(false, WithAnimation); 0331 Q_EMIT selectionModeChanged(false); 0332 0333 if (m_selectionModeTopBar->isAncestorOf(QApplication::focusWidget()) || m_selectionModeBottomBar->isAncestorOf(QApplication::focusWidget())) { 0334 m_view->setFocus(); 0335 } 0336 return; 0337 } 0338 0339 if (!m_selectionModeTopBar) { 0340 // Changing the location will disable selection mode. 0341 connect(m_urlNavigator.get(), &DolphinUrlNavigator::urlChanged, this, [this]() { 0342 setSelectionModeEnabled(false); 0343 }); 0344 0345 m_selectionModeTopBar = new SelectionMode::TopBar(this); // will be created hidden 0346 connect(m_selectionModeTopBar, &SelectionMode::TopBar::selectionModeLeavingRequested, this, [this]() { 0347 setSelectionModeEnabled(false); 0348 }); 0349 m_topLayout->addWidget(m_selectionModeTopBar, positionFor.selectionModeTopBar, 0); 0350 } 0351 0352 if (!m_selectionModeBottomBar) { 0353 m_selectionModeBottomBar = new SelectionMode::BottomBar(actionCollection, this); 0354 connect(m_view, &DolphinView::selectionChanged, this, [this](const KFileItemList &selection) { 0355 m_selectionModeBottomBar->slotSelectionChanged(selection, m_view->url()); 0356 }); 0357 connect(m_selectionModeBottomBar, &SelectionMode::BottomBar::error, this, [this](const QString &errorMessage) { 0358 showErrorMessage(errorMessage); 0359 }); 0360 connect(m_selectionModeBottomBar, &SelectionMode::BottomBar::selectionModeLeavingRequested, this, [this]() { 0361 setSelectionModeEnabled(false); 0362 }); 0363 m_topLayout->addWidget(m_selectionModeBottomBar, positionFor.selectionModeBottomBar, 0); 0364 } 0365 m_selectionModeBottomBar->resetContents(bottomBarContents); 0366 if (bottomBarContents == SelectionMode::BottomBar::GeneralContents) { 0367 m_selectionModeBottomBar->slotSelectionChanged(m_view->selectedItems(), m_view->url()); 0368 } 0369 0370 if (!wasEnabled) { 0371 m_selectionModeTopBar->setVisible(true, WithAnimation); 0372 m_selectionModeBottomBar->setVisible(true, WithAnimation); 0373 Q_EMIT selectionModeChanged(true); 0374 } 0375 } 0376 0377 bool DolphinViewContainer::isSelectionModeEnabled() const 0378 { 0379 const bool isEnabled = m_view->selectionMode(); 0380 Q_ASSERT((!isEnabled 0381 // We can't assert that the bars are invisible only because the selection mode is disabled because the hide animation might still be playing. 0382 && (!m_selectionModeBottomBar || !m_selectionModeBottomBar->isEnabled() || !m_selectionModeBottomBar->isVisible() 0383 || m_selectionModeBottomBar->contents() == SelectionMode::BottomBar::PasteContents)) 0384 || (isEnabled && m_selectionModeTopBar 0385 && m_selectionModeTopBar->isVisible() 0386 // The bottom bar is either visible or was hidden because it has nothing to show in GeneralContents mode e.g. because no items are selected. 0387 && m_selectionModeBottomBar 0388 && (m_selectionModeBottomBar->isVisible() || m_selectionModeBottomBar->contents() == SelectionMode::BottomBar::GeneralContents))); 0389 return isEnabled; 0390 } 0391 0392 void DolphinViewContainer::slotSplitTabDisabled() 0393 { 0394 if (m_selectionModeBottomBar) { 0395 m_selectionModeBottomBar->slotSplitTabDisabled(); 0396 } 0397 } 0398 0399 void DolphinViewContainer::showMessage(const QString &msg, MessageType type) 0400 { 0401 if (msg.isEmpty()) { 0402 return; 0403 } 0404 0405 m_messageWidget->setText(msg); 0406 0407 // TODO: wrap at arbitrary character positions once QLabel can do this 0408 // https://bugreports.qt.io/browse/QTBUG-1276 0409 m_messageWidget->setWordWrap(true); 0410 0411 switch (type) { 0412 case Information: 0413 m_messageWidget->setMessageType(KMessageWidget::Information); 0414 break; 0415 case Warning: 0416 m_messageWidget->setMessageType(KMessageWidget::Warning); 0417 break; 0418 case Error: 0419 m_messageWidget->setMessageType(KMessageWidget::Error); 0420 break; 0421 default: 0422 Q_ASSERT(false); 0423 break; 0424 } 0425 0426 m_messageWidget->setWordWrap(false); 0427 const int unwrappedWidth = m_messageWidget->sizeHint().width(); 0428 m_messageWidget->setWordWrap(unwrappedWidth > size().width()); 0429 0430 if (m_messageWidget->isVisible()) { 0431 m_messageWidget->hide(); 0432 } 0433 m_messageWidget->animatedShow(); 0434 } 0435 0436 void DolphinViewContainer::readSettings() 0437 { 0438 // The startup settings should (only) get applied if they have been 0439 // modified by the user. Otherwise keep the (possibly) different current 0440 // setting of the filterbar. 0441 if (GeneralSettings::modifiedStartupSettings()) { 0442 setFilterBarVisible(GeneralSettings::filterBar()); 0443 } 0444 0445 m_view->readSettings(); 0446 m_statusBar->readSettings(); 0447 } 0448 0449 bool DolphinViewContainer::isFilterBarVisible() const 0450 { 0451 return m_filterBar->isVisible(); 0452 } 0453 0454 void DolphinViewContainer::setSearchModeEnabled(bool enabled) 0455 { 0456 m_searchBox->setVisible(enabled); 0457 0458 if (enabled) { 0459 const QUrl &locationUrl = m_urlNavigator->locationUrl(); 0460 m_searchBox->fromSearchUrl(locationUrl); 0461 } 0462 0463 if (enabled == isSearchModeEnabled()) { 0464 if (enabled && !m_searchBox->hasFocus()) { 0465 m_searchBox->setFocus(); 0466 m_searchBox->selectAll(); 0467 } 0468 return; 0469 } 0470 0471 if (!enabled) { 0472 m_view->setViewPropertiesContext(QString()); 0473 0474 // Restore the URL for the URL navigator. If Dolphin has been 0475 // started with a search-URL, the home URL is used as fallback. 0476 QUrl url = m_searchBox->searchPath(); 0477 if (url.isEmpty() || !url.isValid() || isSearchUrl(url)) { 0478 url = Dolphin::homeUrl(); 0479 } 0480 m_urlNavigatorConnected->setLocationUrl(url); 0481 } 0482 0483 m_searchModeEnabled = enabled; 0484 0485 Q_EMIT searchModeEnabledChanged(enabled); 0486 } 0487 0488 bool DolphinViewContainer::isSearchModeEnabled() const 0489 { 0490 return m_searchModeEnabled; 0491 } 0492 0493 QString DolphinViewContainer::placesText() const 0494 { 0495 QString text; 0496 0497 if (isSearchModeEnabled()) { 0498 text = i18n("Search for %1 in %2", m_searchBox->text(), m_searchBox->searchPath().fileName()); 0499 } else { 0500 text = url().adjusted(QUrl::StripTrailingSlash).fileName(); 0501 if (text.isEmpty()) { 0502 text = url().host(); 0503 } 0504 if (text.isEmpty()) { 0505 text = url().scheme(); 0506 } 0507 } 0508 0509 return text; 0510 } 0511 0512 void DolphinViewContainer::reload() 0513 { 0514 view()->reload(); 0515 m_messageWidget->hide(); 0516 } 0517 0518 QString DolphinViewContainer::captionWindowTitle() const 0519 { 0520 if (GeneralSettings::showFullPathInTitlebar() && !isSearchModeEnabled()) { 0521 if (!url().isLocalFile()) { 0522 return url().adjusted(QUrl::StripTrailingSlash).toString(); 0523 } 0524 return url().adjusted(QUrl::StripTrailingSlash).path(); 0525 } else { 0526 return DolphinViewContainer::caption(); 0527 } 0528 } 0529 0530 QString DolphinViewContainer::caption() const 0531 { 0532 if (isSearchModeEnabled()) { 0533 if (currentSearchText().isEmpty()) { 0534 return i18n("Search"); 0535 } else { 0536 return i18n("Search for %1", currentSearchText()); 0537 } 0538 } 0539 0540 KFilePlacesModel *placesModel = DolphinPlacesModelSingleton::instance().placesModel(); 0541 const QString pattern = url().adjusted(QUrl::StripTrailingSlash).toString(QUrl::FullyEncoded).append("/?"); 0542 const auto &matchedPlaces = 0543 placesModel->match(placesModel->index(0, 0), KFilePlacesModel::UrlRole, QRegularExpression::anchoredPattern(pattern), 1, Qt::MatchRegularExpression); 0544 0545 if (!matchedPlaces.isEmpty()) { 0546 return placesModel->text(matchedPlaces.first()); 0547 } 0548 0549 if (!url().isLocalFile()) { 0550 QUrl adjustedUrl = url().adjusted(QUrl::StripTrailingSlash); 0551 QString caption; 0552 if (!adjustedUrl.fileName().isEmpty()) { 0553 caption = adjustedUrl.fileName(); 0554 } else if (!adjustedUrl.path().isEmpty() && adjustedUrl.path() != "/") { 0555 caption = adjustedUrl.path(); 0556 } else if (!adjustedUrl.host().isEmpty()) { 0557 caption = adjustedUrl.host(); 0558 } else { 0559 caption = adjustedUrl.toString(); 0560 } 0561 return caption; 0562 } 0563 0564 QString fileName = url().adjusted(QUrl::StripTrailingSlash).fileName(); 0565 if (fileName.isEmpty()) { 0566 fileName = '/'; 0567 } 0568 0569 return fileName; 0570 } 0571 0572 void DolphinViewContainer::setUrl(const QUrl &newUrl) 0573 { 0574 if (newUrl != m_urlNavigator->locationUrl()) { 0575 m_urlNavigator->setLocationUrl(newUrl); 0576 } 0577 0578 #if HAVE_PLASMA_ACTIVITIES 0579 KActivities::ResourceInstance::notifyAccessed(newUrl); 0580 #endif 0581 } 0582 0583 void DolphinViewContainer::setFilterBarVisible(bool visible) 0584 { 0585 Q_ASSERT(m_filterBar); 0586 if (visible) { 0587 m_view->hideToolTip(ToolTipManager::HideBehavior::Instantly); 0588 m_filterBar->show(); 0589 m_filterBar->setFocus(); 0590 m_filterBar->selectAll(); 0591 } else { 0592 closeFilterBar(); 0593 } 0594 } 0595 0596 void DolphinViewContainer::delayedStatusBarUpdate() 0597 { 0598 if (m_statusBarTimer->isActive() && (m_statusBarTimestamp.elapsed() > 2000)) { 0599 // No update of the statusbar has been done during the last 2 seconds, 0600 // although an update has been requested. Trigger an immediate update. 0601 m_statusBarTimer->stop(); 0602 updateStatusBar(); 0603 } else { 0604 // Invoke updateStatusBar() with a small delay. This assures that 0605 // when a lot of delayedStatusBarUpdates() are done in a short time, 0606 // no bottleneck is given. 0607 m_statusBarTimer->start(); 0608 } 0609 } 0610 0611 void DolphinViewContainer::updateStatusBar() 0612 { 0613 m_statusBarTimestamp.start(); 0614 m_view->requestStatusBarText(); 0615 } 0616 0617 void DolphinViewContainer::updateDirectoryLoadingProgress(int percent) 0618 { 0619 if (m_statusBar->progressText().isEmpty()) { 0620 m_statusBar->setProgressText(i18nc("@info:progress", "Loading folder…")); 0621 } 0622 m_statusBar->setProgress(percent); 0623 } 0624 0625 void DolphinViewContainer::updateDirectorySortingProgress(int percent) 0626 { 0627 if (m_statusBar->progressText().isEmpty()) { 0628 m_statusBar->setProgressText(i18nc("@info:progress", "Sorting…")); 0629 } 0630 m_statusBar->setProgress(percent); 0631 } 0632 0633 void DolphinViewContainer::slotDirectoryLoadingStarted() 0634 { 0635 if (isSearchUrl(url())) { 0636 // Search KIO-slaves usually don't provide any progress information. Give 0637 // a hint to the user that a searching is done: 0638 updateStatusBar(); 0639 m_statusBar->setProgressText(i18nc("@info", "Searching…")); 0640 m_statusBar->setProgress(-1); 0641 } else { 0642 // Trigger an undetermined progress indication. The progress 0643 // information in percent will be triggered by the percent() signal 0644 // of the directory lister later. 0645 m_statusBar->setProgressText(QString()); 0646 updateDirectoryLoadingProgress(-1); 0647 } 0648 } 0649 0650 void DolphinViewContainer::slotDirectoryLoadingCompleted() 0651 { 0652 if (!m_statusBar->progressText().isEmpty()) { 0653 m_statusBar->setProgressText(QString()); 0654 m_statusBar->setProgress(100); 0655 } 0656 0657 if (isSearchUrl(url()) && m_view->itemsCount() == 0) { 0658 // The dir lister has been completed on a Baloo-URI and no items have been found. Instead 0659 // of showing the default status bar information ("0 items") a more helpful information is given: 0660 m_statusBar->setText(i18nc("@info:status", "No items found.")); 0661 } else { 0662 updateStatusBar(); 0663 } 0664 } 0665 0666 void DolphinViewContainer::slotDirectoryLoadingCanceled() 0667 { 0668 if (!m_statusBar->progressText().isEmpty()) { 0669 m_statusBar->setProgressText(QString()); 0670 m_statusBar->setProgress(100); 0671 } 0672 0673 m_statusBar->setText(QString()); 0674 } 0675 0676 void DolphinViewContainer::slotUrlIsFileError(const QUrl &url) 0677 { 0678 const KFileItem item(url); 0679 0680 // Find out if the file can be opened in the view (for example, this is the 0681 // case if the file is an archive). The mime type must be known for that. 0682 item.determineMimeType(); 0683 const QUrl &folderUrl = DolphinView::openItemAsFolderUrl(item, true); 0684 if (!folderUrl.isEmpty()) { 0685 setUrl(folderUrl); 0686 } else { 0687 slotItemActivated(item); 0688 } 0689 } 0690 0691 void DolphinViewContainer::slotItemActivated(const KFileItem &item) 0692 { 0693 // It is possible to activate items on inactive views by 0694 // drag & drop operations. Assure that activating an item always 0695 // results in an active view. 0696 m_view->setActive(true); 0697 0698 const QUrl &url = DolphinView::openItemAsFolderUrl(item, GeneralSettings::browseThroughArchives()); 0699 if (!url.isEmpty()) { 0700 const auto modifiers = QGuiApplication::keyboardModifiers(); 0701 // keep in sync with KUrlNavigator::slotNavigatorButtonClicked 0702 if (modifiers & Qt::ControlModifier && modifiers & Qt::ShiftModifier) { 0703 Q_EMIT activeTabRequested(url); 0704 } else if (modifiers & Qt::ControlModifier) { 0705 Q_EMIT tabRequested(url); 0706 } else if (modifiers & Qt::ShiftModifier) { 0707 Dolphin::openNewWindow({KFilePlacesModel::convertedUrl(url)}, this); 0708 } else { 0709 setUrl(url); 0710 } 0711 return; 0712 } 0713 0714 KIO::OpenUrlJob *job = new KIO::OpenUrlJob(item.targetUrl(), item.mimetype()); 0715 // Auto*Warning*Handling, errors are put in a KMessageWidget by us in slotOpenUrlFinished. 0716 job->setUiDelegate(KIO::createDefaultJobUiDelegate(KJobUiDelegate::AutoWarningHandlingEnabled, this)); 0717 job->setShowOpenOrExecuteDialog(true); 0718 connect(job, &KIO::OpenUrlJob::finished, this, &DolphinViewContainer::slotOpenUrlFinished); 0719 job->start(); 0720 } 0721 0722 void DolphinViewContainer::slotfileMiddleClickActivated(const KFileItem &item) 0723 { 0724 KService::List services = KApplicationTrader::queryByMimeType(item.mimetype()); 0725 0726 int indexOfAppToOpenFileWith = 1; 0727 0728 // executable scripts 0729 auto mimeType = item.currentMimeType(); 0730 if (item.isLocalFile() && mimeType.inherits(QStringLiteral("application/x-executable")) && mimeType.inherits(QStringLiteral("text/plain")) 0731 && QFileInfo(item.localPath()).isExecutable()) { 0732 KConfigGroup cfgGroup(KSharedConfig::openConfig(QStringLiteral("kiorc")), QStringLiteral("Executable scripts")); 0733 const QString value = cfgGroup.readEntry("behaviourOnLaunch", "alwaysAsk"); 0734 0735 // in case KIO::WidgetsOpenOrExecuteFileHandler::promptUserOpenOrExecute would not open the file 0736 if (value != QLatin1String("open")) { 0737 indexOfAppToOpenFileWith = 0; 0738 } 0739 } 0740 0741 if (services.length() >= indexOfAppToOpenFileWith + 1) { 0742 auto service = services.at(indexOfAppToOpenFileWith); 0743 0744 KIO::ApplicationLauncherJob *job = new KIO::ApplicationLauncherJob(service, this); 0745 job->setUrls({item.url()}); 0746 0747 job->setUiDelegate(KIO::createDefaultJobUiDelegate(KJobUiDelegate::AutoHandlingEnabled, this)); 0748 connect(job, &KIO::OpenUrlJob::finished, this, &DolphinViewContainer::slotOpenUrlFinished); 0749 job->start(); 0750 } 0751 } 0752 0753 void DolphinViewContainer::slotItemsActivated(const KFileItemList &items) 0754 { 0755 Q_ASSERT(items.count() >= 2); 0756 0757 KFileItemActions fileItemActions(this); 0758 fileItemActions.runPreferredApplications(items); 0759 } 0760 0761 void DolphinViewContainer::showItemInfo(const KFileItem &item) 0762 { 0763 if (item.isNull()) { 0764 m_statusBar->resetToDefaultText(); 0765 } else { 0766 m_statusBar->setText(item.getStatusBarInfo()); 0767 } 0768 } 0769 0770 void DolphinViewContainer::closeFilterBar() 0771 { 0772 m_filterBar->closeFilterBar(); 0773 m_view->setFocus(); 0774 Q_EMIT showFilterBarChanged(false); 0775 } 0776 0777 void DolphinViewContainer::clearFilterBar() 0778 { 0779 m_filterBar->clearIfUnlocked(); 0780 } 0781 0782 void DolphinViewContainer::setNameFilter(const QString &nameFilter) 0783 { 0784 m_view->hideToolTip(ToolTipManager::HideBehavior::Instantly); 0785 m_view->setNameFilter(nameFilter); 0786 delayedStatusBarUpdate(); 0787 } 0788 0789 void DolphinViewContainer::activate() 0790 { 0791 setActive(true); 0792 } 0793 0794 void DolphinViewContainer::slotUrlNavigatorLocationAboutToBeChanged(const QUrl &) 0795 { 0796 saveViewState(); 0797 } 0798 0799 void DolphinViewContainer::slotUrlNavigatorLocationChanged(const QUrl &url) 0800 { 0801 if (m_urlNavigatorConnected) { 0802 m_urlNavigatorConnected->slotReturnPressed(); 0803 } 0804 0805 if (KProtocolManager::supportsListing(url)) { 0806 const bool searchBoxInitialized = isSearchModeEnabled() && m_searchBox->text().isEmpty(); 0807 setSearchModeEnabled(isSearchUrl(url) || searchBoxInitialized); 0808 0809 m_view->setUrl(url); 0810 tryRestoreViewState(); 0811 0812 if (m_autoGrabFocus && isActive() && !isSearchModeEnabled()) { 0813 // When an URL has been entered, the view should get the focus. 0814 // The focus must be requested asynchronously, as changing the URL might create 0815 // a new view widget. 0816 QTimer::singleShot(0, this, &DolphinViewContainer::requestFocus); 0817 } 0818 } else if (KProtocolManager::isSourceProtocol(url)) { 0819 if (url.scheme().startsWith(QLatin1String("http"))) { 0820 showMessage(i18nc("@info:status", // krazy:exclude=qmethods 0821 "Dolphin does not support web pages, the web browser has been launched"), 0822 Information); 0823 } else { 0824 showMessage(i18nc("@info:status", "Protocol not supported by Dolphin, default application has been launched"), Information); 0825 } 0826 0827 QDesktopServices::openUrl(url); 0828 redirect(QUrl(), m_urlNavigator->locationUrl(1)); 0829 } else { 0830 if (!url.scheme().isEmpty()) { 0831 showMessage(i18nc("@info:status", "Invalid protocol '%1'", url.scheme()), Error); 0832 } else { 0833 showMessage(i18nc("@info:status", "Invalid protocol"), Error); 0834 } 0835 m_urlNavigator->goBack(); 0836 } 0837 } 0838 0839 void DolphinViewContainer::slotUrlSelectionRequested(const QUrl &url) 0840 { 0841 m_view->markUrlsAsSelected({url}); 0842 m_view->markUrlAsCurrent(url); // makes the item scroll into view 0843 } 0844 0845 void DolphinViewContainer::disableUrlNavigatorSelectionRequests() 0846 { 0847 disconnect(m_urlNavigator.get(), &KUrlNavigator::urlSelectionRequested, this, &DolphinViewContainer::slotUrlSelectionRequested); 0848 } 0849 0850 void DolphinViewContainer::enableUrlNavigatorSelectionRequests() 0851 { 0852 connect(m_urlNavigator.get(), &KUrlNavigator::urlSelectionRequested, this, &DolphinViewContainer::slotUrlSelectionRequested); 0853 } 0854 0855 void DolphinViewContainer::redirect(const QUrl &oldUrl, const QUrl &newUrl) 0856 { 0857 Q_UNUSED(oldUrl) 0858 const bool block = m_urlNavigator->signalsBlocked(); 0859 m_urlNavigator->blockSignals(true); 0860 0861 // Assure that the location state is reset for redirection URLs. This 0862 // allows to skip redirection URLs when going back or forward in the 0863 // URL history. 0864 m_urlNavigator->saveLocationState(QByteArray()); 0865 m_urlNavigator->setLocationUrl(newUrl); 0866 setSearchModeEnabled(isSearchUrl(newUrl)); 0867 0868 m_urlNavigator->blockSignals(block); 0869 } 0870 0871 void DolphinViewContainer::requestFocus() 0872 { 0873 m_view->setFocus(); 0874 } 0875 0876 void DolphinViewContainer::startSearching() 0877 { 0878 Q_CHECK_PTR(m_urlNavigatorConnected); 0879 const QUrl url = m_searchBox->urlForSearching(); 0880 if (url.isValid() && !url.isEmpty()) { 0881 m_view->setViewPropertiesContext(QStringLiteral("search")); 0882 m_urlNavigatorConnected->setLocationUrl(url); 0883 } 0884 } 0885 0886 void DolphinViewContainer::openSearchBox() 0887 { 0888 setSearchModeEnabled(true); 0889 } 0890 0891 void DolphinViewContainer::closeSearchBox() 0892 { 0893 setSearchModeEnabled(false); 0894 } 0895 0896 void DolphinViewContainer::stopDirectoryLoading() 0897 { 0898 m_view->stopLoading(); 0899 m_statusBar->setProgress(100); 0900 } 0901 0902 void DolphinViewContainer::slotStatusBarZoomLevelChanged(int zoomLevel) 0903 { 0904 m_view->setZoomLevel(zoomLevel); 0905 } 0906 0907 void DolphinViewContainer::showErrorMessage(const QString &msg) 0908 { 0909 showMessage(msg, Error); 0910 } 0911 0912 void DolphinViewContainer::slotPlacesModelChanged() 0913 { 0914 if (!GeneralSettings::showFullPathInTitlebar() && !isSearchModeEnabled()) { 0915 Q_EMIT captionChanged(); 0916 } 0917 } 0918 0919 void DolphinViewContainer::slotHiddenFilesShownChanged(bool showHiddenFiles) 0920 { 0921 if (m_urlNavigatorConnected) { 0922 m_urlNavigatorConnected->setShowHiddenFolders(showHiddenFiles); 0923 } 0924 } 0925 0926 void DolphinViewContainer::slotSortHiddenLastChanged(bool hiddenLast) 0927 { 0928 if (m_urlNavigatorConnected) { 0929 m_urlNavigatorConnected->setSortHiddenFoldersLast(hiddenLast); 0930 } 0931 } 0932 0933 void DolphinViewContainer::slotCurrentDirectoryRemoved() 0934 { 0935 const QString location(url().toDisplayString(QUrl::PreferLocalFile)); 0936 if (url().isLocalFile()) { 0937 const QString dirPath = url().toLocalFile(); 0938 const QString newPath = getNearestExistingAncestorOfPath(dirPath); 0939 const QUrl newUrl = QUrl::fromLocalFile(newPath); 0940 setUrl(newUrl); 0941 } 0942 0943 showMessage(xi18n("Current location changed, <filename>%1</filename> is no longer accessible.", location), Warning); 0944 } 0945 0946 void DolphinViewContainer::slotOpenUrlFinished(KJob *job) 0947 { 0948 if (job->error() && job->error() != KIO::ERR_USER_CANCELED) { 0949 showErrorMessage(job->errorString()); 0950 } 0951 } 0952 0953 bool DolphinViewContainer::isSearchUrl(const QUrl &url) const 0954 { 0955 return url.scheme().contains(QLatin1String("search")); 0956 } 0957 0958 void DolphinViewContainer::saveViewState() 0959 { 0960 QByteArray locationState; 0961 QDataStream stream(&locationState, QIODevice::WriteOnly); 0962 m_view->saveState(stream); 0963 m_urlNavigator->saveLocationState(locationState); 0964 } 0965 0966 void DolphinViewContainer::tryRestoreViewState() 0967 { 0968 QByteArray locationState = m_urlNavigator->locationState(); 0969 if (!locationState.isEmpty()) { 0970 QDataStream stream(&locationState, QIODevice::ReadOnly); 0971 m_view->restoreState(stream); 0972 } 0973 } 0974 0975 QString DolphinViewContainer::getNearestExistingAncestorOfPath(const QString &path) const 0976 { 0977 QDir dir(path); 0978 do { 0979 dir.setPath(QDir::cleanPath(dir.filePath(QStringLiteral("..")))); 0980 } while (!dir.exists() && !dir.isRoot()); 0981 0982 return dir.exists() ? dir.path() : QString{}; 0983 } 0984 0985 #include "moc_dolphinviewcontainer.cpp"