1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * @file FileFilterHelper.cpp
5 * @brief Implementation file for FileFilterHelper class
9 #include "FileFilterHelper.h"
10 #include "UnicodeString.h"
11 #include "FilterList.h"
13 #include "FileFilterMgr.h"
15 #include "Environment.h"
21 * @brief Constructor, creates new filtermanager.
23 FileFilterHelper::FileFilterHelper()
24 : m_pMaskFilter(nullptr)
26 , m_fileFilterMgr(new FileFilterMgr)
27 , m_currentFilter(nullptr)
32 * @brief Destructor, deletes filtermanager.
34 FileFilterHelper::~FileFilterHelper()
39 * @brief Store current filter path.
41 * Select filter based on filepath. If filter with that path
42 * is found select it. Otherwise set path to empty (default).
43 * @param [in] szFileFilterPath Full path to filter to select.
45 void FileFilterHelper::SetFileFilterPath(const String& szFileFilterPath)
47 // Use none as default path
48 m_sFileFilterPath.clear();
50 if (m_fileFilterMgr == nullptr)
53 // Don't bother to lookup empty path
54 if (!szFileFilterPath.empty())
56 m_currentFilter = m_fileFilterMgr->GetFilterByPath(szFileFilterPath);
57 if (m_currentFilter != nullptr)
58 m_sFileFilterPath = szFileFilterPath;
63 * @brief Get list of filters currently available.
65 * @param [out] selected Filepath of currently selected filter.
66 * @return Filter list to receive found filters.
68 std::vector<FileFilterInfo> FileFilterHelper::GetFileFilters(String & selected) const
70 std::vector<FileFilterInfo> filters;
71 if (m_fileFilterMgr != nullptr)
73 const int count = m_fileFilterMgr->GetFilterCount();
74 filters.reserve(count);
75 for (int i = 0; i < count; ++i)
77 FileFilterInfo filter;
78 filter.fullpath = m_fileFilterMgr->GetFilterPath(i);
79 filter.name = m_fileFilterMgr->GetFilterName(i);
80 filter.description = m_fileFilterMgr->GetFilterDesc(i);
81 filters.push_back(filter);
84 selected = m_sFileFilterPath;
89 * @brief Return name of filter in given file.
90 * If no filter cannot be found, return empty string.
91 * @param [in] filterPath Path to filterfile.
92 * @sa FileFilterHelper::GetFileFilterPath()
94 String FileFilterHelper::GetFileFilterName(const String& filterPath) const
98 vector<FileFilterInfo> filters = GetFileFilters(selected);
99 vector<FileFilterInfo>::const_iterator iter = filters.begin();
100 while (iter != filters.end())
102 if ((*iter).fullpath == filterPath)
113 * @brief Return path to filter with given name.
114 * @param [in] filterName Name of filter.
115 * @sa FileFilterHelper::GetFileFilterName()
117 String FileFilterHelper::GetFileFilterPath(const String& filterName) const
121 vector<FileFilterInfo> filters = GetFileFilters(selected);
122 vector<FileFilterInfo>::const_iterator iter = filters.begin();
123 while (iter != filters.end())
125 if ((*iter).name == filterName)
127 path = (*iter).fullpath;
136 * @brief Set User's filter folder.
137 * @param [in] filterPath Location of User's filters.
139 void FileFilterHelper::SetUserFilterPath(const String & filterPath)
141 m_sUserSelFilterPath = filterPath;
142 paths::normalize(m_sUserSelFilterPath);
146 * @brief Select between mask and filterfile.
147 * @param [in] bUseMask If true we use mask instead of filter files.
149 void FileFilterHelper::UseMask(bool bUseMask)
151 m_bUseMask = bUseMask;
154 if (m_pMaskFilter == nullptr)
156 m_pMaskFilter.reset(new FilterList);
161 m_pMaskFilter.reset();
166 * @brief Set filemask for filtering.
167 * @param [in] strMask Mask to set (e.g. *.cpp;*.h).
169 void FileFilterHelper::SetMask(const String& strMask)
173 throw "Filter mask tried to set when masks disabled!";
176 String regExp = ParseExtensions(strMask);
178 std::string regexp_str = ucr::toUTF8(regExp);
180 m_pMaskFilter->RemoveAllFilters();
181 m_pMaskFilter->AddRegExp(regexp_str);
185 * @brief Check if any of filefilter rules match to filename.
187 * @param [in] szFileName Filename to test.
188 * @return true unless we're suppressing this file by filter
190 bool FileFilterHelper::includeFile(const String& szFileName) const
194 if (m_pMaskFilter == nullptr)
196 throw "Use mask set, but no filter rules for mask!";
199 // preprend a backslash if there is none
200 String strFileName = strutils::makelower(szFileName);
201 if (strFileName.empty() || strFileName[0] != '\\')
202 strFileName = _T("\\") + strFileName;
203 // append a point if there is no extension
204 if (strFileName.find('.') == String::npos)
205 strFileName = strFileName + _T(".");
207 return m_pMaskFilter->Match(ucr::toUTF8(strFileName));
211 if (m_fileFilterMgr == nullptr || m_currentFilter ==nullptr)
213 return m_fileFilterMgr->TestFileNameAgainstFilter(m_currentFilter, szFileName);
218 * @brief Check if any of filefilter rules match to directoryname.
220 * @param [in] szFileName Directoryname to test.
221 * @return true unless we're suppressing this directory by filter
223 bool FileFilterHelper::includeDir(const String& szDirName) const
227 // directories have no extension
232 if (m_fileFilterMgr == nullptr || m_currentFilter == nullptr)
236 String strDirName(_T("\\"));
237 strDirName += szDirName;
239 return m_fileFilterMgr->TestDirNameAgainstFilter(m_currentFilter, strDirName);
244 * @brief Load in all filters in a folder.
245 * @param [in] dir Folder from where to load filters.
246 * @param [in] sPattern Wildcard defining files to add to map as filter files.
247 * It is filemask, for example, "*.flt"
249 void FileFilterHelper::LoadFileFilterDirPattern(const String& dir, const String& szPattern)
251 m_fileFilterMgr->LoadFromDirectory(dir, szPattern, FileFilterExt);
255 * @brief Convert user-given extension list to valid regular expression.
256 * @param [in] Extension list/mask to convert to regular expression.
257 * @return Regular expression that matches extension list.
259 String FileFilterHelper::ParseExtensions(const String &extensions) const
263 String ext(extensions);
264 bool bFilterAdded = false;
265 static const TCHAR pszSeps[] = _T(" ;|,:");
267 ext += _T(";"); // Add one separator char to end
268 size_t pos = ext.find_first_of(pszSeps);
270 while (pos != String::npos)
272 String token = ext.substr(0, pos); // Get first extension
273 ext = ext.substr(pos + 1); // Remove extension + separator
275 // Only "*." or "*.something" allowed, other ignored
276 if (token.length() >= 1)
279 String strRegex = token;
280 strutils::replace(strRegex, _T("."), _T("\\."));
281 strutils::replace(strRegex, _T("?"), _T("."));
282 strutils::replace(strRegex, _T("("), _T("\\("));
283 strutils::replace(strRegex, _T(")"), _T("\\)"));
284 strutils::replace(strRegex, _T("["), _T("\\["));
285 strutils::replace(strRegex, _T("]"), _T("\\]"));
286 strutils::replace(strRegex, _T("$"), _T("\\$"));
287 strutils::replace(strRegex, _T("*"), _T(".*"));
289 strPattern += _T("(^|\\\\)") + strRegex;
292 bFilterAdded = false;
294 pos = ext.find_first_of(pszSeps);
295 if (bFilterAdded && pos != String::npos && extensions.length() > 1)
296 strPattern += _T("|");
299 if (strPattern.empty())
300 strParsed = _T(".*"); // Match everything
304 strPattern = strutils::makelower(strPattern);
305 strParsed = strPattern; //+ _T("$");
311 * @brief Returns active filter (or mask string)
312 * @return The active filter.
314 String FileFilterHelper::GetFilterNameOrMask() const
319 sFilter = GetFileFilterName(m_sFileFilterPath);
329 * Simple-to-use function to select filter. This function determines
330 * filter type so caller doesn't need to care about it.
332 * @param [in] filter File mask or filter name.
333 * @return true if given filter was set, false if default filter was set.
334 * @note If function returns false, you should ask filter set with
335 * GetFilterNameOrMask().
337 bool FileFilterHelper::SetFilter(const String &filter)
339 // If filter is empty string set default filter
344 SetFileFilterPath(_T(""));
348 // Remove leading and trailing whitespace characters from the string.
349 String flt = strutils::trim_ws(filter);
351 // Star means we have a file extension mask
352 if (filter.find_first_of(_T("*?")) != -1)
356 SetFileFilterPath(_T(""));
360 String path = GetFileFilterPath(flt);
364 SetFileFilterPath(path);
366 // If filter not found with given name, use default filter
371 SetFileFilterPath(_T(""));
379 * @brief Reloads changed filter files
381 * Checks if filter file has been modified since it was last time
382 * loaded/reloaded. If file has been modified we reload it.
383 * @todo How to handle an error in reloading filter?
385 void FileFilterHelper::ReloadUpdatedFilters()
389 vector<FileFilterInfo> filters = GetFileFilters(selected);
390 vector<FileFilterInfo>::const_iterator iter = filters.begin();
391 while (iter != filters.end())
393 String path = (*iter).fullpath;
395 fileInfo.Update(path);
396 if (fileInfo.mtime != (*iter).fileinfo.mtime ||
397 fileInfo.size != (*iter).fileinfo.size)
399 // Reload filter after changing it
400 int retval = m_fileFilterMgr->ReloadFilterFromDisk(path);
402 if (retval == FILTER_OK)
404 // If it was active filter we have to re-set it
405 if (path == selected)
406 SetFileFilterPath(path);
414 * @brief Load any known file filters
415 * @todo Preserve filter selection? How?
417 void FileFilterHelper::LoadAllFileFilters()
419 // First delete existing filters
420 m_fileFilterMgr->DeleteAllFilters();
422 // Program application directory
423 m_sGlobalFilterPath = paths::ConcatPath(env::GetProgPath(), _T("Filters"));
424 paths::normalize(m_sGlobalFilterPath);
425 String pattern(_T("*"));
426 pattern += FileFilterExt;
427 LoadFileFilterDirPattern(m_sGlobalFilterPath, pattern);
428 if (strutils::compare_nocase(m_sGlobalFilterPath, m_sUserSelFilterPath) != 0)
429 LoadFileFilterDirPattern(m_sUserSelFilterPath, pattern);
433 * @brief Return path to global filters (& create if needed), or empty if cannot create
435 String FileFilterHelper::GetGlobalFilterPathWithCreate() const
437 return paths::EnsurePathExist(m_sGlobalFilterPath);
441 * @brief Return path to user filters (& create if needed), or empty if cannot create
443 String FileFilterHelper::GetUserFilterPathWithCreate() const
445 return paths::EnsurePathExist(m_sUserSelFilterPath);