]> cloud.milkyroute.net Git - dolphin.git/commitdiff
Merge branch 'release/20.04'
authorKai Uwe Broulik <kde@privat.broulik.de>
Wed, 6 May 2020 08:52:38 +0000 (10:52 +0200)
committerKai Uwe Broulik <kde@privat.broulik.de>
Wed, 6 May 2020 08:52:38 +0000 (10:52 +0200)
57 files changed:
.gitignore
CMakeLists.txt
doc/index.docbook
doc/preferences-startup.png
src/CMakeLists.txt
src/config-packagekit.h.cmake [new file with mode: 0644]
src/dbusinterface.h
src/dolphinbookmarkhandler.cpp
src/dolphinmainwindow.cpp
src/dolphinpart.cpp
src/dolphintabwidget.cpp
src/dolphinviewcontainer.cpp
src/dolphinviewcontainer.h
src/global.cpp
src/global.h
src/kitemviews/kfileitemlistwidget.cpp
src/kitemviews/kfileitemmodel.cpp
src/kitemviews/kfileitemmodelrolesupdater.cpp
src/kitemviews/kfileitemmodelrolesupdater.h
src/kitemviews/kitemlistcontroller.cpp
src/kitemviews/kitemlistwidget.cpp
src/kitemviews/private/kdirectorycontentscounter.cpp
src/kitemviews/private/kdirectorycontentscounter.h
src/kitemviews/private/kdirectorycontentscounterworker.cpp
src/kitemviews/private/kdirectorycontentscounterworker.h
src/main.cpp
src/org.kde.dolphin.appdata.xml
src/panels/information/informationpanel.cpp
src/panels/information/informationpanelcontent.cpp
src/panels/terminal/org.kde.KIOFuse.VFS.xml [new file with mode: 0644]
src/panels/terminal/terminalpanel.cpp
src/panels/terminal/terminalpanel.h
src/search/dolphinsearchbox.cpp
src/search/dolphinsearchbox.h
src/settings/dolphin_detailsmodesettings.kcfg
src/settings/dolphin_generalsettings.kcfg
src/settings/kcm/kcmdolphingeneral.cpp
src/settings/kcm/kcmdolphingeneral.h
src/settings/kcm/kcmdolphinnavigation.cpp
src/settings/kcm/kcmdolphinnavigation.h
src/settings/kcm/kcmdolphinservices.cpp
src/settings/kcm/kcmdolphinservices.h
src/settings/kcm/kcmdolphinviewmodes.cpp
src/settings/kcm/kcmdolphinviewmodes.h
src/settings/services/servicemenuinstaller/CMakeLists.txt
src/settings/services/servicemenuinstaller/servicemenuinstaller.cpp
src/settings/services/servicessettingspage.cpp
src/settings/services/servicessettingspage.h
src/settings/startup/startupsettingspage.cpp
src/settings/startup/startupsettingspage.h
src/settings/viewmodes/viewsettingstab.cpp
src/settings/viewmodes/viewsettingstab.h
src/views/dolphinview.cpp
src/views/dolphinviewactionhandler.cpp
src/views/versioncontrol/kversioncontrolplugin.h
src/views/versioncontrol/versioncontrolobserver.cpp
src/views/versioncontrol/versioncontrolobserver.h

index c48f92390db5edc685e822fdc67e9900d4765815..6c1df4de47c08cbf08b9eb3526c4e24e5cdd105b 100644 (file)
@@ -4,3 +4,4 @@ CMakeLists.txt.user
 .directory
 *.kdev4
 /build*/
+.cmake/
index 68ca540867d1eba456cf8045df1de78391642448..ee0082a8797e4e5626bced2353310654b0087eff 100644 (file)
@@ -2,13 +2,13 @@ cmake_minimum_required(VERSION 3.0)
 
 # KDE Application Version, managed by release script
 set (RELEASE_SERVICE_VERSION_MAJOR "20")
-set (RELEASE_SERVICE_VERSION_MINOR "04")
-set (RELEASE_SERVICE_VERSION_MICRO "0")
+set (RELEASE_SERVICE_VERSION_MINOR "07")
+set (RELEASE_SERVICE_VERSION_MICRO "70")
 set (RELEASE_SERVICE_VERSION "${RELEASE_SERVICE_VERSION_MAJOR}.${RELEASE_SERVICE_VERSION_MINOR}.${RELEASE_SERVICE_VERSION_MICRO}")
 project(Dolphin VERSION ${RELEASE_SERVICE_VERSION})
 
 set(QT_MIN_VERSION "5.11.0")
-set(KF5_MIN_VERSION "5.67.0")
+set(KF5_MIN_VERSION "5.70.0")
 
 # ECM setup
 find_package(ECM ${KF5_MIN_VERSION} CONFIG REQUIRED)
@@ -77,6 +77,16 @@ set_package_properties(KF5Activities PROPERTIES DESCRIPTION "KActivities librari
 
 find_package(Phonon4Qt5 CONFIG REQUIRED)
 
+find_package(PackageKitQt5)
+set_package_properties(PackageKitQt5
+        PROPERTIES DESCRIPTION "Software Manager integration"
+        TYPE OPTIONAL
+        PURPOSE "Used in the service menu installer"
+        )
+if(PackageKitQt5_FOUND)
+    set(HAVE_PACKAGEKIT TRUE)
+endif()
+
 find_package(KF5Baloo ${KF5_MIN_VERSION})
 set_package_properties(KF5Baloo PROPERTIES DESCRIPTION "Baloo Core libraries"
                        URL "https://www.kde.org"
index d1342d0d479c60dcdf049e01e81762d4ea946580..965303477f4743269d16e81affe5852577943ed7 100644 (file)
@@ -70,8 +70,8 @@
 
 <legalnotice>&FDLNotice;</legalnotice>
 
-<date>2020-03-17</date>
-<releaseinfo>Applications 20.04</releaseinfo>
+<date>2020-05-05</date>
+<releaseinfo>Applications 20.08</releaseinfo>
 
 <abstract>
 <para>
@@ -1137,26 +1137,55 @@ This group contains settings which control the appearance of &dolphin; on startu
 <itemizedlist>
 
 <listitem><para>
-The <guilabel>Start in</guilabel> folder is the folder which is opened on startup. The
-location of the folder can be entered directly or chosen in a dialog which can
+The <guilabel>Show on startup</guilabel> option allows choosing the folder which is opened on startup.
+</para>
+<para>
+    If the <guimenuitem>Folders, tabs, and window state from last time</guimenuitem> item is selected then
+</para>
+<itemizedlist>
+    <listitem>
+        <para>When launched from the &GUI; or CLI without any &URL;s, &dolphin; restores session</para>
+    </listitem>
+    <listitem>
+        <para>When rebooting with &dolphin; open, it restores session normally after the system comes back</para>
+    </listitem>
+    <listitem>
+        <para>When launched with &URL;s, &dolphin; window is opened showing those &URL;s instead of restoring session</para>
+    </listitem>
+    <listitem>
+        <para>When &dolphin; is already running and a new window is opened, that new window shows a single tab with the same &URL; as was visible in the previously-open &dolphin; instance</para>
+    </listitem>
+</itemizedlist>
+<para>
+The location of the folder can also be entered directly or chosen in a dialog which can
 be opened by clicking the button showing a <quote>folder</quote> icon. Moreover,
 the current location or the default location (which is the user's home folder)
 can be used as the startup folder by clicking the corresponding button.
 </para></listitem>
 
 <listitem><para>
-<guilabel>Split view mode</guilabel> controls if the
+<guilabel>Begin in split view mode</guilabel> controls if the
 <link linkend="dolphin-view">&dolphin; view</link> is split on startup
-or not.
+or not for new windows.
 </para></listitem>
 
 <listitem><para>
-<guilabel>Editable location bar</guilabel> controls if the location bar is in
+<guilabel>Show filter bar</guilabel> controls if the filter bar is shown on
+startup or not. See the <link linkend="filter-files">section on the filter bar</link>
+for details.
+</para></listitem>
+
+<listitem><para>
+<guilabel>Make location bar editable</guilabel> controls if the location bar is in
 editable mode on startup. The bread crumb mode of the location bar is used
 otherwise. See the <link linkend="location-bar">section about the location bar</link>
 for details about the two modes.
 </para></listitem>
 
+<listitem><para>
+<guilabel>Open new folders in tabs</guilabel> controls whether &dolphin; should open a new folder in a new tab of the current instance when called externally. If not enabled, the new folders will be opened in new instances of &dolphin;. By default this option is enabled.
+</para></listitem>
+
 <listitem><para>
 If <guilabel>Show full path inside location bar</guilabel> is enabled, the full
 path of the current location is shown in the bread crumb mode of the location bar.
@@ -1164,20 +1193,11 @@ Otherwise, a shortened version of the path is shown if it begins with the path o
 one of the places in the <guilabel>Places</guilabel> panel.
 </para></listitem>
 
-<listitem><para>
-<guilabel>Show filter bar</guilabel> controls if the filter bar is shown on
-startup or not. See the <link linkend="filter-files">section on the filter bar</link>
-for details.
-</para></listitem>
-
 <listitem><para>
 <guilabel>Show full path in title bar</guilabel> makes it easy to distinguish
 between files or folders with the same name in different folders.
 </para></listitem>
 
-<listitem><para>
-<guilabel>Open new folders in tabs</guilabel> controls whether &dolphin; should open a new folder in a new tab of the current instance when called externally. If not enabled, the new folders will be opened in new instances of &dolphin;. By default this option is enabled.
-</para></listitem>
 </itemizedlist>
 
 </para>
@@ -1272,7 +1292,11 @@ the text of a file item.
 <guilabel>Expandable folders</guilabel> determines whether any folders that have subfolders
 are displayed in a tree view, where the sub items can be expanded by &LMB; clicking the
 <guiicon>&gt;</guiicon> icon and collapsed by clicking the <guiicon>v</guiicon> icon.
-</para></listitem>
+</para>
+<para>
+<guilabel>Folder size displays</guilabel> allows defining the property to use then sorting folders by their size. It is possible to sort folders by <guilabel>Number of items</guilabel> or <guilabel>Size of contents</guilabel> and choose a limit to the recursive level (can be useful to constrain unneeded iterations in the deep folder structures or on the slow file systems).
+</para>
+</listitem>
 </itemizedlist>
 </para>
 </sect3>
@@ -2116,7 +2140,7 @@ project. If you wish to contribute to the documentation please email the
 <para>
 The official channel for submitting bug reports is via the &kde; bug tracking
 system. The &kde; bug tracker can be found at
-<ulink url="http://bugs.kde.org">http://bugs.kde.org</ulink>.
+<ulink url="https://bugs.kde.org">https://bugs.kde.org</ulink>.
 </para>
 </answer>
 </qandaentry>
@@ -2129,7 +2153,7 @@ system. The &kde; bug tracker can be found at
 <para>
 The official channel for submitting feature requests is via the &kde; bug
 tracking system. The &kde; bug tracker can be found at
-<ulink url="http://bugs.kde.org">http://bugs.kde.org</ulink>.
+<ulink url="https://bugs.kde.org">https://bugs.kde.org</ulink>.
 </para>
 
 </answer>
index 2b780a4c1e6abff12f337bc12cd19b35807997d6..192efe193f74ee3ccdaf06622bac43802b92d492 100644 (file)
Binary files a/doc/preferences-startup.png and b/doc/preferences-startup.png differ
index 02a43a2094270a53c572b2dc5bddf70862cbe4bf..eabe80f61187e3c7b696fd507920ee48a53df6fc 100644 (file)
@@ -4,6 +4,8 @@ configure_file(config-baloo.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-baloo.h)
 
 configure_file(config-kactivities.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-kactivities.h)
 
+configure_file(config-packagekit.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-packagekit.h)
+
 configure_file(config-terminal.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-terminal.h)
 
 add_definitions(
@@ -272,6 +274,7 @@ qt5_add_resources(dolphinstatic_SRCS dolphin.qrc)
 qt5_generate_dbus_interface(${CMAKE_CURRENT_SOURCE_DIR}/dolphinmainwindow.h org.kde.DolphinMainWindow.xml)
 qt5_add_dbus_adaptor(dolphinstatic_SRCS ${CMAKE_CURRENT_BINARY_DIR}/org.kde.DolphinMainWindow.xml dolphinmainwindow.h DolphinMainWindow)
 qt5_add_dbus_interface(dolphinstatic_SRCS ${CMAKE_CURRENT_BINARY_DIR}/org.kde.DolphinMainWindow.xml dolphinmainwindowinterface)
+qt5_add_dbus_interface(dolphinstatic_SRCS panels/terminal/org.kde.KIOFuse.VFS.xml kiofuse_interface)
 
 add_library(dolphinstatic STATIC ${dolphinstatic_SRCS})
 
diff --git a/src/config-packagekit.h.cmake b/src/config-packagekit.h.cmake
new file mode 100644 (file)
index 0000000..780fff5
--- /dev/null
@@ -0,0 +1 @@
+#cmakedefine HAVE_PACKAGEKIT
index 391916d6271998150ed9566aa27bc12eeec6b708..c1029ea23c516a5dd3f92ed615224813a6c16964 100644 (file)
@@ -34,12 +34,12 @@ public:
     Q_SCRIPTABLE void ShowItemProperties(const QStringList& uriList, const QString& startUpId);
 
     /**
-     * Set whether this interface has been created by dolphin --deamon.
+     * Set whether this interface has been created by dolphin --daemon.
      */
     void setAsDaemon();
 
     /**
-     * @return Whether this interface has been created by dolphin --deamon.
+     * @return Whether this interface has been created by dolphin --daemon.
      */
     bool isDaemon() const;
 
index ded83d6bb9c87967a95980e1403be1825166201e..0d31b8984d56a76af46840027a8f70fb427f7593 100644 (file)
@@ -21,6 +21,7 @@
 #include "dolphinmainwindow.h"
 #include "dolphinviewcontainer.h"
 #include "global.h"
+#include <KActionCollection>
 #include <KBookmarkMenu>
 #include <KIO/Global>
 #include <QDebug>
@@ -47,7 +48,11 @@ DolphinBookmarkHandler::DolphinBookmarkHandler(DolphinMainWindow *mainWindow,
     }
     m_bookmarkManager = KBookmarkManager::managerForFile(bookmarksFile, QStringLiteral("dolphin"));
     m_bookmarkManager->setUpdate(true);
-    m_bookmarkMenu.reset(new KBookmarkMenu(m_bookmarkManager, this, menu, collection));
+    m_bookmarkMenu.reset(new KBookmarkMenu(m_bookmarkManager, this, menu));
+
+    collection->addAction(QStringLiteral("add_bookmark"), m_bookmarkMenu->addBookmarkAction());
+    collection->addAction(QStringLiteral("edit_bookmarks"), m_bookmarkMenu->editBookmarksAction());
+    collection->addAction(QStringLiteral("add_bookmarks_list"), m_bookmarkMenu->bookmarkTabsAsFolderAction());
 }
 
 DolphinBookmarkHandler::~DolphinBookmarkHandler()
index f374db41ded32c1a4a169a4963bea4a32d61903c..77937790aa08ea286f991b27b568966ea19f7812 100644 (file)
 #include <KActionMenu>
 #include <KAuthorized>
 #include <KConfig>
+#include <KConfigGui>
 #include <KDualAction>
 #include <KFileItemListProperties>
 #include <KHelpMenu>
+#include <KIO/CommandLauncherJob>
 #include <KIO/JobUiDelegate>
 #include <KIO/OpenFileManagerWindowJob>
 #include <KJobWidgets>
@@ -579,6 +581,14 @@ void DolphinMainWindow::closeEvent(QCloseEvent* event)
         }
     }
 
+    if (GeneralSettings::rememberOpenedTabs())  {
+        KConfigGui::setSessionConfig(QStringLiteral("dolphin"), QStringLiteral("dolphin"));
+        KConfig *config = KConfigGui::sessionConfig();
+        saveGlobalProperties(config);
+        savePropertiesInternal(config, 1);
+        config->sync();
+    }
+
     GeneralSettings::setVersion(CurrentDolphinVersion);
     GeneralSettings::self()->save();
 
@@ -926,7 +936,10 @@ void DolphinMainWindow::compareFiles()
     command.append("\" \"");
     command.append(urlB.toDisplayString(QUrl::PreferLocalFile));
     command.append('\"');
-    KRun::runCommand(command, QStringLiteral("Kompare"), QStringLiteral("kompare"), this);
+
+    KIO::CommandLauncherJob *job = new KIO::CommandLauncherJob(command, this);
+    job->setDesktopName(QStringLiteral("org.kde.kompare"));
+    job->start();
 }
 
 void DolphinMainWindow::toggleShowMenuBar()
