X-Git-Url: https://cloud.milkyroute.net/gitweb/dolphin.git/blobdiff_plain/a3052c3942fc8c1354a9d26878f517e677eccb17..8b4d3624ad1be3de678cc5fcaa5a6baa43767914:/src/views/dolphinview.cpp diff --git a/src/views/dolphinview.cpp b/src/views/dolphinview.cpp index 02210baf4..9c85303fd 100644 --- a/src/views/dolphinview.cpp +++ b/src/views/dolphinview.cpp @@ -1,26 +1,15 @@ -/*************************************************************************** - * Copyright (C) 2006-2009 by Peter Penz * - * Copyright (C) 2006 by Gregor Kališnik * - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program; if not, write to the * - * Free Software Foundation, Inc., * - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * - ***************************************************************************/ +/* + * SPDX-FileCopyrightText: 2006-2009 Peter Penz + * SPDX-FileCopyrightText: 2006 Gregor Kališnik + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ #include "dolphinview.h" +#include "dolphin_compactmodesettings.h" #include "dolphin_detailsmodesettings.h" +#include "dolphin_iconsmodesettings.h" #include "dolphin_generalsettings.h" #include "dolphinitemlistview.h" #include "dolphinnewfilemenuobserver.h" @@ -31,6 +20,7 @@ #include "kitemviews/kitemlistcontroller.h" #include "kitemviews/kitemlistheader.h" #include "kitemviews/kitemlistselectionmanager.h" +#include "kitemviews/private/kitemlistroleeditor.h" #include "versioncontrol/versioncontrolobserver.h" #include "viewproperties.h" #include "views/tooltips/tooltipmanager.h" @@ -58,10 +48,13 @@ #include #include +#include #include #include #include +#include #include +#include #include #include #include @@ -78,6 +71,7 @@ DolphinView::DolphinView(const QUrl& url, QWidget* parent) : m_assureVisibleCurrentIndex(false), m_isFolderWritable(true), m_dragging(false), + m_loading(false), m_url(url), m_viewPropertiesContext(), m_mode(DolphinView::IconsView), @@ -95,7 +89,9 @@ DolphinView::DolphinView(const QUrl& url, QWidget* parent) : m_clearSelectionBeforeSelectingNewItems(false), m_markFirstNewlySelectedItemAsCurrent(false), m_versionControlObserver(nullptr), - m_twoClicksRenamingTimer(nullptr) + m_twoClicksRenamingTimer(nullptr), + m_placeholderLabel(nullptr), + m_showLoadingPlaceholderTimer(nullptr) { m_topLayout = new QVBoxLayout(this); m_topLayout->setSpacing(0); @@ -133,6 +129,33 @@ DolphinView::DolphinView(const QUrl& url, QWidget* parent) : connect(m_container->horizontalScrollBar(), &QScrollBar::valueChanged, this, [=] { hideToolTip(); }); connect(m_container->verticalScrollBar(), &QScrollBar::valueChanged, this, [=] { hideToolTip(); }); + m_showLoadingPlaceholderTimer = new QTimer(this); + m_showLoadingPlaceholderTimer->setInterval(500); + m_showLoadingPlaceholderTimer->setSingleShot(true); + connect(m_showLoadingPlaceholderTimer, &QTimer::timeout, this, &DolphinView::showLoadingPlaceholder); + + // Show some placeholder text for empty folders + // This is made using a heavily-modified QLabel rather than a KTitleWidget + // because KTitleWidget can't be told to turn off mouse-selectable text + m_placeholderLabel = new QLabel(this); + QFont placeholderLabelFont; + // To match the size of a level 2 Heading/KTitleWidget + placeholderLabelFont.setPointSize(qRound(placeholderLabelFont.pointSize() * 1.3)); + m_placeholderLabel->setFont(placeholderLabelFont); + m_placeholderLabel->setTextInteractionFlags(Qt::NoTextInteraction); + m_placeholderLabel->setWordWrap(true); + m_placeholderLabel->setAlignment(Qt::AlignCenter); + // Match opacity of QML placeholder label component + auto *effect = new QGraphicsOpacityEffect(m_placeholderLabel); + effect->setOpacity(0.5); + m_placeholderLabel->setGraphicsEffect(effect); + // Set initial text and visibility + updatePlaceholderLabel(); + + auto *centeringLayout = new QVBoxLayout(m_container); + centeringLayout->addWidget(m_placeholderLabel); + centeringLayout->setAlignment(m_placeholderLabel, Qt::AlignCenter); + controller->setSelectionBehavior(KItemListController::MultiSelection); connect(controller, &KItemListController::itemActivated, this, &DolphinView::slotItemActivated); connect(controller, &KItemListController::itemsActivated, this, &DolphinView::slotItemsActivated); @@ -147,10 +170,13 @@ DolphinView::DolphinView(const QUrl& url, QWidget* parent) : connect(controller, &KItemListController::escapePressed, this, &DolphinView::stopLoading); connect(controller, &KItemListController::modelChanged, this, &DolphinView::slotModelChanged); connect(controller, &KItemListController::selectedItemTextPressed, this, &DolphinView::slotSelectedItemTextPressed); + connect(controller, &KItemListController::increaseZoom, this, &DolphinView::slotIncreaseZoom); + connect(controller, &KItemListController::decreaseZoom, this, &DolphinView::slotDecreaseZoom); + connect(controller, &KItemListController::swipeUp, this, &DolphinView::slotSwipeUp); connect(m_model, &KFileItemModel::directoryLoadingStarted, this, &DolphinView::slotDirectoryLoadingStarted); connect(m_model, &KFileItemModel::directoryLoadingCompleted, this, &DolphinView::slotDirectoryLoadingCompleted); - connect(m_model, &KFileItemModel::directoryLoadingCanceled, this, &DolphinView::directoryLoadingCanceled); + connect(m_model, &KFileItemModel::directoryLoadingCanceled, this, &DolphinView::slotDirectoryLoadingCanceled); connect(m_model, &KFileItemModel::directoryLoadingProgress, this, &DolphinView::directoryLoadingProgress); connect(m_model, &KFileItemModel::directorySortingProgress, this, &DolphinView::directorySortingProgress); connect(m_model, &KFileItemModel::itemsChanged, @@ -161,6 +187,10 @@ DolphinView::DolphinView(const QUrl& url, QWidget* parent) : connect(m_model, &KFileItemModel::errorMessage, this, &DolphinView::errorMessage); connect(m_model, &KFileItemModel::directoryRedirection, this, &DolphinView::slotDirectoryRedirection); connect(m_model, &KFileItemModel::urlIsFileError, this, &DolphinView::urlIsFileError); + connect(m_model, &KFileItemModel::fileItemsChanged, this, &DolphinView::fileItemsChanged); + + connect(this, &DolphinView::itemCountChanged, + this, &DolphinView::updatePlaceholderLabel); m_view->installEventFilter(this); connect(m_view, &DolphinItemListView::sortOrderChanged, @@ -221,8 +251,8 @@ void DolphinView::setActive(bool active) if (active) { m_container->setFocus(); - emit activated(); - emit writeStateChanged(m_isFolderWritable); + Q_EMIT activated(); + Q_EMIT writeStateChanged(m_isFolderWritable); } } @@ -261,11 +291,11 @@ void DolphinView::setPreviewsShown(bool show) const int oldZoomLevel = m_view->zoomLevel(); m_view->setPreviewsShown(show); - emit previewsShownChanged(show); + Q_EMIT previewsShownChanged(show); const int newZoomLevel = m_view->zoomLevel(); if (newZoomLevel != oldZoomLevel) { - emit zoomLevelChanged(newZoomLevel, oldZoomLevel); + Q_EMIT zoomLevelChanged(newZoomLevel, oldZoomLevel); } } @@ -288,7 +318,7 @@ void DolphinView::setHiddenFilesShown(bool show) props.setHiddenFilesShown(show); m_model->setShowHiddenFiles(show); - emit hiddenFilesShownChanged(show); + Q_EMIT hiddenFilesShownChanged(show); } bool DolphinView::hiddenFilesShown() const @@ -308,7 +338,7 @@ void DolphinView::setGroupedSorting(bool grouped) m_container->controller()->model()->setGroupedSorting(grouped); - emit groupedSortingChanged(grouped); + Q_EMIT groupedSortingChanged(grouped); } bool DolphinView::groupedSorting() const @@ -389,7 +419,7 @@ void DolphinView::setZoomLevel(int level) m_view->setZoomLevel(level); if (zoomLevel() != oldZoomLevel) { hideToolTip(); - emit zoomLevelChanged(zoomLevel(), oldZoomLevel); + Q_EMIT zoomLevelChanged(zoomLevel(), oldZoomLevel); } } @@ -445,7 +475,7 @@ void DolphinView::setVisibleRoles(const QList& roles) m_visibleRoles = roles; m_view->setVisibleRoles(roles); - emit visibleRolesChanged(m_visibleRoles, previousRoles); + Q_EMIT visibleRolesChanged(m_visibleRoles, previousRoles); } QList DolphinView::visibleRoles() const @@ -479,7 +509,7 @@ void DolphinView::readSettings() const int newZoomLevel = m_view->zoomLevel(); if (newZoomLevel != oldZoomLevel) { - emit zoomLevelChanged(newZoomLevel, oldZoomLevel); + Q_EMIT zoomLevelChanged(newZoomLevel, oldZoomLevel); } } @@ -509,20 +539,21 @@ QStringList DolphinView::mimeTypeFilters() const return m_model->mimeTypeFilters(); } -QString DolphinView::statusBarText() const +void DolphinView::requestStatusBarText() { - QString summary; - QString foldersText; - QString filesText; - - int folderCount = 0; - int fileCount = 0; - KIO::filesize_t totalFileSize = 0; + if (m_statJobForStatusBarText) { + // Kill the pending request. + m_statJobForStatusBarText->kill(); + } if (m_container->controller()->selectionManager()->hasSelection()) { + int folderCount = 0; + int fileCount = 0; + KIO::filesize_t totalFileSize = 0; + // Give a summary of the status of the selected files const KFileItemList list = selectedItems(); - foreach (const KFileItem& item, list) { + for (const KFileItem& item : list) { if (item.isDir()) { ++folderCount; } else { @@ -533,14 +564,37 @@ QString DolphinView::statusBarText() const if (folderCount + fileCount == 1) { // If only one item is selected, show info about it - return list.first().getStatusBarInfo(); + Q_EMIT statusBarTextChanged(list.first().getStatusBarInfo()); } else { // At least 2 items are selected - foldersText = i18ncp("@info:status", "1 Folder selected", "%1 Folders selected", folderCount); - filesText = i18ncp("@info:status", "1 File selected", "%1 Files selected", fileCount); + emitStatusBarText(folderCount, fileCount, totalFileSize, HasSelection); + } + } else { // has no selection + if (!m_model->rootItem().url().isValid()) { + return; } + + m_statJobForStatusBarText = KIO::statDetails(m_model->rootItem().url(), + KIO::StatJob::SourceSide, KIO::StatRecursiveSize, KIO::HideProgressInfo); + connect(m_statJobForStatusBarText, &KJob::result, + this, &DolphinView::slotStatJobResult); + m_statJobForStatusBarText->start(); + } +} + +void DolphinView::emitStatusBarText(const int folderCount, const int fileCount, + KIO::filesize_t totalFileSize, const Selection selection) +{ + QString foldersText; + QString filesText; + QString summary; + + if (selection == HasSelection) { + // At least 2 items are selected because the case of 1 selected item is handled in + // DolphinView::requestStatusBarText(). + foldersText = i18ncp("@info:status", "1 Folder selected", "%1 Folders selected", folderCount); + filesText = i18ncp("@info:status", "1 File selected", "%1 Files selected", fileCount); } else { - calculateItemCount(fileCount, folderCount, totalFileSize); foldersText = i18ncp("@info:status", "1 Folder", "%1 Folders", folderCount); filesText = i18ncp("@info:status", "1 File", "%1 Files", fileCount); } @@ -558,8 +612,7 @@ QString DolphinView::statusBarText() const } else { summary = i18nc("@info:status", "0 Folders, 0 Files"); } - - return summary; + Q_EMIT statusBarTextChanged(summary); } QList DolphinView::versionControlActions(const KFileItemList& items) const @@ -601,7 +654,7 @@ void DolphinView::setUrl(const QUrl& url) applyViewProperties(); loadDirectory(url); - emit urlChanged(url); + Q_EMIT urlChanged(url); } void DolphinView::selectAll() @@ -631,12 +684,21 @@ void DolphinView::renameSelectedItems() if (items.count() == 1 && GeneralSettings::renameInline()) { const int index = m_model->index(items.first()); - m_view->editRole(index, "text"); - hideToolTip(); + QMetaObject::Connection * const connection = new QMetaObject::Connection; + *connection = connect(m_view, &KItemListView::scrollingStopped, this, [=](){ + QObject::disconnect(*connection); + delete connection; + + m_view->editRole(index, "text"); + + hideToolTip(); + + connect(m_view, &DolphinItemListView::roleEditingFinished, + this, &DolphinView::slotRoleEditingFinished); + }); + m_view->scrollToItem(index); - connect(m_view, &DolphinItemListView::roleEditingFinished, - this, &DolphinView::slotRoleEditingFinished); } else { KIO::RenameFileDialog* dialog = new KIO::RenameFileDialog(items, this); connect(dialog, &KIO::RenameFileDialog::renamingFinished, @@ -761,7 +823,7 @@ void DolphinView::duplicateSelectedItems() duplicateURL.setPath(originalDirectoryPath + i18nc(" copy", "%1 copy", originalFilenameWithoutExtension) + originalExtension); } - KIO::CopyJob* job = KIO::copyAs(originalURL, duplicateURL, KIO::HideProgressInfo); + KIO::CopyJob* job = KIO::copyAs(originalURL, duplicateURL); KJobWidgets::setWindow(job, this); if (job) { @@ -819,7 +881,7 @@ bool DolphinView::eventFilter(QObject* watched, QEvent* event) if (GeneralSettings::useTabForSwitchingSplitView()) { QKeyEvent* keyEvent = static_cast(event); if (keyEvent->key() == Qt::Key_Tab && keyEvent->modifiers() == Qt::NoModifier) { - emit toggleActiveViewRequested(); + Q_EMIT toggleActiveViewRequested(); return true; } } @@ -857,10 +919,10 @@ bool DolphinView::eventFilter(QObject* watched, QEvent* event) void DolphinView::wheelEvent(QWheelEvent* event) { if (event->modifiers().testFlag(Qt::ControlModifier)) { - const int numDegrees = event->delta() / 8; - const int numSteps = numDegrees / 15; + const QPoint numDegrees = event->angleDelta() / 8; + const QPoint numSteps = numDegrees / 15; - setZoomLevel(zoomLevel() + numSteps); + setZoomLevel(zoomLevel() + numSteps.y()); event->accept(); } else { event->ignore(); @@ -899,7 +961,7 @@ void DolphinView::slotItemActivated(int index) const KFileItem item = m_model->fileItem(index); if (!item.isNull()) { - emit itemActivated(item); + Q_EMIT itemActivated(item); } } @@ -925,16 +987,16 @@ void DolphinView::slotItemsActivated(const KItemSet& indexes) const QUrl& url = openItemAsFolderUrl(item); if (!url.isEmpty()) { // Open folders in new tabs - emit tabRequested(url, DolphinTabWidget::AfterLastTab); + Q_EMIT tabRequested(url); } else { items.append(item); } } if (items.count() == 1) { - emit itemActivated(items.first()); + Q_EMIT itemActivated(items.first()); } else if (items.count() > 1) { - emit itemsActivated(items); + Q_EMIT itemsActivated(items); } } @@ -943,9 +1005,9 @@ void DolphinView::slotItemMiddleClicked(int index) const KFileItem& item = m_model->fileItem(index); const QUrl& url = openItemAsFolderUrl(item); if (!url.isEmpty()) { - emit tabRequested(url, DolphinTabWidget::AfterCurrentTab); + Q_EMIT tabRequested(url); } else if (isTabsForFilesEnabled()) { - emit tabRequested(item.url(), DolphinTabWidget::AfterCurrentTab); + Q_EMIT tabRequested(item.url()); } } @@ -958,12 +1020,12 @@ void DolphinView::slotItemContextMenuRequested(int index, const QPointF& pos) } const KFileItem item = m_model->fileItem(index); - emit requestContextMenu(pos.toPoint(), item, url(), QList()); + Q_EMIT requestContextMenu(pos.toPoint(), item, url(), QList()); } void DolphinView::slotViewContextMenuRequested(const QPointF& pos) { - emit requestContextMenu(pos.toPoint(), KFileItem(), url(), QList()); + Q_EMIT requestContextMenu(pos.toPoint(), KFileItem(), url(), QList()); } void DolphinView::slotHeaderContextMenuRequested(const QPointF& pos) @@ -973,7 +1035,7 @@ void DolphinView::slotHeaderContextMenuRequested(const QPointF& pos) QPointer menu = new QMenu(QApplication::activeWindow()); KItemListView* view = m_container->controller()->view(); - const QSet visibleRolesSet = view->visibleRoles().toSet(); + const QList visibleRolesSet = view->visibleRoles(); bool indexingEnabled = false; #ifdef HAVE_BALOO @@ -986,7 +1048,7 @@ void DolphinView::slotHeaderContextMenuRequested(const QPointF& pos) // Add all roles to the menu that can be shown or hidden by the user const QList rolesInfo = KFileItemModel::rolesInformation(); - foreach (const KFileItemModel::RoleInfo& info, rolesInfo) { + for (const KFileItemModel::RoleInfo& info : rolesInfo) { if (info.role == "text") { // It should not be possible to hide the "text" role continue; @@ -1043,8 +1105,9 @@ void DolphinView::slotHeaderContextMenuRequested(const QPointF& pos) // Apply the current column-widths as custom column-widths and turn // off the automatic resizing of the columns QList columnWidths; - columnWidths.reserve(view->visibleRoles().count()); - foreach (const QByteArray& role, view->visibleRoles()) { + const auto visibleRoles = view->visibleRoles(); + columnWidths.reserve(visibleRoles.count()); + for (const QByteArray& role : visibleRoles) { columnWidths.append(header->columnWidth(role)); } props.setHeaderColumnWidths(columnWidths); @@ -1065,8 +1128,9 @@ void DolphinView::slotHeaderContextMenuRequested(const QPointF& pos) QList columnWidths; if (!header->automaticColumnResizing()) { - columnWidths.reserve(view->visibleRoles().count()); - foreach (const QByteArray& role, view->visibleRoles()) { + const auto visibleRoles = view->visibleRoles(); + columnWidths.reserve(visibleRoles.count()); + for (const QByteArray& role : visibleRoles) { columnWidths.append(header->columnWidth(role)); } } @@ -1087,7 +1151,7 @@ void DolphinView::slotHeaderColumnWidthChangeFinished(const QByteArray& role, qr columnWidths.clear(); columnWidths.reserve(visibleRoles.count()); const KItemListHeader* header = m_view->header(); - foreach (const QByteArray& role, visibleRoles) { + for (const QByteArray& role : visibleRoles) { const int width = header->columnWidth(role); columnWidths.append(width); } @@ -1114,14 +1178,14 @@ void DolphinView::slotItemHovered(int index) #endif } - emit requestItemInfo(item); + Q_EMIT requestItemInfo(item); } void DolphinView::slotItemUnhovered(int index) { Q_UNUSED(index) hideToolTip(); - emit requestItemInfo(KFileItem()); + Q_EMIT requestItemInfo(KFileItem()); } void DolphinView::slotItemDropEvent(int index, QGraphicsSceneDragDropEvent* event) @@ -1188,9 +1252,9 @@ void DolphinView::slotMouseButtonPressed(int itemIndex, Qt::MouseButtons buttons hideToolTip(); if (buttons & Qt::BackButton) { - emit goBackRequested(); + Q_EMIT goBackRequested(); } else if (buttons & Qt::ForwardButton) { - emit goForwardRequested(); + Q_EMIT goForwardRequested(); } } @@ -1223,7 +1287,7 @@ void DolphinView::slotItemCreated(const QUrl& url) void DolphinView::slotJobResult(KJob *job) { if (job->error()) { - emit errorMessage(job->errorString()); + Q_EMIT errorMessage(job->errorString()); } if (!m_selectedUrls.isEmpty()) { m_selectedUrls = KDirModel::simplifiedUrlList(m_selectedUrls); @@ -1247,7 +1311,37 @@ void DolphinView::slotSelectionChanged(const KItemSet& current, const KItemSet& void DolphinView::emitSelectionChangedSignal() { m_selectionChangedTimer->stop(); - emit selectionChanged(selectedItems()); + Q_EMIT selectionChanged(selectedItems()); +} + +void DolphinView::slotStatJobResult(KJob *job) +{ + int folderCount = 0; + int fileCount = 0; + KIO::filesize_t totalFileSize = 0; + bool countFileSize = true; + + const auto entry = static_cast(job)->statResult(); + if (entry.contains(KIO::UDSEntry::UDS_RECURSIVE_SIZE)) { + // We have a precomputed value. + totalFileSize = static_cast( + entry.numberValue(KIO::UDSEntry::UDS_RECURSIVE_SIZE)); + countFileSize = false; + } + + const int itemCount = m_model->count(); + for (int i = 0; i < itemCount; ++i) { + const KFileItem item = m_model->fileItem(i); + if (item.isDir()) { + ++folderCount; + } else { + ++fileCount; + if (countFileSize) { + totalFileSize += item.size(); + } + } + } + emitStatusBarText(folderCount, fileCount, totalFileSize, NoSelection); } void DolphinView::updateSortRole(const QByteArray& role) @@ -1258,7 +1352,7 @@ void DolphinView::updateSortRole(const QByteArray& role) KItemModelBase* model = m_container->controller()->model(); model->setSortRole(role); - emit sortRoleChanged(role); + Q_EMIT sortRoleChanged(role); } void DolphinView::updateSortOrder(Qt::SortOrder order) @@ -1268,7 +1362,7 @@ void DolphinView::updateSortOrder(Qt::SortOrder order) m_model->setSortOrder(order); - emit sortOrderChanged(order); + Q_EMIT sortOrderChanged(order); } void DolphinView::updateSortFoldersFirst(bool foldersFirst) @@ -1278,7 +1372,7 @@ void DolphinView::updateSortFoldersFirst(bool foldersFirst) m_model->setSortDirectoriesFirst(foldersFirst); - emit sortFoldersFirstChanged(foldersFirst); + Q_EMIT sortFoldersFirstChanged(foldersFirst); } QPair DolphinView::pasteInfo() const @@ -1416,16 +1510,30 @@ QUrl DolphinView::openItemAsFolderUrl(const KFileItem& item, const bool browseTh void DolphinView::resetZoomLevel() { - ViewModeSettings::ViewMode mode; - + // TODO : Switch to using ViewModeSettings after MR #256 is merged + int defaultIconSize = KIconLoader::SizeSmall; switch (m_mode) { - case IconsView: mode = ViewModeSettings::IconsMode; break; - case CompactView: mode = ViewModeSettings::CompactMode; break; - case DetailsView: mode = ViewModeSettings::DetailsMode; break; + case IconsView: + IconsModeSettings::self()->useDefaults(true); + defaultIconSize = IconsModeSettings::iconSize(); + IconsModeSettings::self()->useDefaults(false); + break; + case DetailsView: + DetailsModeSettings::self()->useDefaults(true); + defaultIconSize = DetailsModeSettings::iconSize(); + DetailsModeSettings::self()->useDefaults(false); + break; + case CompactView: + CompactModeSettings::self()->useDefaults(true); + defaultIconSize = CompactModeSettings::iconSize(); + CompactModeSettings::self()->useDefaults(false); + break; + default: + Q_ASSERT(false); + break; } - const ViewModeSettings settings(mode); - const QSize iconSize = QSize(settings.iconSize(), settings.iconSize()); - setZoomLevel(ZoomLevelInfo::zoomLevelForIconSize(iconSize)); + + setZoomLevel(ZoomLevelInfo::zoomLevelForIconSize(QSize(defaultIconSize, defaultIconSize))); } void DolphinView::observeCreatedItem(const QUrl& url) @@ -1438,7 +1546,7 @@ void DolphinView::observeCreatedItem(const QUrl& url) void DolphinView::slotDirectoryRedirection(const QUrl& oldUrl, const QUrl& newUrl) { if (oldUrl.matches(url(), QUrl::StripTrailingSlash)) { - emit redirection(oldUrl, newUrl); + Q_EMIT redirection(oldUrl, newUrl); m_url = newUrl; // #186947 } } @@ -1511,39 +1619,11 @@ void DolphinView::hideToolTip(const ToolTipManager::HideBehavior behavior) if (GeneralSettings::showToolTips()) { m_toolTipManager->hideToolTip(behavior); } +#else + Q_UNUSED(behavior) #endif } -void DolphinView::calculateItemCount(int& fileCount, - int& folderCount, - KIO::filesize_t& totalFileSize) const -{ - const int itemCount = m_model->count(); - - bool countFileSize = true; - - // In case we have a precomputed value - const auto job = KIO::statDetails(m_model->rootItem().url(), KIO::StatJob::SourceSide, KIO::StatRecursiveSize); - job->exec(); - const auto entry = job->statResult(); - if (entry.contains(KIO::UDSEntry::UDS_RECURSIVE_SIZE)) { - totalFileSize = static_cast(entry.numberValue(KIO::UDSEntry::UDS_RECURSIVE_SIZE)); - countFileSize = false; - } - - for (int i = 0; i < itemCount; ++i) { - const KFileItem item = m_model->fileItem(i); - if (item.isDir()) { - ++folderCount; - } else { - ++fileCount; - if (countFileSize) { - totalFileSize += item.size(); - } - } - } -} - void DolphinView::slotTwoClicksRenamingTimerTimeout() { const KItemListSelectionManager* selectionManager = m_container->controller()->selectionManager(); @@ -1563,18 +1643,18 @@ void DolphinView::slotTwoClicksRenamingTimerTimeout() void DolphinView::slotTrashFileFinished(KJob* job) { if (job->error() == 0) { - emit operationCompletedMessage(i18nc("@info:status", "Trash operation completed.")); + Q_EMIT operationCompletedMessage(i18nc("@info:status", "Trash operation completed.")); } else if (job->error() != KIO::ERR_USER_CANCELED) { - emit errorMessage(job->errorString()); + Q_EMIT errorMessage(job->errorString()); } } void DolphinView::slotDeleteFileFinished(KJob* job) { if (job->error() == 0) { - emit operationCompletedMessage(i18nc("@info:status", "Delete operation completed.")); + Q_EMIT operationCompletedMessage(i18nc("@info:status", "Delete operation completed.")); } else if (job->error() != KIO::ERR_USER_CANCELED) { - emit errorMessage(job->errorString()); + Q_EMIT errorMessage(job->errorString()); } } @@ -1596,27 +1676,45 @@ void DolphinView::slotRenamingResult(KJob* job) void DolphinView::slotDirectoryLoadingStarted() { + m_loading = true; + updatePlaceholderLabel(); + // Disable the writestate temporary until it can be determined in a fast way // in DolphinView::slotDirectoryLoadingCompleted() if (m_isFolderWritable) { m_isFolderWritable = false; - emit writeStateChanged(m_isFolderWritable); + Q_EMIT writeStateChanged(m_isFolderWritable); } - emit directoryLoadingStarted(); + Q_EMIT directoryLoadingStarted(); } void DolphinView::slotDirectoryLoadingCompleted() { + m_loading = false; + // Update the view-state. This has to be done asynchronously // because the view might not be in its final state yet. QTimer::singleShot(0, this, &DolphinView::updateViewState); - emit directoryLoadingCompleted(); + // Update the placeholder label in case we found that the folder was empty + // after loading it + Q_EMIT directoryLoadingCompleted(); + + updatePlaceholderLabel(); updateWritableState(); } +void DolphinView::slotDirectoryLoadingCanceled() +{ + m_loading = false; + + updatePlaceholderLabel(); + + Q_EMIT directoryLoadingCanceled(); +} + void DolphinView::slotItemsChanged() { m_assureVisibleCurrentIndex = false; @@ -1630,7 +1728,7 @@ void DolphinView::slotSortOrderChangedByHeader(Qt::SortOrder current, Qt::SortOr ViewProperties props(viewPropertiesUrl()); props.setSortOrder(current); - emit sortOrderChanged(current); + Q_EMIT sortOrderChanged(current); } void DolphinView::slotSortRoleChangedByHeader(const QByteArray& current, const QByteArray& previous) @@ -1641,7 +1739,7 @@ void DolphinView::slotSortRoleChangedByHeader(const QByteArray& current, const Q ViewProperties props(viewPropertiesUrl()); props.setSortRole(current); - emit sortRoleChanged(current); + Q_EMIT sortRoleChanged(current); } void DolphinView::slotVisibleRolesChangedByHeader(const QList& current, @@ -1657,7 +1755,7 @@ void DolphinView::slotVisibleRolesChangedByHeader(const QList& curre ViewProperties props(viewPropertiesUrl()); props.setVisibleRoles(m_visibleRoles); - emit visibleRolesChanged(m_visibleRoles, previousVisibleRoles); + Q_EMIT visibleRolesChanged(m_visibleRoles, previousVisibleRoles); } void DolphinView::slotRoleEditingCanceled() @@ -1671,13 +1769,15 @@ void DolphinView::slotRoleEditingFinished(int index, const QByteArray& role, con disconnect(m_view, &DolphinItemListView::roleEditingFinished, this, &DolphinView::slotRoleEditingFinished); - if (index < 0 || index >= m_model->count()) { + const KFileItemList items = selectedItems(); + if (items.count() != 1) { return; } if (role == "text") { - const KFileItem oldItem = m_model->fileItem(index); - const QString newName = value.toString(); + const KFileItem oldItem = items.first(); + const EditResult retVal = value.value(); + const QString newName = retVal.newName; if (!newName.isEmpty() && newName != oldItem.text() && newName != QLatin1Char('.') && newName != QLatin1String("..")) { const QUrl oldUrl = oldItem.url(); @@ -1708,14 +1808,14 @@ void DolphinView::slotRoleEditingFinished(int index, const QByteArray& role, con #endif const bool newNameExistsAlready = (m_model->index(newUrl) >= 0); - if (!newNameExistsAlready) { + if (!newNameExistsAlready && m_model->index(oldUrl) == index) { // Only change the data in the model if no item with the new name // is in the model yet. If there is an item with the new name // already, calling KIO::CopyJob will open a dialog // asking for a new name, and KFileItemModel will update the // data when the dir lister signals that the file name has changed. QHash data; - data.insert(role, value); + data.insert(role, retVal.newName); m_model->setData(index, data); } @@ -1732,6 +1832,13 @@ void DolphinView::slotRoleEditingFinished(int index, const QByteArray& role, con connect(job, &KJob::result, this, &DolphinView::slotRenamingResult); } } + if (retVal.direction != EditDone) { + const short indexShift = retVal.direction == EditNext ? 1 : -1; + m_container->controller()->selectionManager()->setSelected(index, 1, KItemListSelectionManager::Deselect); + m_container->controller()->selectionManager()->setSelected(index + indexShift, 1, + KItemListSelectionManager::Select); + renameSelectedItems(); + } } } @@ -1740,9 +1847,9 @@ void DolphinView::loadDirectory(const QUrl& url, bool reload) if (!url.isValid()) { const QString location(url.toDisplayString(QUrl::PreferLocalFile)); if (location.isEmpty()) { - emit errorMessage(i18nc("@info:status", "The location is empty.")); + Q_EMIT errorMessage(i18nc("@info:status", "The location is empty.")); } else { - emit errorMessage(i18nc("@info:status", "The location '%1' is invalid.", location)); + Q_EMIT errorMessage(i18nc("@info:status", "The location '%1' is invalid.", location)); } return; } @@ -1775,41 +1882,41 @@ void DolphinView::applyViewProperties(const ViewProperties& props) const int oldZoomLevel = m_view->zoomLevel(); applyModeToView(); - emit modeChanged(m_mode, previousMode); + Q_EMIT modeChanged(m_mode, previousMode); if (m_view->zoomLevel() != oldZoomLevel) { - emit zoomLevelChanged(m_view->zoomLevel(), oldZoomLevel); + Q_EMIT zoomLevelChanged(m_view->zoomLevel(), oldZoomLevel); } } const bool hiddenFilesShown = props.hiddenFilesShown(); if (hiddenFilesShown != m_model->showHiddenFiles()) { m_model->setShowHiddenFiles(hiddenFilesShown); - emit hiddenFilesShownChanged(hiddenFilesShown); + Q_EMIT hiddenFilesShownChanged(hiddenFilesShown); } const bool groupedSorting = props.groupedSorting(); if (groupedSorting != m_model->groupedSorting()) { m_model->setGroupedSorting(groupedSorting); - emit groupedSortingChanged(groupedSorting); + Q_EMIT groupedSortingChanged(groupedSorting); } const QByteArray sortRole = props.sortRole(); if (sortRole != m_model->sortRole()) { m_model->setSortRole(sortRole); - emit sortRoleChanged(sortRole); + Q_EMIT sortRoleChanged(sortRole); } const Qt::SortOrder sortOrder = props.sortOrder(); if (sortOrder != m_model->sortOrder()) { m_model->setSortOrder(sortOrder); - emit sortOrderChanged(sortOrder); + Q_EMIT sortOrderChanged(sortOrder); } const bool sortFoldersFirst = props.sortFoldersFirst(); if (sortFoldersFirst != m_model->sortDirectoriesFirst()) { m_model->setSortDirectoriesFirst(sortFoldersFirst); - emit sortFoldersFirstChanged(sortFoldersFirst); + Q_EMIT sortFoldersFirstChanged(sortFoldersFirst); } const QList visibleRoles = props.visibleRoles(); @@ -1817,7 +1924,7 @@ void DolphinView::applyViewProperties(const ViewProperties& props) const QList previousVisibleRoles = m_visibleRoles; m_visibleRoles = visibleRoles; m_view->setVisibleRoles(visibleRoles); - emit visibleRolesChanged(m_visibleRoles, previousVisibleRoles); + Q_EMIT visibleRolesChanged(m_visibleRoles, previousVisibleRoles); } const bool previewsShown = props.previewsShown(); @@ -1825,11 +1932,11 @@ void DolphinView::applyViewProperties(const ViewProperties& props) const int oldZoomLevel = zoomLevel(); m_view->setPreviewsShown(previewsShown); - emit previewsShownChanged(previewsShown); + Q_EMIT previewsShownChanged(previewsShown); // Changing the preview-state might result in a changed zoom-level if (oldZoomLevel != zoomLevel()) { - emit zoomLevelChanged(zoomLevel(), oldZoomLevel); + Q_EMIT zoomLevelChanged(zoomLevel(), oldZoomLevel); } } @@ -1880,7 +1987,7 @@ QList DolphinView::simplifiedSelectedUrls() const const KFileItemList items = selectedItems(); urls.reserve(items.count()); - foreach (const KFileItem& item, items) { + for (const KFileItem& item : items) { urls.append(item.url()); } @@ -1917,7 +2024,7 @@ void DolphinView::updateWritableState() m_isFolderWritable = capabilities.supportsWriting(); if (m_isFolderWritable != wasFolderWritable) { - emit writeStateChanged(m_isFolderWritable); + Q_EMIT writeStateChanged(m_isFolderWritable); } } @@ -1945,3 +2052,81 @@ void DolphinView::forceUrlsSelection(const QUrl& current, const QList& sel markUrlAsCurrent(current); markUrlsAsSelected(selected); } + +void DolphinView::copyPathToClipboard() +{ + const KFileItemList list = selectedItems(); + if (list.isEmpty()) { + return; + } + const KFileItem& item = list.at(0); + QString path = item.localPath(); + if (path.isEmpty()) { + path = item.url().toDisplayString(); + } + QClipboard* clipboard = QApplication::clipboard(); + if (clipboard == nullptr) { + return; + } + clipboard->setText(path); +} + +void DolphinView::slotIncreaseZoom() +{ + setZoomLevel(zoomLevel() + 1); +} + +void DolphinView::slotDecreaseZoom() +{ + setZoomLevel(zoomLevel() - 1); +} + +void DolphinView::slotSwipeUp() +{ + Q_EMIT goUpRequested(); +} + +void DolphinView::showLoadingPlaceholder() +{ + m_placeholderLabel->setText(i18n("Loading...")); + m_placeholderLabel->setVisible(true); +} + +void DolphinView::updatePlaceholderLabel() +{ + m_showLoadingPlaceholderTimer->stop(); + if (itemsCount() > 0) { + m_placeholderLabel->setVisible(false); + return; + } + + if (m_loading) { + m_placeholderLabel->setVisible(false); + m_showLoadingPlaceholderTimer->start(); + return; + } + + if (!nameFilter().isEmpty()) { + m_placeholderLabel->setText(i18n("No items matching the filter")); + } else if (m_url.scheme() == QLatin1String("baloosearch") || m_url.scheme() == QLatin1String("filenamesearch")) { + m_placeholderLabel->setText(i18n("No items matching the search")); + } else if (m_url.scheme() == QLatin1String("trash") && m_url.path() == QLatin1String("/")) { + m_placeholderLabel->setText(i18n("Trash is empty")); + } else if (m_url.scheme() == QLatin1String("tags")) { + m_placeholderLabel->setText(i18n("No tags")); + } else if (m_url.scheme() == QLatin1String("recentlyused")) { + m_placeholderLabel->setText(i18n("No recently used items")); + } else if (m_url.scheme() == QLatin1String("smb")) { + m_placeholderLabel->setText(i18n("No shared folders found")); + } else if (m_url.scheme() == QLatin1String("network")) { + m_placeholderLabel->setText(i18n("No relevant network resources found")); + } else if (m_url.scheme() == QLatin1String("mtp") && m_url.path() == QLatin1String("/")) { + m_placeholderLabel->setText(i18n("No MTP-compatible devices found")); + } else if (m_url.scheme() == QLatin1String("bluetooth")) { + m_placeholderLabel->setText(i18n("No Bluetooth devices found")); + } else { + m_placeholderLabel->setText(i18n("Folder is empty")); + } + + m_placeholderLabel->setVisible(true); +}