include/vcl/UserResourceScanner.hxx          |   56 +++++++++++
 vcl/CppunitTest_vcl_app_test.mk              |    5 
 vcl/Library_vcl.mk                           |    1 
 vcl/inc/IconThemeScanner.hxx                 |   25 +---
 vcl/qa/cppunit/app/test_IconThemeScanner.cxx |   34 ++----
 vcl/source/app/IconThemeScanner.cxx          |  130 ++-----------------------
 vcl/source/app/UserResourceScanner.cxx       |  137 +++++++++++++++++++++++++++
 vcl/source/app/settings.cxx                  |   11 +-
 8 files changed, 230 insertions(+), 169 deletions(-)

New commits:
commit c212fd9254ed53e478b815c7fc4606d5e6d9850b
Author:     Tomaž Vajngerl <[email protected]>
AuthorDate: Thu Jan 16 15:02:28 2025 +0900
Commit:     Tomaž Vajngerl <[email protected]>
CommitDate: Fri Jan 17 15:17:26 2025 +0100

    vcl: extract scanning for resources to UserResourceScanner class
    
    So the scanning for resources can be reused for other things.
    
    Change-Id: Ia1589eb6c2ce4f254be0a62e296b1e186d0ba323
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/180371
    Reviewed-by: Tomaž Vajngerl <[email protected]>
    Tested-by: Jenkins

diff --git a/include/vcl/UserResourceScanner.hxx 
b/include/vcl/UserResourceScanner.hxx
new file mode 100644
index 000000000000..7a591a5951b4
--- /dev/null
+++ b/include/vcl/UserResourceScanner.hxx
@@ -0,0 +1,56 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#pragma once
+
+#include <vcl/dllapi.h>
+#include <rtl/ustring.hxx>
+#include <memory>
+#include <vector>
+#include <osl/file.hxx>
+
+namespace vcl
+{
+namespace file
+{
+VCL_DLLPUBLIC bool readFileStatus(osl::FileStatus& rStatus, const OUString& 
rFile);
+}
+
+class VCL_DLLPUBLIC UserResourceScanner
+{
+protected:
+    /** Scans the provided directory for the resoruce.
+     *
+     * The returned strings will contain the URLs to the resources.
+     */
+    std::vector<OUString> readFilesFromPath(const OUString& dir);
+
+    /** Return true if the filename is a valid resource */
+    virtual bool isValidResource(const OUString& rFilename) = 0;
+
+    /** Adds the provided resource by path. */
+    virtual bool addResource(const OUString& /*path*/) = 0;
+
+public:
+    UserResourceScanner();
+    virtual ~UserResourceScanner() {}
+
+    /** Provide a semicolon-separated list of paths to search for resource.
+     *
+     * There are several cases when scan will fail:
+     * - The directory does not exist
+     * - There are no files which have a valid resource
+     */
+
+    void addPaths(std::u16string_view paths);
+};
+
+} // end namespace vcl
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/CppunitTest_vcl_app_test.mk b/vcl/CppunitTest_vcl_app_test.mk
index 3749a7f29ca5..6545babe40fb 100644
--- a/vcl/CppunitTest_vcl_app_test.mk
+++ b/vcl/CppunitTest_vcl_app_test.mk
@@ -34,10 +34,7 @@ $(eval $(call gb_CppunitTest_use_sdk_api,vcl_app_test))
 $(eval $(call gb_CppunitTest_use_ure,vcl_app_test))
 $(eval $(call gb_CppunitTest_use_vcl,vcl_app_test))
 
-$(eval $(call gb_CppunitTest_use_components,vcl_app_test,\
-    configmgr/source/configmgr \
-    i18npool/util/i18npool \
-))
+$(eval $(call gb_CppunitTest_use_rdb,vcl_app_test,services))
 
 $(eval $(call gb_CppunitTest_use_configuration,vcl_app_test))
 
diff --git a/vcl/Library_vcl.mk b/vcl/Library_vcl.mk
index c8e797dfd270..769329d30cba 100644
--- a/vcl/Library_vcl.mk
+++ b/vcl/Library_vcl.mk
@@ -478,6 +478,7 @@ $(eval $(call gb_Library_add_exception_objects,vcl,\
     vcl/source/app/timer \
     vcl/source/app/unohelp2 \
     vcl/source/app/htmltransferable \
+    vcl/source/app/UserResourceScanner \
     vcl/source/app/unohelp \
     vcl/source/app/vclevent \
     vcl/source/app/watchdog \
diff --git a/vcl/inc/IconThemeScanner.hxx b/vcl/inc/IconThemeScanner.hxx
index 60841eeddaab..85a7d8543ba4 100644
--- a/vcl/inc/IconThemeScanner.hxx
+++ b/vcl/inc/IconThemeScanner.hxx
@@ -13,6 +13,7 @@
 
 #include <rtl/ustring.hxx>
 #include <vcl/IconThemeInfo.hxx>
+#include <vcl/UserResourceScanner.hxx>
 
 #include <memory>
 #include <vector>
@@ -24,16 +25,10 @@ namespace vcl
 {
 /** This class scans a folder for icon themes and provides the results.
  */
-class VCL_DLLPUBLIC IconThemeScanner
+class VCL_DLLPUBLIC IconThemeScanner : public UserResourceScanner
 {
 public:
-    /** Provide a semicolon-separated list of paths to search for IconThemes.
-     *
-     * There are several cases when scan will fail:
-     * - The directory does not exist
-     * - There are no files which match the pattern images_xxx.zip
-     */
-    explicit IconThemeScanner(std::u16string_view paths);
+    IconThemeScanner();
 
     /** This method will return the standard path where icon themes are 
located.
      */
@@ -53,19 +48,11 @@ public:
     bool IconThemeIsInstalled(const OUString& themeId) const;
 
 private:
-    IconThemeScanner();
-
-    /** Adds the provided icon theme by path.
-     */
-    bool AddIconThemeByPath(const OUString& path);
-
-    /** Scans the provided directory for icon themes.
-     * The returned strings will contain the URLs to the icon themes.
-     */
-    static std::vector<OUString> ReadIconThemesFromPath(const OUString& dir);
+    /** Adds the provided icon theme by path. */
+    bool addResource(const OUString& path) override;
 
     /** Check whether a single file is valid */
-    static bool FileIsValidIconTheme(const OUString&);
+    bool isValidResource(const OUString& rFilename) override;
 
     std::vector<IconThemeInfo> mFoundIconThemes;
 
diff --git a/vcl/qa/cppunit/app/test_IconThemeScanner.cxx 
b/vcl/qa/cppunit/app/test_IconThemeScanner.cxx
index d0eb13402e9a..ab8d5df35f89 100644
--- a/vcl/qa/cppunit/app/test_IconThemeScanner.cxx
+++ b/vcl/qa/cppunit/app/test_IconThemeScanner.cxx
@@ -19,51 +19,41 @@
 
 class IconThemeScannerTest : public CppUnit::TestFixture
 {
-    void
-    AddedThemeIsFoundById();
-
-    void
-    AddedThemeInfoIsReturned();
-
-    void
-    ExceptionIsThrownIfInvalidInfoIsRequested();
+    void testAddedThemeIsFoundById();
+    void testAddedThemeInfoIsReturned();
+    void testExceptionIsThrownIfInvalidInfoIsRequested();
 
     // Adds code needed to register the test suite
     CPPUNIT_TEST_SUITE(IconThemeScannerTest);
-    CPPUNIT_TEST(AddedThemeIsFoundById);
-    CPPUNIT_TEST(AddedThemeInfoIsReturned);
-    CPPUNIT_TEST(ExceptionIsThrownIfInvalidInfoIsRequested);
-
-    // End of test suite definition
+    CPPUNIT_TEST(testAddedThemeIsFoundById);
+    CPPUNIT_TEST(testAddedThemeInfoIsReturned);
+    CPPUNIT_TEST(testExceptionIsThrownIfInvalidInfoIsRequested);
     CPPUNIT_TEST_SUITE_END();
 };
 
-void
-IconThemeScannerTest::AddedThemeIsFoundById()
+void IconThemeScannerTest::testAddedThemeIsFoundById()
 {
     vcl::IconThemeScanner scanner;
-    scanner.AddIconThemeByPath(u"file:://images_katze.zip"_ustr);
+    scanner.addResource(u"file:://images_katze.zip"_ustr);
     OUString id = vcl::IconThemeInfo::FileNameToThemeId(u"images_katze.zip");
     bool found = scanner.IconThemeIsInstalled(id);
     CPPUNIT_ASSERT_EQUAL_MESSAGE("icon theme could be added by url", true, 
found);
 }
 
-void
-IconThemeScannerTest::AddedThemeInfoIsReturned()
+void IconThemeScannerTest::testAddedThemeInfoIsReturned()
 {
     vcl::IconThemeScanner scanner;
     OUString theme(u"file:://images_katze.zip"_ustr);
-    scanner.AddIconThemeByPath(theme);
+    scanner.addResource(theme);
     OUString id = vcl::IconThemeInfo::FileNameToThemeId(u"images_katze.zip");
     const vcl::IconThemeInfo& info = scanner.GetIconThemeInfo(id);
     CPPUNIT_ASSERT_EQUAL_MESSAGE("'katze' icon theme is found from id", theme, 
info.GetUrlToFile());
 }
 
-void
-IconThemeScannerTest::ExceptionIsThrownIfInvalidInfoIsRequested()
+void IconThemeScannerTest::testExceptionIsThrownIfInvalidInfoIsRequested()
 {
     vcl::IconThemeScanner scanner;
-    scanner.AddIconThemeByPath(u"file:://images_katze.zip"_ustr);
+    scanner.addResource(u"file:://images_katze.zip"_ustr);
     bool thrown = false;
     try
     {
diff --git a/vcl/source/app/IconThemeScanner.cxx 
b/vcl/source/app/IconThemeScanner.cxx
index 9cc5fc83d54d..70f8c64f52f1 100644
--- a/vcl/source/app/IconThemeScanner.cxx
+++ b/vcl/source/app/IconThemeScanner.cxx
@@ -9,155 +9,47 @@
 
 #include <sal/config.h>
 #include <sal/log.hxx>
-
-#include <deque>
-
 #include <IconThemeScanner.hxx>
 
-#include <osl/file.hxx>
 #include <salhelper/linkhelper.hxx>
 #include <unotools/pathoptions.hxx>
 #include <vcl/IconThemeInfo.hxx>
 #include <o3tl/string_view.hxx>
 
-namespace vcl {
-
-namespace {
-
-// set the status of a file. Returns false if the status could not be 
determined.
-bool set_file_status(osl::FileStatus& status, const OUString& file)
+namespace vcl
 {
-    osl::DirectoryItem dirItem;
-    osl::FileBase::RC retvalGet = osl::DirectoryItem::get(file, dirItem);
-    if (retvalGet != osl::FileBase::E_None) {
-        SAL_WARN("vcl.app", "Could not determine status for file '" << file << 
"'.");
-        return false;
-    }
-    osl::FileBase::RC retvalStatus = dirItem.getFileStatus(status);
-    if (retvalStatus != osl::FileBase::E_None) {
-        SAL_WARN("vcl.app", "Could not determine status for file '" << file << 
"'.");
-        return false;
-    }
-    return true;
-}
-
-OUString convert_to_absolute_path(const OUString& path)
-{
-    salhelper::LinkResolver resolver(0);
-    osl::FileBase::RC rc = resolver.fetchFileStatus(path);
-    if (rc != osl::FileBase::E_None) {
-        SAL_WARN("vcl.app", "Could not resolve path '" << path << "' to search 
for icon themes.");
-        if (rc == osl::FileBase::E_MULTIHOP)
-        {
-            throw std::runtime_error("Provided a recursive symlink to an icon 
theme directory that could not be resolved.");
-        }
-    }
-    return resolver.m_aStatus.getFileURL();
-}
-
-}
 
 IconThemeScanner::IconThemeScanner() = default;
 
-IconThemeScanner::IconThemeScanner(std::u16string_view paths)
-{
-    mFoundIconThemes.clear();
-
-    std::deque<OUString> aPaths;
-
-    sal_Int32 nIndex = 0;
-    do
-    {
-        aPaths.push_front(OUString(o3tl::getToken(paths, 0, ';', nIndex)));
-    }
-    while (nIndex >= 0);
-
-    for (const auto& path : aPaths)
-    {
-        osl::FileStatus fileStatus(osl_FileStatus_Mask_Type);
-        bool couldSetFileStatus = set_file_status(fileStatus, path);
-        if (!couldSetFileStatus) {
-            continue;
-        }
-
-        if (!fileStatus.isDirectory()) {
-            SAL_INFO("vcl.app", "Cannot search for icon themes in '"<< path << 
"'. It is not a directory.");
-            continue;
-        }
-
-        std::vector<OUString> iconThemePaths = ReadIconThemesFromPath(path);
-        if (iconThemePaths.empty()) {
-            SAL_WARN("vcl.app", "Could not find any icon themes in the 
provided directory ('" <<path<<"'.");
-            continue;
-        }
-        for (auto const& iconThemePath : iconThemePaths)
-        {
-            AddIconThemeByPath(iconThemePath);
-        }
-    }
-}
-
-bool
-IconThemeScanner::AddIconThemeByPath(const OUString &url)
+bool IconThemeScanner::addResource(const OUString& rURL)
 {
-    if (!IconThemeInfo::UrlCanBeParsed(url)) {
+    if (!IconThemeInfo::UrlCanBeParsed(rURL)) {
         return false;
     }
-    SAL_INFO("vcl.app", "Found a file that seems to be an icon theme: '" << 
url << "'" );
-    IconThemeInfo newTheme(url);
+    SAL_INFO("vcl.app", "Found a file that seems to be an icon theme: '" << 
rURL << "'" );
+    IconThemeInfo newTheme(rURL);
     mFoundIconThemes.push_back(newTheme);
     SAL_INFO("vcl.app", "Adding the file as '" << newTheme.GetDisplayName() <<
             "' with id '" << newTheme.GetThemeId() << "'.");
     return true;
 }
 
-/*static*/ std::vector<OUString>
-IconThemeScanner::ReadIconThemesFromPath(const OUString& dir)
-{
-    std::vector<OUString> found;
-    SAL_INFO("vcl.app", "Scanning directory '" << dir << " for icon themes.");
-
-    osl::Directory dirToScan(dir);
-    osl::FileBase::RC retvalOpen = dirToScan.open();
-    if (retvalOpen != osl::FileBase::E_None) {
-        return found;
-    }
-
-    osl::DirectoryItem directoryItem;
-    while (dirToScan.getNextItem(directoryItem) == osl::FileBase::E_None) {
-        osl::FileStatus status(osl_FileStatus_Mask_Type | 
osl_FileStatus_Mask_FileURL | osl_FileStatus_Mask_FileName);
-        osl::FileBase::RC retvalStatus = directoryItem.getFileStatus(status);
-        if (retvalStatus != osl::FileBase::E_None) {
-            continue;
-        }
-
-        OUString filename = convert_to_absolute_path(status.getFileURL());
-        if (!FileIsValidIconTheme(filename)) {
-            continue;
-        }
-        found.push_back(filename);
-    }
-    return found;
-}
-
-/*static*/ bool
-IconThemeScanner::FileIsValidIconTheme(const OUString& filename)
+bool IconThemeScanner::isValidResource(const OUString& filename)
 {
     // check whether we can construct an IconThemeInfo from it
-    if (!IconThemeInfo::UrlCanBeParsed(filename)) {
+    if (!IconThemeInfo::UrlCanBeParsed(filename))
+    {
         SAL_INFO("vcl.app", "File '" << filename << "' does not seem to be an 
icon theme.");
         return false;
     }
 
     osl::FileStatus fileStatus(osl_FileStatus_Mask_Type);
-    bool couldSetFileStatus = set_file_status(fileStatus, filename);
-    if (!couldSetFileStatus) {
+    if (!vcl::file::readFileStatus(fileStatus, filename))
         return false;
-    }
 
-    if (!fileStatus.isRegular()) {
+    if (!fileStatus.isRegular())
         return false;
-    }
+
     return true;
 }
 
diff --git a/vcl/source/app/UserResourceScanner.cxx 
b/vcl/source/app/UserResourceScanner.cxx
new file mode 100644
index 000000000000..0e01da5b3b0d
--- /dev/null
+++ b/vcl/source/app/UserResourceScanner.cxx
@@ -0,0 +1,137 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <vcl/UserResourceScanner.hxx>
+
+#include <sal/config.h>
+#include <sal/log.hxx>
+
+#include <salhelper/linkhelper.hxx>
+#include <unotools/pathoptions.hxx>
+#include <o3tl/string_view.hxx>
+
+#include <deque>
+
+namespace vcl
+{
+namespace
+{
+OUString convertToAbsolutePath(const OUString& path)
+{
+    salhelper::LinkResolver resolver(0);
+    osl::FileBase::RC rc = resolver.fetchFileStatus(path);
+    if (rc != osl::FileBase::E_None)
+    {
+        SAL_WARN("vcl.app", "Could not resolve path '" << path << "' to search 
for icon themes.");
+        if (rc == osl::FileBase::E_MULTIHOP)
+        {
+            throw std::runtime_error("Provided a recursive symlink to an icon 
theme directory that "
+                                     "could not be resolved.");
+        }
+    }
+    return resolver.m_aStatus.getFileURL();
+}
+
+void splitPathString(std::u16string_view aPathString, std::deque<OUString>& 
rPaths)
+{
+    sal_Int32 nIndex = 0;
+    do
+    {
+        rPaths.push_front(OUString(o3tl::getToken(aPathString, 0, ';', 
nIndex)));
+    } while (nIndex >= 0);
+}
+}
+
+namespace file
+{
+// read the status of a file. Returns false if the status could not be 
determined.
+bool readFileStatus(osl::FileStatus& status, const OUString& file)
+{
+    osl::DirectoryItem dirItem;
+    osl::FileBase::RC retvalGet = osl::DirectoryItem::get(file, dirItem);
+    if (retvalGet != osl::FileBase::E_None)
+    {
+        SAL_WARN("vcl.app", "Could not determine status for file '" << file << 
"'.");
+        return false;
+    }
+    osl::FileBase::RC retvalStatus = dirItem.getFileStatus(status);
+    if (retvalStatus != osl::FileBase::E_None)
+    {
+        SAL_WARN("vcl.app", "Could not determine status for file '" << file << 
"'.");
+        return false;
+    }
+    return true;
+}
+}
+
+UserResourceScanner::UserResourceScanner() = default;
+
+void UserResourceScanner::addPaths(std::u16string_view aPathString)
+{
+    std::deque<OUString> aPaths;
+    splitPathString(aPathString, aPaths);
+
+    for (const auto& path : aPaths)
+    {
+        osl::FileStatus aFileStatus(osl_FileStatus_Mask_Type);
+
+        if (!vcl::file::readFileStatus(aFileStatus, path))
+            continue;
+
+        if (!aFileStatus.isDirectory())
+        {
+            SAL_INFO("vcl.app",
+                     "Cannot search for icon themes in '" << path << "'. It is 
not a directory.");
+            continue;
+        }
+
+        std::vector<OUString> aResourcePaths = readFilesFromPath(path);
+
+        if (aResourcePaths.empty())
+        {
+            SAL_WARN("vcl.app",
+                     "Could not find any icon themes in the provided directory 
('" << path << "'.");
+            continue;
+        }
+
+        for (auto const& iconThemePath : aResourcePaths)
+            addResource(iconThemePath);
+    }
+}
+
+std::vector<OUString> UserResourceScanner::readFilesFromPath(const OUString& 
dir)
+{
+    std::vector<OUString> found;
+    SAL_INFO("vcl", "Scanning directory '" << dir << " for potential resource 
files.");
+
+    osl::Directory dirToScan(dir);
+    osl::FileBase::RC retvalOpen = dirToScan.open();
+    if (retvalOpen != osl::FileBase::E_None)
+        return found;
+
+    osl::DirectoryItem directoryItem;
+    while (dirToScan.getNextItem(directoryItem) == osl::FileBase::E_None)
+    {
+        osl::FileStatus status(osl_FileStatus_Mask_Type | 
osl_FileStatus_Mask_FileURL
+                               | osl_FileStatus_Mask_FileName);
+        osl::FileBase::RC retvalStatus = directoryItem.getFileStatus(status);
+        if (retvalStatus != osl::FileBase::E_None)
+            continue;
+
+        OUString filename = convertToAbsolutePath(status.getFileURL());
+        if (isValidResource(filename))
+            found.push_back(filename);
+    }
+
+    return found;
+}
+
+} // end namespace vcl
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/app/settings.cxx b/vcl/source/app/settings.cxx
index 6c69bda88a37..0ad5de4bd045 100644
--- a/vcl/source/app/settings.cxx
+++ b/vcl/source/app/settings.cxx
@@ -209,8 +209,7 @@ struct ImplStyleData
     ToolbarIconSize                 mnToolbarIconSize = 
ToolbarIconSize::Unknown;
     StyleSettingsOptions            mnOptions = StyleSettingsOptions::NONE;
     TriState                        meUseImagesInMenus = TRISTATE_INDET;
-    std::optional<vcl::IconThemeScanner> mutable
-                                    mIconThemeScanner;
+    std::shared_ptr<vcl::IconThemeScanner> mutable mpIconThemeScanner;
     vcl::IconThemeSelector          mIconThemeSelector;
 
     OUString                        mIconTheme;
@@ -2789,10 +2788,12 @@ StyleSettings::GetOptions() const
 std::vector<vcl::IconThemeInfo> const &
 StyleSettings::GetInstalledIconThemes() const
 {
-    if (!mxData->mIconThemeScanner) {
-        
mxData->mIconThemeScanner.emplace(vcl::IconThemeScanner::GetStandardIconThemePath());
+    if (!mxData->mpIconThemeScanner)
+    {
+        mxData->mpIconThemeScanner.reset(new vcl::IconThemeScanner);
+        
mxData->mpIconThemeScanner->addPaths(vcl::IconThemeScanner::GetStandardIconThemePath());
     }
-    return mxData->mIconThemeScanner->GetFoundIconThemes();
+    return mxData->mpIconThemeScanner->GetFoundIconThemes();
 }
 
 OUString

Reply via email to