update emoji autocorrect entries from po-files
[LibreOffice.git] / fpicker / source / aqua / FilterHelper.mm
blobe37378d074dd34a0b8a0437ff1ec2cd685b0b788
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 namespace {
35 void fillSuffixList(OUStringList& aSuffixList, const ::rtl::OUString& suffixString) {
36     sal_Int32 nIndex = 0;
37     do {
38         rtl::OUString aToken = suffixString.getToken( 0, ';', nIndex );
39         aSuffixList.push_back(aToken.copy(1));
40     } while ( nIndex >= 0 );
45 #pragma mark DEFINES
46 #define CLASS_NAME "FilterEntry"
48 #pragma mark FilterEntry
50 FilterEntry::FilterEntry( const rtl::OUString& _rTitle, const UnoFilterList& _rSubFilters )
51 :m_sTitle( _rTitle )
52 ,m_aSubFilters( _rSubFilters )
54     DBG_PRINT_ENTRY(CLASS_NAME, __func__, "title", _rTitle);
55     DBG_PRINT_EXIT(CLASS_NAME, __func__);
59 bool FilterEntry::hasSubFilters() const
61 //    OSL_TRACE(">>> FilterEntry::%s", __func__);
62     bool bReturn = ( 0 < m_aSubFilters.getLength() );
63 //    OSL_TRACE("<<< FilterEntry::%s retVal: %d", __func__, bReturn);
64     return bReturn;
68 sal_Int32 FilterEntry::getSubFilters( UnoFilterList& _rSubFilterList )
70     DBG_PRINT_ENTRY(CLASS_NAME, __func__);
72     _rSubFilterList = m_aSubFilters;
73     sal_Int32 nReturn = m_aSubFilters.getLength();
75     DBG_PRINT_EXIT(CLASS_NAME, __func__, nReturn);
77     return nReturn;
80 #pragma mark statics
81 static bool
82 isFilterString( const rtl::OUString& rFilterString, const char *pMatch )
84     sal_Int32 nIndex = 0;
85     rtl::OUString aToken;
86     bool bIsFilter = true;
88     rtl::OUString aMatch(rtl::OUString::createFromAscii(pMatch));
90     do
91     {
92         aToken = rFilterString.getToken( 0, ';', nIndex );
93         if( !aToken.match( aMatch ) )
94         {
95             bIsFilter = false;
96             break;
97         }
98     }
99     while( nIndex >= 0 );
101     return bIsFilter;
106 static rtl::OUString
107 shrinkFilterName( const rtl::OUString& aFilterName, bool bAllowNoStar = false )
109     // DBG_PRINT_ENTRY(CLASS_NAME, "shrinkFilterName", "filterName", aFilterName);
111     sal_Int32 nBracketEnd = -1;
112     rtl::OUString aRealName(aFilterName);
114     for( sal_Int32 i = aRealName.getLength() - 1; i > 0; i-- )
115     {
116         if( aFilterName[i] == ')' )
117             nBracketEnd = i;
118         else if( aFilterName[i] == '(' )
119         {
120             sal_Int32 nBracketLen = nBracketEnd - i;
121             if( nBracketEnd <= 0 )
122                 continue;
123             if( isFilterString( aFilterName.copy( i + 1, nBracketLen - 1 ), "*." ) )
124                 aRealName = aRealName.replaceAt( i, nBracketLen + 1, rtl::OUString() );
125             else if (bAllowNoStar)
126             {
127                 if( isFilterString( aFilterName.copy( i + 1, nBracketLen - 1 ), ".") )
128                     aRealName = aRealName.replaceAt( i, nBracketLen + 1, rtl::OUString() );
129             }
130         }
131     }
133     return aRealName;
137 namespace {
139     struct FilterTitleMatch : public ::std::unary_function< FilterEntry, bool >
140     {
141 protected:
142         const rtl::OUString rTitle;
144 public:
145         FilterTitleMatch( const rtl::OUString& _rTitle ) : rTitle( _rTitle ) { }
148         bool operator () ( const FilterEntry& _rEntry )
149         {
150             bool bMatch;
151             if( !_rEntry.hasSubFilters() ) {
152                 //first try the complete filter name
153                 rtl::OUString title = _rEntry.getTitle();
154                 bMatch = title.equals(rTitle);
155                 if (!bMatch) {
156                     //we didn't find a match using the full name, let's give it another
157                     //try using the shrunk version
158                     rtl::OUString aShrunkName = shrinkFilterName( _rEntry.getTitle() ).trim();
159                     bMatch = aShrunkName.equals(rTitle);
160                 }
161             }
162             else
163                 // a filter group -> search the sub filters
164                 bMatch =
165                     _rEntry.endSubFilters() != ::std::find_if(
166                                                               _rEntry.beginSubFilters(),
167                                                               _rEntry.endSubFilters(),
168                                                               *this
169                                                               );
171             return bMatch;
172         }
174         bool operator () ( const UnoFilterEntry& _rEntry )
175         {
176             rtl::OUString aShrunkName = shrinkFilterName( _rEntry.First );
177             bool retVal = aShrunkName.equals(rTitle);
178             return retVal;
179         }
180     };
183 #undef CLASS_NAME
184 #define CLASS_NAME "FilterHelper"
186 FilterHelper::FilterHelper()
187 : m_pFilterList(NULL)
188 , m_pFilterNames(NULL)
190     DBG_PRINT_ENTRY(CLASS_NAME, __func__);
191     DBG_PRINT_EXIT(CLASS_NAME, __func__);
194 FilterHelper::~FilterHelper()
196     DBG_PRINT_ENTRY(CLASS_NAME, __func__);
198     NSAutoreleasePool *pool = [NSAutoreleasePool new];
200     if (NULL != m_pFilterList) {
201         delete m_pFilterList;
202     }
204     if (NULL != m_pFilterNames) {
205         //we called retain when we added the strings to the list, so we should release them now
206         for (NSStringList::iterator iter = m_pFilterNames->begin(); iter != m_pFilterNames->end(); iter++) {
207             [*iter release];
208         }
209         delete m_pFilterNames;
210     }
212     [pool release];
214     DBG_PRINT_EXIT(CLASS_NAME, __func__);
218 bool FilterHelper::FilterNameExists( const rtl::OUString& rTitle )
220     bool bRet = false;
222     if( m_pFilterList )
223         bRet =
224             m_pFilterList->end() != ::std::find_if(
225                                                    m_pFilterList->begin(),
226                                                    m_pFilterList->end(),
227                                                    FilterTitleMatch( rTitle )
228                                                    );
230     return bRet;
234 bool FilterHelper::FilterNameExists( const UnoFilterList& _rGroupedFilters )
236     bool bRet = false;
238     if( m_pFilterList )
239     {
240         const UnoFilterEntry* pStart = _rGroupedFilters.getConstArray();
241         const UnoFilterEntry* pEnd = pStart + _rGroupedFilters.getLength();
242         for( ; pStart != pEnd; ++pStart )
243             if( m_pFilterList->end() != ::std::find_if(
244                                                         m_pFilterList->begin(),
245                                                         m_pFilterList->end(),
246                                                         FilterTitleMatch( pStart->First ) ) )
247                 break;
249         bRet = (pStart != pEnd);
250     }
252     return bRet;
256 void FilterHelper::ensureFilterList( const ::rtl::OUString& _rInitialCurrentFilter )
258     //OSL_TRACE(">>> FilterHelper::%s", __func__);
259     if( NULL == m_pFilterList )
260     {
261         m_pFilterList = new FilterList;
263         // set the first filter to the current filter
264         m_aCurrentFilter = _rInitialCurrentFilter;
265         OSL_TRACE("ensureFilterList filter:%s", OUStringToOString(m_aCurrentFilter, RTL_TEXTENCODING_UTF8).getStr());
266     }
267     //OSL_TRACE("<<< FilterHelper::%s", __func__);
270 void FilterHelper::SetCurFilter( const rtl::OUString& rFilter )
272     DBG_PRINT_ENTRY(CLASS_NAME, __func__, "filter", rFilter);
274     SolarMutexGuard aGuard;
276     if(!m_aCurrentFilter.equals(rFilter))
277     {
278         m_aCurrentFilter = rFilter;
279     }
281     //only for output purposes
282 #if OSL_DEBUG_LEVEL > 1
283     FilterList::iterator aFilter = ::std::find_if(m_pFilterList->begin(), m_pFilterList->end(), FilterTitleMatch(m_aCurrentFilter));
284     if (aFilter != m_pFilterList->end()) {
285         OUStringList suffixes = aFilter->getFilterSuffixList();
286         if (!suffixes.empty()) {
287             OSL_TRACE("Current active suffixes: ");
288             OUStringList::iterator suffIter = suffixes.begin();
289             while(suffIter != suffixes.end()) {
290                 OSL_TRACE("%s", OUStringToOString((*suffIter), RTL_TEXTENCODING_UTF8).getStr());
291                 suffIter++;
292             }
293         }
294     } else {
295         OSL_TRACE("No filter entry was found for that name!");
296     }
297 #endif
299     DBG_PRINT_EXIT(CLASS_NAME, __func__);
302 void FilterHelper::SetFilters()
304     DBG_PRINT_ENTRY(CLASS_NAME, __func__);
306     // set the default filter
307     if( m_aCurrentFilter.getLength() > 0 )
308     {
309         OSL_TRACE( "Setting current filter to %s", OUStringToOString(m_aCurrentFilter, RTL_TEXTENCODING_UTF8).getStr());
311         SetCurFilter( m_aCurrentFilter );
312     }
314     DBG_PRINT_EXIT(CLASS_NAME, __func__);
317 void FilterHelper::appendFilter(const ::rtl::OUString& aTitle, const ::rtl::OUString& aFilterString)
318 throw( ::com::sun::star::lang::IllegalArgumentException, ::com::sun::star::uno::RuntimeException ) {
319     DBG_PRINT_ENTRY(CLASS_NAME, __func__, "title", aTitle, "filter", aFilterString);
321     SolarMutexGuard aGuard;
323     if( FilterNameExists( aTitle ) ) {
324         throw com::sun::star::lang::IllegalArgumentException();
325     }
327     // ensure that we have a filter list
328     ensureFilterList( aTitle );
330     // append the filter
331     OUStringList suffixList;
332     fillSuffixList(suffixList, aFilterString);
333     m_pFilterList->push_back(FilterEntry( aTitle, suffixList ) );
335     DBG_PRINT_EXIT(CLASS_NAME, __func__);
338 void FilterHelper::setCurrentFilter( const ::rtl::OUString& aTitle )
339 throw( ::com::sun::star::lang::IllegalArgumentException, ::com::sun::star::uno::RuntimeException ) {
340     DBG_PRINT_ENTRY(CLASS_NAME, __func__, "aTitle", OUStringToOString(aTitle, RTL_TEXTENCODING_UTF8).getStr());
342     SetCurFilter(aTitle);
344     DBG_PRINT_EXIT(CLASS_NAME, __func__);
347 ::rtl::OUString SAL_CALL FilterHelper::getCurrentFilter(  )
348 throw( ::com::sun::star::uno::RuntimeException ) {
349     DBG_PRINT_ENTRY(CLASS_NAME, __func__);
351     ::rtl::OUString sReturn = (m_aCurrentFilter);
353     DBG_PRINT_EXIT(CLASS_NAME, __func__, OUStringToOString(sReturn, RTL_TEXTENCODING_UTF8).getStr());
355     return sReturn;
358 void SAL_CALL FilterHelper::appendFilterGroup( const ::rtl::OUString& sGroupTitle, const ::com::sun::star::uno::Sequence< ::com::sun::star::beans::StringPair >& aFilters )
359 throw (::com::sun::star::lang::IllegalArgumentException, ::com::sun::star::uno::RuntimeException) {
361     DBG_PRINT_ENTRY(CLASS_NAME, __func__, "title", OUStringToOString(sGroupTitle, RTL_TEXTENCODING_UTF8).getStr());
363     SolarMutexGuard aGuard;
365     //add a separator if this is not the first group to be added
366     bool bPrependSeparator = m_pFilterList != NULL;
368     // ensure that we have a filter list
369     ::rtl::OUString sInitialCurrentFilter;
370     if( aFilters.getLength() > 0)
371         sInitialCurrentFilter = aFilters[0].First;
372     ensureFilterList( sInitialCurrentFilter );
374     // append the filter
375     if (bPrependSeparator) {
376         rtl::OUString dash("-");
377         OUStringList emptyList;
378         m_pFilterList->push_back(FilterEntry(dash, emptyList));
379     }
381     const com::sun::star::beans::StringPair* pSubFilters   = aFilters.getConstArray();
382     const com::sun::star::beans::StringPair* pSubFiltersEnd = pSubFilters + aFilters.getLength();
383     for( ; pSubFilters != pSubFiltersEnd; ++pSubFilters ) {
384         appendFilter(pSubFilters->First, pSubFilters->Second);
385     }
387     DBG_PRINT_EXIT(CLASS_NAME, __func__);
390 bool FilterHelper::filenameMatchesFilter(NSString* sFilename)
392     DBG_PRINT_ENTRY(CLASS_NAME, __func__);
394     if (m_aCurrentFilter == NULL) {
395         OSL_TRACE("filter name is null");
396         return true;
397     }
399     NSFileManager *manager = [NSFileManager defaultManager];
400     NSDictionary* pAttribs = [manager attributesOfItemAtPath: sFilename error: nil];
401     if( pAttribs )
402     {
403         NSObject* pType = [pAttribs objectForKey: NSFileType];
404         if( pType && [pType isKindOfClass: [NSString class]] )
405         {
406             NSString* pT = (NSString*)pType;
407             if( [pT isEqualToString: NSFileTypeDirectory]    ||
408                 [pT isEqualToString: NSFileTypeSymbolicLink] )
409                 return true;
410         }
411     }
413     FilterList::iterator filter = ::std::find_if(m_pFilterList->begin(), m_pFilterList->end(), FilterTitleMatch(m_aCurrentFilter));
414     if (filter == m_pFilterList->end()) {
415         OSL_TRACE("filter not found in list");
416         return true;
417     }
419     OUStringList suffixList = filter->getFilterSuffixList();
421     {
422         rtl::OUString aName = [sFilename OUString];
423         rtl::OUString allMatcher(".*");
424         for(OUStringList::iterator iter = suffixList.begin(); iter != suffixList.end(); iter++) {
425             if (aName.matchIgnoreAsciiCase(*iter, aName.getLength() - (*iter).getLength()) || ((*iter).equals(allMatcher))) {
426                 return true;
427             }
428         }
429     }
431     // might be an alias
432     NSString* pResolved = resolveAlias( sFilename );
433     if( pResolved )
434     {
435         bool bResult = filenameMatchesFilter( pResolved );
436         [pResolved autorelease];
437         if( bResult )
438             return true;
439     }
441     DBG_PRINT_EXIT(CLASS_NAME, __func__);
443     return false;
446 FilterList* FilterHelper::getFilterList() {
447     DBG_PRINT_ENTRY(CLASS_NAME, __func__);
448     DBG_PRINT_EXIT(CLASS_NAME, __func__);
450     return m_pFilterList;
453 NSStringList* FilterHelper::getFilterNames() {
454     DBG_PRINT_ENTRY(CLASS_NAME, __func__);
456     if (NULL == m_pFilterList)
457         return NULL;
458     if (NULL == m_pFilterNames) {
459         //build filter names list
460         m_pFilterNames = new NSStringList;
461         for (FilterList::iterator iter = m_pFilterList->begin(); iter != m_pFilterList->end(); iter++) {
462             m_pFilterNames->push_back([[NSString stringWithOUString:iter->getTitle()] retain]);
463         }
464     }
466     DBG_PRINT_EXIT(CLASS_NAME, __func__);
468     return m_pFilterNames;
471 void FilterHelper::SetFilterAtIndex(unsigned index) {
472     DBG_PRINT_ENTRY(CLASS_NAME, __func__, "index", index);
474     if (m_pFilterList->size() <= index) {
475         index = 0;
476     }
477     FilterEntry entry = m_pFilterList->at(index);
478     SetCurFilter(entry.getTitle());
480     DBG_PRINT_EXIT(CLASS_NAME, __func__);
483 int FilterHelper::getCurrentFilterIndex() {
484     DBG_PRINT_ENTRY(CLASS_NAME, __func__);
486     int result = 0;//default to first filter
487     if (m_aCurrentFilter.getLength() > 0) {
488         int i = 0;
489         for (FilterList::iterator iter = m_pFilterList->begin(); iter != m_pFilterList->end(); iter++, i++) {
490             rtl::OUString aTitle = iter->getTitle();
491             if (m_aCurrentFilter.equals(aTitle)) {
492                 result = i;
493                 break;
494             } else {
495                 aTitle = shrinkFilterName(aTitle).trim();
496                 if (m_aCurrentFilter.equals(aTitle)) {
497                     result = i;
498                     break;
499                 }
500             }
501         }
502     }
504     DBG_PRINT_EXIT(CLASS_NAME, __func__, result);
506     return result;
509 OUStringList FilterHelper::getCurrentFilterSuffixList() {
510     DBG_PRINT_ENTRY(CLASS_NAME, __func__);
512     OUStringList retVal;
513     if (m_aCurrentFilter.getLength() > 0) {
514         for (FilterList::iterator iter = m_pFilterList->begin(); iter != m_pFilterList->end(); iter++) {
515             rtl::OUString aTitle = iter->getTitle();
516             if (m_aCurrentFilter.equals(aTitle)) {
517                 retVal = iter->getFilterSuffixList();
518                 break;
519             } else {
520                 aTitle = shrinkFilterName(aTitle).trim();
521                 if (m_aCurrentFilter.equals(aTitle)) {
522                     retVal = iter->getFilterSuffixList();
523                     break;
524                 }
525             }
526         }
527     }
529     DBG_PRINT_EXIT(CLASS_NAME, __func__);
531     return retVal;
534 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */