summaryrefslogtreecommitdiffstats
path: root/fpicker/source/aqua/FilterHelper.mm
diff options
context:
space:
mode:
Diffstat (limited to 'fpicker/source/aqua/FilterHelper.mm')
-rw-r--r--fpicker/source/aqua/FilterHelper.mm443
1 files changed, 443 insertions, 0 deletions
diff --git a/fpicker/source/aqua/FilterHelper.mm b/fpicker/source/aqua/FilterHelper.mm
new file mode 100644
index 000000000..f11d8447d
--- /dev/null
+++ b/fpicker/source/aqua/FilterHelper.mm
@@ -0,0 +1,443 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+#include <sal/log.hxx>
+
+#include <algorithm>
+#include <cstddef>
+#include <string_view>
+#include <o3tl/string_view.hxx>
+#include <osl/mutex.hxx>
+#include <vcl/svapp.hxx>
+
+#include "NSString_OOoAdditions.hxx"
+#include "NSURL_OOoAdditions.hxx"
+
+#include "FilterHelper.hxx"
+
+namespace {
+
+void fillSuffixList(OUStringList& aSuffixList, std::u16string_view suffixString) {
+ std::size_t nIndex = 0;
+ do {
+ std::u16string_view aToken = o3tl::getToken( suffixString, u';', nIndex );
+ aSuffixList.push_back(OUString(aToken.substr(1)));
+ } while ( nIndex != std::u16string_view::npos );
+}
+
+}
+
+#pragma mark DEFINES
+
+#pragma mark FilterEntry
+
+FilterEntry::FilterEntry( const OUString& _rTitle, const UnoFilterList& _rSubFilters )
+:m_sTitle( _rTitle )
+,m_aSubFilters( _rSubFilters )
+{
+}
+
+
+bool FilterEntry::hasSubFilters() const
+{
+ bool bReturn = ( 0 < m_aSubFilters.getLength() );
+
+ return bReturn;
+}
+
+
+sal_Int32 FilterEntry::getSubFilters( UnoFilterList& _rSubFilterList )
+{
+ _rSubFilterList = m_aSubFilters;
+ sal_Int32 nReturn = m_aSubFilters.getLength();
+
+ return nReturn;
+}
+
+#pragma mark statics
+static bool
+isFilterString( std::u16string_view rFilterString, std::u16string_view pMatch )
+{
+ std::size_t nIndex = 0;
+ std::u16string_view aToken;
+ bool bIsFilter = true;
+
+ do
+ {
+ aToken = o3tl::getToken( rFilterString, u';', nIndex );
+ if( !o3tl::starts_with( aToken, pMatch ) )
+ {
+ bIsFilter = false;
+ break;
+ }
+ }
+ while( nIndex != std::u16string_view::npos );
+
+ return bIsFilter;
+}
+
+
+
+static OUString
+shrinkFilterName( const OUString& aFilterName, bool bAllowNoStar = false )
+{
+ sal_Int32 nBracketEnd = -1;
+ OUString aRealName(aFilterName);
+
+ for( sal_Int32 i = aRealName.getLength() - 1; i > 0; i-- )
+ {
+ if( aFilterName[i] == ')' )
+ nBracketEnd = i;
+ else if( aFilterName[i] == '(' )
+ {
+ sal_Int32 nBracketLen = nBracketEnd - i;
+ if( nBracketEnd <= 0 )
+ continue;
+ if( isFilterString( aFilterName.subView( i + 1, nBracketLen - 1 ), u"*." ) )
+ aRealName = aRealName.replaceAt( i, nBracketLen + 1, u"" );
+ else if (bAllowNoStar)
+ {
+ if( isFilterString( aFilterName.subView( i + 1, nBracketLen - 1 ), u".") )
+ aRealName = aRealName.replaceAt( i, nBracketLen + 1, u"" );
+ }
+ }
+ }
+
+ return aRealName;
+}
+
+
+namespace {
+
+ struct FilterTitleMatch
+ {
+protected:
+ const OUString rTitle;
+
+public:
+ FilterTitleMatch( const OUString& _rTitle ) : rTitle( _rTitle ) { }
+
+
+ bool operator () ( const FilterEntry& _rEntry )
+ {
+ bool bMatch;
+ if( !_rEntry.hasSubFilters() ) {
+ //first try the complete filter name
+ OUString title = _rEntry.getTitle();
+ bMatch = title.equals(rTitle);
+ if (!bMatch) {
+ //we didn't find a match using the full name, let's give it another
+ //try using the shrunk version
+ OUString aShrunkName = shrinkFilterName( _rEntry.getTitle() ).trim();
+ bMatch = aShrunkName.equals(rTitle);
+ }
+ }
+ else
+ // a filter group -> search the sub filters
+ bMatch =
+ ::std::any_of(_rEntry.beginSubFilters(),
+ _rEntry.endSubFilters(),
+ *this);
+
+ return bMatch;
+ }
+
+ bool operator () ( const UnoFilterEntry& _rEntry )
+ {
+ OUString aShrunkName = shrinkFilterName( _rEntry.First );
+ bool retVal = aShrunkName.equals(rTitle);
+ return retVal;
+ }
+ };
+}
+
+FilterHelper::FilterHelper()
+: m_pFilterList(nullptr)
+, m_pFilterNames(nullptr)
+{
+}
+
+FilterHelper::~FilterHelper()
+{
+ NSAutoreleasePool *pool = [NSAutoreleasePool new];
+
+ if (nullptr != m_pFilterList) {
+ delete m_pFilterList;
+ }
+
+ if (nullptr != m_pFilterNames) {
+ //we called retain when we added the strings to the list, so we should release them now
+ for (NSStringList::iterator iter = m_pFilterNames->begin(); iter != m_pFilterNames->end(); ++iter) {
+ [*iter release];
+ }
+ delete m_pFilterNames;
+ }
+
+ [pool release];
+}
+
+
+bool FilterHelper::FilterNameExists( const OUString& rTitle )
+{
+ bool bRet = false;
+
+ if( m_pFilterList )
+ bRet =
+ ::std::any_of(m_pFilterList->begin(),
+ m_pFilterList->end(),
+ FilterTitleMatch( rTitle ));
+
+ return bRet;
+}
+
+
+bool FilterHelper::FilterNameExists( const UnoFilterList& _rGroupedFilters )
+{
+ bool bRet = false;
+
+ if( m_pFilterList )
+ {
+ const UnoFilterEntry* pStart = _rGroupedFilters.getConstArray();
+ const UnoFilterEntry* pEnd = pStart + _rGroupedFilters.getLength();
+ for( ; pStart != pEnd; ++pStart )
+ if( ::std::any_of(m_pFilterList->begin(),
+ m_pFilterList->end(),
+ FilterTitleMatch( pStart->First ) ) )
+ break;
+
+ bRet = (pStart != pEnd);
+ }
+
+ return bRet;
+}
+
+
+void FilterHelper::ensureFilterList( const OUString& _rInitialCurrentFilter )
+{
+ if( nullptr == m_pFilterList )
+ {
+ m_pFilterList = new FilterList;
+
+ // set the first filter to the current filter
+ m_aCurrentFilter = _rInitialCurrentFilter;
+ }
+}
+
+void FilterHelper::SetCurFilter( const OUString& rFilter )
+{
+ SolarMutexGuard aGuard;
+
+ if(!m_aCurrentFilter.equals(rFilter))
+ {
+ m_aCurrentFilter = rFilter;
+ }
+
+}
+
+void FilterHelper::SetFilters()
+{
+ // set the default filter
+ if( m_aCurrentFilter.getLength() > 0 )
+ {
+ SetCurFilter( m_aCurrentFilter );
+ }
+}
+
+void FilterHelper::appendFilter(const OUString& aTitle, std::u16string_view aFilterString)
+{
+ SolarMutexGuard aGuard;
+
+ if( FilterNameExists( aTitle ) ) {
+ throw css::lang::IllegalArgumentException();
+ }
+
+ // ensure that we have a filter list
+ ensureFilterList( aTitle );
+
+ // append the filter
+ OUStringList suffixList;
+ fillSuffixList(suffixList, aFilterString);
+ m_pFilterList->push_back(FilterEntry( aTitle, suffixList ) );
+}
+
+void FilterHelper::setCurrentFilter( const OUString& aTitle )
+{
+ SetCurFilter(aTitle);
+}
+
+OUString FilterHelper::getCurrentFilter( )
+{
+ OUString sReturn = m_aCurrentFilter;
+
+ return sReturn;
+}
+
+void FilterHelper::appendFilterGroup( const css::uno::Sequence< css::beans::StringPair >& aFilters )
+{
+ SolarMutexGuard aGuard;
+
+ //add a separator if this is not the first group to be added
+ bool bPrependSeparator = m_pFilterList != nullptr;
+
+ // ensure that we have a filter list
+ OUString sInitialCurrentFilter;
+ if( aFilters.getLength() > 0)
+ sInitialCurrentFilter = aFilters[0].First;
+ ensureFilterList( sInitialCurrentFilter );
+
+ // append the filter
+ if (bPrependSeparator) {
+ OUStringList emptyList;
+ m_pFilterList->push_back(FilterEntry("-", emptyList));
+ }
+
+ const css::beans::StringPair* pSubFilters = aFilters.getConstArray();
+ const css::beans::StringPair* pSubFiltersEnd = pSubFilters + aFilters.getLength();
+ for( ; pSubFilters != pSubFiltersEnd; ++pSubFilters ) {
+ appendFilter(pSubFilters->First, pSubFilters->Second);
+ }
+}
+
+bool FilterHelper::filenameMatchesFilter(NSString* sFilename)
+{
+ if (m_aCurrentFilter.isEmpty()) {
+ SAL_WARN("fpicker", "filter name is empty");
+ return true;
+ }
+
+ NSFileManager *manager = [NSFileManager defaultManager];
+ NSDictionary* pAttribs = [manager attributesOfItemAtPath: sFilename error: nil];
+ if( pAttribs )
+ {
+ NSObject* pType = [pAttribs objectForKey: NSFileType];
+ if( pType && [pType isKindOfClass: [NSString class]] )
+ {
+ NSString* pT = static_cast<NSString*>(pType);
+ if( [pT isEqualToString: NSFileTypeDirectory] ||
+ [pT isEqualToString: NSFileTypeSymbolicLink] )
+ return true;
+ }
+ }
+
+ FilterList::iterator filter = ::std::find_if(m_pFilterList->begin(), m_pFilterList->end(), FilterTitleMatch(m_aCurrentFilter));
+ if (filter == m_pFilterList->end()) {
+ SAL_WARN("fpicker", "filter not found in list");
+ return true;
+ }
+
+ OUStringList suffixList = filter->getFilterSuffixList();
+
+ {
+ OUString aName = [sFilename OUString];
+ for(OUStringList::iterator iter = suffixList.begin(); iter != suffixList.end(); ++iter) {
+ if (*iter == ".*" || aName.endsWithIgnoreAsciiCase(*iter)) {
+ return true;
+ }
+ }
+ }
+
+ // might be an alias
+ NSString* pResolved = resolveAlias( sFilename );
+ if( pResolved )
+ {
+ bool bResult = filenameMatchesFilter( pResolved );
+ [pResolved autorelease];
+ if( bResult )
+ return true;
+ }
+
+ return false;
+}
+
+FilterList* FilterHelper::getFilterList()
+{
+ return m_pFilterList;
+}
+
+NSStringList* FilterHelper::getFilterNames()
+{
+ if (nullptr == m_pFilterList)
+ return nullptr;
+ if (nullptr == m_pFilterNames) {
+ //build filter names list
+ m_pFilterNames = new NSStringList;
+ for (FilterList::iterator iter = m_pFilterList->begin(); iter != m_pFilterList->end(); ++iter) {
+ m_pFilterNames->push_back([[NSString stringWithOUString:iter->getTitle()] retain]);
+ }
+ }
+
+ return m_pFilterNames;
+}
+
+void FilterHelper::SetFilterAtIndex(unsigned index)
+{
+ if (m_pFilterList->size() <= index) {
+ index = 0;
+ }
+ FilterEntry entry = m_pFilterList->at(index);
+ SetCurFilter(entry.getTitle());
+}
+
+int FilterHelper::getCurrentFilterIndex()
+{
+ int result = 0;//default to first filter
+ if (m_aCurrentFilter.getLength() > 0) {
+ int i = 0;
+ for (FilterList::iterator iter = m_pFilterList->begin(); iter != m_pFilterList->end(); ++iter, ++i) {
+ OUString aTitle = iter->getTitle();
+ if (m_aCurrentFilter.equals(aTitle)) {
+ result = i;
+ break;
+ } else {
+ aTitle = shrinkFilterName(aTitle).trim();
+ if (m_aCurrentFilter.equals(aTitle)) {
+ result = i;
+ break;
+ }
+ }
+ }
+ }
+
+ return result;
+}
+
+OUStringList FilterHelper::getCurrentFilterSuffixList()
+{
+ OUStringList retVal;
+ if (m_aCurrentFilter.getLength() > 0) {
+ for (FilterList::iterator iter = m_pFilterList->begin(); iter != m_pFilterList->end(); ++iter) {
+ OUString aTitle = iter->getTitle();
+ if (m_aCurrentFilter.equals(aTitle)) {
+ retVal = iter->getFilterSuffixList();
+ break;
+ } else {
+ aTitle = shrinkFilterName(aTitle).trim();
+ if (m_aCurrentFilter.equals(aTitle)) {
+ retVal = iter->getFilterSuffixList();
+ break;
+ }
+ }
+ }
+ }
+
+ return retVal;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */