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 <osl/mutex.hxx>
25 #include <vcl/svapp.hxx>
26 
27 #include "CFStringUtilities.hxx"
29 #include "NSURL_OOoAdditions.hxx"
30 
31 #include "FilterHelper.hxx"
32 
33 namespace {
34 
35 void fillSuffixList(OUStringList& aSuffixList, const OUString& suffixString) {
36  sal_Int32 nIndex = 0;
37  do {
38  OUString aToken = suffixString.getToken( 0, ';', nIndex );
39  aSuffixList.push_back(aToken.copy(1));
40  } while ( nIndex >= 0 );
41 }
42 
43 }
44 
45 #pragma mark DEFINES
46 
47 #pragma mark FilterEntry
48 
49 FilterEntry::FilterEntry( const OUString& _rTitle, const UnoFilterList& _rSubFilters )
50 :m_sTitle( _rTitle )
51 ,m_aSubFilters( _rSubFilters )
52 {
53 }
54 
55 
56 bool FilterEntry::hasSubFilters() const
57 {
58  bool bReturn = ( 0 < m_aSubFilters.getLength() );
59 
60  return bReturn;
61 }
62 
63 
64 sal_Int32 FilterEntry::getSubFilters( UnoFilterList& _rSubFilterList )
65 {
66  _rSubFilterList = m_aSubFilters;
67  sal_Int32 nReturn = m_aSubFilters.getLength();
68 
69  return nReturn;
70 }
71 
72 #pragma mark statics
73 static bool
74 isFilterString( const OUString& rFilterString, const char *pMatch )
75 {
76  sal_Int32 nIndex = 0;
77  OUString aToken;
78  bool bIsFilter = true;
79 
80  OUString aMatch(OUString::createFromAscii(pMatch));
81 
82  do
83  {
84  aToken = rFilterString.getToken( 0, ';', nIndex );
85  if( !aToken.match( aMatch ) )
86  {
87  bIsFilter = false;
88  break;
89  }
90  }
91  while( nIndex >= 0 );
92 
93  return bIsFilter;
94 }
95 
96 
97 
98 static OUString
99 shrinkFilterName( 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.copy( i + 1, nBracketLen - 1 ), "*." ) )
114  aRealName = aRealName.replaceAt( i, nBracketLen + 1, OUString() );
115  else if (bAllowNoStar)
116  {
117  if( isFilterString( aFilterName.copy( i + 1, nBracketLen - 1 ), ".") )
118  aRealName = aRealName.replaceAt( i, nBracketLen + 1, OUString() );
119  }
120  }
121  }
122 
123  return aRealName;
124 }
125 
126 
127 namespace {
128 
129  struct FilterTitleMatch
130  {
131 protected:
132  const OUString rTitle;
133 
134 public:
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 
197 bool 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 
211 bool 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 
232 void 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 
243 void 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 
263 void FilterHelper::appendFilter(const OUString& aTitle, const OUString& 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 
280 void FilterHelper::setCurrentFilter( const OUString& aTitle )
281 {
282  SetCurFilter(aTitle);
283 }
284 
286 {
287  OUString sReturn = m_aCurrentFilter;
288 
289  return sReturn;
290 }
291 
292 void 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 
318 bool 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 
389 void FilterHelper::SetFilterAtIndex(unsigned index)
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: */
virtual ~FilterHelper()
sal_Int32 nIndex
::std::list< NSString * > NSStringList
void setCurrentFilter(const OUString &aTitle)
OUString m_sTitle
static OUString shrinkFilterName(const OUString &aFilterName, bool bAllowNoStar=false)
Definition: FilterHelper.mm:99
OUString getCurrentFilter()
::std::vector< FilterEntry > FilterList
::std::list< OUString > OUStringList
css::uno::Sequence< UnoFilterEntry > UnoFilterList
void ensureFilterList(const OUString &_rInitialCurrentFilter)
NSStringList * getFilterNames()
void appendFilterGroup(const css::uno::Sequence< css::beans::StringPair > &aFilters)
int i
bool filenameMatchesFilter(NSString *sFilename)
css::beans::StringPair UnoFilterEntry
void SetFilters()
void SetFilterAtIndex(unsigned index)
FilterList * m_pFilterList
rtl::Reference< ParseManager > manager
OUString m_aCurrentFilter
bool FilterNameExists(const OUString &rTitle)
FilterList * getFilterList()
NSStringList * m_pFilterNames
OUStringList getCurrentFilterSuffixList()
void appendFilter(const OUString &aTitle, const OUString &aFilter)
int getCurrentFilterIndex()
NSString * resolveAlias(NSString *i_pSystemPath)
OUString aName
void SetCurFilter(const OUString &rFilter)
static bool isFilterString(const OUString &rFilterString, const char *pMatch)
Definition: FilterHelper.mm:74
Any result
#define SAL_WARN(area, stream)