diff options
| author | Méven Car <[email protected]> | 2023-06-28 09:49:46 +0200 |
|---|---|---|
| committer | Méven Car <[email protected]> | 2023-06-28 09:49:46 +0200 |
| commit | cd2e64154fd5446a7e19aff4cb147efe2f2ba31e (patch) | |
| tree | 37f4e2c8644129f809a66fd2f6b2c9b28d52fed8 /src | |
| parent | dcd5c994bb1d331b94fdea8a5c6cd55a471b34b8 (diff) | |
| parent | ea56e1f75eae435c18e3934c402c94ae76ec9c11 (diff) | |
Merge branch 'master' into kf6
Diffstat (limited to 'src')
63 files changed, 1179 insertions, 537 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 56637ff6c..9cd4e2932 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -184,6 +184,7 @@ kconfig_add_kcfg_files(dolphinprivate settings/dolphin_directoryviewpropertysettings.kcfgc settings/dolphin_detailsmodesettings.kcfgc settings/dolphin_iconsmodesettings.kcfgc + settings/dolphin_contentdisplaysettings.kcfgc settings/dolphin_generalsettings.kcfgc settings/dolphin_contextmenusettings.kcfgc settings/dolphin_versioncontrolsettings.kcfgc @@ -307,6 +308,7 @@ target_sources(dolphinstatic PRIVATE settings/viewmodes/viewsettingspage.cpp settings/viewmodes/viewmodesettings.cpp settings/viewmodes/viewsettingstab.cpp + settings/viewmodes/contentdisplaytab.cpp statusbar/dolphinstatusbar.cpp statusbar/mountpointobserver.cpp statusbar/mountpointobservercache.cpp @@ -365,6 +367,7 @@ target_sources(dolphinstatic PRIVATE settings/viewmodes/viewsettingspage.h settings/viewmodes/viewmodesettings.h settings/viewmodes/viewsettingstab.h + settings/viewmodes/contentdisplaytab.h statusbar/dolphinstatusbar.h statusbar/mountpointobserver.h statusbar/mountpointobservercache.h @@ -409,6 +412,7 @@ kconfig_add_kcfg_files(dolphinstatic settings/dolphin_detailsmodesettings.kcfgc settings/dolphin_contextmenusettings.kcfgc settings/dolphin_iconsmodesettings.kcfgc + settings/dolphin_contentdisplaysettings.kcfgc search/dolphin_searchsettings.kcfgc settings/dolphin_versioncontrolsettings.kcfgc ) @@ -543,6 +547,7 @@ if(NOT WIN32) settings/dolphin_directoryviewpropertysettings.kcfgc settings/dolphin_detailsmodesettings.kcfgc settings/dolphin_iconsmodesettings.kcfgc + settings/dolphin_contentdisplaysettings.kcfgc settings/dolphin_generalsettings.kcfgc settings/dolphin_versioncontrolsettings.kcfgc ) @@ -585,6 +590,7 @@ install( FILES settings/dolphin_directoryviewpropertysettings.kcfg settings/dolphin_compactmodesettings.kcfg settings/dolphin_iconsmodesettings.kcfg settings/dolphin_detailsmodesettings.kcfg + settings/dolphin_contentdisplaysettings.kcfg settings/dolphin_versioncontrolsettings.kcfg DESTINATION ${KDE_INSTALL_KCFGDIR} ) diff --git a/src/dolphinmainwindow.cpp b/src/dolphinmainwindow.cpp index a03655f5a..480e91727 100644 --- a/src/dolphinmainwindow.cpp +++ b/src/dolphinmainwindow.cpp @@ -277,31 +277,6 @@ void DolphinMainWindow::openFiles(const QStringList &files, bool splitView) openFiles(QUrl::fromStringList(files), splitView); } -bool DolphinMainWindow::isOnCurrentDesktop() const -{ -#if HAVE_X11 - if (KWindowSystem::isPlatformX11()) { - const NET::Properties properties = NET::WMDesktop; - KWindowInfo info(this->winId(), properties); - return info.isOnCurrentDesktop(); - } -#endif - return true; -} - -bool DolphinMainWindow::isOnActivity(const QString &activityId) const -{ -#if HAVE_X11 && HAVE_KACTIVITIES - if (KWindowSystem::isPlatformX11()) { - const NET::Properties properties = NET::Supported; - const NET::Properties2 properties2 = NET::WM2Activities; - KWindowInfo info(this->winId(), properties, properties2); - return info.activities().contains(activityId); - } -#endif - return true; -} - void DolphinMainWindow::activateWindow(const QString &activationToken) { window()->setAttribute(Qt::WA_NativeWindow, true); @@ -1782,7 +1757,14 @@ void DolphinMainWindow::setupActions() stashSplit->setVisible(sessionInterface && sessionInterface->isServiceRegistered(QStringLiteral("org.kde.kio.StashNotifier"))); connect(stashSplit, &QAction::triggered, this, &DolphinMainWindow::toggleSplitStash); - KStandardAction::redisplay(this, &DolphinMainWindow::reloadView, actionCollection()); + QAction *redisplay = KStandardAction::redisplay(this, &DolphinMainWindow::reloadView, actionCollection()); + redisplay->setToolTip(i18nc("@info:tooltip", "Refresh view")); + redisplay->setWhatsThis(xi18nc("@info:whatsthis refresh", + "<para>This refreshes " + "the folder view.</para>" + "<para>If the contents of this folder have changed, refreshing will re-scan this folder " + "and show you a newly-updated view of the files and folders contained here.</para>" + "<para>If the view is split, this refreshes the one that is currently in focus.</para>")); QAction *stop = actionCollection()->addAction(QStringLiteral("stop")); stop->setText(i18nc("@action:inmenu View", "Stop")); diff --git a/src/dolphinmainwindow.h b/src/dolphinmainwindow.h index 92ddb24c3..5bb17e79a 100644 --- a/src/dolphinmainwindow.h +++ b/src/dolphinmainwindow.h @@ -204,9 +204,6 @@ public Q_SLOTS: /** @see GeneralSettings::splitViewChanged() */ void slotSplitViewChanged(); - bool isOnActivity(const QString &activityId) const; - bool isOnCurrentDesktop() const; - Q_SIGNALS: /** * Is sent if the selection of the currently active view has diff --git a/src/dolphinnavigatorswidgetaction.cpp b/src/dolphinnavigatorswidgetaction.cpp index cb2e4bc00..a68118395 100644 --- a/src/dolphinnavigatorswidgetaction.cpp +++ b/src/dolphinnavigatorswidgetaction.cpp @@ -1,6 +1,6 @@ /* This file is part of the KDE project - SPDX-FileCopyrightText: 2020 Felix Ernst <[email protected]> + SPDX-FileCopyrightText: 2020 Felix Ernst <[email protected]> SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL */ diff --git a/src/dolphinnavigatorswidgetaction.h b/src/dolphinnavigatorswidgetaction.h index 67c93d22d..6f068e27d 100644 --- a/src/dolphinnavigatorswidgetaction.h +++ b/src/dolphinnavigatorswidgetaction.h @@ -1,6 +1,6 @@ /* This file is part of the KDE project - SPDX-FileCopyrightText: 2020 Felix Ernst <[email protected]> + SPDX-FileCopyrightText: 2020 Felix Ernst <[email protected]> SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL */ diff --git a/src/dolphinpartactions.desktop b/src/dolphinpartactions.desktop index 9a547f0ca..24ee78f7e 100644 --- a/src/dolphinpartactions.desktop +++ b/src/dolphinpartactions.desktop @@ -97,7 +97,7 @@ Name[ie]=Compact Name[is]=Þétt Name[it]=Compatta Name[ja]=コンパクト -Name[ka]=დაპატარავება +Name[ka]=კომპაქტური Name[ko]=축소됨 Name[lt]=Kompaktiškas Name[lv]=Kompakts diff --git a/src/dolphintabbar.cpp b/src/dolphintabbar.cpp index aa74e17ae..ac9f1f734 100644 --- a/src/dolphintabbar.cpp +++ b/src/dolphintabbar.cpp @@ -103,14 +103,15 @@ void DolphinTabBar::mouseReleaseEvent(QMouseEvent *event) void DolphinTabBar::mouseDoubleClickEvent(QMouseEvent *event) { - const int index = tabAt(event->pos()); + int index = tabAt(event->pos()); if (index < 0) { - // Double click on the empty tabbar area opens a new activated tab - // with the url from the current tab. - Q_EMIT openNewActivatedTab(currentIndex()); - return; + // empty tabbar area case + index = currentIndex(); } + // Double click on the tabbar opens a new activated tab + // with the url from the doubleclicked tab or currentTab otherwise. + Q_EMIT openNewActivatedTab(index); QTabBar::mouseDoubleClickEvent(event); } diff --git a/src/dolphintabpage.cpp b/src/dolphintabpage.cpp index 2979cb568..413e77e81 100644 --- a/src/dolphintabpage.cpp +++ b/src/dolphintabpage.cpp @@ -1,6 +1,6 @@ /* * SPDX-FileCopyrightText: 2014 Emmanuel Pescosta <[email protected]> - * SPDX-FileCopyrightText: 2020 Felix Ernst <[email protected]> + * SPDX-FileCopyrightText: 2020 Felix Ernst <[email protected]> * * SPDX-License-Identifier: GPL-2.0-or-later */ diff --git a/src/dolphintabpage.h b/src/dolphintabpage.h index 4e89d22ee..0c691830e 100644 --- a/src/dolphintabpage.h +++ b/src/dolphintabpage.h @@ -1,6 +1,6 @@ /* * SPDX-FileCopyrightText: 2014 Emmanuel Pescosta <[email protected]> - * SPDX-FileCopyrightText: 2020 Felix Ernst <[email protected]> + * SPDX-FileCopyrightText: 2020 Felix Ernst <[email protected]> * * SPDX-License-Identifier: GPL-2.0-or-later */ diff --git a/src/dolphinurlnavigator.cpp b/src/dolphinurlnavigator.cpp index ad0a86b77..280b8b2e1 100644 --- a/src/dolphinurlnavigator.cpp +++ b/src/dolphinurlnavigator.cpp @@ -1,6 +1,6 @@ /* This file is part of the KDE project - SPDX-FileCopyrightText: 2020 Felix Ernst <[email protected]> + SPDX-FileCopyrightText: 2020 Felix Ernst <[email protected]> SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL */ diff --git a/src/dolphinurlnavigator.h b/src/dolphinurlnavigator.h index 572c29f63..93a573105 100644 --- a/src/dolphinurlnavigator.h +++ b/src/dolphinurlnavigator.h @@ -1,6 +1,6 @@ /* This file is part of the KDE project - SPDX-FileCopyrightText: 2020 Felix Ernst <[email protected]> + SPDX-FileCopyrightText: 2020 Felix Ernst <[email protected]> SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL */ diff --git a/src/dolphinurlnavigatorscontroller.cpp b/src/dolphinurlnavigatorscontroller.cpp index bdf6513f1..32845710d 100644 --- a/src/dolphinurlnavigatorscontroller.cpp +++ b/src/dolphinurlnavigatorscontroller.cpp @@ -1,6 +1,6 @@ /* This file is part of the KDE project - SPDX-FileCopyrightText: 2020 Felix Ernst <[email protected]> + SPDX-FileCopyrightText: 2020 Felix Ernst <[email protected]> SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL */ diff --git a/src/dolphinurlnavigatorscontroller.h b/src/dolphinurlnavigatorscontroller.h index fec15f481..8e9cf562d 100644 --- a/src/dolphinurlnavigatorscontroller.h +++ b/src/dolphinurlnavigatorscontroller.h @@ -1,6 +1,6 @@ /* This file is part of the KDE project - SPDX-FileCopyrightText: 2020 Felix Ernst <[email protected]> + SPDX-FileCopyrightText: 2020 Felix Ernst <[email protected]> SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL */ diff --git a/src/dolphinviewcontainer.cpp b/src/dolphinviewcontainer.cpp index 57b443eb4..12108b729 100644 --- a/src/dolphinviewcontainer.cpp +++ b/src/dolphinviewcontainer.cpp @@ -6,8 +6,11 @@ #include "dolphinviewcontainer.h" +#include "dolphin_compactmodesettings.h" +#include "dolphin_contentdisplaysettings.h" #include "dolphin_detailsmodesettings.h" #include "dolphin_generalsettings.h" +#include "dolphin_iconsmodesettings.h" #include "dolphindebug.h" #include "dolphinplacesmodelsingleton.h" #include "filterbar/filterbar.h" @@ -171,11 +174,9 @@ DolphinViewContainer::DolphinViewContainer(const QUrl &url, QWidget *parent) setSearchModeEnabled(isSearchUrl(url)); - connect(DetailsModeSettings::self(), &KCoreConfigSkeleton::configChanged, this, [=]() { - if (view()->viewMode() == DolphinView::Mode::DetailsView) { - view()->reload(); - } - }); + // 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 + connect(ContentDisplaySettings::self(), &KCoreConfigSkeleton::configChanged, m_view, &DolphinView::reload); KFilePlacesModel *placesModel = DolphinPlacesModelSingleton::instance().placesModel(); connect(placesModel, &KFilePlacesModel::dataChanged, this, &DolphinViewContainer::slotPlacesModelChanged); diff --git a/src/global.cpp b/src/global.cpp index 8babbbddc..554eb41fa 100644 --- a/src/global.cpp +++ b/src/global.cpp @@ -16,9 +16,6 @@ #include <KIO/ApplicationLauncherJob> #include <KService> #include <KWindowSystem> -#ifdef HAVE_KACTIVITIES -#include <KActivities/Consumer> -#endif #include <QApplication> @@ -143,37 +140,13 @@ bool Dolphin::attachToExistingInstance(const QList<QUrl> &inputUrls, QVector<QPair<QSharedPointer<OrgKdeDolphinMainWindowInterface>, QStringList>> Dolphin::dolphinGuiInstances(const QString &preferredService) { -#ifdef HAVE_KACTIVITIES - static std::once_flag one_consumer; - static KActivities::Consumer *consumer; - std::call_once(one_consumer, []() { - consumer = new KActivities::Consumer(); - // ensures the consumer is ready for query - QEventLoop loop; - QObject::connect(consumer, &KActivities::Consumer::serviceStatusChanged, &loop, &QEventLoop::quit); - loop.exec(); - }); -#endif - QVector<QPair<QSharedPointer<OrgKdeDolphinMainWindowInterface>, QStringList>> dolphinInterfaces; - const auto tryAppendInterface = [&dolphinInterfaces](const QString &service) { - // Check if instance can handle our URLs - QSharedPointer<OrgKdeDolphinMainWindowInterface> interface( - new OrgKdeDolphinMainWindowInterface(service, QStringLiteral("/dolphin/Dolphin_1"), QDBusConnection::sessionBus())); - if (interface->isValid() && !interface->lastError().isValid()) { -#ifdef HAVE_KACTIVITIES - const auto currentActivity = consumer->currentActivity(); - if (currentActivity.isEmpty() || currentActivity == QStringLiteral("00000000-0000-0000-0000-000000000000") - || interface->isOnActivity(consumer->currentActivity())) -#endif - if (interface->isOnCurrentDesktop()) { - dolphinInterfaces.append(qMakePair(interface, QStringList())); - } - } - }; - if (!preferredService.isEmpty()) { - tryAppendInterface(preferredService); + QSharedPointer<OrgKdeDolphinMainWindowInterface> preferredInterface( + new OrgKdeDolphinMainWindowInterface(preferredService, QStringLiteral("/dolphin/Dolphin_1"), QDBusConnection::sessionBus())); + if (preferredInterface->isValid() && !preferredInterface->lastError().isValid()) { + dolphinInterfaces.append(qMakePair(preferredInterface, QStringList())); + } } // Look for dolphin instances among all available dbus services. @@ -185,7 +158,12 @@ QVector<QPair<QSharedPointer<OrgKdeDolphinMainWindowInterface>, QStringList>> Do const QString myPid = QLatin1Char('-') + QString::number(QCoreApplication::applicationPid()); for (const QString &service : dbusServices) { if (service.startsWith(pattern) && !service.endsWith(myPid)) { - tryAppendInterface(service); + // Check if instance can handle our URLs + QSharedPointer<OrgKdeDolphinMainWindowInterface> interface( + new OrgKdeDolphinMainWindowInterface(service, QStringLiteral("/dolphin/Dolphin_1"), QDBusConnection::sessionBus())); + if (interface->isValid() && !interface->lastError().isValid()) { + dolphinInterfaces.append(qMakePair(interface, QStringList())); + } } } diff --git a/src/kitemviews/kfileitemlisttostring.cpp b/src/kitemviews/kfileitemlisttostring.cpp index d10680adc..9f281c813 100644 --- a/src/kitemviews/kfileitemlisttostring.cpp +++ b/src/kitemviews/kfileitemlisttostring.cpp @@ -1,6 +1,6 @@ /* This file is part of the KDE project - SPDX-FileCopyrightText: 2022 Felix Ernst <[email protected]> + SPDX-FileCopyrightText: 2022 Felix Ernst <[email protected]> SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL */ diff --git a/src/kitemviews/kfileitemlisttostring.h b/src/kitemviews/kfileitemlisttostring.h index f1c2902b8..1cba1a43c 100644 --- a/src/kitemviews/kfileitemlisttostring.h +++ b/src/kitemviews/kfileitemlisttostring.h @@ -1,6 +1,6 @@ /* This file is part of the KDE project - SPDX-FileCopyrightText: 2022 Felix Ernst <[email protected]> + SPDX-FileCopyrightText: 2022 Felix Ernst <[email protected]> SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL */ diff --git a/src/kitemviews/kfileitemlistview.cpp b/src/kitemviews/kfileitemlistview.cpp index e2a27a5ea..668ebdfb2 100644 --- a/src/kitemviews/kfileitemlistview.cpp +++ b/src/kitemviews/kfileitemlistview.cpp @@ -41,7 +41,6 @@ KFileItemListView::KFileItemListView(QGraphicsWidget *parent) , m_modelRolesUpdater(nullptr) , m_updateVisibleIndexRangeTimer(nullptr) , m_updateIconSizeTimer(nullptr) - , m_scanDirectories(true) { setAcceptDrops(true); @@ -119,19 +118,6 @@ qlonglong KFileItemListView::localFileSizePreviewLimit() const return m_modelRolesUpdater ? m_modelRolesUpdater->localFileSizePreviewLimit() : 0; } -void KFileItemListView::setScanDirectories(bool enabled) -{ - m_scanDirectories = enabled; - if (m_modelRolesUpdater) { - m_modelRolesUpdater->setScanDirectories(m_scanDirectories); - } -} - -bool KFileItemListView::scanDirectories() -{ - return m_scanDirectories; -} - QPixmap KFileItemListView::createDragPixmap(const KItemSet &indexes) const { if (!model()) { @@ -269,7 +255,6 @@ void KFileItemListView::onModelChanged(KItemModelBase *current, KItemModelBase * if (current) { m_modelRolesUpdater = new KFileItemModelRolesUpdater(static_cast<KFileItemModel *>(current), this); m_modelRolesUpdater->setIconSize(availableIconSize()); - m_modelRolesUpdater->setScanDirectories(scanDirectories()); applyRolesToModel(); } diff --git a/src/kitemviews/kfileitemlistview.h b/src/kitemviews/kfileitemlistview.h index 63bcf9e75..b4be0093e 100644 --- a/src/kitemviews/kfileitemlistview.h +++ b/src/kitemviews/kfileitemlistview.h @@ -71,13 +71,6 @@ public: void setLocalFileSizePreviewLimit(qlonglong size); qlonglong localFileSizePreviewLimit() const; - /** - * If set to true, directories contents are scanned to determine their size - * Default true - */ - void setScanDirectories(bool enabled); - bool scanDirectories(); - QPixmap createDragPixmap(const KItemSet &indexes) const override; /** @@ -137,7 +130,6 @@ private: KFileItemModelRolesUpdater *m_modelRolesUpdater; QTimer *m_updateVisibleIndexRangeTimer; QTimer *m_updateIconSizeTimer; - bool m_scanDirectories; friend class KFileItemListViewTest; // For unit testing }; diff --git a/src/kitemviews/kfileitemlistwidget.cpp b/src/kitemviews/kfileitemlistwidget.cpp index d9644bef5..385067af0 100644 --- a/src/kitemviews/kfileitemlistwidget.cpp +++ b/src/kitemviews/kfileitemlistwidget.cpp @@ -9,7 +9,7 @@ #include "kfileitemmodel.h" #include "kitemlistview.h" -#include "dolphin_detailsmodesettings.h" +#include "dolphin_contentdisplaysettings.h" #include <KFormat> #include <KLocalizedString> @@ -56,7 +56,7 @@ QString KFileItemListWidgetInformant::roleText(const QByteArray &role, const QHa // use a hash + switch for a linear runtime. auto formatDate = [formatter, local](const QDateTime &time) { - if (DetailsModeSettings::useShortRelativeDates()) { + if (ContentDisplaySettings::useShortRelativeDates()) { return formatter.formatRelativeDateTime(time, QLocale::ShortFormat); } else { return local.toString(time, QLocale::ShortFormat); @@ -67,7 +67,7 @@ QString KFileItemListWidgetInformant::roleText(const QByteArray &role, const QHa if (values.value("isDir").toBool()) { if (!roleValue.isNull() && roleValue != -1) { // The item represents a directory. - if (DetailsModeSettings::directorySizeCount()) { + if (ContentDisplaySettings::directorySizeCount() || roleValue == -2 /* size is invalid */) { // Show the number of sub directories instead of the file size of the directory. const int count = values.value("count").toInt(); text = i18ncp("@item:intable", "%1 item", "%1 items", count); @@ -101,14 +101,14 @@ QString KFileItemListWidgetInformant::roleText(const QByteArray &role, const QHa } else if (role == "permissions") { const auto permissions = roleValue.value<QVariantList>(); - switch (DetailsModeSettings::usePermissionsFormat()) { - case DetailsModeSettings::EnumUsePermissionsFormat::SymbolicFormat: + switch (ContentDisplaySettings::usePermissionsFormat()) { + case ContentDisplaySettings::EnumUsePermissionsFormat::SymbolicFormat: text = permissions.at(0).toString(); break; - case DetailsModeSettings::EnumUsePermissionsFormat::NumericFormat: + case ContentDisplaySettings::EnumUsePermissionsFormat::NumericFormat: text = QString::number(permissions.at(1).toInt(), 8); break; - case DetailsModeSettings::EnumUsePermissionsFormat::CombinedFormat: + case ContentDisplaySettings::EnumUsePermissionsFormat::CombinedFormat: text = QString("%1 (%2)").arg(permissions.at(0).toString()).arg(permissions.at(1).toInt(), 0, 8); break; } diff --git a/src/kitemviews/kfileitemmodel.cpp b/src/kitemviews/kfileitemmodel.cpp index 4b06525f5..28e0876b9 100644 --- a/src/kitemviews/kfileitemmodel.cpp +++ b/src/kitemviews/kfileitemmodel.cpp @@ -8,7 +8,7 @@ #include "kfileitemmodel.h" -#include "dolphin_detailsmodesettings.h" +#include "dolphin_contentdisplaysettings.h" #include "dolphin_generalsettings.h" #include "dolphindebug.h" #include "private/kfileitemmodelsortalgorithm.h" @@ -104,6 +104,8 @@ KFileItemModel::KFileItemModel(QObject *parent) connect(m_resortAllItemsTimer, &QTimer::timeout, this, &KFileItemModel::resortAllItems); connect(GeneralSettings::self(), &GeneralSettings::sortingChoiceChanged, this, &KFileItemModel::slotSortingChoiceChanged); + + setShowTrashMime(m_dirLister->showHiddenFiles()); } KFileItemModel::~KFileItemModel() @@ -128,6 +130,8 @@ void KFileItemModel::refreshDirectory(const QUrl &url) } m_dirLister->openUrl(url, KDirLister::Reload); + + Q_EMIT directoryRefreshing(); } QUrl KFileItemModel::directory() const @@ -235,9 +239,31 @@ bool KFileItemModel::sortHiddenLast() const return m_sortHiddenLast; } +void KFileItemModel::setShowTrashMime(bool show) +{ + const auto trashMime = QStringLiteral("application/x-trash"); + QStringList excludeFilter = m_filter.excludeMimeTypes(); + bool wasShown = !excludeFilter.contains(trashMime); + + if (show) { + if (!wasShown) { + excludeFilter.removeAll(trashMime); + } + } else { + if (wasShown) { + excludeFilter.append(trashMime); + } + } + + if (wasShown != show) { + setExcludeMimeTypeFilter(excludeFilter); + } +} + void KFileItemModel::setShowHiddenFiles(bool show) { m_dirLister->setShowHiddenFiles(show); + setShowTrashMime(show); m_dirLister->emitChanges(); if (show) { dispatchPendingItemsToInsert(); @@ -725,6 +751,20 @@ QStringList KFileItemModel::mimeTypeFilters() const return m_filter.mimeTypes(); } +void KFileItemModel::setExcludeMimeTypeFilter(const QStringList &filters) +{ + if (m_filter.excludeMimeTypes() != filters) { + dispatchPendingItemsToInsert(); + m_filter.setExcludeMimeTypes(filters); + applyFilters(); + } +} + +QStringList KFileItemModel::excludeMimeTypeFilter() const +{ + return m_filter.excludeMimeTypes(); +} + void KFileItemModel::applyFilters() { // ===STEP 1=== @@ -1808,7 +1848,7 @@ QHash<QByteArray, QVariant> KFileItemModel::retrieveData(const KFileItem &item, } if (m_requestRole[IsHiddenRole]) { - data.insert(sharedValue("isHidden"), item.isHidden()); + data.insert(sharedValue("isHidden"), item.isHidden() || item.mimetype() == QStringLiteral("application/x-trash")); } if (m_requestRole[NameRole]) { @@ -1816,6 +1856,7 @@ QHash<QByteArray, QVariant> KFileItemModel::retrieveData(const KFileItem &item, } if (m_requestRole[ExtensionRole] && !isDir) { + // TODO KF6 use KFileItem::suffix 464722 data.insert(sharedValue("extension"), QFileInfo(item.name()).suffix()); } @@ -1975,7 +2016,7 @@ bool KFileItemModel::lessThan(const ItemData *a, const ItemData *b, const QColla } } - if (m_sortDirsFirst || (DetailsModeSettings::directorySizeCount() && m_sortRole == SizeRole)) { + if (m_sortDirsFirst || (ContentDisplaySettings::directorySizeCount() && m_sortRole == SizeRole)) { const bool isDirA = a->item.isDir(); const bool isDirB = b->item.isDir(); if (isDirA && !isDirB) { @@ -2027,7 +2068,7 @@ int KFileItemModel::sortRoleCompare(const ItemData *a, const ItemData *b, const break; case SizeRole: { - if (DetailsModeSettings::directorySizeCount() && itemA.isDir()) { + if (ContentDisplaySettings::directorySizeCount() && itemA.isDir()) { // folders first then // items A and B are folders thanks to lessThan checks auto valueA = a->values.value("count"); @@ -2287,7 +2328,7 @@ QList<QPair<int, QVariant>> KFileItemModel::sizeRoleGroups() const KIO::filesize_t fileSize = !item.isNull() ? item.size() : ~0U; QString newGroupValue; if (!item.isNull() && item.isDir()) { - if (DetailsModeSettings::directorySizeCount() || m_sortDirsFirst) { + if (ContentDisplaySettings::directorySizeCount() || m_sortDirsFirst) { newGroupValue = i18nc("@title:group Size", "Folders"); } else { fileSize = m_itemData.at(i)->values.value("size").toULongLong(); diff --git a/src/kitemviews/kfileitemmodel.h b/src/kitemviews/kfileitemmodel.h index afcd633b0..3c2721d8f 100644 --- a/src/kitemviews/kfileitemmodel.h +++ b/src/kitemviews/kfileitemmodel.h @@ -182,6 +182,9 @@ public: void setMimeTypeFilters(const QStringList &filters); QStringList mimeTypeFilters() const; + void setExcludeMimeTypeFilter(const QStringList &filters); + QStringList excludeMimeTypeFilter() const; + struct RoleInfo { QByteArray role; QString translation; @@ -199,6 +202,9 @@ public: */ static QList<RoleInfo> rolesInformation(); + /** set to true to hide application/x-trash files */ + void setShowTrashMime(bool show); + Q_SIGNALS: /** * Is emitted if the loading of a directory has been started. It is @@ -218,6 +224,11 @@ Q_SIGNALS: void directoryLoadingCompleted(); /** + * Is emitted when the model is being refreshed (F5 key press) + */ + void directoryRefreshing(); + + /** * Is emitted after the loading of a directory has been canceled. */ void directoryLoadingCanceled(); diff --git a/src/kitemviews/kfileitemmodelrolesupdater.cpp b/src/kitemviews/kfileitemmodelrolesupdater.cpp index 8d5656daf..09dd2eba1 100644 --- a/src/kitemviews/kfileitemmodelrolesupdater.cpp +++ b/src/kitemviews/kfileitemmodelrolesupdater.cpp @@ -20,6 +20,8 @@ #include <KPluginMetaData> #include <KSharedConfig> +#include "dolphin_contentdisplaysettings.h" + #if HAVE_BALOO #include "private/kbaloorolesprovider.h" #include <Baloo/File> @@ -72,7 +74,6 @@ KFileItemModelRolesUpdater::KFileItemModelRolesUpdater(KFileItemModel *model, QO , m_resolvableRoles() , m_enabledPlugins() , m_localFileSizePreviewLimit(0) - , m_scanDirectories(true) , m_pendingSortRoleItems() , m_pendingIndexes() , m_pendingPreviewItems() @@ -321,16 +322,6 @@ qlonglong KFileItemModelRolesUpdater::localFileSizePreviewLimit() const return m_localFileSizePreviewLimit; } -void KFileItemModelRolesUpdater::setScanDirectories(bool enabled) -{ - m_scanDirectories = enabled; -} - -bool KFileItemModelRolesUpdater::scanDirectories() const -{ - return m_scanDirectories; -} - void KFileItemModelRolesUpdater::setHoverSequenceState(const QUrl &itemUrl, int seqIdx) { const KFileItem item = m_model->fileItem(itemUrl); @@ -423,8 +414,7 @@ void KFileItemModelRolesUpdater::slotItemsRemoved(const KItemRangeList &itemRang m_hoverSequenceLoadedItems.clear(); killPreviewJob(); - - if (m_scanDirectories) { + if (!m_model->showDirectoriesOnly()) { m_directoryContentsCounter->stopWorker(); } } else { @@ -856,10 +846,10 @@ void KFileItemModelRolesUpdater::applyChangedBalooRolesForItem(const KFileItem & #endif } -void KFileItemModelRolesUpdater::slotDirectoryContentsCountReceived(const QString &path, int count, long size) +void KFileItemModelRolesUpdater::slotDirectoryContentsCountReceived(const QString &path, int count, long long size) { - const bool getSizeRole = m_roles.contains("size"); const bool getIsExpandableRole = m_roles.contains("isExpandable"); + const bool getSizeRole = m_roles.contains("size"); if (getSizeRole || getIsExpandableRole) { const int index = m_model->index(QUrl::fromLocalFile(path)); @@ -1278,18 +1268,71 @@ bool KFileItemModelRolesUpdater::applyResolvedRoles(int index, ResolveHint hint) void KFileItemModelRolesUpdater::startDirectorySizeCounting(const KFileItem &item, int index) { - if (item.isSlow()) { + if (ContentDisplaySettings::directorySizeCount() || item.isSlow() || !item.isLocalFile()) { + // fastpath no recursion necessary + + auto data = m_model->data(index); + if (data.value("size") == -2) { + // means job already started + return; + } + + auto url = item.url(); + if (!item.localPath().isEmpty()) { + // optimization for desktop:/, trash:/ + url = QUrl::fromLocalFile(item.localPath()); + } + + data.insert("isExpandable", false); + data.insert("count", 0); + data.insert("size", -2); // invalid size, -1 means size unknown + + disconnect(m_model, &KFileItemModel::itemsChanged, this, &KFileItemModelRolesUpdater::slotItemsChanged); + m_model->setData(index, data); + connect(m_model, &KFileItemModel::itemsChanged, this, &KFileItemModelRolesUpdater::slotItemsChanged); + + auto listJob = KIO::listDir(url); + QObject::connect(listJob, &KIO::ListJob::entries, this, [this, index](const KJob * /*job*/, const KIO::UDSEntryList &list) { + auto data = m_model->data(index); + int origCount = data.value("count").toInt(); + int entryCount = origCount; + + for (const KIO::UDSEntry &entry : list) { + const auto name = entry.stringValue(KIO::UDSEntry::UDS_NAME); + + if (name == QStringLiteral("..") || name == QStringLiteral(".")) { + continue; + } + if (!m_model->showHiddenFiles() && name.startsWith(QLatin1Char('.'))) { + continue; + } + if (m_model->showDirectoriesOnly() && !entry.isDir()) { + continue; + } + ++entryCount; + } + + // count has changed + if (origCount < entryCount) { + QHash<QByteArray, QVariant> data; + data.insert("isExpandable", entryCount > 0); + data.insert("count", entryCount); + + disconnect(m_model, &KFileItemModel::itemsChanged, this, &KFileItemModelRolesUpdater::slotItemsChanged); + m_model->setData(index, data); + connect(m_model, &KFileItemModel::itemsChanged, this, &KFileItemModelRolesUpdater::slotItemsChanged); + } + }); return; } // Tell m_directoryContentsCounter that we want to count the items // inside the directory. The result will be received in slotDirectoryContentsCountReceived. - if (m_scanDirectories && item.isLocalFile()) { - const QString path = item.localPath(); - const auto priority = index >= m_firstVisibleIndex && index <= m_lastVisibleIndex ? KDirectoryContentsCounter::PathCountPriority::High - : KDirectoryContentsCounter::PathCountPriority::Normal; - m_directoryContentsCounter->scanDirectory(path, priority); - } + const QString path = item.localPath(); + const auto priority = index >= m_firstVisibleIndex && index <= m_lastVisibleIndex ? KDirectoryContentsCounter::PathCountPriority::High + : KDirectoryContentsCounter::PathCountPriority::Normal; + + m_directoryContentsCounter->scanDirectory(path, priority); } QHash<QByteArray, QVariant> KFileItemModelRolesUpdater::rolesData(const KFileItem &item, int index) @@ -1304,6 +1347,7 @@ QHash<QByteArray, QVariant> KFileItemModelRolesUpdater::rolesData(const KFileIte } if (m_roles.contains("extension")) { + // TODO KF6 use KFileItem::suffix 464722 data.insert("extension", QFileInfo(item.name()).suffix()); } diff --git a/src/kitemviews/kfileitemmodelrolesupdater.h b/src/kitemviews/kfileitemmodelrolesupdater.h index be6f23193..78fa757f8 100644 --- a/src/kitemviews/kfileitemmodelrolesupdater.h +++ b/src/kitemviews/kfileitemmodelrolesupdater.h @@ -260,7 +260,7 @@ private Q_SLOTS: void applyChangedBalooRoles(const QString &file); void applyChangedBalooRolesForItem(const KFileItem &file); - void slotDirectoryContentsCountReceived(const QString &path, int count, long size); + void slotDirectoryContentsCountReceived(const QString &path, int count, long long size); private: /** @@ -334,6 +334,9 @@ private: void trimHoverSequenceLoadedItems(); private: + /** + * enqueue directory size counting for KFileItem item at index + */ void startDirectorySizeCounting(const KFileItem &item, int index); enum State { Idle, Paused, ResolvingSortRole, ResolvingAllRoles, PreviewJobRunning }; @@ -370,7 +373,6 @@ private: QSet<QByteArray> m_resolvableRoles; QStringList m_enabledPlugins; qulonglong m_localFileSizePreviewLimit; - bool m_scanDirectories; // Items for which the sort role still has to be determined. QSet<KFileItem> m_pendingSortRoleItems; diff --git a/src/kitemviews/kitemlistcontroller.cpp b/src/kitemviews/kitemlistcontroller.cpp index 2e7d2f057..74a631d8d 100644 --- a/src/kitemviews/kitemlistcontroller.cpp +++ b/src/kitemviews/kitemlistcontroller.cpp @@ -237,21 +237,34 @@ bool KItemListController::keyPressEvent(QKeyEvent *event) { int index = m_selectionManager->currentItem(); int key = event->key(); + const bool shiftPressed = event->modifiers() & Qt::ShiftModifier; // Handle the expanding/collapsing of items - if (m_view->supportsItemExpanding() && m_model->isExpandable(index)) { - if (key == Qt::Key_Right) { - if (m_model->setExpanded(index, true)) { - return true; + // expand / collapse all selected directories + if (m_view->supportsItemExpanding() && m_model->isExpandable(index) && (key == Qt::Key_Right || key == Qt::Key_Left)) { + const bool expandOrCollapse = key == Qt::Key_Right ? true : false; + bool shouldReturn = m_model->setExpanded(index, expandOrCollapse); + + // edit in reverse to preserve index of the first handled items + const auto selectedItems = m_selectionManager->selectedItems(); + for (auto it = selectedItems.rbegin(); it != selectedItems.rend(); ++it) { + shouldReturn |= m_model->setExpanded(*it, expandOrCollapse); + if (!shiftPressed) { + m_selectionManager->setSelected(*it); } - } else if (key == Qt::Key_Left) { - if (m_model->setExpanded(index, false)) { - return true; + } + if (shouldReturn) { + // update keyboard anchors + if (shiftPressed) { + m_keyboardAnchorIndex = selectedItems.count() > 0 ? qMin(index, selectedItems.last()) : index; + m_keyboardAnchorPos = keyboardAnchorPos(m_keyboardAnchorIndex); } + + event->ignore(); + return true; } } - const bool shiftPressed = event->modifiers() & Qt::ShiftModifier; const bool controlPressed = event->modifiers() & Qt::ControlModifier; const bool shiftOrControlPressed = shiftPressed || controlPressed; const bool navigationPressed = key == Qt::Key_Home || key == Qt::Key_End || key == Qt::Key_PageUp || key == Qt::Key_PageDown || key == Qt::Key_Up @@ -327,11 +340,17 @@ bool KItemListController::keyPressEvent(QKeyEvent *event) case Qt::Key_Up: updateKeyboardAnchor(); + if (shiftPressed && !m_selectionManager->isAnchoredSelectionActive() && m_selectionManager->isSelected(index)) { + m_selectionManager->beginAnchoredSelection(index); + } index = previousRowIndex(index); break; case Qt::Key_Down: updateKeyboardAnchor(); + if (shiftPressed && !m_selectionManager->isAnchoredSelectionActive() && m_selectionManager->isSelected(index)) { + m_selectionManager->beginAnchoredSelection(index); + } index = nextRowIndex(index); break; diff --git a/src/kitemviews/kitemlistwidget.cpp b/src/kitemviews/kitemlistwidget.cpp index 1d6b9641a..2c8ef70a5 100644 --- a/src/kitemviews/kitemlistwidget.cpp +++ b/src/kitemviews/kitemlistwidget.cpp @@ -40,6 +40,7 @@ KItemListWidget::KItemListWidget(KItemListWidgetInformant *informant, QGraphicsI , m_data() , m_visibleRoles() , m_columnWidths() + , m_sidePadding(0) , m_styleOption() , m_siblingsInfo() , m_hoverOpacity(0) @@ -49,7 +50,6 @@ KItemListWidget::KItemListWidget(KItemListWidgetInformant *informant, QGraphicsI , m_selectionToggle(nullptr) , m_editedRole() , m_iconSize(-1) - , m_sidePadding(0) { connect(&m_hoverSequenceTimer, &QTimer::timeout, this, &KItemListWidget::slotHoverSequenceTimerTimeout); } diff --git a/src/kitemviews/kitemset.h b/src/kitemviews/kitemset.h index fd73c0e02..b8ab6864d 100644 --- a/src/kitemviews/kitemset.h +++ b/src/kitemviews/kitemset.h @@ -50,7 +50,7 @@ public: class iterator { - iterator(const KItemRangeList::iterator &rangeIt, int offset) + iterator(const KItemRangeList::iterator &rangeIt, int offset = 0) : m_rangeIt(rangeIt) , m_offset(offset) { @@ -135,7 +135,7 @@ public: class const_iterator { - const_iterator(KItemRangeList::const_iterator rangeIt, int offset) + const_iterator(KItemRangeList::const_iterator rangeIt, int offset = 0) : m_rangeIt(rangeIt) , m_offset(offset) { @@ -223,6 +223,70 @@ public: friend class KItemSet; }; + class const_reverse_iterator + { + public: + const_reverse_iterator(KItemSet::const_iterator rangeIt) + : m_current(rangeIt) + { + } + + const_reverse_iterator(const KItemSet::const_reverse_iterator &other) + : m_current(other.base()) + { + } + + int operator*() const + { + // analog to std::prev + auto t = const_iterator(m_current); + --t; + return *t; + } + + inline bool operator==(const const_reverse_iterator &other) const + { + return m_current == other.m_current; + } + + bool operator!=(const const_reverse_iterator &other) const + { + return !(*this == other); + } + + const_reverse_iterator &operator++() + { + --m_current; + return *this; + } + const_reverse_iterator operator++(int) + { + auto tmp = *this; + ++(*this); + return tmp; + } + + const_reverse_iterator &operator--() + { + ++m_current; + return *this; + } + const_reverse_iterator operator--(int) + { + auto tmp = *this; + --(*this); + return tmp; + } + + KItemSet::const_iterator base() const + { + return m_current; + } + + private: + KItemSet::const_iterator m_current; + }; + iterator begin(); const_iterator begin() const; const_iterator constBegin() const; @@ -230,6 +294,9 @@ public: const_iterator end() const; const_iterator constEnd() const; + const_reverse_iterator rend() const; + const_reverse_iterator rbegin() const; + int first() const; int last() const; @@ -366,32 +433,32 @@ inline bool KItemSet::remove(int i) inline KItemSet::iterator KItemSet::begin() { - return iterator(m_itemRanges.begin(), 0); + return iterator(m_itemRanges.begin()); } inline KItemSet::const_iterator KItemSet::begin() const { - return const_iterator(m_itemRanges.begin(), 0); + return const_iterator(m_itemRanges.begin()); } inline KItemSet::const_iterator KItemSet::constBegin() const { - return const_iterator(m_itemRanges.constBegin(), 0); + return const_iterator(m_itemRanges.constBegin()); } inline KItemSet::iterator KItemSet::end() { - return iterator(m_itemRanges.end(), 0); + return iterator(m_itemRanges.end()); } inline KItemSet::const_iterator KItemSet::end() const { - return const_iterator(m_itemRanges.end(), 0); + return const_iterator(m_itemRanges.end()); } inline KItemSet::const_iterator KItemSet::constEnd() const { - return const_iterator(m_itemRanges.constEnd(), 0); + return const_iterator(m_itemRanges.constEnd()); } inline int KItemSet::first() const @@ -405,6 +472,16 @@ inline int KItemSet::last() const return lastRange.index + lastRange.count - 1; } +inline KItemSet::const_reverse_iterator KItemSet::rend() const +{ + return KItemSet::const_reverse_iterator(constBegin()); +} + +inline KItemSet::const_reverse_iterator KItemSet::rbegin() const +{ + return KItemSet::const_reverse_iterator(constEnd()); +} + inline KItemSet &KItemSet::operator<<(int i) { insert(i); diff --git a/src/kitemviews/private/kdirectorycontentscounter.cpp b/src/kitemviews/private/kdirectorycontentscounter.cpp index 37e852ab9..648b20b6f 100644 --- a/src/kitemviews/private/kdirectorycontentscounter.cpp +++ b/src/kitemviews/private/kdirectorycontentscounter.cpp @@ -6,6 +6,7 @@ */ #include "kdirectorycontentscounter.h" +#include "dolphin_contentdisplaysettings.h" #include "kitemviews/kfileitemmodel.h" #include <KDirWatch> @@ -16,36 +17,102 @@ namespace { + +class LocalCache +{ +public: + struct cacheData { + int count = 0; + long long size = 0; + ushort refCount = 0; + qint64 timestamp = 0; + + inline operator bool() const + { + return timestamp != 0 && count != -1; + } + }; + + LocalCache() + : m_cache() + { + } + + cacheData insert(const QString &key, cacheData data, bool inserted) + { + data.timestamp = QDateTime::currentMSecsSinceEpoch(); + if (inserted) { + data.refCount += 1; + } + m_cache.insert(key, data); + return data; + } + + cacheData value(const QString &key) const + { + return m_cache.value(key); + } + + void unRefAll(const QSet<QString> &keys) + { + for (const auto &key : keys) { + auto entry = m_cache[key]; + entry.refCount -= 1; + if (entry.refCount == 0) { + m_cache.remove(key); + } + } + } + + void removeAll(const QSet<QString> &keys) + { + for (const auto &key : keys) { + m_cache.remove(key); + } + } + +private: + QHash<QString, cacheData> m_cache; +}; + /// cache of directory counting result -static QHash<QString, QPair<int, long>> *s_cache; +static LocalCache *s_cache; +static QThread *s_workerThread; +static KDirectoryContentsCounterWorker *s_worker; } KDirectoryContentsCounter::KDirectoryContentsCounter(KFileItemModel *model, QObject *parent) : QObject(parent) , m_model(model) + , m_priorityQueue() , m_queue() - , m_worker(nullptr) , m_workerIsBusy(false) , m_dirWatcher(nullptr) , m_watchedDirs() { - connect(m_model, &KFileItemModel::itemsRemoved, this, &KDirectoryContentsCounter::slotItemsRemoved); + if (s_cache == nullptr) { + s_cache = new LocalCache(); + } - if (!m_workerThread) { - m_workerThread = new QThread(); - m_workerThread->start(); + if (!s_workerThread) { + s_workerThread = new QThread(); + s_workerThread->setObjectName(QStringLiteral("KDirectoryContentsCounterThread")); + s_workerThread->start(); } - if (s_cache == nullptr) { - s_cache = new QHash<QString, QPair<int, long>>(); + if (!s_worker) { + s_worker = new KDirectoryContentsCounterWorker(); + s_worker->moveToThread(s_workerThread); } - m_worker = new KDirectoryContentsCounterWorker(); - m_worker->moveToThread(m_workerThread); + connect(m_model, &KFileItemModel::itemsRemoved, this, &KDirectoryContentsCounter::slotItemsRemoved); + connect(m_model, &KFileItemModel::directoryRefreshing, this, &KDirectoryContentsCounter::slotDirectoryRefreshing); + + connect(this, &KDirectoryContentsCounter::requestDirectoryContentsCount, s_worker, &KDirectoryContentsCounterWorker::countDirectoryContents); - connect(this, &KDirectoryContentsCounter::requestDirectoryContentsCount, m_worker, &KDirectoryContentsCounterWorker::countDirectoryContents); - connect(this, &KDirectoryContentsCounter::stop, m_worker, &KDirectoryContentsCounterWorker::stop); - connect(m_worker, &KDirectoryContentsCounterWorker::result, this, &KDirectoryContentsCounter::slotResult); + connect(s_worker, &KDirectoryContentsCounterWorker::result, this, &KDirectoryContentsCounter::slotResult); + connect(s_worker, &KDirectoryContentsCounterWorker::intermediateResult, this, &KDirectoryContentsCounter::result); + connect(s_worker, &KDirectoryContentsCounterWorker::finished, this, &KDirectoryContentsCounter::scheduleNext); m_dirWatcher = new KDirWatch(this); connect(m_dirWatcher, &KDirWatch::dirty, this, &KDirectoryContentsCounter::slotDirWatchDirty); @@ -53,60 +120,20 @@ KDirectoryContentsCounter::KDirectoryContentsCounter(KFileItemModel *model, QObj KDirectoryContentsCounter::~KDirectoryContentsCounter() { - if (m_workerThread->isRunning()) { - // The worker thread will continue running. It could even be running - // a method of m_worker at the moment, so we delete it using - // deleteLater() to prevent a crash. - m_worker->deleteLater(); - } else { - // There are no remaining workers -> stop the worker thread. - m_workerThread->quit(); - m_workerThread->wait(); - delete m_workerThread; - m_workerThread = nullptr; - - // The worker thread has finished running now, so it's safe to delete - // m_worker. deleteLater() would not work at all because the event loop - // which would deliver the event to m_worker is not running any more. - delete m_worker; - } + s_cache->unRefAll(m_watchedDirs); } -void KDirectoryContentsCounter::slotResult(const QString &path, int count, long size) +void KDirectoryContentsCounter::slotResult(const QString &path, int count, long long size) { - m_workerIsBusy = false; - - const QFileInfo info = QFileInfo(path); - const QString resolvedPath = info.canonicalFilePath(); - - if (!m_dirWatcher->contains(resolvedPath)) { + const auto fileInfo = QFileInfo(path); + const QString resolvedPath = fileInfo.canonicalFilePath(); + if (fileInfo.isReadable() && !m_watchedDirs.contains(resolvedPath)) { m_dirWatcher->addDir(resolvedPath); - m_watchedDirs.insert(resolvedPath); } + bool inserted = m_watchedDirs.insert(resolvedPath) == m_watchedDirs.end(); - if (!m_priorityQueue.empty()) { - const QString firstPath = m_priorityQueue.front(); - m_priorityQueue.pop_front(); - scanDirectory(firstPath, PathCountPriority::High); - } else if (!m_queue.empty()) { - const QString firstPath = m_queue.front(); - m_queue.pop_front(); - scanDirectory(firstPath, PathCountPriority::Normal); - } - - if (s_cache->contains(resolvedPath)) { - const auto pair = s_cache->value(resolvedPath); - if (pair.first == count && pair.second == size) { - // no change no need to send another result event - return; - } - } - - if (info.dir().path() == m_model->rootItem().url().path()) { - // update cache or overwrite value - // when path is a direct children of the current model root - s_cache->insert(resolvedPath, QPair<int, long>(count, size)); - } + // update cache or overwrite value + s_cache->insert(resolvedPath, {count, size, true}, inserted); // sends the results Q_EMIT result(path, count, size); @@ -131,6 +158,11 @@ void KDirectoryContentsCounter::slotItemsRemoved() { const bool allItemsRemoved = (m_model->count() == 0); + if (allItemsRemoved) { + s_cache->removeAll(m_watchedDirs); + stopWorker(); + } + if (!m_watchedDirs.isEmpty()) { // Don't let KDirWatch watch for removed items if (allItemsRemoved) { @@ -138,7 +170,6 @@ void KDirectoryContentsCounter::slotItemsRemoved() m_dirWatcher->removeDir(path); } m_watchedDirs.clear(); - m_queue.clear(); } else { QMutableSetIterator<QString> it(m_watchedDirs); while (it.hasNext()) { @@ -152,46 +183,102 @@ void KDirectoryContentsCounter::slotItemsRemoved() } } -void KDirectoryContentsCounter::scanDirectory(const QString &path, PathCountPriority priority) +void KDirectoryContentsCounter::slotDirectoryRefreshing() +{ + s_cache->removeAll(m_watchedDirs); +} + +void KDirectoryContentsCounter::scheduleNext() { - const QString resolvedPath = QFileInfo(path).canonicalFilePath(); - const bool alreadyInCache = s_cache->contains(resolvedPath); - if (alreadyInCache) { + if (!m_priorityQueue.empty()) { + m_currentPath = m_priorityQueue.front(); + m_priorityQueue.pop_front(); + } else if (!m_queue.empty()) { + m_currentPath = m_queue.front(); + m_queue.pop_front(); + } else { + m_currentPath.clear(); + m_workerIsBusy = false; + return; + } + + const auto fileInfo = QFileInfo(m_currentPath); + const QString resolvedPath = fileInfo.canonicalFilePath(); + const auto pair = s_cache->value(resolvedPath); + if (pair) { // fast path when in cache // will be updated later if result has changed - const auto pair = s_cache->value(resolvedPath); - Q_EMIT result(path, pair.first, pair.second); + Q_EMIT result(m_currentPath, pair.count, pair.size); } - if (m_workerIsBusy) { - // only enqueue path not yet in queue - if (std::find(m_queue.begin(), m_queue.end(), path) == m_queue.end() - && std::find(m_priorityQueue.begin(), m_priorityQueue.end(), path) == m_priorityQueue.end()) { - if (priority == PathCountPriority::Normal) { - if (alreadyInCache) { - // if we already knew the dir size, it gets lower priority - m_queue.push_back(path); - } else { - m_queue.push_front(path); - } - } else { - // append to priority queue - m_priorityQueue.push_back(path); - } - } - } else { - KDirectoryContentsCounterWorker::Options options; + // if scanned fully recently, skip rescan + if (pair && pair.timestamp >= fileInfo.fileTime(QFile::FileModificationTime).toMSecsSinceEpoch()) { + scheduleNext(); + return; + } + + KDirectoryContentsCounterWorker::Options options; + + if (m_model->showHiddenFiles()) { + options |= KDirectoryContentsCounterWorker::CountHiddenFiles; + } + + m_workerIsBusy = true; + Q_EMIT requestDirectoryContentsCount(m_currentPath, options, ContentDisplaySettings::recursiveDirectorySizeLimit()); +} - if (m_model->showHiddenFiles()) { - options |= KDirectoryContentsCounterWorker::CountHiddenFiles; +void KDirectoryContentsCounter::enqueuePathScanning(const QString &path, bool alreadyInCache, PathCountPriority priority) +{ + // ensure to update the entry in the queue + auto it = std::find(m_queue.begin(), m_queue.end(), path); + if (it != m_queue.end()) { + m_queue.erase(it); + } else { + it = std::find(m_priorityQueue.begin(), m_priorityQueue.end(), path); + if (it != m_priorityQueue.end()) { + m_priorityQueue.erase(it); } + } - if (m_model->showDirectoriesOnly()) { - options |= KDirectoryContentsCounterWorker::CountDirectoriesOnly; + if (priority == PathCountPriority::Normal) { + if (alreadyInCache) { + // we already knew the dir size + // otherwise it gets lower priority + m_queue.push_back(path); + } else { + m_queue.push_front(path); } + } else { + // append to priority queue + m_priorityQueue.push_front(path); + } +} - Q_EMIT requestDirectoryContentsCount(path, options); - m_workerIsBusy = true; +void KDirectoryContentsCounter::scanDirectory(const QString &path, PathCountPriority priority) +{ + if (m_workerIsBusy && m_currentPath == path) { + // already listing + return; + } + + const auto fileInfo = QFileInfo(path); + const QString resolvedPath = fileInfo.canonicalFilePath(); + const auto pair = s_cache->value(resolvedPath); + if (pair) { + // fast path when in cache + // will be updated later if result has changed + Q_EMIT result(path, pair.count, pair.size); + } + + // if scanned fully recently, skip rescan + if (pair && pair.timestamp >= fileInfo.fileTime(QFile::FileModificationTime).toMSecsSinceEpoch()) { + return; + } + + enqueuePathScanning(path, pair, priority); + + if (!m_workerIsBusy && !s_worker->stopping()) { + scheduleNext(); } } @@ -199,7 +286,9 @@ void KDirectoryContentsCounter::stopWorker() { m_queue.clear(); m_priorityQueue.clear(); - Q_EMIT stop(); -} -QThread *KDirectoryContentsCounter::m_workerThread = nullptr; + if (m_workerIsBusy && m_currentPath == s_worker->scannedPath()) { + s_worker->stop(); + } + m_currentPath.clear(); +} diff --git a/src/kitemviews/private/kdirectorycontentscounter.h b/src/kitemviews/private/kdirectorycontentscounter.h index 9a5e4a86b..0da3ccd7d 100644 --- a/src/kitemviews/private/kdirectorycontentscounter.h +++ b/src/kitemviews/private/kdirectorycontentscounter.h @@ -47,34 +47,34 @@ public: Q_SIGNALS: /** * Signals that the directory \a path contains \a count items of size \a - * Size calculation depends on parameter DetailsModeSettings::recursiveDirectorySizeLimit + * Size calculation depends on parameter ContentDisplaySettings::recursiveDirectorySizeLimit */ - void result(const QString &path, int count, long size); + void result(const QString &path, int count, long long size); - void requestDirectoryContentsCount(const QString &path, KDirectoryContentsCounterWorker::Options options); - - void stop(); + void requestDirectoryContentsCount(const QString &path, KDirectoryContentsCounterWorker::Options options, int maxRecursiveLevel); private Q_SLOTS: - void slotResult(const QString &path, int count, long size); + void slotResult(const QString &path, int count, long long size); void slotDirWatchDirty(const QString &path); void slotItemsRemoved(); + void slotDirectoryRefreshing(); + void scheduleNext(); private: + void enqueuePathScanning(const QString &path, bool alreadyInCache, PathCountPriority priority); + KFileItemModel *m_model; // Used as FIFO queues. std::list<QString> m_priorityQueue; std::list<QString> m_queue; - static QThread *m_workerThread; - - KDirectoryContentsCounterWorker *m_worker; bool m_workerIsBusy; KDirWatch *m_dirWatcher; QSet<QString> m_watchedDirs; // Required as sadly KDirWatch does not offer a getter method // to get all watched directories. + QString m_currentPath; }; #endif diff --git a/src/kitemviews/private/kdirectorycontentscounterworker.cpp b/src/kitemviews/private/kdirectorycontentscounterworker.cpp index 2227ebbc2..eb456da25 100644 --- a/src/kitemviews/private/kdirectorycontentscounterworker.cpp +++ b/src/kitemviews/private/kdirectorycontentscounterworker.cpp @@ -7,16 +7,16 @@ #include "kdirectorycontentscounterworker.h" -// Required includes for subItemsCount(): +// Required includes for countDirectoryContents(): #ifdef Q_OS_WIN #include <QDir> #else -#include <QFile> -#include <qplatformdefs.h> +#include <QElapsedTimer> +#include <fts.h> +#include <sys/stat.h> +#include <sys/types.h> #endif -#include "dolphin_detailsmodesettings.h" - KDirectoryContentsCounterWorker::KDirectoryContentsCounterWorker(QObject *parent) : QObject(parent) { @@ -24,100 +24,135 @@ KDirectoryContentsCounterWorker::KDirectoryContentsCounterWorker(QObject *parent } #ifndef Q_OS_WIN -KDirectoryContentsCounterWorker::CountResult -KDirectoryContentsCounterWorker::walkDir(const QString &dirPath, const bool countHiddenFiles, const bool countDirectoriesOnly, const uint allowedRecursiveLevel) +void KDirectoryContentsCounterWorker::walkDir(const QString &dirPath, bool countHiddenFiles, uint allowedRecursiveLevel) { - int count = -1; - long size = -1; - auto dir = QT_OPENDIR(QFile::encodeName(dirPath)); - if (dir) { - count = 0; - size = 0; - QT_DIRENT *dirEntry; - QT_STATBUF buf; + QByteArray text = dirPath.toLocal8Bit(); + char *rootPath = new char[text.size() + 1]; + ::strncpy(rootPath, text.constData(), text.size() + 1); + char *path[2]{rootPath, nullptr}; + + // follow symlink only for root dir + auto tree = ::fts_open(path, FTS_COMFOLLOW | FTS_PHYSICAL | FTS_XDEV, nullptr); + if (!tree) { + delete[] rootPath; + return; + } + + FTSENT *node; + long long totalSize = -1; + int totalCount = -1; + QElapsedTimer timer; + timer.start(); + + while ((node = fts_read(tree)) && !m_stopping) { + auto info = node->fts_info; + + if (info == FTS_DC) { + // ignore directories clausing cycles + continue; + } + if (info == FTS_DNR) { + // ignore directories that can’t be read + continue; + } + if (info == FTS_ERR) { + // ignore directories causing errors + fts_set(tree, node, FTS_SKIP); + continue; + } + if (info == FTS_DP) { + // ignore end traversal of dir + continue; + } - while (!m_stopping && (dirEntry = QT_READDIR(dir))) { - if (dirEntry->d_name[0] == '.') { - if (dirEntry->d_name[1] == '\0' || !countHiddenFiles) { - // Skip "." or hidden files - continue; - } - if (dirEntry->d_name[1] == '.' && dirEntry->d_name[2] == '\0') { - // Skip ".." - continue; - } + if (!countHiddenFiles && node->fts_name[0] == '.' && strncmp(".git", node->fts_name, 4) != 0) { + // skip hidden files, except .git dirs + if (info == FTS_D) { + fts_set(tree, node, FTS_SKIP); } + continue; + } - // If only directories are counted, consider an unknown file type and links also - // as directory instead of trying to do an expensive stat() - // (see bugs 292642 and 299997). - const bool countEntry = !countDirectoriesOnly || dirEntry->d_type == DT_DIR || dirEntry->d_type == DT_LNK || dirEntry->d_type == DT_UNKNOWN; - if (countEntry) { - ++count; + if (info == FTS_F) { + // only count files that are physical (aka skip /proc/kcore...) + // naive size counting not taking into account effective disk space used (aka size/block_size * block_size) + // skip directory size (usually a 4KB block) + if (node->fts_statp->st_blocks > 0) { + totalSize += node->fts_statp->st_size; } + } - if (allowedRecursiveLevel > 0) { - QString nameBuf = QStringLiteral("%1/%2").arg(dirPath, dirEntry->d_name); + if (info == FTS_D) { + if (node->fts_level == 0) { + // first read was sucessful, we can init counters + totalSize = 0; + totalCount = 0; + } - if (dirEntry->d_type == DT_REG) { - if (QT_STAT(nameBuf.toLocal8Bit(), &buf) == 0) { - size += buf.st_size; - } - } - if (!m_stopping && dirEntry->d_type == DT_DIR) { - // recursion for dirs - auto subdirResult = walkDir(nameBuf, countHiddenFiles, countDirectoriesOnly, allowedRecursiveLevel - 1); - if (subdirResult.size > 0) { - size += subdirResult.size; - } - } + if (node->fts_level > (int)allowedRecursiveLevel) { + // skip too deep nodes + fts_set(tree, node, FTS_SKIP); + continue; } } - QT_CLOSEDIR(dir); + // count first level elements + if (node->fts_level == 1) { + ++totalCount; + } + + // delay intermediate results + if (timer.hasExpired(200) || node->fts_level == 0) { + Q_EMIT intermediateResult(dirPath, totalCount, totalSize); + timer.restart(); + } + } + + delete[] rootPath; + fts_close(tree); + if (errno != 0) { + return; + } + + if (!m_stopping) { + Q_EMIT result(dirPath, totalCount, totalSize); } - return KDirectoryContentsCounterWorker::CountResult{count, size}; } #endif -KDirectoryContentsCounterWorker::CountResult KDirectoryContentsCounterWorker::subItemsCount(const QString &path, Options options) +void KDirectoryContentsCounterWorker::stop() +{ + m_stopping = true; +} + +bool KDirectoryContentsCounterWorker::stopping() const +{ + return m_stopping; +} + +QString KDirectoryContentsCounterWorker::scannedPath() const +{ + return m_scannedPath; +} + +void KDirectoryContentsCounterWorker::countDirectoryContents(const QString &path, Options options, int maxRecursiveLevel) { const bool countHiddenFiles = options & CountHiddenFiles; - const bool countDirectoriesOnly = options & CountDirectoriesOnly; #ifdef Q_OS_WIN QDir dir(path); - QDir::Filters filters = QDir::NoDotAndDotDot | QDir::System; + QDir::Filters filters = QDir::NoDotAndDotDot | QDir::System | QDir::AllEntries; if (countHiddenFiles) { filters |= QDir::Hidden; } - if (countDirectoriesOnly) { - filters |= QDir::Dirs; - } else { - filters |= QDir::AllEntries; - } - return {static_cast<int>(dir.entryList(filters).count()), 0}; -#else - const uint maxRecursiveLevel = DetailsModeSettings::directorySizeCount() ? 1 : DetailsModeSettings::recursiveDirectorySizeLimit(); + Q_EMIT result(path, static_cast<int>(dir.entryList(filters).count()), 0); +#else - auto res = walkDir(path, countHiddenFiles, countDirectoriesOnly, maxRecursiveLevel); + m_scannedPath = path; + walkDir(path, countHiddenFiles, maxRecursiveLevel); - return res; #endif -} - -void KDirectoryContentsCounterWorker::stop() -{ - m_stopping = true; -} -void KDirectoryContentsCounterWorker::countDirectoryContents(const QString &path, Options options) -{ m_stopping = false; - - auto res = subItemsCount(path, options); - - if (!m_stopping) { - Q_EMIT result(path, res.count, res.size); - } + Q_EMIT finished(); } diff --git a/src/kitemviews/private/kdirectorycontentscounterworker.h b/src/kitemviews/private/kdirectorycontentscounterworker.h index 5266960cd..077df9f69 100644 --- a/src/kitemviews/private/kdirectorycontentscounterworker.h +++ b/src/kitemviews/private/kdirectorycontentscounterworker.h @@ -17,32 +17,20 @@ class KDirectoryContentsCounterWorker : public QObject Q_OBJECT public: - enum Option { NoOptions = 0x0, CountHiddenFiles = 0x1, CountDirectoriesOnly = 0x2 }; + enum Option { NoOptions = 0x0, CountHiddenFiles = 0x1 }; Q_DECLARE_FLAGS(Options, Option) - struct CountResult { - /// number of elements in the directory - int count; - /// Recursive sum of the size of the directory content files and folders - /// Calculation depends on DetailsModeSettings::recursiveDirectorySizeLimit - long size; - }; - explicit KDirectoryContentsCounterWorker(QObject *parent = nullptr); - /** - * Counts the items inside the directory \a path using the options - * \a options. - * - * @return The number of items. - */ - CountResult subItemsCount(const QString &path, Options options); - + bool stopping() const; + QString scannedPath() const; Q_SIGNALS: /** * Signals that the directory \a path contains \a count items and optionally the size of its content. */ - void result(const QString &path, int count, long size); + void result(const QString &path, int count, long long size); + void intermediateResult(const QString &path, int count, long long size); + void finished(); public Q_SLOTS: /** @@ -52,16 +40,17 @@ public Q_SLOTS: // Note that the full type name KDirectoryContentsCounterWorker::Options // is needed here. Just using 'Options' is OK for the compiler, but // confuses moc. - void countDirectoryContents(const QString &path, KDirectoryContentsCounterWorker::Options options); + void countDirectoryContents(const QString &path, KDirectoryContentsCounterWorker::Options options, int maxRecursiveLevel); void stop(); private: #ifndef Q_OS_WIN - KDirectoryContentsCounterWorker::CountResult - walkDir(const QString &dirPath, const bool countHiddenFiles, const bool countDirectoriesOnly, const uint allowedRecursiveLevel); + void walkDir(const QString &dirPath, bool countHiddenFiles, uint allowedRecursiveLevel); #endif - bool m_stopping = false; + QString m_scannedPath; + + std::atomic<bool> m_stopping = false; }; Q_DECLARE_METATYPE(KDirectoryContentsCounterWorker::Options) diff --git a/src/kitemviews/private/kfileitemmodelfilter.cpp b/src/kitemviews/private/kfileitemmodelfilter.cpp index f199c314f..0f9530801 100644 --- a/src/kitemviews/private/kfileitemmodelfilter.cpp +++ b/src/kitemviews/private/kfileitemmodelfilter.cpp @@ -8,6 +8,8 @@ #include <QRegularExpression> +#include <algorithm> + #include <KFileItem> KFileItemModelFilter::KFileItemModelFilter() @@ -56,15 +58,25 @@ QStringList KFileItemModelFilter::mimeTypes() const return m_mimeTypes; } +void KFileItemModelFilter::setExcludeMimeTypes(const QStringList &types) +{ + m_excludeMimeTypes = types; +} + +QStringList KFileItemModelFilter::excludeMimeTypes() const +{ + return m_excludeMimeTypes; +} + bool KFileItemModelFilter::hasSetFilters() const { - return (!m_pattern.isEmpty() || !m_mimeTypes.isEmpty()); + return (!m_pattern.isEmpty() || !m_mimeTypes.isEmpty() || !m_excludeMimeTypes.isEmpty()); } bool KFileItemModelFilter::matches(const KFileItem &item) const { const bool hasPatternFilter = !m_pattern.isEmpty(); - const bool hasMimeTypesFilter = !m_mimeTypes.isEmpty(); + const bool hasMimeTypesFilter = !m_mimeTypes.isEmpty() || !m_excludeMimeTypes.isEmpty(); // If no filter is set, return true. if (!hasPatternFilter && !hasMimeTypesFilter) { @@ -95,10 +107,18 @@ bool KFileItemModelFilter::matchesPattern(const KFileItem &item) const bool KFileItemModelFilter::matchesType(const KFileItem &item) const { - for (const QString &mimeType : qAsConst(m_mimeTypes)) { - if (item.mimetype() == mimeType) { - return true; - } + bool excluded = std::any_of(m_excludeMimeTypes.constBegin(), m_excludeMimeTypes.constEnd(), [item](const QString &excludeMimetype) { + return item.mimetype() == excludeMimetype; + }); + if (excluded) { + return false; + } + + bool included = std::any_of(m_mimeTypes.constBegin(), m_mimeTypes.constEnd(), [item](const QString &mimeType) { + return item.mimetype() == mimeType; + }); + if (included) { + return true; } return m_mimeTypes.isEmpty(); diff --git a/src/kitemviews/private/kfileitemmodelfilter.h b/src/kitemviews/private/kfileitemmodelfilter.h index 959590da8..ce6cbeebb 100644 --- a/src/kitemviews/private/kfileitemmodelfilter.h +++ b/src/kitemviews/private/kfileitemmodelfilter.h @@ -45,6 +45,13 @@ public: QStringList mimeTypes() const; /** + * Set the list of mimetypes that are used for comparison and excluded with the + * item in KFileItemModelFilter::matchesMimeType. + */ + void setExcludeMimeTypes(const QStringList &types); + QStringList excludeMimeTypes() const; + + /** * @return True if either the pattern or mimetype filters has been set. */ bool hasSetFilters() const; @@ -73,5 +80,6 @@ private: // faster comparison in matches(). QString m_pattern; // Property set by setPattern(). QStringList m_mimeTypes; // Property set by setMimeTypes() + QStringList m_excludeMimeTypes; // Property set by setExcludeMimeTypes() }; #endif diff --git a/src/org.kde.dolphin.appdata.xml b/src/org.kde.dolphin.appdata.xml index 010f81c4c..8c546c4ad 100644 --- a/src/org.kde.dolphin.appdata.xml +++ b/src/org.kde.dolphin.appdata.xml @@ -80,7 +80,7 @@ <summary xml:lang="eu">Fitxategi-kudeatzailea</summary> <summary xml:lang="fi">Tiedostonhallinta</summary> <summary xml:lang="fr">Gestionnaire de fichiers</summary> - <summary xml:lang="gl">Xestor de ficheiros</summary> + <summary xml:lang="gl">Xestor de ficheiros.</summary> <summary xml:lang="he">מנהל קבצים</summary> <summary xml:lang="hi">फ़ाइल प्रबंधक</summary> <summary xml:lang="hu">Fájlkezelő</summary> @@ -177,7 +177,7 @@ <p xml:lang="eu">Dolphin-ek denbora aurreztuko dizuten produktibitate-ezaugarri ugari ditu. Fitxa anitzen eta ikuspegi zatituen ezaugarriek aldi berean karpeta anitz nabigatzen uzten dute, eta fitxategiak ikuspegien artean arrastatu eta jaregin ditzakezu haiek erraz mugitu edo kopiatzeko. Dolphin-en eskuin-klik laster-menuak, besteak beste, fitxategiak konprimatzeko, partekatzeko, eta bikoizteko ekintza azkarrak eskaintzen ditu. Zuk nahi dituzun ekintzak ere erants ditzakezu.</p> <p xml:lang="fi">Dolphinin monet tuottavuusominaisuudet säästävät aikaasi. Välilehdet ja jaetut näkyvät mahdollistavat useamman kansion tarkastelun yhtä aikaa, jolloin tiedostoja voi helposti kopioida tai siirtää näkymästä toiseen vetämällä ja pudottamalla. Dolphinin kontekstivalikon tarjoamin pikatoiminnoin tiedostoja voi muun muassa pakata, jakaa ja monistaa. Myös omia mukautettuja toimintoja voi luoda.</p> <p xml:lang="fr">Dolphin contient de nombreuses fonctionnalités pour la productivité vous permettant de gagner du temps. Les fonctionnalités d'onglets multiples et d'affichage scindé vous permettent de naviguer dans plusieurs dossiers en même temps. Vous pouvez aussi réaliser facilement des glisser et déposer entre les affichages pour les déplacer ou les copier. Le menu de Dolphin accessible par un clic droit fournit plusieurs actions rapides vous permettant de compresser, partager et dupliquer des fichiers, entre autres actions. Vous pouvez aussi ajouter votre propres actions personnalisées.</p> - <p xml:lang="gl">Dolphin contén una morea de funcionalidades de produtividade para aforrarlle tempo. As funcionalidades de lapelas e vista dividida permiten navegar varios cartafoles ao mesmo tempo, e pode arrastrar e soltar ficheiros entre as súas vistas facilmente para movelos ou copialos. O menú de clic dereito de Dolphin fornece moitas accións rápidas que lle permiten comprimir, compartir, e duplicar ficheiros, entre outras cousas. Tamén pode engadir as súas propias accións personalizadas.</p> + <p xml:lang="gl">Dolphin contén una morea de funcionalidades de produtividade para aforrarlle tempo. As funcionalidades de separadores e vista dividida permiten navegar varios cartafoles ao mesmo tempo, e pode arrastrar e soltar ficheiros entre as súas vistas facilmente para movelos ou copialos. O menú de clic dereito de Dolphin fornece moitas accións rápidas que lle permiten comprimir, compartir, e duplicar ficheiros, entre outras cousas. Tamén pode engadir as súas propias accións personalizadas.</p> <p xml:lang="hi">डॉल्फिन में बहुत सारी उत्पादकता विशेषताएं होती हैं जो आपका समय बचाएगी। एकाधिक टैब और विभाजित दृश्य सुविधाएं एक ही समय में एकाधिक फ़ोल्डरों को संचालित करने में काम आतीं हैं, और आप फ़ाइलों को स्थानांतरित करने या नकल करने के लिए आसानी से फ़ाइलों को खींच कर छोड़ सकते हैं। डॉल्फ़िन का दाहिना-क्लिक मेनू कई द्रुत क्रियाएं प्रदान करता है जो आपको कई अन्य चीजों के साथ-साथ फ़ाइलों को संपीड़ित, साझा और प्रतिलिपि बनाने देता है। आप अपनी खुद की तदनुकूल कार्रवाइयां भी जोड़ सकते हैं।</p> <p xml:lang="hu">A Dolphin számos olyan funkcióval bír, amelyek időt spórolnak Önnek. A lapokkal és osztott nézetekkel egyszerre navigálhat több mappában egyszerre, és könnyen húzhat át fájlokat az egyik nézetből a másikba, átmásolva vagy áthelyezve azokat. A Dolphin jobb gombos menüje rengeteg gyors műveletet biztosít, például tömörítést, megosztást vagy fájlduplikálást. Akár saját műveletekkel is bővítheti azt.</p> <p xml:lang="ia">Dolphin contine abundantia de characteristicas de productivitate que te salveguardera tempore. Le characteristicas de multiple schedas e vista dividite permitte navigar dossieres multiple al mesme tempore, e tu pote facilemente traher e deponer files inter vistas per mover o copiar los. Le menu de pulsar a dextere de Dolphines fornite con multe actiones rapide que te permitte comprimer, compartir, e duplicar files, inter multe altere cosas. Tu anqu pote adder tu actiones personalisate. </p> @@ -261,7 +261,7 @@ <p xml:lang="eu">Dolphin-ek Interneteko hodei-zerbitzu askotako eta urruneko beste makina batzuetako fitxategi eta karpetak zure mahaigainean egongo balira bezala erakuts ditzake.</p> <p xml:lang="fi">Dolphin osaa näyttää tiedostot ja kansiot monista internetin pilvipalveluista sekä etäkoneilta kuin ne olisivat välittömästi työpöydälläsi.</p> <p xml:lang="fr">Dolphin peut afficher des fichiers et des dossiers à partir de nombreux services de stockage sur Internet (Cloud) et d'ordinateurs distants, comme s'ils étaient directement sur votre ordinateur.</p> - <p xml:lang="gl">Dolphin pode mostrar ficheiros e cartafoles de moitos servizos de nube de internet e outras máquinas remotas como se estivesen no seu escritorio.</p> + <p xml:lang="gl">Dolphin pode amosar ficheiros e cartafoles de moitos servizos de nube de internet e outras máquinas remotas como se estivesen no seu escritorio.</p> <p xml:lang="hi">डॉल्फ़िन कई इंटरनेट क्लाउड सेवाओं और अन्य दूरस्थ मशीनों से फ़ाइलें और फ़ोल्डर प्रदर्शित इस प्रकार कर सकता है जैसे कि वे आपके डेस्कटॉप पर ही हैं।</p> <p xml:lang="hu">A Dolphin úgy képes fájlokat megjeleníteni számos felhőszolgáltatásból vagy távoli gépekről, mintha azok a saját számítógépén lennének.</p> <p xml:lang="ia">Dolphin pote monstrar files e dossieres ab multe servicios cloud de internet e altere machinas remote como si illos esseva exactemente sur tu scriptorio.</p> @@ -356,7 +356,7 @@ <caption xml:lang="eu">Fitxategi-kudeaketa Dolphin-ekin</caption> <caption xml:lang="fi">Tiedostonhallinta Dolphinissa</caption> <caption xml:lang="fr">Gestion de fichiers dans Dolphin</caption> - <caption xml:lang="gl">Xestión de ficheiros en Dolphin</caption> + <caption xml:lang="gl">Xestión de ficheiros en Dolphin.</caption> <caption xml:lang="he">ניהול קבצים ב־Dolphin</caption> <caption xml:lang="hi">डॉल्फिन में फ़ाइल प्रबंधन</caption> <caption xml:lang="hu">Fájlkezelés a Dolphinban</caption> @@ -408,7 +408,7 @@ <caption xml:lang="eu">Dolphinen txertatutako terminala</caption> <caption xml:lang="fi">Upotettu pääte Dolphinissa</caption> <caption xml:lang="fr">Terminal intégré dans Dolphin</caption> - <caption xml:lang="gl">Terminal incrustado en Dolphin</caption> + <caption xml:lang="gl">Terminal incrustado en Dolphin.</caption> <caption xml:lang="hi">डॉल्फिन में सन्निहित टर्मिनल</caption> <caption xml:lang="hu">Beágyazott terminál a Dolphinban</caption> <caption xml:lang="ia">Terminal incorporate in Dolphin</caption> @@ -456,7 +456,7 @@ <caption xml:lang="eu">Dolphinek zure fitxategi-kudeatzailea nahi duzun eran konfiguratzen uzten dizu</caption> <caption xml:lang="fi">Dolphinilla voit tehdä tiedostonhallintaa juuri kuten haluat</caption> <caption xml:lang="fr">Dolphin vous permet de configurer votre gestionnaire de fichiers, exactement comme vous le souhaitez.</caption> - <caption xml:lang="gl">Dolphin permítelle configurar o seu xestor de ficheiros como queira</caption> + <caption xml:lang="gl">Dolphin permítelle configurar o seu xestor de ficheiros como queira.</caption> <caption xml:lang="hi">डॉल्फ़िन आपको अपने फ़ाइल प्रबंधक को ठीक वैसे ही विन्यस्त करने देता है जैसे आप चाहते हैं </caption> <caption xml:lang="hu">A Dolphin lehetővé teszi, hogy úgy állítsa be a fájlkezelőkét, ahogyan szeretné</caption> <caption xml:lang="ia">Dolphin te permitte configurar tu gerente de file exactemente como tu lo vole</caption> @@ -493,10 +493,10 @@ <binary>dolphin</binary> </provides> <releases> + <release version="23.04.2" date="2023-06-08"/> <release version="23.04.1" date="2023-05-11"/> <release version="23.04.0" date="2023-04-20"/> <release version="22.12.3" date="2023-03-02"/> - <release version="22.12.2" date="2023-02-02"/> </releases> <content_rating type="oars-1.1"/> </component> diff --git a/src/panels/folders/folderspanel.cpp b/src/panels/folders/folderspanel.cpp index 914692000..f304654cb 100644 --- a/src/panels/folders/folderspanel.cpp +++ b/src/panels/folders/folderspanel.cpp @@ -131,7 +131,6 @@ void FoldersPanel::showEvent(QShowEvent *event) // This assures that no performance and memory overhead is given when the folders panel is not // used at all and stays invisible. KFileItemListView *view = new KFileItemListView(); - view->setScanDirectories(false); view->setWidgetCreator(new KItemListWidgetCreator<FoldersItemListWidget>()); view->setSupportsItemExpanding(true); // Set the opacity to 0 initially. The opacity will be increased after the loading of the initial tree diff --git a/src/selectionmode/actiontexthelper.cpp b/src/selectionmode/actiontexthelper.cpp index 28a247185..b54e9b004 100644 --- a/src/selectionmode/actiontexthelper.cpp +++ b/src/selectionmode/actiontexthelper.cpp @@ -1,6 +1,6 @@ /* This file is part of the KDE project - SPDX-FileCopyrightText: 2022 Felix Ernst <[email protected]> + SPDX-FileCopyrightText: 2022 Felix Ernst <[email protected]> SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL */ diff --git a/src/selectionmode/actiontexthelper.h b/src/selectionmode/actiontexthelper.h index 6ca99990f..aa6569411 100644 --- a/src/selectionmode/actiontexthelper.h +++ b/src/selectionmode/actiontexthelper.h @@ -1,6 +1,6 @@ /* This file is part of the KDE project - SPDX-FileCopyrightText: 2022 Felix Ernst <[email protected]> + SPDX-FileCopyrightText: 2022 Felix Ernst <[email protected]> SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL */ diff --git a/src/selectionmode/actionwithwidget.cpp b/src/selectionmode/actionwithwidget.cpp index 1284912b6..cc407d334 100644 --- a/src/selectionmode/actionwithwidget.cpp +++ b/src/selectionmode/actionwithwidget.cpp @@ -1,6 +1,6 @@ /* This file is part of the KDE project - SPDX-FileCopyrightText: 2022 Felix Ernst <[email protected]> + SPDX-FileCopyrightText: 2022 Felix Ernst <[email protected]> SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL */ diff --git a/src/selectionmode/actionwithwidget.h b/src/selectionmode/actionwithwidget.h index 9b83022de..e56729a6f 100644 --- a/src/selectionmode/actionwithwidget.h +++ b/src/selectionmode/actionwithwidget.h @@ -1,6 +1,6 @@ /* This file is part of the KDE project - SPDX-FileCopyrightText: 2022 Felix Ernst <[email protected]> + SPDX-FileCopyrightText: 2022 Felix Ernst <[email protected]> SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL */ diff --git a/src/selectionmode/backgroundcolorhelper.cpp b/src/selectionmode/backgroundcolorhelper.cpp index 1615c36bb..74f5bda1a 100644 --- a/src/selectionmode/backgroundcolorhelper.cpp +++ b/src/selectionmode/backgroundcolorhelper.cpp @@ -1,6 +1,6 @@ /* This file is part of the KDE project - SPDX-FileCopyrightText: 2022 Felix Ernst <[email protected]> + SPDX-FileCopyrightText: 2022 Felix Ernst <[email protected]> SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL */ diff --git a/src/selectionmode/backgroundcolorhelper.h b/src/selectionmode/backgroundcolorhelper.h index 251c5eddf..3450c8e19 100644 --- a/src/selectionmode/backgroundcolorhelper.h +++ b/src/selectionmode/backgroundcolorhelper.h @@ -1,6 +1,6 @@ /* This file is part of the KDE project - SPDX-FileCopyrightText: 2022 Felix Ernst <[email protected]> + SPDX-FileCopyrightText: 2022 Felix Ernst <[email protected]> SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL */ diff --git a/src/selectionmode/bottombar.cpp b/src/selectionmode/bottombar.cpp index f39431076..c85286cbb 100644 --- a/src/selectionmode/bottombar.cpp +++ b/src/selectionmode/bottombar.cpp @@ -1,6 +1,6 @@ /* This file is part of the KDE project - SPDX-FileCopyrightText: 2022 Felix Ernst <[email protected]> + SPDX-FileCopyrightText: 2022 Felix Ernst <[email protected]> SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL */ diff --git a/src/selectionmode/bottombar.h b/src/selectionmode/bottombar.h index 3ad361ef6..fd6eaebd9 100644 --- a/src/selectionmode/bottombar.h +++ b/src/selectionmode/bottombar.h @@ -1,6 +1,6 @@ /* This file is part of the KDE project - SPDX-FileCopyrightText: 2022 Felix Ernst <[email protected]> + SPDX-FileCopyrightText: 2022 Felix Ernst <[email protected]> SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL */ diff --git a/src/selectionmode/bottombarcontentscontainer.cpp b/src/selectionmode/bottombarcontentscontainer.cpp index 4619bcae0..730471ab0 100644 --- a/src/selectionmode/bottombarcontentscontainer.cpp +++ b/src/selectionmode/bottombarcontentscontainer.cpp @@ -1,6 +1,6 @@ /* This file is part of the KDE project - SPDX-FileCopyrightText: 2022 Felix Ernst <[email protected]> + SPDX-FileCopyrightText: 2022 Felix Ernst <[email protected]> SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL */ diff --git a/src/selectionmode/bottombarcontentscontainer.h b/src/selectionmode/bottombarcontentscontainer.h index 560220258..10e7ba5fd 100644 --- a/src/selectionmode/bottombarcontentscontainer.h +++ b/src/selectionmode/bottombarcontentscontainer.h @@ -1,6 +1,6 @@ /* This file is part of the KDE project - SPDX-FileCopyrightText: 2022 Felix Ernst <[email protected]> + SPDX-FileCopyrightText: 2022 Felix Ernst <[email protected]> SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL */ diff --git a/src/selectionmode/singleclickselectionproxystyle.h b/src/selectionmode/singleclickselectionproxystyle.h index a0c1585d5..e0bdc9df2 100644 --- a/src/selectionmode/singleclickselectionproxystyle.h +++ b/src/selectionmode/singleclickselectionproxystyle.h @@ -1,6 +1,6 @@ /* This file is part of the KDE project - SPDX-FileCopyrightText: 2022 Felix Ernst <[email protected]> + SPDX-FileCopyrightText: 2022 Felix Ernst <[email protected]> SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL */ diff --git a/src/selectionmode/topbar.cpp b/src/selectionmode/topbar.cpp index 1981be411..d58d10fb4 100644 --- a/src/selectionmode/topbar.cpp +++ b/src/selectionmode/topbar.cpp @@ -1,6 +1,6 @@ /* This file is part of the KDE project - SPDX-FileCopyrightText: 2022 Felix Ernst <[email protected]> + SPDX-FileCopyrightText: 2022 Felix Ernst <[email protected]> SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL */ diff --git a/src/selectionmode/topbar.h b/src/selectionmode/topbar.h index 820af23c6..028fc4985 100644 --- a/src/selectionmode/topbar.h +++ b/src/selectionmode/topbar.h @@ -1,6 +1,6 @@ /* This file is part of the KDE project - SPDX-FileCopyrightText: 2022 Felix Ernst <[email protected]> + SPDX-FileCopyrightText: 2022 Felix Ernst <[email protected]> SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL */ diff --git a/src/settings/dolphin_contentdisplaysettings.kcfg b/src/settings/dolphin_contentdisplaysettings.kcfg new file mode 100644 index 000000000..36895b869 --- /dev/null +++ b/src/settings/dolphin_contentdisplaysettings.kcfg @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE kcfg SYSTEM "http://www.kde.org/standards/kcfg/1.0/kcfg.dtd"> +<kcfg xmlns="http://www.kde.org/standards/kcfg/1.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://www.kde.org/standards/kcfg/1.0 + http://www.kde.org/standards/kcfg/1.0/kcfg.xsd"> + <kcfgfile name="dolphinrc"/> + <group name="ContentDisplay"> + <entry name="DirectorySizeCount" type="Bool"> + <label>Whether or not content count is used as directory size</label> + <default>true</default> + </entry> + <entry name="RecursiveDirectorySizeLimit" type="UInt"> + <label>Recursive directory size limit</label> + <default>10</default> + </entry> + <entry name="UseShortRelativeDates" type="Bool"> + <label>if true we use short relative dates, if not short dates</label> + <default>true</default> + </entry> + <entry name="UsePermissionsFormat" type="Enum"> + <label>Permissions style format</label> + <choices> + <choice name="SymbolicFormat" /> + <choice name="NumericFormat" /> + <choice name="CombinedFormat" /> + </choices> + <default>0</default> + </entry> + </group> +</kcfg> diff --git a/src/settings/dolphin_contentdisplaysettings.kcfgc b/src/settings/dolphin_contentdisplaysettings.kcfgc new file mode 100644 index 000000000..c986578b2 --- /dev/null +++ b/src/settings/dolphin_contentdisplaysettings.kcfgc @@ -0,0 +1,4 @@ +File=dolphin_contentdisplaysettings.kcfg +ClassName=ContentDisplaySettings +Singleton=yes +Mutators=true diff --git a/src/settings/dolphin_detailsmodesettings.kcfg b/src/settings/dolphin_detailsmodesettings.kcfg index 9efdebe71..98fe0efff 100644 --- a/src/settings/dolphin_detailsmodesettings.kcfg +++ b/src/settings/dolphin_detailsmodesettings.kcfg @@ -39,26 +39,5 @@ <label>Expandable folders</label> <default>true</default> </entry> - <entry name="DirectorySizeCount" type="Bool"> - <label>Whether or not content count is used as directory size</label> - <default>true</default> - </entry> - <entry name="RecursiveDirectorySizeLimit" type="UInt"> - <label>Recursive directory size limit</label> - <default>10</default> - </entry> - <entry name="UseShortRelativeDates" type="Bool"> - <label>if true we use short relative dates, if not short dates</label> - <default>true</default> - </entry> - <entry name="UsePermissionsFormat" type="Enum"> - <label>Permissions style format</label> - <choices> - <choice name="SymbolicFormat" /> - <choice name="NumericFormat" /> - <choice name="CombinedFormat" /> - </choices> - <default>0</default> - </entry> </group> </kcfg> diff --git a/src/settings/dolphin_detailsmodesettings.upd b/src/settings/dolphin_detailsmodesettings.upd index 091fee52c..da8f4b9cd 100644 --- a/src/settings/dolphin_detailsmodesettings.upd +++ b/src/settings/dolphin_detailsmodesettings.upd @@ -5,4 +5,13 @@ Version=5 Id=rename-leading-padding File=dolphinrc Group=DetailsMode -Key=LeadingPadding,SidePadding
\ No newline at end of file +Key=LeadingPadding,SidePadding + +#Rename Move content-display from detailsMode +Id=move-content-display +File=dolphinrc +Group=DetailsMode,ContentDisplay +Key=DirectorySizeCount +Key=RecursiveDirectorySizeLimit +Key=UseShortRelativeDates +Key=UsePermissionsFormat diff --git a/src/settings/viewmodes/contentdisplaytab.cpp b/src/settings/viewmodes/contentdisplaytab.cpp new file mode 100644 index 000000000..442bd3b49 --- /dev/null +++ b/src/settings/viewmodes/contentdisplaytab.cpp @@ -0,0 +1,148 @@ +/* + * SPDX-FileCopyrightText: 2023 Méven Car <[email protected]> + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "contentdisplaytab.h" +#include "dolphin_contentdisplaysettings.h" + +#include <KFormat> +#include <KLocalizedString> + +#include <QButtonGroup> +#include <QFormLayout> +#include <QHBoxLayout> +#include <QRadioButton> +#include <QSpinBox> + +ContentDisplayTab::ContentDisplayTab(QWidget *parent) + : SettingsPageBase(parent) + , m_numberOfItems(nullptr) + , m_sizeOfContents(nullptr) + , m_recursiveDirectorySizeLimit(nullptr) + , m_useRelatetiveDates(nullptr) + , m_useShortDates(nullptr) + , m_useSymbolicPermissions(nullptr) + , m_useNumericPermissions(nullptr) + , m_useCombinedPermissions(nullptr) +{ + QFormLayout *topLayout = new QFormLayout(this); + +#ifndef Q_OS_WIN + // Sorting properties + m_numberOfItems = new QRadioButton(i18nc("option:radio", "Number of items")); + m_sizeOfContents = new QRadioButton(i18nc("option:radio", "Size of contents, up to ")); + + QButtonGroup *sortingModeGroup = new QButtonGroup(this); + sortingModeGroup->addButton(m_numberOfItems); + sortingModeGroup->addButton(m_sizeOfContents); + + m_recursiveDirectorySizeLimit = new QSpinBox(); + connect(m_recursiveDirectorySizeLimit, &QSpinBox::valueChanged, this, [this](int value) { + m_recursiveDirectorySizeLimit->setSuffix(i18np(" level deep", " levels deep", value)); + }); + m_recursiveDirectorySizeLimit->setRange(1, 20); + m_recursiveDirectorySizeLimit->setSingleStep(1); + + QHBoxLayout *contentsSizeLayout = new QHBoxLayout(); + contentsSizeLayout->addWidget(m_sizeOfContents); + contentsSizeLayout->addWidget(m_recursiveDirectorySizeLimit); + + topLayout->addRow(i18nc("@title:group", "Folder size displays:"), m_numberOfItems); + topLayout->addRow(QString(), contentsSizeLayout); +#endif + + QDateTime thirtyMinutesAgo = QDateTime::currentDateTime().addSecs(-30 * 60); + QLocale local; + KFormat formatter(local); + + m_useRelatetiveDates = new QRadioButton( + i18nc("option:radio as in relative date", "Relative (e.g. '%1')", formatter.formatRelativeDateTime(thirtyMinutesAgo, QLocale::ShortFormat))); + m_useShortDates = + new QRadioButton(i18nc("option:radio as in absolute date", "Absolute (e.g. '%1')", local.toString(thirtyMinutesAgo, QLocale::ShortFormat))); + + QButtonGroup *dateFormatGroup = new QButtonGroup(this); + dateFormatGroup->addButton(m_useRelatetiveDates); + dateFormatGroup->addButton(m_useShortDates); + + topLayout->addRow(i18nc("@title:group", "Date style:"), m_useRelatetiveDates); + topLayout->addRow(QString(), m_useShortDates); + + m_useSymbolicPermissions = new QRadioButton(i18nc("option:radio as symbolic style ", "Symbolic (e.g. 'drwxr-xr-x')")); + m_useNumericPermissions = new QRadioButton(i18nc("option:radio as numeric style", "Numeric (Octal) (e.g. '755')")); + m_useCombinedPermissions = new QRadioButton(i18nc("option:radio as combined style", "Combined (e.g. 'drwxr-xr-x (755)')")); + + topLayout->addRow(i18nc("@title:group", "Permissions style:"), m_useSymbolicPermissions); + topLayout->addRow(QString(), m_useNumericPermissions); + topLayout->addRow(QString(), m_useCombinedPermissions); + + QButtonGroup *permissionsFormatGroup = new QButtonGroup(this); + permissionsFormatGroup->addButton(m_useSymbolicPermissions); + permissionsFormatGroup->addButton(m_useNumericPermissions); + permissionsFormatGroup->addButton(m_useCombinedPermissions); + +#ifndef Q_OS_WIN + connect(m_recursiveDirectorySizeLimit, &QSpinBox::valueChanged, this, &SettingsPageBase::changed); + connect(m_numberOfItems, &QRadioButton::toggled, this, &SettingsPageBase::changed); + connect(m_sizeOfContents, &QRadioButton::toggled, this, [=]() { + m_recursiveDirectorySizeLimit->setEnabled(m_sizeOfContents->isChecked()); + }); +#endif + + connect(m_useRelatetiveDates, &QRadioButton::toggled, this, &SettingsPageBase::changed); + connect(m_useShortDates, &QRadioButton::toggled, this, &SettingsPageBase::changed); + connect(m_useSymbolicPermissions, &QRadioButton::toggled, this, &SettingsPageBase::changed); + connect(m_useNumericPermissions, &QRadioButton::toggled, this, &SettingsPageBase::changed); + connect(m_useCombinedPermissions, &QRadioButton::toggled, this, &SettingsPageBase::changed); + + loadSettings(); +} + +void ContentDisplayTab::applySettings() +{ + auto settings = ContentDisplaySettings::self(); +#ifndef Q_OS_WIN + settings->setDirectorySizeCount(m_numberOfItems->isChecked()); + settings->setRecursiveDirectorySizeLimit(m_recursiveDirectorySizeLimit->value()); +#endif + + settings->setUseShortRelativeDates(m_useRelatetiveDates->isChecked()); + + if (m_useSymbolicPermissions->isChecked()) { + settings->setUsePermissionsFormat(ContentDisplaySettings::EnumUsePermissionsFormat::SymbolicFormat); + } else if (m_useNumericPermissions->isChecked()) { + settings->setUsePermissionsFormat(ContentDisplaySettings::EnumUsePermissionsFormat::NumericFormat); + } else if (m_useCombinedPermissions->isChecked()) { + settings->setUsePermissionsFormat(ContentDisplaySettings::EnumUsePermissionsFormat::CombinedFormat); + } + settings->save(); +} + +void ContentDisplayTab::loadSettings() +{ + auto settings = ContentDisplaySettings::self(); +#ifndef Q_OS_WIN + if (settings->directorySizeCount()) { + m_numberOfItems->setChecked(true); + m_recursiveDirectorySizeLimit->setEnabled(false); + } else { + m_sizeOfContents->setChecked(true); + m_recursiveDirectorySizeLimit->setEnabled(true); + } + m_recursiveDirectorySizeLimit->setValue(settings->recursiveDirectorySizeLimit()); +#endif + m_useRelatetiveDates->setChecked(settings->useShortRelativeDates()); + m_useShortDates->setChecked(!settings->useShortRelativeDates()); + m_useSymbolicPermissions->setChecked(settings->usePermissionsFormat() == ContentDisplaySettings::EnumUsePermissionsFormat::SymbolicFormat); + m_useNumericPermissions->setChecked(settings->usePermissionsFormat() == ContentDisplaySettings::EnumUsePermissionsFormat::NumericFormat); + m_useCombinedPermissions->setChecked(settings->usePermissionsFormat() == ContentDisplaySettings::EnumUsePermissionsFormat::CombinedFormat); +} + +void ContentDisplayTab::restoreDefaults() +{ + auto settings = ContentDisplaySettings::self(); + settings->useDefaults(true); + loadSettings(); + settings->useDefaults(false); +} diff --git a/src/settings/viewmodes/contentdisplaytab.h b/src/settings/viewmodes/contentdisplaytab.h new file mode 100644 index 000000000..5484bf413 --- /dev/null +++ b/src/settings/viewmodes/contentdisplaytab.h @@ -0,0 +1,39 @@ +/* + * SPDX-FileCopyrightText: 2023 Méven Car <[email protected]> + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ +#ifndef GENERALTAB_H +#define GENERALTAB_H + +#include "settings/settingspagebase.h" + +class QRadioButton; +class QSpinBox; + +class ContentDisplayTab : public SettingsPageBase +{ + Q_OBJECT + +public: + ContentDisplayTab(QWidget *parent); + +public: + // SettingsPageBase interface + void applySettings() override; + void restoreDefaults() override; + +private: + void loadSettings(); + + QRadioButton *m_numberOfItems; + QRadioButton *m_sizeOfContents; + QSpinBox *m_recursiveDirectorySizeLimit; + QRadioButton *m_useRelatetiveDates; + QRadioButton *m_useShortDates; + QRadioButton *m_useSymbolicPermissions; + QRadioButton *m_useNumericPermissions; + QRadioButton *m_useCombinedPermissions; +}; + +#endif // GENERALTAB_H diff --git a/src/settings/viewmodes/viewmodesettings.cpp b/src/settings/viewmodes/viewmodesettings.cpp index 0cfc0634d..504ec12e7 100644 --- a/src/settings/viewmodes/viewmodesettings.cpp +++ b/src/settings/viewmodes/viewmodesettings.cpp @@ -1,6 +1,6 @@ /* * SPDX-FileCopyrightText: 2011 Peter Penz <[email protected]> - * SPDX-FileCopyrightText: 2021 Felix Ernst <[email protected]> + * SPDX-FileCopyrightText: 2021 Felix Ernst <[email protected]> * * SPDX-License-Identifier: GPL-2.0-or-later */ diff --git a/src/settings/viewmodes/viewsettingspage.cpp b/src/settings/viewmodes/viewsettingspage.cpp index cb0b5ecee..33409ec25 100644 --- a/src/settings/viewmodes/viewsettingspage.cpp +++ b/src/settings/viewmodes/viewsettingspage.cpp @@ -6,7 +6,7 @@ #include "viewsettingspage.h" -#include "views/dolphinview.h" +#include "contentdisplaytab.h" #include "viewsettingstab.h" #include <KLocalizedString> @@ -21,7 +21,14 @@ ViewSettingsPage::ViewSettingsPage(QWidget *parent) QVBoxLayout *topLayout = new QVBoxLayout(this); topLayout->setContentsMargins(0, 0, 0, 0); - QTabWidget *tabWidget = new QTabWidget(this); + tabWidget = new QTabWidget(this); + + // Content Display Tab + contentDisplayTab = new ContentDisplayTab(tabWidget); + tabWidget->addTab(contentDisplayTab, + QIcon::fromTheme(QStringLiteral("view-choose")), + i18nc("@title:tab how file items columns are displayed", "Content Display")); + connect(contentDisplayTab, &SettingsPageBase::changed, this, &ViewSettingsPage::changed); // Initialize 'Icons' tab ViewSettingsTab *iconsTab = new ViewSettingsTab(ViewSettingsTab::IconsMode, tabWidget); @@ -51,6 +58,8 @@ ViewSettingsPage::~ViewSettingsPage() void ViewSettingsPage::applySettings() { + contentDisplayTab->applySettings(); + for (ViewSettingsTab *tab : qAsConst(m_tabs)) { tab->applySettings(); } @@ -58,7 +67,15 @@ void ViewSettingsPage::applySettings() void ViewSettingsPage::restoreDefaults() { + if (tabWidget->currentWidget() == contentDisplayTab) { + contentDisplayTab->restoreDefaults(); + return; + } + for (ViewSettingsTab *tab : qAsConst(m_tabs)) { - tab->restoreDefaultSettings(); + if (tabWidget->currentWidget() == tab) { + tab->restoreDefaultSettings(); + return; + } } } diff --git a/src/settings/viewmodes/viewsettingspage.h b/src/settings/viewmodes/viewsettingspage.h index 797c0b712..e52f6b2c0 100644 --- a/src/settings/viewmodes/viewsettingspage.h +++ b/src/settings/viewmodes/viewsettingspage.h @@ -10,6 +10,8 @@ class ViewSettingsTab; class QWidget; +class ContentDisplayTab; +class QTabWidget; /** * @brief Page for the 'View' settings of the Dolphin settings dialog. @@ -32,6 +34,8 @@ public: void restoreDefaults() override; private: + ContentDisplayTab *contentDisplayTab; + QTabWidget *tabWidget; QList<ViewSettingsTab *> m_tabs; }; diff --git a/src/settings/viewmodes/viewsettingstab.cpp b/src/settings/viewmodes/viewsettingstab.cpp index 1d4c5f5d5..df850e530 100644 --- a/src/settings/viewmodes/viewsettingstab.cpp +++ b/src/settings/viewmodes/viewsettingstab.cpp @@ -14,7 +14,6 @@ #include "settings/viewmodes/viewmodesettings.h" #include "views/zoomlevelinfo.h" -#include <KFormat> #include <KLocalizedString> #include <QApplication> @@ -35,12 +34,6 @@ ViewSettingsTab::ViewSettingsTab(Mode mode, QWidget *parent) , m_widthBox(nullptr) , m_maxLinesBox(nullptr) , m_expandableFolders(nullptr) - , m_recursiveDirectorySizeLimit(nullptr) - , m_useRelatetiveDates(nullptr) - , m_useShortDates(nullptr) - , m_useSymbolicPermissions(nullptr) - , m_useNumericPermissions(nullptr) - , m_useCombinedPermissions(nullptr) { QFormLayout *topLayout = new QFormLayout(this); @@ -113,61 +106,6 @@ ViewSettingsTab::ViewSettingsTab(Mode mode, QWidget *parent) topLayout->addRow(i18nc("@title:group", "Open files and folders:"), m_entireRow); // clang-format on topLayout->addRow(QString(), m_iconAndNameOnly); - -#ifndef Q_OS_WIN - // Sorting properties - m_numberOfItems = new QRadioButton(i18nc("option:radio", "Number of items")); - m_sizeOfContents = new QRadioButton(i18nc("option:radio", "Size of contents, up to ")); - - QButtonGroup *sortingModeGroup = new QButtonGroup(this); - sortingModeGroup->addButton(m_numberOfItems); - sortingModeGroup->addButton(m_sizeOfContents); - - m_recursiveDirectorySizeLimit = new QSpinBox(); - connect(m_recursiveDirectorySizeLimit, &QSpinBox::valueChanged, this, [this](int value) { - m_recursiveDirectorySizeLimit->setSuffix(i18np(" level deep", " levels deep", value)); - }); - m_recursiveDirectorySizeLimit->setRange(1, 20); - m_recursiveDirectorySizeLimit->setSingleStep(1); - - QHBoxLayout *contentsSizeLayout = new QHBoxLayout(); - contentsSizeLayout->addWidget(m_sizeOfContents); - contentsSizeLayout->addWidget(m_recursiveDirectorySizeLimit); - - topLayout->addRow(i18nc("@title:group", "Folder size displays:"), m_numberOfItems); - topLayout->addRow(QString(), contentsSizeLayout); -#endif - - QDateTime thirtyMinutesAgo = QDateTime::currentDateTime().addSecs(-30 * 60); - QLocale local; - KFormat formatter(local); - - m_useRelatetiveDates = new QRadioButton( - i18nc("option:radio as in relative date", "Relative (e.g. '%1')", formatter.formatRelativeDateTime(thirtyMinutesAgo, QLocale::ShortFormat))); - m_useShortDates = - new QRadioButton(i18nc("option:radio as in absolute date", "Absolute (e.g. '%1')", local.toString(thirtyMinutesAgo, QLocale::ShortFormat))); - - QButtonGroup *dateFormatGroup = new QButtonGroup(this); - dateFormatGroup->addButton(m_useRelatetiveDates); - dateFormatGroup->addButton(m_useShortDates); - - topLayout->addRow(i18nc("@title:group", "Date style:"), m_useRelatetiveDates); - topLayout->addRow(QString(), m_useShortDates); - - m_useSymbolicPermissions = new QRadioButton(i18nc("option:radio as symbolic style ", "Symbolic (e.g. 'drwxr-xr-x')")); - - m_useNumericPermissions = new QRadioButton(i18nc("option:radio as numeric style", "Numeric (Octal) (e.g. '755')")); - - m_useCombinedPermissions = new QRadioButton(i18nc("option:radio as combined style", "Combined (e.g. 'drwxr-xr-x (755)')")); - - topLayout->addRow(i18nc("@title:group", "Permissions style:"), m_useSymbolicPermissions); - topLayout->addRow(QString(), m_useNumericPermissions); - topLayout->addRow(QString(), m_useCombinedPermissions); - - QButtonGroup *permissionsFormatGroup = new QButtonGroup(this); - permissionsFormatGroup->addButton(m_useSymbolicPermissions); - permissionsFormatGroup->addButton(m_useNumericPermissions); - permissionsFormatGroup->addButton(m_useCombinedPermissions); break; } @@ -188,18 +126,6 @@ ViewSettingsTab::ViewSettingsTab(Mode mode, QWidget *parent) case DetailsMode: connect(m_entireRow, &QCheckBox::toggled, this, &ViewSettingsTab::changed); connect(m_expandableFolders, &QCheckBox::toggled, this, &ViewSettingsTab::changed); -#ifndef Q_OS_WIN - connect(m_recursiveDirectorySizeLimit, &QSpinBox::valueChanged, this, &ViewSettingsTab::changed); - connect(m_numberOfItems, &QRadioButton::toggled, this, &ViewSettingsTab::changed); - connect(m_sizeOfContents, &QRadioButton::toggled, this, [=]() { - m_recursiveDirectorySizeLimit->setEnabled(m_sizeOfContents->isChecked()); - }); -#endif - connect(m_useRelatetiveDates, &QRadioButton::toggled, this, &ViewSettingsTab::changed); - connect(m_useShortDates, &QRadioButton::toggled, this, &ViewSettingsTab::changed); - connect(m_useSymbolicPermissions, &QRadioButton::toggled, this, &ViewSettingsTab::changed); - connect(m_useNumericPermissions, &QRadioButton::toggled, this, &ViewSettingsTab::changed); - connect(m_useCombinedPermissions, &QRadioButton::toggled, this, &ViewSettingsTab::changed); break; default: break; @@ -212,50 +138,35 @@ ViewSettingsTab::~ViewSettingsTab() void ViewSettingsTab::applySettings() { - const QFont font = m_fontRequester->currentFont(); - const bool useSystemFont = (m_fontRequester->mode() == DolphinFontRequester::SystemFont); - switch (m_mode) { case IconsMode: IconsModeSettings::setTextWidthIndex(m_widthBox->currentIndex()); IconsModeSettings::setMaximumTextLines(m_maxLinesBox->currentIndex()); + IconsModeSettings::self()->save(); break; case CompactMode: CompactModeSettings::setMaximumTextWidthIndex(m_widthBox->currentIndex()); + CompactModeSettings::self()->save(); break; case DetailsMode: + auto detailsModeSettings = DetailsModeSettings::self(); // We need side-padding when the full row is a click target to still be able to not click items. // So here the default padding is enabled when the full row highlight is enabled. - if (m_entireRow->isChecked() && !DetailsModeSettings::highlightEntireRow()) { - auto detailsModeSettings = DetailsModeSettings::self(); + if (m_entireRow->isChecked() && !detailsModeSettings->highlightEntireRow()) { const bool usedDefaults = detailsModeSettings->useDefaults(true); const uint defaultSidePadding = detailsModeSettings->sidePadding(); detailsModeSettings->useDefaults(usedDefaults); - if (DetailsModeSettings::sidePadding() < defaultSidePadding) { - DetailsModeSettings::setSidePadding(defaultSidePadding); + if (detailsModeSettings->sidePadding() < defaultSidePadding) { + detailsModeSettings->setSidePadding(defaultSidePadding); } - } else if (!m_entireRow->isChecked() && DetailsModeSettings::highlightEntireRow()) { + } else if (!m_entireRow->isChecked() && detailsModeSettings->highlightEntireRow()) { // The full row click target is disabled so now most of the view area can be used to interact // with the view background. Having an extra side padding has no usability benefit in this case. - DetailsModeSettings::setSidePadding(0); - } - DetailsModeSettings::setHighlightEntireRow(m_entireRow->isChecked()); - DetailsModeSettings::setExpandableFolders(m_expandableFolders->isChecked()); -#ifndef Q_OS_WIN - DetailsModeSettings::setDirectorySizeCount(m_numberOfItems->isChecked()); - DetailsModeSettings::setRecursiveDirectorySizeLimit(m_recursiveDirectorySizeLimit->value()); -#endif - DetailsModeSettings::setUseShortRelativeDates(m_useRelatetiveDates->isChecked()); - - if (m_useSymbolicPermissions->isChecked()) { - DetailsModeSettings::setUsePermissionsFormat(DetailsModeSettings::EnumUsePermissionsFormat::SymbolicFormat); - } else if (m_useNumericPermissions->isChecked()) { - DetailsModeSettings::setUsePermissionsFormat(DetailsModeSettings::EnumUsePermissionsFormat::NumericFormat); - } else if (m_useCombinedPermissions->isChecked()) { - DetailsModeSettings::setUsePermissionsFormat(DetailsModeSettings::EnumUsePermissionsFormat::CombinedFormat); + detailsModeSettings->setSidePadding(0); } - break; - default: + detailsModeSettings->setHighlightEntireRow(m_entireRow->isChecked()); + detailsModeSettings->setExpandableFolders(m_expandableFolders->isChecked()); + detailsModeSettings->save(); break; } @@ -266,6 +177,9 @@ void ViewSettingsTab::applySettings() settings.setIconSize(iconSize); settings.setPreviewSize(previewSize); + const QFont font = m_fontRequester->currentFont(); + const bool useSystemFont = (m_fontRequester->mode() == DolphinFontRequester::SystemFont); + settings.setUseSystemFont(useSystemFont); settings.setViewFont(font); @@ -294,21 +208,6 @@ void ViewSettingsTab::loadSettings() m_entireRow->setChecked(DetailsModeSettings::highlightEntireRow()); m_iconAndNameOnly->setChecked(!m_entireRow->isChecked()); m_expandableFolders->setChecked(DetailsModeSettings::expandableFolders()); -#ifndef Q_OS_WIN - if (DetailsModeSettings::directorySizeCount()) { - m_numberOfItems->setChecked(true); - m_recursiveDirectorySizeLimit->setEnabled(false); - } else { - m_sizeOfContents->setChecked(true); - m_recursiveDirectorySizeLimit->setEnabled(true); - } - m_recursiveDirectorySizeLimit->setValue(DetailsModeSettings::recursiveDirectorySizeLimit()); -#endif - m_useRelatetiveDates->setChecked(DetailsModeSettings::useShortRelativeDates()); - m_useShortDates->setChecked(!DetailsModeSettings::useShortRelativeDates()); - m_useSymbolicPermissions->setChecked(DetailsModeSettings::usePermissionsFormat() == DetailsModeSettings::EnumUsePermissionsFormat::SymbolicFormat); - m_useNumericPermissions->setChecked(DetailsModeSettings::usePermissionsFormat() == DetailsModeSettings::EnumUsePermissionsFormat::NumericFormat); - m_useCombinedPermissions->setChecked(DetailsModeSettings::usePermissionsFormat() == DetailsModeSettings::EnumUsePermissionsFormat::CombinedFormat); break; default: break; diff --git a/src/settings/viewmodes/viewsettingstab.h b/src/settings/viewmodes/viewsettingstab.h index b59fb399e..6be8fc3b5 100644 --- a/src/settings/viewmodes/viewsettingstab.h +++ b/src/settings/viewmodes/viewsettingstab.h @@ -55,14 +55,6 @@ private: QRadioButton *m_entireRow; QRadioButton *m_iconAndNameOnly; QCheckBox *m_expandableFolders; - QRadioButton *m_numberOfItems; - QRadioButton *m_sizeOfContents; - QSpinBox *m_recursiveDirectorySizeLimit; - QRadioButton *m_useRelatetiveDates; - QRadioButton *m_useShortDates; - QRadioButton *m_useSymbolicPermissions; - QRadioButton *m_useNumericPermissions; - QRadioButton *m_useCombinedPermissions; }; #endif diff --git a/src/tests/CMakeLists.txt b/src/tests/CMakeLists.txt index 2495a3a59..6431ff3fa 100644 --- a/src/tests/CMakeLists.txt +++ b/src/tests/CMakeLists.txt @@ -20,6 +20,11 @@ ecm_add_test(kitemlistcontrollertest.cpp testdir.cpp TEST_NAME kitemlistcontrollertest LINK_LIBRARIES dolphinprivate Qt${QT_MAJOR_VERSION}::Test) +# KItemListControllerExpandTest +ecm_add_test(kitemlistcontrollerexpandtest.cpp testdir.cpp +TEST_NAME kitemlistcontrollerexpandtest +LINK_LIBRARIES dolphinprivate Qt${QT_MAJOR_VERSION}::Test) + # KFileItemListViewTest ecm_add_test(kfileitemlistviewtest.cpp testdir.cpp TEST_NAME kfileitemlistviewtest diff --git a/src/tests/kitemlistcontrollerexpandtest.cpp b/src/tests/kitemlistcontrollerexpandtest.cpp new file mode 100644 index 000000000..20bc0cc0c --- /dev/null +++ b/src/tests/kitemlistcontrollerexpandtest.cpp @@ -0,0 +1,235 @@ +/* + * SPDX-FileCopyrightText: 2023 Méven Car <[email protected]> + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "kitemviews/kfileitemlistview.h" +#include "kitemviews/kfileitemmodel.h" +#include "kitemviews/kitemlistcontainer.h" +#include "kitemviews/kitemlistcontroller.h" +#include "kitemviews/kitemlistselectionmanager.h" +#include "testdir.h" + +#include <QSignalSpy> +#include <QStandardPaths> +#include <QTest> + +class KItemListControllerExpandTest : public QObject +{ + Q_OBJECT + +private Q_SLOTS: + void initTestCase(); + void cleanupTestCase(); + + void init(); + void cleanup(); + + void testDirExpand(); + +private: + KFileItemListView *m_view; + KItemListController *m_controller; + KItemListSelectionManager *m_selectionManager; + KFileItemModel *m_model; + TestDir *m_testDir; + KItemListContainer *m_container; + QSignalSpy *m_spyDirectoryLoadingCompleted; +}; + +void KItemListControllerExpandTest::initTestCase() +{ + QStandardPaths::setTestModeEnabled(true); + qRegisterMetaType<KItemSet>("KItemSet"); + + m_testDir = new TestDir(); + m_model = new KFileItemModel(); + m_view = new KFileItemListView(); + m_controller = new KItemListController(m_model, m_view, this); + m_container = new KItemListContainer(m_controller); + m_controller = m_container->controller(); + m_controller->setSelectionBehavior(KItemListController::MultiSelection); + m_selectionManager = m_controller->selectionManager(); + + QStringList files; + files << "dir1/file1"; + files << "dir1/file2"; + files << "dir1/file3"; + files << "dir1/file4"; + files << "dir1/file5"; + + files << "dir2/file1"; + files << "dir2/file2"; + files << "dir2/file3"; + files << "dir2/file4"; + files << "dir2/file5"; + + files << "dir3/file1"; + files << "dir3/file2"; + files << "dir3/file3"; + files << "dir3/file4"; + files << "dir3/file5"; + + m_testDir->createFiles(files); + m_model->loadDirectory(m_testDir->url()); + m_spyDirectoryLoadingCompleted = new QSignalSpy(m_model, &KFileItemModel::directoryLoadingCompleted); + QVERIFY(m_spyDirectoryLoadingCompleted->wait()); + + m_container->show(); + QVERIFY(QTest::qWaitForWindowExposed(m_container)); +} +void KItemListControllerExpandTest::cleanupTestCase() +{ + delete m_container; + m_container = nullptr; + + delete m_testDir; + m_testDir = nullptr; +} + +void KItemListControllerExpandTest::init() +{ + m_selectionManager->setCurrentItem(0); + QCOMPARE(m_selectionManager->currentItem(), 0); + + m_selectionManager->clearSelection(); + QVERIFY(!m_selectionManager->hasSelection()); +} + +void KItemListControllerExpandTest::cleanup() +{ +} + +void KItemListControllerExpandTest::testDirExpand() +{ + m_view->setItemLayout(KFileItemListView::DetailsLayout); + QCOMPARE(m_view->itemLayout(), KFileItemListView::DetailsLayout); + m_view->setSupportsItemExpanding(true); + + // intial state + QCOMPARE(m_spyDirectoryLoadingCompleted->count(), 1); + QCOMPARE(m_model->count(), 3); + QCOMPARE(m_selectionManager->currentItem(), 0); + QCOMPARE(m_selectionManager->selectedItems().count(), 0); + + // extend first folder + QTest::keyClick(m_container, Qt::Key_Right); + QVERIFY(m_spyDirectoryLoadingCompleted->wait()); + QCOMPARE(m_model->count(), 8); + QCOMPARE(m_selectionManager->currentItem(), 0); + QCOMPARE(m_selectionManager->selectedItems().count(), 0); + + // collapse folder + QTest::keyClick(m_container, Qt::Key_Left); + + QCOMPARE(m_model->count(), 3); + QCOMPARE(m_selectionManager->currentItem(), 0); + QCOMPARE(m_selectionManager->selectedItems().count(), 0); + + // make the first folder selected + QTest::keyClick(m_container, Qt::Key_Down); + QCOMPARE(m_model->count(), 3); + QCOMPARE(m_selectionManager->currentItem(), 1); + QCOMPARE(m_selectionManager->selectedItems().count(), 1); + + QTest::keyClick(m_container, Qt::Key_Up); + QCOMPARE(m_model->count(), 3); + QCOMPARE(m_selectionManager->currentItem(), 0); + QCOMPARE(m_selectionManager->selectedItems().count(), 1); + + // expand the two first folders + QTest::keyClick(m_container, Qt::Key_Down, Qt::ShiftModifier); + QCOMPARE(m_model->count(), 3); + QCOMPARE(m_selectionManager->currentItem(), 1); + QCOMPARE(m_selectionManager->selectedItems().count(), 2); + + // precondition + QCOMPARE(m_spyDirectoryLoadingCompleted->count(), 2); + + // expand selected folders + QTest::keyClick(m_container, Qt::Key_Right); + QVERIFY(QTest::qWaitFor( + [this]() { + return m_spyDirectoryLoadingCompleted->count() == 3; + }, + 100)); + QCOMPARE(m_model->count(), 8); + QCOMPARE(m_selectionManager->currentItem(), 6); + QCOMPARE(m_selectionManager->selectedItems().count(), 2); + + // collapse the folders + QTest::keyClick(m_container, Qt::Key_Left); + QCOMPARE(m_model->count(), 3); + QCOMPARE(m_selectionManager->currentItem(), 1); + QCOMPARE(m_selectionManager->selectedItems().count(), 2); + + // select third folder + QTest::keyClick(m_container, Qt::Key_Down, Qt::ShiftModifier); + QCOMPARE(m_model->count(), 3); + QCOMPARE(m_selectionManager->currentItem(), 2); + QCOMPARE(m_selectionManager->selectedItems().count(), 3); + + // precondition + QCOMPARE(m_spyDirectoryLoadingCompleted->count(), 3); + + // expand the three folders + QTest::keyClick(m_container, Qt::Key_Right); + + QVERIFY(QTest::qWaitFor( + [this]() { + return m_spyDirectoryLoadingCompleted->count() == 6; + }, + 100)); + + QCOMPARE(m_model->count(), 18); + QCOMPARE(m_selectionManager->currentItem(), 12); + QCOMPARE(m_selectionManager->selectedItems().count(), 3); + + // collapse the folders + QTest::keyClick(m_container, Qt::Key_Left); + QCOMPARE(m_model->count(), 3); + QCOMPARE(m_selectionManager->currentItem(), 2); + QCOMPARE(m_selectionManager->selectedItems().count(), 3); + + // shift select the directories + QTest::keyClick(m_container, Qt::Key_Up); + QCOMPARE(m_model->count(), 3); + QCOMPARE(m_selectionManager->currentItem(), 1); + QCOMPARE(m_selectionManager->selectedItems().count(), 1); + + QTest::keyClick(m_container, Qt::Key_Up); + QCOMPARE(m_model->count(), 3); + QCOMPARE(m_selectionManager->currentItem(), 0); + QCOMPARE(m_selectionManager->selectedItems().count(), 1); + + QTest::keyClick(m_container, Qt::Key_Down, Qt::ShiftModifier); + QTest::keyClick(m_container, Qt::Key_Down, Qt::ShiftModifier); + + QCOMPARE(m_model->count(), 3); + QCOMPARE(m_selectionManager->currentItem(), 2); + QCOMPARE(m_selectionManager->selectedItems().count(), 3); + + // expand the three folders with shift modifier + QTest::keyClick(m_container, Qt::Key_Right, Qt::ShiftModifier); + + QVERIFY(QTest::qWaitFor( + [this]() { + return m_spyDirectoryLoadingCompleted->count() == 9; + }, + 100)); + + QCOMPARE(m_model->count(), 18); + QCOMPARE(m_selectionManager->currentItem(), 12); + QCOMPARE(m_selectionManager->selectedItems().count(), 13); + + // collapse the folders + QTest::keyClick(m_container, Qt::Key_Left); + QCOMPARE(m_model->count(), 3); + QCOMPARE(m_selectionManager->currentItem(), 2); + QCOMPARE(m_selectionManager->selectedItems().count(), 3); +} + +QTEST_MAIN(KItemListControllerExpandTest) + +#include "kitemlistcontrollerexpandtest.moc" diff --git a/src/views/dolphinitemlistview.cpp b/src/views/dolphinitemlistview.cpp index a24e31347..9902f651b 100644 --- a/src/views/dolphinitemlistview.cpp +++ b/src/views/dolphinitemlistview.cpp @@ -7,6 +7,7 @@ #include "dolphinitemlistview.h" #include "dolphin_compactmodesettings.h" +#include "dolphin_contentdisplaysettings.h" #include "dolphin_detailsmodesettings.h" #include "dolphin_generalsettings.h" #include "dolphin_iconsmodesettings.h" @@ -81,6 +82,8 @@ void DolphinItemListView::readSettings() CompactModeSettings::self()->load(); DetailsModeSettings::self()->load(); + ContentDisplaySettings::self()->load(); + beginTransaction(); setEnabledSelectionToggles(m_selectionTogglesEnabled); diff --git a/src/views/dolphinview.h b/src/views/dolphinview.h index 705272308..21ca49c24 100644 --- a/src/views/dolphinview.h +++ b/src/views/dolphinview.h @@ -225,8 +225,6 @@ public: /** Returns the additional information which should be shown for the items. */ QList<QByteArray> visibleRoles() const; - void reload(); - /** * Refreshes the view to get synchronized with the settings (e.g. icons size, * font, ...). @@ -347,6 +345,9 @@ public: bool eventFilter(QObject *watched, QEvent *event) override; public Q_SLOTS: + + void reload(); + /** * Changes the directory to \a url. If the current directory is equal to * \a url, nothing will be done (use DolphinView::reload() instead). |
