bump product version to 6.1.0.2
[LibreOffice.git] / fpicker / source / aqua / FilterHelper.mm
blob4bfff715e7487dc0a0f55e34f5604e0079a631a5
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 <algorithm>
23 #include <osl/mutex.hxx>
24 #include <vcl/svapp.hxx>
26 #include "CFStringUtilities.hxx"
27 #include "NSString_OOoAdditions.hxx"
28 #include "NSURL_OOoAdditions.hxx"
30 #include "FilterHelper.hxx"
32 namespace {
34 void fillSuffixList(OUStringList& aSuffixList, const ::rtl::OUString& suffixString) {
35     sal_Int32 nIndex = 0;
36     do {
37         rtl::OUString aToken = suffixString.getToken( 0, ';', nIndex );
38         aSuffixList.push_back(aToken.copy(1));
39     } while ( nIndex >= 0 );
44 #pragma mark DEFINES
46 #pragma mark FilterEntry
48 FilterEntry::FilterEntry( const rtl::OUString& _rTitle, const UnoFilterList& _rSubFilters )
49 :m_sTitle( _rTitle )
50 ,m_aSubFilters( _rSubFilters )
55 bool FilterEntry::hasSubFilters() const
57     bool bReturn = ( 0 < m_aSubFilters.getLength() );
59     return bReturn;
63 sal_Int32 FilterEntry::getSubFilters( UnoFilterList& _rSubFilterList )
65     _rSubFilterList = m_aSubFilters;
66     sal_Int32 nReturn = m_aSubFilters.getLength();
68     return nReturn;
71 #pragma mark statics
72 static bool
73 isFilterString( const rtl::OUString& rFilterString, const char *pMatch )
75     sal_Int32 nIndex = 0;
76     rtl::OUString aToken;
77     bool bIsFilter = true;
79     rtl::OUString aMatch(rtl::OUString::createFromAscii(pMatch));
81     do
82     {
83         aToken = rFilterString.getToken( 0, ';', nIndex );
84         if( !aToken.match( aMatch ) )
85         {
86             bIsFilter = false;
87             break;
88         }
89     }
90     while( nIndex >= 0 );
92     return bIsFilter;
97 static rtl::OUString
98 shrinkFilterName( const rtl::OUString& aFilterName, bool bAllowNoStar = false )
100     sal_Int32 nBracketEnd = -1;
101     rtl::OUString aRealName(aFilterName);
103     for( sal_Int32 i = aRealName.getLength() - 1; i > 0; i-- )
104     {
105         if( aFilterName[i] == ')' )
106             nBracketEnd = i;
107         else if( aFilterName[i] == '(' )
108         {
109             sal_Int32 nBracketLen = nBracketEnd - i;
110             if( nBracketEnd <= 0 )
111                 continue;
112             if( isFilterString( aFilterName.copy( i + 1, nBracketLen - 1 ), "*." ) )
113                 aRealName = aRealName.replaceAt( i, nBracketLen + 1, rtl::OUString() );
114             else if (bAllowNoStar)
115             {
116                 if( isFilterString( aFilterName.copy( i + 1, nBracketLen - 1 ), ".") )
117                     aRealName = aRealName.replaceAt( i, nBracketLen + 1, rtl::OUString() );
118             }
119         }
120     }
122     return aRealName;
126 namespace {
128     struct FilterTitleMatch
129     {
130 protected:
131         const rtl::OUString rTitle;
133 public:
134         FilterTitleMatch( const rtl::OUString& _rTitle ) : rTitle( _rTitle ) { }
137         bool operator () ( const FilterEntry& _rEntry )
138         {
139             bool bMatch;
140             if( !_rEntry.hasSubFilters() ) {
141                 //first try the complete filter name
142                 rtl::OUString title = _rEntry.getTitle();
143                 bMatch = title.equals(rTitle);
144                 if (!bMatch) {
145                     //we didn't find a match using the full name, let's give it another
146                     //try using the shrunk version
147                     rtl::OUString aShrunkName = shrinkFilterName( _rEntry.getTitle() ).trim();
148                     bMatch = aShrunkName.equals(rTitle);
149                 }
150             }
151             else
152                 // a filter group -> search the sub filters
153                 bMatch =
154                     ::std::any_of(_rEntry.beginSubFilters(),
155                                   _rEntry.endSubFilters(),
156                                   *this);
158             return bMatch;
159         }
161         bool operator () ( const UnoFilterEntry& _rEntry )
162         {
163             rtl::OUString aShrunkName = shrinkFilterName( _rEntry.First );
164             bool retVal = aShrunkName.equals(rTitle);
165             return retVal;
166         }
167     };
170 FilterHelper::FilterHelper()
171 : m_pFilterList(nullptr)
172 , m_pFilterNames(nullptr)
176 FilterHelper::~FilterHelper()
178     NSAutoreleasePool *pool = [NSAutoreleasePool new];
180     if (nullptr != m_pFilterList) {
181         delete m_pFilterList;
182     }
184     if (nullptr != m_pFilterNames) {
185         //we called retain when we added the strings to the list, so we should release them now
186         for (NSStringList::iterator iter = m_pFilterNames->begin(); iter != m_pFilterNames->end(); ++iter) {
187             [*iter release];
188         }
189         delete m_pFilterNames;
190     }
192     [pool release];
196 bool FilterHelper::FilterNameExists( const rtl::OUString& rTitle )
198     bool bRet = false;
200     if( m_pFilterList )
201         bRet =
202             ::std::any_of(m_pFilterList->begin(),
203                           m_pFilterList->end(),
204                           FilterTitleMatch( rTitle ));
206     return bRet;
210 bool FilterHelper::FilterNameExists( const UnoFilterList& _rGroupedFilters )
212     bool bRet = false;
214     if( m_pFilterList )
215     {
216         const UnoFilterEntry* pStart = _rGroupedFilters.getConstArray();
217         const UnoFilterEntry* pEnd = pStart + _rGroupedFilters.getLength();
218         for( ; pStart != pEnd; ++pStart )
219             if( ::std::any_of(m_pFilterList->begin(),
220                               m_pFilterList->end(),
221                               FilterTitleMatch( pStart->First ) ) )
222                 break;
224         bRet = (pStart != pEnd);
225     }
227     return bRet;
231 void FilterHelper::ensureFilterList( const ::rtl::OUString& _rInitialCurrentFilter )
233     if( nullptr == m_pFilterList )
234     {
235         m_pFilterList = new FilterList;
237         // set the first filter to the current filter
238         m_aCurrentFilter = _rInitialCurrentFilter;
239     }
242 void FilterHelper::SetCurFilter( const rtl::OUString& rFilter )
244     SolarMutexGuard aGuard;
246     if(!m_aCurrentFilter.equals(rFilter))
247     {
248         m_aCurrentFilter = rFilter;
249     }
253 void FilterHelper::SetFilters()
255     // set the default filter
256     if( m_aCurrentFilter.getLength() > 0 )
257     {
258         SetCurFilter( m_aCurrentFilter );
259     }
262 void FilterHelper::appendFilter(const ::rtl::OUString& aTitle, const ::rtl::OUString& aFilterString)
264     SolarMutexGuard aGuard;
266     if( FilterNameExists( aTitle ) ) {
267         throw css::lang::IllegalArgumentException();
268     }
270     // ensure that we have a filter list
271     ensureFilterList( aTitle );
273     // append the filter
274     OUStringList suffixList;
275     fillSuffixList(suffixList, aFilterString);
276     m_pFilterList->push_back(FilterEntry( aTitle, suffixList ) );
279 void FilterHelper::setCurrentFilter( const ::rtl::OUString& aTitle )
281     SetCurFilter(aTitle);
284 ::rtl::OUString FilterHelper::getCurrentFilter(  )
286     ::rtl::OUString sReturn = m_aCurrentFilter;
288     return sReturn;
291 void FilterHelper::appendFilterGroup( const ::rtl::OUString& /* sGroupTitle */, const css::uno::Sequence< css::beans::StringPair >& aFilters )
293     SolarMutexGuard aGuard;
295     //add a separator if this is not the first group to be added
296     bool bPrependSeparator = m_pFilterList != nullptr;
298     // ensure that we have a filter list
299     ::rtl::OUString sInitialCurrentFilter;
300     if( aFilters.getLength() > 0)
301         sInitialCurrentFilter = aFilters[0].First;
302     ensureFilterList( sInitialCurrentFilter );
304     // append the filter
305     if (bPrependSeparator) {
306         OUStringList emptyList;
307         m_pFilterList->push_back(FilterEntry("-", emptyList));
308     }
310     const css::beans::StringPair* pSubFilters   = aFilters.getConstArray();
311     const css::beans::StringPair* pSubFiltersEnd = pSubFilters + aFilters.getLength();
312     for( ; pSubFilters != pSubFiltersEnd; ++pSubFilters ) {
313         appendFilter(pSubFilters->First, pSubFilters->Second);
314     }
317 bool FilterHelper::filenameMatchesFilter(NSString* sFilename)
319     if (m_aCurrentFilter.isEmpty()) {
320         SAL_WARN("fpicker", "filter name is empty");
321         return true;
322     }
324     NSFileManager *manager = [NSFileManager defaultManager];
325     NSDictionary* pAttribs = [manager attributesOfItemAtPath: sFilename error: nil];
326     if( pAttribs )
327     {
328         NSObject* pType = [pAttribs objectForKey: NSFileType];
329         if( pType && [pType isKindOfClass: [NSString class]] )
330         {
331             NSString* pT = static_cast<NSString*>(pType);
332             if( [pT isEqualToString: NSFileTypeDirectory]    ||
333                 [pT isEqualToString: NSFileTypeSymbolicLink] )
334                 return true;
335         }
336     }
338     FilterList::iterator filter = ::std::find_if(m_pFilterList->begin(), m_pFilterList->end(), FilterTitleMatch(m_aCurrentFilter));
339     if (filter == m_pFilterList->end()) {
340         SAL_WARN("fpicker", "filter not found in list");
341         return true;
342     }
344     OUStringList suffixList = filter->getFilterSuffixList();
346     {
347         rtl::OUString aName = [sFilename OUString];
348         for(OUStringList::iterator iter = suffixList.begin(); iter != suffixList.end(); ++iter) {
349             if (*iter == ".*" || aName.endsWithIgnoreAsciiCase(*iter)) {
350                 return true;
351             }
352         }
353     }
355     // might be an alias
356     NSString* pResolved = resolveAlias( sFilename );
357     if( pResolved )
358     {
359         bool bResult = filenameMatchesFilter( pResolved );
360         [pResolved autorelease];
361         if( bResult )
362             return true;
363     }
365     return false;
368 FilterList* FilterHelper::getFilterList()
370     return m_pFilterList;
373 NSStringList* FilterHelper::getFilterNames()
375     if (nullptr == m_pFilterList)
376         return nullptr;
377     if (nullptr == m_pFilterNames) {
378         //build filter names list
379         m_pFilterNames = new NSStringList;
380         for (FilterList::iterator iter = m_pFilterList->begin(); iter != m_pFilterList->end(); ++iter) {
381             m_pFilterNames->push_back([[NSString stringWithOUString:iter->getTitle()] retain]);
382         }
383     }
385     return m_pFilterNames;
388 void FilterHelper::SetFilterAtIndex(unsigned index)
390     if (m_pFilterList->size() <= index) {
391         index = 0;
392     }
393     FilterEntry entry = m_pFilterList->at(index);
394     SetCurFilter(entry.getTitle());
397 int FilterHelper::getCurrentFilterIndex()
399     int result = 0;//default to first filter
400     if (m_aCurrentFilter.getLength() > 0) {
401         int i = 0;
402         for (FilterList::iterator iter = m_pFilterList->begin(); iter != m_pFilterList->end(); ++iter, ++i) {
403             rtl::OUString aTitle = iter->getTitle();
404             if (m_aCurrentFilter.equals(aTitle)) {
405                 result = i;
406                 break;
407             } else {
408                 aTitle = shrinkFilterName(aTitle).trim();
409                 if (m_aCurrentFilter.equals(aTitle)) {
410                     result = i;
411                     break;
412                 }
413             }
414         }
415     }
417     return result;
420 OUStringList FilterHelper::getCurrentFilterSuffixList()
422     OUStringList retVal;
423     if (m_aCurrentFilter.getLength() > 0) {
424         for (FilterList::iterator iter = m_pFilterList->begin(); iter != m_pFilterList->end(); ++iter) {
425             rtl::OUString aTitle = iter->getTitle();
426             if (m_aCurrentFilter.equals(aTitle)) {
427                 retVal = iter->getFilterSuffixList();
428                 break;
429             } else {
430                 aTitle = shrinkFilterName(aTitle).trim();
431                 if (m_aCurrentFilter.equals(aTitle)) {
432                     retVal = iter->getFilterSuffixList();
433                     break;
434                 }
435             }
436         }
437     }
439     return retVal;
442 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */