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