Version 6.4.0.0.beta1, tag libreoffice-6.4.0.0.beta1
[LibreOffice.git] / fpicker / source / aqua / FilterHelper.mm
blob5db89a745f6f909da7722a027e386cbdec0ec03f
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>
21 #include <sal/log.hxx>
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 namespace {
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 );
45 #pragma mark DEFINES
47 #pragma mark FilterEntry
49 FilterEntry::FilterEntry( const OUString& _rTitle, const UnoFilterList& _rSubFilters )
50 :m_sTitle( _rTitle )
51 ,m_aSubFilters( _rSubFilters )
56 bool FilterEntry::hasSubFilters() const
58     bool bReturn = ( 0 < m_aSubFilters.getLength() );
60     return bReturn;
64 sal_Int32 FilterEntry::getSubFilters( UnoFilterList& _rSubFilterList )
66     _rSubFilterList = m_aSubFilters;
67     sal_Int32 nReturn = m_aSubFilters.getLength();
69     return nReturn;
72 #pragma mark statics
73 static bool
74 isFilterString( const OUString& rFilterString, const char *pMatch )
76     sal_Int32 nIndex = 0;
77     OUString aToken;
78     bool bIsFilter = true;
80     OUString aMatch(OUString::createFromAscii(pMatch));
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 );
93     return bIsFilter;
98 static OUString
99 shrinkFilterName( const OUString& aFilterName, bool bAllowNoStar = false )
101     sal_Int32 nBracketEnd = -1;
102     OUString aRealName(aFilterName);
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     }
123     return aRealName;
127 namespace {
129     struct FilterTitleMatch
130     {
131 protected:
132         const OUString rTitle;
134 public:
135         FilterTitleMatch( const OUString& _rTitle ) : rTitle( _rTitle ) { }
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);
159             return bMatch;
160         }
162         bool operator () ( const UnoFilterEntry& _rEntry )
163         {
164             OUString aShrunkName = shrinkFilterName( _rEntry.First );
165             bool retVal = aShrunkName.equals(rTitle);
166             return retVal;
167         }
168     };
171 FilterHelper::FilterHelper()
172 : m_pFilterList(nullptr)
173 , m_pFilterNames(nullptr)
177 FilterHelper::~FilterHelper()
179     NSAutoreleasePool *pool = [NSAutoreleasePool new];
181     if (nullptr != m_pFilterList) {
182         delete m_pFilterList;
183     }
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     }
193     [pool release];
197 bool FilterHelper::FilterNameExists( const OUString& rTitle )
199     bool bRet = false;
201     if( m_pFilterList )
202         bRet =
203             ::std::any_of(m_pFilterList->begin(),
204                           m_pFilterList->end(),
205                           FilterTitleMatch( rTitle ));
207     return bRet;
211 bool FilterHelper::FilterNameExists( const UnoFilterList& _rGroupedFilters )
213     bool bRet = false;
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;
225         bRet = (pStart != pEnd);
226     }
228     return bRet;
232 void FilterHelper::ensureFilterList( const OUString& _rInitialCurrentFilter )
234     if( nullptr == m_pFilterList )
235     {
236         m_pFilterList = new FilterList;
238         // set the first filter to the current filter
239         m_aCurrentFilter = _rInitialCurrentFilter;
240     }
243 void FilterHelper::SetCurFilter( const OUString& rFilter )
245     SolarMutexGuard aGuard;
247     if(!m_aCurrentFilter.equals(rFilter))
248     {
249         m_aCurrentFilter = rFilter;
250     }
254 void FilterHelper::SetFilters()
256     // set the default filter
257     if( m_aCurrentFilter.getLength() > 0 )
258     {
259         SetCurFilter( m_aCurrentFilter );
260     }
263 void FilterHelper::appendFilter(const OUString& aTitle, const OUString& aFilterString)
265     SolarMutexGuard aGuard;
267     if( FilterNameExists( aTitle ) ) {
268         throw css::lang::IllegalArgumentException();
269     }
271     // ensure that we have a filter list
272     ensureFilterList( aTitle );
274     // append the filter
275     OUStringList suffixList;
276     fillSuffixList(suffixList, aFilterString);
277     m_pFilterList->push_back(FilterEntry( aTitle, suffixList ) );
280 void FilterHelper::setCurrentFilter( const OUString& aTitle )
282     SetCurFilter(aTitle);
285 OUString FilterHelper::getCurrentFilter(  )
287     OUString sReturn = m_aCurrentFilter;
289     return sReturn;
292 void FilterHelper::appendFilterGroup( const OUString& /* sGroupTitle */, const css::uno::Sequence< css::beans::StringPair >& aFilters )
294     SolarMutexGuard aGuard;
296     //add a separator if this is not the first group to be added
297     bool bPrependSeparator = m_pFilterList != nullptr;
299     // ensure that we have a filter list
300     OUString sInitialCurrentFilter;
301     if( aFilters.getLength() > 0)
302         sInitialCurrentFilter = aFilters[0].First;
303     ensureFilterList( sInitialCurrentFilter );
305     // append the filter
306     if (bPrependSeparator) {
307         OUStringList emptyList;
308         m_pFilterList->push_back(FilterEntry("-", emptyList));
309     }
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     }
318 bool FilterHelper::filenameMatchesFilter(NSString* sFilename)
320     if (m_aCurrentFilter.isEmpty()) {
321         SAL_WARN("fpicker", "filter name is empty");
322         return true;
323     }
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     }
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     }
345     OUStringList suffixList = filter->getFilterSuffixList();
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     }
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     }
366     return false;
369 FilterList* FilterHelper::getFilterList()
371     return m_pFilterList;
374 NSStringList* FilterHelper::getFilterNames()
376     if (nullptr == m_pFilterList)
377         return nullptr;
378     if (nullptr == m_pFilterNames) {
379         //build filter names list
380         m_pFilterNames = new NSStringList;
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     }
386     return m_pFilterNames;
389 void FilterHelper::SetFilterAtIndex(unsigned index)
391     if (m_pFilterList->size() <= index) {
392         index = 0;
393     }
394     FilterEntry entry = m_pFilterList->at(index);
395     SetCurFilter(entry.getTitle());
398 int FilterHelper::getCurrentFilterIndex()
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     }
418     return result;
421 OUStringList FilterHelper::getCurrentFilterSuffixList()
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     }
440     return retVal;
443 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */