diff options
Diffstat (limited to 'sfx2/source/dialog/filtergrouping.cxx')
-rw-r--r-- | sfx2/source/dialog/filtergrouping.cxx | 1274 |
1 files changed, 1274 insertions, 0 deletions
diff --git a/sfx2/source/dialog/filtergrouping.cxx b/sfx2/source/dialog/filtergrouping.cxx new file mode 100644 index 000000000000..ea2429be8157 --- /dev/null +++ b/sfx2/source/dialog/filtergrouping.cxx @@ -0,0 +1,1274 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_sfx2.hxx" +#include "filtergrouping.hxx" +#include <sfx2/fcontnr.hxx> +#include <sfx2/filedlghelper.hxx> +#include <sfx2/sfx.hrc> +#include <sfx2/docfac.hxx> +#include "sfxresid.hxx" +#include <osl/thread.h> +#include <com/sun/star/ui/dialogs/XFilterGroupManager.hpp> +#include <com/sun/star/beans/StringPair.hpp> +#include <com/sun/star/uno/Sequence.hxx> +#include <unotools/confignode.hxx> +#include <comphelper/processfactory.hxx> +#include <comphelper/sequenceashashmap.hxx> +#include <tools/wldcrd.hxx> +#include <tools/diagnose_ex.h> + +#include <list> +#include <vector> +#include <map> +#include <algorithm> + +//........................................................................ +namespace sfx2 +{ +//........................................................................ + +//#define DISABLE_GROUPING_AND_CLASSIFYING + // not using the functionallity herein, yet + + using namespace ::com::sun::star::uno; + using namespace ::com::sun::star::ui::dialogs; + using namespace ::com::sun::star::lang; + using namespace ::com::sun::star::beans; + using namespace ::utl; + + //==================================================================== + /** + + Some general words about what's going on here .... + + <p>In our file open dialog, usually we display every filter we know. That's how it was before: every filter + lead to an own line in the filter list box, e.g. "StarWriter 5.0 Dokument" or "Microsoft Word 97".</p> + + <p>But then the PM came. And everything changed ....</p> + + <p>A basic idea are groups: Why simply listing all the single filters? Couldn't we draw nice separators + between the filters which logically belong together? I.e. all the filters which open a document in StarWriter: + couldn't we separate them from all the filters which open the document in StarCalc?<br/> + So spoke the PM, and engineering obeyed.</p> + + <p>So we have groups. They're just a visual aspect: All the filters of a group are presented together, separated + by a line from other groups.</p> + + <p>Let's be honest: How the concrete implementation of the file picker service separates the different groups + is a matter of this implementation. We only do this grouping and suggest it to the FilePicker service ...</p> + + <p>Now for the second concept:<br/> + Thinking about it (and that's what the PM did), both "StarWriter 5.0 Dokument" and "Microsoft Word 97" + describe a text document. It's a text. It's of no interest for the user that one of the texts was saved in + MS' format, and one in our own format.<br/> + So in a first step, we want to have a filter entry "Text documents". This would cover both above-mentioned + filters, as well as any other filters for documents which are texts.</p> + + <p>Such an entry as "Text documents" is - within the scope of this file - called "class" or "filter class".</p> + + <p>In the file-open-dialog, such a class looks like an ordinary filter: it's simply a name in the filter + listbox. Selecting means that all the files matching one of the "sub-filters" are displayed (in the example above, + this would be "*.sdw", "*.doc" and so on).</p> + + <p>Now there are two types of filter classes: global ones and local ones. "Text documents" is a global class. As + well as "Spreadsheets". Or "Web pages".<br/> + Let's have a look at a local class: The filters "MS Word 95" and "MS WinWord 6.0" together form the class + "Microsoft Word 6.0 / 95" (don't ask for the reasons. At least not me. Ask the PM). There are a lot of such + local classes ...</p> + + <p>The difference between global and local classes is as follows: Global classes are presented in an own group. + There is one dedicated group at the top of the list, containing all the global groups - no local groups and no + single filters.</p> + + <p>Ehm - it was a lie. Not really at the top. Before this group, there is this single "All files" entry. It forms + it's own group. But this is uninteresting here.</p> + + <p>Local classes must consist of filters which - without the classification - would all belong to the same group. + Then, they're combined to one entry (in the example above: "Microsoft Word 6.0 / 95"), and this entry is inserted + into the file picker filter list, instead of the single filters which form the class.</p> + + <p>This is an interesting difference between local and global classes: Filters which are part of a global class + are listed in there own group, too. Filters in local classes aren't listed a second time - neither directly (as + the filter itself) nor indirectly (as part of another local group).</p> + + <p>The only exception are filters which are part of a global class <em>and</em> a local class. This is allowed. + Beeing cotained in two local classes isn't.</p> + + <p>So that's all what you need to know: Understand the concept of "filter classes" (a filter class combines + different filters and acts as if it's a filter itself) and the concept of groups (a group just describes a + logical correlation of filters and usually is represented to the user by drawing group separators in the filter + list).</p> + + <p>If you got it, go try understanding this file :).</p> + + */ + + + //==================================================================== + + typedef StringPair FilterDescriptor; // a single filter or a filter class (display name and filter mask) + typedef ::std::list< FilterDescriptor > FilterGroup; // a list of single filter entries + typedef ::std::list< FilterGroup > GroupedFilterList; // a list of all filters, already grouped + + /// the logical name of a filter + typedef ::rtl::OUString FilterName; + + // a struct which holds references from a logical filter name to a filter group entry + // used for quick lookup of classes (means class entries - entries representing a class) + // which a given filter may belong to + typedef ::std::map< ::rtl::OUString, FilterGroup::iterator > FilterGroupEntryReferrer; + + /// a descriptor for a filter class (which in the final dialog is represented by one filter entry) + typedef struct _tagFilterClass + { + ::rtl::OUString sDisplayName; // the display name + Sequence< FilterName > aSubFilters; // the (logical) names of the filter which belong to the class + } FilterClass; + + typedef ::std::list< FilterClass > FilterClassList; + typedef ::std::map< ::rtl::OUString, FilterClassList::iterator > FilterClassReferrer; + + typedef ::std::vector< ::rtl::OUString > StringArray; + +// ======================================================================= +// = reading of configuration data +// ======================================================================= + + //-------------------------------------------------------------------- + void lcl_ReadFilterClass( const OConfigurationNode& _rClassesNode, const ::rtl::OUString& _rLogicalClassName, + FilterClass& /* [out] */ _rClass ) + { + static const ::rtl::OUString sDisplaNameNodeName( RTL_CONSTASCII_USTRINGPARAM( "DisplayName" ) ); + static const ::rtl::OUString sSubFiltersNodeName( RTL_CONSTASCII_USTRINGPARAM( "Filters" ) ); + + // the description node for the current class + OConfigurationNode aClassDesc = _rClassesNode.openNode( _rLogicalClassName ); + + // the values + aClassDesc.getNodeValue( sDisplaNameNodeName ) >>= _rClass.sDisplayName; + aClassDesc.getNodeValue( sSubFiltersNodeName ) >>= _rClass.aSubFilters; + } + + //-------------------------------------------------------------------- + struct CreateEmptyClassRememberPos : public ::std::unary_function< FilterName, void > + { + protected: + FilterClassList& m_rClassList; + FilterClassReferrer& m_rClassesReferrer; + + public: + CreateEmptyClassRememberPos( FilterClassList& _rClassList, FilterClassReferrer& _rClassesReferrer ) + :m_rClassList ( _rClassList ) + ,m_rClassesReferrer ( _rClassesReferrer ) + { + } + + // operate on a single class name + void operator() ( const FilterName& _rLogicalFilterName ) + { + // insert a new (empty) class + m_rClassList.push_back( FilterClass() ); + // get the position of this new entry + FilterClassList::iterator aInsertPos = m_rClassList.end(); + --aInsertPos; + // remember this position + m_rClassesReferrer.insert( FilterClassReferrer::value_type( _rLogicalFilterName, aInsertPos ) ); + } + }; + + //-------------------------------------------------------------------- + struct ReadGlobalFilter : public ::std::unary_function< FilterName, void > + { + protected: + OConfigurationNode m_aClassesNode; + FilterClassReferrer& m_aClassReferrer; + + public: + ReadGlobalFilter( const OConfigurationNode& _rClassesNode, FilterClassReferrer& _rClassesReferrer ) + :m_aClassesNode ( _rClassesNode ) + ,m_aClassReferrer ( _rClassesReferrer ) + { + } + + // operate on a single logical name + void operator() ( const FilterName& _rName ) + { + FilterClassReferrer::iterator aClassRef = m_aClassReferrer.find( _rName ); + if ( m_aClassReferrer.end() == aClassRef ) + { + // we do not know this global class + DBG_ERROR( "ReadGlobalFilter::operator(): unknown filter name!" ); + // TODO: perhaps we should be more tolerant - at the moment, the filter is dropped + // We could silently push_back it to the container .... + } + else + { + // read the data of this class into the node referred to by aClassRef + lcl_ReadFilterClass( m_aClassesNode, _rName, *aClassRef->second ); + } + } + }; + + //-------------------------------------------------------------------- + void lcl_ReadGlobalFilters( const OConfigurationNode& _rFilterClassification, FilterClassList& _rGlobalClasses, StringArray& _rGlobalClassNames ) + { + _rGlobalClasses.clear(); + _rGlobalClassNames.clear(); + + //================================================================ + // get the list describing the order of all global classes + Sequence< ::rtl::OUString > aGlobalClasses; + _rFilterClassification.getNodeValue( DEFINE_CONST_OUSTRING( "GlobalFilters/Order" ) ) >>= aGlobalClasses; + + const ::rtl::OUString* pNames = aGlobalClasses.getConstArray(); + const ::rtl::OUString* pNamesEnd = pNames + aGlobalClasses.getLength(); + + // copy the logical names + _rGlobalClassNames.resize( aGlobalClasses.getLength() ); + ::std::copy( pNames, pNamesEnd, _rGlobalClassNames.begin() ); + + // Global classes are presented in an own group, so their order matters (while the order of the + // "local classes" doesn't). + // That's why we can't simply add the global classes to _rGlobalClasses using the order in which they + // are returned from the configuration - it is completely undefined, and we need a _defined_ order. + FilterClassReferrer aClassReferrer; + ::std::for_each( + pNames, + pNamesEnd, + CreateEmptyClassRememberPos( _rGlobalClasses, aClassReferrer ) + ); + // now _rGlobalClasses contains a dummy entry for each global class, + // while aClassReferrer maps from the logical name of the class to the position within _rGlobalClasses where + // it's dummy entry resides + + //================================================================ + // go for all the single class entries + OConfigurationNode aFilterClassesNode = + _rFilterClassification.openNode( DEFINE_CONST_OUSTRING( "GlobalFilters/Classes" ) ); + Sequence< ::rtl::OUString > aFilterClasses = aFilterClassesNode.getNodeNames(); + ::std::for_each( + aFilterClasses.getConstArray(), + aFilterClasses.getConstArray() + aFilterClasses.getLength(), + ReadGlobalFilter( aFilterClassesNode, aClassReferrer ) + ); + } + + //-------------------------------------------------------------------- + struct ReadLocalFilter : public ::std::unary_function< FilterName, void > + { + protected: + OConfigurationNode m_aClassesNode; + FilterClassList& m_rClasses; + + public: + ReadLocalFilter( const OConfigurationNode& _rClassesNode, FilterClassList& _rClasses ) + :m_aClassesNode ( _rClassesNode ) + ,m_rClasses ( _rClasses ) + { + } + + // operate on a single logical name + void operator() ( const FilterName& _rName ) + { + // read the data for this class + FilterClass aClass; + lcl_ReadFilterClass( m_aClassesNode, _rName, aClass ); + + // insert the class descriptor + m_rClasses.push_back( aClass ); + } + }; + + //-------------------------------------------------------------------- + void lcl_ReadLocalFilters( const OConfigurationNode& _rFilterClassification, FilterClassList& _rLocalClasses ) + { + _rLocalClasses.clear(); + + // the node for the local classes + OConfigurationNode aFilterClassesNode = + _rFilterClassification.openNode( DEFINE_CONST_OUSTRING( "LocalFilters/Classes" ) ); + Sequence< ::rtl::OUString > aFilterClasses = aFilterClassesNode.getNodeNames(); + + ::std::for_each( + aFilterClasses.getConstArray(), + aFilterClasses.getConstArray() + aFilterClasses.getLength(), + ReadLocalFilter( aFilterClassesNode, _rLocalClasses ) + ); + } + + //-------------------------------------------------------------------- + void lcl_ReadClassification( FilterClassList& _rGlobalClasses, StringArray& _rGlobalClassNames, FilterClassList& _rLocalClasses ) + { + //================================================================ + // open our config node + OConfigurationTreeRoot aFilterClassification = OConfigurationTreeRoot::createWithServiceFactory( + ::comphelper::getProcessServiceFactory(), + DEFINE_CONST_OUSTRING( "org.openoffice.Office.UI/FilterClassification" ), + -1, + OConfigurationTreeRoot::CM_READONLY + ); + + //================================================================ + // go for the global classes + lcl_ReadGlobalFilters( aFilterClassification, _rGlobalClasses, _rGlobalClassNames ); + + //================================================================ + // fo for the local classes + lcl_ReadLocalFilters( aFilterClassification, _rLocalClasses ); + + } + +// ======================================================================= +// = grouping and classifying +// ======================================================================= + + //-------------------------------------------------------------------- + // a struct which adds helps remembering a reference to a class entry + struct ReferToFilterEntry : public ::std::unary_function< FilterName, void > + { + protected: + FilterGroupEntryReferrer& m_rEntryReferrer; + FilterGroup::iterator m_aClassPos; + + public: + ReferToFilterEntry( FilterGroupEntryReferrer& _rEntryReferrer, const FilterGroup::iterator& _rClassPos ) + :m_rEntryReferrer( _rEntryReferrer ) + ,m_aClassPos( _rClassPos ) + { + } + + // operate on a single filter name + void operator() ( const FilterName& _rName ) + { +#ifdef DBG_UTIL + ::std::pair< FilterGroupEntryReferrer::iterator, bool > aInsertRes = +#endif + m_rEntryReferrer.insert( FilterGroupEntryReferrer::value_type( _rName, m_aClassPos ) ); + DBG_ASSERT( aInsertRes.second, "ReferToFilterEntry::operator(): already have an element for this name!" ); + } + }; + + //-------------------------------------------------------------------- + struct FillClassGroup : public ::std::unary_function< FilterClass, void > + { + protected: + FilterGroup& m_rClassGroup; + FilterGroupEntryReferrer& m_rClassReferrer; + + public: + FillClassGroup( FilterGroup& _rClassGroup, FilterGroupEntryReferrer& _rClassReferrer ) + :m_rClassGroup ( _rClassGroup ) + ,m_rClassReferrer ( _rClassReferrer ) + { + } + + // operate on a single class + void operator() ( const FilterClass& _rClass ) + { + // create an empty filter descriptor for the class + FilterDescriptor aClassEntry; + // set it's name (which is all we know by now) + aClassEntry.First = _rClass.sDisplayName; + + // add it to the group + m_rClassGroup.push_back( aClassEntry ); + // the position of the newly added class + FilterGroup::iterator aClassEntryPos = m_rClassGroup.end(); + --aClassEntryPos; + + // and for all the sub filters of the class, remember the class + // (respectively the position of the class it the group) + ::std::for_each( + _rClass.aSubFilters.getConstArray(), + _rClass.aSubFilters.getConstArray() + _rClass.aSubFilters.getLength(), + ReferToFilterEntry( m_rClassReferrer, aClassEntryPos ) + ); + } + }; + + //-------------------------------------------------------------------- + static const sal_Unicode s_cWildcardSeparator( ';' ); + + //==================================================================== + const ::rtl::OUString& getSeparatorString() + { + static ::rtl::OUString s_sSeparatorString( &s_cWildcardSeparator, 1 ); + return s_sSeparatorString; + } + + //==================================================================== + struct CheckAppendSingleWildcard : public ::std::unary_function< ::rtl::OUString, void > + { + ::rtl::OUString& _rToBeExtended; + + CheckAppendSingleWildcard( ::rtl::OUString& _rBase ) : _rToBeExtended( _rBase ) { } + + void operator() ( const ::rtl::OUString& _rWC ) + { + // check for double wildcards + sal_Int32 nExistentPos = _rToBeExtended.indexOf( _rWC ); + if ( -1 < nExistentPos ) + { // found this wildcard (already part of _rToBeExtended) + const sal_Unicode* pBuffer = _rToBeExtended.getStr(); + if ( ( 0 == nExistentPos ) + || ( s_cWildcardSeparator == pBuffer[ nExistentPos - 1 ] ) + ) + { // the wildcard really starts at this position (it starts at pos 0 or the previous character is a separator + sal_Int32 nExistentWCEnd = nExistentPos + _rWC.getLength(); + if ( ( _rToBeExtended.getLength() == nExistentWCEnd ) + || ( s_cWildcardSeparator == pBuffer[ nExistentWCEnd ] ) + ) + { // it's really the complete wildcard we found + // (not something like _rWC beeing "*.t" and _rToBeExtended containing "*.txt") + // -> outta here + return; + } + } + } + + if ( _rToBeExtended.getLength() ) + _rToBeExtended += getSeparatorString(); + _rToBeExtended += _rWC; + } + }; + + //==================================================================== + // a helper struct which adds a fixed (Sfx-)filter to a filter group entry given by iterator + struct AppendWildcardToDescriptor : public ::std::unary_function< FilterGroupEntryReferrer::value_type, void > + { + protected: + ::std::vector< ::rtl::OUString > aWildCards; + + public: + AppendWildcardToDescriptor( const String& _rWildCard ); + + // operate on a single class entry + void operator() ( const FilterGroupEntryReferrer::value_type& _rClassReference ) + { + // simply add our wildcards + ::std::for_each( + aWildCards.begin(), + aWildCards.end(), + CheckAppendSingleWildcard( _rClassReference.second->Second ) + ); + } + }; + + //==================================================================== + AppendWildcardToDescriptor::AppendWildcardToDescriptor( const String& _rWildCard ) + { + DBG_ASSERT( _rWildCard.Len(), + "AppendWildcardToDescriptor::AppendWildcardToDescriptor: invalid wildcard!" ); + DBG_ASSERT( _rWildCard.GetBuffer()[0] != s_cWildcardSeparator, + "AppendWildcardToDescriptor::AppendWildcardToDescriptor: wildcard already separated!" ); + + aWildCards.reserve( _rWildCard.GetTokenCount( s_cWildcardSeparator ) ); + + const sal_Unicode* pTokenLoop = _rWildCard.GetBuffer(); + const sal_Unicode* pTokenLoopEnd = pTokenLoop + _rWildCard.Len(); + const sal_Unicode* pTokenStart = pTokenLoop; + for ( ; pTokenLoop != pTokenLoopEnd; ++pTokenLoop ) + { + if ( ( s_cWildcardSeparator == *pTokenLoop ) && ( pTokenLoop > pTokenStart ) ) + { // found a new token separator (and a non-empty token) + aWildCards.push_back( ::rtl::OUString( pTokenStart, pTokenLoop - pTokenStart ) ); + + // search the start of the next token + while ( ( pTokenStart != pTokenLoopEnd ) && ( *pTokenStart != s_cWildcardSeparator ) ) + ++pTokenStart; + + if ( pTokenStart == pTokenLoopEnd ) + // reached the end + break; + + ++pTokenStart; + pTokenLoop = pTokenStart; + } + } + if ( pTokenLoop > pTokenStart ) + // the last one .... + aWildCards.push_back( ::rtl::OUString( pTokenStart, pTokenLoop - pTokenStart ) ); + } + + //-------------------------------------------------------------------- + void lcl_InitGlobalClasses( GroupedFilterList& _rAllFilters, const FilterClassList& _rGlobalClasses, FilterGroupEntryReferrer& _rGlobalClassesRef ) + { + // we need an extra group in our "all filters" container + _rAllFilters.push_front( FilterGroup() ); + FilterGroup& rGlobalFilters = _rAllFilters.front(); + // it's important to work on the reference: we want to access the members of this filter group + // by an iterator (FilterGroup::const_iterator) + // the referrer for the global classes + + // initialize the group + ::std::for_each( + _rGlobalClasses.begin(), + _rGlobalClasses.end(), + FillClassGroup( rGlobalFilters, _rGlobalClassesRef ) + ); + // now we have: + // in rGlobalFilters: a list of FilterDescriptor's, where each's discriptor's display name is set to the name of a class + // in aGlobalClassesRef: a mapping from logical filter names to positions within rGlobalFilters + // this way, if we encounter an arbitrary filter, we can easily (and efficient) check if it belongs to a global class + // and modify the descriptor for this class accordingly + } + + //-------------------------------------------------------------------- + typedef ::std::vector< ::std::pair< FilterGroupEntryReferrer::mapped_type, FilterGroup::iterator > > + MapGroupEntry2GroupEntry; + // this is not really a map - it's just called this way because it is used as a map + + struct FindGroupEntry : public ::std::unary_function< MapGroupEntry2GroupEntry::value_type, sal_Bool > + { + FilterGroupEntryReferrer::mapped_type aLookingFor; + FindGroupEntry( FilterGroupEntryReferrer::mapped_type _rLookingFor ) : aLookingFor( _rLookingFor ) { } + + sal_Bool operator() ( const MapGroupEntry2GroupEntry::value_type& _rMapEntry ) + { + return _rMapEntry.first == aLookingFor ? sal_True : sal_False; + } + }; + + struct CopyGroupEntryContent : public ::std::unary_function< MapGroupEntry2GroupEntry::value_type, void > + { + void operator() ( const MapGroupEntry2GroupEntry::value_type& _rMapEntry ) + { +#ifdef DBG_UTIL + FilterDescriptor aHaveALook = *_rMapEntry.first; +#endif + *_rMapEntry.second = *_rMapEntry.first; + } + }; + + //-------------------------------------------------------------------- + struct CopyNonEmptyFilter : public ::std::unary_function< FilterDescriptor, void > + { + FilterGroup& rTarget; + CopyNonEmptyFilter( FilterGroup& _rTarget ) :rTarget( _rTarget ) { } + + void operator() ( const FilterDescriptor& _rFilter ) + { + if ( _rFilter.Second.getLength() ) + rTarget.push_back( _rFilter ); + } + }; + + //-------------------------------------------------------------------- + void lcl_GroupAndClassify( TSortedFilterList& _rFilterMatcher, GroupedFilterList& _rAllFilters ) + { + _rAllFilters.clear(); + + // =============================================================== + // read the classification of filters + FilterClassList aGlobalClasses, aLocalClasses; + StringArray aGlobalClassNames; + lcl_ReadClassification( aGlobalClasses, aGlobalClassNames, aLocalClasses ); + + // =============================================================== + // for the global filter classes + FilterGroupEntryReferrer aGlobalClassesRef; + lcl_InitGlobalClasses( _rAllFilters, aGlobalClasses, aGlobalClassesRef ); + + // insert as much placeholders (FilterGroup's) into _rAllFilter for groups as we have global classes + // (this assumes that both numbers are the same, which, speaking strictly, must not hold - but it does, as we know ...) + sal_Int32 nGlobalClasses = aGlobalClasses.size(); + while ( nGlobalClasses-- ) + _rAllFilters.push_back( FilterGroup() ); + + // =============================================================== + // for the local classes: + // if n filters belong to a local class, they do not appear in their respective group explicitly, instead + // and entry for the class is added to the group and the extensions of the filters are collected under + // this entry + FilterGroupEntryReferrer aLocalClassesRef; + FilterGroup aCollectedLocals; + ::std::for_each( + aLocalClasses.begin(), + aLocalClasses.end(), + FillClassGroup( aCollectedLocals, aLocalClassesRef ) + ); + // to map from the position within aCollectedLocals to positions within the real groups + // (where they finally belong to) + MapGroupEntry2GroupEntry aLocalFinalPositions; + + // =============================================================== + // now add the filters + // the group which we currently work with + GroupedFilterList::iterator aCurrentGroup = _rAllFilters.end(); // no current group + // the filter container of the current group - if this changes between two filters, a new group is reached + String aCurrentServiceName; + + String sFilterWildcard; + ::rtl::OUString sFilterName; + // loop through all the filters + for ( const SfxFilter* pFilter = _rFilterMatcher.First(); pFilter; pFilter = _rFilterMatcher.Next() ) + { + sFilterName = pFilter->GetFilterName(); + sFilterWildcard = pFilter->GetWildcard().GetWildCard(); + AppendWildcardToDescriptor aExtendWildcard( sFilterWildcard ); + + DBG_ASSERT( sFilterWildcard.Len(), "sfx2::lcl_GroupAndClassify: invalid wildcard of this filter!" ); + + // =========================================================== + // check for a change in the group + String aServiceName = pFilter->GetServiceName(); + if ( aServiceName != aCurrentServiceName ) + { // we reached a new group + + ::rtl::OUString sDocServName = aServiceName; + + // look for the place in _rAllFilters where this ne group belongs - this is determined + // by the order of classes in aGlobalClassNames + GroupedFilterList::iterator aGroupPos = _rAllFilters.begin(); + DBG_ASSERT( aGroupPos != _rAllFilters.end(), + "sfx2::lcl_GroupAndClassify: invalid all-filters array here!" ); + // the loop below will work on invalid objects else ... + ++aGroupPos; + StringArray::iterator aGlobalIter = aGlobalClassNames.begin(); + while ( ( aGroupPos != _rAllFilters.end() ) + && ( aGlobalIter != aGlobalClassNames.end() ) + && ( *aGlobalIter != sDocServName ) + ) + { + ++aGlobalIter; + ++aGroupPos; + } + if ( aGroupPos != _rAllFilters.end() ) + // we found a global class name which matchies the doc service name -> fill the filters of this + // group in the respective prepared group + aCurrentGroup = aGroupPos; + else + // insert a new entry in our overall-list + aCurrentGroup = _rAllFilters.insert( _rAllFilters.end(), FilterGroup() ); + + // remember the container to properly detect the next group + aCurrentServiceName = aServiceName; + } + + DBG_ASSERT( aCurrentGroup != _rAllFilters.end(), "sfx2::lcl_GroupAndClassify: invalid current group!" ); + + // =========================================================== + // check if the filter is part of a global group + ::std::pair< FilterGroupEntryReferrer::iterator, FilterGroupEntryReferrer::iterator > + aBelongsTo = aGlobalClassesRef.equal_range( sFilterName ); + // add the filter to the entries for these classes + // (if they exist - if not, the range is empty and the for_each is a no-op) + ::std::for_each( + aBelongsTo.first, + aBelongsTo.second, + aExtendWildcard + ); + + // =========================================================== + // add the filter to it's group + + // for this, check if the filter is part of a local filter + FilterGroupEntryReferrer::iterator aBelongsToLocal = aLocalClassesRef.find( sFilterName ); + if ( aLocalClassesRef.end() != aBelongsToLocal ) + { +/* +#ifdef DBG_UTIL + const ::rtl::OUString& rLocalClassDisplayName = aBelongsToLocal->second->First; + const ::rtl::OUString& rLocalClassExtension = aBelongsToLocal->second->Second; +#endif +*/ + // okay, there is a local class which the filter belongs to + // -> append the wildcard + aExtendWildcard( *aBelongsToLocal ); + + MapGroupEntry2GroupEntry::iterator aThisGroupFinalPos = + ::std::find_if( aLocalFinalPositions.begin(), aLocalFinalPositions.end(), FindGroupEntry( aBelongsToLocal->second ) ); + + if ( aLocalFinalPositions.end() == aThisGroupFinalPos ) + { // the position within aCollectedLocals has not been mapped to a final position + // within the "real" group (aCollectedLocals is only temporary) + // -> do this now (as we just encountered the first filter belonging to this local class + // add a new entry which is the "real" group entry + aCurrentGroup->push_back( FilterDescriptor( aBelongsToLocal->second->First, String() ) ); + // the position where we inserted the entry + FilterGroup::iterator aInsertPos = aCurrentGroup->end(); + --aInsertPos; + // remember this pos + aLocalFinalPositions.push_back( MapGroupEntry2GroupEntry::value_type( aBelongsToLocal->second, aInsertPos ) ); + } + } + else + aCurrentGroup->push_back( FilterDescriptor( pFilter->GetUIName(), sFilterWildcard ) ); + } + + // now just complete the infos for the local groups: + // During the above loop, they have been collected in aCollectedLocals, but this is only temporary + // They have to be copied into their final positions (which are stored in aLocalFinalPositions) + ::std::for_each( + aLocalFinalPositions.begin(), + aLocalFinalPositions.end(), + CopyGroupEntryContent() + ); + + // and remove local groups which do not apply - e.g. have no entries due to the limited content of the + // current SfxFilterMatcherIter + + FilterGroup& rGlobalFilters = _rAllFilters.front(); + FilterGroup aNonEmptyGlobalFilters; + ::std::for_each( + rGlobalFilters.begin(), + rGlobalFilters.end(), + CopyNonEmptyFilter( aNonEmptyGlobalFilters ) + ); + rGlobalFilters.swap( aNonEmptyGlobalFilters ); + } + + //-------------------------------------------------------------------- + struct AppendFilter : public ::std::unary_function< FilterDescriptor, void > + { + protected: + Reference< XFilterManager > m_xFilterManager; + FileDialogHelper_Impl* m_pFileDlgImpl; + bool m_bAddExtension; + + public: + AppendFilter( const Reference< XFilterManager >& _rxFilterManager, + FileDialogHelper_Impl* _pImpl, bool _bAddExtension ) : + + m_xFilterManager( _rxFilterManager ), + m_pFileDlgImpl ( _pImpl ), + m_bAddExtension ( _bAddExtension ) + + { + DBG_ASSERT( m_xFilterManager.is(), "AppendFilter::AppendFilter: invalid filter manager!" ); + DBG_ASSERT( m_pFileDlgImpl, "AppendFilter::AppendFilter: invalid filedlg impl!" ); + } + + // operate on a single filter + void operator() ( const FilterDescriptor& _rFilterEntry ) + { + String sDisplayText = m_bAddExtension + ? addExtension( _rFilterEntry.First, _rFilterEntry.Second, sal_True, *m_pFileDlgImpl ) + : _rFilterEntry.First; + m_xFilterManager->appendFilter( sDisplayText, _rFilterEntry.Second ); + } + }; + +// ======================================================================= +// = handling for the "all files" entry +// ======================================================================= + + //-------------------------------------------------------------------- + sal_Bool lcl_hasAllFilesFilter( TSortedFilterList& _rFilterMatcher, String& /* [out] */ _rAllFilterName ) + { + ::rtl::OUString sUIName; + sal_Bool bHasAll = sal_False; + _rAllFilterName = String( SfxResId( STR_SFX_FILTERNAME_ALL ) ); + + // =============================================================== + // check if there's already a filter <ALL> + for ( const SfxFilter* pFilter = _rFilterMatcher.First(); pFilter && !bHasAll; pFilter = _rFilterMatcher.Next() ) + { + if ( pFilter->GetUIName() == _rAllFilterName ) + bHasAll = sal_True; + } + return bHasAll; + } + + //-------------------------------------------------------------------- + void lcl_EnsureAllFilesEntry( TSortedFilterList& _rFilterMatcher, GroupedFilterList& _rFilters ) + { + // =============================================================== + String sAllFilterName; + if ( !lcl_hasAllFilesFilter( _rFilterMatcher, sAllFilterName ) ) + { + // get the first group of filters (by definition, this group contains the global classes) + DBG_ASSERT( !_rFilters.empty(), "lcl_EnsureAllFilesEntry: invalid filter list!" ); + if ( !_rFilters.empty() ) + { + FilterGroup& rGlobalClasses = *_rFilters.begin(); + rGlobalClasses.push_front( FilterDescriptor( sAllFilterName, DEFINE_CONST_UNICODE( FILEDIALOG_FILTER_ALL ) ) ); + } + } + } + +#ifdef DISABLE_GROUPING_AND_CLASSIFYING + //-------------------------------------------------------------------- + void lcl_EnsureAllFilesEntry( TSortedFilterList& _rFilterMatcher, const Reference< XFilterManager >& _rxFilterManager, ::rtl::OUString& _rFirstNonEmpty ) + { + // =============================================================== + String sAllFilterName; + if ( !lcl_hasAllFilesFilter( _rFilterMatcher, sAllFilterName ) ) + { + try + { + _rxFilterManager->appendFilter( sAllFilterName, DEFINE_CONST_UNICODE( FILEDIALOG_FILTER_ALL ) ); + _rFirstNonEmpty = sAllFilterName; + } + catch( const IllegalArgumentException& ) + { +#ifdef DBG_UTIL + ByteString aMsg( "sfx2::lcl_EnsureAllFilesEntry: could not append Filter" ); + aMsg += ByteString( String( sAllFilterName ), RTL_TEXTENCODING_UTF8 ); + DBG_ERROR( aMsg.GetBuffer() ); +#endif + } + } + + } +#endif + +// ======================================================================= +// = filling an XFilterManager +// ======================================================================= + + //-------------------------------------------------------------------- + struct AppendFilterGroup : public ::std::unary_function< FilterGroup, void > + { + protected: + Reference< XFilterManager > m_xFilterManager; + Reference< XFilterGroupManager > m_xFilterGroupManager; + FileDialogHelper_Impl* m_pFileDlgImpl; + + public: + AppendFilterGroup( const Reference< XFilterManager >& _rxFilterManager, FileDialogHelper_Impl* _pImpl ) + :m_xFilterManager ( _rxFilterManager ) + ,m_xFilterGroupManager ( _rxFilterManager, UNO_QUERY ) + ,m_pFileDlgImpl ( _pImpl ) + { + DBG_ASSERT( m_xFilterManager.is(), "AppendFilterGroup::AppendFilterGroup: invalid filter manager!" ); + DBG_ASSERT( m_pFileDlgImpl, "AppendFilterGroup::AppendFilterGroup: invalid filedlg impl!" ); + } + + void appendGroup( const FilterGroup& _rGroup, bool _bAddExtension ) + { + try + { + if ( m_xFilterGroupManager.is() ) + { // the file dialog implementation supports visual grouping of filters + // create a representation of the group which is understandable by the XFilterGroupManager + if ( _rGroup.size() ) + { + Sequence< StringPair > aFilters( _rGroup.size() ); + ::std::copy( + _rGroup.begin(), + _rGroup.end(), + aFilters.getArray() + ); + if ( _bAddExtension ) + { + StringPair* pFilters = aFilters.getArray(); + StringPair* pEnd = pFilters + aFilters.getLength(); + for ( ; pFilters != pEnd; ++pFilters ) + pFilters->First = addExtension( pFilters->First, pFilters->Second, sal_True, *m_pFileDlgImpl ); + } + m_xFilterGroupManager->appendFilterGroup( ::rtl::OUString(), aFilters ); + } + } + else + { + ::std::for_each( + _rGroup.begin(), + _rGroup.end(), + AppendFilter( m_xFilterManager, m_pFileDlgImpl, _bAddExtension ) ); + } + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION(); + } + } + + // operate on a single filter group + void operator() ( const FilterGroup& _rGroup ) + { + appendGroup( _rGroup, true ); + } + }; + + //-------------------------------------------------------------------- + TSortedFilterList::TSortedFilterList(const ::com::sun::star::uno::Reference< ::com::sun::star::container::XEnumeration >& xFilterList) + : m_nIterator(0) + { + if (!xFilterList.is()) + return; + + m_lFilters.clear(); + while(xFilterList->hasMoreElements()) + { + ::comphelper::SequenceAsHashMap lFilterProps (xFilterList->nextElement()); + ::rtl::OUString sFilterName = lFilterProps.getUnpackedValueOrDefault( + ::rtl::OUString::createFromAscii("Name"), + ::rtl::OUString()); + if (sFilterName.getLength()) + m_lFilters.push_back(sFilterName); + } + } + + //-------------------------------------------------------------------- + const SfxFilter* TSortedFilterList::First() + { + m_nIterator = 0; + return impl_getFilter(m_nIterator); + } + + //-------------------------------------------------------------------- + const SfxFilter* TSortedFilterList::Next() + { + ++m_nIterator; + return impl_getFilter(m_nIterator); + } + + //-------------------------------------------------------------------- + const SfxFilter* TSortedFilterList::impl_getFilter(sal_Int32 nIndex) + { + if (nIndex<0 || nIndex>=(sal_Int32)m_lFilters.size()) + return 0; + const ::rtl::OUString& sFilterName = m_lFilters[nIndex]; + if (!sFilterName.getLength()) + return 0; + return SfxFilter::GetFilterByName(String(sFilterName)); + } + + //-------------------------------------------------------------------- + void appendFiltersForSave( TSortedFilterList& _rFilterMatcher, + const Reference< XFilterManager >& _rxFilterManager, + ::rtl::OUString& _rFirstNonEmpty, FileDialogHelper_Impl& _rFileDlgImpl, + const ::rtl::OUString& _rFactory ) + { + DBG_ASSERT( _rxFilterManager.is(), "sfx2::appendFiltersForSave: invalid manager!" ); + if ( !_rxFilterManager.is() ) + return; + + ::rtl::OUString sUIName; + ::rtl::OUString sExtension; + + // retrieve the default filter for this application module. + // It must be set as first of the generated filter list. + const SfxFilter* pDefaultFilter = SfxFilterContainer::GetDefaultFilter_Impl(_rFactory); + // --> PB 2004-11-01 #i32434# only use one extension + // (and always the first if there are more than one) + sExtension = pDefaultFilter->GetWildcard().GetWildCard().GetToken( 0, ';' ); + // <-- + sUIName = addExtension( pDefaultFilter->GetUIName(), sExtension, sal_False, _rFileDlgImpl ); + try + { + _rxFilterManager->appendFilter( sUIName, sExtension ); + if ( !_rFirstNonEmpty.getLength() ) + _rFirstNonEmpty = sUIName; + } + catch( IllegalArgumentException ) + { +#ifdef DBG_UTIL + ByteString aMsg( "Could not append DefaultFilter" ); + aMsg += ByteString( String( sUIName ), osl_getThreadTextEncoding() ); + DBG_ERRORFILE( aMsg.GetBuffer() ); +#endif + } + + for ( const SfxFilter* pFilter = _rFilterMatcher.First(); pFilter; pFilter = _rFilterMatcher.Next() ) + { + if (pFilter->GetName() == pDefaultFilter->GetName()) + continue; + + // --> PB 2004-09-21 #i32434# only use one extension + // (and always the first if there are more than one) + sExtension = pFilter->GetWildcard().GetWildCard().GetToken( 0, ';' ); + // <-- + sUIName = addExtension( pFilter->GetUIName(), sExtension, sal_False, _rFileDlgImpl ); + try + { + _rxFilterManager->appendFilter( sUIName, sExtension ); + if ( !_rFirstNonEmpty.getLength() ) + _rFirstNonEmpty = sUIName; + } + catch( IllegalArgumentException ) + { + #ifdef DBG_UTIL + ByteString aMsg( "Could not append Filter" ); + aMsg += ByteString( String( sUIName ), osl_getThreadTextEncoding() ); + DBG_ERRORFILE( aMsg.GetBuffer() ); + #endif + } + } + } + + struct ExportFilter + { + ExportFilter( const rtl::OUString& _aUIName, const rtl::OUString& _aWildcard ) : + aUIName( _aUIName ), aWildcard( _aWildcard ) {} + + rtl::OUString aUIName; + rtl::OUString aWildcard; + }; + + //-------------------------------------------------------------------- + void appendExportFilters( TSortedFilterList& _rFilterMatcher, + const Reference< XFilterManager >& _rxFilterManager, + ::rtl::OUString& _rFirstNonEmpty, FileDialogHelper_Impl& _rFileDlgImpl ) + { + DBG_ASSERT( _rxFilterManager.is(), "sfx2::appendExportFilters: invalid manager!" ); + if ( !_rxFilterManager.is() ) + return; + + sal_Int32 nHTMLIndex = -1; + sal_Int32 nXHTMLIndex = -1; + sal_Int32 nPDFIndex = -1; + sal_Int32 nFlashIndex = -1; + ::rtl::OUString sUIName; + ::rtl::OUString sExtensions; + std::vector< ExportFilter > aImportantFilterGroup; + std::vector< ExportFilter > aFilterGroup; + Reference< XFilterGroupManager > xFilterGroupManager( _rxFilterManager, UNO_QUERY ); + ::rtl::OUString sTypeName; + const ::rtl::OUString sWriterHTMLType( DEFINE_CONST_OUSTRING("writer_web_HTML") ); + const ::rtl::OUString sGraphicHTMLType( DEFINE_CONST_OUSTRING("graphic_HTML") ); + const ::rtl::OUString sXHTMLType( DEFINE_CONST_OUSTRING("XHTML_File") ); + const ::rtl::OUString sPDFType( DEFINE_CONST_OUSTRING("pdf_Portable_Document_Format") ); + const ::rtl::OUString sFlashType( DEFINE_CONST_OUSTRING("graphic_SWF") ); + + for ( const SfxFilter* pFilter = _rFilterMatcher.First(); pFilter; pFilter = _rFilterMatcher.Next() ) + { + sTypeName = pFilter->GetTypeName(); + sUIName = pFilter->GetUIName(); + sExtensions = pFilter->GetWildcard().GetWildCard(); + ExportFilter aExportFilter( sUIName, sExtensions ); + String aExt = sExtensions; + + if ( nHTMLIndex == -1 && + ( sTypeName.equals( sWriterHTMLType ) || sTypeName.equals( sGraphicHTMLType ) ) ) + { + aImportantFilterGroup.insert( aImportantFilterGroup.begin(), aExportFilter ); + nHTMLIndex = 0; + } + else if ( nXHTMLIndex == -1 && sTypeName.equals( sXHTMLType ) ) + { + std::vector< ExportFilter >::iterator aIter = aImportantFilterGroup.begin(); + if ( nHTMLIndex == -1 ) + aImportantFilterGroup.insert( aIter, aExportFilter ); + else + aImportantFilterGroup.insert( ++aIter, aExportFilter ); + nXHTMLIndex = 0; + } + else if ( nPDFIndex == -1 && sTypeName.equals( sPDFType ) ) + { + std::vector< ExportFilter >::iterator aIter = aImportantFilterGroup.begin(); + if ( nHTMLIndex != -1 ) + aIter++; + if ( nXHTMLIndex != -1 ) + aIter++; + aImportantFilterGroup.insert( aIter, aExportFilter ); + nPDFIndex = 0; + } + else if ( nFlashIndex == -1 && sTypeName.equals( sFlashType ) ) + { + std::vector< ExportFilter >::iterator aIter = aImportantFilterGroup.begin(); + if ( nHTMLIndex != -1 ) + aIter++; + if ( nXHTMLIndex != -1 ) + aIter++; + if ( nPDFIndex != -1 ) + aIter++; + aImportantFilterGroup.insert( aIter, aExportFilter ); + nFlashIndex = 0; + } + else + aFilterGroup.push_back( aExportFilter ); + } + + if ( xFilterGroupManager.is() ) + { + // Add both html/pdf filter as a filter group to get a separator between both groups + if ( aImportantFilterGroup.size() > 0 ) + { + Sequence< StringPair > aFilters( aImportantFilterGroup.size() ); + for ( sal_Int32 i = 0; i < (sal_Int32)aImportantFilterGroup.size(); i++ ) + { + aFilters[i].First = addExtension( aImportantFilterGroup[i].aUIName, + aImportantFilterGroup[i].aWildcard, + sal_False, _rFileDlgImpl ); + aFilters[i].Second = aImportantFilterGroup[i].aWildcard; + } + + try + { + xFilterGroupManager->appendFilterGroup( ::rtl::OUString(), aFilters ); + } + catch( IllegalArgumentException ) + { + } + } + + if ( aFilterGroup.size() > 0 ) + { + Sequence< StringPair > aFilters( aFilterGroup.size() ); + for ( sal_Int32 i = 0; i < (sal_Int32)aFilterGroup.size(); i++ ) + { + aFilters[i].First = addExtension( aFilterGroup[i].aUIName, + aFilterGroup[i].aWildcard, + sal_False, _rFileDlgImpl ); + aFilters[i].Second = aFilterGroup[i].aWildcard; + } + + try + { + xFilterGroupManager->appendFilterGroup( ::rtl::OUString(), aFilters ); + } + catch( IllegalArgumentException ) + { + } + } + } + else + { + // Fallback solution just add both filter groups as single filters + sal_Int32 n; + + for ( n = 0; n < (sal_Int32)aImportantFilterGroup.size(); n++ ) + { + try + { + rtl::OUString aUIName = addExtension( aImportantFilterGroup[n].aUIName, + aImportantFilterGroup[n].aWildcard, + sal_False, _rFileDlgImpl ); + _rxFilterManager->appendFilter( aUIName, aImportantFilterGroup[n].aWildcard ); + if ( !_rFirstNonEmpty.getLength() ) + _rFirstNonEmpty = sUIName; + + } + catch( IllegalArgumentException ) + { + #ifdef DBG_UTIL + ByteString aMsg( "Could not append Filter" ); + aMsg += ByteString( String( sUIName ), osl_getThreadTextEncoding() ); + DBG_ERRORFILE( aMsg.GetBuffer() ); + #endif + } + } + + for ( n = 0; n < (sal_Int32)aFilterGroup.size(); n++ ) + { + try + { + rtl::OUString aUIName = addExtension( aFilterGroup[n].aUIName, + aFilterGroup[n].aWildcard, + sal_False, _rFileDlgImpl ); + _rxFilterManager->appendFilter( aUIName, aFilterGroup[n].aWildcard ); + if ( !_rFirstNonEmpty.getLength() ) + _rFirstNonEmpty = sUIName; + + } + catch( IllegalArgumentException ) + { + #ifdef DBG_UTIL + ByteString aMsg( "Could not append Filter" ); + aMsg += ByteString( String( sUIName ), osl_getThreadTextEncoding() ); + DBG_ERRORFILE( aMsg.GetBuffer() ); + #endif + } + } + } + } + + //-------------------------------------------------------------------- + void appendFiltersForOpen( TSortedFilterList& _rFilterMatcher, + const Reference< XFilterManager >& _rxFilterManager, + ::rtl::OUString& _rFirstNonEmpty, FileDialogHelper_Impl& _rFileDlgImpl ) + { + DBG_ASSERT( _rxFilterManager.is(), "sfx2::appendFiltersForOpen: invalid manager!" ); + if ( !_rxFilterManager.is() ) + return; + +#ifdef DISABLE_GROUPING_AND_CLASSIFYING + // =============================================================== + // ensure that there's an entry "all" (with wildcard *.*) + lcl_EnsureAllFilesEntry( _rFilterMatcher, _rxFilterManager, _rFirstNonEmpty ); + + // =============================================================== + appendFilters( _rFilterMatcher, _rxFilterManager, _rFirstNonEmpty ); +#else + + // =============================================================== + // group and classify the filters + GroupedFilterList aAllFilters; + lcl_GroupAndClassify( _rFilterMatcher, aAllFilters ); + + // =============================================================== + // ensure that we have the one "all files" entry + lcl_EnsureAllFilesEntry( _rFilterMatcher, aAllFilters ); + + // =============================================================== + // the first non-empty string - which we assume is the first overall entry + if ( !aAllFilters.empty() ) + { + const FilterGroup& rFirstGroup = *aAllFilters.begin(); // should be the global classes + if ( !rFirstGroup.empty() ) + _rFirstNonEmpty = rFirstGroup.begin()->First; + // append first group, without extension + AppendFilterGroup aGroup( _rxFilterManager, &_rFileDlgImpl ); + aGroup.appendGroup( rFirstGroup, false ); + } + + // =============================================================== + // append the filters to the manager + if ( !aAllFilters.empty() ) + { + ::std::list< FilterGroup >::iterator pIter = aAllFilters.begin(); + ++pIter; + ::std::for_each( + pIter, // first filter group was handled seperately, see above + aAllFilters.end(), + AppendFilterGroup( _rxFilterManager, &_rFileDlgImpl ) ); + } +#endif + } + + ::rtl::OUString addExtension( const ::rtl::OUString& _rDisplayText, + const ::rtl::OUString& _rExtension, + sal_Bool _bForOpen, FileDialogHelper_Impl& _rFileDlgImpl ) + { + static ::rtl::OUString sAllFilter( RTL_CONSTASCII_USTRINGPARAM( "(*.*)" ) ); + static ::rtl::OUString sOpenBracket( RTL_CONSTASCII_USTRINGPARAM( " (" ) ); + static ::rtl::OUString sCloseBracket( RTL_CONSTASCII_USTRINGPARAM( ")" ) ); + ::rtl::OUString sRet = _rDisplayText; + + if ( sRet.indexOf( sAllFilter ) == -1 ) + { + String sExt = _rExtension; + if ( !_bForOpen ) + // show '*' in extensions only when opening a document + sExt.EraseAllChars( '*' ); + sRet += sOpenBracket; + sRet += sExt; + sRet += sCloseBracket; + } + _rFileDlgImpl.addFilterPair( _rDisplayText, sRet ); + return sRet; + } + +//........................................................................ +} // namespace sfx2 +//........................................................................ + + |