Version 3.6.0.4, tag libreoffice-3.6.0.4
[LibreOffice.git] / sfx2 / source / dialog / filtergrouping.cxx
blob770149f1d52ac2b1f9bb3345829d105d79f5797b
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*************************************************************************
4 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
6 * Copyright 2000, 2010 Oracle and/or its affiliates.
8 * OpenOffice.org - a multi-platform office productivity suite
10 * This file is part of OpenOffice.org.
12 * OpenOffice.org is free software: you can redistribute it and/or modify
13 * it under the terms of the GNU Lesser General Public License version 3
14 * only, as published by the Free Software Foundation.
16 * OpenOffice.org is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU Lesser General Public License version 3 for more details
20 * (a copy is included in the LICENSE file that accompanied this code).
22 * You should have received a copy of the GNU Lesser General Public License
23 * version 3 along with OpenOffice.org. If not, see
24 * <http://www.openoffice.org/license.html>
25 * for a copy of the LGPLv3 License.
27 ************************************************************************/
29 #include "filtergrouping.hxx"
30 #include <sfx2/fcontnr.hxx>
31 #include <sfx2/filedlghelper.hxx>
32 #include <sfx2/sfx.hrc>
33 #include <sfx2/docfac.hxx>
34 #include "sfx2/sfxresid.hxx"
35 #include <osl/thread.h>
36 #include <rtl/oustringostreaminserter.hxx>
37 #include <rtl/strbuf.hxx>
38 #include <com/sun/star/ui/dialogs/XFilterGroupManager.hpp>
39 #include <com/sun/star/beans/StringPair.hpp>
40 #include <com/sun/star/uno/Sequence.hxx>
41 #include <unotools/confignode.hxx>
42 #include <comphelper/processfactory.hxx>
43 #include <comphelper/sequenceashashmap.hxx>
44 #include <comphelper/string.hxx>
45 #include <tools/diagnose_ex.h>
47 #include <list>
48 #include <vector>
49 #include <map>
50 #include <algorithm>
52 //........................................................................
53 namespace sfx2
55 //........................................................................
57 using namespace ::com::sun::star::uno;
58 using namespace ::com::sun::star::ui::dialogs;
59 using namespace ::com::sun::star::lang;
60 using namespace ::com::sun::star::beans;
61 using namespace ::utl;
63 //====================================================================
64 /**
66 Some general words about what's going on here ....
68 <p>In our file open dialog, usually we display every filter we know. That's how it was before: every filter
69 lead to an own line in the filter list box, e.g. "StarWriter 5.0 Dokument" or "Microsoft Word 97".</p>
71 <p>But then the PM came. And everything changed ....</p>
73 <p>A basic idea are groups: Why simply listing all the single filters? Couldn't we draw nice separators
74 between the filters which logically belong together? I.e. all the filters which open a document in StarWriter:
75 couldn't we separate them from all the filters which open the document in StarCalc?<br/>
76 So spoke the PM, and engineering obeyed.</p>
78 <p>So we have groups. They're just a visual aspect: All the filters of a group are presented together, separated
79 by a line from other groups.</p>
81 <p>Let's be honest: How the concrete implementation of the file picker service separates the different groups
82 is a matter of this implementation. We only do this grouping and suggest it to the FilePicker service ...</p>
84 <p>Now for the second concept:<br/>
85 Thinking about it (and that's what the PM did), both "StarWriter 5.0 Dokument" and "Microsoft Word 97"
86 describe a text document. It's a text. It's of no interest for the user that one of the texts was saved in
87 MS' format, and one in our own format.<br/>
88 So in a first step, we want to have a filter entry "Text documents". This would cover both above-mentioned
89 filters, as well as any other filters for documents which are texts.</p>
91 <p>Such an entry as "Text documents" is - within the scope of this file - called "class" or "filter class".</p>
93 <p>In the file-open-dialog, such a class looks like an ordinary filter: it's simply a name in the filter
94 listbox. Selecting means that all the files matching one of the "sub-filters" are displayed (in the example above,
95 this would be "*.sdw", "*.doc" and so on).</p>
97 <p>Now there are two types of filter classes: global ones and local ones. "Text documents" is a global class. As
98 well as "Spreadsheets". Or "Web pages".<br/>
99 Let's have a look at a local class: The filters "MS Word 95" and "MS WinWord 6.0" together form the class
100 "Microsoft Word 6.0 / 95" (don't ask for the reasons. At least not me. Ask the PM). There are a lot of such
101 local classes ...</p>
103 <p>The difference between global and local classes is as follows: Global classes are presented in an own group.
104 There is one dedicated group at the top of the list, containing all the global groups - no local groups and no
105 single filters.</p>
107 <p>Ehm - it was a lie. Not really at the top. Before this group, there is this single "All files" entry. It forms
108 it's own group. But this is uninteresting here.</p>
110 <p>Local classes must consist of filters which - without the classification - would all belong to the same group.
111 Then, they're combined to one entry (in the example above: "Microsoft Word 6.0 / 95"), and this entry is inserted
112 into the file picker filter list, instead of the single filters which form the class.</p>
114 <p>This is an interesting difference between local and global classes: Filters which are part of a global class
115 are listed in there own group, too. Filters in local classes aren't listed a second time - neither directly (as
116 the filter itself) nor indirectly (as part of another local group).</p>
118 <p>The only exception are filters which are part of a global class <em>and</em> a local class. This is allowed.
119 Beeing cotained in two local classes isn't.</p>
121 <p>So that's all what you need to know: Understand the concept of "filter classes" (a filter class combines
122 different filters and acts as if it's a filter itself) and the concept of groups (a group just describes a
123 logical correlation of filters and usually is represented to the user by drawing group separators in the filter
124 list).</p>
126 <p>If you got it, go try understanding this file :).</p>
131 //====================================================================
133 typedef StringPair FilterDescriptor; // a single filter or a filter class (display name and filter mask)
134 typedef ::std::list< FilterDescriptor > FilterGroup; // a list of single filter entries
135 typedef ::std::list< FilterGroup > GroupedFilterList; // a list of all filters, already grouped
137 /// the logical name of a filter
138 typedef ::rtl::OUString FilterName;
140 // a struct which holds references from a logical filter name to a filter group entry
141 // used for quick lookup of classes (means class entries - entries representing a class)
142 // which a given filter may belong to
143 typedef ::std::map< ::rtl::OUString, FilterGroup::iterator > FilterGroupEntryReferrer;
145 /// a descriptor for a filter class (which in the final dialog is represented by one filter entry)
146 typedef struct _tagFilterClass
148 ::rtl::OUString sDisplayName; // the display name
149 Sequence< FilterName > aSubFilters; // the (logical) names of the filter which belong to the class
150 } FilterClass;
152 typedef ::std::list< FilterClass > FilterClassList;
153 typedef ::std::map< ::rtl::OUString, FilterClassList::iterator > FilterClassReferrer;
155 typedef ::std::vector< ::rtl::OUString > StringArray;
157 // =======================================================================
158 // = reading of configuration data
159 // =======================================================================
161 //--------------------------------------------------------------------
162 void lcl_ReadFilterClass( const OConfigurationNode& _rClassesNode, const ::rtl::OUString& _rLogicalClassName,
163 FilterClass& /* [out] */ _rClass )
165 static const ::rtl::OUString sDisplaNameNodeName( "DisplayName" );
166 static const ::rtl::OUString sSubFiltersNodeName( "Filters" );
168 // the description node for the current class
169 OConfigurationNode aClassDesc = _rClassesNode.openNode( _rLogicalClassName );
171 // the values
172 aClassDesc.getNodeValue( sDisplaNameNodeName ) >>= _rClass.sDisplayName;
173 aClassDesc.getNodeValue( sSubFiltersNodeName ) >>= _rClass.aSubFilters;
176 //--------------------------------------------------------------------
177 struct CreateEmptyClassRememberPos : public ::std::unary_function< FilterName, void >
179 protected:
180 FilterClassList& m_rClassList;
181 FilterClassReferrer& m_rClassesReferrer;
183 public:
184 CreateEmptyClassRememberPos( FilterClassList& _rClassList, FilterClassReferrer& _rClassesReferrer )
185 :m_rClassList ( _rClassList )
186 ,m_rClassesReferrer ( _rClassesReferrer )
190 // operate on a single class name
191 void operator() ( const FilterName& _rLogicalFilterName )
193 // insert a new (empty) class
194 m_rClassList.push_back( FilterClass() );
195 // get the position of this new entry
196 FilterClassList::iterator aInsertPos = m_rClassList.end();
197 --aInsertPos;
198 // remember this position
199 m_rClassesReferrer.insert( FilterClassReferrer::value_type( _rLogicalFilterName, aInsertPos ) );
203 //--------------------------------------------------------------------
204 struct ReadGlobalFilter : public ::std::unary_function< FilterName, void >
206 protected:
207 OConfigurationNode m_aClassesNode;
208 FilterClassReferrer& m_aClassReferrer;
210 public:
211 ReadGlobalFilter( const OConfigurationNode& _rClassesNode, FilterClassReferrer& _rClassesReferrer )
212 :m_aClassesNode ( _rClassesNode )
213 ,m_aClassReferrer ( _rClassesReferrer )
217 // operate on a single logical name
218 void operator() ( const FilterName& _rName )
220 FilterClassReferrer::iterator aClassRef = m_aClassReferrer.find( _rName );
221 if ( m_aClassReferrer.end() == aClassRef )
223 // we do not know this global class
224 OSL_FAIL( "ReadGlobalFilter::operator(): unknown filter name!" );
225 // TODO: perhaps we should be more tolerant - at the moment, the filter is dropped
226 // We could silently push_back it to the container ....
228 else
230 // read the data of this class into the node referred to by aClassRef
231 lcl_ReadFilterClass( m_aClassesNode, _rName, *aClassRef->second );
236 //--------------------------------------------------------------------
237 void lcl_ReadGlobalFilters( const OConfigurationNode& _rFilterClassification, FilterClassList& _rGlobalClasses, StringArray& _rGlobalClassNames )
239 _rGlobalClasses.clear();
240 _rGlobalClassNames.clear();
242 //================================================================
243 // get the list describing the order of all global classes
244 Sequence< ::rtl::OUString > aGlobalClasses;
245 _rFilterClassification.getNodeValue( DEFINE_CONST_OUSTRING( "GlobalFilters/Order" ) ) >>= aGlobalClasses;
247 const ::rtl::OUString* pNames = aGlobalClasses.getConstArray();
248 const ::rtl::OUString* pNamesEnd = pNames + aGlobalClasses.getLength();
250 // copy the logical names
251 _rGlobalClassNames.resize( aGlobalClasses.getLength() );
252 ::std::copy( pNames, pNamesEnd, _rGlobalClassNames.begin() );
254 // Global classes are presented in an own group, so their order matters (while the order of the
255 // "local classes" doesn't).
256 // That's why we can't simply add the global classes to _rGlobalClasses using the order in which they
257 // are returned from the configuration - it is completely undefined, and we need a _defined_ order.
258 FilterClassReferrer aClassReferrer;
259 ::std::for_each(
260 pNames,
261 pNamesEnd,
262 CreateEmptyClassRememberPos( _rGlobalClasses, aClassReferrer )
264 // now _rGlobalClasses contains a dummy entry for each global class,
265 // while aClassReferrer maps from the logical name of the class to the position within _rGlobalClasses where
266 // it's dummy entry resides
268 //================================================================
269 // go for all the single class entries
270 OConfigurationNode aFilterClassesNode =
271 _rFilterClassification.openNode( DEFINE_CONST_OUSTRING( "GlobalFilters/Classes" ) );
272 Sequence< ::rtl::OUString > aFilterClasses = aFilterClassesNode.getNodeNames();
273 ::std::for_each(
274 aFilterClasses.getConstArray(),
275 aFilterClasses.getConstArray() + aFilterClasses.getLength(),
276 ReadGlobalFilter( aFilterClassesNode, aClassReferrer )
280 //--------------------------------------------------------------------
281 struct ReadLocalFilter : public ::std::unary_function< FilterName, void >
283 protected:
284 OConfigurationNode m_aClassesNode;
285 FilterClassList& m_rClasses;
287 public:
288 ReadLocalFilter( const OConfigurationNode& _rClassesNode, FilterClassList& _rClasses )
289 :m_aClassesNode ( _rClassesNode )
290 ,m_rClasses ( _rClasses )
294 // operate on a single logical name
295 void operator() ( const FilterName& _rName )
297 // read the data for this class
298 FilterClass aClass;
299 lcl_ReadFilterClass( m_aClassesNode, _rName, aClass );
301 // insert the class descriptor
302 m_rClasses.push_back( aClass );
306 //--------------------------------------------------------------------
307 void lcl_ReadLocalFilters( const OConfigurationNode& _rFilterClassification, FilterClassList& _rLocalClasses )
309 _rLocalClasses.clear();
311 // the node for the local classes
312 OConfigurationNode aFilterClassesNode =
313 _rFilterClassification.openNode( DEFINE_CONST_OUSTRING( "LocalFilters/Classes" ) );
314 Sequence< ::rtl::OUString > aFilterClasses = aFilterClassesNode.getNodeNames();
316 ::std::for_each(
317 aFilterClasses.getConstArray(),
318 aFilterClasses.getConstArray() + aFilterClasses.getLength(),
319 ReadLocalFilter( aFilterClassesNode, _rLocalClasses )
323 //--------------------------------------------------------------------
324 void lcl_ReadClassification( FilterClassList& _rGlobalClasses, StringArray& _rGlobalClassNames, FilterClassList& _rLocalClasses )
326 //================================================================
327 // open our config node
328 OConfigurationTreeRoot aFilterClassification = OConfigurationTreeRoot::createWithServiceFactory(
329 ::comphelper::getProcessServiceFactory(),
330 DEFINE_CONST_OUSTRING( "org.openoffice.Office.UI/FilterClassification" ),
332 OConfigurationTreeRoot::CM_READONLY
335 //================================================================
336 // go for the global classes
337 lcl_ReadGlobalFilters( aFilterClassification, _rGlobalClasses, _rGlobalClassNames );
339 //================================================================
340 // fo for the local classes
341 lcl_ReadLocalFilters( aFilterClassification, _rLocalClasses );
345 // =======================================================================
346 // = grouping and classifying
347 // =======================================================================
349 //--------------------------------------------------------------------
350 // a struct which adds helps remembering a reference to a class entry
351 struct ReferToFilterEntry : public ::std::unary_function< FilterName, void >
353 protected:
354 FilterGroupEntryReferrer& m_rEntryReferrer;
355 FilterGroup::iterator m_aClassPos;
357 public:
358 ReferToFilterEntry( FilterGroupEntryReferrer& _rEntryReferrer, const FilterGroup::iterator& _rClassPos )
359 :m_rEntryReferrer( _rEntryReferrer )
360 ,m_aClassPos( _rClassPos )
364 // operate on a single filter name
365 void operator() ( const FilterName& _rName )
367 #ifdef DBG_UTIL
368 ::std::pair< FilterGroupEntryReferrer::iterator, bool > aInsertRes =
369 #endif
370 m_rEntryReferrer.insert( FilterGroupEntryReferrer::value_type( _rName, m_aClassPos ) );
371 DBG_ASSERT( aInsertRes.second, "ReferToFilterEntry::operator(): already have an element for this name!" );
375 //--------------------------------------------------------------------
376 struct FillClassGroup : public ::std::unary_function< FilterClass, void >
378 protected:
379 FilterGroup& m_rClassGroup;
380 FilterGroupEntryReferrer& m_rClassReferrer;
382 public:
383 FillClassGroup( FilterGroup& _rClassGroup, FilterGroupEntryReferrer& _rClassReferrer )
384 :m_rClassGroup ( _rClassGroup )
385 ,m_rClassReferrer ( _rClassReferrer )
389 // operate on a single class
390 void operator() ( const FilterClass& _rClass )
392 // create an empty filter descriptor for the class
393 FilterDescriptor aClassEntry;
394 // set it's name (which is all we know by now)
395 aClassEntry.First = _rClass.sDisplayName;
397 // add it to the group
398 m_rClassGroup.push_back( aClassEntry );
399 // the position of the newly added class
400 FilterGroup::iterator aClassEntryPos = m_rClassGroup.end();
401 --aClassEntryPos;
403 // and for all the sub filters of the class, remember the class
404 // (respectively the position of the class it the group)
405 ::std::for_each(
406 _rClass.aSubFilters.getConstArray(),
407 _rClass.aSubFilters.getConstArray() + _rClass.aSubFilters.getLength(),
408 ReferToFilterEntry( m_rClassReferrer, aClassEntryPos )
413 //--------------------------------------------------------------------
414 static const sal_Unicode s_cWildcardSeparator( ';' );
416 //====================================================================
417 const ::rtl::OUString& getSeparatorString()
419 static ::rtl::OUString s_sSeparatorString( &s_cWildcardSeparator, 1 );
420 return s_sSeparatorString;
423 //====================================================================
424 struct CheckAppendSingleWildcard : public ::std::unary_function< ::rtl::OUString, void >
426 ::rtl::OUString& _rToBeExtended;
428 CheckAppendSingleWildcard( ::rtl::OUString& _rBase ) : _rToBeExtended( _rBase ) { }
430 void operator() ( const ::rtl::OUString& _rWC )
432 // check for double wildcards
433 sal_Int32 nExistentPos = _rToBeExtended.indexOf( _rWC );
434 if ( -1 < nExistentPos )
435 { // found this wildcard (already part of _rToBeExtended)
436 const sal_Unicode* pBuffer = _rToBeExtended.getStr();
437 if ( ( 0 == nExistentPos )
438 || ( s_cWildcardSeparator == pBuffer[ nExistentPos - 1 ] )
440 { // the wildcard really starts at this position (it starts at pos 0 or the previous character is a separator
441 sal_Int32 nExistentWCEnd = nExistentPos + _rWC.getLength();
442 if ( ( _rToBeExtended.getLength() == nExistentWCEnd )
443 || ( s_cWildcardSeparator == pBuffer[ nExistentWCEnd ] )
445 { // it's really the complete wildcard we found
446 // (not something like _rWC beeing "*.t" and _rToBeExtended containing "*.txt")
447 // -> outta here
448 return;
453 if ( !_rToBeExtended.isEmpty() )
454 _rToBeExtended += getSeparatorString();
455 _rToBeExtended += _rWC;
459 //====================================================================
460 // a helper struct which adds a fixed (Sfx-)filter to a filter group entry given by iterator
461 struct AppendWildcardToDescriptor : public ::std::unary_function< FilterGroupEntryReferrer::value_type, void >
463 protected:
464 ::std::vector< ::rtl::OUString > aWildCards;
466 public:
467 AppendWildcardToDescriptor( const String& _rWildCard );
469 // operate on a single class entry
470 void operator() ( const FilterGroupEntryReferrer::value_type& _rClassReference )
472 // simply add our wildcards
473 ::std::for_each(
474 aWildCards.begin(),
475 aWildCards.end(),
476 CheckAppendSingleWildcard( _rClassReference.second->Second )
481 //====================================================================
482 AppendWildcardToDescriptor::AppendWildcardToDescriptor( const String& _rWildCard )
484 DBG_ASSERT( _rWildCard.Len(),
485 "AppendWildcardToDescriptor::AppendWildcardToDescriptor: invalid wildcard!" );
486 DBG_ASSERT( _rWildCard.GetBuffer()[0] != s_cWildcardSeparator,
487 "AppendWildcardToDescriptor::AppendWildcardToDescriptor: wildcard already separated!" );
489 aWildCards.reserve( comphelper::string::getTokenCount(_rWildCard, s_cWildcardSeparator) );
491 const sal_Unicode* pTokenLoop = _rWildCard.GetBuffer();
492 const sal_Unicode* pTokenLoopEnd = pTokenLoop + _rWildCard.Len();
493 const sal_Unicode* pTokenStart = pTokenLoop;
494 for ( ; pTokenLoop != pTokenLoopEnd; ++pTokenLoop )
496 if ( ( s_cWildcardSeparator == *pTokenLoop ) && ( pTokenLoop > pTokenStart ) )
497 { // found a new token separator (and a non-empty token)
498 aWildCards.push_back( ::rtl::OUString( pTokenStart, pTokenLoop - pTokenStart ) );
500 // search the start of the next token
501 while ( ( pTokenStart != pTokenLoopEnd ) && ( *pTokenStart != s_cWildcardSeparator ) )
502 ++pTokenStart;
504 if ( pTokenStart == pTokenLoopEnd )
505 // reached the end
506 break;
508 ++pTokenStart;
509 pTokenLoop = pTokenStart;
512 if ( pTokenLoop > pTokenStart )
513 // the last one ....
514 aWildCards.push_back( ::rtl::OUString( pTokenStart, pTokenLoop - pTokenStart ) );
517 //--------------------------------------------------------------------
518 void lcl_InitGlobalClasses( GroupedFilterList& _rAllFilters, const FilterClassList& _rGlobalClasses, FilterGroupEntryReferrer& _rGlobalClassesRef )
520 // we need an extra group in our "all filters" container
521 _rAllFilters.push_front( FilterGroup() );
522 FilterGroup& rGlobalFilters = _rAllFilters.front();
523 // it's important to work on the reference: we want to access the members of this filter group
524 // by an iterator (FilterGroup::const_iterator)
525 // the referrer for the global classes
527 // initialize the group
528 ::std::for_each(
529 _rGlobalClasses.begin(),
530 _rGlobalClasses.end(),
531 FillClassGroup( rGlobalFilters, _rGlobalClassesRef )
533 // now we have:
534 // in rGlobalFilters: a list of FilterDescriptor's, where each's discriptor's display name is set to the name of a class
535 // in aGlobalClassesRef: a mapping from logical filter names to positions within rGlobalFilters
536 // this way, if we encounter an arbitrary filter, we can easily (and efficient) check if it belongs to a global class
537 // and modify the descriptor for this class accordingly
540 //--------------------------------------------------------------------
541 typedef ::std::vector< ::std::pair< FilterGroupEntryReferrer::mapped_type, FilterGroup::iterator > >
542 MapGroupEntry2GroupEntry;
543 // this is not really a map - it's just called this way because it is used as a map
545 struct FindGroupEntry : public ::std::unary_function< MapGroupEntry2GroupEntry::value_type, sal_Bool >
547 FilterGroupEntryReferrer::mapped_type aLookingFor;
548 FindGroupEntry( FilterGroupEntryReferrer::mapped_type _rLookingFor ) : aLookingFor( _rLookingFor ) { }
550 sal_Bool operator() ( const MapGroupEntry2GroupEntry::value_type& _rMapEntry )
552 return _rMapEntry.first == aLookingFor ? sal_True : sal_False;
556 struct CopyGroupEntryContent : public ::std::unary_function< MapGroupEntry2GroupEntry::value_type, void >
558 void operator() ( const MapGroupEntry2GroupEntry::value_type& _rMapEntry )
560 #ifdef DBG_UTIL
561 FilterDescriptor aHaveALook = *_rMapEntry.first;
562 #endif
563 *_rMapEntry.second = *_rMapEntry.first;
567 //--------------------------------------------------------------------
568 struct CopyNonEmptyFilter : public ::std::unary_function< FilterDescriptor, void >
570 FilterGroup& rTarget;
571 CopyNonEmptyFilter( FilterGroup& _rTarget ) :rTarget( _rTarget ) { }
573 void operator() ( const FilterDescriptor& _rFilter )
575 if ( !_rFilter.Second.isEmpty() )
576 rTarget.push_back( _rFilter );
580 //--------------------------------------------------------------------
581 void lcl_GroupAndClassify( TSortedFilterList& _rFilterMatcher, GroupedFilterList& _rAllFilters )
583 _rAllFilters.clear();
585 // ===============================================================
586 // read the classification of filters
587 FilterClassList aGlobalClasses, aLocalClasses;
588 StringArray aGlobalClassNames;
589 lcl_ReadClassification( aGlobalClasses, aGlobalClassNames, aLocalClasses );
591 // ===============================================================
592 // for the global filter classes
593 FilterGroupEntryReferrer aGlobalClassesRef;
594 lcl_InitGlobalClasses( _rAllFilters, aGlobalClasses, aGlobalClassesRef );
596 // insert as much placeholders (FilterGroup's) into _rAllFilter for groups as we have global classes
597 // (this assumes that both numbers are the same, which, speaking strictly, must not hold - but it does, as we know ...)
598 sal_Int32 nGlobalClasses = aGlobalClasses.size();
599 while ( nGlobalClasses-- )
600 _rAllFilters.push_back( FilterGroup() );
602 // ===============================================================
603 // for the local classes:
604 // if n filters belong to a local class, they do not appear in their respective group explicitly, instead
605 // and entry for the class is added to the group and the extensions of the filters are collected under
606 // this entry
607 FilterGroupEntryReferrer aLocalClassesRef;
608 FilterGroup aCollectedLocals;
609 ::std::for_each(
610 aLocalClasses.begin(),
611 aLocalClasses.end(),
612 FillClassGroup( aCollectedLocals, aLocalClassesRef )
614 // to map from the position within aCollectedLocals to positions within the real groups
615 // (where they finally belong to)
616 MapGroupEntry2GroupEntry aLocalFinalPositions;
618 // ===============================================================
619 // now add the filters
620 // the group which we currently work with
621 GroupedFilterList::iterator aCurrentGroup = _rAllFilters.end(); // no current group
622 // the filter container of the current group - if this changes between two filters, a new group is reached
623 String aCurrentServiceName;
625 String sFilterWildcard;
626 ::rtl::OUString sFilterName;
627 // loop through all the filters
628 for ( const SfxFilter* pFilter = _rFilterMatcher.First(); pFilter; pFilter = _rFilterMatcher.Next() )
630 sFilterName = pFilter->GetFilterName();
631 sFilterWildcard = pFilter->GetWildcard().getGlob();
632 AppendWildcardToDescriptor aExtendWildcard( sFilterWildcard );
634 DBG_ASSERT( sFilterWildcard.Len(), "sfx2::lcl_GroupAndClassify: invalid wildcard of this filter!" );
636 // ===========================================================
637 // check for a change in the group
638 String aServiceName = pFilter->GetServiceName();
639 if ( aServiceName != aCurrentServiceName )
640 { // we reached a new group
642 ::rtl::OUString sDocServName = aServiceName;
644 // look for the place in _rAllFilters where this ne group belongs - this is determined
645 // by the order of classes in aGlobalClassNames
646 GroupedFilterList::iterator aGroupPos = _rAllFilters.begin();
647 DBG_ASSERT( aGroupPos != _rAllFilters.end(),
648 "sfx2::lcl_GroupAndClassify: invalid all-filters array here!" );
649 // the loop below will work on invalid objects else ...
650 ++aGroupPos;
651 StringArray::iterator aGlobalIter = aGlobalClassNames.begin();
652 while ( ( aGroupPos != _rAllFilters.end() )
653 && ( aGlobalIter != aGlobalClassNames.end() )
654 && ( *aGlobalIter != sDocServName )
657 ++aGlobalIter;
658 ++aGroupPos;
660 if ( aGroupPos != _rAllFilters.end() )
661 // we found a global class name which matchies the doc service name -> fill the filters of this
662 // group in the respective prepared group
663 aCurrentGroup = aGroupPos;
664 else
665 // insert a new entry in our overall-list
666 aCurrentGroup = _rAllFilters.insert( _rAllFilters.end(), FilterGroup() );
668 // remember the container to properly detect the next group
669 aCurrentServiceName = aServiceName;
672 DBG_ASSERT( aCurrentGroup != _rAllFilters.end(), "sfx2::lcl_GroupAndClassify: invalid current group!" );
674 // ===========================================================
675 // check if the filter is part of a global group
676 ::std::pair< FilterGroupEntryReferrer::iterator, FilterGroupEntryReferrer::iterator >
677 aBelongsTo = aGlobalClassesRef.equal_range( sFilterName );
678 // add the filter to the entries for these classes
679 // (if they exist - if not, the range is empty and the for_each is a no-op)
680 ::std::for_each(
681 aBelongsTo.first,
682 aBelongsTo.second,
683 aExtendWildcard
686 // ===========================================================
687 // add the filter to it's group
689 // for this, check if the filter is part of a local filter
690 FilterGroupEntryReferrer::iterator aBelongsToLocal = aLocalClassesRef.find( sFilterName );
691 if ( aLocalClassesRef.end() != aBelongsToLocal )
693 // okay, there is a local class which the filter belongs to
694 // -> append the wildcard
695 aExtendWildcard( *aBelongsToLocal );
697 MapGroupEntry2GroupEntry::iterator aThisGroupFinalPos =
698 ::std::find_if( aLocalFinalPositions.begin(), aLocalFinalPositions.end(), FindGroupEntry( aBelongsToLocal->second ) );
700 if ( aLocalFinalPositions.end() == aThisGroupFinalPos )
701 { // the position within aCollectedLocals has not been mapped to a final position
702 // within the "real" group (aCollectedLocals is only temporary)
703 // -> do this now (as we just encountered the first filter belonging to this local class
704 // add a new entry which is the "real" group entry
705 aCurrentGroup->push_back( FilterDescriptor( aBelongsToLocal->second->First, String() ) );
706 // the position where we inserted the entry
707 FilterGroup::iterator aInsertPos = aCurrentGroup->end();
708 --aInsertPos;
709 // remember this pos
710 aLocalFinalPositions.push_back( MapGroupEntry2GroupEntry::value_type( aBelongsToLocal->second, aInsertPos ) );
713 else
714 aCurrentGroup->push_back( FilterDescriptor( pFilter->GetUIName(), sFilterWildcard ) );
717 // now just complete the infos for the local groups:
718 // During the above loop, they have been collected in aCollectedLocals, but this is only temporary
719 // They have to be copied into their final positions (which are stored in aLocalFinalPositions)
720 ::std::for_each(
721 aLocalFinalPositions.begin(),
722 aLocalFinalPositions.end(),
723 CopyGroupEntryContent()
726 // and remove local groups which do not apply - e.g. have no entries due to the limited content of the
727 // current SfxFilterMatcherIter
729 FilterGroup& rGlobalFilters = _rAllFilters.front();
730 FilterGroup aNonEmptyGlobalFilters;
731 ::std::for_each(
732 rGlobalFilters.begin(),
733 rGlobalFilters.end(),
734 CopyNonEmptyFilter( aNonEmptyGlobalFilters )
736 rGlobalFilters.swap( aNonEmptyGlobalFilters );
739 //--------------------------------------------------------------------
740 struct AppendFilter : public ::std::unary_function< FilterDescriptor, void >
742 protected:
743 Reference< XFilterManager > m_xFilterManager;
744 FileDialogHelper_Impl* m_pFileDlgImpl;
745 bool m_bAddExtension;
747 public:
748 AppendFilter( const Reference< XFilterManager >& _rxFilterManager,
749 FileDialogHelper_Impl* _pImpl, bool _bAddExtension ) :
751 m_xFilterManager( _rxFilterManager ),
752 m_pFileDlgImpl ( _pImpl ),
753 m_bAddExtension ( _bAddExtension )
756 DBG_ASSERT( m_xFilterManager.is(), "AppendFilter::AppendFilter: invalid filter manager!" );
757 DBG_ASSERT( m_pFileDlgImpl, "AppendFilter::AppendFilter: invalid filedlg impl!" );
760 // operate on a single filter
761 void operator() ( const FilterDescriptor& _rFilterEntry )
763 String sDisplayText = m_bAddExtension
764 ? addExtension( _rFilterEntry.First, _rFilterEntry.Second, sal_True, *m_pFileDlgImpl )
765 : _rFilterEntry.First;
766 m_xFilterManager->appendFilter( sDisplayText, _rFilterEntry.Second );
770 // =======================================================================
771 // = handling for the "all files" entry
772 // =======================================================================
774 //--------------------------------------------------------------------
775 sal_Bool lcl_hasAllFilesFilter( TSortedFilterList& _rFilterMatcher, String& /* [out] */ _rAllFilterName )
777 ::rtl::OUString sUIName;
778 sal_Bool bHasAll = sal_False;
779 _rAllFilterName = String( SfxResId( STR_SFX_FILTERNAME_ALL ) );
781 // ===============================================================
782 // check if there's already a filter <ALL>
783 for ( const SfxFilter* pFilter = _rFilterMatcher.First(); pFilter && !bHasAll; pFilter = _rFilterMatcher.Next() )
785 if ( pFilter->GetUIName() == _rAllFilterName )
786 bHasAll = sal_True;
788 return bHasAll;
791 //--------------------------------------------------------------------
792 void lcl_EnsureAllFilesEntry( TSortedFilterList& _rFilterMatcher, GroupedFilterList& _rFilters )
794 // ===============================================================
795 String sAllFilterName;
796 if ( !lcl_hasAllFilesFilter( _rFilterMatcher, sAllFilterName ) )
798 // get the first group of filters (by definition, this group contains the global classes)
799 DBG_ASSERT( !_rFilters.empty(), "lcl_EnsureAllFilesEntry: invalid filter list!" );
800 if ( !_rFilters.empty() )
802 FilterGroup& rGlobalClasses = *_rFilters.begin();
803 rGlobalClasses.push_front( FilterDescriptor( sAllFilterName, DEFINE_CONST_UNICODE( FILEDIALOG_FILTER_ALL ) ) );
808 // =======================================================================
809 // = filling an XFilterManager
810 // =======================================================================
812 //--------------------------------------------------------------------
813 struct AppendFilterGroup : public ::std::unary_function< FilterGroup, void >
815 protected:
816 Reference< XFilterManager > m_xFilterManager;
817 Reference< XFilterGroupManager > m_xFilterGroupManager;
818 FileDialogHelper_Impl* m_pFileDlgImpl;
820 public:
821 AppendFilterGroup( const Reference< XFilterManager >& _rxFilterManager, FileDialogHelper_Impl* _pImpl )
822 :m_xFilterManager ( _rxFilterManager )
823 ,m_xFilterGroupManager ( _rxFilterManager, UNO_QUERY )
824 ,m_pFileDlgImpl ( _pImpl )
826 DBG_ASSERT( m_xFilterManager.is(), "AppendFilterGroup::AppendFilterGroup: invalid filter manager!" );
827 DBG_ASSERT( m_pFileDlgImpl, "AppendFilterGroup::AppendFilterGroup: invalid filedlg impl!" );
830 void appendGroup( const FilterGroup& _rGroup, bool _bAddExtension )
834 if ( m_xFilterGroupManager.is() )
835 { // the file dialog implementation supports visual grouping of filters
836 // create a representation of the group which is understandable by the XFilterGroupManager
837 if ( _rGroup.size() )
839 Sequence< StringPair > aFilters( _rGroup.size() );
840 ::std::copy(
841 _rGroup.begin(),
842 _rGroup.end(),
843 aFilters.getArray()
845 if ( _bAddExtension )
847 StringPair* pFilters = aFilters.getArray();
848 StringPair* pEnd = pFilters + aFilters.getLength();
849 for ( ; pFilters != pEnd; ++pFilters )
850 pFilters->First = addExtension( pFilters->First, pFilters->Second, sal_True, *m_pFileDlgImpl );
852 m_xFilterGroupManager->appendFilterGroup( ::rtl::OUString(), aFilters );
855 else
857 ::std::for_each(
858 _rGroup.begin(),
859 _rGroup.end(),
860 AppendFilter( m_xFilterManager, m_pFileDlgImpl, _bAddExtension ) );
863 catch( const Exception& )
865 DBG_UNHANDLED_EXCEPTION();
869 // operate on a single filter group
870 void operator() ( const FilterGroup& _rGroup )
872 appendGroup( _rGroup, true );
876 //--------------------------------------------------------------------
877 TSortedFilterList::TSortedFilterList(const ::com::sun::star::uno::Reference< ::com::sun::star::container::XEnumeration >& xFilterList)
878 : m_nIterator(0)
880 if (!xFilterList.is())
881 return;
883 m_lFilters.clear();
884 while(xFilterList->hasMoreElements())
886 ::comphelper::SequenceAsHashMap lFilterProps (xFilterList->nextElement());
887 ::rtl::OUString sFilterName = lFilterProps.getUnpackedValueOrDefault(
888 ::rtl::OUString("Name"),
889 ::rtl::OUString());
890 if (!sFilterName.isEmpty())
891 m_lFilters.push_back(sFilterName);
895 //--------------------------------------------------------------------
896 const SfxFilter* TSortedFilterList::First()
898 m_nIterator = 0;
899 return impl_getFilter(m_nIterator);
902 //--------------------------------------------------------------------
903 const SfxFilter* TSortedFilterList::Next()
905 ++m_nIterator;
906 return impl_getFilter(m_nIterator);
909 //--------------------------------------------------------------------
910 const SfxFilter* TSortedFilterList::impl_getFilter(sal_Int32 nIndex)
912 if (nIndex<0 || nIndex>=(sal_Int32)m_lFilters.size())
913 return 0;
914 const ::rtl::OUString& sFilterName = m_lFilters[nIndex];
915 if (sFilterName.isEmpty())
916 return 0;
917 return SfxFilter::GetFilterByName(String(sFilterName));
920 //--------------------------------------------------------------------
921 void appendFiltersForSave( TSortedFilterList& _rFilterMatcher,
922 const Reference< XFilterManager >& _rxFilterManager,
923 ::rtl::OUString& _rFirstNonEmpty, FileDialogHelper_Impl& _rFileDlgImpl,
924 const ::rtl::OUString& _rFactory )
926 DBG_ASSERT( _rxFilterManager.is(), "sfx2::appendFiltersForSave: invalid manager!" );
927 if ( !_rxFilterManager.is() )
928 return;
930 ::rtl::OUString sUIName;
931 ::rtl::OUString sExtension;
933 // retrieve the default filter for this application module.
934 // It must be set as first of the generated filter list.
935 const SfxFilter* pDefaultFilter = SfxFilterContainer::GetDefaultFilter_Impl(_rFactory);
936 // Only use one extension (#i32434#)
937 // (and always the first if there are more than one)
938 using comphelper::string::getToken;
939 sExtension = getToken(pDefaultFilter->GetWildcard().getGlob(), 0, ';');
940 sUIName = addExtension( pDefaultFilter->GetUIName(), sExtension, sal_False, _rFileDlgImpl );
943 _rxFilterManager->appendFilter( sUIName, sExtension );
944 if ( _rFirstNonEmpty.isEmpty() )
945 _rFirstNonEmpty = sUIName;
947 catch( const IllegalArgumentException& )
949 SAL_WARN( "sfx2.dialog", "Could not append DefaultFilter" << sUIName );
952 for ( const SfxFilter* pFilter = _rFilterMatcher.First(); pFilter; pFilter = _rFilterMatcher.Next() )
954 if (pFilter->GetName() == pDefaultFilter->GetName())
955 continue;
957 // Only use one extension (#i32434#)
958 // (and always the first if there are more than one)
959 sExtension = getToken(pFilter->GetWildcard().getGlob(), 0, ';');
960 sUIName = addExtension( pFilter->GetUIName(), sExtension, sal_False, _rFileDlgImpl );
963 _rxFilterManager->appendFilter( sUIName, sExtension );
964 if ( _rFirstNonEmpty.isEmpty() )
965 _rFirstNonEmpty = sUIName;
967 catch( const IllegalArgumentException& )
969 SAL_WARN( "sfx2.dialog", "Could not append Filter" << sUIName );
974 struct ExportFilter
976 ExportFilter( const rtl::OUString& _aUIName, const rtl::OUString& _aWildcard ) :
977 aUIName( _aUIName ), aWildcard( _aWildcard ) {}
979 rtl::OUString aUIName;
980 rtl::OUString aWildcard;
983 //--------------------------------------------------------------------
984 void appendExportFilters( TSortedFilterList& _rFilterMatcher,
985 const Reference< XFilterManager >& _rxFilterManager,
986 ::rtl::OUString& _rFirstNonEmpty, FileDialogHelper_Impl& _rFileDlgImpl )
988 DBG_ASSERT( _rxFilterManager.is(), "sfx2::appendExportFilters: invalid manager!" );
989 if ( !_rxFilterManager.is() )
990 return;
992 sal_Int32 nHTMLIndex = -1;
993 sal_Int32 nXHTMLIndex = -1;
994 sal_Int32 nPDFIndex = -1;
995 sal_Int32 nFlashIndex = -1;
996 ::rtl::OUString sUIName;
997 ::rtl::OUString sExtensions;
998 std::vector< ExportFilter > aImportantFilterGroup;
999 std::vector< ExportFilter > aFilterGroup;
1000 Reference< XFilterGroupManager > xFilterGroupManager( _rxFilterManager, UNO_QUERY );
1001 ::rtl::OUString sTypeName;
1002 const ::rtl::OUString sWriterHTMLType( DEFINE_CONST_OUSTRING("generic_HTML") );
1003 const ::rtl::OUString sGraphicHTMLType( DEFINE_CONST_OUSTRING("graphic_HTML") );
1004 const ::rtl::OUString sXHTMLType( DEFINE_CONST_OUSTRING("XHTML_File") );
1005 const ::rtl::OUString sPDFType( DEFINE_CONST_OUSTRING("pdf_Portable_Document_Format") );
1006 const ::rtl::OUString sFlashType( DEFINE_CONST_OUSTRING("graphic_SWF") );
1008 for ( const SfxFilter* pFilter = _rFilterMatcher.First(); pFilter; pFilter = _rFilterMatcher.Next() )
1010 sTypeName = pFilter->GetTypeName();
1011 sUIName = pFilter->GetUIName();
1012 sExtensions = pFilter->GetWildcard().getGlob();
1013 ExportFilter aExportFilter( sUIName, sExtensions );
1014 String aExt = sExtensions;
1016 if ( nHTMLIndex == -1 &&
1017 ( sTypeName.equals( sWriterHTMLType ) || sTypeName.equals( sGraphicHTMLType ) ) )
1019 aImportantFilterGroup.insert( aImportantFilterGroup.begin(), aExportFilter );
1020 nHTMLIndex = 0;
1022 else if ( nXHTMLIndex == -1 && sTypeName.equals( sXHTMLType ) )
1024 std::vector< ExportFilter >::iterator aIter = aImportantFilterGroup.begin();
1025 if ( nHTMLIndex == -1 )
1026 aImportantFilterGroup.insert( aIter, aExportFilter );
1027 else
1028 aImportantFilterGroup.insert( ++aIter, aExportFilter );
1029 nXHTMLIndex = 0;
1031 else if ( nPDFIndex == -1 && sTypeName.equals( sPDFType ) )
1033 std::vector< ExportFilter >::iterator aIter = aImportantFilterGroup.begin();
1034 if ( nHTMLIndex != -1 )
1035 ++aIter;
1036 if ( nXHTMLIndex != -1 )
1037 ++aIter;
1038 aImportantFilterGroup.insert( aIter, aExportFilter );
1039 nPDFIndex = 0;
1041 else if ( nFlashIndex == -1 && sTypeName.equals( sFlashType ) )
1043 std::vector< ExportFilter >::iterator aIter = aImportantFilterGroup.begin();
1044 if ( nHTMLIndex != -1 )
1045 ++aIter;
1046 if ( nXHTMLIndex != -1 )
1047 ++aIter;
1048 if ( nPDFIndex != -1 )
1049 ++aIter;
1050 aImportantFilterGroup.insert( aIter, aExportFilter );
1051 nFlashIndex = 0;
1053 else
1054 aFilterGroup.push_back( aExportFilter );
1057 if ( xFilterGroupManager.is() )
1059 // Add both html/pdf filter as a filter group to get a separator between both groups
1060 if ( !aImportantFilterGroup.empty() )
1062 Sequence< StringPair > aFilters( aImportantFilterGroup.size() );
1063 for ( sal_Int32 i = 0; i < (sal_Int32)aImportantFilterGroup.size(); i++ )
1065 aFilters[i].First = addExtension( aImportantFilterGroup[i].aUIName,
1066 aImportantFilterGroup[i].aWildcard,
1067 sal_False, _rFileDlgImpl );
1068 aFilters[i].Second = aImportantFilterGroup[i].aWildcard;
1073 xFilterGroupManager->appendFilterGroup( ::rtl::OUString(), aFilters );
1075 catch( const IllegalArgumentException& )
1080 if ( !aFilterGroup.empty() )
1082 Sequence< StringPair > aFilters( aFilterGroup.size() );
1083 for ( sal_Int32 i = 0; i < (sal_Int32)aFilterGroup.size(); i++ )
1085 aFilters[i].First = addExtension( aFilterGroup[i].aUIName,
1086 aFilterGroup[i].aWildcard,
1087 sal_False, _rFileDlgImpl );
1088 aFilters[i].Second = aFilterGroup[i].aWildcard;
1093 xFilterGroupManager->appendFilterGroup( ::rtl::OUString(), aFilters );
1095 catch( const IllegalArgumentException& )
1100 else
1102 // Fallback solution just add both filter groups as single filters
1103 sal_Int32 n;
1105 for ( n = 0; n < (sal_Int32)aImportantFilterGroup.size(); n++ )
1109 rtl::OUString aUIName = addExtension( aImportantFilterGroup[n].aUIName,
1110 aImportantFilterGroup[n].aWildcard,
1111 sal_False, _rFileDlgImpl );
1112 _rxFilterManager->appendFilter( aUIName, aImportantFilterGroup[n].aWildcard );
1113 if ( _rFirstNonEmpty.isEmpty() )
1114 _rFirstNonEmpty = sUIName;
1117 catch( const IllegalArgumentException& )
1119 SAL_WARN( "sfx2.dialog", "Could not append Filter" << sUIName );
1123 for ( n = 0; n < (sal_Int32)aFilterGroup.size(); n++ )
1127 rtl::OUString aUIName = addExtension( aFilterGroup[n].aUIName,
1128 aFilterGroup[n].aWildcard,
1129 sal_False, _rFileDlgImpl );
1130 _rxFilterManager->appendFilter( aUIName, aFilterGroup[n].aWildcard );
1131 if ( _rFirstNonEmpty.isEmpty() )
1132 _rFirstNonEmpty = sUIName;
1135 catch( const IllegalArgumentException& )
1137 SAL_WARN( "sfx2.dialog", "Could not append Filter" << sUIName );
1143 //--------------------------------------------------------------------
1144 void appendFiltersForOpen( TSortedFilterList& _rFilterMatcher,
1145 const Reference< XFilterManager >& _rxFilterManager,
1146 ::rtl::OUString& _rFirstNonEmpty, FileDialogHelper_Impl& _rFileDlgImpl )
1148 DBG_ASSERT( _rxFilterManager.is(), "sfx2::appendFiltersForOpen: invalid manager!" );
1149 if ( !_rxFilterManager.is() )
1150 return;
1152 // ===============================================================
1153 // group and classify the filters
1154 GroupedFilterList aAllFilters;
1155 lcl_GroupAndClassify( _rFilterMatcher, aAllFilters );
1157 // ===============================================================
1158 // ensure that we have the one "all files" entry
1159 lcl_EnsureAllFilesEntry( _rFilterMatcher, aAllFilters );
1161 // ===============================================================
1162 // the first non-empty string - which we assume is the first overall entry
1163 if ( !aAllFilters.empty() )
1165 const FilterGroup& rFirstGroup = *aAllFilters.begin(); // should be the global classes
1166 if ( !rFirstGroup.empty() )
1167 _rFirstNonEmpty = rFirstGroup.begin()->First;
1168 // append first group, without extension
1169 AppendFilterGroup aGroup( _rxFilterManager, &_rFileDlgImpl );
1170 aGroup.appendGroup( rFirstGroup, false );
1173 // ===============================================================
1174 // append the filters to the manager
1175 if ( !aAllFilters.empty() )
1177 ::std::list< FilterGroup >::iterator pIter = aAllFilters.begin();
1178 ++pIter;
1179 ::std::for_each(
1180 pIter, // first filter group was handled seperately, see above
1181 aAllFilters.end(),
1182 AppendFilterGroup( _rxFilterManager, &_rFileDlgImpl ) );
1186 ::rtl::OUString addExtension( const ::rtl::OUString& _rDisplayText,
1187 const ::rtl::OUString& _rExtension,
1188 sal_Bool _bForOpen, FileDialogHelper_Impl& _rFileDlgImpl )
1190 static ::rtl::OUString sAllFilter( "(*.*)" );
1191 static ::rtl::OUString sOpenBracket( " (" );
1192 static ::rtl::OUString sCloseBracket( ")" );
1193 ::rtl::OUString sRet = _rDisplayText;
1195 if ( sRet.indexOf( sAllFilter ) == -1 )
1197 String sExt = _rExtension;
1198 if ( !_bForOpen )
1200 // show '*' in extensions only when opening a document
1201 sExt = comphelper::string::remove(sExt, '*');
1203 sRet += sOpenBracket;
1204 sRet += sExt;
1205 sRet += sCloseBracket;
1207 _rFileDlgImpl.addFilterPair( _rDisplayText, sRet );
1208 return sRet;
1211 //........................................................................
1212 } // namespace sfx2
1213 //........................................................................
1216 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */