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
