xref: /trunk/main/fpicker/source/aqua/FilterHelper.cxx (revision 91144cd0085a7583d2099b982122deb2184ab956)
1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 
23 
24 #include <functional>
25 #include <algorithm>
26 #include <vos/mutex.hxx>
27 #include <vcl/svapp.hxx>
28 
29 // #ifndef _OSL_DIAGNOSE_H_
30 // #include <osl/diagnose.h>
31 // #endif
32 #include "CFStringUtilities.hxx"
33 #include "NSString_OOoAdditions.hxx"
34 #include "NSURL_OOoAdditions.hxx"
35 
36 #include "FilterHelper.hxx"
37 
38 #pragma mark DEFINES
39 #define CLASS_NAME "FilterEntry"
40 
41 #pragma mark FilterEntry
42 //---------------------------------------------------------------------
FilterEntry(const rtl::OUString & _rTitle,const UnoFilterList & _rSubFilters)43 FilterEntry::FilterEntry( const rtl::OUString& _rTitle, const UnoFilterList& _rSubFilters )
44 :m_sTitle( _rTitle )
45 ,m_aSubFilters( _rSubFilters )
46 {
47     DBG_PRINT_ENTRY(CLASS_NAME, __func__, "title", _rTitle);
48     DBG_PRINT_EXIT(CLASS_NAME, __func__);
49 }
50 
51 //---------------------------------------------------------------------
hasSubFilters() const52 sal_Bool FilterEntry::hasSubFilters() const
53 {
54 //    OSL_TRACE(">>> FilterEntry::%s", __func__);
55     sal_Bool bReturn = ( 0 < m_aSubFilters.getLength() );
56 //    OSL_TRACE("<<< FilterEntry::%s retVal: %d", __func__, bReturn);
57     return bReturn;
58 }
59 
60 //---------------------------------------------------------------------
getSubFilters(UnoFilterList & _rSubFilterList)61 sal_Int32 FilterEntry::getSubFilters( UnoFilterList& _rSubFilterList )
62 {
63     DBG_PRINT_ENTRY(CLASS_NAME, __func__);
64 
65     _rSubFilterList = m_aSubFilters;
66     sal_Int32 nReturn = m_aSubFilters.getLength();
67 
68     DBG_PRINT_EXIT(CLASS_NAME, __func__, nReturn);
69 
70     return nReturn;
71 }
72 
73 #pragma mark statics
74 static bool
isFilterString(const rtl::OUString & rFilterString,const char * pMatch)75 isFilterString( const rtl::OUString& rFilterString, const char *pMatch )
76 {
77     sal_Int32 nIndex = 0;
78     rtl::OUString aToken;
79     bool bIsFilter = true;
80 
81     rtl::OUString aMatch(rtl::OUString::createFromAscii(pMatch));
82 
83     do
84     {
85         aToken = rFilterString.getToken( 0, ';', nIndex );
86         if( !aToken.match( aMatch ) )
87         {
88             bIsFilter = false;
89             break;
90         }
91     }
92     while( nIndex >= 0 );
93 
94     return bIsFilter;
95 }
96 
97 //=====================================================================
98 
99 static rtl::OUString
shrinkFilterName(const rtl::OUString aFilterName,bool bAllowNoStar=false)100 shrinkFilterName( const rtl::OUString aFilterName, bool bAllowNoStar = false )
101 {
102     // DBG_PRINT_ENTRY(CLASS_NAME, "shrinkFilterName", "filterName", aFilterName);
103 
104     int i;
105     int nBracketLen = -1;
106     int nBracketEnd = -1;
107     rtl::OUString rFilterName = aFilterName;
108     const sal_Unicode* pStr = rFilterName.getStr();
109     rtl::OUString aRealName = rFilterName;
110 
111     for( i = aRealName.getLength() - 1; i > 0; i-- )
112     {
113         if( pStr[i] == ')' )
114             nBracketEnd = i;
115         else if( pStr[i] == '(' )
116         {
117             nBracketLen = nBracketEnd - i;
118             if( nBracketEnd <= 0 )
119                 continue;
120             if( isFilterString( rFilterName.copy( i + 1, nBracketLen - 1 ), "*." ) )
121                 aRealName = aRealName.replaceAt( i, nBracketLen + 1, rtl::OUString() );
122             else if (bAllowNoStar)
123             {
124                 if( isFilterString( rFilterName.copy( i + 1, nBracketLen - 1 ), ".") )
125                     aRealName = aRealName.replaceAt( i, nBracketLen + 1, rtl::OUString() );
126             }
127         }
128     }
129 
130     return aRealName;
131 }
132 
133 //------------------------------------------------------------------------------------
134 namespace {
135     //................................................................................
136     struct FilterTitleMatch : public ::std::unary_function< FilterEntry, bool >
137     {
138 protected:
139         const rtl::OUString rTitle;
140 
141 public:
FilterTitleMatch__anon243a73870111::FilterTitleMatch142         FilterTitleMatch( const rtl::OUString _rTitle ) : rTitle( _rTitle ) { }
143 
144         //............................................................................
operator ()__anon243a73870111::FilterTitleMatch145         bool operator () ( const FilterEntry& _rEntry )
146         {
147             sal_Bool bMatch;
148             if( !_rEntry.hasSubFilters() ) {
149                 //first try the complete filter name
150                 rtl::OUString title = _rEntry.getTitle();
151                 bMatch = ( title.equals(rTitle) );
152                 if (!bMatch) {
153                     //we didn't find a match using the full name, let's give it another
154                     //try using the shrunk version
155                     rtl::OUString aShrunkName = shrinkFilterName( _rEntry.getTitle() ).trim();
156                     bMatch = ( aShrunkName.equals(rTitle) );
157                 }
158             }
159             else
160                 // a filter group -> search the sub filters
161                 bMatch =
162                     _rEntry.endSubFilters() != ::std::find_if(
163                                                               _rEntry.beginSubFilters(),
164                                                               _rEntry.endSubFilters(),
165                                                               *this
166                                                               );
167 
168             return bMatch ? true : false;
169         }
170 
operator ()__anon243a73870111::FilterTitleMatch171         bool operator () ( const UnoFilterEntry& _rEntry )
172         {
173             rtl::OUString aShrunkName = shrinkFilterName( _rEntry.First );
174             bool retVal = aShrunkName.equals(rTitle);
175             return retVal;
176         }
177     };
178 }
179 
180 #undef CLASS_NAME
181 #define CLASS_NAME "FilterHelper"
182 
FilterHelper()183 FilterHelper::FilterHelper()
184 : m_pFilterList(NULL)
185 , m_pFilterNames(NULL)
186 {
187     DBG_PRINT_ENTRY(CLASS_NAME, __func__);
188     DBG_PRINT_EXIT(CLASS_NAME, __func__);
189 }
190 
~FilterHelper()191 FilterHelper::~FilterHelper()
192 {
193     DBG_PRINT_ENTRY(CLASS_NAME, __func__);
194 
195     NSAutoreleasePool *pool = [NSAutoreleasePool new];
196 
197     if (NULL != m_pFilterList) {
198         delete m_pFilterList;
199     }
200 
201     if (NULL != m_pFilterNames) {
202         //we called retain when we added the strings to the list, so we should release them now
203         for (NSStringList::iterator iter = m_pFilterNames->begin(); iter != m_pFilterNames->end(); iter++) {
204             [*iter release];
205         }
206         delete m_pFilterNames;
207     }
208 
209     [pool release];
210 
211     DBG_PRINT_EXIT(CLASS_NAME, __func__);
212 }
213 
214 //------------------------------------------------------------------------------------
FilterNameExists(const rtl::OUString rTitle)215 sal_Bool FilterHelper::FilterNameExists( const rtl::OUString rTitle )
216 {
217     sal_Bool bRet = sal_False;
218 
219     if( m_pFilterList )
220         bRet =
221             m_pFilterList->end() != ::std::find_if(
222                                                    m_pFilterList->begin(),
223                                                    m_pFilterList->end(),
224                                                    FilterTitleMatch( rTitle )
225                                                    );
226 
227     return bRet;
228 }
229 
230 //------------------------------------------------------------------------------------
FilterNameExists(const UnoFilterList & _rGroupedFilters)231 sal_Bool FilterHelper::FilterNameExists( const UnoFilterList& _rGroupedFilters )
232 {
233     sal_Bool bRet = sal_False;
234 
235     if( m_pFilterList )
236     {
237         const UnoFilterEntry* pStart = _rGroupedFilters.getConstArray();
238         const UnoFilterEntry* pEnd = pStart + _rGroupedFilters.getLength();
239         for( ; pStart != pEnd; ++pStart )
240             if( m_pFilterList->end() != ::std::find_if(
241                                                         m_pFilterList->begin(),
242                                                         m_pFilterList->end(),
243                                                         FilterTitleMatch( pStart->First ) ) )
244                 break;
245 
246         bRet = (pStart != pEnd);
247     }
248 
249     return bRet;
250 }
251 
252 //------------------------------------------------------------------------------------
ensureFilterList(const::rtl::OUString & _rInitialCurrentFilter)253 void FilterHelper::ensureFilterList( const ::rtl::OUString& _rInitialCurrentFilter )
254 {
255     //OSL_TRACE(">>> FilterHelper::%s", __func__);
256     if( NULL == m_pFilterList )
257     {
258         m_pFilterList = new FilterList;
259 
260         // set the first filter to the current filter
261         m_aCurrentFilter = _rInitialCurrentFilter;
262         OSL_TRACE("ensureFilterList filter:%s", OUStringToOString(m_aCurrentFilter, RTL_TEXTENCODING_UTF8).getStr());
263     }
264     //OSL_TRACE("<<< FilterHelper::%s", __func__);
265 }
266 
SetCurFilter(const rtl::OUString & rFilter)267 void FilterHelper::SetCurFilter( const rtl::OUString& rFilter )
268 {
269     DBG_PRINT_ENTRY(CLASS_NAME, __func__, "filter", rFilter);
270 
271     ::vos::OGuard aGuard( Application::GetSolarMutex() );
272 
273     if(m_aCurrentFilter.equals(rFilter) == false)
274     {
275         m_aCurrentFilter = rFilter;
276     }
277 
278     //only for output purposes
279 #if OSL_DEBUG_LEVEL > 1
280     FilterList::iterator aFilter = ::std::find_if(m_pFilterList->begin(), m_pFilterList->end(), FilterTitleMatch(m_aCurrentFilter));
281     if (aFilter != m_pFilterList->end()) {
282         OUStringList suffixes = aFilter->getFilterSuffixList();
283         if (!suffixes.empty()) {
284             OSL_TRACE("Current active suffixes: ");
285             OUStringList::iterator suffIter = suffixes.begin();
286             while(suffIter != suffixes.end()) {
287                 OSL_TRACE("%s", OUStringToOString((*suffIter), RTL_TEXTENCODING_UTF8).getStr());
288                 suffIter++;
289             }
290         }
291     } else {
292         OSL_TRACE("No filter entry was found for that name!");
293     }
294 #endif
295 
296     DBG_PRINT_EXIT(CLASS_NAME, __func__);
297 }
298 
SetFilters()299 void FilterHelper::SetFilters()
300 {
301     DBG_PRINT_ENTRY(CLASS_NAME, __func__);
302 
303     // set the default filter
304     if( m_aCurrentFilter.getLength() > 0 )
305     {
306         OSL_TRACE( "Setting current filter to %s", OUStringToOString(m_aCurrentFilter, RTL_TEXTENCODING_UTF8).getStr());
307 
308         SetCurFilter( m_aCurrentFilter );
309     }
310 
311     DBG_PRINT_EXIT(CLASS_NAME, __func__);
312 }
313 
appendFilter(const::rtl::OUString & aTitle,const::rtl::OUString & aFilterString)314 void FilterHelper::appendFilter(const ::rtl::OUString& aTitle, const ::rtl::OUString& aFilterString) {
315     DBG_PRINT_ENTRY(CLASS_NAME, __func__, "title", aTitle, "filter", aFilterString);
316 
317     ::vos::OGuard aGuard( Application::GetSolarMutex() );
318 
319     if( FilterNameExists( aTitle ) ) {
320         throw com::sun::star::lang::IllegalArgumentException();
321     }
322 
323     // ensure that we have a filter list
324     ensureFilterList( aTitle );
325 
326     // append the filter
327     OUStringList suffixList;
328     fillSuffixList(suffixList, aFilterString);
329     m_pFilterList->push_back(FilterEntry( aTitle, suffixList ) );
330 
331     DBG_PRINT_EXIT(CLASS_NAME, __func__);
332 }
333 
setCurrentFilter(const::rtl::OUString & aTitle)334 void FilterHelper::setCurrentFilter( const ::rtl::OUString& aTitle ) {
335     DBG_PRINT_ENTRY(CLASS_NAME, __func__, "aTitle", OUStringToOString(aTitle, RTL_TEXTENCODING_UTF8).getStr());
336 
337     SetCurFilter(aTitle);
338 
339     DBG_PRINT_EXIT(CLASS_NAME, __func__);
340 }
341 
getCurrentFilter()342 ::rtl::OUString SAL_CALL FilterHelper::getCurrentFilter(  ) {
343     DBG_PRINT_ENTRY(CLASS_NAME, __func__);
344 
345     ::rtl::OUString sReturn = (m_aCurrentFilter);
346 
347     DBG_PRINT_EXIT(CLASS_NAME, __func__, OUStringToOString(sReturn, RTL_TEXTENCODING_UTF8).getStr());
348 
349     return sReturn;
350 }
351 
appendFilterGroup(const::rtl::OUString & sGroupTitle,const::com::sun::star::uno::Sequence<::com::sun::star::beans::StringPair> & aFilters)352 void SAL_CALL FilterHelper::appendFilterGroup( const ::rtl::OUString& sGroupTitle, const ::com::sun::star::uno::Sequence< ::com::sun::star::beans::StringPair >& aFilters ) {
353 
354     DBG_PRINT_ENTRY(CLASS_NAME, __func__, "title", OUStringToOString(sGroupTitle, RTL_TEXTENCODING_UTF8).getStr());
355 
356     ::vos::OGuard aGuard( Application::GetSolarMutex() );
357 
358     //add a separator if this is not the first group to be added
359     sal_Bool bPrependSeparator = m_pFilterList != NULL;
360 
361     // ensure that we have a filter list
362     ::rtl::OUString sInitialCurrentFilter;
363     if( aFilters.getLength() > 0)
364         sInitialCurrentFilter = aFilters[0].First;
365     ensureFilterList( sInitialCurrentFilter );
366 
367     // append the filter
368     if (bPrependSeparator) {
369         rtl::OUString dash = rtl::OUString::createFromAscii("-");
370         OUStringList emptyList;
371         m_pFilterList->push_back(FilterEntry(dash, emptyList));
372     }
373 
374     const com::sun::star::beans::StringPair* pSubFilters   = aFilters.getConstArray();
375     const com::sun::star::beans::StringPair* pSubFiltersEnd = pSubFilters + aFilters.getLength();
376     for( ; pSubFilters != pSubFiltersEnd; ++pSubFilters ) {
377         appendFilter(pSubFilters->First, pSubFilters->Second);
378     }
379 
380     DBG_PRINT_EXIT(CLASS_NAME, __func__);
381 }
382 
filenameMatchesFilter(NSString * sFilename)383 sal_Bool FilterHelper::filenameMatchesFilter(NSString* sFilename)
384 {
385     DBG_PRINT_ENTRY(CLASS_NAME, __func__);
386 
387     if (m_aCurrentFilter == NULL) {
388         OSL_TRACE("filter name is null");
389         return sal_True;
390     }
391 
392     NSFileManager *manager = [NSFileManager defaultManager];
393     NSDictionary* pAttribs = [manager fileAttributesAtPath: sFilename traverseLink: NO];
394     if( pAttribs )
395     {
396         NSObject* pType = [pAttribs objectForKey: NSFileType];
397         if( pType && [pType isKindOfClass: [NSString class]] )
398         {
399             NSString* pT = (NSString*)pType;
400             if( [pT isEqualToString: NSFileTypeDirectory]    ||
401                 [pT isEqualToString: NSFileTypeSymbolicLink] )
402                 return sal_True;
403         }
404     }
405 
406     FilterList::iterator filter = ::std::find_if(m_pFilterList->begin(), m_pFilterList->end(), FilterTitleMatch(m_aCurrentFilter));
407     if (filter == m_pFilterList->end()) {
408         OSL_TRACE("filter not found in list");
409         return sal_True;
410     }
411 
412     OUStringList suffixList = filter->getFilterSuffixList();
413 
414     {
415         rtl::OUString aName = [sFilename OUString];
416         rtl::OUString allMatcher = rtl::OUString::createFromAscii(".*");
417         for(OUStringList::iterator iter = suffixList.begin(); iter != suffixList.end(); iter++) {
418             if (aName.matchIgnoreAsciiCase(*iter, aName.getLength() - (*iter).getLength()) || ((*iter).equals(allMatcher))) {
419                 return sal_True;
420             }
421         }
422     }
423 
424     // might be an alias
425     NSString* pResolved = resolveAlias( sFilename );
426     if( pResolved )
427     {
428         sal_Bool bResult = filenameMatchesFilter( pResolved );
429         [pResolved autorelease];
430         if( bResult )
431             return sal_True;
432     }
433 
434     DBG_PRINT_EXIT(CLASS_NAME, __func__);
435 
436     return sal_False;
437 }
438 
getFilterList()439 FilterList* FilterHelper::getFilterList() {
440     DBG_PRINT_ENTRY(CLASS_NAME, __func__);
441     DBG_PRINT_EXIT(CLASS_NAME, __func__);
442 
443     return m_pFilterList;
444 }
445 
getFilterNames()446 NSStringList* FilterHelper::getFilterNames() {
447     DBG_PRINT_ENTRY(CLASS_NAME, __func__);
448 
449     if (NULL == m_pFilterList)
450         return NULL;
451     if (NULL == m_pFilterNames) {
452         //build filter names list
453         m_pFilterNames = new NSStringList;
454         for (FilterList::iterator iter = m_pFilterList->begin(); iter != m_pFilterList->end(); iter++) {
455             m_pFilterNames->push_back([[NSString stringWithOUString:iter->getTitle()] retain]);
456         }
457     }
458 
459     DBG_PRINT_EXIT(CLASS_NAME, __func__);
460 
461     return m_pFilterNames;
462 }
463 
SetFilterAtIndex(unsigned index)464 void FilterHelper::SetFilterAtIndex(unsigned index) {
465     DBG_PRINT_ENTRY(CLASS_NAME, __func__, "index", index);
466 
467     if (m_pFilterList->size() <= index) {
468         index = 0;
469     }
470     FilterEntry entry = m_pFilterList->at(index);
471     SetCurFilter(entry.getTitle());
472 
473     DBG_PRINT_EXIT(CLASS_NAME, __func__);
474 }
475 
fillSuffixList(OUStringList & aSuffixList,const::rtl::OUString & suffixString)476 void FilterHelper::fillSuffixList(OUStringList& aSuffixList, const ::rtl::OUString& suffixString) {
477     DBG_PRINT_ENTRY(CLASS_NAME, __func__, "aSuffixList", suffixString);
478 
479     sal_Int32 nIndex = 0;
480     do {
481         rtl::OUString aToken = suffixString.getToken( 0, ';', nIndex );
482         aSuffixList.push_back(aToken.copy(1));
483     } while ( nIndex >= 0 );
484 
485     DBG_PRINT_EXIT(CLASS_NAME, __func__);
486 }
487 
getCurrentFilterIndex()488 int FilterHelper::getCurrentFilterIndex() {
489     DBG_PRINT_ENTRY(CLASS_NAME, __func__);
490 
491     int result = 0;//default to first filter
492     if (m_aCurrentFilter.getLength() > 0) {
493         int i = 0;
494         for (FilterList::iterator iter = m_pFilterList->begin(); iter != m_pFilterList->end(); iter++, i++) {
495             rtl::OUString aTitle = iter->getTitle();
496             if (m_aCurrentFilter.equals(aTitle)) {
497                 result = i;
498                 break;
499             } else {
500                 aTitle = shrinkFilterName(aTitle).trim();
501                 if (m_aCurrentFilter.equals(aTitle)) {
502                     result = i;
503                     break;
504                 }
505             }
506         }
507     }
508 
509     DBG_PRINT_EXIT(CLASS_NAME, __func__, result);
510 
511     return result;
512 }
513 
getCurrentFilterSuffixList()514 OUStringList FilterHelper::getCurrentFilterSuffixList() {
515     DBG_PRINT_ENTRY(CLASS_NAME, __func__);
516 
517     OUStringList retVal;
518     if (m_aCurrentFilter.getLength() > 0) {
519         for (FilterList::iterator iter = m_pFilterList->begin(); iter != m_pFilterList->end(); iter++) {
520             rtl::OUString aTitle = iter->getTitle();
521             if (m_aCurrentFilter.equals(aTitle)) {
522                 retVal = iter->getFilterSuffixList();
523                 break;
524             } else {
525                 aTitle = shrinkFilterName(aTitle).trim();
526                 if (m_aCurrentFilter.equals(aTitle)) {
527                     retVal = iter->getFilterSuffixList();
528                     break;
529                 }
530             }
531         }
532     }
533 
534     DBG_PRINT_EXIT(CLASS_NAME, __func__);
535 
536     return retVal;
537 }
538