bump product version to 4.1.6.2
[LibreOffice.git] / fpicker / source / aqua / FilterHelper.mm
blobfd0e47b4a25d73caf02261624bba89c36ab7b56b
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 "sal/config.h"
22 #include <functional>
23 #include <algorithm>
24 #include <osl/mutex.hxx>
25 #include <vcl/svapp.hxx>
27 #include "CFStringUtilities.hxx"
28 #include "NSString_OOoAdditions.hxx"
29 #include "NSURL_OOoAdditions.hxx"
31 #include "FilterHelper.hxx"
33 #pragma mark DEFINES
34 #define CLASS_NAME "FilterEntry"
36 #pragma mark FilterEntry
37 //---------------------------------------------------------------------
38 FilterEntry::FilterEntry( const rtl::OUString& _rTitle, const UnoFilterList& _rSubFilters )
39 :m_sTitle( _rTitle )
40 ,m_aSubFilters( _rSubFilters )
42     DBG_PRINT_ENTRY(CLASS_NAME, __func__, "title", _rTitle);
43     DBG_PRINT_EXIT(CLASS_NAME, __func__);
46 //---------------------------------------------------------------------
47 sal_Bool FilterEntry::hasSubFilters() const
49 //    OSL_TRACE(">>> FilterEntry::%s", __func__);
50     sal_Bool bReturn = ( 0 < m_aSubFilters.getLength() );
51 //    OSL_TRACE("<<< FilterEntry::%s retVal: %d", __func__, bReturn);
52     return bReturn;
55 //---------------------------------------------------------------------
56 sal_Int32 FilterEntry::getSubFilters( UnoFilterList& _rSubFilterList )
58     DBG_PRINT_ENTRY(CLASS_NAME, __func__);
60     _rSubFilterList = m_aSubFilters;
61     sal_Int32 nReturn = m_aSubFilters.getLength();
63     DBG_PRINT_EXIT(CLASS_NAME, __func__, nReturn);
65     return nReturn;
68 #pragma mark statics
69 static bool
70 isFilterString( const rtl::OUString& rFilterString, const char *pMatch )
72     sal_Int32 nIndex = 0;
73     rtl::OUString aToken;
74     bool bIsFilter = true;
76     rtl::OUString aMatch(rtl::OUString::createFromAscii(pMatch));
78     do
79     {
80         aToken = rFilterString.getToken( 0, ';', nIndex );
81         if( !aToken.match( aMatch ) )
82         {
83             bIsFilter = false;
84             break;
85         }
86     }
87     while( nIndex >= 0 );
89     return bIsFilter;
92 //=====================================================================
94 static rtl::OUString
95 shrinkFilterName( const rtl::OUString aFilterName, bool bAllowNoStar = false )
97     // DBG_PRINT_ENTRY(CLASS_NAME, "shrinkFilterName", "filterName", aFilterName);
99     sal_Int32 nBracketEnd = -1;
100     rtl::OUString aRealName(aFilterName);
102     for( sal_Int32 i = aRealName.getLength() - 1; i > 0; i-- )
103     {
104         if( aFilterName[i] == ')' )
105             nBracketEnd = i;
106         else if( aFilterName[i] == '(' )
107         {
108             sal_Int32 nBracketLen = nBracketEnd - i;
109             if( nBracketEnd <= 0 )
110                 continue;
111             if( isFilterString( aFilterName.copy( i + 1, nBracketLen - 1 ), "*." ) )
112                 aRealName = aRealName.replaceAt( i, nBracketLen + 1, rtl::OUString() );
113             else if (bAllowNoStar)
114             {
115                 if( isFilterString( aFilterName.copy( i + 1, nBracketLen - 1 ), ".") )
116                     aRealName = aRealName.replaceAt( i, nBracketLen + 1, rtl::OUString() );
117             }
118         }
119     }
121     return aRealName;
124 //------------------------------------------------------------------------------------
125 namespace {
126     //................................................................................
127     struct FilterTitleMatch : public ::std::unary_function< FilterEntry, bool >
128     {
129 protected:
130         const rtl::OUString rTitle;
132 public:
133         FilterTitleMatch( const rtl::OUString _rTitle ) : rTitle( _rTitle ) { }
135         //............................................................................
136         bool operator () ( const FilterEntry& _rEntry )
137         {
138             sal_Bool bMatch;
139             if( !_rEntry.hasSubFilters() ) {
140                 //first try the complete filter name
141                 rtl::OUString title = _rEntry.getTitle();
142                 bMatch = ( title.equals(rTitle) );
143                 if (!bMatch) {
144                     //we didn't find a match using the full name, let's give it another
145                     //try using the shrunk version
146                     rtl::OUString aShrunkName = shrinkFilterName( _rEntry.getTitle() ).trim();
147                     bMatch = ( aShrunkName.equals(rTitle) );
148                 }
149             }
150             else
151                 // a filter group -> search the sub filters
152                 bMatch =
153                     _rEntry.endSubFilters() != ::std::find_if(
154                                                               _rEntry.beginSubFilters(),
155                                                               _rEntry.endSubFilters(),
156                                                               *this
157                                                               );
159             return bMatch ? true : false;
160         }
162         bool operator () ( const UnoFilterEntry& _rEntry )
163         {
164             rtl::OUString aShrunkName = shrinkFilterName( _rEntry.First );
165             bool retVal = aShrunkName.equals(rTitle);
166             return retVal;
167         }
168     };
171 #undef CLASS_NAME
172 #define CLASS_NAME "FilterHelper"
174 FilterHelper::FilterHelper()
175 : m_pFilterList(NULL)
176 , m_pFilterNames(NULL)
178     DBG_PRINT_ENTRY(CLASS_NAME, __func__);
179     DBG_PRINT_EXIT(CLASS_NAME, __func__);
182 FilterHelper::~FilterHelper()
184     DBG_PRINT_ENTRY(CLASS_NAME, __func__);
186     NSAutoreleasePool *pool = [NSAutoreleasePool new];
188     if (NULL != m_pFilterList) {
189         delete m_pFilterList;
190     }
192     if (NULL != m_pFilterNames) {
193         //we called retain when we added the strings to the list, so we should release them now
194         for (NSStringList::iterator iter = m_pFilterNames->begin(); iter != m_pFilterNames->end(); iter++) {
195             [*iter release];
196         }
197         delete m_pFilterNames;
198     }
200     [pool release];
202     DBG_PRINT_EXIT(CLASS_NAME, __func__);
205 //------------------------------------------------------------------------------------
206 sal_Bool FilterHelper::FilterNameExists( const rtl::OUString rTitle )
208     sal_Bool bRet = sal_False;
210     if( m_pFilterList )
211         bRet =
212             m_pFilterList->end() != ::std::find_if(
213                                                    m_pFilterList->begin(),
214                                                    m_pFilterList->end(),
215                                                    FilterTitleMatch( rTitle )
216                                                    );
218     return bRet;
221 //------------------------------------------------------------------------------------
222 sal_Bool FilterHelper::FilterNameExists( const UnoFilterList& _rGroupedFilters )
224     sal_Bool bRet = sal_False;
226     if( m_pFilterList )
227     {
228         const UnoFilterEntry* pStart = _rGroupedFilters.getConstArray();
229         const UnoFilterEntry* pEnd = pStart + _rGroupedFilters.getLength();
230         for( ; pStart != pEnd; ++pStart )
231             if( m_pFilterList->end() != ::std::find_if(
232                                                         m_pFilterList->begin(),
233                                                         m_pFilterList->end(),
234                                                         FilterTitleMatch( pStart->First ) ) )
235                 break;
237         bRet = (pStart != pEnd);
238     }
240     return bRet;
243 //------------------------------------------------------------------------------------
244 void FilterHelper::ensureFilterList( const ::rtl::OUString& _rInitialCurrentFilter )
246     //OSL_TRACE(">>> FilterHelper::%s", __func__);
247     if( NULL == m_pFilterList )
248     {
249         m_pFilterList = new FilterList;
251         // set the first filter to the current filter
252         m_aCurrentFilter = _rInitialCurrentFilter;
253         OSL_TRACE("ensureFilterList filter:%s", OUStringToOString(m_aCurrentFilter, RTL_TEXTENCODING_UTF8).getStr());
254     }
255     //OSL_TRACE("<<< FilterHelper::%s", __func__);
258 void FilterHelper::SetCurFilter( const rtl::OUString& rFilter )
260     DBG_PRINT_ENTRY(CLASS_NAME, __func__, "filter", rFilter);
262     SolarMutexGuard aGuard;
264     if(m_aCurrentFilter.equals(rFilter) == false)
265     {
266         m_aCurrentFilter = rFilter;
267     }
269     //only for output purposes
270 #if OSL_DEBUG_LEVEL > 1
271     FilterList::iterator aFilter = ::std::find_if(m_pFilterList->begin(), m_pFilterList->end(), FilterTitleMatch(m_aCurrentFilter));
272     if (aFilter != m_pFilterList->end()) {
273         OUStringList suffixes = aFilter->getFilterSuffixList();
274         if (!suffixes.empty()) {
275             OSL_TRACE("Current active suffixes: ");
276             OUStringList::iterator suffIter = suffixes.begin();
277             while(suffIter != suffixes.end()) {
278                 OSL_TRACE("%s", OUStringToOString((*suffIter), RTL_TEXTENCODING_UTF8).getStr());
279                 suffIter++;
280             }
281         }
282     } else {
283         OSL_TRACE("No filter entry was found for that name!");
284     }
285 #endif
287     DBG_PRINT_EXIT(CLASS_NAME, __func__);
290 void FilterHelper::SetFilters()
292     DBG_PRINT_ENTRY(CLASS_NAME, __func__);
294     // set the default filter
295     if( m_aCurrentFilter.getLength() > 0 )
296     {
297         OSL_TRACE( "Setting current filter to %s", OUStringToOString(m_aCurrentFilter, RTL_TEXTENCODING_UTF8).getStr());
299         SetCurFilter( m_aCurrentFilter );
300     }
302     DBG_PRINT_EXIT(CLASS_NAME, __func__);
305 void FilterHelper::appendFilter(const ::rtl::OUString& aTitle, const ::rtl::OUString& aFilterString)
306 throw( ::com::sun::star::lang::IllegalArgumentException, ::com::sun::star::uno::RuntimeException ) {
307     DBG_PRINT_ENTRY(CLASS_NAME, __func__, "title", aTitle, "filter", aFilterString);
309     SolarMutexGuard aGuard;
311     if( FilterNameExists( aTitle ) ) {
312         throw com::sun::star::lang::IllegalArgumentException();
313     }
315     // ensure that we have a filter list
316     ensureFilterList( aTitle );
318     // append the filter
319     OUStringList suffixList;
320     fillSuffixList(suffixList, aFilterString);
321     m_pFilterList->push_back(FilterEntry( aTitle, suffixList ) );
323     DBG_PRINT_EXIT(CLASS_NAME, __func__);
326 void FilterHelper::setCurrentFilter( const ::rtl::OUString& aTitle )
327 throw( ::com::sun::star::lang::IllegalArgumentException, ::com::sun::star::uno::RuntimeException ) {
328     DBG_PRINT_ENTRY(CLASS_NAME, __func__, "aTitle", OUStringToOString(aTitle, RTL_TEXTENCODING_UTF8).getStr());
330     SetCurFilter(aTitle);
332     DBG_PRINT_EXIT(CLASS_NAME, __func__);
335 ::rtl::OUString SAL_CALL FilterHelper::getCurrentFilter(  )
336 throw( ::com::sun::star::uno::RuntimeException ) {
337     DBG_PRINT_ENTRY(CLASS_NAME, __func__);
339     ::rtl::OUString sReturn = (m_aCurrentFilter);
341     DBG_PRINT_EXIT(CLASS_NAME, __func__, OUStringToOString(sReturn, RTL_TEXTENCODING_UTF8).getStr());
343     return sReturn;
346 void SAL_CALL FilterHelper::appendFilterGroup( const ::rtl::OUString& sGroupTitle, const ::com::sun::star::uno::Sequence< ::com::sun::star::beans::StringPair >& aFilters )
347 throw (::com::sun::star::lang::IllegalArgumentException, ::com::sun::star::uno::RuntimeException) {
349     DBG_PRINT_ENTRY(CLASS_NAME, __func__, "title", OUStringToOString(sGroupTitle, RTL_TEXTENCODING_UTF8).getStr());
351     SolarMutexGuard aGuard;
353     //add a separator if this is not the first group to be added
354     sal_Bool bPrependSeparator = m_pFilterList != NULL;
356     // ensure that we have a filter list
357     ::rtl::OUString sInitialCurrentFilter;
358     if( aFilters.getLength() > 0)
359         sInitialCurrentFilter = aFilters[0].First;
360     ensureFilterList( sInitialCurrentFilter );
362     // append the filter
363     if (bPrependSeparator) {
364         rtl::OUString dash("-");
365         OUStringList emptyList;
366         m_pFilterList->push_back(FilterEntry(dash, emptyList));
367     }
369     const com::sun::star::beans::StringPair* pSubFilters   = aFilters.getConstArray();
370     const com::sun::star::beans::StringPair* pSubFiltersEnd = pSubFilters + aFilters.getLength();
371     for( ; pSubFilters != pSubFiltersEnd; ++pSubFilters ) {
372         appendFilter(pSubFilters->First, pSubFilters->Second);
373     }
375     DBG_PRINT_EXIT(CLASS_NAME, __func__);
378 // 'fileAttributesAtPath:traverseLink:' is deprecated 
379 #if HAVE_GCC_PRAGMA_DIAGNOSTIC_MODIFY
380 #pragma GCC diagnostic warning "-Wdeprecated-declarations"
381 #endif
383 sal_Bool FilterHelper::filenameMatchesFilter(NSString* sFilename)
385     DBG_PRINT_ENTRY(CLASS_NAME, __func__);
387     if (m_aCurrentFilter == NULL) {
388         OSL_TRACE("filter name is null");
389         return sal_True;
390     }
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     }
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     }
412     OUStringList suffixList = filter->getFilterSuffixList();
414     {
415         rtl::OUString aName = [sFilename OUString];
416         rtl::OUString allMatcher(".*");
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     }
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     }
434     DBG_PRINT_EXIT(CLASS_NAME, __func__);
436     return sal_False;
439 #if HAVE_GCC_PRAGMA_DIAGNOSTIC_MODIFY
440 #pragma GCC diagnostic error "-Wdeprecated-declarations"
441 #endif
443 FilterList* FilterHelper::getFilterList() {
444     DBG_PRINT_ENTRY(CLASS_NAME, __func__);
445     DBG_PRINT_EXIT(CLASS_NAME, __func__);
447     return m_pFilterList;
450 NSStringList* FilterHelper::getFilterNames() {
451     DBG_PRINT_ENTRY(CLASS_NAME, __func__);
453     if (NULL == m_pFilterList)
454         return NULL;
455     if (NULL == m_pFilterNames) {
456         //build filter names list
457         m_pFilterNames = new NSStringList;
458         for (FilterList::iterator iter = m_pFilterList->begin(); iter != m_pFilterList->end(); iter++) {
459             m_pFilterNames->push_back([[NSString stringWithOUString:iter->getTitle()] retain]);
460         }
461     }
463     DBG_PRINT_EXIT(CLASS_NAME, __func__);
465     return m_pFilterNames;
468 void FilterHelper::SetFilterAtIndex(unsigned index) {
469     DBG_PRINT_ENTRY(CLASS_NAME, __func__, "index", index);
471     if (m_pFilterList->size() <= index) {
472         index = 0;
473     }
474     FilterEntry entry = m_pFilterList->at(index);
475     SetCurFilter(entry.getTitle());
477     DBG_PRINT_EXIT(CLASS_NAME, __func__);
480 void FilterHelper::fillSuffixList(OUStringList& aSuffixList, const ::rtl::OUString& suffixString) {
481     DBG_PRINT_ENTRY(CLASS_NAME, __func__, "aSuffixList", suffixString);
483     sal_Int32 nIndex = 0;
484     do {
485         rtl::OUString aToken = suffixString.getToken( 0, ';', nIndex );
486         aSuffixList.push_back(aToken.copy(1));
487     } while ( nIndex >= 0 );
489     DBG_PRINT_EXIT(CLASS_NAME, __func__);
492 int FilterHelper::getCurrentFilterIndex() {
493     DBG_PRINT_ENTRY(CLASS_NAME, __func__);
495     int result = 0;//default to first filter
496     if (m_aCurrentFilter.getLength() > 0) {
497         int i = 0;
498         for (FilterList::iterator iter = m_pFilterList->begin(); iter != m_pFilterList->end(); iter++, i++) {
499             rtl::OUString aTitle = iter->getTitle();
500             if (m_aCurrentFilter.equals(aTitle)) {
501                 result = i;
502                 break;
503             } else {
504                 aTitle = shrinkFilterName(aTitle).trim();
505                 if (m_aCurrentFilter.equals(aTitle)) {
506                     result = i;
507                     break;
508                 }
509             }
510         }
511     }
513     DBG_PRINT_EXIT(CLASS_NAME, __func__, result);
515     return result;
518 OUStringList FilterHelper::getCurrentFilterSuffixList() {
519     DBG_PRINT_ENTRY(CLASS_NAME, __func__);
521     OUStringList retVal;
522     if (m_aCurrentFilter.getLength() > 0) {
523         for (FilterList::iterator iter = m_pFilterList->begin(); iter != m_pFilterList->end(); iter++) {
524             rtl::OUString aTitle = iter->getTitle();
525             if (m_aCurrentFilter.equals(aTitle)) {
526                 retVal = iter->getFilterSuffixList();
527                 break;
528             } else {
529                 aTitle = shrinkFilterName(aTitle).trim();
530                 if (m_aCurrentFilter.equals(aTitle)) {
531                     retVal = iter->getFilterSuffixList();
532                     break;
533                 }
534             }
535         }
536     }
538     DBG_PRINT_EXIT(CLASS_NAME, __func__);
540     return retVal;
543 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */