Bump for 4.0-11
[LibreOffice.git] / fpicker / source / aqua / FilterHelper.mm
blobb3ccbcdbeb626d270958e1ca03b3f1c11581f2a1
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  */
20 #include <functional>
21 #include <algorithm>
22 #include <osl/mutex.hxx>
23 #include <vcl/svapp.hxx>
25 #include "CFStringUtilities.hxx"
26 #include "NSString_OOoAdditions.hxx"
27 #include "NSURL_OOoAdditions.hxx"
29 #include "FilterHelper.hxx"
31 #pragma mark DEFINES
32 #define CLASS_NAME "FilterEntry"
34 #pragma mark FilterEntry
35 //---------------------------------------------------------------------
36 FilterEntry::FilterEntry( const rtl::OUString& _rTitle, const UnoFilterList& _rSubFilters )
37 :m_sTitle( _rTitle )
38 ,m_aSubFilters( _rSubFilters )
40     DBG_PRINT_ENTRY(CLASS_NAME, __func__, "title", _rTitle);
41     DBG_PRINT_EXIT(CLASS_NAME, __func__);
44 //---------------------------------------------------------------------
45 sal_Bool FilterEntry::hasSubFilters() const
47 //    OSL_TRACE(">>> FilterEntry::%s", __func__);
48     sal_Bool bReturn = ( 0 < m_aSubFilters.getLength() );
49 //    OSL_TRACE("<<< FilterEntry::%s retVal: %d", __func__, bReturn);
50     return bReturn;
53 //---------------------------------------------------------------------
54 sal_Int32 FilterEntry::getSubFilters( UnoFilterList& _rSubFilterList )
56     DBG_PRINT_ENTRY(CLASS_NAME, __func__);
58     _rSubFilterList = m_aSubFilters;
59     sal_Int32 nReturn = m_aSubFilters.getLength();
61     DBG_PRINT_EXIT(CLASS_NAME, __func__, nReturn);
63     return nReturn;
66 #pragma mark statics
67 static bool
68 isFilterString( const rtl::OUString& rFilterString, const char *pMatch )
70     sal_Int32 nIndex = 0;
71     rtl::OUString aToken;
72     bool bIsFilter = true;
74     rtl::OUString aMatch(rtl::OUString::createFromAscii(pMatch));
76     do
77     {
78         aToken = rFilterString.getToken( 0, ';', nIndex );
79         if( !aToken.match( aMatch ) )
80         {
81             bIsFilter = false;
82             break;
83         }
84     }
85     while( nIndex >= 0 );
87     return bIsFilter;
90 //=====================================================================
92 static rtl::OUString
93 shrinkFilterName( const rtl::OUString aFilterName, bool bAllowNoStar = false )
95     // DBG_PRINT_ENTRY(CLASS_NAME, "shrinkFilterName", "filterName", aFilterName);
97     sal_Int32 nBracketEnd = -1;
98     rtl::OUString aRealName(aFilterName);
100     for( sal_Int32 i = aRealName.getLength() - 1; i > 0; i-- )
101     {
102         if( aFilterName[i] == ')' )
103             nBracketEnd = i;
104         else if( aFilterName[i] == '(' )
105         {
106             sal_Int32 nBracketLen = nBracketEnd - i;
107             if( nBracketEnd <= 0 )
108                 continue;
109             if( isFilterString( aFilterName.copy( i + 1, nBracketLen - 1 ), "*." ) )
110                 aRealName = aRealName.replaceAt( i, nBracketLen + 1, rtl::OUString() );
111             else if (bAllowNoStar)
112             {
113                 if( isFilterString( aFilterName.copy( i + 1, nBracketLen - 1 ), ".") )
114                     aRealName = aRealName.replaceAt( i, nBracketLen + 1, rtl::OUString() );
115             }
116         }
117     }
119     return aRealName;
122 //------------------------------------------------------------------------------------
123 namespace {
124     //................................................................................
125     struct FilterTitleMatch : public ::std::unary_function< FilterEntry, bool >
126     {
127 protected:
128         const rtl::OUString rTitle;
130 public:
131         FilterTitleMatch( const rtl::OUString _rTitle ) : rTitle( _rTitle ) { }
133         //............................................................................
134         bool operator () ( const FilterEntry& _rEntry )
135         {
136             sal_Bool bMatch;
137             if( !_rEntry.hasSubFilters() ) {
138                 //first try the complete filter name
139                 rtl::OUString title = _rEntry.getTitle();
140                 bMatch = ( title.equals(rTitle) );
141                 if (!bMatch) {
142                     //we didn't find a match using the full name, let's give it another
143                     //try using the shrunk version
144                     rtl::OUString aShrunkName = shrinkFilterName( _rEntry.getTitle() ).trim();
145                     bMatch = ( aShrunkName.equals(rTitle) );
146                 }
147             }
148             else
149                 // a filter group -> search the sub filters
150                 bMatch =
151                     _rEntry.endSubFilters() != ::std::find_if(
152                                                               _rEntry.beginSubFilters(),
153                                                               _rEntry.endSubFilters(),
154                                                               *this
155                                                               );
157             return bMatch ? true : false;
158         }
160         bool operator () ( const UnoFilterEntry& _rEntry )
161         {
162             rtl::OUString aShrunkName = shrinkFilterName( _rEntry.First );
163             bool retVal = aShrunkName.equals(rTitle);
164             return retVal;
165         }
166     };
169 #undef CLASS_NAME
170 #define CLASS_NAME "FilterHelper"
172 FilterHelper::FilterHelper()
173 : m_pFilterList(NULL)
174 , m_pFilterNames(NULL)
176     DBG_PRINT_ENTRY(CLASS_NAME, __func__);
177     DBG_PRINT_EXIT(CLASS_NAME, __func__);
180 FilterHelper::~FilterHelper()
182     DBG_PRINT_ENTRY(CLASS_NAME, __func__);
184     NSAutoreleasePool *pool = [NSAutoreleasePool new];
186     if (NULL != m_pFilterList) {
187         delete m_pFilterList;
188     }
190     if (NULL != m_pFilterNames) {
191         //we called retain when we added the strings to the list, so we should release them now
192         for (NSStringList::iterator iter = m_pFilterNames->begin(); iter != m_pFilterNames->end(); iter++) {
193             [*iter release];
194         }
195         delete m_pFilterNames;
196     }
198     [pool release];
200     DBG_PRINT_EXIT(CLASS_NAME, __func__);
203 //------------------------------------------------------------------------------------
204 sal_Bool FilterHelper::FilterNameExists( const rtl::OUString rTitle )
206     sal_Bool bRet = sal_False;
208     if( m_pFilterList )
209         bRet =
210             m_pFilterList->end() != ::std::find_if(
211                                                    m_pFilterList->begin(),
212                                                    m_pFilterList->end(),
213                                                    FilterTitleMatch( rTitle )
214                                                    );
216     return bRet;
219 //------------------------------------------------------------------------------------
220 sal_Bool FilterHelper::FilterNameExists( const UnoFilterList& _rGroupedFilters )
222     sal_Bool bRet = sal_False;
224     if( m_pFilterList )
225     {
226         const UnoFilterEntry* pStart = _rGroupedFilters.getConstArray();
227         const UnoFilterEntry* pEnd = pStart + _rGroupedFilters.getLength();
228         for( ; pStart != pEnd; ++pStart )
229             if( m_pFilterList->end() != ::std::find_if(
230                                                         m_pFilterList->begin(),
231                                                         m_pFilterList->end(),
232                                                         FilterTitleMatch( pStart->First ) ) )
233                 break;
235         bRet = (pStart != pEnd);
236     }
238     return bRet;
241 //------------------------------------------------------------------------------------
242 void FilterHelper::ensureFilterList( const ::rtl::OUString& _rInitialCurrentFilter )
244     //OSL_TRACE(">>> FilterHelper::%s", __func__);
245     if( NULL == m_pFilterList )
246     {
247         m_pFilterList = new FilterList;
249         // set the first filter to the current filter
250         m_aCurrentFilter = _rInitialCurrentFilter;
251         OSL_TRACE("ensureFilterList filter:%s", OUStringToOString(m_aCurrentFilter, RTL_TEXTENCODING_UTF8).getStr());
252     }
253     //OSL_TRACE("<<< FilterHelper::%s", __func__);
256 void FilterHelper::SetCurFilter( const rtl::OUString& rFilter )
258     DBG_PRINT_ENTRY(CLASS_NAME, __func__, "filter", rFilter);
260     SolarMutexGuard aGuard;
262     if(m_aCurrentFilter.equals(rFilter) == false)
263     {
264         m_aCurrentFilter = rFilter;
265     }
267     //only for output purposes
268 #if OSL_DEBUG_LEVEL > 1
269     FilterList::iterator aFilter = ::std::find_if(m_pFilterList->begin(), m_pFilterList->end(), FilterTitleMatch(m_aCurrentFilter));
270     if (aFilter != m_pFilterList->end()) {
271         OUStringList suffixes = aFilter->getFilterSuffixList();
272         if (!suffixes.empty()) {
273             OSL_TRACE("Current active suffixes: ");
274             OUStringList::iterator suffIter = suffixes.begin();
275             while(suffIter != suffixes.end()) {
276                 OSL_TRACE("%s", OUStringToOString((*suffIter), RTL_TEXTENCODING_UTF8).getStr());
277                 suffIter++;
278             }
279         }
280     } else {
281         OSL_TRACE("No filter entry was found for that name!");
282     }
283 #endif
285     DBG_PRINT_EXIT(CLASS_NAME, __func__);
288 void FilterHelper::SetFilters()
290     DBG_PRINT_ENTRY(CLASS_NAME, __func__);
292     // set the default filter
293     if( m_aCurrentFilter.getLength() > 0 )
294     {
295         OSL_TRACE( "Setting current filter to %s", OUStringToOString(m_aCurrentFilter, RTL_TEXTENCODING_UTF8).getStr());
297         SetCurFilter( m_aCurrentFilter );
298     }
300     DBG_PRINT_EXIT(CLASS_NAME, __func__);
303 void FilterHelper::appendFilter(const ::rtl::OUString& aTitle, const ::rtl::OUString& aFilterString)
304 throw( ::com::sun::star::lang::IllegalArgumentException, ::com::sun::star::uno::RuntimeException ) {
305     DBG_PRINT_ENTRY(CLASS_NAME, __func__, "title", aTitle, "filter", aFilterString);
307     SolarMutexGuard aGuard;
309     if( FilterNameExists( aTitle ) ) {
310         throw com::sun::star::lang::IllegalArgumentException();
311     }
313     // ensure that we have a filter list
314     ensureFilterList( aTitle );
316     // append the filter
317     OUStringList suffixList;
318     fillSuffixList(suffixList, aFilterString);
319     m_pFilterList->push_back(FilterEntry( aTitle, suffixList ) );
321     DBG_PRINT_EXIT(CLASS_NAME, __func__);
324 void FilterHelper::setCurrentFilter( const ::rtl::OUString& aTitle )
325 throw( ::com::sun::star::lang::IllegalArgumentException, ::com::sun::star::uno::RuntimeException ) {
326     DBG_PRINT_ENTRY(CLASS_NAME, __func__, "aTitle", OUStringToOString(aTitle, RTL_TEXTENCODING_UTF8).getStr());
328     SetCurFilter(aTitle);
330     DBG_PRINT_EXIT(CLASS_NAME, __func__);
333 ::rtl::OUString SAL_CALL FilterHelper::getCurrentFilter(  )
334 throw( ::com::sun::star::uno::RuntimeException ) {
335     DBG_PRINT_ENTRY(CLASS_NAME, __func__);
337     ::rtl::OUString sReturn = (m_aCurrentFilter);
339     DBG_PRINT_EXIT(CLASS_NAME, __func__, OUStringToOString(sReturn, RTL_TEXTENCODING_UTF8).getStr());
341     return sReturn;
344 void SAL_CALL FilterHelper::appendFilterGroup( const ::rtl::OUString& sGroupTitle, const ::com::sun::star::uno::Sequence< ::com::sun::star::beans::StringPair >& aFilters )
345 throw (::com::sun::star::lang::IllegalArgumentException, ::com::sun::star::uno::RuntimeException) {
347     DBG_PRINT_ENTRY(CLASS_NAME, __func__, "title", OUStringToOString(sGroupTitle, RTL_TEXTENCODING_UTF8).getStr());
349     SolarMutexGuard aGuard;
351     //add a separator if this is not the first group to be added
352     sal_Bool bPrependSeparator = m_pFilterList != NULL;
354     // ensure that we have a filter list
355     ::rtl::OUString sInitialCurrentFilter;
356     if( aFilters.getLength() > 0)
357         sInitialCurrentFilter = aFilters[0].First;
358     ensureFilterList( sInitialCurrentFilter );
360     // append the filter
361     if (bPrependSeparator) {
362         rtl::OUString dash(RTL_CONSTASCII_USTRINGPARAM("-"));
363         OUStringList emptyList;
364         m_pFilterList->push_back(FilterEntry(dash, emptyList));
365     }
367     const com::sun::star::beans::StringPair* pSubFilters   = aFilters.getConstArray();
368     const com::sun::star::beans::StringPair* pSubFiltersEnd = pSubFilters + aFilters.getLength();
369     for( ; pSubFilters != pSubFiltersEnd; ++pSubFilters ) {
370         appendFilter(pSubFilters->First, pSubFilters->Second);
371     }
373     DBG_PRINT_EXIT(CLASS_NAME, __func__);
376 // 'fileAttributesAtPath:traverseLink:' is deprecated 
377 #if defined LIBO_WERROR && defined __GNUC__
378 #define GCC_VERSION (__GNUC__ * 10000 \
379                      + __GNUC_MINOR__ * 100 \
380                      + __GNUC_PATCHLEVEL__)
381 #if GCC_VERSION >= 40201
382 #pragma GCC diagnostic warning "-Wdeprecated-declarations"
383 #define DID_TURN_OFF_DEPRECATED_DECLARATIONS_WARNING
384 #endif
385 #endif
387 sal_Bool FilterHelper::filenameMatchesFilter(NSString* sFilename)
389     DBG_PRINT_ENTRY(CLASS_NAME, __func__);
391     if (m_aCurrentFilter == NULL) {
392         OSL_TRACE("filter name is null");
393         return sal_True;
394     }
396     NSFileManager *manager = [NSFileManager defaultManager];
397     NSDictionary* pAttribs = [manager fileAttributesAtPath: sFilename traverseLink: NO];
398     if( pAttribs )
399     {
400         NSObject* pType = [pAttribs objectForKey: NSFileType];
401         if( pType && [pType isKindOfClass: [NSString class]] )
402         {
403             NSString* pT = (NSString*)pType;
404             if( [pT isEqualToString: NSFileTypeDirectory]    ||
405                 [pT isEqualToString: NSFileTypeSymbolicLink] )
406                 return sal_True;
407         }
408     }
410     FilterList::iterator filter = ::std::find_if(m_pFilterList->begin(), m_pFilterList->end(), FilterTitleMatch(m_aCurrentFilter));
411     if (filter == m_pFilterList->end()) {
412         OSL_TRACE("filter not found in list");
413         return sal_True;
414     }
416     OUStringList suffixList = filter->getFilterSuffixList();
418     {
419         rtl::OUString aName = [sFilename OUString];
420         rtl::OUString allMatcher(RTL_CONSTASCII_USTRINGPARAM(".*"));
421         for(OUStringList::iterator iter = suffixList.begin(); iter != suffixList.end(); iter++) {
422             if (aName.matchIgnoreAsciiCase(*iter, aName.getLength() - (*iter).getLength()) || ((*iter).equals(allMatcher))) {
423                 return sal_True;
424             }
425         }
426     }
428     // might be an alias
429     NSString* pResolved = resolveAlias( sFilename );
430     if( pResolved )
431     {
432         sal_Bool bResult = filenameMatchesFilter( pResolved );
433         [pResolved autorelease];
434         if( bResult )
435             return sal_True;
436     }
438     DBG_PRINT_EXIT(CLASS_NAME, __func__);
440     return sal_False;
443 #ifdef DID_TURN_OFF_DEPRECATED_DECLARATIONS_WARNING
444 #pragma GCC diagnostic error "-Wdeprecated-declarations"
445 #endif
447 FilterList* FilterHelper::getFilterList() {
448     DBG_PRINT_ENTRY(CLASS_NAME, __func__);
449     DBG_PRINT_EXIT(CLASS_NAME, __func__);
451     return m_pFilterList;
454 NSStringList* FilterHelper::getFilterNames() {
455     DBG_PRINT_ENTRY(CLASS_NAME, __func__);
457     if (NULL == m_pFilterList)
458         return NULL;
459     if (NULL == m_pFilterNames) {
460         //build filter names list
461         m_pFilterNames = new NSStringList;
462         for (FilterList::iterator iter = m_pFilterList->begin(); iter != m_pFilterList->end(); iter++) {
463             m_pFilterNames->push_back([[NSString stringWithOUString:iter->getTitle()] retain]);
464         }
465     }
467     DBG_PRINT_EXIT(CLASS_NAME, __func__);
469     return m_pFilterNames;
472 void FilterHelper::SetFilterAtIndex(unsigned index) {
473     DBG_PRINT_ENTRY(CLASS_NAME, __func__, "index", index);
475     if (m_pFilterList->size() <= index) {
476         index = 0;
477     }
478     FilterEntry entry = m_pFilterList->at(index);
479     SetCurFilter(entry.getTitle());
481     DBG_PRINT_EXIT(CLASS_NAME, __func__);
484 void FilterHelper::fillSuffixList(OUStringList& aSuffixList, const ::rtl::OUString& suffixString) {
485     DBG_PRINT_ENTRY(CLASS_NAME, __func__, "aSuffixList", suffixString);
487     sal_Int32 nIndex = 0;
488     do {
489         rtl::OUString aToken = suffixString.getToken( 0, ';', nIndex );
490         aSuffixList.push_back(aToken.copy(1));
491     } while ( nIndex >= 0 );
493     DBG_PRINT_EXIT(CLASS_NAME, __func__);
496 int FilterHelper::getCurrentFilterIndex() {
497     DBG_PRINT_ENTRY(CLASS_NAME, __func__);
499     int result = 0;//default to first filter
500     if (m_aCurrentFilter.getLength() > 0) {
501         int i = 0;
502         for (FilterList::iterator iter = m_pFilterList->begin(); iter != m_pFilterList->end(); iter++, i++) {
503             rtl::OUString aTitle = iter->getTitle();
504             if (m_aCurrentFilter.equals(aTitle)) {
505                 result = i;
506                 break;
507             } else {
508                 aTitle = shrinkFilterName(aTitle).trim();
509                 if (m_aCurrentFilter.equals(aTitle)) {
510                     result = i;
511                     break;
512                 }
513             }
514         }
515     }
517     DBG_PRINT_EXIT(CLASS_NAME, __func__, result);
519     return result;
522 OUStringList FilterHelper::getCurrentFilterSuffixList() {
523     DBG_PRINT_ENTRY(CLASS_NAME, __func__);
525     OUStringList retVal;
526     if (m_aCurrentFilter.getLength() > 0) {
527         for (FilterList::iterator iter = m_pFilterList->begin(); iter != m_pFilterList->end(); iter++) {
528             rtl::OUString aTitle = iter->getTitle();
529             if (m_aCurrentFilter.equals(aTitle)) {
530                 retVal = iter->getFilterSuffixList();
531                 break;
532             } else {
533                 aTitle = shrinkFilterName(aTitle).trim();
534                 if (m_aCurrentFilter.equals(aTitle)) {
535                     retVal = iter->getFilterSuffixList();
536                     break;
537                 }
538             }
539         }
540     }
542     DBG_PRINT_EXIT(CLASS_NAME, __func__);
544     return retVal;
547 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */