#include "dolphinviewcontainer.h"
#include "admin/bar.h"
+#include "admin/workerintegration.h"
#include "dolphin_compactmodesettings.h"
#include "dolphin_contentdisplaysettings.h"
#include "dolphin_detailsmodesettings.h"
#include "statusbar/dolphinstatusbar.h"
#include <KActionCollection>
-#if HAVE_PLASMA_ACTIVITIES
-#include <PlasmaActivities/ResourceInstance>
-#endif
#include <KApplicationTrader>
#include <KFileItemActions>
#include <KFilePlacesModel>
#include <KShell>
#include <kio_version.h>
+#ifndef QT_NO_ACCESSIBILITY
+#include <QAccessible>
+#endif
#include <QApplication>
#include <QDesktopServices>
#include <QDropEvent>
#include <QRegularExpression>
#include <QTimer>
#include <QUrl>
+#include <QUrlQuery>
// An overview of the widgets contained by this ViewContainer
struct LayoutStructure {
, m_searchBox(nullptr)
, m_searchModeEnabled(false)
, m_adminBar{nullptr}
+ , m_authorizeToEnterFolderAction{nullptr}
, m_messageWidget(nullptr)
, m_selectionModeTopBar{nullptr}
, m_view(nullptr)
connect(m_view, &DolphinView::directoryLoadingCanceled, this, &DolphinViewContainer::slotDirectoryLoadingCanceled);
connect(m_view, &DolphinView::itemCountChanged, this, &DolphinViewContainer::delayedStatusBarUpdate);
connect(m_view, &DolphinView::selectionChanged, this, &DolphinViewContainer::delayedStatusBarUpdate);
- connect(m_view, &DolphinView::errorMessage, this, &DolphinViewContainer::showErrorMessage);
+ connect(m_view, &DolphinView::errorMessage, this, &DolphinViewContainer::slotErrorMessageFromView);
connect(m_view, &DolphinView::urlIsFileError, this, &DolphinViewContainer::slotUrlIsFileError);
connect(m_view, &DolphinView::activated, this, &DolphinViewContainer::activate);
connect(m_view, &DolphinView::hiddenFilesShownChanged, this, &DolphinViewContainer::slotHiddenFilesShownChanged);
connect(m_view, &DolphinView::sortHiddenLastChanged, this, &DolphinViewContainer::slotSortHiddenLastChanged);
connect(m_view, &DolphinView::currentDirectoryRemoved, this, &DolphinViewContainer::slotCurrentDirectoryRemoved);
- connect(m_view, &DolphinView::urlChanged, this, &DolphinViewContainer::updateAdminBarVisibility);
// Initialize status bar
m_statusBar = new DolphinStatusBar(this);
});
connect(m_statusBar, &DolphinStatusBar::stopPressed, this, &DolphinViewContainer::stopDirectoryLoading);
connect(m_statusBar, &DolphinStatusBar::zoomLevelChanged, this, &DolphinViewContainer::slotStatusBarZoomLevelChanged);
+ connect(m_statusBar, &DolphinStatusBar::showMessage, this, [this](const QString &message, KMessageWidget::MessageType messageType) {
+ showMessage(message, messageType);
+ });
m_statusBarTimer = new QTimer(this);
m_statusBarTimer->setSingleShot(true);
m_topLayout->addWidget(m_statusBar, positionFor.statusBar, 0);
setSearchModeEnabled(isSearchUrl(url));
- updateAdminBarVisibility(url);
// Update view as the ContentDisplaySettings change
// this happens here and not in DolphinView as DolphinviewContainer and DolphinView are not in the same build target ATM
// Url changes are still done via m_urlNavigator.
connect(urlNavigator, &DolphinUrlNavigator::urlChanged, m_urlNavigator.get(), &DolphinUrlNavigator::setLocationUrl);
- connect(urlNavigator, &DolphinUrlNavigator::urlsDropped, this, [=](const QUrl &destination, QDropEvent *event) {
+ connect(urlNavigator, &DolphinUrlNavigator::urlsDropped, this, [=, this](const QUrl &destination, QDropEvent *event) {
m_view->dropUrls(destination, event, urlNavigator->dropWidget());
});
// Aside from these, only visual things need to be connected.
connect(m_view, &DolphinView::urlChanged, urlNavigator, &DolphinUrlNavigator::setLocationUrl);
connect(urlNavigator, &DolphinUrlNavigator::activated, this, &DolphinViewContainer::activate);
+ connect(urlNavigator, &DolphinUrlNavigator::requestToLoseFocus, m_view, [this]() {
+ m_view->setFocus();
+ });
urlNavigator->setReadOnlyBadgeVisible(rootItem().isLocalFile() && !rootItem().isWritable());
disconnect(m_urlNavigatorConnected, &DolphinUrlNavigator::urlsDropped, this, nullptr);
disconnect(m_view, &DolphinView::urlChanged, m_urlNavigatorConnected, &DolphinUrlNavigator::setLocationUrl);
disconnect(m_urlNavigatorConnected, &DolphinUrlNavigator::activated, this, &DolphinViewContainer::activate);
+ disconnect(m_urlNavigatorConnected, &DolphinUrlNavigator::requestToLoseFocus, m_view, nullptr);
m_urlNavigatorVisualState = m_urlNavigatorConnected->visualState();
m_urlNavigatorConnected = nullptr;
}
}
-void DolphinViewContainer::showMessage(const QString &message, KMessageWidget::MessageType messageType)
+void DolphinViewContainer::showMessage(const QString &message, KMessageWidget::MessageType messageType, std::initializer_list<QAction *> buttonActions)
{
if (message.isEmpty()) {
return;
m_messageWidget->setWordWrap(true);
m_messageWidget->setMessageType(messageType);
+ const QList<QAction *> previousMessageWidgetActions = m_messageWidget->actions();
+ for (auto action : previousMessageWidgetActions) {
+ m_messageWidget->removeAction(action);
+ }
+ for (QAction *action : buttonActions) {
+ m_messageWidget->addAction(action);
+ }
+
m_messageWidget->setWordWrap(false);
const int unwrappedWidth = m_messageWidget->sizeHint().width();
m_messageWidget->setWordWrap(unwrappedWidth > size().width());
m_messageWidget->hide();
}
m_messageWidget->animatedShow();
+
+#ifndef QT_NO_ACCESSIBILITY
+ if (QAccessible::isActive() && isActive()) {
+ // To announce the new message keyboard focus must be moved to the message label. However, we do not have direct access to the label that is internal
+ // to the KMessageWidget. Instead we setFocus() on the KMessageWidget and trust that it has set correct focus handling.
+ m_messageWidget->setFocus();
+ }
+#endif
}
void DolphinViewContainer::readSettings()
if (url.isEmpty() || !url.isValid() || isSearchUrl(url)) {
url = Dolphin::homeUrl();
}
- m_urlNavigatorConnected->setLocationUrl(url);
+ if (m_urlNavigatorConnected) {
+ m_urlNavigatorConnected->setLocationUrl(url);
+ }
}
m_searchModeEnabled = enabled;
QString DolphinViewContainer::caption() const
{
+ // see KUrlNavigatorPrivate::firstButtonText().
+ if (url().path().isEmpty() || url().path() == QLatin1Char('/')) {
+ QUrlQuery query(url());
+ const QString title = query.queryItemValue(QStringLiteral("title"));
+ if (!title.isEmpty()) {
+ return title;
+ }
+ }
+
if (isSearchModeEnabled()) {
if (currentSearchText().isEmpty()) {
return i18n("Search");
}
KFilePlacesModel *placesModel = DolphinPlacesModelSingleton::instance().placesModel();
- const QString pattern = url().adjusted(QUrl::StripTrailingSlash).toString(QUrl::FullyEncoded).append("/?");
- const auto &matchedPlaces =
- placesModel->match(placesModel->index(0, 0), KFilePlacesModel::UrlRole, QRegularExpression::anchoredPattern(pattern), 1, Qt::MatchRegularExpression);
- if (!matchedPlaces.isEmpty()) {
- return placesModel->text(matchedPlaces.first());
+ QModelIndex url_index = placesModel->closestItem(url());
+
+ if (url_index.isValid() && placesModel->url(url_index).matches(url(), QUrl::StripTrailingSlash)) {
+ return placesModel->text(url_index);
}
if (!url().isLocalFile()) {
if (newUrl != m_urlNavigator->locationUrl()) {
m_urlNavigator->setLocationUrl(newUrl);
}
-
-#if HAVE_PLASMA_ACTIVITIES
- KActivities::ResourceInstance::notifyAccessed(newUrl);
-#endif
}
void DolphinViewContainer::setFilterBarVisible(bool visible)
if (m_urlNavigatorConnected) {
m_urlNavigatorConnected->setReadOnlyBadgeVisible(rootItem().isLocalFile() && !rootItem().isWritable());
}
+
+ // Update admin bar visibility
+ if (m_view->url().scheme() == QStringLiteral("admin")) {
+ if (!m_adminBar) {
+ m_adminBar = new Admin::Bar(this);
+ m_topLayout->addWidget(m_adminBar, positionFor.adminBar, 0);
+ }
+ m_adminBar->setVisible(true, WithAnimation);
+ } else if (m_adminBar) {
+ m_adminBar->setVisible(false, WithAnimation);
+ }
}
void DolphinViewContainer::slotDirectoryLoadingCanceled()
job->setUiDelegate(KIO::createDefaultJobUiDelegate(KJobUiDelegate::AutoHandlingEnabled, this));
connect(job, &KIO::OpenUrlJob::finished, this, &DolphinViewContainer::slotOpenUrlFinished);
job->start();
+ } else {
+ // If no 2nd service available, try to open archives in new tabs, regardless of the "Open archives as folder" setting.
+ const QUrl &url = DolphinView::openItemAsFolderUrl(item);
+ const auto modifiers = QGuiApplication::keyboardModifiers();
+ if (!url.isEmpty()) {
+ // keep in sync with KUrlNavigator::slotNavigatorButtonClicked
+ if (modifiers & Qt::ShiftModifier) {
+ Q_EMIT activeTabRequested(url);
+ } else {
+ Q_EMIT tabRequested(url);
+ }
+ }
}
}
}
}
-void DolphinViewContainer::updateAdminBarVisibility(const QUrl &url)
-{
- if (url.scheme() == QStringLiteral("admin")) {
- if (!m_adminBar) {
- m_adminBar = new Admin::Bar(this);
- m_topLayout->addWidget(m_adminBar, positionFor.adminBar, 0);
- connect(m_adminBar, &Admin::Bar::activated, this, &DolphinViewContainer::activate);
- }
- m_adminBar->setVisible(true, WithAnimation);
- } else if (m_adminBar) {
- m_adminBar->setVisible(false, WithAnimation);
- }
-}
-
void DolphinViewContainer::closeFilterBar()
{
m_filterBar->closeFilterBar();
// URL history.
m_urlNavigator->saveLocationState(QByteArray());
m_urlNavigator->setLocationUrl(newUrl);
+ if (m_searchBox->isActive()) {
+ m_searchBox->setSearchPath(newUrl);
+ }
setSearchModeEnabled(isSearchUrl(newUrl));
m_urlNavigator->blockSignals(block);
m_view->setZoomLevel(zoomLevel);
}
+void DolphinViewContainer::slotErrorMessageFromView(const QString &message, const int kioErrorCode)
+{
+ if (kioErrorCode == KIO::ERR_CANNOT_ENTER_DIRECTORY && m_view->url().scheme() == QStringLiteral("file")
+ && KProtocolInfo::isKnownProtocol(QStringLiteral("admin")) && !rootItem().isReadable()) {
+ // Explain to users that they need authentication to see the folder contents.
+ if (!m_authorizeToEnterFolderAction) { // This code is similar to parts of Admin::Bar::hideTheNextTimeAuthorizationExpires().
+ // We should not simply use the actAsAdminAction() itself here because that one always refers to the active view instead of this->m_view.
+ auto actAsAdminAction = Admin::WorkerIntegration::FriendAccess::actAsAdminAction();
+ m_authorizeToEnterFolderAction = new QAction{actAsAdminAction->icon(), actAsAdminAction->text(), this};
+ m_authorizeToEnterFolderAction->setToolTip(actAsAdminAction->toolTip());
+ m_authorizeToEnterFolderAction->setWhatsThis(actAsAdminAction->whatsThis());
+ connect(m_authorizeToEnterFolderAction, &QAction::triggered, this, [this, actAsAdminAction]() {
+ setActive(true);
+ actAsAdminAction->trigger();
+ });
+ }
+ showMessage(i18nc("@info", "Authorization required to enter this folder."), KMessageWidget::Error, {m_authorizeToEnterFolderAction});
+ return;
+ }
+ Q_EMIT showErrorMessage(message);
+}
+
void DolphinViewContainer::showErrorMessage(const QString &message)
{
showMessage(message, KMessageWidget::Error);
const QString dirPath = url().toLocalFile();
const QString newPath = getNearestExistingAncestorOfPath(dirPath);
const QUrl newUrl = QUrl::fromLocalFile(newPath);
- setUrl(newUrl);
- }
-
- showMessage(xi18n("Current location changed, <filename>%1</filename> is no longer accessible.", location), KMessageWidget::Warning);
+ // #473377: Delay changing the url to avoid modifying KCoreDirLister before KCoreDirListerCache::deleteDir() returns.
+ QTimer::singleShot(0, this, [&, newUrl, location] {
+ setUrl(newUrl);
+ showMessage(xi18n("Current location changed, <filename>%1</filename> is no longer accessible.", location), KMessageWidget::Warning);
+ });
+ } else
+ showMessage(xi18n("Current location changed, <filename>%1</filename> is no longer accessible.", location), KMessageWidget::Warning);
}
void DolphinViewContainer::slotOpenUrlFinished(KJob *job)