@@ -1265,7 +1278,7 @@ void DolphinMainWindow::tabCountChanged(int count)
 
 void DolphinMainWindow::updateWindowTitle()
 {
-    const QString newTitle = m_activeViewContainer->caption();
+    const QString newTitle = m_activeViewContainer->captionWindowTitle();
     if (windowTitle() != newTitle) {
         setWindowTitle(newTitle);
     }
index 7e7425121ca81105c7016630a2b6d9289550d5d6..0c41b2becaeecab1fac93cde86fc143978818ac1 100644 (file)
@@ -34,6 +34,7 @@
 #include <KActionCollection>
 #include <KAuthorized>
 #include <KConfigGroup>
+#include <KDialogJobUiDelegate>
 #include <KFileItemListProperties>
 #include <KIconLoader>
 #include <KJobWidgets>
@@ -42,7 +43,7 @@
 #include <KMimeTypeEditor>
 #include <KNS3/KMoreToolsMenuFactory>
 #include <KPluginFactory>
-#include <KRun>
+#include <KIO/CommandLauncherJob>
 #include <KSharedConfig>
 #include <KToolInvocation>
 
@@ -546,7 +547,10 @@ void DolphinPart::slotFindFile()
     if (!(actions.isEmpty())) {
         actions.first()->trigger();
     } else {
-        KRun::run(QStringLiteral("kfind"), {url()}, widget());
+        KIO::CommandLauncherJob *job = new KIO::CommandLauncherJob(QStringLiteral("kfind"), {url().toString()}, this);
+        job->setDesktopName(QStringLiteral("org.kde.kfind"));
+        job->setUiDelegate(new KDialogJobUiDelegate(KJobUiDelegate::AutoHandlingEnabled, widget()));
+        job->start();
     }
 }
 
index 89c54baf54b421611318eba1283ef513eeae9fa1..fba6fe084992dc50a7b20f8ce21d984b2afc4bc3 100644 (file)
@@ -25,9 +25,9 @@
 #include "dolphinviewcontainer.h"
 
 #include <KConfigGroup>
-#include <KRun>
 #include <KShell>
 #include <kio/global.h>
+#include <KIO/CommandLauncherJob>
 #include <KAcceleratorManager>
 
 #include <QApplication>
@@ -334,8 +334,9 @@ void DolphinTabWidget::detachTab(int index)
     }
     args << QStringLiteral("--new-window");
 
-    const QString command = QStringLiteral("dolphin %1").arg(KShell::joinArgs(args));
-    KRun::runCommand(command, this);
+    KIO::CommandLauncherJob *job = new KIO::CommandLauncherJob("dolphin", args, this);
+    job->setDesktopName(QStringLiteral("org.kde.dolphin"));
+    job->start();
 
     closeTab(index);
 }
index 3a17805ab8916667d15752c942c699fdcccc1b24..4ab34a06a85ef1632c6bdd8ed499489a1e03023b 100644 (file)
@@ -29,6 +29,8 @@
 #include "trash/dolphintrash.h"
 #include "views/viewmodecontroller.h"
 #include "views/viewproperties.h"
+#include "dolphin_detailsmodesettings.h"
+#include "views/dolphinview.h"
 
 #ifdef HAVE_KACTIVITIES
 #include <KActivities/ResourceInstance>
@@ -119,7 +121,7 @@ DolphinViewContainer::DolphinViewContainer(const QUrl& url, QWidget* parent) :
     connect(m_searchBox, &DolphinSearchBox::activated, this, &DolphinViewContainer::activate);
     connect(m_searchBox, &DolphinSearchBox::closeRequest, this, &DolphinViewContainer::closeSearchBox);
     connect(m_searchBox, &DolphinSearchBox::searchRequest, this, &DolphinViewContainer::startSearching);
