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>
21 #include <sal/log.hxx>
25 #include <string_view>
26 #include <o3tl/string_view.hxx>
27 #include <osl/mutex.hxx>
28 #include <vcl/svapp.hxx>
30 #include "NSString_OOoAdditions.hxx"
31 #include "NSURL_OOoAdditions.hxx"
33 #include "FilterHelper.hxx"
37 void fillSuffixList(OUStringList& aSuffixList, std::u16string_view suffixString) {
38 std::size_t nIndex = 0;
40 std::u16string_view aToken = o3tl::getToken( suffixString, u';', nIndex );
41 aSuffixList.push_back(OUString(aToken.substr(1)));
42 } while ( nIndex != std::u16string_view::npos );
49 #pragma mark FilterEntry
51 FilterEntry::FilterEntry( const OUString& _rTitle, const UnoFilterList& _rSubFilters )
53 ,m_aSubFilters( _rSubFilters )
58 bool FilterEntry::hasSubFilters() const
60 bool bReturn = ( 0 < m_aSubFilters.getLength() );
66 sal_Int32 FilterEntry::getSubFilters( UnoFilterList& _rSubFilterList )
68 _rSubFilterList = m_aSubFilters;
69 sal_Int32 nReturn = m_aSubFilters.getLength();
76 isFilterString( std::u16string_view rFilterString, std::u16string_view pMatch )
78 std::size_t nIndex = 0;
79 std::u16string_view aToken;
80 bool bIsFilter = true;
84 aToken = o3tl::getToken( rFilterString, u';', nIndex );
85 if( !o3tl::starts_with( aToken, pMatch ) )
91 while( nIndex != std::u16string_view::npos );
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-- )
106 if( aFilterName[i] == ')' )
108 else if( aFilterName[i] == '(' )
110 sal_Int32 nBracketLen = nBracketEnd - i;
111 if( nBracketEnd <= 0 )
113 if( isFilterString( aFilterName.subView( i + 1, nBracketLen - 1 ), u"*." ) )
114 aRealName = aRealName.replaceAt( i, nBracketLen + 1, u"" );
115 else if (bAllowNoStar)
117 if( isFilterString( aFilterName.subView( i + 1, nBracketLen - 1 ), u".") )
118 aRealName = aRealName.replaceAt( i, nBracketLen + 1, u"" );
129 struct FilterTitleMatch
132 const OUString rTitle;
135 FilterTitleMatch( const OUString& _rTitle ) : rTitle( _rTitle ) { }
138 bool operator () ( const FilterEntry& _rEntry )
141 if( !_rEntry.hasSubFilters() ) {
142 //first try the complete filter name
143 OUString title = _rEntry.getTitle();
144 bMatch = title.equals(rTitle);
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);
153 // a filter group -> search the sub filters
155 ::std::any_of(_rEntry.beginSubFilters(),
156 _rEntry.endSubFilters(),
162 bool operator () ( const UnoFilterEntry& _rEntry )
164 OUString aShrunkName = shrinkFilterName( _rEntry.First );
165 bool retVal = aShrunkName.equals(rTitle);
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;
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) {
190 delete m_pFilterNames;
197 bool FilterHelper::FilterNameExists( const OUString& rTitle )
203 ::std::any_of(m_pFilterList->begin(),
204 m_pFilterList->end(),
205 FilterTitleMatch( rTitle ));
211 bool FilterHelper::FilterNameExists( const UnoFilterList& _rGroupedFilters )
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 ) ) )
225 bRet = (pStart != pEnd);
232 void FilterHelper::ensureFilterList( const OUString& _rInitialCurrentFilter )
234 if( nullptr == m_pFilterList )
236 m_pFilterList = new FilterList;
238 // set the first filter to the current filter
239 m_aCurrentFilter = _rInitialCurrentFilter;
243 void FilterHelper::SetCurFilter( const OUString& rFilter )
245 SolarMutexGuard aGuard;
247 if(!m_aCurrentFilter.equals(rFilter))
249 m_aCurrentFilter = rFilter;
254 void FilterHelper::SetFilters()
256 // set the default filter
257 if( m_aCurrentFilter.getLength() > 0 )
259 SetCurFilter( m_aCurrentFilter );
263 void FilterHelper::appendFilter(const OUString& aTitle, std::u16string_view aFilterString)
265 SolarMutexGuard aGuard;
267 if( FilterNameExists( aTitle ) ) {
268 throw css::lang::IllegalArgumentException();
271 // ensure that we have a filter list
272 ensureFilterList( aTitle );
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;
292 void FilterHelper::appendFilterGroup( 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 );
306 if (bPrependSeparator) {
307 OUStringList emptyList;
308 m_pFilterList->push_back(FilterEntry("-", emptyList));
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);
318 bool FilterHelper::filenameMatchesFilter(NSString* sFilename)
320 if (m_aCurrentFilter.isEmpty()) {
321 SAL_WARN("fpicker", "filter name is empty");
325 NSFileManager *manager = [NSFileManager defaultManager];
326 NSDictionary* pAttribs = [manager attributesOfItemAtPath: sFilename error: nil];
329 NSObject* pType = [pAttribs objectForKey: NSFileType];
330 if( pType && [pType isKindOfClass: [NSString class]] )
332 NSString* pT = static_cast<NSString*>(pType);
333 if( [pT isEqualToString: NSFileTypeDirectory] ||
334 [pT isEqualToString: NSFileTypeSymbolicLink] )
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");
345 OUStringList suffixList = filter->getFilterSuffixList();
348 OUString aName = [sFilename OUString];
349 for(OUStringList::iterator iter = suffixList.begin(); iter != suffixList.end(); ++iter) {
350 if (*iter == ".*" || aName.endsWithIgnoreAsciiCase(*iter)) {
357 NSString* pResolved = resolveAlias( sFilename );
360 bool bResult = filenameMatchesFilter( pResolved );
361 [pResolved autorelease];
369 FilterList* FilterHelper::getFilterList()
371 return m_pFilterList;
374 NSStringList* FilterHelper::getFilterNames()
376 if (nullptr == m_pFilterList)
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]);
386 return m_pFilterNames;
389 void FilterHelper::SetFilterAtIndex(unsigned index)
391 if (m_pFilterList->size() <= index) {
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) {
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)) {
409 aTitle = shrinkFilterName(aTitle).trim();
410 if (m_aCurrentFilter.equals(aTitle)) {
421 OUStringList FilterHelper::getCurrentFilterSuffixList()
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();
431 aTitle = shrinkFilterName(aTitle).trim();
432 if (m_aCurrentFilter.equals(aTitle)) {
433 retVal = iter->getFilterSuffixList();
443 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */