1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
10 #include <sal/config.h>
11 #include <sal/log.hxx>
15 #include <IconThemeScanner.hxx>
17 #include <osl/file.hxx>
18 #include <salhelper/linkhelper.hxx>
19 #include <unotools/pathoptions.hxx>
20 #include <vcl/IconThemeInfo.hxx>
21 #include <o3tl/string_view.hxx>
27 // set the status of a file. Returns false if the status could not be determined.
28 bool set_file_status(osl::FileStatus
& status
, const OUString
& file
)
30 osl::DirectoryItem dirItem
;
31 osl::FileBase::RC retvalGet
= osl::DirectoryItem::get(file
, dirItem
);
32 if (retvalGet
!= osl::FileBase::E_None
) {
33 SAL_WARN("vcl.app", "Could not determine status for file '" << file
<< "'.");
36 osl::FileBase::RC retvalStatus
= dirItem
.getFileStatus(status
);
37 if (retvalStatus
!= osl::FileBase::E_None
) {
38 SAL_WARN("vcl.app", "Could not determine status for file '" << file
<< "'.");
44 OUString
convert_to_absolute_path(const OUString
& path
)
46 salhelper::LinkResolver
resolver(0);
47 osl::FileBase::RC rc
= resolver
.fetchFileStatus(path
);
48 if (rc
!= osl::FileBase::E_None
) {
49 SAL_WARN("vcl.app", "Could not resolve path '" << path
<< "' to search for icon themes.");
50 if (rc
== osl::FileBase::E_MULTIHOP
)
52 throw std::runtime_error("Provided a recursive symlink to an icon theme directory that could not be resolved.");
55 return resolver
.m_aStatus
.getFileURL();
60 IconThemeScanner::IconThemeScanner()
63 void IconThemeScanner::ScanDirectoryForIconThemes(std::u16string_view paths
)
65 mFoundIconThemes
.clear();
67 std::deque
<OUString
> aPaths
;
72 aPaths
.push_front(OUString(o3tl::getToken(paths
, 0, ';', nIndex
)));
76 for (const auto& path
: aPaths
)
78 osl::FileStatus
fileStatus(osl_FileStatus_Mask_Type
);
79 bool couldSetFileStatus
= set_file_status(fileStatus
, path
);
80 if (!couldSetFileStatus
) {
84 if (!fileStatus
.isDirectory()) {
85 SAL_INFO("vcl.app", "Cannot search for icon themes in '"<< path
<< "'. It is not a directory.");
89 std::vector
<OUString
> iconThemePaths
= ReadIconThemesFromPath(path
);
90 if (iconThemePaths
.empty()) {
91 SAL_WARN("vcl.app", "Could not find any icon themes in the provided directory ('" <<path
<<"'.");
94 for (auto const& iconThemePath
: iconThemePaths
)
96 AddIconThemeByPath(iconThemePath
);
102 IconThemeScanner::AddIconThemeByPath(const OUString
&url
)
104 if (!IconThemeInfo::UrlCanBeParsed(url
)) {
107 SAL_INFO("vcl.app", "Found a file that seems to be an icon theme: '" << url
<< "'" );
108 IconThemeInfo
newTheme(url
);
109 mFoundIconThemes
.push_back(newTheme
);
110 SAL_INFO("vcl.app", "Adding the file as '" << newTheme
.GetDisplayName() <<
111 "' with id '" << newTheme
.GetThemeId() << "'.");
115 /*static*/ std::vector
<OUString
>
116 IconThemeScanner::ReadIconThemesFromPath(const OUString
& dir
)
118 std::vector
<OUString
> found
;
119 SAL_INFO("vcl.app", "Scanning directory '" << dir
<< " for icon themes.");
121 osl::Directory
dirToScan(dir
);
122 osl::FileBase::RC retvalOpen
= dirToScan
.open();
123 if (retvalOpen
!= osl::FileBase::E_None
) {
127 osl::DirectoryItem directoryItem
;
128 while (dirToScan
.getNextItem(directoryItem
) == osl::FileBase::E_None
) {
129 osl::FileStatus
status(osl_FileStatus_Mask_Type
| osl_FileStatus_Mask_FileURL
| osl_FileStatus_Mask_FileName
);
130 osl::FileBase::RC retvalStatus
= directoryItem
.getFileStatus(status
);
131 if (retvalStatus
!= osl::FileBase::E_None
) {
135 OUString filename
= convert_to_absolute_path(status
.getFileURL());
136 if (!FileIsValidIconTheme(filename
)) {
139 found
.push_back(filename
);
145 IconThemeScanner::FileIsValidIconTheme(const OUString
& filename
)
147 // check whether we can construct an IconThemeInfo from it
148 if (!IconThemeInfo::UrlCanBeParsed(filename
)) {
149 SAL_INFO("vcl.app", "File '" << filename
<< "' does not seem to be an icon theme.");
153 osl::FileStatus
fileStatus(osl_FileStatus_Mask_Type
);
154 bool couldSetFileStatus
= set_file_status(fileStatus
, filename
);
155 if (!couldSetFileStatus
) {
159 if (!fileStatus
.isRegular()) {
166 IconThemeScanner::IconThemeIsInstalled(const OUString
& themeId
) const
168 return IconThemeInfo::IconThemeIsInVector(mFoundIconThemes
, themeId
);
171 /*static*/ std::shared_ptr
<IconThemeScanner
>
172 IconThemeScanner::Create(std::u16string_view path
)
174 std::shared_ptr
<IconThemeScanner
> retval(new IconThemeScanner
);
175 retval
->ScanDirectoryForIconThemes(path
);
180 IconThemeScanner::GetStandardIconThemePath()
182 SvtPathOptions aPathOptions
;
183 return aPathOptions
.GetIconsetPath();
191 const OUString
& m_rThemeId
;
193 explicit SameTheme(const OUString
&rThemeId
) : m_rThemeId(rThemeId
) {}
194 bool operator()(const vcl::IconThemeInfo
&rInfo
)
196 return m_rThemeId
== rInfo
.GetThemeId();
201 const vcl::IconThemeInfo
&
202 IconThemeScanner::GetIconThemeInfo(const OUString
& themeId
)
204 std::vector
<IconThemeInfo
>::iterator info
= std::find_if(mFoundIconThemes
.begin(), mFoundIconThemes
.end(),
206 if (info
== mFoundIconThemes
.end()) {
207 SAL_WARN("vcl.app", "Requested information for icon theme with id '" << themeId
208 << "' which does not exist.");
209 throw std::runtime_error("Requested information on not-installed icon theme");
214 } // end namespace vcl
216 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */