1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include <sal/config.h>
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"
34 void fillSuffixList(OUStringList& aSuffixList, const ::rtl::OUString& suffixString) {
37 rtl::OUString aToken = suffixString.getToken( 0, ';', nIndex );
38 aSuffixList.push_back(aToken.copy(1));
39 } while ( nIndex >= 0 );
46 #pragma mark FilterEntry
48 FilterEntry::FilterEntry( const rtl::OUString& _rTitle, const UnoFilterList& _rSubFilters )
50 ,m_aSubFilters( _rSubFilters )
55 bool FilterEntry::hasSubFilters() const
57 bool bReturn = ( 0 < m_aSubFilters.getLength() );
63 sal_Int32 FilterEntry::getSubFilters( UnoFilterList& _rSubFilterList )
65 _rSubFilterList = m_aSubFilters;
66 sal_Int32 nReturn = m_aSubFilters.getLength();
73 isFilterString( const rtl::OUString& rFilterString, const char *pMatch )
77 bool bIsFilter = true;
79 rtl::OUString aMatch(rtl::OUString::createFromAscii(pMatch));
83 aToken = rFilterString.getToken( 0, ';', nIndex );
84 if( !aToken.match( aMatch ) )
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-- )
105 if( aFilterName[i] == ')' )
107 else if( aFilterName[i] == '(' )
109 sal_Int32 nBracketLen = nBracketEnd - i;
110 if( nBracketEnd <= 0 )
112 if( isFilterString( aFilterName.copy( i + 1, nBracketLen - 1 ), "*." ) )
113 aRealName = aRealName.replaceAt( i, nBracketLen + 1, rtl::OUString() );
114 else if (bAllowNoStar)
116 if( isFilterString( aFilterName.copy( i + 1, nBracketLen - 1 ), ".") )
117 aRealName = aRealName.replaceAt( i, nBracketLen + 1, rtl::OUString() );
128 struct FilterTitleMatch
131 const rtl::OUString rTitle;
134 FilterTitleMatch( const rtl::OUString& _rTitle ) : rTitle( _rTitle ) { }
137 bool operator () ( const FilterEntry& _rEntry )
140 if( !_rEntry.hasSubFilters() ) {
141 //first try the complete filter name
142 rtl::OUString title = _rEntry.getTitle();
143 bMatch = title.equals(rTitle);
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);
152 // a filter group -> search the sub filters
154 ::std::any_of(_rEntry.beginSubFilters(),
155 _rEntry.endSubFilters(),
161 bool operator () ( const UnoFilterEntry& _rEntry )
163 rtl::OUString aShrunkName = shrinkFilterName( _rEntry.First );
164 bool retVal = aShrunkName.equals(rTitle);
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;
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) {
189 delete m_pFilterNames;
196 bool FilterHelper::FilterNameExists( const rtl::OUString& rTitle )
202 ::std::any_of(m_pFilterList->begin(),
203 m_pFilterList->end(),
204 FilterTitleMatch( rTitle ));
210 bool FilterHelper::FilterNameExists( const UnoFilterList& _rGroupedFilters )
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 ) ) )
224 bRet = (pStart != pEnd);
231 void FilterHelper::ensureFilterList( const ::rtl::OUString& _rInitialCurrentFilter )
233 if( nullptr == m_pFilterList )
235 m_pFilterList = new FilterList;
237 // set the first filter to the current filter
238 m_aCurrentFilter = _rInitialCurrentFilter;
242 void FilterHelper::SetCurFilter( const rtl::OUString& rFilter )
244 SolarMutexGuard aGuard;
246 if(!m_aCurrentFilter.equals(rFilter))
248 m_aCurrentFilter = rFilter;
253 void FilterHelper::SetFilters()
255 // set the default filter
256 if( m_aCurrentFilter.getLength() > 0 )
258 SetCurFilter( m_aCurrentFilter );
262 void FilterHelper::appendFilter(const ::rtl::OUString& aTitle, const ::rtl::OUString& aFilterString)
264 SolarMutexGuard aGuard;
266 if( FilterNameExists( aTitle ) ) {
267 throw css::lang::IllegalArgumentException();
270 // ensure that we have a filter list
271 ensureFilterList( aTitle );
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;
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 );
305 if (bPrependSeparator) {
306 OUStringList emptyList;
307 m_pFilterList->push_back(FilterEntry("-", emptyList));
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);
317 bool FilterHelper::filenameMatchesFilter(NSString* sFilename)
319 if (m_aCurrentFilter.isEmpty()) {
320 SAL_WARN("fpicker", "filter name is empty");
324 NSFileManager *manager = [NSFileManager defaultManager];
325 NSDictionary* pAttribs = [manager attributesOfItemAtPath: sFilename error: nil];
328 NSObject* pType = [pAttribs objectForKey: NSFileType];
329 if( pType && [pType isKindOfClass: [NSString class]] )
331 NSString* pT = static_cast<NSString*>(pType);
332 if( [pT isEqualToString: NSFileTypeDirectory] ||
333 [pT isEqualToString: NSFileTypeSymbolicLink] )
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");
344 OUStringList suffixList = filter->getFilterSuffixList();
347 rtl::OUString aName = [sFilename OUString];
348 for(OUStringList::iterator iter = suffixList.begin(); iter != suffixList.end(); ++iter) {
349 if (*iter == ".*" || aName.endsWithIgnoreAsciiCase(*iter)) {
356 NSString* pResolved = resolveAlias( sFilename );
359 bool bResult = filenameMatchesFilter( pResolved );
360 [pResolved autorelease];
368 FilterList* FilterHelper::getFilterList()
370 return m_pFilterList;
373 NSStringList* FilterHelper::getFilterNames()
375 if (nullptr == m_pFilterList)
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]);
385 return m_pFilterNames;
388 void FilterHelper::SetFilterAtIndex(unsigned index)
390 if (m_pFilterList->size() <= index) {
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) {
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)) {
408 aTitle = shrinkFilterName(aTitle).trim();
409 if (m_aCurrentFilter.equals(aTitle)) {
420 OUStringList FilterHelper::getCurrentFilterSuffixList()
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();
430 aTitle = shrinkFilterName(aTitle).trim();
431 if (m_aCurrentFilter.equals(aTitle)) {
432 retVal = iter->getFilterSuffixList();
442 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */