LibreOffice Module fpicker (master) 1
FilterHelper.mm
Go to the documentation of this file.
1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2/*
3 * This file is part of the LibreOffice project.
4 *
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/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
18 */
19
20#include <sal/config.h>
21#include <sal/log.hxx>
22
23#include <algorithm>
24#include <cstddef>
25#include <string_view>
26#include <o3tl/string_view.hxx>
27#include <osl/mutex.hxx>
28#include <vcl/svapp.hxx>
29
32
33#include "FilterHelper.hxx"
34
35namespace {
36
37void fillSuffixList(OUStringList& aSuffixList, std::u16string_view suffixString) {
38 std::size_t nIndex = 0;
39 do {
40 std::u16string_view aToken = o3tl::getToken( suffixString, u';', nIndex );
41 aSuffixList.push_back(OUString(aToken.substr(1)));
42 } while ( nIndex != std::u16string_view::npos );
43}
44
45}
46
47#pragma mark DEFINES
48
49#pragma mark FilterEntry
50
51FilterEntry::FilterEntry( const OUString& _rTitle, const UnoFilterList& _rSubFilters )
52:m_sTitle( _rTitle )
53,m_aSubFilters( _rSubFilters )
54{
55}
56
57
58bool FilterEntry::hasSubFilters() const
59{
60 bool bReturn = ( 0 < m_aSubFilters.getLength() );
61
62 return bReturn;
63}
64
65
66sal_Int32 FilterEntry::getSubFilters( UnoFilterList& _rSubFilterList )
67{
68 _rSubFilterList = m_aSubFilters;
69 sal_Int32 nReturn = m_aSubFilters.getLength();
70
71 return nReturn;
72}
73
74#pragma mark statics
75static bool
76isFilterString( std::u16string_view rFilterString, std::u16string_view pMatch )
77{
78 std::size_t nIndex = 0;
79 std::u16string_view aToken;
80 bool bIsFilter = true;
81
82 do
83 {
84 aToken = o3tl::getToken( rFilterString, u';', nIndex );
85 if( !o3tl::starts_with( aToken, pMatch ) )
86 {
87 bIsFilter = false;
88 break;
89 }
90 }
91 while( nIndex != std::u16string_view::npos );
92
93 return bIsFilter;
94}
95
96
97
98static OUString
99shrinkFilterName( const OUString& aFilterName, bool bAllowNoStar = false )
100{
101 sal_Int32 nBracketEnd = -1;
102 OUString aRealName(aFilterName);
103
104 for( sal_Int32 i = aRealName.getLength() - 1; i > 0; i-- )
105 {
106 if( aFilterName[i] == ')' )
107 nBracketEnd = i;
108 else if( aFilterName[i] == '(' )
109 {
110 sal_Int32 nBracketLen = nBracketEnd - i;
111 if( nBracketEnd <= 0 )
112 continue;
113 if( isFilterString( aFilterName.subView( i + 1, nBracketLen - 1 ), u"*." ) )
114 aRealName = aRealName.replaceAt( i, nBracketLen + 1, u"" );
115 else if (bAllowNoStar)
116 {
117 if( isFilterString( aFilterName.subView( i + 1, nBracketLen - 1 ), u".") )
118 aRealName = aRealName.replaceAt( i, nBracketLen + 1, u"" );
119 }
120 }
121 }
122
123 return aRealName;
124}
125
126
127namespace {
128
129 struct FilterTitleMatch
130 {
131protected:
132 const OUString rTitle;
133
134public:
135 FilterTitleMatch( const OUString& _rTitle ) : rTitle( _rTitle ) { }
136
137
138 bool operator () ( const FilterEntry& _rEntry )
139 {
140 bool bMatch;
141 if( !_rEntry.hasSubFilters() ) {
142 //first try the complete filter name
143 OUString title = _rEntry.getTitle();
144 bMatch = title.equals(rTitle);
145 if (!bMatch) {
146 //we didn't find a match using the full name, let's give it another
147 //try using the shrunk version
148 OUString aShrunkName = shrinkFilterName( _rEntry.getTitle() ).trim();
149 bMatch = aShrunkName.equals(rTitle);
150 }
151 }
152 else
153 // a filter group -> search the sub filters
154 bMatch =
155 ::std::any_of(_rEntry.beginSubFilters(),
156 _rEntry.endSubFilters(),
157 *this);
158
159 return bMatch;
160 }
161
162 bool operator () ( const UnoFilterEntry& _rEntry )
163 {
164 OUString aShrunkName = shrinkFilterName( _rEntry.First );
165 bool retVal = aShrunkName.equals(rTitle);
166 return retVal;
167 }
168 };
169}
170
172: m_pFilterList(nullptr)
173, m_pFilterNames(nullptr)
174{
175}
176
178{
179 NSAutoreleasePool *pool = [NSAutoreleasePool new];
180
181 if (nullptr != m_pFilterList) {
182 delete m_pFilterList;
183 }
184
185 if (nullptr != m_pFilterNames) {
186 //we called retain when we added the strings to the list, so we should release them now
187 for (NSStringList::iterator iter = m_pFilterNames->begin(); iter != m_pFilterNames->end(); ++iter) {
188 [*iter release];
189 }
190 delete m_pFilterNames;
191 }
192
193 [pool release];
194}
195
196
197bool FilterHelper::FilterNameExists( const OUString& rTitle )
198{
199 bool bRet = false;
200
201 if( m_pFilterList )
202 bRet =
203 ::std::any_of(m_pFilterList->begin(),
204 m_pFilterList->end(),
205 FilterTitleMatch( rTitle ));
206
207 return bRet;
208}
209
210
211bool FilterHelper::FilterNameExists( const UnoFilterList& _rGroupedFilters )
212{
213 bool bRet = false;
214
215 if( m_pFilterList )
216 {
217 const UnoFilterEntry* pStart = _rGroupedFilters.getConstArray();
218 const UnoFilterEntry* pEnd = pStart + _rGroupedFilters.getLength();
219 for( ; pStart != pEnd; ++pStart )
220 if( ::std::any_of(m_pFilterList->begin(),
221 m_pFilterList->end(),
222 FilterTitleMatch( pStart->First ) ) )
223 break;
224
225 bRet = (pStart != pEnd);
226 }
227
228 return bRet;
229}
230
231
232void FilterHelper::ensureFilterList( const OUString& _rInitialCurrentFilter )
233{
234 if( nullptr == m_pFilterList )
235 {
237
238 // set the first filter to the current filter
239 m_aCurrentFilter = _rInitialCurrentFilter;
240 }
241}
242
243void FilterHelper::SetCurFilter( const OUString& rFilter )
244{
245 SolarMutexGuard aGuard;
246
247 if(!m_aCurrentFilter.equals(rFilter))
248 {
249 m_aCurrentFilter = rFilter;
250 }
251
252}
253
255{
256 // set the default filter
257 if( m_aCurrentFilter.getLength() > 0 )
258 {
260 }
261}
262
263void FilterHelper::appendFilter(const OUString& aTitle, std::u16string_view aFilterString)
264{
265 SolarMutexGuard aGuard;
266
267 if( FilterNameExists( aTitle ) ) {
268 throw css::lang::IllegalArgumentException();
269 }
270
271 // ensure that we have a filter list
272 ensureFilterList( aTitle );
273
274 // append the filter
275 OUStringList suffixList;
276 fillSuffixList(suffixList, aFilterString);
277 m_pFilterList->push_back(FilterEntry( aTitle, suffixList ) );
278}
279
280void FilterHelper::setCurrentFilter( const OUString& aTitle )
281{
282 SetCurFilter(aTitle);
283}
284
286{
287 OUString sReturn = m_aCurrentFilter;
288
289 return sReturn;
290}
291
292void FilterHelper::appendFilterGroup( const css::uno::Sequence< css::beans::StringPair >& aFilters )
293{
294 SolarMutexGuard aGuard;
295
296 //add a separator if this is not the first group to be added
297 bool bPrependSeparator = m_pFilterList != nullptr;
298
299 // ensure that we have a filter list
300 OUString sInitialCurrentFilter;
301 if( aFilters.getLength() > 0)
302 sInitialCurrentFilter = aFilters[0].First;
303 ensureFilterList( sInitialCurrentFilter );
304
305 // append the filter
306 if (bPrependSeparator) {
307 OUStringList emptyList;
308 m_pFilterList->push_back(FilterEntry("-", emptyList));
309 }
310
311 const css::beans::StringPair* pSubFilters = aFilters.getConstArray();
312 const css::beans::StringPair* pSubFiltersEnd = pSubFilters + aFilters.getLength();
313 for( ; pSubFilters != pSubFiltersEnd; ++pSubFilters ) {
314 appendFilter(pSubFilters->First, pSubFilters->Second);
315 }
316}
317
318bool FilterHelper::filenameMatchesFilter(NSString* sFilename)
319{
320 if (m_aCurrentFilter.isEmpty()) {
321 SAL_WARN("fpicker", "filter name is empty");
322 return true;
323 }
324
325 NSFileManager *manager = [NSFileManager defaultManager];
326 NSDictionary* pAttribs = [manager attributesOfItemAtPath: sFilename error: nil];
327 if( pAttribs )
328 {
329 NSObject* pType = [pAttribs objectForKey: NSFileType];
330 if( pType && [pType isKindOfClass: [NSString class]] )
331 {
332 NSString* pT = static_cast<NSString*>(pType);
333 if( [pT isEqualToString: NSFileTypeDirectory] ||
334 [pT isEqualToString: NSFileTypeSymbolicLink] )
335 return true;
336 }
337 }
338
339 FilterList::iterator filter = ::std::find_if(m_pFilterList->begin(), m_pFilterList->end(), FilterTitleMatch(m_aCurrentFilter));
340 if (filter == m_pFilterList->end()) {
341 SAL_WARN("fpicker", "filter not found in list");
342 return true;
343 }
344
345 OUStringList suffixList = filter->getFilterSuffixList();
346
347 {
348 OUString aName = [sFilename OUString];
349 for(OUStringList::iterator iter = suffixList.begin(); iter != suffixList.end(); ++iter) {
350 if (*iter == ".*" || aName.endsWithIgnoreAsciiCase(*iter)) {
351 return true;
352 }
353 }
354 }
355
356 // might be an alias
357 NSString* pResolved = resolveAlias( sFilename );
358 if( pResolved )
359 {
360 bool bResult = filenameMatchesFilter( pResolved );
361 [pResolved autorelease];
362 if( bResult )
363 return true;
364 }
365
366 return false;
367}
368
370{
371 return m_pFilterList;
372}
373
375{
376 if (nullptr == m_pFilterList)
377 return nullptr;
378 if (nullptr == m_pFilterNames) {
379 //build filter names list
381 for (FilterList::iterator iter = m_pFilterList->begin(); iter != m_pFilterList->end(); ++iter) {
382 m_pFilterNames->push_back([[NSString stringWithOUString:iter->getTitle()] retain]);
383 }
384 }
385
386 return m_pFilterNames;
387}
388
390{
391 if (m_pFilterList->size() <= index) {
392 index = 0;
393 }
394 FilterEntry entry = m_pFilterList->at(index);
395 SetCurFilter(entry.getTitle());
396}
397
399{
400 int result = 0;//default to first filter
401 if (m_aCurrentFilter.getLength() > 0) {
402 int i = 0;
403 for (FilterList::iterator iter = m_pFilterList->begin(); iter != m_pFilterList->end(); ++iter, ++i) {
404 OUString aTitle = iter->getTitle();
405 if (m_aCurrentFilter.equals(aTitle)) {
406 result = i;
407 break;
408 } else {
409 aTitle = shrinkFilterName(aTitle).trim();
410 if (m_aCurrentFilter.equals(aTitle)) {
411 result = i;
412 break;
413 }
414 }
415 }
416 }
417
418 return result;
419}
420
422{
423 OUStringList retVal;
424 if (m_aCurrentFilter.getLength() > 0) {
425 for (FilterList::iterator iter = m_pFilterList->begin(); iter != m_pFilterList->end(); ++iter) {
426 OUString aTitle = iter->getTitle();
427 if (m_aCurrentFilter.equals(aTitle)) {
428 retVal = iter->getFilterSuffixList();
429 break;
430 } else {
431 aTitle = shrinkFilterName(aTitle).trim();
432 if (m_aCurrentFilter.equals(aTitle)) {
433 retVal = iter->getFilterSuffixList();
434 break;
435 }
436 }
437 }
438 }
439
440 return retVal;
441}
442
443/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
::std::vector< FilterEntry > FilterList
css::uno::Sequence< UnoFilterEntry > UnoFilterList
::std::list< OUString > OUStringList
css::beans::StringPair UnoFilterEntry
::std::list< NSString * > NSStringList
static bool isFilterString(std::u16string_view rFilterString, std::u16string_view pMatch)
Definition: FilterHelper.mm:76
static OUString shrinkFilterName(const OUString &aFilterName, bool bAllowNoStar=false)
Definition: FilterHelper.mm:99
NSString * resolveAlias(NSString *i_pSystemPath)
int getCurrentFilterIndex()
void SetCurFilter(const OUString &rFilter)
OUString getCurrentFilter()
void ensureFilterList(const OUString &_rInitialCurrentFilter)
bool filenameMatchesFilter(NSString *sFilename)
void appendFilter(const OUString &aTitle, std::u16string_view aFilter)
FilterList * m_pFilterList
OUString m_aCurrentFilter
FilterList * getFilterList()
bool FilterNameExists(const OUString &rTitle)
void appendFilterGroup(const css::uno::Sequence< css::beans::StringPair > &aFilters)
NSStringList * m_pFilterNames
OUStringList getCurrentFilterSuffixList()
void SetFilterAtIndex(unsigned index)
virtual ~FilterHelper()
NSStringList * getFilterNames()
void SetFilters()
void setCurrentFilter(const OUString &aTitle)
rtl::Reference< ParseManager > manager
OUString m_sTitle
float u
sal_Int32 nIndex
OUString aName
#define SAL_WARN(area, stream)
int i
index
constexpr bool starts_with(std::basic_string_view< charT, traits > sv, std::basic_string_view< charT, traits > x) noexcept
std::basic_string_view< charT, traits > getToken(std::basic_string_view< charT, traits > sv, charT delimiter, std::size_t &position)
Any result