xref: /AOO41X/main/sfx2/source/dialog/filtergrouping.cxx (revision d119d52d53d0b2180f2ae51341d882123be2af2b)
1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 
23 
24 // MARKER(update_precomp.py): autogen include statement, do not remove
25 #include "precompiled_sfx2.hxx"
26 #include "filtergrouping.hxx"
27 #include <sfx2/fcontnr.hxx>
28 #include <sfx2/filedlghelper.hxx>
29 #include <sfx2/sfx.hrc>
30 #include <sfx2/docfac.hxx>
31 #include "sfx2/sfxresid.hxx"
32 #include <osl/thread.h>
33 #include <com/sun/star/ui/dialogs/XFilterGroupManager.hpp>
34 #include <com/sun/star/beans/StringPair.hpp>
35 #include <com/sun/star/uno/Sequence.hxx>
36 #include <unotools/confignode.hxx>
37 #include <comphelper/processfactory.hxx>
38 #include <comphelper/sequenceashashmap.hxx>
39 #include <tools/wldcrd.hxx>
40 #include <tools/diagnose_ex.h>
41 
42 #include <list>
43 #include <vector>
44 #include <map>
45 #include <algorithm>
46 
47 //........................................................................
48 namespace sfx2
49 {
50 //........................................................................
51 
52 //#define DISABLE_GROUPING_AND_CLASSIFYING
53     // not using the functionallity herein, yet
54 
55     using namespace ::com::sun::star::uno;
56     using namespace ::com::sun::star::ui::dialogs;
57     using namespace ::com::sun::star::lang;
58     using namespace ::com::sun::star::beans;
59     using namespace ::utl;
60 
61     //====================================================================
62     /**
63 
64     Some general words about what's going on here ....
65 
66     <p>In our file open dialog, usually we display every filter we know. That's how it was before: every filter
67     lead to an own line in the filter list box, e.g. "StarWriter 5.0 Dokument" or "Microsoft Word 97".</p>
68 
69     <p>But then the PM came. And everything changed ....</p>
70 
71     <p>A basic idea are groups: Why simply listing all the single filters? Couldn't we draw nice separators
72     between the filters which logically belong together? I.e. all the filters which open a document in StarWriter:
73     couldn't we separate them from all the filters which open the document in StarCalc?<br/>
74     So spoke the PM, and engineering obeyed.</p>
75 
76     <p>So we have groups. They're just a visual aspect: All the filters of a group are presented together, separated
77     by a line from other groups.</p>
78 
79     <p>Let's be honest: How the concrete implementation of the file picker service separates the different groups
80     is a matter of this implementation. We only do this grouping and suggest it to the FilePicker service ...</p>
81 
82     <p>Now for the second concept:<br/>
83     Thinking about it (and that's what the PM did), both "StarWriter 5.0 Dokument" and "Microsoft Word 97"
84     describe a text document. It's a text. It's of no interest for the user that one of the texts was saved in
85     MS' format, and one in our own format.<br/>
86     So in a first step, we want to have a filter entry "Text documents". This would cover both above-mentioned
87     filters, as well as any other filters for documents which are texts.</p>
88 
89     <p>Such an entry as "Text documents" is - within the scope of this file - called "class" or "filter class".</p>
90 
91     <p>In the file-open-dialog, such a class looks like an ordinary filter: it's simply a name in the filter
92     listbox. Selecting means that all the files matching one of the "sub-filters" are displayed (in the example above,
93     this would be "*.sdw", "*.doc" and so on).</p>
94 
95     <p>Now there are two types of filter classes: global ones and local ones. "Text documents" is a global class. As
96     well as "Spreadsheets". Or "Web pages".<br/>
97     Let's have a look at a local class: The filters "MS Word 95" and "MS WinWord 6.0" together form the class
98     "Microsoft Word 6.0 / 95" (don't ask for the reasons. At least not me. Ask the PM). There are a lot of such
99     local classes ...</p>
100 
101     <p>The difference between global and local classes is as follows: Global classes are presented in an own group.
102     There is one dedicated group at the top of the list, containing all the global groups - no local groups and no
103     single filters.</p>
104 
105     <p>Ehm - it was a lie. Not really at the top. Before this group, there is this single "All files" entry. It forms
106     it's own group. But this is uninteresting here.</p>
107 
108     <p>Local classes must consist of filters which - without the classification - would all belong to the same group.
109     Then, they're combined to one entry (in the example above: "Microsoft Word 6.0 / 95"), and this entry is inserted
110     into the file picker filter list, instead of the single filters which form the class.</p>
111 
112     <p>This is an interesting difference between local and global classes: Filters which are part of a global class
113     are listed in there own group, too. Filters in local classes aren't listed a second time - neither directly (as
114     the filter itself) nor indirectly (as part of another local group).</p>
115 
116     <p>The only exception are filters which are part of a global class <em>and</em> a local class. This is allowed.
117     Beeing cotained in two local classes isn't.</p>
118 
119     <p>So that's all what you need to know: Understand the concept of "filter classes" (a filter class combines
120     different filters and acts as if it's a filter itself) and the concept of groups (a group just describes a
121     logical correlation of filters and usually is represented to the user by drawing group separators in the filter
122     list).</p>
123 
124     <p>If you got it, go try understanding this file :).</p>
125 
126     */
127 
128 
129     //====================================================================
130 
131     typedef StringPair                          FilterDescriptor;   // a single filter or a filter class (display name and filter mask)
132     typedef ::std::list< FilterDescriptor >     FilterGroup;        // a list of single filter entries
133     typedef ::std::list< FilterGroup >          GroupedFilterList;  // a list of all filters, already grouped
134 
135     /// the logical name of a filter
136     typedef ::rtl::OUString                     FilterName;
137 
138     // a struct which holds references from a logical filter name to a filter group entry
139     // used for quick lookup of classes (means class entries - entries representing a class)
140     // which a given filter may belong to
141     typedef ::std::map< ::rtl::OUString, FilterGroup::iterator >    FilterGroupEntryReferrer;
142 
143     /// a descriptor for a filter class (which in the final dialog is represented by one filter entry)
144     typedef struct _tagFilterClass
145     {
146         ::rtl::OUString             sDisplayName;       // the display name
147         Sequence< FilterName >      aSubFilters;        // the (logical) names of the filter which belong to the class
148     } FilterClass;
149 
150     typedef ::std::list< FilterClass >                                  FilterClassList;
151     typedef ::std::map< ::rtl::OUString, FilterClassList::iterator >    FilterClassReferrer;
152 
153     typedef ::std::vector< ::rtl::OUString >                            StringArray;
154 
155 // =======================================================================
156 // = reading of configuration data
157 // =======================================================================
158 
159     //--------------------------------------------------------------------
lcl_ReadFilterClass(const OConfigurationNode & _rClassesNode,const::rtl::OUString & _rLogicalClassName,FilterClass & _rClass)160     void lcl_ReadFilterClass( const OConfigurationNode& _rClassesNode, const ::rtl::OUString& _rLogicalClassName,
161         FilterClass& /* [out] */ _rClass )
162     {
163         static const ::rtl::OUString sDisplaNameNodeName( RTL_CONSTASCII_USTRINGPARAM( "DisplayName" ) );
164         static const ::rtl::OUString sSubFiltersNodeName( RTL_CONSTASCII_USTRINGPARAM( "Filters" ) );
165 
166             // the description node for the current class
167         OConfigurationNode aClassDesc = _rClassesNode.openNode( _rLogicalClassName );
168 
169         // the values
170         aClassDesc.getNodeValue( sDisplaNameNodeName ) >>= _rClass.sDisplayName;
171         aClassDesc.getNodeValue( sSubFiltersNodeName ) >>= _rClass.aSubFilters;
172     }
173 
174     //--------------------------------------------------------------------
175     struct CreateEmptyClassRememberPos : public ::std::unary_function< FilterName, void >
176     {
177     protected:
178         FilterClassList&        m_rClassList;
179         FilterClassReferrer&    m_rClassesReferrer;
180 
181     public:
CreateEmptyClassRememberPossfx2::CreateEmptyClassRememberPos182         CreateEmptyClassRememberPos( FilterClassList& _rClassList, FilterClassReferrer& _rClassesReferrer )
183             :m_rClassList       ( _rClassList )
184             ,m_rClassesReferrer ( _rClassesReferrer )
185         {
186         }
187 
188         // operate on a single class name
operator ()sfx2::CreateEmptyClassRememberPos189         void operator() ( const FilterName& _rLogicalFilterName )
190         {
191             // insert a new (empty) class
192             m_rClassList.push_back( FilterClass() );
193             // get the position of this new entry
194             FilterClassList::iterator aInsertPos = m_rClassList.end();
195             --aInsertPos;
196             // remember this position
197             m_rClassesReferrer.insert( FilterClassReferrer::value_type( _rLogicalFilterName, aInsertPos ) );
198         }
199     };
200 
201     //--------------------------------------------------------------------
202     struct ReadGlobalFilter : public ::std::unary_function< FilterName, void >
203     {
204     protected:
205         OConfigurationNode      m_aClassesNode;
206         FilterClassReferrer&    m_aClassReferrer;
207 
208     public:
ReadGlobalFiltersfx2::ReadGlobalFilter209         ReadGlobalFilter( const OConfigurationNode& _rClassesNode, FilterClassReferrer& _rClassesReferrer )
210             :m_aClassesNode     ( _rClassesNode )
211             ,m_aClassReferrer   ( _rClassesReferrer )
212         {
213         }
214 
215         // operate on a single logical name
operator ()sfx2::ReadGlobalFilter216         void operator() ( const FilterName& _rName )
217         {
218             FilterClassReferrer::iterator aClassRef = m_aClassReferrer.find( _rName );
219             if ( m_aClassReferrer.end() == aClassRef )
220             {
221                 // we do not know this global class
222                 DBG_ERROR( "ReadGlobalFilter::operator(): unknown filter name!" );
223                 // TODO: perhaps we should be more tolerant - at the moment, the filter is dropped
224                 // We could silently push_back it to the container ....
225             }
226             else
227             {
228                 // read the data of this class into the node referred to by aClassRef
229                 lcl_ReadFilterClass( m_aClassesNode, _rName, *aClassRef->second );
230             }
231         }
232     };
233 
234     //--------------------------------------------------------------------
lcl_ReadGlobalFilters(const OConfigurationNode & _rFilterClassification,FilterClassList & _rGlobalClasses,StringArray & _rGlobalClassNames)235     void lcl_ReadGlobalFilters( const OConfigurationNode& _rFilterClassification, FilterClassList& _rGlobalClasses, StringArray& _rGlobalClassNames )
236     {
237         _rGlobalClasses.clear();
238         _rGlobalClassNames.clear();
239 
240         //================================================================
241         // get the list describing the order of all global classes
242         Sequence< ::rtl::OUString > aGlobalClasses;
243         _rFilterClassification.getNodeValue( DEFINE_CONST_OUSTRING( "GlobalFilters/Order" ) ) >>= aGlobalClasses;
244 
245         const ::rtl::OUString* pNames = aGlobalClasses.getConstArray();
246         const ::rtl::OUString* pNamesEnd = pNames + aGlobalClasses.getLength();
247 
248         // copy the logical names
249         _rGlobalClassNames.resize( aGlobalClasses.getLength() );
250         ::std::copy( pNames, pNamesEnd, _rGlobalClassNames.begin() );
251 
252         // Global classes are presented in an own group, so their order matters (while the order of the
253         // "local classes" doesn't).
254         // That's why we can't simply add the global classes to _rGlobalClasses using the order in which they
255         // are returned from the configuration - it is completely undefined, and we need a _defined_ order.
256         FilterClassReferrer aClassReferrer;
257         ::std::for_each(
258             pNames,
259             pNamesEnd,
260             CreateEmptyClassRememberPos( _rGlobalClasses, aClassReferrer )
261         );
262             // now _rGlobalClasses contains a dummy entry for each global class,
263             // while aClassReferrer maps from the logical name of the class to the position within _rGlobalClasses where
264             // it's dummy entry resides
265 
266         //================================================================
267         // go for all the single class entries
268         OConfigurationNode aFilterClassesNode =
269             _rFilterClassification.openNode( DEFINE_CONST_OUSTRING( "GlobalFilters/Classes" ) );
270         Sequence< ::rtl::OUString > aFilterClasses = aFilterClassesNode.getNodeNames();
271         ::std::for_each(
272             aFilterClasses.getConstArray(),
273             aFilterClasses.getConstArray() + aFilterClasses.getLength(),
274             ReadGlobalFilter( aFilterClassesNode, aClassReferrer )
275         );
276     }
277 
278     //--------------------------------------------------------------------
279     struct ReadLocalFilter : public ::std::unary_function< FilterName, void >
280     {
281     protected:
282         OConfigurationNode      m_aClassesNode;
283         FilterClassList&        m_rClasses;
284 
285     public:
ReadLocalFiltersfx2::ReadLocalFilter286         ReadLocalFilter( const OConfigurationNode& _rClassesNode, FilterClassList& _rClasses )
287             :m_aClassesNode ( _rClassesNode )
288             ,m_rClasses     ( _rClasses )
289         {
290         }
291 
292         // operate on a single logical name
operator ()sfx2::ReadLocalFilter293         void operator() ( const FilterName& _rName )
294         {
295             // read the data for this class
296             FilterClass aClass;
297             lcl_ReadFilterClass( m_aClassesNode, _rName, aClass );
298 
299             // insert the class descriptor
300             m_rClasses.push_back( aClass );
301         }
302     };
303 
304     //--------------------------------------------------------------------
lcl_ReadLocalFilters(const OConfigurationNode & _rFilterClassification,FilterClassList & _rLocalClasses)305     void lcl_ReadLocalFilters( const OConfigurationNode& _rFilterClassification, FilterClassList& _rLocalClasses )
306     {
307         _rLocalClasses.clear();
308 
309         // the node for the local classes
310         OConfigurationNode aFilterClassesNode =
311             _rFilterClassification.openNode( DEFINE_CONST_OUSTRING( "LocalFilters/Classes" ) );
312         Sequence< ::rtl::OUString > aFilterClasses = aFilterClassesNode.getNodeNames();
313 
314         ::std::for_each(
315             aFilterClasses.getConstArray(),
316             aFilterClasses.getConstArray() + aFilterClasses.getLength(),
317             ReadLocalFilter( aFilterClassesNode, _rLocalClasses )
318         );
319     }
320 
321     //--------------------------------------------------------------------
lcl_ReadClassification(FilterClassList & _rGlobalClasses,StringArray & _rGlobalClassNames,FilterClassList & _rLocalClasses)322     void lcl_ReadClassification( FilterClassList& _rGlobalClasses, StringArray& _rGlobalClassNames, FilterClassList& _rLocalClasses )
323     {
324         //================================================================
325         // open our config node
326         OConfigurationTreeRoot aFilterClassification = OConfigurationTreeRoot::createWithServiceFactory(
327             ::comphelper::getProcessServiceFactory(),
328             DEFINE_CONST_OUSTRING( "org.openoffice.Office.UI/FilterClassification" ),
329             -1,
330             OConfigurationTreeRoot::CM_READONLY
331         );
332 
333         //================================================================
334         // go for the global classes
335         lcl_ReadGlobalFilters( aFilterClassification, _rGlobalClasses, _rGlobalClassNames );
336 
337         //================================================================
338         // fo for the local classes
339         lcl_ReadLocalFilters( aFilterClassification, _rLocalClasses );
340 
341     }
342 
343 // =======================================================================
344 // = grouping and classifying
345 // =======================================================================
346 
347     //--------------------------------------------------------------------
348     // a struct which adds helps remembering a reference to a class entry
349     struct ReferToFilterEntry : public ::std::unary_function< FilterName, void >
350     {
351     protected:
352         FilterGroupEntryReferrer&   m_rEntryReferrer;
353         FilterGroup::iterator       m_aClassPos;
354 
355     public:
ReferToFilterEntrysfx2::ReferToFilterEntry356         ReferToFilterEntry( FilterGroupEntryReferrer& _rEntryReferrer, const FilterGroup::iterator& _rClassPos )
357             :m_rEntryReferrer( _rEntryReferrer )
358             ,m_aClassPos( _rClassPos )
359         {
360         }
361 
362         // operate on a single filter name
operator ()sfx2::ReferToFilterEntry363         void operator() ( const FilterName& _rName )
364         {
365 #ifdef DBG_UTIL
366             ::std::pair< FilterGroupEntryReferrer::iterator, bool > aInsertRes =
367 #endif
368             m_rEntryReferrer.insert( FilterGroupEntryReferrer::value_type( _rName, m_aClassPos ) );
369             DBG_ASSERT( aInsertRes.second, "ReferToFilterEntry::operator(): already have an element for this name!" );
370         }
371     };
372 
373     //--------------------------------------------------------------------
374     struct FillClassGroup : public ::std::unary_function< FilterClass, void >
375     {
376     protected:
377         FilterGroup&                m_rClassGroup;
378         FilterGroupEntryReferrer&   m_rClassReferrer;
379 
380     public:
FillClassGroupsfx2::FillClassGroup381         FillClassGroup( FilterGroup& _rClassGroup, FilterGroupEntryReferrer& _rClassReferrer )
382             :m_rClassGroup      ( _rClassGroup )
383             ,m_rClassReferrer   ( _rClassReferrer )
384         {
385         }
386 
387         // operate on a single class
operator ()sfx2::FillClassGroup388         void operator() ( const FilterClass& _rClass )
389         {
390             // create an empty filter descriptor for the class
391             FilterDescriptor aClassEntry;
392             // set it's name (which is all we know by now)
393             aClassEntry.First = _rClass.sDisplayName;
394 
395             // add it to the group
396             m_rClassGroup.push_back( aClassEntry );
397             // the position of the newly added class
398             FilterGroup::iterator aClassEntryPos = m_rClassGroup.end();
399             --aClassEntryPos;
400 
401             // and for all the sub filters of the class, remember the class
402             // (respectively the position of the class it the group)
403             ::std::for_each(
404                 _rClass.aSubFilters.getConstArray(),
405                 _rClass.aSubFilters.getConstArray() + _rClass.aSubFilters.getLength(),
406                 ReferToFilterEntry( m_rClassReferrer, aClassEntryPos )
407             );
408         }
409     };
410 
411     //--------------------------------------------------------------------
412     static const sal_Unicode s_cWildcardSeparator( ';' );
413 
414     //====================================================================
getSeparatorString()415     const ::rtl::OUString& getSeparatorString()
416     {
417         static ::rtl::OUString s_sSeparatorString( &s_cWildcardSeparator, 1 );
418         return s_sSeparatorString;
419     }
420 
421     //====================================================================
422     struct CheckAppendSingleWildcard : public ::std::unary_function< ::rtl::OUString, void >
423     {
424         ::rtl::OUString& _rToBeExtended;
425 
CheckAppendSingleWildcardsfx2::CheckAppendSingleWildcard426         CheckAppendSingleWildcard( ::rtl::OUString& _rBase ) : _rToBeExtended( _rBase ) { }
427 
operator ()sfx2::CheckAppendSingleWildcard428         void operator() ( const ::rtl::OUString& _rWC )
429         {
430             // check for double wildcards
431             sal_Int32 nExistentPos = _rToBeExtended.indexOf( _rWC );
432             if  ( -1 < nExistentPos )
433             {   // found this wildcard (already part of _rToBeExtended)
434                 const sal_Unicode* pBuffer = _rToBeExtended.getStr();
435                 if  (   ( 0 == nExistentPos )
436                     ||  ( s_cWildcardSeparator == pBuffer[ nExistentPos - 1 ] )
437                     )
438                 {   // the wildcard really starts at this position (it starts at pos 0 or the previous character is a separator
439                     sal_Int32 nExistentWCEnd = nExistentPos + _rWC.getLength();
440                     if  (   ( _rToBeExtended.getLength() == nExistentWCEnd )
441                         ||  ( s_cWildcardSeparator == pBuffer[ nExistentWCEnd ] )
442                         )
443                     {   // it's really the complete wildcard we found
444                         // (not something like _rWC beeing "*.t" and _rToBeExtended containing "*.txt")
445                         // -> outta here
446                         return;
447                     }
448                 }
449             }
450 
451             if ( _rToBeExtended.getLength() )
452                 _rToBeExtended += getSeparatorString();
453             _rToBeExtended += _rWC;
454         }
455     };
456 
457     //====================================================================
458     // a helper struct which adds a fixed (Sfx-)filter to a filter group entry given by iterator
459     struct AppendWildcardToDescriptor : public ::std::unary_function< FilterGroupEntryReferrer::value_type, void >
460     {
461     protected:
462         ::std::vector< ::rtl::OUString > aWildCards;
463 
464     public:
465         AppendWildcardToDescriptor( const String& _rWildCard );
466 
467         // operate on a single class entry
operator ()sfx2::AppendWildcardToDescriptor468         void operator() ( const FilterGroupEntryReferrer::value_type& _rClassReference )
469         {
470             // simply add our wildcards
471             ::std::for_each(
472                 aWildCards.begin(),
473                 aWildCards.end(),
474                 CheckAppendSingleWildcard( _rClassReference.second->Second )
475             );
476         }
477     };
478 
479     //====================================================================
AppendWildcardToDescriptor(const String & _rWildCard)480     AppendWildcardToDescriptor::AppendWildcardToDescriptor( const String& _rWildCard )
481     {
482         DBG_ASSERT( _rWildCard.Len(),
483             "AppendWildcardToDescriptor::AppendWildcardToDescriptor: invalid wildcard!" );
484         DBG_ASSERT( _rWildCard.GetBuffer()[0] != s_cWildcardSeparator,
485             "AppendWildcardToDescriptor::AppendWildcardToDescriptor: wildcard already separated!" );
486 
487         aWildCards.reserve( _rWildCard.GetTokenCount( s_cWildcardSeparator ) );
488 
489         const sal_Unicode* pTokenLoop = _rWildCard.GetBuffer();
490         const sal_Unicode* pTokenLoopEnd = pTokenLoop + _rWildCard.Len();
491         const sal_Unicode* pTokenStart = pTokenLoop;
492         for ( ; pTokenLoop != pTokenLoopEnd; ++pTokenLoop )
493         {
494             if ( ( s_cWildcardSeparator == *pTokenLoop ) && ( pTokenLoop > pTokenStart ) )
495             {   // found a new token separator (and a non-empty token)
496                 aWildCards.push_back( ::rtl::OUString( pTokenStart, pTokenLoop - pTokenStart ) );
497 
498                 // search the start of the next token
499                 while ( ( pTokenStart != pTokenLoopEnd ) && ( *pTokenStart != s_cWildcardSeparator ) )
500                     ++pTokenStart;
501 
502                 if ( pTokenStart == pTokenLoopEnd )
503                     // reached the end
504                     break;
505 
506                 ++pTokenStart;
507                 pTokenLoop = pTokenStart;
508             }
509         }
510         if ( pTokenLoop > pTokenStart )
511             // the last one ....
512             aWildCards.push_back( ::rtl::OUString( pTokenStart, pTokenLoop - pTokenStart ) );
513     }
514 
515     //--------------------------------------------------------------------
lcl_InitGlobalClasses(GroupedFilterList & _rAllFilters,const FilterClassList & _rGlobalClasses,FilterGroupEntryReferrer & _rGlobalClassesRef)516     void lcl_InitGlobalClasses( GroupedFilterList& _rAllFilters, const FilterClassList& _rGlobalClasses, FilterGroupEntryReferrer& _rGlobalClassesRef )
517     {
518         // we need an extra group in our "all filters" container
519         _rAllFilters.push_front( FilterGroup() );
520         FilterGroup& rGlobalFilters = _rAllFilters.front();
521             // it's important to work on the reference: we want to access the members of this filter group
522             // by an iterator (FilterGroup::const_iterator)
523         // the referrer for the global classes
524 
525         // initialize the group
526         ::std::for_each(
527             _rGlobalClasses.begin(),
528             _rGlobalClasses.end(),
529             FillClassGroup( rGlobalFilters, _rGlobalClassesRef )
530         );
531             // now we have:
532             // in rGlobalFilters: a list of FilterDescriptor's, where each's discriptor's display name is set to the name of a class
533             // in aGlobalClassesRef: a mapping from logical filter names to positions within rGlobalFilters
534             //  this way, if we encounter an arbitrary filter, we can easily (and efficient) check if it belongs to a global class
535             //  and modify the descriptor for this class accordingly
536     }
537 
538     //--------------------------------------------------------------------
539     typedef ::std::vector< ::std::pair< FilterGroupEntryReferrer::mapped_type, FilterGroup::iterator > >
540             MapGroupEntry2GroupEntry;
541             // this is not really a map - it's just called this way because it is used as a map
542 
543     struct FindGroupEntry : public ::std::unary_function< MapGroupEntry2GroupEntry::value_type, sal_Bool >
544     {
545         FilterGroupEntryReferrer::mapped_type aLookingFor;
FindGroupEntrysfx2::FindGroupEntry546         FindGroupEntry( FilterGroupEntryReferrer::mapped_type _rLookingFor ) : aLookingFor( _rLookingFor ) { }
547 
operator ()sfx2::FindGroupEntry548         sal_Bool operator() ( const MapGroupEntry2GroupEntry::value_type& _rMapEntry )
549         {
550             return _rMapEntry.first == aLookingFor ? sal_True : sal_False;
551         }
552     };
553 
554     struct CopyGroupEntryContent : public ::std::unary_function< MapGroupEntry2GroupEntry::value_type, void >
555     {
operator ()sfx2::CopyGroupEntryContent556         void operator() ( const MapGroupEntry2GroupEntry::value_type& _rMapEntry )
557         {
558 #ifdef DBG_UTIL
559             FilterDescriptor aHaveALook = *_rMapEntry.first;
560 #endif
561             *_rMapEntry.second = *_rMapEntry.first;
562         }
563     };
564 
565     //--------------------------------------------------------------------
566     struct CopyNonEmptyFilter : public ::std::unary_function< FilterDescriptor, void >
567     {
568         FilterGroup& rTarget;
CopyNonEmptyFiltersfx2::CopyNonEmptyFilter569         CopyNonEmptyFilter( FilterGroup& _rTarget ) :rTarget( _rTarget ) { }
570 
operator ()sfx2::CopyNonEmptyFilter571         void operator() ( const FilterDescriptor& _rFilter )
572         {
573             if ( _rFilter.Second.getLength() )
574                 rTarget.push_back( _rFilter );
575         }
576     };
577 
578     //--------------------------------------------------------------------
lcl_GroupAndClassify(TSortedFilterList & _rFilterMatcher,GroupedFilterList & _rAllFilters)579     void lcl_GroupAndClassify( TSortedFilterList& _rFilterMatcher, GroupedFilterList& _rAllFilters )
580     {
581         _rAllFilters.clear();
582 
583         // ===============================================================
584         // read the classification of filters
585         FilterClassList aGlobalClasses, aLocalClasses;
586         StringArray aGlobalClassNames;
587         lcl_ReadClassification( aGlobalClasses, aGlobalClassNames, aLocalClasses );
588 
589         // ===============================================================
590         // for the global filter classes
591         FilterGroupEntryReferrer aGlobalClassesRef;
592         lcl_InitGlobalClasses( _rAllFilters, aGlobalClasses, aGlobalClassesRef );
593 
594         // insert as much placeholders (FilterGroup's) into _rAllFilter for groups as we have global classes
595         // (this assumes that both numbers are the same, which, speaking strictly, must not hold - but it does, as we know ...)
596         sal_Int32 nGlobalClasses = aGlobalClasses.size();
597         while ( nGlobalClasses-- )
598             _rAllFilters.push_back( FilterGroup() );
599 
600         // ===============================================================
601         // for the local classes:
602         // if n filters belong to a local class, they do not appear in their respective group explicitly, instead
603         // and entry for the class is added to the group and the extensions of the filters are collected under
604         // this entry
605         FilterGroupEntryReferrer aLocalClassesRef;
606         FilterGroup aCollectedLocals;
607         ::std::for_each(
608             aLocalClasses.begin(),
609             aLocalClasses.end(),
610             FillClassGroup( aCollectedLocals, aLocalClassesRef )
611         );
612         // to map from the position within aCollectedLocals to positions within the real groups
613         // (where they finally belong to)
614         MapGroupEntry2GroupEntry    aLocalFinalPositions;
615 
616         // ===============================================================
617         // now add the filters
618         // the group which we currently work with
619         GroupedFilterList::iterator aCurrentGroup = _rAllFilters.end(); // no current group
620         // the filter container of the current group - if this changes between two filters, a new group is reached
621         String aCurrentServiceName;
622 
623         String sFilterWildcard;
624         ::rtl::OUString sFilterName;
625         // loop through all the filters
626         for ( const SfxFilter* pFilter = _rFilterMatcher.First(); pFilter; pFilter = _rFilterMatcher.Next() )
627         {
628             sFilterName = pFilter->GetFilterName();
629             sFilterWildcard = pFilter->GetWildcard().GetWildCard();
630             AppendWildcardToDescriptor aExtendWildcard( sFilterWildcard );
631 
632             DBG_ASSERT( sFilterWildcard.Len(), "sfx2::lcl_GroupAndClassify: invalid wildcard of this filter!" );
633 
634             // ===========================================================
635             // check for a change in the group
636             String aServiceName = pFilter->GetServiceName();
637             if ( aServiceName != aCurrentServiceName )
638             {   // we reached a new group
639 
640                 ::rtl::OUString sDocServName = aServiceName;
641 
642                 // look for the place in _rAllFilters where this ne group belongs - this is determined
643                 // by the order of classes in aGlobalClassNames
644                 GroupedFilterList::iterator aGroupPos = _rAllFilters.begin();
645                 DBG_ASSERT( aGroupPos != _rAllFilters.end(),
646                     "sfx2::lcl_GroupAndClassify: invalid all-filters array here!" );
647                     // the loop below will work on invalid objects else ...
648                 ++aGroupPos;
649                 StringArray::iterator aGlobalIter = aGlobalClassNames.begin();
650                 while   (   ( aGroupPos != _rAllFilters.end() )
651                         &&  ( aGlobalIter != aGlobalClassNames.end() )
652                         &&  ( *aGlobalIter != sDocServName )
653                         )
654                 {
655                     ++aGlobalIter;
656                     ++aGroupPos;
657                 }
658                 if ( aGroupPos != _rAllFilters.end() )
659                     // we found a global class name which matchies the doc service name -> fill the filters of this
660                     // group in the respective prepared group
661                     aCurrentGroup = aGroupPos;
662                 else
663                     // insert a new entry in our overall-list
664                     aCurrentGroup = _rAllFilters.insert( _rAllFilters.end(), FilterGroup() );
665 
666                 // remember the container to properly detect the next group
667                 aCurrentServiceName = aServiceName;
668             }
669 
670             DBG_ASSERT( aCurrentGroup != _rAllFilters.end(), "sfx2::lcl_GroupAndClassify: invalid current group!" );
671 
672             // ===========================================================
673             // check if the filter is part of a global group
674             ::std::pair< FilterGroupEntryReferrer::iterator, FilterGroupEntryReferrer::iterator >
675                 aBelongsTo = aGlobalClassesRef.equal_range( sFilterName );
676             // add the filter to the entries for these classes
677             // (if they exist - if not, the range is empty and the for_each is a no-op)
678             ::std::for_each(
679                 aBelongsTo.first,
680                 aBelongsTo.second,
681                 aExtendWildcard
682             );
683 
684             // ===========================================================
685             // add the filter to it's group
686 
687             // for this, check if the filter is part of a local filter
688             FilterGroupEntryReferrer::iterator aBelongsToLocal = aLocalClassesRef.find( sFilterName );
689             if ( aLocalClassesRef.end() != aBelongsToLocal )
690             {
691 /*
692 #ifdef DBG_UTIL
693                 const ::rtl::OUString& rLocalClassDisplayName = aBelongsToLocal->second->First;
694                 const ::rtl::OUString& rLocalClassExtension = aBelongsToLocal->second->Second;
695 #endif
696 */
697                 // okay, there is a local class which the filter belongs to
698                 // -> append the wildcard
699                 aExtendWildcard( *aBelongsToLocal );
700 
701                 MapGroupEntry2GroupEntry::iterator aThisGroupFinalPos =
702                     ::std::find_if( aLocalFinalPositions.begin(), aLocalFinalPositions.end(), FindGroupEntry( aBelongsToLocal->second ) );
703 
704                 if ( aLocalFinalPositions.end() == aThisGroupFinalPos )
705                 {   // the position within aCollectedLocals has not been mapped to a final position
706                     // within the "real" group (aCollectedLocals is only temporary)
707                     // -> do this now (as we just encountered the first filter belonging to this local class
708                     // add a new entry which is the "real" group entry
709                     aCurrentGroup->push_back( FilterDescriptor( aBelongsToLocal->second->First, String() ) );
710                     // the position where we inserted the entry
711                     FilterGroup::iterator aInsertPos = aCurrentGroup->end();
712                     --aInsertPos;
713                     // remember this pos
714                     aLocalFinalPositions.push_back( MapGroupEntry2GroupEntry::value_type( aBelongsToLocal->second, aInsertPos ) );
715                 }
716             }
717             else
718                 aCurrentGroup->push_back( FilterDescriptor( pFilter->GetUIName(), sFilterWildcard ) );
719         }
720 
721         // now just complete the infos for the local groups:
722         // During the above loop, they have been collected in aCollectedLocals, but this is only temporary
723         // They have to be copied into their final positions (which are stored in aLocalFinalPositions)
724         ::std::for_each(
725             aLocalFinalPositions.begin(),
726             aLocalFinalPositions.end(),
727             CopyGroupEntryContent()
728         );
729 
730         // and remove local groups which do not apply - e.g. have no entries due to the limited content of the
731         // current SfxFilterMatcherIter
732 
733         FilterGroup& rGlobalFilters = _rAllFilters.front();
734         FilterGroup aNonEmptyGlobalFilters;
735         ::std::for_each(
736             rGlobalFilters.begin(),
737             rGlobalFilters.end(),
738             CopyNonEmptyFilter( aNonEmptyGlobalFilters )
739         );
740         rGlobalFilters.swap( aNonEmptyGlobalFilters );
741     }
742 
743     //--------------------------------------------------------------------
744     struct AppendFilter : public ::std::unary_function< FilterDescriptor, void >
745     {
746         protected:
747             Reference< XFilterManager >         m_xFilterManager;
748             FileDialogHelper_Impl*              m_pFileDlgImpl;
749             bool                                m_bAddExtension;
750 
751         public:
AppendFiltersfx2::AppendFilter752             AppendFilter( const Reference< XFilterManager >& _rxFilterManager,
753                           FileDialogHelper_Impl* _pImpl, bool _bAddExtension ) :
754 
755                 m_xFilterManager( _rxFilterManager ),
756                 m_pFileDlgImpl  ( _pImpl ),
757                 m_bAddExtension ( _bAddExtension )
758 
759             {
760                 DBG_ASSERT( m_xFilterManager.is(), "AppendFilter::AppendFilter: invalid filter manager!" );
761                 DBG_ASSERT( m_pFileDlgImpl, "AppendFilter::AppendFilter: invalid filedlg impl!" );
762             }
763 
764             // operate on a single filter
operator ()sfx2::AppendFilter765             void operator() ( const FilterDescriptor& _rFilterEntry )
766             {
767                 String sDisplayText = m_bAddExtension
768                     ? addExtension( _rFilterEntry.First, _rFilterEntry.Second, sal_True, *m_pFileDlgImpl )
769                     : _rFilterEntry.First;
770                 m_xFilterManager->appendFilter( sDisplayText, _rFilterEntry.Second );
771             }
772     };
773 
774 // =======================================================================
775 // = handling for the "all files" entry
776 // =======================================================================
777 
778     //--------------------------------------------------------------------
lcl_hasAllFilesFilter(TSortedFilterList & _rFilterMatcher,String & _rAllFilterName)779     sal_Bool lcl_hasAllFilesFilter( TSortedFilterList& _rFilterMatcher, String& /* [out] */ _rAllFilterName )
780     {
781         ::rtl::OUString sUIName;
782         sal_Bool        bHasAll = sal_False;
783         _rAllFilterName = String( SfxResId( STR_SFX_FILTERNAME_ALL ) );
784 
785         // ===============================================================
786         // check if there's already a filter <ALL>
787         for ( const SfxFilter* pFilter = _rFilterMatcher.First(); pFilter && !bHasAll; pFilter = _rFilterMatcher.Next() )
788         {
789             if ( pFilter->GetUIName() == _rAllFilterName )
790                 bHasAll = sal_True;
791         }
792         return bHasAll;
793     }
794 
795     //--------------------------------------------------------------------
lcl_EnsureAllFilesEntry(TSortedFilterList & _rFilterMatcher,GroupedFilterList & _rFilters)796     void lcl_EnsureAllFilesEntry( TSortedFilterList& _rFilterMatcher, GroupedFilterList& _rFilters )
797     {
798         // ===============================================================
799         String sAllFilterName;
800         if ( !lcl_hasAllFilesFilter( _rFilterMatcher, sAllFilterName ) )
801         {
802             // get the first group of filters (by definition, this group contains the global classes)
803             DBG_ASSERT( !_rFilters.empty(), "lcl_EnsureAllFilesEntry: invalid filter list!" );
804             if ( !_rFilters.empty() )
805             {
806                 FilterGroup& rGlobalClasses = *_rFilters.begin();
807                 rGlobalClasses.push_front( FilterDescriptor( sAllFilterName, DEFINE_CONST_UNICODE( FILEDIALOG_FILTER_ALL ) ) );
808             }
809         }
810     }
811 
812 #ifdef DISABLE_GROUPING_AND_CLASSIFYING
813     //--------------------------------------------------------------------
lcl_EnsureAllFilesEntry(TSortedFilterList & _rFilterMatcher,const Reference<XFilterManager> & _rxFilterManager,::rtl::OUString & _rFirstNonEmpty)814     void lcl_EnsureAllFilesEntry( TSortedFilterList& _rFilterMatcher, const Reference< XFilterManager >& _rxFilterManager, ::rtl::OUString& _rFirstNonEmpty )
815     {
816         // ===============================================================
817         String sAllFilterName;
818         if ( !lcl_hasAllFilesFilter( _rFilterMatcher, sAllFilterName ) )
819         {
820             try
821             {
822                 _rxFilterManager->appendFilter( sAllFilterName, DEFINE_CONST_UNICODE( FILEDIALOG_FILTER_ALL ) );
823                 _rFirstNonEmpty = sAllFilterName;
824             }
825             catch( const IllegalArgumentException& )
826             {
827 #ifdef DBG_UTIL
828                 ByteString aMsg( "sfx2::lcl_EnsureAllFilesEntry: could not append Filter" );
829                 aMsg += ByteString( String( sAllFilterName ), RTL_TEXTENCODING_UTF8 );
830                 DBG_ERROR( aMsg.GetBuffer() );
831 #endif
832             }
833         }
834 
835     }
836 #endif
837 
838 // =======================================================================
839 // = filling an XFilterManager
840 // =======================================================================
841 
842     //--------------------------------------------------------------------
843     struct AppendFilterGroup : public ::std::unary_function< FilterGroup, void >
844     {
845     protected:
846         Reference< XFilterManager >         m_xFilterManager;
847         Reference< XFilterGroupManager >    m_xFilterGroupManager;
848         FileDialogHelper_Impl*              m_pFileDlgImpl;
849 
850     public:
AppendFilterGroupsfx2::AppendFilterGroup851         AppendFilterGroup( const Reference< XFilterManager >& _rxFilterManager, FileDialogHelper_Impl* _pImpl )
852             :m_xFilterManager       ( _rxFilterManager )
853             ,m_xFilterGroupManager  ( _rxFilterManager, UNO_QUERY )
854             ,m_pFileDlgImpl         ( _pImpl )
855         {
856             DBG_ASSERT( m_xFilterManager.is(), "AppendFilterGroup::AppendFilterGroup: invalid filter manager!" );
857             DBG_ASSERT( m_pFileDlgImpl, "AppendFilterGroup::AppendFilterGroup: invalid filedlg impl!" );
858         }
859 
appendGroupsfx2::AppendFilterGroup860         void appendGroup( const FilterGroup& _rGroup, bool _bAddExtension )
861         {
862             try
863             {
864                 if ( m_xFilterGroupManager.is() )
865                 {   // the file dialog implementation supports visual grouping of filters
866                     // create a representation of the group which is understandable by the XFilterGroupManager
867                     if ( _rGroup.size() )
868                     {
869                         Sequence< StringPair > aFilters( _rGroup.size() );
870                         ::std::copy(
871                             _rGroup.begin(),
872                             _rGroup.end(),
873                             aFilters.getArray()
874                         );
875                         if ( _bAddExtension )
876                         {
877                             StringPair* pFilters = aFilters.getArray();
878                             StringPair* pEnd = pFilters + aFilters.getLength();
879                             for ( ; pFilters != pEnd; ++pFilters )
880                                 pFilters->First = addExtension( pFilters->First, pFilters->Second, sal_True, *m_pFileDlgImpl );
881                         }
882                         m_xFilterGroupManager->appendFilterGroup( ::rtl::OUString(), aFilters );
883                     }
884                 }
885                 else
886                 {
887                     ::std::for_each(
888                         _rGroup.begin(),
889                         _rGroup.end(),
890                         AppendFilter( m_xFilterManager, m_pFileDlgImpl, _bAddExtension ) );
891                 }
892             }
893             catch( const Exception& )
894             {
895                 DBG_UNHANDLED_EXCEPTION();
896             }
897         }
898 
899         // operate on a single filter group
operator ()sfx2::AppendFilterGroup900         void operator() ( const FilterGroup& _rGroup )
901         {
902             appendGroup( _rGroup, true );
903         }
904     };
905 
906     //--------------------------------------------------------------------
TSortedFilterList(const::com::sun::star::uno::Reference<::com::sun::star::container::XEnumeration> & xFilterList)907     TSortedFilterList::TSortedFilterList(const ::com::sun::star::uno::Reference< ::com::sun::star::container::XEnumeration >& xFilterList)
908         : m_nIterator(0)
909     {
910         if (!xFilterList.is())
911             return;
912 
913         m_lFilters.clear();
914         while(xFilterList->hasMoreElements())
915         {
916             ::comphelper::SequenceAsHashMap lFilterProps (xFilterList->nextElement());
917             ::rtl::OUString                 sFilterName  = lFilterProps.getUnpackedValueOrDefault(
918                                                              ::rtl::OUString::createFromAscii("Name"),
919                                                              ::rtl::OUString());
920             if (sFilterName.getLength())
921                 m_lFilters.push_back(sFilterName);
922         }
923     }
924 
925     //--------------------------------------------------------------------
First()926     const SfxFilter* TSortedFilterList::First()
927     {
928         m_nIterator = 0;
929         return impl_getFilter(m_nIterator);
930     }
931 
932     //--------------------------------------------------------------------
Next()933     const SfxFilter* TSortedFilterList::Next()
934     {
935         ++m_nIterator;
936         return impl_getFilter(m_nIterator);
937     }
938 
939     //--------------------------------------------------------------------
impl_getFilter(sal_Int32 nIndex)940     const SfxFilter* TSortedFilterList::impl_getFilter(sal_Int32 nIndex)
941     {
942         if (nIndex<0 || nIndex>=(sal_Int32)m_lFilters.size())
943             return 0;
944         const ::rtl::OUString& sFilterName = m_lFilters[nIndex];
945         if (!sFilterName.getLength())
946             return 0;
947         return SfxFilter::GetFilterByName(String(sFilterName));
948     }
949 
950     //--------------------------------------------------------------------
appendFiltersForSave(TSortedFilterList & _rFilterMatcher,const Reference<XFilterManager> & _rxFilterManager,::rtl::OUString & _rFirstNonEmpty,FileDialogHelper_Impl & _rFileDlgImpl,const::rtl::OUString & _rFactory)951     void appendFiltersForSave( TSortedFilterList& _rFilterMatcher,
952                                const Reference< XFilterManager >& _rxFilterManager,
953                                ::rtl::OUString& _rFirstNonEmpty, FileDialogHelper_Impl& _rFileDlgImpl,
954                                const ::rtl::OUString& _rFactory )
955     {
956         DBG_ASSERT( _rxFilterManager.is(), "sfx2::appendFiltersForSave: invalid manager!" );
957         if ( !_rxFilterManager.is() )
958             return;
959 
960         ::rtl::OUString sUIName;
961         ::rtl::OUString sExtension;
962 
963         // retrieve the default filter for this application module.
964         // It must be set as first of the generated filter list.
965         const SfxFilter* pDefaultFilter = SfxFilterContainer::GetDefaultFilter_Impl(_rFactory);
966         // --> PB 2004-11-01 #i32434# only use one extension
967         // (and always the first if there are more than one)
968         sExtension = pDefaultFilter->GetWildcard().GetWildCard().GetToken( 0, ';' );
969         // <--
970         sUIName = addExtension( pDefaultFilter->GetUIName(), sExtension, sal_False, _rFileDlgImpl );
971         try
972         {
973             _rxFilterManager->appendFilter( sUIName, sExtension );
974             if ( !_rFirstNonEmpty.getLength() )
975                 _rFirstNonEmpty = sUIName;
976         }
977         catch( IllegalArgumentException )
978         {
979 #ifdef DBG_UTIL
980             ByteString aMsg( "Could not append DefaultFilter" );
981             aMsg += ByteString( String( sUIName ), osl_getThreadTextEncoding() );
982             DBG_ERRORFILE( aMsg.GetBuffer() );
983 #endif
984         }
985 
986         for ( const SfxFilter* pFilter = _rFilterMatcher.First(); pFilter; pFilter = _rFilterMatcher.Next() )
987         {
988             if (pFilter->GetName() == pDefaultFilter->GetName())
989                 continue;
990 
991             // --> PB 2004-09-21 #i32434# only use one extension
992             // (and always the first if there are more than one)
993             sExtension = pFilter->GetWildcard().GetWildCard().GetToken( 0, ';' );
994             // <--
995             sUIName = addExtension( pFilter->GetUIName(), sExtension, sal_False, _rFileDlgImpl );
996             try
997             {
998                 _rxFilterManager->appendFilter( sUIName, sExtension );
999                 if ( !_rFirstNonEmpty.getLength() )
1000                     _rFirstNonEmpty = sUIName;
1001             }
1002             catch( IllegalArgumentException )
1003             {
1004     #ifdef DBG_UTIL
1005                 ByteString aMsg( "Could not append Filter" );
1006                 aMsg += ByteString( String( sUIName ), osl_getThreadTextEncoding() );
1007                 DBG_ERRORFILE( aMsg.GetBuffer() );
1008     #endif
1009             }
1010         }
1011     }
1012 
1013     struct ExportFilter
1014     {
ExportFiltersfx2::ExportFilter1015         ExportFilter( const rtl::OUString& _aUIName, const rtl::OUString& _aWildcard ) :
1016             aUIName( _aUIName ), aWildcard( _aWildcard ) {}
1017 
1018         rtl::OUString aUIName;
1019         rtl::OUString aWildcard;
1020     };
1021 
1022     //--------------------------------------------------------------------
appendExportFilters(TSortedFilterList & _rFilterMatcher,const Reference<XFilterManager> & _rxFilterManager,::rtl::OUString & _rFirstNonEmpty,FileDialogHelper_Impl & _rFileDlgImpl)1023     void appendExportFilters( TSortedFilterList& _rFilterMatcher,
1024                               const Reference< XFilterManager >& _rxFilterManager,
1025                               ::rtl::OUString& _rFirstNonEmpty, FileDialogHelper_Impl& _rFileDlgImpl )
1026     {
1027         DBG_ASSERT( _rxFilterManager.is(), "sfx2::appendExportFilters: invalid manager!" );
1028         if ( !_rxFilterManager.is() )
1029             return;
1030 
1031         sal_Int32                           nHTMLIndex  = -1;
1032         sal_Int32                           nXHTMLIndex  = -1;
1033         sal_Int32                           nPDFIndex   = -1;
1034         sal_Int32                           nFlashIndex = -1;
1035         ::rtl::OUString                     sUIName;
1036         ::rtl::OUString                     sExtensions;
1037         std::vector< ExportFilter >         aImportantFilterGroup;
1038         std::vector< ExportFilter >         aFilterGroup;
1039         Reference< XFilterGroupManager >    xFilterGroupManager( _rxFilterManager, UNO_QUERY );
1040         ::rtl::OUString                     sTypeName;
1041         const ::rtl::OUString               sWriterHTMLType( DEFINE_CONST_OUSTRING("writer_web_HTML") );
1042         const ::rtl::OUString               sGraphicHTMLType( DEFINE_CONST_OUSTRING("graphic_HTML") );
1043         const ::rtl::OUString               sXHTMLType( DEFINE_CONST_OUSTRING("XHTML_File") );
1044         const ::rtl::OUString               sPDFType( DEFINE_CONST_OUSTRING("pdf_Portable_Document_Format") );
1045         const ::rtl::OUString               sFlashType( DEFINE_CONST_OUSTRING("graphic_SWF") );
1046 
1047         for ( const SfxFilter* pFilter = _rFilterMatcher.First(); pFilter; pFilter = _rFilterMatcher.Next() )
1048         {
1049             sTypeName   = pFilter->GetTypeName();
1050             sUIName     = pFilter->GetUIName();
1051             sExtensions = pFilter->GetWildcard().GetWildCard();
1052             ExportFilter aExportFilter( sUIName, sExtensions );
1053             String aExt = sExtensions;
1054 
1055             if ( nHTMLIndex == -1 &&
1056                 ( sTypeName.equals( sWriterHTMLType ) || sTypeName.equals( sGraphicHTMLType ) ) )
1057             {
1058                 aImportantFilterGroup.insert( aImportantFilterGroup.begin(), aExportFilter );
1059                 nHTMLIndex = 0;
1060             }
1061             else if ( nXHTMLIndex == -1 && sTypeName.equals( sXHTMLType ) )
1062             {
1063                 std::vector< ExportFilter >::iterator aIter = aImportantFilterGroup.begin();
1064                 if ( nHTMLIndex == -1 )
1065                     aImportantFilterGroup.insert( aIter, aExportFilter );
1066                 else
1067                     aImportantFilterGroup.insert( ++aIter, aExportFilter );
1068                 nXHTMLIndex = 0;
1069             }
1070             else if ( nPDFIndex == -1 && sTypeName.equals( sPDFType ) )
1071             {
1072                 std::vector< ExportFilter >::iterator aIter = aImportantFilterGroup.begin();
1073                 if ( nHTMLIndex != -1 )
1074                     aIter++;
1075                 if ( nXHTMLIndex != -1 )
1076                     aIter++;
1077                 aImportantFilterGroup.insert( aIter, aExportFilter );
1078                 nPDFIndex = 0;
1079             }
1080             else if ( nFlashIndex == -1 && sTypeName.equals( sFlashType ) )
1081             {
1082                 std::vector< ExportFilter >::iterator aIter = aImportantFilterGroup.begin();
1083                 if ( nHTMLIndex != -1 )
1084                     aIter++;
1085                 if ( nXHTMLIndex != -1 )
1086                     aIter++;
1087                 if ( nPDFIndex != -1 )
1088                     aIter++;
1089                 aImportantFilterGroup.insert( aIter, aExportFilter );
1090                 nFlashIndex = 0;
1091             }
1092             else
1093                 aFilterGroup.push_back( aExportFilter );
1094         }
1095 
1096         if ( xFilterGroupManager.is() )
1097         {
1098             // Add both html/pdf filter as a filter group to get a separator between both groups
1099             if ( aImportantFilterGroup.size() > 0 )
1100             {
1101                 Sequence< StringPair > aFilters( aImportantFilterGroup.size() );
1102                 for ( sal_Int32 i = 0; i < (sal_Int32)aImportantFilterGroup.size(); i++ )
1103                 {
1104                     aFilters[i].First   = addExtension( aImportantFilterGroup[i].aUIName,
1105                                                         aImportantFilterGroup[i].aWildcard,
1106                                                         sal_False, _rFileDlgImpl );
1107                     aFilters[i].Second  = aImportantFilterGroup[i].aWildcard;
1108                 }
1109 
1110                 try
1111                 {
1112                     xFilterGroupManager->appendFilterGroup( ::rtl::OUString(), aFilters );
1113                 }
1114                 catch( IllegalArgumentException )
1115                 {
1116                 }
1117             }
1118 
1119             if ( aFilterGroup.size() > 0 )
1120             {
1121                 Sequence< StringPair > aFilters( aFilterGroup.size() );
1122                 for ( sal_Int32 i = 0; i < (sal_Int32)aFilterGroup.size(); i++ )
1123                 {
1124                     aFilters[i].First   = addExtension( aFilterGroup[i].aUIName,
1125                                                         aFilterGroup[i].aWildcard,
1126                                                         sal_False, _rFileDlgImpl );
1127                     aFilters[i].Second  = aFilterGroup[i].aWildcard;
1128                 }
1129 
1130                 try
1131                 {
1132                     xFilterGroupManager->appendFilterGroup( ::rtl::OUString(), aFilters );
1133                 }
1134                 catch( IllegalArgumentException )
1135                 {
1136                 }
1137             }
1138         }
1139         else
1140         {
1141             // Fallback solution just add both filter groups as single filters
1142             sal_Int32 n;
1143 
1144             for ( n = 0; n < (sal_Int32)aImportantFilterGroup.size(); n++ )
1145             {
1146                 try
1147                 {
1148                     rtl::OUString aUIName = addExtension( aImportantFilterGroup[n].aUIName,
1149                                                           aImportantFilterGroup[n].aWildcard,
1150                                                           sal_False, _rFileDlgImpl );
1151                     _rxFilterManager->appendFilter( aUIName, aImportantFilterGroup[n].aWildcard  );
1152                     if ( !_rFirstNonEmpty.getLength() )
1153                         _rFirstNonEmpty = sUIName;
1154 
1155                 }
1156                 catch( IllegalArgumentException )
1157                 {
1158         #ifdef DBG_UTIL
1159                     ByteString aMsg( "Could not append Filter" );
1160                     aMsg += ByteString( String( sUIName ), osl_getThreadTextEncoding() );
1161                     DBG_ERRORFILE( aMsg.GetBuffer() );
1162         #endif
1163                 }
1164             }
1165 
1166             for ( n = 0; n < (sal_Int32)aFilterGroup.size(); n++ )
1167             {
1168                 try
1169                 {
1170                     rtl::OUString aUIName = addExtension( aFilterGroup[n].aUIName,
1171                                                           aFilterGroup[n].aWildcard,
1172                                                           sal_False, _rFileDlgImpl );
1173                     _rxFilterManager->appendFilter( aUIName, aFilterGroup[n].aWildcard );
1174                     if ( !_rFirstNonEmpty.getLength() )
1175                         _rFirstNonEmpty = sUIName;
1176 
1177                 }
1178                 catch( IllegalArgumentException )
1179                 {
1180         #ifdef DBG_UTIL
1181                     ByteString aMsg( "Could not append Filter" );
1182                     aMsg += ByteString( String( sUIName ), osl_getThreadTextEncoding() );
1183                     DBG_ERRORFILE( aMsg.GetBuffer() );
1184         #endif
1185                 }
1186             }
1187         }
1188     }
1189 
1190     //--------------------------------------------------------------------
appendFiltersForOpen(TSortedFilterList & _rFilterMatcher,const Reference<XFilterManager> & _rxFilterManager,::rtl::OUString & _rFirstNonEmpty,FileDialogHelper_Impl & _rFileDlgImpl)1191     void appendFiltersForOpen( TSortedFilterList& _rFilterMatcher,
1192                                const Reference< XFilterManager >& _rxFilterManager,
1193                                ::rtl::OUString& _rFirstNonEmpty, FileDialogHelper_Impl& _rFileDlgImpl )
1194     {
1195         DBG_ASSERT( _rxFilterManager.is(), "sfx2::appendFiltersForOpen: invalid manager!" );
1196         if ( !_rxFilterManager.is() )
1197             return;
1198 
1199 #ifdef DISABLE_GROUPING_AND_CLASSIFYING
1200         // ===============================================================
1201         // ensure that there's an entry "all" (with wildcard *.*)
1202         lcl_EnsureAllFilesEntry( _rFilterMatcher, _rxFilterManager, _rFirstNonEmpty );
1203 
1204         // ===============================================================
1205         appendFilters( _rFilterMatcher, _rxFilterManager, _rFirstNonEmpty );
1206 #else
1207 
1208         // ===============================================================
1209         // group and classify the filters
1210         GroupedFilterList aAllFilters;
1211         lcl_GroupAndClassify( _rFilterMatcher, aAllFilters );
1212 
1213         // ===============================================================
1214         // ensure that we have the one "all files" entry
1215         lcl_EnsureAllFilesEntry( _rFilterMatcher, aAllFilters );
1216 
1217         // ===============================================================
1218         // the first non-empty string - which we assume is the first overall entry
1219         if ( !aAllFilters.empty() )
1220         {
1221             const FilterGroup& rFirstGroup = *aAllFilters.begin();  // should be the global classes
1222             if ( !rFirstGroup.empty() )
1223                 _rFirstNonEmpty = rFirstGroup.begin()->First;
1224             // append first group, without extension
1225             AppendFilterGroup aGroup( _rxFilterManager, &_rFileDlgImpl );
1226             aGroup.appendGroup( rFirstGroup, false );
1227         }
1228 
1229         // ===============================================================
1230         // append the filters to the manager
1231         if ( !aAllFilters.empty() )
1232         {
1233             ::std::list< FilterGroup >::iterator pIter = aAllFilters.begin();
1234             ++pIter;
1235             ::std::for_each(
1236                 pIter, // first filter group was handled seperately, see above
1237                 aAllFilters.end(),
1238                 AppendFilterGroup( _rxFilterManager, &_rFileDlgImpl ) );
1239         }
1240 #endif
1241     }
1242 
addExtension(const::rtl::OUString & _rDisplayText,const::rtl::OUString & _rExtension,sal_Bool _bForOpen,FileDialogHelper_Impl & _rFileDlgImpl)1243     ::rtl::OUString addExtension( const ::rtl::OUString& _rDisplayText,
1244                                   const ::rtl::OUString& _rExtension,
1245                                   sal_Bool _bForOpen, FileDialogHelper_Impl& _rFileDlgImpl )
1246     {
1247         static ::rtl::OUString sAllFilter( RTL_CONSTASCII_USTRINGPARAM( "(*.*)" ) );
1248         static ::rtl::OUString sOpenBracket( RTL_CONSTASCII_USTRINGPARAM( " (" ) );
1249         static ::rtl::OUString sCloseBracket( RTL_CONSTASCII_USTRINGPARAM( ")" ) );
1250         ::rtl::OUString sRet = _rDisplayText;
1251 
1252         if ( sRet.indexOf( sAllFilter ) == -1 )
1253         {
1254             String sExt = _rExtension;
1255             if ( !_bForOpen )
1256                 // show '*' in extensions only when opening a document
1257                 sExt.EraseAllChars( '*' );
1258             sRet += sOpenBracket;
1259             sRet += sExt;
1260             sRet += sCloseBracket;
1261         }
1262         _rFileDlgImpl.addFilterPair( _rDisplayText, sRet );
1263         return sRet;
1264     }
1265 
1266 //........................................................................
1267 }   // namespace sfx2
1268 //........................................................................
1269 
1270 
1271