-    connect(m_searchBox, &DolphinSearchBox::returnPressed, this, &DolphinViewContainer::requestFocus);
+    connect(m_searchBox, &DolphinSearchBox::focusViewRequest, this, &DolphinViewContainer::requestFocus);
     m_searchBox->setWhatsThis(xi18nc("@info:whatsthis findbar",
         "<para>This helps you find files and folders. Enter a <emphasis>"
         "search term</emphasis> and specify search settings with the "
@@ -249,6 +251,12 @@ DolphinViewContainer::DolphinViewContainer(const QUrl& url, QWidget* parent) :
 
     setSearchModeEnabled(isSearchUrl(url));
 
+    connect(DetailsModeSettings::self(), &KCoreConfigSkeleton::configChanged, this, [=]() {
+        if (view()->mode() == DolphinView::Mode::DetailsView) {
+            view()->reload();
+        }
+    });
+
     // Initialize kactivities resource instance
 
 #ifdef HAVE_KACTIVITIES
@@ -449,6 +457,18 @@ void DolphinViewContainer::reload()
     m_messageWidget->hide();
 }
 
+QString DolphinViewContainer::captionWindowTitle() const
+{
+    if (GeneralSettings::showFullPathInTitlebar() && !isSearchModeEnabled()) {
+        if (!url().isLocalFile()) {
+            return url().adjusted(QUrl::StripTrailingSlash).toString();
+        }
+        return url().adjusted(QUrl::StripTrailingSlash).path();
+    } else {
+        return DolphinViewContainer::caption();
+    }
+}
+
 QString DolphinViewContainer::caption() const
 {
     if (isSearchModeEnabled()) {
@@ -459,13 +479,6 @@ QString DolphinViewContainer::caption() const
         }
     }
 
-    if (GeneralSettings::showFullPathInTitlebar()) {
-        if (!url().isLocalFile()) {
-            return url().adjusted(QUrl::StripTrailingSlash).toString();
-        }
-        return url().adjusted(QUrl::StripTrailingSlash).path();
-    }
-
     KFilePlacesModel *placesModel = DolphinPlacesModelSingleton::instance().placesModel();
     const auto& matchedPlaces = placesModel->match(placesModel->index(0,0), KFilePlacesModel::UrlRole, QUrl(url().adjusted(QUrl::StripTrailingSlash).toString(QUrl::FullyEncoded).append("/?")), 1, Qt::MatchRegExp);
 
index 5207d2d35d47cf39572d156916d8ce504d65809f..52e63cbe04f34e9e670c8c39232c0854fe45fbf5 100644 (file)
@@ -133,6 +133,13 @@ public:
      */
     void reload();
 
+    /**
+     * @return Returns a Caption suitable for display in the window title.
+     * It is calculated depending on GeneralSettings::showFullPathInTitlebar().
+     * If it's false, it calls caption().
+     */
+    QString captionWindowTitle() const;
+
     /**
      * @return Returns a Caption suitable for display to the user. It is
      * calculated depending on settings, if a search is active and other
index 5236fa4d102a9086367bfa61d62dd9eaafff3331..32a2d4ebb04bf6a57a7059e0c41e4af89824bcf7 100644 (file)
@@ -23,7 +23,9 @@
 #include "dolphindebug.h"
 #include "dolphinmainwindowinterface.h"
 
-#include <KRun>
+#include <KDialogJobUiDelegate>
+#include <KIO/ApplicationLauncherJob>
+#include <KService>
 #include <KWindowSystem>
 
 #include <QApplication>
@@ -60,13 +62,11 @@ void Dolphin::openNewWindow(const QList<QUrl> &urls, QWidget *window, const Open
     if (!urls.isEmpty()) {
         command.append(QLatin1String(" %U"));
     }
-    KRun::run(
-        command,
-        urls,
-        window,
-        QApplication::applicationDisplayName(),
-        QApplication::windowIcon().name()
-    );
+    KService::Ptr service(new KService(QApplication::applicationDisplayName(), command, QApplication::windowIcon().name()));
+    auto *job = new KIO::ApplicationLauncherJob(service, window);
+    job->setUrls(urls);
+    job->setUiDelegate(new KDialogJobUiDelegate(KJobUiDelegate::AutoHandlingEnabled, window));
+    job->start();
 }
 
 bool Dolphin::attachToExistingInstance(const QList<QUrl>& inputUrls, bool openFiles, bool splitView, const QString& preferredService)
@@ -78,36 +78,7 @@ bool Dolphin::attachToExistingInstance(const QList<QUrl>& inputUrls, bool openFi
         return false;
     }
 
-    QVector<QPair<QSharedPointer<OrgKdeDolphinMainWindowInterface>, QStringList>> dolphinInterfaces;
-    if (!preferredService.isEmpty()) {
-        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.
-    const QStringList dbusServices = QDBusConnection::sessionBus().interface()->registeredServiceNames().value();
-    // Don't match the service without trailing "-" (unique instance)
-    const QString pattern = QStringLiteral("org.kde.dolphin-");
-    // Don't match the pid without leading "-"
-    const QString myPid = QLatin1Char('-') + QString::number(QCoreApplication::applicationPid());
-    for (const QString& service : dbusServices) {
-        if (service.startsWith(pattern) && !service.endsWith(myPid)) {
-            // 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()));
-            }
-        }
-    }
-
+    auto dolphinInterfaces = dolphinGuiInstances(preferredService);
     if (dolphinInterfaces.isEmpty()) {
         return false;
     }
@@ -145,3 +116,38 @@ bool Dolphin::attachToExistingInstance(const QList<QUrl>& inputUrls, bool openFi
     }
     return attached;
 }
+
+QVector<QPair<QSharedPointer<OrgKdeDolphinMainWindowInterface>, QStringList>> Dolphin::dolphinGuiInstances(const QString& preferredService)
+{
+    QVector<QPair<QSharedPointer<OrgKdeDolphinMainWindowInterface>, QStringList>> dolphinInterfaces;
+    if (!preferredService.isEmpty()) {
+        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.
+    const QStringList dbusServices = QDBusConnection::sessionBus().interface()->registeredServiceNames().value();
+    // Don't match the service without trailing "-" (unique instance)
+    const QString pattern = QStringLiteral("org.kde.dolphin-");
+    // Don't match the pid without leading "-"
+    const QString myPid = QLatin1Char('-') + QString::number(QCoreApplication::applicationPid());
+    for (const QString& service : dbusServices) {
+        if (service.startsWith(pattern) && !service.endsWith(myPid)) {
+            // 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()));
+            }
+        }
+    }
+
+    return dolphinInterfaces;
+}
index 7ee564581c6b7b1ff14e71610489ec7e0d8817db..daf86134e435265cc834de4bd49d7cef3c906ea8 100644 (file)
@@ -24,6 +24,8 @@
 #include <QUrl>
 #include <QWidget>
 
+class OrgKdeDolphinMainWindowInterface;
+
 namespace Dolphin {
     QList<QUrl> validateUris(const QStringList& uriList);
 
@@ -51,6 +53,11 @@ namespace Dolphin {
      */
     bool attachToExistingInstance(const QList<QUrl>& inputUrls, bool openFiles, bool splitView, const QString& preferredService = QString());
 
+    /**
+     * Returns a QVector with all GUI-capable Dolphin instances
+     */
+    QVector<QPair<QSharedPointer<OrgKdeDolphinMainWindowInterface>, QStringList>> dolphinGuiInstances(const QString& preferredService);
+
     /**
      * TODO: Move this somewhere global to all KDE apps, not just Dolphin
      */
index 40b8ccf3717a4a10965cf8c361c8be7e743fb57a..a495a4c2ff63095511f421b740cc68c23f1e1388 100644 (file)
@@ -21,6 +21,8 @@
 #include "kfileitemmodel.h"
 #include "kitemlistview.h"
 
+#include "dolphin_detailsmodesettings.h"
+
 #include <KFormat>
 #include <KLocalizedString>
 
@@ -64,14 +66,24 @@ QString KFileItemListWidgetInformant::roleText(const QByteArray& role,
 
     if (role == "size") {
         if (values.value("isDir").toBool()) {
-            // The item represents a directory. Show the number of sub directories
-            // instead of the file size of the directory.
+            // The item represents a directory.
             if (!roleValue.isNull()) {
-                const int count = roleValue.toInt();
+                const int count = values.value("count").toInt();
                 if (count < 0) {
                     text = i18nc("@item:intable", "Unknown");
                 } else {
-                    text = i18ncp("@item:intable", "%1 item", "%1 items", count);
+                    if (DetailsModeSettings::directorySizeCount()) {
+                        //  Show the number of sub directories instead of the file size of the directory.
+                        text = i18ncp("@item:intable", "%1 item", "%1 items", count);
+                    } else {
+                        // if we have directory size available
+                        if (roleValue == -1) {
+                            text = i18nc("@item:intable", "Unknown");
+                        } else {
+                            const KIO::filesize_t size = roleValue.value<KIO::filesize_t>();
+                            text = KFormat().formatByteSize(size);
+                        }
+                    }
                 }
             }
         } else {
index e4dca2734871d3b0c9e0dc0da1375fab76d202e5..ac3c33e22e44af1a0680c1684c50dfd0139c084b 100644 (file)
@@ -22,6 +22,7 @@
 #include "kfileitemmodel.h"
 
 #include "dolphin_generalsettings.h"
+#include "dolphin_detailsmodesettings.h"
 #include "dolphindebug.h"
 #include "private/kfileitemmodeldirlister.h"
 #include "private/kfileitemmodelsortalgorithm.h"
@@ -1767,8 +1768,15 @@ int KFileItemModel::sortRoleCompare(const ItemData* a, const ItemData* b, const
             // See "if (m_sortFoldersFirst || m_sortRole == SizeRole)" in KFileItemModel::lessThan():
             Q_ASSERT(itemB.isDir());
 
-            const QVariant valueA = a->values.value("size");
-            const QVariant valueB = b->values.value("size");
+            QVariant valueA, valueB;
+            if (DetailsModeSettings::directorySizeCount()) {
+                valueA = a->values.value("count");
+                valueB = b->values.value("count");
+            } else {
+                // use dir size then
+                valueA = a->values.value("size");
+                valueB = b->values.value("size");
+            }
             if (valueA.isNull() && valueB.isNull()) {
                 result = 0;
             } else if (valueA.isNull()) {
@@ -1776,7 +1784,11 @@ int KFileItemModel::sortRoleCompare(const ItemData* a, const ItemData* b, const
             } else if (valueB.isNull()) {
                 result = +1;
             } else {
-                result = valueA.toInt() - valueB.toInt();
+                if (valueA < valueB) {
+                    return -1;
+                } else {
+                    return +1;
+                }
             }
         } else {
             // See "if (m_sortFoldersFirst || m_sortRole == SizeRole)" in KFileItemModel::lessThan():
index bf2c84c4031ffa6346dc2e5813d6c7a0ea7b1007..c28e240a5f97ba6b54dc1bd6e5c5453a769da363 100644 (file)
@@ -44,7 +44,6 @@
 #include <QElapsedTimer>
 #include <QTimer>
 
-
 // #define KFILEITEMMODELROLESUPDATER_DEBUG
 
 namespace {
@@ -108,9 +107,9 @@ KFileItemModelRolesUpdater::KFileItemModelRolesUpdater(KFileItemModel* model, QO
             this,    &KFileItemModelRolesUpdater::slotSortRoleChanged);
 
     // Use a timer to prevent that each call of slotItemsChanged() results in a synchronous
-    // resolving of the roles. Postpone the resolving until no update has been done for 1 second.
+    // resolving of the roles. Postpone the resolving until no update has been done for 100 ms.
     m_recentlyChangedItemsTimer = new QTimer(this);
-    m_recentlyChangedItemsTimer->setInterval(1000);
+    m_recentlyChangedItemsTimer->setInterval(100);
     m_recentlyChangedItemsTimer->setSingleShot(true);
     connect(m_recentlyChangedItemsTimer, &QTimer::timeout, this, &KFileItemModelRolesUpdater::resolveRecentlyChangedItems);
 
@@ -750,7 +749,7 @@ void KFileItemModelRolesUpdater::applyChangedBalooRolesForItem(const KFileItem &
 #endif
 }
 
-void KFileItemModelRolesUpdater::slotDirectoryContentsCountReceived(const QString& path, int count)
+void KFileItemModelRolesUpdater::slotDirectoryContentsCountReceived(const QString& path, int count, long size)
 {
     const bool getSizeRole = m_roles.contains("size");
     const bool getIsExpandableRole = m_roles.contains("isExpandable");
@@ -761,17 +760,16 @@ void KFileItemModelRolesUpdater::slotDirectoryContentsCountReceived(const QStrin
             QHash<QByteArray, QVariant> data;
 
             if (getSizeRole) {
-                data.insert("size", count);
+                data.insert("count", count);
+                if (size != -1) {
+                    data.insert("size", QVariant::fromValue(size));
+                }
             }
             if (getIsExpandableRole) {
                 data.insert("isExpandable", count > 0);
             }
 
-            disconnect(m_model, &KFileItemModel::itemsChanged,
-                       this,    &KFileItemModelRolesUpdater::slotItemsChanged);
             m_model->setData(index, data);
-            connect(m_model, &KFileItemModel::itemsChanged,
-                    this,    &KFileItemModelRolesUpdater::slotItemsChanged);
         }
     }
 }
@@ -997,7 +995,7 @@ void KFileItemModelRolesUpdater::applySortRole(int index)
         data.insert("type", item.mimeComment());
     } else if (m_model->sortRole() == "size" && item.isLocalFile() && item.isDir()) {
         const QString path = item.localPath();
-        data.insert("size", m_directoryContentsCounter->countDirectoryContentsSynchronously(path));
+        m_directoryContentsCounter->scanDirectory(path);
     } else {
         // Probably the sort role is a baloo role - just determine all roles.
         data = rolesData(item);
@@ -1070,7 +1068,7 @@ QHash<QByteArray, QVariant> KFileItemModelRolesUpdater::rolesData(const KFileIte
             // Tell m_directoryContentsCounter that we want to count the items
             // inside the directory. The result will be received in slotDirectoryContentsCountReceived.
             const QString path = item.localPath();
-            m_directoryContentsCounter->addDirectory(path);
+            m_directoryContentsCounter->scanDirectory(path);
         } else if (getSizeRole) {
             data.insert("size", -1); // -1 indicates an unknown number of items
         }
index 9078c8e0d64cdc89607b27b656c46967a919865d..e21cd30dfe0672b358fd012467c9d73fc4b0399e 100644 (file)
@@ -212,7 +212,7 @@ private slots:
     void applyChangedBalooRoles(const QString& file);
     void applyChangedBalooRolesForItem(const KFileItem& file);
 
-    void slotDirectoryContentsCountReceived(const QString& path, int count);
+    void slotDirectoryContentsCountReceived(const QString& path, int count, long size);
 
 private:
     /**
index 5ddf52e5f193dc84671c3248696d74f2bbeb8bf3..0c25ebb8b9ace0f2703ffcb4dffdf64172cc21d5 100644 (file)
@@ -1219,7 +1219,7 @@ void KItemListController::startDragging()
     const QPoint hotSpot((pixmap.width() / pixmap.devicePixelRatio()) / 2, 0);
     drag->setHotSpot(hotSpot);
 
-    drag->exec(Qt::MoveAction | Qt::CopyAction | Qt::LinkAction, Qt::CopyAction);
+    drag->exec(Qt::MoveAction | Qt::CopyAction | Qt::LinkAction, Qt::MoveAction);
 
     QAccessibleEvent accessibilityEvent(view(), QAccessible::DragDropStart);
     QAccessible::updateAccessibility(&accessibilityEvent);
index 49a13f68fa096ef549a069d6d30147602f673bb9..42bf9ebdcbb8397cf5feb23a19c766a9b4a36510 100644 (file)
@@ -378,7 +378,6 @@ QPixmap KItemListWidget::createDragPixmap(const QStyleOptionGraphicsItem* option
     const bool wasHovered = m_hovered;
 
     setAlternateBackground(false);
-    setSelected(false);
     setHovered(false);
 
     paint(&painter, option, widget);
index bd204fe8e24a353a16a0dccdc6ff821b668c8474..a19bce8b3b061cd6b0ba3685b2a4603257056510 100644 (file)
 #include <KDirWatch>
 
 #include <QFileInfo>
+#include <QDir>
 #include <QThread>
 
+namespace  {
+    /// cache of directory counting result
+    static QHash<QString, QPair<int, long>> *s_cache;
+}
+
 KDirectoryContentsCounter::KDirectoryContentsCounter(KFileItemModel* model, QObject* parent) :
     QObject(parent),
     m_model(model),
@@ -43,9 +49,12 @@ KDirectoryContentsCounter::KDirectoryContentsCounter(KFileItemModel* model, QObj
         m_workerThread->start();
     }
 
+    if (s_cache == nullptr) {
+        s_cache = new QHash<QString, QPair<int, long>>();
+    }
+
     m_worker = new KDirectoryContentsCounterWorker();
     m_worker->moveToThread(m_workerThread);
-    ++m_workersCount;
 
     connect(this,     &KDirectoryContentsCounter::requestDirectoryContentsCount,
             m_worker, &KDirectoryContentsCounterWorker::countDirectoryContents);
@@ -58,9 +67,7 @@ KDirectoryContentsCounter::KDirectoryContentsCounter(KFileItemModel* model, QObj
 
 KDirectoryContentsCounter::~KDirectoryContentsCounter()
 {
-    --m_workersCount;
-
-    if (m_workersCount > 0) {
+    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.
@@ -79,38 +86,17 @@ KDirectoryContentsCounter::~KDirectoryContentsCounter()
     }
 }
 
-void KDirectoryContentsCounter::addDirectory(const QString& path)
+void KDirectoryContentsCounter::scanDirectory(const QString& path)
 {
     startWorker(path);
 }
 
-int KDirectoryContentsCounter::countDirectoryContentsSynchronously(const QString& path)
-{
-    const QString resolvedPath = QFileInfo(path).canonicalFilePath();
-
-    if (!m_dirWatcher->contains(resolvedPath)) {
-        m_dirWatcher->addDir(resolvedPath);
-        m_watchedDirs.insert(resolvedPath);
-    }
-
-    KDirectoryContentsCounterWorker::Options options;
-
-    if (m_model->showHiddenFiles()) {
-        options |= KDirectoryContentsCounterWorker::CountHiddenFiles;
-    }
-
-    if (m_model->showDirectoriesOnly()) {
-        options |= KDirectoryContentsCounterWorker::CountDirectoriesOnly;
-    }
-
-    return KDirectoryContentsCounterWorker::subItemsCount(path, options);
-}
-
-void KDirectoryContentsCounter::slotResult(const QString& path, int count)
+void KDirectoryContentsCounter::slotResult(const QString& path, int count, long size)
 {
     m_workerIsBusy = false;
 
-    const QString resolvedPath = QFileInfo(path).canonicalFilePath();
+    const QFileInfo info = QFileInfo(path);
+    const QString resolvedPath = info.canonicalFilePath();
 
     if (!m_dirWatcher->contains(resolvedPath)) {
         m_dirWatcher->addDir(resolvedPath);
@@ -121,7 +107,22 @@ void KDirectoryContentsCounter::slotResult(const QString& path, int count)
         startWorker(m_queue.dequeue());
     }
 
-    emit result(path, count);
+    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));
+    }
+
+    // sends the results
+    emit result(resolvedPath, count, size);
 }
 
 void KDirectoryContentsCounter::slotDirWatchDirty(const QString& path)
@@ -146,7 +147,7 @@ void KDirectoryContentsCounter::slotItemsRemoved()
     if (!m_watchedDirs.isEmpty()) {
         // Don't let KDirWatch watch for removed items
         if (allItemsRemoved) {
-            foreach (const QString& path, m_watchedDirs) {
+            for (const QString& path : qAsConst(m_watchedDirs)) {
                 m_dirWatcher->removeDir(path);
             }
             m_watchedDirs.clear();
@@ -166,6 +167,13 @@ void KDirectoryContentsCounter::slotItemsRemoved()
 
 void KDirectoryContentsCounter::startWorker(const QString& path)
 {
+    if (s_cache->contains(path)) {
+        // fast path when in cache
+        // will be updated later if result has changed
+        const auto pair = s_cache->value(path);
+        emit result(path, pair.first, pair.second);
+    }
+
     if (m_workerIsBusy) {
         m_queue.enqueue(path);
     } else {
@@ -185,4 +193,3 @@ void KDirectoryContentsCounter::startWorker(const QString& path)
 }
 
 QThread* KDirectoryContentsCounter::m_workerThread = nullptr;
-int KDirectoryContentsCounter::m_workersCount = 0;
index 349860757ac52bc6f5cfb8dbabe5d9fa3b61699d..0c900ec644334ff12fc78f84da2cdda8a9275332 100644 (file)
@@ -25,6 +25,7 @@
 
 #include <QQueue>
 #include <QSet>
+#include <QHash>
 
 class KDirWatch;
 class KFileItemModel;
@@ -45,28 +46,23 @@ public:
      *
      * The directory \a path is watched for changes, and the signal is emitted
      * again if a change occurs.
-     */
-    void addDirectory(const QString& path);
-
-    /**
-     * In contrast to \a addDirectory, this function counts the items inside
-     * the directory \a path synchronously and returns the result.
      *
-     * The directory is watched for changes, and the signal \a result is
-     * emitted if a change occurs.
+     * Uses a cache internally to speed up first result,
+     * but emit again result when the cache was updated
      */
-    int countDirectoryContentsSynchronously(const QString& path);
+    void scanDirectory(const QString& path);
 
 signals:
     /**
-     * Signals that the directory \a path contains \a count items.
+     * Signals that the directory \a path contains \a count items of size \a
+     * Size calculation depends on parameter DetailsModeSettings::recursiveDirectorySizeLimit
      */
-    void result(const QString& path, int count);
+    void result(const QString& path, int count, long size);
 
     void requestDirectoryContentsCount(const QString& path, KDirectoryContentsCounterWorker::Options options);
 
 private slots:
-    void slotResult(const QString& path, int count);
+    void slotResult(const QString& path, int count, long size);
     void slotDirWatchDirty(const QString& path);
     void slotItemsRemoved();
 
@@ -79,7 +75,6 @@ private:
     QQueue<QString> m_queue;
 
     static QThread* m_workerThread;
-    static int m_workersCount;
 
     KDirectoryContentsCounterWorker* m_worker;
     bool m_workerIsBusy;
index e9c954ed9ce32183716bf3f978f36a1ff4780a1b..1e4a0b3b47eb5d483be8139203b9e77a597d3964 100644 (file)
 
 // Required includes for subItemsCount():
 #ifdef Q_OS_WIN
-    #include <QDir>
+#include <QDir>
 #else
-    #include <QFile>
-    #include <qplatformdefs.h>
+#include <QFile>
+#include <qplatformdefs.h>
 #endif
 
+#include "dolphin_detailsmodesettings.h"
+
 KDirectoryContentsCounterWorker::KDirectoryContentsCounterWorker(QObject* parent) :
     QObject(parent)
 {
     qRegisterMetaType<KDirectoryContentsCounterWorker::Options>();
 }
 
-int KDirectoryContentsCounterWorker::subItemsCount(const QString& path, Options options)
+#ifndef Q_OS_WIN
+KDirectoryContentsCounterWorker::CountResult walkDir(const QString &dirPath,
+                                                     const bool countHiddenFiles,
+                                                     const bool countDirectoriesOnly,
+                                                     QT_DIRENT *dirEntry,
+                                                     const uint allowedRecursiveLevel)
 {
-    const bool countHiddenFiles = options & CountHiddenFiles;
-    const bool countDirectoriesOnly = options & CountDirectoriesOnly;
-
-#ifdef Q_OS_WIN
-    QDir dir(path);
-    QDir::Filters filters = QDir::NoDotAndDotDot | QDir::System;
-    if (countHiddenFiles) {
-        filters |= QDir::Hidden;
-    }
-    if (countDirectoriesOnly) {
-        filters |= QDir::Dirs;
-    } else {
-        filters |= QDir::AllEntries;
-    }
-    return dir.entryList(filters).count();
-#else
-    // Taken from kio/src/widgets/kdirmodel.cpp
-    // Copyright (C) 2006 David Faure <faure@kde.org>
-
     int count = -1;
-    auto dir = QT_OPENDIR(QFile::encodeName(path));
+    long size = -1;
+    auto dir = QT_OPENDIR(QFile::encodeName(dirPath));
     if (dir) {
         count = 0;
-        QT_DIRENT *dirEntry = nullptr;
+        QT_STATBUF buf;
+
         while ((dirEntry = QT_READDIR(dir))) {
             if (dirEntry->d_name[0] == '.') {
                 if (dirEntry->d_name[1] == '\0' || !countHiddenFiles) {
@@ -76,20 +66,70 @@ int KDirectoryContentsCounterWorker::subItemsCount(const QString& path, Options
             // 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;
+                    dirEntry->d_type == DT_DIR ||
+                    dirEntry->d_type == DT_LNK ||
+                    dirEntry->d_type == DT_UNKNOWN;
             if (countEntry) {
                 ++count;
             }
+
+            if (allowedRecursiveLevel > 0) {
+
+                bool linkFound = false;
+                QString nameBuf = QStringLiteral("%1/%2").arg(dirPath, dirEntry->d_name);
+
+                if (dirEntry->d_type == DT_REG || dirEntry->d_type == DT_LNK) {
+                    if (QT_STAT(nameBuf.toLocal8Bit(), &buf) == 0) {
+                        if (S_ISDIR(buf.st_mode)) {
+                            // was a dir link, recurse
+                            linkFound = true;
+                        }
+                        size += buf.st_size;
+                    }
+                }
+                if (dirEntry->d_type == DT_DIR || linkFound) {
+                    // recursion for dirs and dir links
+                    size += walkDir(nameBuf, countHiddenFiles, countDirectoriesOnly, dirEntry, allowedRecursiveLevel - 1).size;
+                }
+            }
         }
         QT_CLOSEDIR(dir);
     }
-    return count;
+    return KDirectoryContentsCounterWorker::CountResult{count, size};
+}
+#endif
+
+KDirectoryContentsCounterWorker::CountResult KDirectoryContentsCounterWorker::subItemsCount(const QString& path, Options options)
+{
+    const bool countHiddenFiles = options & CountHiddenFiles;
+    const bool countDirectoriesOnly = options & CountDirectoriesOnly;
+
+#ifdef Q_OS_WIN
+    QDir dir(path);
+    QDir::Filters filters = QDir::NoDotAndDotDot | QDir::System;
+    if (countHiddenFiles) {
+        filters |= QDir::Hidden;
+    }
+    if (countDirectoriesOnly) {
+        filters |= QDir::Dirs;
+    } else {
+        filters |= QDir::AllEntries;
+    }
+    return {dir.entryList(filters).count(), 0};
+#else
+
+    const uint maxRecursiveLevel = DetailsModeSettings::directorySizeCount() ? 1 : DetailsModeSettings::recursiveDirectorySizeLimit();
+
+    QT_DIRENT *dirEntry = nullptr;
+
+    auto res = walkDir(QFile::encodeName(path), countHiddenFiles, countDirectoriesOnly, dirEntry, maxRecursiveLevel);
+
+    return res;
 #endif
 }
 
 void KDirectoryContentsCounterWorker::countDirectoryContents(const QString& path, Options options)
 {
-    emit result(path, subItemsCount(path, options));
+    auto res = subItemsCount(path, options);
+    emit result(path, res.count, res.size);
 }
index b40da6e87b57f5f99eb48c57b302a012bdc8b2d5..fac9978d5fe357d08af1f68fd3ccb5367d12a94b 100644 (file)
@@ -37,6 +37,14 @@ public:
     };
     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);
 
     /**
@@ -45,13 +53,13 @@ public:
      *
      * @return The number of items.
      */
-    static int subItemsCount(const QString& path, Options options);
+    static CountResult subItemsCount(const QString& path, Options options);
 
 signals:
     /**
-     * Signals that the directory \a path contains \a count items.
+     * Signals that the directory \a path contains \a count items and optionally the size of its content.
      */
-    void result(const QString& path, int count);
+    void result(const QString& path, int count, long size);
 
 public slots:
     /**
index 5932df5ce0b66426e99ecc5d2f622e1cc4bb70bb..802e64d2579352b70a32752da732ffdb2d379fb4 100644 (file)
@@ -31,6 +31,7 @@
 #include <KDBusService>
 #include <KLocalizedString>
 #include <Kdelibs4ConfigMigrator>
+#include <KConfigGui>
 
 #include <QApplication>
 #include <QCommandLineParser>
@@ -139,6 +140,9 @@ extern "C" Q_DECL_EXPORT int kdemain(int argc, char **argv)
     const bool openFiles = parser.isSet(QStringLiteral("select"));
     const QStringList args = parser.positionalArguments();
     QList<QUrl> urls = Dolphin::validateUris(args);
+    // We later mutate urls, so we need to store if it was empty originally
+    const bool startedWithURLs = !urls.isEmpty();
+
 
     if (parser.isSet(QStringLiteral("daemon"))) {
         KDBusService dolphinDBusService;
@@ -154,7 +158,7 @@ extern "C" Q_DECL_EXPORT int kdemain(int argc, char **argv)
         }
     }
 
-    if (urls.isEmpty()) {
+    if (!startedWithURLs) {
         // We need at least one URL to open Dolphin
         urls.append(Dolphin::homeUrl());
     }
@@ -174,12 +178,25 @@ extern "C" Q_DECL_EXPORT int kdemain(int argc, char **argv)
 
     mainWindow->show();
 
-    if (app.isSessionRestored()) {
-        const QString className = KXmlGuiWindow::classNameOfToplevel(1);
-        if (className == QLatin1String("DolphinMainWindow")) {
-            mainWindow->restore(1);
-        } else {
-           qCWarning(DolphinDebug) << "Unknown class " << className << " in session saved data!";
+    if (!app.isSessionRestored()) {
+        KConfigGui::setSessionConfig(QStringLiteral("dolphin"), QStringLiteral("dolphin"));
+    }
+
+    // Only restore session if:
+    // 1. Dolphin was not started with command line args
+    // 2. The "remember state" setting is enabled or session restoration after
+    //    reboot is in use
+    // 3. There is a session available to restore
+    if (!startedWithURLs && (app.isSessionRestored() || GeneralSettings::rememberOpenedTabs()) ) {
+        // Get saved state data for the last-closed Dolphin instance
+        const QString serviceName = QStringLiteral("org.kde.dolphin-%1").arg(QCoreApplication::applicationPid());
+        if (Dolphin::dolphinGuiInstances(serviceName).size() > 0) {
+            const QString className = KXmlGuiWindow::classNameOfToplevel(1);
+            if (className == QLatin1String("DolphinMainWindow")) {
+                mainWindow->restore(1);
+            } else {
+                qCWarning(DolphinDebug) << "Unknown class " << className << " in session saved data!";
+            }
         }
     }
 
index c3d2c7fc0dd369a1911ab60b358770dee6900121..05228bdbbc5434e6c106499d01af214f84592e29 100644 (file)
       <caption xml:lang="pt-BR">Gerenciamento de arquivos no Dolphin</caption>
       <caption xml:lang="ru">Управление файлами</caption>
       <caption xml:lang="sk">Správa súborov v Dolphin</caption>
+      <caption xml:lang="sl">Upravljanje datotek v Dolphinu</caption>
       <caption xml:lang="sv">Filhantering i Dolphin</caption>
       <caption xml:lang="uk">Керування файлами у Dolphin</caption>
       <caption xml:lang="x-test">xxFile management in Dolphinxx</caption>
index 23e7f1922b0b0625d719a60c26781d39721f509c..4f0e4e5eb80ff51e784f559f3a69991b66dd055d 100644 (file)
@@ -248,7 +248,7 @@ void InformationPanel::showItemInfo()
         if (item.isNull()) {
             // No item is hovered and no selection has been done: provide
             // an item for the currently shown directory.
-            m_folderStatJob = KIO::stat(url(), KIO::HideProgressInfo);
+            m_folderStatJob = KIO::statDetails(url(), KIO::StatJob::SourceSide, KIO::StatDefaultDetails | KIO::StatRecursiveSize, KIO::HideProgressInfo);
             if (m_folderStatJob->uiDelegate()) {
                 KJobWidgets::setWindow(m_folderStatJob, this);
             }
index 5c7c7c3f158936286b187716e9a2a4e688e3ff0e..a0c872560558ad1da824080fd5848d17a6744bd3 100644 (file)
@@ -78,7 +78,7 @@ InformationPanelContent::InformationPanelContent(QWidget* parent) :
     // delay. This prevents flickering if the new preview can be generated
     // within a very small timeframe.
     m_outdatedPreviewTimer = new QTimer(this);
-    m_outdatedPreviewTimer->setInterval(300);
+    m_outdatedPreviewTimer->setInterval(100);
     m_outdatedPreviewTimer->setSingleShot(true);
     connect(m_outdatedPreviewTimer, &QTimer::timeout,
             this, &InformationPanelContent::markOutdatedPreview);
@@ -189,12 +189,8 @@ void InformationPanelContent::refreshPixmapView()
 
     // Mark the currently shown preview as outdated. This is done
     // with a small delay to prevent a flickering when the next preview
-    // can be shown within a short timeframe. This timer is not started
-    // for directories, as directory previews might fail and return the
-    // same icon.
-    if (!m_item.isDir()) {
-        m_outdatedPreviewTimer->start();
-    }
+    // can be shown within a short timeframe.
+    m_outdatedPreviewTimer->start();
 
     QStringList plugins = KIO::PreviewJob::availablePlugins();
     m_previewJob = new KIO::PreviewJob(KFileItemList() << m_item,
@@ -221,7 +217,6 @@ void InformationPanelContent::refreshPreview()
     }
 
     m_preview->setCursor(Qt::ArrowCursor);
-    bool usePhonon = false;
     setNameLabelText(m_item.text());
     if (InformationPanelSettings::previewsShown()) {
 
@@ -229,11 +224,12 @@ void InformationPanelContent::refreshPreview()
         const bool isSearchUrl = itemUrl.scheme().contains(QLatin1String("search")) && m_item.localPath().isEmpty();
         if (isSearchUrl) {
             m_preview->show();
+            m_phononWidget->hide();
 
             // in the case of a search-URL the URL is not readable for humans
             // (at least not useful to show in the Information Panel)
             m_preview->setPixmap(
-                QIcon::fromTheme(QStringLiteral("baloo")).pixmap(KIconLoader::SizeEnormous, KIconLoader::SizeEnormous)
+                QIcon::fromTheme(QStringLiteral("baloo")).pixmap(m_preview->height(), m_preview->width())
             );
         } else {
 
@@ -242,12 +238,13 @@ void InformationPanelContent::refreshPreview()
             const QString mimeType = m_item.mimetype();
             const bool isAnimatedImage = m_preview->isAnimatedImage(itemUrl.toLocalFile());
             m_isVideo = !isAnimatedImage && mimeType.startsWith(QLatin1String("video/"));
-            usePhonon = m_isVideo || mimeType.startsWith(QLatin1String("audio/"));
+            bool usePhonon = m_isVideo || mimeType.startsWith(QLatin1String("audio/"));
 
             if (usePhonon) {
                 // change the cursor of the preview
                 m_preview->setCursor(Qt::PointingHandCursor);
                 m_preview->installEventFilter(m_phononWidget);
+                m_phononWidget->show();
 
                 // if the video is playing, has been paused or stopped
                 // we don't need to update the preview/phonon widget states
@@ -267,7 +264,6 @@ void InformationPanelContent::refreshPreview()
                         m_preview->show();
                     }
 
-                    m_phononWidget->show();
                     m_phononWidget->setUrl(m_item.targetUrl(), m_isVideo ? PhononWidget::MediaKind::Video : PhononWidget::MediaKind::Audio);
                     adjustWidgetSizes(parentWidget()->width());
                 }
@@ -314,7 +310,7 @@ void InformationPanelContent::showItems(const KFileItemList& items)
     m_preview->stopAnimatedImage();
 
     m_preview->setPixmap(
-        QIcon::fromTheme(QStringLiteral("dialog-information")).pixmap(KIconLoader::SizeEnormous, KIconLoader::SizeEnormous)
+        QIcon::fromTheme(QStringLiteral("dialog-information")).pixmap(m_preview->height(), m_preview->width())
     );
     setNameLabelText(i18ncp("@label", "%1 item selected", "%1 items selected", items.count()));
 
@@ -358,7 +354,7 @@ bool InformationPanelContent::eventFilter(QObject* obj, QEvent* event)
 void InformationPanelContent::showIcon(const KFileItem& item)
 {
     m_outdatedPreviewTimer->stop();
-    QPixmap pixmap = QIcon::fromTheme(item.iconName()).pixmap(KIconLoader::SizeEnormous, KIconLoader::SizeEnormous);
+    QPixmap pixmap = QIcon::fromTheme(item.iconName()).pixmap(m_preview->height(), m_preview->width());
     KIconLoader::global()->drawOverlays(item.overlays(), pixmap, KIconLoader::Desktop);
     m_preview->setPixmap(pixmap);
 }
@@ -411,11 +407,18 @@ void InformationPanelContent::showPreview(const KFileItem& item,
 
 void InformationPanelContent::markOutdatedPreview()
 {
-    KIconEffect *iconEffect = KIconLoader::global()->iconEffect();
-    QPixmap disabledPixmap = iconEffect->apply(m_preview->pixmap(),
-                                               KIconLoader::Desktop,
-                                               KIconLoader::DisabledState);
-    m_preview->setPixmap(disabledPixmap);
+    if (m_item.isDir()) {
+        // directory preview can be long
+        // but since we always have icons to display
+        // use it until the preview is done
+        showIcon(m_item);
+    } else {
+        KIconEffect *iconEffect = KIconLoader::global()->iconEffect();
+        QPixmap disabledPixmap = iconEffect->apply(m_preview->pixmap(),
+                                                   KIconLoader::Desktop,
+                                                   KIconLoader::DisabledState);
+        m_preview->setPixmap(disabledPixmap);
+    }
 }
 
 KFileItemList InformationPanelContent::items()
diff --git a/src/panels/terminal/org.kde.KIOFuse.VFS.xml b/src/panels/terminal/org.kde.KIOFuse.VFS.xml
new file mode 100644 (file)
index 0000000..56f753e
--- /dev/null
@@ -0,0 +1,13 @@
+<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
+<node>
+  <interface name="org.kde.KIOFuse.VFS">
+    <method name="mountUrl">
+      <arg name="remoteUrl" type="s" direction="in"/>
+      <arg type="s" direction="out"/>
+    </method>
+    <method name="remoteUrl">
+      <arg name="localUrl" type="s" direction="in"/>
+      <arg type="s" direction="out"/>
+    </method>
+  </interface>
+</node>
index 59b2694fb01aaf03cb0fe01702df5489afc8122c..ac0bcd0fee5c1ef9fadd3ed0b2946da9dc4b3565 100644 (file)
@@ -18,6 +18,7 @@
  ***************************************************************************/
 
 #include "terminalpanel.h"
+#include "kiofuse_interface.h"
 
 #include <KIO/DesktopExecParser>
 #include <KIO/Job>
@@ -25,6 +26,7 @@
 #include <KJobWidgets>
 #include <KLocalizedString>
 #include <KMessageWidget>
+#include <KMountPoint>
 #include <KParts/ReadOnlyPart>
 #include <KPluginFactory>
 #include <KPluginLoader>
@@ -50,7 +52,10 @@ TerminalPanel::TerminalPanel(QWidget* parent) :
     m_konsolePartMissingMessage(nullptr),
     m_konsolePart(nullptr),
     m_konsolePartCurrentDirectory(),
-    m_sendCdToTerminalHistory()
+    m_sendCdToTerminalHistory(),
+    m_kiofuseInterface(QStringLiteral("org.kde.KIOFuse"),
+                       QStringLiteral("/org/kde/KIOFuse"),
+                       QDBusConnection::sessionBus())
 {
     m_layout = new QVBoxLayout(this);
     m_layout->setContentsMargins(0, 0, 0, 0);
@@ -244,6 +249,19 @@ void TerminalPanel::slotMostLocalUrlResult(KJob* job)
     const QUrl url = statJob->mostLocalUrl();
     if (url.isLocalFile()) {
         sendCdToTerminal(url.toLocalFile());
+    } else {
+        // URL isn't local, only hope for the terminal to be in sync with the
+        // DolphinView is to mount the remote URL in KIOFuse and point to it.
+        // If we can't do that for any reason, silently fail.
+        auto reply = m_kiofuseInterface.mountUrl(url.toString());
+        QDBusPendingCallWatcher * watcher = new QDBusPendingCallWatcher(reply, this);
+        QObject::connect(watcher, &QDBusPendingCallWatcher::finished, this, [=] (QDBusPendingCallWatcher* watcher) {
+            watcher->deleteLater();
+            if (!reply.isError()) {
+                // Successfully mounted, point to the KIOFuse equivalent path.
+                sendCdToTerminal(reply.value());
+            }
+        });
     }
 
     m_mostLocalUrlJob = nullptr;
@@ -261,8 +279,31 @@ void TerminalPanel::slotKonsolePartCurrentDirectoryChanged(const QString& dir)
         }
     }
 
+    // User may potentially be browsing inside a KIOFuse mount.
+    // If so lets try and change the DolphinView to point to the remote URL equivalent.
+    // instead of into the KIOFuse mount itself (which can cause performance issues!)
     const QUrl url(QUrl::fromLocalFile(dir));
-    emit changeUrl(url);
+
+    KMountPoint::Ptr mountPoint = KMountPoint::currentMountPoints().findByPath(m_konsolePartCurrentDirectory);
+    if (mountPoint && mountPoint->mountType() != QStringLiteral("fuse.kio-fuse")) {
+        // Not in KIOFUse mount, so just switch to the corresponding URL.
+        emit changeUrl(url);
+        return;
+    }
+
+    auto reply = m_kiofuseInterface.remoteUrl(m_konsolePartCurrentDirectory);
+    QDBusPendingCallWatcher * watcher = new QDBusPendingCallWatcher(reply, this);
+    QObject::connect(watcher, &QDBusPendingCallWatcher::finished, this, [=] (QDBusPendingCallWatcher* watcher) {
+        watcher->deleteLater();
+        if (reply.isError()) {
+            // KIOFuse errored out... just show the normal URL
+            emit changeUrl(url);
+        } else {
+            // Our location happens to be in a KIOFuse mount and is mounted.
+            // Let's change the DolphinView to point to the remote URL equivalent.
+            emit changeUrl(QUrl::fromUserInput(reply.value()));
+        }
+    });
 }
 
 bool TerminalPanel::terminalHasFocus() const
index 6ab205fe32cb01828dff012bdfb49e6b8d59ad5e..661fee4d486d2374b6e9044819e6095cfa19a9d3 100644 (file)
@@ -21,6 +21,7 @@
 #define TERMINALPANEL_H
 
 #include "panels/panel.h"
+#include "kiofuse_interface.h"
 
 #include <QQueue>
 
@@ -101,6 +102,7 @@ private:
     KParts::ReadOnlyPart* m_konsolePart;
     QString m_konsolePartCurrentDirectory;
     QQueue<QString> m_sendCdToTerminalHistory;
+    org::kde::KIOFuse::VFS m_kiofuseInterface;
 };
 
 #endif // TERMINALPANEL_H
index 23f520de148a3f1ba710cb652828bb2de5830f81..cdc0718dfb321391ef25c9e8bd7e8412282dc8cf 100644 (file)
@@ -136,6 +136,7 @@ QUrl DolphinSearchBox::urlForSearching() const
         }
 
         query.addQueryItem(QStringLiteral("url"), searchPath().url());
+        query.addQueryItem(QStringLiteral("title"), queryTitle(m_searchInput->text()));
 
         url.setQuery(query);
     }
@@ -219,6 +220,9 @@ void DolphinSearchBox::keyReleaseEvent(QKeyEvent* event)
             m_searchInput->clear();
         }
     }
+    else if (event->key() == Qt::Key_Down) {
+        emit focusViewRequest();
+    }
 }
 
 bool DolphinSearchBox::eventFilter(QObject* obj, QEvent* event)
@@ -283,7 +287,7 @@ void DolphinSearchBox::slotSearchTextChanged(const QString& text)
 void DolphinSearchBox::slotReturnPressed()
 {
     emitSearchRequest();
-    emit returnPressed();
+    emit focusViewRequest();
 }
 
 void DolphinSearchBox::slotFacetChanged()
@@ -469,6 +473,12 @@ void DolphinSearchBox::init()
     connect(m_startSearchTimer, &QTimer::timeout, this, &DolphinSearchBox::emitSearchRequest);
 }
 
+QString DolphinSearchBox::queryTitle(const QString& text) const
+{
+    return i18nc("@title UDS_DISPLAY_NAME for a KIO directory listing. %1 is the query the user entered.",
+                             "Query Results from '%1'", text);
+}
+
 QUrl DolphinSearchBox::balooUrlForSearching() const
 {
 #ifdef HAVE_BALOO
@@ -491,8 +501,7 @@ QUrl DolphinSearchBox::balooUrlForSearching() const
 
     query.setSearchString(queryStrings.join(QLatin1Char(' ')));
 
-    return query.toSearchUrl(i18nc("@title UDS_DISPLAY_NAME for a KIO directory listing. %1 is the query the user entered.",
-                                   "Query Results from '%1'", text));
+    return query.toSearchUrl(queryTitle(text));
 #else
     return QUrl();
 #endif
@@ -541,7 +550,7 @@ bool DolphinSearchBox::isIndexingEnabled() const
 {
 #ifdef HAVE_BALOO
     const Baloo::IndexerConfig searchInfo;
-    return searchInfo.fileIndexingEnabled() && searchInfo.shouldBeIndexed(searchPath().toLocalFile());
+    return searchInfo.fileIndexingEnabled() && !searchPath().isEmpty() && searchInfo.shouldBeIndexed(searchPath().toLocalFile());
 #else
     return false;
 #endif
index 5fef4ec5a7bdd5464e27818b762ea1456b754d6c..4afd752bc15b00680ffb65981a826eaabed3a61c 100644 (file)
@@ -118,8 +118,6 @@ signals:
      */
     void searchTextChanged(const QString& text);
 
-    void returnPressed();
-
     /**
      * Emitted as soon as the search box should get closed.
      */
@@ -131,6 +129,7 @@ signals:
      * @see DolphinSearchBox::setActive()
      */
     void activated();
+    void focusViewRequest();
 
 private slots:
     void emitSearchRequest();
@@ -162,6 +161,8 @@ private:
     bool isIndexingEnabled() const;
 
 private:
+    QString queryTitle(const QString& text) const;
+
     bool m_startedSearching;
     bool m_active;
 
index e9a8fb28d26de61a0a3ba4e87a76491a902f3e50..6ef344ac4f27a74d47dc667e028351a1d772d6b3 100644 (file)
             <label>Expandable folders</label>
             <default>true</default>
         </entry>
+        <entry name="DirectorySizeCount" type="Bool">
+            <label>Whether or not content count is use as directory size</label>
+            <default>true</default>
+        </entry>
+        <entry name="RecursiveDirectorySizeLimit" type="UInt">
+            <label>Recursive directory size limit</label>
+            <default>10</default>
+        </entry>
     </group>
 </kcfg>
index fca70656d0dc651df1d058599db1fe11abe8de5c..c397b2945124a49ab4867ff608337842c8d75e3d 100644 (file)
             <label>Home URL</label>
             <default code="true">QUrl::fromLocalFile(QDir::homePath()).toDisplayString(QUrl::PreferLocalFile)</default>
         </entry>
+        <entry name="RememberOpenedTabs" type="Bool">
+            <label>Remember open folders and tabs</label>
+            <default>true</default>
+        </entry>
         <entry name="SplitView" type="Bool">
             <label>Split the view into two panes</label>
             <default>false</default>
index a82cb3858c409609fedb07b468ed34a7a427d39e..39eccff76fab0ab3df20bef6b0293c2978160afc 100644 (file)
 
 K_PLUGIN_FACTORY(KCMDolphinGeneralConfigFactory, registerPlugin<DolphinGeneralConfigModule>(QStringLiteral("dolphingeneral"));)
 
-DolphinGeneralConfigModule::DolphinGeneralConfigModule(QWidget* parent, const QVariantList& args) :
-    KCModule(parent),
+DolphinGeneralConfigModule::DolphinGeneralConfigModule(QWidget *parent, const QVariantList &args) :
+    KCModule(parent, args),
     m_pages()
 {
-    Q_UNUSED(args)
-
     setButtons(KCModule::Default | KCModule::Help);
 
     QVBoxLayout* topLayout = new QVBoxLayout(this);
@@ -49,29 +47,17 @@ DolphinGeneralConfigModule::DolphinGeneralConfigModule(QWidget* parent, const QV
     // initialize 'Behavior' tab
     BehaviorSettingsPage* behaviorPage = new BehaviorSettingsPage(QUrl::fromLocalFile(QDir::homePath()), tabWidget);
     tabWidget->addTab(behaviorPage, i18nc("@title:tab Behavior settings", "Behavior"));
-#if KCONFIGWIDGETS_VERSION < QT_VERSION_CHECK(5, 64, 0)
-    connect(behaviorPage, &BehaviorSettingsPage::changed, this, QOverload<>::of(&DolphinGeneralConfigModule::changed));
-#else
     connect(behaviorPage, &BehaviorSettingsPage::changed, this, &DolphinGeneralConfigModule::markAsChanged);
-#endif
 
     // initialize 'Previews' tab
     PreviewsSettingsPage* previewsPage = new PreviewsSettingsPage(tabWidget);
     tabWidget->addTab(previewsPage, i18nc("@title:tab Previews settings", "Previews"));
-#if KCONFIGWIDGETS_VERSION < QT_VERSION_CHECK(5, 64, 0)
-    connect(previewsPage, &PreviewsSettingsPage::changed, this, QOverload<>::of(&DolphinGeneralConfigModule::changed));
-#else
     connect(previewsPage, &PreviewsSettingsPage::changed, this, &DolphinGeneralConfigModule::markAsChanged);
-#endif
 
     // initialize 'Confirmations' tab
     ConfirmationsSettingsPage* confirmationsPage = new ConfirmationsSettingsPage(tabWidget);
     tabWidget->addTab(confirmationsPage,  i18nc("@title:tab Confirmations settings", "Confirmations"));
-#if KCONFIGWIDGETS_VERSION < QT_VERSION_CHECK(5, 64, 0)
-    connect(confirmationsPage, &ConfirmationsSettingsPage::changed, this, QOverload<>::of(&DolphinGeneralConfigModule::changed));
-#else
     connect(confirmationsPage, &ConfirmationsSettingsPage::changed, this, &DolphinGeneralConfigModule::markAsChanged);
-#endif
     m_pages.append(behaviorPage);
     m_pages.append(previewsPage);
     m_pages.append(confirmationsPage);
@@ -85,14 +71,14 @@ DolphinGeneralConfigModule::~DolphinGeneralConfigModule()
 
 void DolphinGeneralConfigModule::save()
 {
-    foreach (SettingsPageBase* page, m_pages) {
+    for (SettingsPageBase* page : qAsConst(m_pages)) {
         page->applySettings();
     }
 }
 
 void DolphinGeneralConfigModule::defaults()
 {
-    foreach (SettingsPageBase* page, m_pages) {
+    for (SettingsPageBase* page : qAsConst(m_pages)) {
         page->applySettings();
     }
 }
index c542c0139545bcb9e6ad441832bc17467b3e47b0..2b60c7591d11e94fceda3f1603cbd3c8fdca310c 100644 (file)
@@ -34,7 +34,7 @@ class DolphinGeneralConfigModule : public KCModule
     Q_OBJECT
 
 public:
-    DolphinGeneralConfigModule(QWidget* parent, const QVariantList& args);
+    DolphinGeneralConfigModule(QWidget *parent, const QVariantList &args);
     ~DolphinGeneralConfigModule() override;
 
     void save() override;
index 2cdabdeee0e22d124cb65b58356f1c4328a59f18..f8de4eed22e2c34ae0f9a18c00ca252c0b0fede9 100644 (file)
 
 K_PLUGIN_FACTORY(KCMDolphinNavigationConfigFactory, registerPlugin<DolphinNavigationConfigModule>(QStringLiteral("dolphinnavigation"));)
 
-DolphinNavigationConfigModule::DolphinNavigationConfigModule(QWidget* parent, const QVariantList& args) :
-    KCModule(parent),
+DolphinNavigationConfigModule::DolphinNavigationConfigModule(QWidget *parent, const QVariantList &args) :
+    KCModule(parent, args),
     m_navigation(nullptr)
 {
-    Q_UNUSED(args)
-
     setButtons(KCModule::Default | KCModule::Help);
 
     QVBoxLayout* topLayout = new QVBoxLayout(this);
     topLayout->setContentsMargins(0, 0, 0, 0);
 
     m_navigation = new NavigationSettingsPage(this);
-#if KCONFIGWIDGETS_VERSION < QT_VERSION_CHECK(5, 64, 0)
-    connect(m_navigation, &NavigationSettingsPage::changed, this, QOverload<>::of(&DolphinNavigationConfigModule::changed));
-#else
     connect(m_navigation, &NavigationSettingsPage::changed, this, &DolphinNavigationConfigModule::markAsChanged);
-#endif
     topLayout->addWidget(m_navigation, 0, {});
 }
 
index 2bcc7ababe2ae7ac2315c1384188c5c0d72a9171..7eb6b26e78b044a73efdd9d9f0f7f02fe88aa393 100644 (file)
@@ -32,14 +32,14 @@ class DolphinNavigationConfigModule : public KCModule
     Q_OBJECT
 
 public:
-    DolphinNavigationConfigModule(QWidget* parent, const QVariantList& args);
+    DolphinNavigationConfigModule(QWidget *parent, const QVariantList &args);
     ~DolphinNavigationConfigModule() override;
 
     void save() override;
     void defaults() override;
 
 private:
-    NavigationSettingsPagem_navigation;
+    NavigationSettingsPage *m_navigation;
 };
 
 #endif
index e6a8867d7dde1754f0d9f88d368e308d0f098d14..92e71bae0eb8da71a2467533f529d760768a37c0 100644 (file)
 K_PLUGIN_FACTORY(KCMDolphinServicesConfigFactory, registerPlugin<DolphinServicesConfigModule>(QStringLiteral("dolphinservices"));)
 
 DolphinServicesConfigModule::DolphinServicesConfigModule(QWidget* parent, const QVariantList& args) :
-    KCModule(parent),
+    KCModule(parent, args),
     m_services(nullptr)
 {
-    Q_UNUSED(args)
-
     setButtons(KCModule::Default | KCModule::Help);
 
     QVBoxLayout* topLayout = new QVBoxLayout(this);
     topLayout->setContentsMargins(0, 0, 0, 0);
 
     m_services = new ServicesSettingsPage(this);
-#if KCONFIGWIDGETS_VERSION < QT_VERSION_CHECK(5, 64, 0)
-    connect(m_services, &ServicesSettingsPage::changed, this, QOverload<>::of(&DolphinServicesConfigModule::changed));
-#else
     connect(m_services, &ServicesSettingsPage::changed, this, &DolphinServicesConfigModule::markAsChanged);
-#endif
     topLayout->addWidget(m_services, 0, {});
 }
 
index 6c6af6728a12f8beee2a7ee4579089886515af98..a567450ca512d04e3a1647109b4afb4a185e32d1 100644 (file)
@@ -39,7 +39,7 @@ public:
     void defaults() override;
 
 private:
-    ServicesSettingsPagem_services;
+    ServicesSettingsPage *m_services;
 };
 
 #endif
index 4fac1160073a7128d47ee0258fcf06a33b8f3c2a..91abe5cd42f34ce43ab4b949fc1e245ec8e9a133 100644 (file)
 
 K_PLUGIN_FACTORY(KCMDolphinViewModesConfigFactory, registerPlugin<DolphinViewModesConfigModule>(QStringLiteral("dolphinviewmodes"));)
 
-DolphinViewModesConfigModule::DolphinViewModesConfigModule(QWidget* parent, const QVariantList& args) :
-    KCModule(parent),
+DolphinViewModesConfigModule::DolphinViewModesConfigModule(QWidget *parent, const QVariantList &args) :
+    KCModule(parent, args),
     m_tabs()
 {
-    Q_UNUSED(args)
-
     setButtons(KCModule::Default | KCModule::Help);
 
     QVBoxLayout* topLayout = new QVBoxLayout(this);
@@ -74,7 +72,7 @@ DolphinViewModesConfigModule::~DolphinViewModesConfigModule()
 
 void DolphinViewModesConfigModule::save()
 {
-    foreach (ViewSettingsTab* tab, m_tabs) {
+    for (ViewSettingsTab *tab : qAsConst(m_tabs)) {
         tab->applySettings();
     }
     reparseConfiguration();
@@ -82,7 +80,7 @@ void DolphinViewModesConfigModule::save()
 
 void DolphinViewModesConfigModule::defaults()
 {
-    foreach (ViewSettingsTab* tab, m_tabs) {
+    for (ViewSettingsTab *tab : qAsConst(m_tabs)) {
         tab->restoreDefaultSettings();
     }
     reparseConfiguration();
@@ -90,13 +88,15 @@ void DolphinViewModesConfigModule::defaults()
 
 void DolphinViewModesConfigModule::reparseConfiguration()
 {
-    QDBusMessage message = QDBusMessage::createSignal(QStringLiteral("/KonqMain"), QStringLiteral("org.kde.Konqueror.Main"), QStringLiteral("reparseConfiguration"));
+    QDBusMessage message = QDBusMessage::createSignal(QStringLiteral("/KonqMain"),
+                                                      QStringLiteral("org.kde.Konqueror.Main"),
+                                                      QStringLiteral("reparseConfiguration"));
     QDBusConnection::sessionBus().send(message);
 }
 
 void DolphinViewModesConfigModule::viewModeChanged()
 {
-    emit changed(true);
+    emit markAsChanged();
 }
 
 #include "kcmdolphinviewmodes.moc"
index c3775adff9d1b90c27ff719f7e918e4816aa4fe5..40965b0e6832e3441f9775631ffd513e86046efc 100644 (file)
@@ -32,7 +32,7 @@ class DolphinViewModesConfigModule : public KCModule
     Q_OBJECT
 
 public:
-    DolphinViewModesConfigModule(QWidget* parent, const QVariantList& args);
+    DolphinViewModesConfigModule(QWidget *parent, const QVariantList &args);
     ~DolphinViewModesConfigModule() override;
 
     void save() override;
index 9888999368267e519553bc125e1d4c3adec2e440..46b1590794f6be249d49442f58576429c2531423 100644 (file)
@@ -6,5 +6,10 @@ target_link_libraries(servicemenuinstaller PRIVATE
     Qt5::Core
     Qt5::Gui
     KF5::I18n
+    KF5::CoreAddons
 )
+
+if(HAVE_PACKAGEKIT)
+    target_link_libraries(servicemenuinstaller PRIVATE PK::packagekitqt5)
+endif()
 install(TARGETS servicemenuinstaller ${KDE_INSTALL_TARGETS_DEFAULT_ARGS})
index 06f34c6b932be4a252ba5454fa35bb467082c3f7..89f2545f80b9de6982a97685fda1abbfdf0bd652 100644 (file)
 
 #include <QDebug>
 #include <QProcess>
+#include <QTimer>
 #include <QStandardPaths>
 #include <QDir>
 #include <QDirIterator>
 #include <QCommandLineParser>
 #include <QMimeDatabase>
 #include <QUrl>
-#include <QDesktopServices>
 #include <QGuiApplication>
-
 #include <KLocalizedString>
+#include <KShell>
+
+#include "../../../config-packagekit.h"
+
+const static QStringList binaryPackages = {QStringLiteral("application/vnd.debian.binary-package"),
+                                     QStringLiteral("application/x-rpm"),
+                                     QStringLiteral("application/x-xz"),
+                                     QStringLiteral("application/zstd")};
+enum PackageOperation {
+    Install,
+    Uninstall
+};
+
+#ifdef HAVE_PACKAGEKIT
+#include <PackageKit/Daemon>
+#include <PackageKit/Details>
+#include <PackageKit/Transaction>
+#else
+#include <QDesktopServices>
+#endif
 
 // @param msg Error that gets logged to CLI
 Q_NORETURN void fail(const QString &str)
 {
     qCritical() << str;
-
-    QProcess process;
-    const QStringList args = {"--passivepopup", i18n("Dolphin service menu installation failed"), "15"};
-    process.start("kdialog", args, QIODevice::ReadOnly);
-    if (!process.waitForStarted()) {
-        qFatal("Failed to run kdialog");
-    }
+    const QStringList args = {"--detailederror" ,i18n("Dolphin service menu installation failed"),  str};
+    QProcess::startDetached("kdialog", args);
 
     exit(1);
 }
@@ -52,6 +66,84 @@ QString getServiceMenusDir()
     return QDir(dataLocation).absoluteFilePath("kservices5/ServiceMenus");
 }
 
+#ifdef HAVE_PACKAGEKIT
+void packageKitInstall(const QString &fileName)
+{
+    PackageKit::Transaction *transaction = PackageKit::Daemon::installFile(fileName);
+
+    const auto exitWithError = [=](PackageKit::Transaction::Error, const QString &details) {
+       fail(details);
+    };
+
+    QObject::connect(transaction, &PackageKit::Transaction::finished,
+                     [=](PackageKit::Transaction::Exit status, uint) {
+                        if (status == PackageKit::Transaction::ExitSuccess) {
+                            exit(0);
+                        }
+                        // Fallback error handling
+                        QTimer::singleShot(500, [=](){
+                            fail(i18n("Failed to install \"%1\", exited with status \"%2\"",
+                                      fileName, QVariant::fromValue(status).toString()));
+                        });
+                    });
+    QObject::connect(transaction, &PackageKit::Transaction::errorCode, exitWithError);
+}
+
+void packageKitUninstall(const QString &fileName)
+{
+    const auto exitWithError = [=](PackageKit::Transaction::Error, const QString &details) {
+        fail(details);
+    };
+    const auto uninstallLambda = [=](PackageKit::Transaction::Exit status, uint) {
+        if (status == PackageKit::Transaction::ExitSuccess) {
+            exit(0);
+        }
+    };
+
+    PackageKit::Transaction *transaction = PackageKit::Daemon::getDetailsLocal(fileName);
+    QObject::connect(transaction, &PackageKit::Transaction::details,
+                     [=](const PackageKit::Details &details) {
+                         PackageKit::Transaction *transaction = PackageKit::Daemon::removePackage(details.packageId());
+                         QObject::connect(transaction, &PackageKit::Transaction::finished, uninstallLambda);
+                         QObject::connect(transaction, &PackageKit::Transaction::errorCode, exitWithError);
+                     });
+
+    QObject::connect(transaction, &PackageKit::Transaction::errorCode, exitWithError);
+    // Fallback error handling
+    QObject::connect(transaction, &PackageKit::Transaction::finished,
+        [=](PackageKit::Transaction::Exit status, uint) {
+            if (status != PackageKit::Transaction::ExitSuccess) {
+                QTimer::singleShot(500, [=]() {
+                    fail(i18n("Failed to uninstall \"%1\", exited with status \"%2\"",
+                              fileName, QVariant::fromValue(status).toString()));
+                });
+            }
+        });
+    }
+#endif
+
+Q_NORETURN void packageKit(PackageOperation operation, const QString &fileName)
+{
+#ifdef HAVE_PACKAGEKIT
+    QFileInfo fileInfo(fileName);
+    if (!fileInfo.exists()) {
+        fail(i18n("The file does not exist!"));
+    }
+    const QString absPath = fileInfo.absoluteFilePath();
+    if (operation == PackageOperation::Install) {
+        packageKitInstall(absPath);
+    } else {
+        packageKitUninstall(absPath);
+    }
+    QGuiApplication::exec(); // For event handling, no return after signals finish
+    fail(i18n("Unknown error when installing package"));
+#else
+    Q_UNUSED(operation)
+    QDesktopServices::openUrl(QUrl(fileName));
+    exit(0);
+#endif
+}
+
 struct UncompressCommand
 {
     QString command;
@@ -59,6 +151,11 @@ struct UncompressCommand
     QStringList args2;
 };
 
+enum ScriptExecution{
+    Process,
+    Konsole
+};
+
 void runUncompress(const QString &inputPath, const QString &outputPath)
 {
     QVector<QPair<QStringList, UncompressCommand>> mimeTypeToCommand;
@@ -126,12 +223,24 @@ QString findRecursive(const QString &dir, const QString &basename)
     return QString();
 }
 
-bool runScriptOnce(const QString &path, const QStringList &args)
+bool runScriptOnce(const QString &path, const QStringList &args, ScriptExecution execution)
 {
     QProcess process;
     process.setWorkingDirectory(QFileInfo(path).absolutePath());
 
-    process.start(path, args, QIODevice::NotOpen);
+    const static bool konsoleAvailable = !QStandardPaths::findExecutable("konsole").isEmpty();
+    if (konsoleAvailable && execution == ScriptExecution::Konsole) {
+        QString bashCommand = KShell::quoteArg(path) + ' ';
+        if (!args.isEmpty()) {
+            bashCommand.append(args.join(' '));
+        }
+        bashCommand.append("|| $SHELL");
+        // If the install script fails a shell opens and the user can fix the problem
+        // without an error konsole closes
+        process.start("konsole", QStringList() << "-e" << "bash" << "-c" << bashCommand, QIODevice::NotOpen);
+    } else {
+        process.start(path, args, QIODevice::NotOpen);
+    }
     if (!process.waitForStarted()) {
         fail(i18n("Failed to run installer script %1", path));
     }
@@ -163,11 +272,11 @@ bool runScriptVariants(const QString &path, bool hasArgVariants, const QStringLi
     qInfo() << "[servicemenuinstaller]: Trying to run installer/uninstaller" << path;
     if (hasArgVariants) {
         for (const auto &arg : argVariants) {
-            if (runScriptOnce(path, {arg})) {
+            if (runScriptOnce(path, {arg}, ScriptExecution::Process)) {
                 return true;
             }
         }
-    } else if (runScriptOnce(path, {})) {
+    } else if (runScriptOnce(path, {}, ScriptExecution::Konsole)) {
         return true;
     }
 
@@ -202,9 +311,8 @@ bool cmdInstall(const QString &archive, QString &errorText)
             return false;
         }
     } else {
-        const QStringList binaryPackages = {"application/vnd.debian.binary-package", "application/x-rpm"};
         if (binaryPackages.contains(QMimeDatabase().mimeTypeForFile(archive).name())) {
-            return QDesktopServices::openUrl(QUrl(archive));
+            packageKit(PackageOperation::Install, archive);
         }
         const QString dir = generateDirPath(archive);
         if (QFile::exists(dir)) {
@@ -247,7 +355,11 @@ bool cmdInstall(const QString &archive, QString &errorText)
         }
 
         if (!installerPath.isEmpty()) {
-            return runScriptVariants(installerPath, true, {"--local", "--local-install", "--install"}, errorText);
+            // Try to run script without variants first
+            if (!runScriptVariants(installerPath, false, {}, errorText)) {
+                return runScriptVariants(installerPath, true, {"--local", "--local-install", "--install"}, errorText);
+            }
+            return true;
         }
 
         fail(i18n("Failed to find an installation script in %1", dir));
@@ -268,6 +380,9 @@ bool cmdUninstall(const QString &archive, QString &errorText)
             return false;
         }
     } else {
+        if (binaryPackages.contains(QMimeDatabase().mimeTypeForFile(archive).name())) {
+            packageKit(PackageOperation::Uninstall, archive);
+        }
         const QString dir = generateDirPath(archive);
 
         // Try "deinstall" first
index fe900bc5c9943ff50cc4ddcaf7777beb25e42a55..ba6c48f80bc8366c4232ca56a480dd8306a2b3eb 100644 (file)
@@ -38,6 +38,7 @@
 #include <QListWidget>
 #include <QShowEvent>
 #include <QSortFilterProxyModel>
+#include <QLineEdit>
 
 namespace
 {
@@ -61,6 +62,11 @@ ServicesSettingsPage::ServicesSettingsPage(QWidget* parent) :
                                      "Select which services should "
                                      "be shown in the context menu:"), this);
     label->setWordWrap(true);
+    m_searchLineEdit = new QLineEdit(this);
+    m_searchLineEdit->setPlaceholderText(i18nc("@label:textbox", "Search..."));
+    connect(m_searchLineEdit, &QLineEdit::textChanged, this, [=](const QString &filter){
+        m_sortModel->setFilterFixedString(filter);
+    });
 
     m_listView = new QListView(this);
     ServiceItemDelegate* delegate = new ServiceItemDelegate(m_listView, m_listView);
@@ -69,6 +75,8 @@ ServicesSettingsPage::ServicesSettingsPage(QWidget* parent) :
     m_sortModel->setSourceModel(m_serviceModel);
     m_sortModel->setSortRole(Qt::DisplayRole);
     m_sortModel->setSortLocaleAware(true);
+    m_sortModel->setFilterRole(Qt::DisplayRole);
+    m_sortModel->setFilterCaseSensitivity(Qt::CaseInsensitive);
     m_listView->setModel(m_sortModel);
     m_listView->setItemDelegate(delegate);
     m_listView->setVerticalScrollMode(QListView::ScrollPerPixel);
@@ -80,6 +88,7 @@ ServicesSettingsPage::ServicesSettingsPage(QWidget* parent) :
     connect(downloadButton, &KNS3::Button::dialogFinished, this, &ServicesSettingsPage::loadServices);
 
     topLayout->addWidget(label);
+    topLayout->addWidget(m_searchLineEdit);
     topLayout->addWidget(m_listView);
     topLayout->addWidget(downloadButton);
 
@@ -236,6 +245,7 @@ void ServicesSettingsPage::loadServices()
     }
 
     m_sortModel->sort(Qt::DisplayRole);
+    m_searchLineEdit->setFocus(Qt::OtherFocusReason);
 }
 
 void ServicesSettingsPage::loadVersionControlSystems()
index cd4cbe52fcc5c463e640b67f53bed514ba2234e1..dd53cf5dd6f7f59440485e989aa0ebd4393cf214 100644 (file)
@@ -26,6 +26,7 @@
 class QListView;
 class QSortFilterProxyModel;
 class ServiceModel;
+class QLineEdit;
 
 /**
  * @brief Page for the 'Services' settings of the Dolphin settings dialog.
@@ -74,6 +75,7 @@ private:
     ServiceModel* m_serviceModel;
     QSortFilterProxyModel* m_sortModel;
     QListView* m_listView;
+    QLineEdit *m_searchLineEdit;
     QStringList m_enabledVcsPlugins;
 };
 
index d7d5fba4cc1e954e925a4e6f7a9004439f56102d..eb149574659dfb82f9cf2cebe49c7330cd0fb254 100644 (file)
 #include <KLocalizedString>
 #include <KMessageBox>
 
+#include <QButtonGroup>
 #include <QCheckBox>
 #include <QFileDialog>
 #include <QLineEdit>
 #include <QPushButton>
+#include <QRadioButton>
 #include <QFormLayout>
+#include <QGridLayout>
 #include <QHBoxLayout>
-#include <QVBoxLayout>
 
 StartupSettingsPage::StartupSettingsPage(const QUrl& url, QWidget* parent) :
     SettingsPageBase(parent),
     m_url(url),
     m_homeUrl(nullptr),
+    m_homeUrlBoxLayoutContainer(nullptr),
+    m_buttonBoxLayoutContainer(nullptr),
+    m_rememberOpenedTabsRadioButton(nullptr),
+    m_homeUrlRadioButton(nullptr),
     m_splitView(nullptr),
     m_editableUrl(nullptr),
     m_showFullPath(nullptr),
@@ -48,9 +54,19 @@ StartupSettingsPage::StartupSettingsPage(const QUrl& url, QWidget* parent) :
 {
     QFormLayout* topLayout = new QFormLayout(this);
 
+    m_rememberOpenedTabsRadioButton = new QRadioButton(i18nc("@option:radio Startup Settings", "Folders, tabs, and window state from last time"));
+    m_homeUrlRadioButton = new QRadioButton();
+    // HACK: otherwise the radio button has too much spacing in a grid layout
+    m_homeUrlRadioButton->setMaximumWidth(24);
+
+    QButtonGroup* initialViewGroup = new QButtonGroup(this);
+    initialViewGroup->addButton(m_rememberOpenedTabsRadioButton);
+    initialViewGroup->addButton(m_homeUrlRadioButton);
+
 
     // create 'Home URL' editor
-    QHBoxLayout* homeUrlBoxLayout = new QHBoxLayout();
+    m_homeUrlBoxLayoutContainer = new QWidget(this);
+    QHBoxLayout* homeUrlBoxLayout = new QHBoxLayout(m_homeUrlBoxLayoutContainer);
     homeUrlBoxLayout->setContentsMargins(0, 0, 0, 0);
 
     m_homeUrl = new QLineEdit();
@@ -67,7 +83,8 @@ StartupSettingsPage::StartupSettingsPage(const QUrl& url, QWidget* parent) :
     connect(selectHomeUrlButton, &QPushButton::clicked,
             this, &StartupSettingsPage::selectHomeUrl);
 
-    QHBoxLayout* buttonBoxLayout = new QHBoxLayout();
+    m_buttonBoxLayoutContainer = new QWidget(this);
+    QHBoxLayout* buttonBoxLayout = new QHBoxLayout(m_buttonBoxLayoutContainer);
     buttonBoxLayout->setContentsMargins(0, 0, 0, 0);
 
     QPushButton* useCurrentButton = new QPushButton(i18nc("@action:button", "Use Current Location"));
@@ -79,41 +96,50 @@ StartupSettingsPage::StartupSettingsPage(const QUrl& url, QWidget* parent) :
     connect(useDefaultButton, &QPushButton::clicked,
             this, &StartupSettingsPage::useDefaultLocation);
 
-    QVBoxLayout* homeBoxLayout = new QVBoxLayout();
-    homeBoxLayout->setContentsMargins(0, 0, 0, 0);
-    homeBoxLayout->addLayout(homeUrlBoxLayout);
-    homeBoxLayout->addLayout(buttonBoxLayout);
+    QGridLayout* startInLocationLayout = new QGridLayout();
+    startInLocationLayout->setHorizontalSpacing(0);
+    startInLocationLayout->setContentsMargins(0, 0, 0, 0);
+    startInLocationLayout->addWidget(m_homeUrlRadioButton, 0, 0);
+    startInLocationLayout->addWidget(m_homeUrlBoxLayoutContainer, 0, 1);
+    startInLocationLayout->addWidget(m_buttonBoxLayoutContainer, 1, 1);
 
-    topLayout->addRow(i18nc("@label:textbox", "Start in:"), homeBoxLayout);
+    topLayout->addRow(i18nc("@label:textbox", "Show on startup:"), m_rememberOpenedTabsRadioButton);
+    topLayout->addRow(QString(), startInLocationLayout);
 
 
     topLayout->addItem(new QSpacerItem(0, Dolphin::VERTICAL_SPACER_HEIGHT, QSizePolicy::Fixed, QSizePolicy::Fixed));
 
-
-    // create 'Split view', 'Show full path', 'Editable location' and 'Filter bar' checkboxes
-    m_splitView = new QCheckBox(i18nc("@option:check Startup Settings", "Split view mode"));
-    topLayout->addRow(i18nc("@label:checkbox", "Window options:"), m_splitView);
-    m_editableUrl = new QCheckBox(i18nc("@option:check Startup Settings", "Editable location bar"));
+    m_splitView = new QCheckBox(i18nc("@option:check Startup Settings", "Begin in split view mode"));
+    topLayout->addRow(i18n("New windows:"), m_splitView);
+    m_filterBar = new QCheckBox(i18nc("@option:check Startup Settings", "Show filter bar"));
+    topLayout->addRow(QString(), m_filterBar);
+    m_editableUrl = new QCheckBox(i18nc("@option:check Startup Settings", "Make location bar editable"));
     topLayout->addRow(QString(), m_editableUrl);
+
+    topLayout->addItem(new QSpacerItem(0, Dolphin::VERTICAL_SPACER_HEIGHT, QSizePolicy::Fixed, QSizePolicy::Fixed));
+
+    m_openExternallyCalledFolderInNewTab = new QCheckBox(i18nc("@option:check Startup Settings", "Open new folders in tabs"));
+    topLayout->addRow(i18nc("@label:checkbox", "General:"), m_openExternallyCalledFolderInNewTab);
     m_showFullPath = new QCheckBox(i18nc("@option:check Startup Settings", "Show full path inside location bar"));
     topLayout->addRow(QString(), m_showFullPath);
-    m_filterBar = new QCheckBox(i18nc("@option:check Startup Settings", "Show filter bar"));
-    topLayout->addRow(QString(), m_filterBar);
     m_showFullPathInTitlebar = new QCheckBox(i18nc("@option:check Startup Settings", "Show full path in title bar"));
     topLayout->addRow(QString(), m_showFullPathInTitlebar);
-    m_openExternallyCalledFolderInNewTab = new QCheckBox(i18nc("@option:check Startup Settings", "Open new folders in tabs"));
-    topLayout->addRow(QString(), m_openExternallyCalledFolderInNewTab);
-
 
     loadSettings();
 
+    updateInitialViewOptions();
+
     connect(m_homeUrl, &QLineEdit::textChanged, this, &StartupSettingsPage::slotSettingsChanged);
+    connect(m_rememberOpenedTabsRadioButton, &QRadioButton::toggled, this, &StartupSettingsPage::slotSettingsChanged);
+    connect(m_homeUrlRadioButton, &QRadioButton::toggled, this, &StartupSettingsPage::slotSettingsChanged);
+
     connect(m_splitView,    &QCheckBox::toggled, this, &StartupSettingsPage::slotSettingsChanged);
     connect(m_editableUrl,  &QCheckBox::toggled, this, &StartupSettingsPage::slotSettingsChanged);
-    connect(m_showFullPath, &QCheckBox::toggled, this, &StartupSettingsPage::slotSettingsChanged);
     connect(m_filterBar,    &QCheckBox::toggled, this, &StartupSettingsPage::slotSettingsChanged);
-    connect(m_showFullPathInTitlebar, &QCheckBox::toggled, this, &StartupSettingsPage::slotSettingsChanged);
+
     connect(m_openExternallyCalledFolderInNewTab, &QCheckBox::toggled, this, &StartupSettingsPage::slotSettingsChanged);
+    connect(m_showFullPath, &QCheckBox::toggled, this, &StartupSettingsPage::slotSettingsChanged);
+    connect(m_showFullPathInTitlebar, &QCheckBox::toggled, this, &StartupSettingsPage::slotSettingsChanged);
 }
 
 StartupSettingsPage::~StartupSettingsPage()
@@ -132,12 +158,21 @@ void StartupSettingsPage::applySettings()
         KMessageBox::error(this, i18nc("@info", "The location for the home folder is invalid or does not exist, it will not be applied."));
     }
 
+    // Remove saved state if "remember open tabs" has been turned off
+    if (!m_rememberOpenedTabsRadioButton->isChecked()) {
+        KConfigGroup windowState{KSharedConfig::openConfig(QStringLiteral("dolphinrc")), "WindowState"};
+        if (windowState.exists()) {
+            windowState.deleteGroup();
+        }
+    }
+
+    settings->setRememberOpenedTabs(m_rememberOpenedTabsRadioButton->isChecked());
     settings->setSplitView(m_splitView->isChecked());
     settings->setEditableUrl(m_editableUrl->isChecked());
-    settings->setShowFullPath(m_showFullPath->isChecked());
     settings->setFilterBar(m_filterBar->isChecked());
-    settings->setShowFullPathInTitlebar(m_showFullPathInTitlebar->isChecked());
     settings->setOpenExternallyCalledFolderInNewTab(m_openExternallyCalledFolderInNewTab->isChecked());
+    settings->setShowFullPath(m_showFullPath->isChecked());
+    settings->setShowFullPathInTitlebar(m_showFullPathInTitlebar->isChecked());
     settings->save();
 }
 
@@ -155,9 +190,18 @@ void StartupSettingsPage::slotSettingsChanged()
     // to apply the startup settings only if they have been explicitly changed by the user
     // (see bug #254947).
     GeneralSettings::setModifiedStartupSettings(true);
+
+    // Enable and disable home URL controls appropriately
+    updateInitialViewOptions();
     emit changed();
 }
 
+void StartupSettingsPage::updateInitialViewOptions()
+{
+    m_homeUrlBoxLayoutContainer->setEnabled(m_homeUrlRadioButton->isChecked());
+    m_buttonBoxLayoutContainer->setEnabled(m_homeUrlRadioButton->isChecked());
+}
+
 void StartupSettingsPage::selectHomeUrl()
 {
     const QUrl homeUrl(QUrl::fromUserInput(m_homeUrl->text(), QString(), QUrl::AssumeLocalFile));
@@ -182,6 +226,8 @@ void StartupSettingsPage::loadSettings()
 {
     const QUrl url(Dolphin::homeUrl());
     m_homeUrl->setText(url.toDisplayString(QUrl::PreferLocalFile));
+    m_rememberOpenedTabsRadioButton->setChecked(GeneralSettings::rememberOpenedTabs());
+    m_homeUrlRadioButton->setChecked(!GeneralSettings::rememberOpenedTabs());
     m_splitView->setChecked(GeneralSettings::splitView());
     m_editableUrl->setChecked(GeneralSettings::editableUrl());
     m_showFullPath->setChecked(GeneralSettings::showFullPath());
index a5e0b236f415f7551da914074e80511188bfdd66..d1c937f1fde58c71d1b1303f4726f642a2f89562 100644 (file)
@@ -23,8 +23,9 @@
 
 #include <QUrl>
 
-class QLineEdit;
 class QCheckBox;
+class QLineEdit;
+class QRadioButton;
 
 /**
  * @brief Page for the 'Startup' settings of the Dolphin settings dialog.
@@ -48,6 +49,7 @@ public:
 
 private slots:
     void slotSettingsChanged();
+    void updateInitialViewOptions();
     void selectHomeUrl();
     void useCurrentLocation();
     void useDefaultLocation();
@@ -58,6 +60,10 @@ private:
 private:
     QUrl m_url;
     QLineEdit* m_homeUrl;
+    QWidget* m_homeUrlBoxLayoutContainer;
+    QWidget* m_buttonBoxLayoutContainer;
+    QRadioButton* m_rememberOpenedTabsRadioButton;
+    QRadioButton* m_homeUrlRadioButton;
 
     QCheckBox* m_splitView;
     QCheckBox* m_editableUrl;
index 06b0b8cf5ca191d80b5e2faef4560eb1374de559..fa891133b38cfbb58d3eccfa72091fa33513cd4b 100644 (file)
 #include <QComboBox>
 #include <QHelpEvent>
 #include <QFormLayout>
+#include <QSpinBox>
+#include <QRadioButton>
+#include <QButtonGroup>
+#include <QLabel>
 
 ViewSettingsTab::ViewSettingsTab(Mode mode, QWidget* parent) :
     QWidget(parent),
@@ -42,11 +46,11 @@ ViewSettingsTab::ViewSettingsTab(Mode mode, QWidget* parent) :
     m_fontRequester(nullptr),
     m_widthBox(nullptr),
     m_maxLinesBox(nullptr),
-    m_expandableFolders(nullptr)
+    m_expandableFolders(nullptr),
+    m_recursiveDirectorySizeLimit(nullptr)
 {
     QFormLayout* topLayout = new QFormLayout(this);
 
-
     // Create "Icon Size" section
     const int minRange = ZoomLevelInfo::minimumLevel();
     const int maxRange = ZoomLevelInfo::maximumLevel();
@@ -75,7 +79,6 @@ ViewSettingsTab::ViewSettingsTab(Mode mode, QWidget* parent) :
     m_fontRequester = new DolphinFontRequester(this);
     topLayout->addRow(i18nc("@label:listbox", "Label font:"), m_fontRequester);
 
-
     switch (m_mode) {
     case IconsMode: {
         m_widthBox = new QComboBox();
@@ -107,8 +110,30 @@ ViewSettingsTab::ViewSettingsTab(Mode mode, QWidget* parent) :
     case DetailsMode:
         m_expandableFolders = new QCheckBox(i18nc("@option:check", "Expandable"));
         topLayout->addRow(i18nc("@label:checkbox", "Folders:"), m_expandableFolders);
-        break;
-    default:
+
+#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, QOverload<int>::of(&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
         break;
     }
 
@@ -128,6 +153,11 @@ ViewSettingsTab::ViewSettingsTab(Mode mode, QWidget* parent) :
         break;
     case DetailsMode:
         connect(m_expandableFolders, &QCheckBox::toggled, this, &ViewSettingsTab::changed);
+        connect(m_recursiveDirectorySizeLimit, QOverload<int>::of(&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());
+        });
         break;
     default:
         break;
@@ -153,6 +183,8 @@ void ViewSettingsTab::applySettings()
         break;
     case DetailsMode:
         DetailsModeSettings::setExpandableFolders(m_expandableFolders->isChecked());
+        DetailsModeSettings::setDirectorySizeCount(m_numberOfItems->isChecked());
+        DetailsModeSettings::setRecursiveDirectorySizeLimit(m_recursiveDirectorySizeLimit->value());
         break;
     default:
         break;
@@ -201,6 +233,14 @@ void ViewSettingsTab::loadSettings()
         break;
     case DetailsMode:
         m_expandableFolders->setChecked(DetailsModeSettings::expandableFolders());
+        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());
         break;
     default:
         break;
index fff882e5e2794806f349ad16ff081d169c3b50f5..4d459fca2e27c3c9324fd8eea5355b04f45ffc91 100644 (file)
@@ -28,6 +28,8 @@ class DolphinFontRequester;
 class QComboBox;
 class QCheckBox;
 class QSlider;
+class QSpinBox;
+class QRadioButton;
 
 /**
  * @brief Represents one tab of the view-settings page.
@@ -72,6 +74,9 @@ private:
     QComboBox* m_widthBox;
     QComboBox* m_maxLinesBox;
     QCheckBox* m_expandableFolders;
+    QRadioButton* m_numberOfItems;
+    QRadioButton* m_sizeOfContents;
+    QSpinBox* m_recursiveDirectorySizeLimit;
 };
 
 #endif
index d8f2aab937bc84f33f8e93a95fb5e1ffee80b892..f0dc17837a5ddcad2bb3a18a50aa5bb19382fb1e 100644 (file)
@@ -1200,7 +1200,7 @@ void DolphinView::slotPasteJobResult(KJob *job)
         emit errorMessage(job->errorString());
     }
     if (!m_selectedUrls.isEmpty()) {
-        m_selectedUrls << KDirModel::simplifiedUrlList(m_selectedUrls);
+        m_selectedUrls = KDirModel::simplifiedUrlList(m_selectedUrls);
     }
 }
 
@@ -1493,13 +1493,27 @@ void DolphinView::calculateItemCount(int& fileCount,
                                      KIO::filesize_t& totalFileSize) const
 {
     const int itemCount = m_model->count();
+
+    bool countFileSize = true;
+
+    // In case we have a precomputed value
+    const auto job = KIO::statDetails(m_model->rootItem().url(), KIO::StatJob::SourceSide, KIO::StatRecursiveSize);
+    job->exec();
+    const auto entry =  job->statResult();
+    if (entry.contains(KIO::UDSEntry::UDS_RECURSIVE_SIZE)) {
+        totalFileSize = static_cast<KIO::filesize_t>(entry.numberValue(KIO::UDSEntry::UDS_RECURSIVE_SIZE));
+        countFileSize = false;
+    }
+
     for (int i = 0; i < itemCount; ++i) {
         const KFileItem item = m_model->fileItem(i);
         if (item.isDir()) {
             ++folderCount;
         } else {
             ++fileCount;
-            totalFileSize += item.size();
+            if (countFileSize) {
+                totalFileSize += item.size();
+            }
         }
     }
 }
index c61e1aaa9f46df99ef418e4059f83cc77a4e9c4a..e89e2e62c2b705e32223dca048334d6c22f7f224 100644 (file)
@@ -283,7 +283,7 @@ void DolphinViewActionHandler::createActions()
         "<para>Hidden items only differ from other ones in that their "
         "name starts with a \".\". In general there is no need for "
         "users to access them which is why they are hidden.</para>"));
-    m_actionCollection->setDefaultShortcuts(showHiddenFiles, {Qt::ALT + Qt::Key_Period, Qt::CTRL + Qt::Key_H, Qt::Key_F8});
+    m_actionCollection->setDefaultShortcuts(showHiddenFiles, KStandardShortcut::showHideHiddenFiles());
     connect(showHiddenFiles, &KToggleAction::triggered, this, &DolphinViewActionHandler::toggleShowHiddenFiles);
 
     QAction* adjustViewProps = m_actionCollection->addAction(QStringLiteral("view_properties"));
index 2879745349653126579ae2ebcff96222d221facd..0d94a3fc88fb0823738380420d68f726c27648b6 100644 (file)
@@ -180,14 +180,17 @@ public:
     virtual ItemVersion itemVersion(const KFileItem& item) const = 0;
 
     /**
-     * @return List of actions that are available for the items \p items.
-     *         It is recommended to keep the number of returned actions small
-     *         in case if an item is an unversioned directory that is not
-     *         inside the hierarchy tree of the version control system. This
-     *         prevents having a cluttered context menu for directories
-     *         outside the version control system.
+     * @return List of actions that are available for the \p items in a version controlled
+     *         path.
      */
-    virtual QList<QAction*> actions(const KFileItemList& items) const = 0;
+    virtual QList<QAction*> versionControlActions(const KFileItemList& items) const = 0;
+
+    /**
+     * @return List of actions that are available for the out of version control
+     *         items \p items. It's opposed to the \p versionedActions. Common usage
+     *         is for clone/checkout actions.
+     */
+    virtual QList<QAction*> outOfVersionControlActions(const KFileItemList& items) const = 0;
 
 Q_SIGNALS:
     /**
index 2d801686e259099a249a301b613f49109d41ca03..f6c74fb5fe4de9eee2e1b8560142b4cab93a1ecb 100644 (file)
@@ -118,11 +118,19 @@ QList<QAction*> VersionControlObserver::actions(const KFileItemList& items) cons
         }
     }
 
-    if (!m_model || hasNullItems || !isVersioned()) {
+    if (!m_model || hasNullItems) {
         return {};
     }
 
-    return m_plugin->actions(items);
+    if (isVersionControlled()) {
+        return m_plugin->versionControlActions(items);
+    } else {
+        QList<QAction*> actions;
+        for (const auto &plugin : qAsConst(m_plugins)) {
+            actions << plugin.first->outOfVersionControlActions(items);
+        }
+        return actions;
+    }
 }
 
 void VersionControlObserver::delayedDirectoryVerification()
@@ -360,7 +368,7 @@ KVersionControlPlugin* VersionControlObserver::searchPlugin(const QUrl& director
     return bestPlugin;
 }
 
-bool VersionControlObserver::isVersioned() const
+bool VersionControlObserver::isVersionControlled() const
 {
     return m_versionedDirectory && m_plugin;
 }
index 66f992963c3532d5080f00844d01ea01d7a25798..648c9d6fdfd126b9fce238f8cd8302f9ae3a9483 100644 (file)
@@ -143,7 +143,7 @@ private:
     /**
      * Returns true, if the directory contains a version control information.
      */
-    bool isVersioned() const;
+    bool isVersionControlled() const;
 
 private:
     bool m_pendingItemStatesUpdate;