1 /*************************************************************************
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * Copyright 2000, 2010 Oracle and/or its affiliates.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * This file is part of OpenOffice.org.
10  *
11  * OpenOffice.org is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License version 3
13  * only, as published by the Free Software Foundation.
14  *
15  * OpenOffice.org is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Lesser General Public License version 3 for more details
19  * (a copy is included in the LICENSE file that accompanied this code).
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * version 3 along with OpenOffice.org.  If not, see
23  * <http://www.openoffice.org/license.html>
24  * for a copy of the LGPLv3 License.
25  *
26  ************************************************************************/
27 
28 // MARKER(update_precomp.py): autogen include statement, do not remove
29 #include "precompiled_slideshow.hxx"
30 
31 // must be first
32 #include <canvas/debug.hxx>
33 #include <tools/diagnose_ex.h>
34 #include <canvas/verbosetrace.hxx>
35 
36 #include <rtl/math.hxx>
37 #include <rtl/logfile.hxx>
38 
39 #include <vcl/metaact.hxx>
40 #include <vcl/gdimtf.hxx>
41 #include <basegfx/numeric/ftools.hxx>
42 
43 #include "drawshapesubsetting.hxx"
44 #include "drawshape.hxx"
45 
46 #include <boost/bind.hpp>
47 
48 #include <algorithm>
49 #include <functional>
50 #include <limits>
51 
52 using namespace ::com::sun::star;
53 
54 
55 namespace slideshow
56 {
57     namespace internal
58     {
59 
60         //////////////////////////////////////////////////////////////////////
61         //
62         // Private methods
63         //
64         //////////////////////////////////////////////////////////////////////
65 
66         void DrawShapeSubsetting::ensureInitializedNodeTree() const
67         {
68             ENSURE_OR_THROW( mpMtf,
69                               "DrawShapeSubsetting::ensureInitializedNodeTree(): Invalid mtf" );
70 
71             if( mbNodeTreeInitialized )
72                 return; // done, already initialized.
73 
74             // init doctree vector
75             maActionClassVector.clear();
76             maActionClassVector.reserve( mpMtf->GetActionCount() );
77 
78             // search metafile for text output
79             MetaAction* pCurrAct;
80 
81             sal_Int32 nActionIndex(0);
82             sal_Int32 nLastTextActionIndex(0);
83             for( pCurrAct = mpMtf->FirstAction(); pCurrAct; pCurrAct = mpMtf->NextAction() )
84             {
85                 // check for one of our special text doctree comments
86                 switch( pCurrAct->GetType() )
87                 {
88                     case META_COMMENT_ACTION:
89                     {
90                         MetaCommentAction* pAct = static_cast<MetaCommentAction*>(pCurrAct);
91 
92                         // skip comment if not a special XTEXT comment
93                         if( pAct->GetComment().CompareIgnoreCaseToAscii( "XTEXT", 5 ) == COMPARE_EQUAL )
94                         {
95                             // fill classification vector with NOOPs,
96                             // then insert corresponding classes at
97                             // the given index
98                             maActionClassVector.resize( nActionIndex+1, CLASS_NOOP );
99 
100                             if( pAct->GetComment().CompareIgnoreCaseToAscii( "XTEXT_EOC" ) == COMPARE_EQUAL )
101                             {
102                                 // special, because can happen
103                                 // in-between of portions - set
104                                 // character-end classificator at
105                                 // given index (relative to last text
106                                 // action).
107                                 const sal_Int32 nIndex( nLastTextActionIndex + pAct->GetValue() );
108 
109                                 ENSURE_OR_THROW( static_cast< ::std::size_t >(nIndex) < maActionClassVector.size(),
110                                                   "DrawShapeSubsetting::ensureInitializedNodeTree(): sentence index out of range" );
111 
112                                 maActionClassVector[ nIndex ] = CLASS_CHARACTER_CELL_END;
113                             }
114                             else if( pAct->GetComment().CompareIgnoreCaseToAscii( "XTEXT_EOW" ) == COMPARE_EQUAL )
115                             {
116                                 // special, because can happen
117                                 // in-between of portions - set
118                                 // word-end classificator at given
119                                 // index (relative to last text
120                                 // action).
121                                 const sal_Int32 nIndex( nLastTextActionIndex + pAct->GetValue() );
122 
123                                 ENSURE_OR_THROW( static_cast< ::std::size_t >(nIndex) < maActionClassVector.size(),
124                                                   "DrawShapeSubsetting::ensureInitializedNodeTree(): sentence index out of range" );
125 
126                                 maActionClassVector[ nIndex ] = CLASS_WORD_END;
127                             }
128                             else if( pAct->GetComment().CompareIgnoreCaseToAscii( "XTEXT_EOS" ) == COMPARE_EQUAL )
129                             {
130                                 // special, because can happen
131                                 // in-between of portions - set
132                                 // sentence-end classificator at given
133                                 // index (relative to last text
134                                 // action).
135                                 const sal_Int32 nIndex( nLastTextActionIndex + pAct->GetValue() );
136 
137                                 ENSURE_OR_THROW( static_cast< ::std::size_t >(nIndex) < maActionClassVector.size(),
138                                                   "DrawShapeSubsetting::ensureInitializedNodeTree(): sentence index out of range" );
139 
140                                 maActionClassVector[ nIndex ] = CLASS_SENTENCE_END;
141                             }
142                             else if( pAct->GetComment().CompareIgnoreCaseToAscii( "XTEXT_EOL" ) == COMPARE_EQUAL )
143                             {
144                                 maActionClassVector[ nActionIndex ] = CLASS_LINE_END;
145                             }
146                             else if( pAct->GetComment().CompareIgnoreCaseToAscii( "XTEXT_EOP" ) == COMPARE_EQUAL )
147                             {
148                                 maActionClassVector[ nActionIndex ] = CLASS_PARAGRAPH_END;
149                             }
150                             else if( pAct->GetComment().CompareIgnoreCaseToAscii( "XTEXT_PAINTSHAPE_END" ) == COMPARE_EQUAL )
151                             {
152                                 maActionClassVector[ nActionIndex ] = CLASS_SHAPE_END;
153                             }
154                             else if( pAct->GetComment().CompareIgnoreCaseToAscii( "XTEXT_PAINTSHAPE_BEGIN" ) == COMPARE_EQUAL )
155                             {
156                                 maActionClassVector[ nActionIndex ] = CLASS_SHAPE_START;
157                             }
158                         }
159                         ++nActionIndex;
160                         break;
161                     }
162                     case META_TEXT_ACTION:
163                     case META_TEXTARRAY_ACTION:
164                     case META_STRETCHTEXT_ACTION:
165                         nLastTextActionIndex = nActionIndex;
166                         // fallthrough intended
167                     default:
168                         // comment action and all actions not
169                         // explicitely handled here:
170                         nActionIndex += getNextActionOffset(pCurrAct);
171                         break;
172                 }
173             }
174 
175             mbNodeTreeInitialized = true;
176         }
177 
178         void DrawShapeSubsetting::updateSubsetBounds( const SubsetEntry& rSubsetEntry )
179         {
180             // TODO(F1): This removes too much from non-contiguous subsets
181             mnMinSubsetActionIndex = ::std::min(
182                 mnMinSubsetActionIndex,
183                 rSubsetEntry.mnStartActionIndex );
184             mnMaxSubsetActionIndex = ::std::max(
185                 mnMaxSubsetActionIndex,
186                 rSubsetEntry.mnEndActionIndex );
187         }
188 
189         void DrawShapeSubsetting::updateSubsets()
190         {
191             maCurrentSubsets.clear();
192 
193             if( !maSubsetShapes.empty() )
194             {
195                 if( maSubset.isEmpty() )
196                 {
197                     // non-subsetted node, with some child subsets
198                     // that subtract from it
199                     maCurrentSubsets.push_back( DocTreeNode( 0,
200                                                              mnMinSubsetActionIndex,
201                                                              DocTreeNode::NODETYPE_INVALID ) );
202                     maCurrentSubsets.push_back( DocTreeNode( mnMaxSubsetActionIndex,
203                                                              maActionClassVector.size(),
204                                                              DocTreeNode::NODETYPE_INVALID ) );
205                 }
206                 else
207                 {
208                     // subsetted node, from which some further child
209                     // subsets subtract content
210                     maCurrentSubsets.push_back( DocTreeNode( maSubset.getStartIndex(),
211                                                              mnMinSubsetActionIndex,
212                                                              DocTreeNode::NODETYPE_INVALID ) );
213                     maCurrentSubsets.push_back( DocTreeNode( mnMaxSubsetActionIndex,
214                                                              maSubset.getEndIndex(),
215                                                              DocTreeNode::NODETYPE_INVALID ) );
216                 }
217             }
218             else
219             {
220                 // no further child subsets, simply add our subset (if any)
221                 if( !maSubset.isEmpty() )
222                 {
223                     // subsetted node, without any subset children
224                     maCurrentSubsets.push_back( maSubset );
225                 }
226             }
227         }
228 
229         //////////////////////////////////////////////////////////////////////
230         //
231         // Public methods
232         //
233         //////////////////////////////////////////////////////////////////////
234 
235         DrawShapeSubsetting::DrawShapeSubsetting() :
236             maActionClassVector(),
237             mpMtf(),
238             maSubset(),
239             maSubsetShapes(),
240             mnMinSubsetActionIndex( SAL_MAX_INT32 ),
241             mnMaxSubsetActionIndex(0),
242             maCurrentSubsets(),
243             mbNodeTreeInitialized( false )
244         {
245         }
246 
247         DrawShapeSubsetting::DrawShapeSubsetting( const GDIMetaFileSharedPtr& rMtf ) :
248             maActionClassVector(),
249             mpMtf( rMtf ),
250             maSubset(),
251             maSubsetShapes(),
252             mnMinSubsetActionIndex( SAL_MAX_INT32 ),
253             mnMaxSubsetActionIndex(0),
254             maCurrentSubsets(),
255             mbNodeTreeInitialized( false )
256         {
257             ENSURE_OR_THROW( mpMtf,
258                               "DrawShapeSubsetting::DrawShapeSubsetting(): Invalid metafile" );
259 
260             initCurrentSubsets();
261         }
262 
263         DrawShapeSubsetting::DrawShapeSubsetting( const DocTreeNode&			rShapeSubset,
264                                                   const GDIMetaFileSharedPtr&	rMtf ) :
265             maActionClassVector(),
266             mpMtf( rMtf ),
267             maSubset( rShapeSubset ),
268             maSubsetShapes(),
269             mnMinSubsetActionIndex( SAL_MAX_INT32 ),
270             mnMaxSubsetActionIndex(0),
271             maCurrentSubsets(),
272             mbNodeTreeInitialized( false )
273         {
274             ENSURE_OR_THROW( mpMtf,
275                               "DrawShapeSubsetting::DrawShapeSubsetting(): Invalid metafile" );
276 
277             initCurrentSubsets();
278         }
279 
280         void DrawShapeSubsetting::reset()
281         {
282             maActionClassVector.clear();
283             mpMtf.reset();
284             maSubset.reset();
285             maSubsetShapes.clear();
286             mnMinSubsetActionIndex = SAL_MAX_INT32;
287             mnMaxSubsetActionIndex = 0;
288             maCurrentSubsets.clear();
289             mbNodeTreeInitialized = false;
290         }
291 
292         void DrawShapeSubsetting::reset( const ::boost::shared_ptr< GDIMetaFile >& rMtf )
293         {
294             reset();
295             mpMtf = rMtf;
296 
297             initCurrentSubsets();
298         }
299 
300         void DrawShapeSubsetting::reset( const DocTreeNode&                          rShapeSubset,
301                                          const ::boost::shared_ptr< GDIMetaFile >&   rMtf )
302         {
303             reset();
304             mpMtf = rMtf;
305             maSubset = rShapeSubset;
306 
307             initCurrentSubsets();
308         }
309 
310         void DrawShapeSubsetting::initCurrentSubsets()
311         {
312             // only add subset to vector, if it's not empty - that's
313             // because the vector's content is later literally used
314             // for e.g. painting.
315             if( !maSubset.isEmpty() )
316                 maCurrentSubsets.push_back( maSubset );
317         }
318 
319         DocTreeNode DrawShapeSubsetting::getSubsetNode() const
320         {
321             return maSubset;
322         }
323 
324         bool DrawShapeSubsetting::hasSubsetShapes() const
325         {
326             return !maSubsetShapes.empty();
327         }
328 
329         AttributableShapeSharedPtr DrawShapeSubsetting::getSubsetShape( const DocTreeNode& rTreeNode ) const
330         {
331             RTL_LOGFILE_CONTEXT( aLog, "::presentation::internal::DrawShapeSubsetting::getSubsetShape()" );
332 
333             // subset shape already created for this DocTreeNode?
334             SubsetEntry aEntry;
335 
336             aEntry.mnStartActionIndex 	= rTreeNode.getStartIndex();
337             aEntry.mnEndActionIndex 	= rTreeNode.getEndIndex();
338 
339             ShapeSet::const_iterator aIter;
340             if( (aIter=maSubsetShapes.find( aEntry )) != maSubsetShapes.end() )
341             {
342                 // already created, return found entry
343                 return aIter->mpShape;
344             }
345 
346             return AttributableShapeSharedPtr();
347         }
348 
349         void DrawShapeSubsetting::addSubsetShape( const AttributableShapeSharedPtr& rShape )
350         {
351             RTL_LOGFILE_CONTEXT( aLog, "::presentation::internal::DrawShapeSubsetting::addSubsetShape()" );
352 
353             // subset shape already created for this DocTreeNode?
354             SubsetEntry aEntry;
355             const DocTreeNode& rEffectiveSubset( rShape->getSubsetNode() );
356 
357             aEntry.mnStartActionIndex 	= rEffectiveSubset.getStartIndex();
358             aEntry.mnEndActionIndex 	= rEffectiveSubset.getEndIndex();
359 
360             ShapeSet::const_iterator aIter;
361             if( (aIter=maSubsetShapes.find( aEntry )) != maSubsetShapes.end() )
362             {
363                 // already created, increment use count and return
364 
365                 // safe cast, since set order does not depend on
366                 // mnSubsetQueriedCount
367                 const_cast<SubsetEntry&>(*aIter).mnSubsetQueriedCount++;
368             }
369             else
370             {
371                 // not yet created, init entry
372                 aEntry.mnSubsetQueriedCount = 1;
373                 aEntry.mpShape = rShape;
374 
375                 maSubsetShapes.insert( aEntry );
376 
377                 // update cached subset borders
378                 updateSubsetBounds( aEntry );
379                 updateSubsets();
380             }
381         }
382 
383         bool DrawShapeSubsetting::revokeSubsetShape( const AttributableShapeSharedPtr& rShape )
384         {
385             RTL_LOGFILE_CONTEXT( aLog, "::presentation::internal::DrawShapeSubsetting::revokeSubsetShape()" );
386 
387             // lookup subset shape
388             SubsetEntry aEntry;
389             const DocTreeNode& rEffectiveSubset( rShape->getSubsetNode() );
390 
391             aEntry.mnStartActionIndex 	= rEffectiveSubset.getStartIndex();
392             aEntry.mnEndActionIndex 	= rEffectiveSubset.getEndIndex();
393 
394             ShapeSet::iterator aIter;
395             if( (aIter=maSubsetShapes.find( aEntry )) == maSubsetShapes.end() )
396                 return false; // not found, subset was never queried
397 
398             // last client of the subset revoking?
399             if( aIter->mnSubsetQueriedCount > 1 )
400             {
401                 // no, still clients out there. Just decrement use count
402                 // safe cast, since order does not depend on mnSubsetQueriedCount
403                 const_cast<SubsetEntry&>(*aIter).mnSubsetQueriedCount--;
404 
405                 VERBOSE_TRACE( "Subset summary: shape 0x%X, %d open subsets, revoked subset has refcount %d",
406                                this,
407                                maSubsetShapes.size(),
408                                aIter->mnSubsetQueriedCount );
409 
410                 return false; // not the last client
411             }
412 
413             VERBOSE_TRACE( "Subset summary: shape 0x%X, %d open subsets, cleared subset has range [%d,%d]",
414                            this,
415                            maSubsetShapes.size(),
416                            aEntry.mnStartActionIndex,
417                            aEntry.mnEndActionIndex );
418 
419             // yes, remove from set
420             maSubsetShapes.erase( aIter );
421 
422 
423             // update currently active subset for _our_ shape (the
424             // part of this shape that is visible, i.e. not displayed
425             // in subset shapes)
426             // ======================================================
427 
428             // init bounds
429             mnMinSubsetActionIndex = SAL_MAX_INT32;
430             mnMaxSubsetActionIndex = 0;
431 
432             // TODO(P2): This is quite expensive, when
433             // after every subset effect end, we have to scan
434             // the whole shape set
435 
436             // determine new subset range
437             ::std::for_each( maSubsetShapes.begin(),
438                              maSubsetShapes.end(),
439                              ::boost::bind(&DrawShapeSubsetting::updateSubsetBounds,
440                                            this,
441                                            _1 ) );
442 
443             updateSubsets();
444 
445             return true;
446         }
447 
448         namespace
449         {
450             /** Iterate over all action classification entries in the
451                 given range, pass each element range found to the
452                 given functor.
453 
454                 This method extracts, for each of the different action
455                 classifications, the count and the ranges for each of
456                 them, and calls the provided functor with that
457                 information.
458 
459                 @tpl FunctorT
460                 This is the functor's operator() calling signature,
461                 with eCurrElemClassification denoting the current
462                 classification type the functor is called for,
463                 nCurrElemCount the running total of elements visited
464                 for the given class (starting from 0), and
465                 rCurrElemBegin/rCurrElemEnd the range of the current
466                 element (i.e. the iterators from the start to the end
467                 of this element).
468                 <pre>
469                 bool operator()( IndexClassificator								 eCurrElemClassification
470                 				 sal_Int32										 nCurrElemCount,
471 								 const IndexClassificatorVector::const_iterator& rCurrElemBegin,
472 								 const IndexClassificatorVector::const_iterator& rCurrElemEnd );
473                 </pre>
474                 If the functor returns false, iteration over the
475                 shapes is immediately stopped.
476 
477                 @param io_pFunctor
478                 This functor is called for every shape found.
479 
480                 @param rBegin
481                 Start of range to iterate over
482 
483                 @param rEnd
484                 End of range to iterate over
485 
486                 @return the number of shapes found in the metafile
487              */
488             template< typename FunctorT > void iterateActionClassifications(
489                 FunctorT& 															 io_rFunctor,
490                 const DrawShapeSubsetting::IndexClassificatorVector::const_iterator& rBegin,
491                 const DrawShapeSubsetting::IndexClassificatorVector::const_iterator& rEnd )
492             {
493                 sal_Int32 nCurrShapeCount( 0 );
494                 sal_Int32 nCurrParaCount( 0 );
495                 sal_Int32 nCurrLineCount( 0 );
496                 sal_Int32 nCurrSentenceCount( 0 );
497                 sal_Int32 nCurrWordCount( 0 );
498                 sal_Int32 nCurrCharCount( 0 );
499 
500                 DrawShapeSubsetting::IndexClassificatorVector::const_iterator aLastShapeStart(rBegin);
501                 DrawShapeSubsetting::IndexClassificatorVector::const_iterator aLastParaStart(rBegin);
502                 DrawShapeSubsetting::IndexClassificatorVector::const_iterator aLastLineStart(rBegin);
503                 DrawShapeSubsetting::IndexClassificatorVector::const_iterator aLastSentenceStart(rBegin);
504                 DrawShapeSubsetting::IndexClassificatorVector::const_iterator aLastWordStart(rBegin);
505                 DrawShapeSubsetting::IndexClassificatorVector::const_iterator aLastCharStart(rBegin);
506 
507                 DrawShapeSubsetting::IndexClassificatorVector::const_iterator aNext;
508                 DrawShapeSubsetting::IndexClassificatorVector::const_iterator aCurr( rBegin );
509                 while( aCurr != rEnd )
510                 {
511                     // aNext will hold an iterator to the next element
512                     // (or the past-the-end iterator, if aCurr
513                     // references the last element). Used to pass a
514                     // valid half-open range to the functors.
515                     aNext = aCurr;
516                     ++aNext;
517 
518                     switch( *aCurr )
519                     {
520                         default:
521                             ENSURE_OR_THROW( false,
522                                               "Unexpected type in iterateDocShapes()" );
523                         case DrawShapeSubsetting::CLASS_NOOP:
524                             // ignore NOOP actions
525                             break;
526 
527                         case DrawShapeSubsetting::CLASS_SHAPE_START:
528                             // regardless of ending action
529                             // classifications before: a new shape
530                             // always also starts contained elements
531                             // anew
532                             aLastShapeStart    =
533                             aLastParaStart     =
534                             aLastLineStart     =
535                             aLastSentenceStart =
536                             aLastWordStart     =
537                             aLastCharStart     = aCurr;
538                             break;
539 
540                         case DrawShapeSubsetting::CLASS_SHAPE_END:
541                             if( !io_rFunctor( DrawShapeSubsetting::CLASS_SHAPE_END,
542                                               nCurrShapeCount,
543                                               aLastShapeStart,
544                                               aNext ) )
545                             {
546                                 return;
547                             }
548 
549                             ++nCurrShapeCount;
550                             // FALLTHROUGH intended: shape end also
551                             // ends lines
552                         case DrawShapeSubsetting::CLASS_PARAGRAPH_END:
553                             if( !io_rFunctor( DrawShapeSubsetting::CLASS_PARAGRAPH_END,
554                                               nCurrParaCount,
555                                               aLastParaStart,
556                                               aNext ) )
557                             {
558                                 return;
559                             }
560 
561                             ++nCurrParaCount;
562                             aLastParaStart = aNext;
563                             // FALLTHROUGH intended: para end also
564                             // ends line
565                         case DrawShapeSubsetting::CLASS_LINE_END:
566                             if( !io_rFunctor( DrawShapeSubsetting::CLASS_LINE_END,
567                                               nCurrLineCount,
568                                               aLastLineStart,
569                                               aNext ) )
570                             {
571                                 return;
572                             }
573 
574                             ++nCurrLineCount;
575                             aLastLineStart = aNext;
576 
577                             if( *aCurr == DrawShapeSubsetting::CLASS_LINE_END )
578                             {
579                                 // DON'T fall through here, as a line
580                                 // does NOT end neither a sentence,
581                                 // nor a word. OTOH, all parent
582                                 // structures (paragraph and shape),
583                                 // which itself fall through to this
584                                 // code, DO end word, sentence and
585                                 // character cell.
586 
587                                 // TODO(F1): Maybe a line should end a
588                                 // character cell, OTOH?
589                                 break;
590                             }
591                             // FALLTHROUGH intended
592                         case DrawShapeSubsetting::CLASS_SENTENCE_END:
593                             if( !io_rFunctor( DrawShapeSubsetting::CLASS_SENTENCE_END,
594                                               nCurrSentenceCount,
595                                               aLastSentenceStart,
596                                               aNext ) )
597                             {
598                                 return;
599                             }
600 
601                             ++nCurrSentenceCount;
602                             aLastSentenceStart = aNext;
603                             // FALLTHROUGH intended
604                         case DrawShapeSubsetting::CLASS_WORD_END:
605                             if( !io_rFunctor( DrawShapeSubsetting::CLASS_WORD_END,
606                                               nCurrWordCount,
607                                               aLastWordStart,
608                                               aNext ) )
609                             {
610                                 return;
611                             }
612 
613                             ++nCurrWordCount;
614                             aLastWordStart = aNext;
615                             // FALLTHROUGH intended
616                         case DrawShapeSubsetting::CLASS_CHARACTER_CELL_END:
617                             if( !io_rFunctor( DrawShapeSubsetting::CLASS_CHARACTER_CELL_END,
618                                               nCurrCharCount,
619                                               aLastCharStart,
620                                               aNext ) )
621                             {
622                                 return;
623                             }
624 
625                             ++nCurrCharCount;
626                             aLastCharStart = aNext;
627                             break;
628                     }
629 
630                     aCurr = aNext;
631                 }
632             }
633 
634             DrawShapeSubsetting::IndexClassificator mapDocTreeNode( DocTreeNode::NodeType eNodeType )
635             {
636                 switch( eNodeType )
637                 {
638                     case DocTreeNode::NODETYPE_INVALID:
639                         // FALLTHROUGH intended
640                     default:
641                         OSL_ENSURE(false,
642                                    "DrawShapeSubsetting::mapDocTreeNode(): unexpected node type");
643                         return DrawShapeSubsetting::CLASS_NOOP;
644 
645                     case DocTreeNode::NODETYPE_LOGICAL_SHAPE:
646                         // FALLTHROUGH intended
647                     case DocTreeNode::NODETYPE_FORMATTING_SHAPE:
648                         return DrawShapeSubsetting::CLASS_SHAPE_END;
649 
650                     case DocTreeNode::NODETYPE_FORMATTING_LINE:
651                         return DrawShapeSubsetting::CLASS_LINE_END;
652 
653                     case DocTreeNode::NODETYPE_LOGICAL_PARAGRAPH:
654                         return DrawShapeSubsetting::CLASS_PARAGRAPH_END;
655 
656                     case DocTreeNode::NODETYPE_LOGICAL_SENTENCE:
657                         return DrawShapeSubsetting::CLASS_SENTENCE_END;
658 
659                     case DocTreeNode::NODETYPE_LOGICAL_WORD:
660                         return DrawShapeSubsetting::CLASS_WORD_END;
661 
662                     case DocTreeNode::NODETYPE_LOGICAL_CHARACTER_CELL:
663                         return DrawShapeSubsetting::CLASS_CHARACTER_CELL_END;
664                 };
665             }
666 
667             /// Counts number of class occurences
668             class CountClassFunctor
669             {
670             public:
671                 CountClassFunctor( DrawShapeSubsetting::IndexClassificator eClass ) :
672                     meClass( eClass ),
673                     mnCurrCount(0)
674                 {
675                 }
676 
677                 bool operator()( DrawShapeSubsetting::IndexClassificator								eCurrElemClassification,
678                 				 sal_Int32																/*nCurrElemCount*/,
679 								 const DrawShapeSubsetting::IndexClassificatorVector::const_iterator&	/*rCurrElemBegin*/,
680 								 const DrawShapeSubsetting::IndexClassificatorVector::const_iterator&	/*rCurrElemEnd*/ )
681                 {
682                     if( eCurrElemClassification == meClass )
683                         ++mnCurrCount;
684 
685                     return true; // never stop, count all occurences
686                 }
687 
688                 sal_Int32 getCount() const
689                 {
690                     return mnCurrCount;
691                 }
692 
693             private:
694                 DrawShapeSubsetting::IndexClassificator meClass;
695                 sal_Int32								mnCurrCount;
696             };
697         }
698 
699         sal_Int32 DrawShapeSubsetting::implGetNumberOfTreeNodes( const DrawShapeSubsetting::IndexClassificatorVector::const_iterator& rBegin,
700                                                                  const DrawShapeSubsetting::IndexClassificatorVector::const_iterator& rEnd,
701                                                                  DocTreeNode::NodeType 												  eNodeType ) const
702         {
703             const IndexClassificator eRequestedClass(
704                 mapDocTreeNode( eNodeType ) );
705 
706             // create a counting functor for the requested class of
707             // actions
708             CountClassFunctor aFunctor( eRequestedClass );
709 
710             // count all occurences in the given range
711             iterateActionClassifications( aFunctor, rBegin, rEnd );
712 
713             return aFunctor.getCount();
714         }
715 
716         sal_Int32 DrawShapeSubsetting::getNumberOfTreeNodes( DocTreeNode::NodeType eNodeType ) const
717         {
718             ensureInitializedNodeTree();
719 
720             return implGetNumberOfTreeNodes( maActionClassVector.begin(),
721                                              maActionClassVector.end(),
722                                              eNodeType );
723         }
724 
725         namespace
726         {
727             /** This functor finds the nth occurrence of a given
728                 action class.
729 
730                 The operator() compares the given index value with the
731                 requested index, as given on the functor's
732                 constructor. Then, the operator() returns false,
733                 denoting that the requested action is found.
734              */
735             class FindNthElementFunctor
736             {
737             public:
738                 FindNthElementFunctor( sal_Int32 								nNodeIndex,
739                                        DrawShapeSubsetting::IndexClassificator	eClass ) :
740                     mnNodeIndex( nNodeIndex ),
741                     meClass( eClass )
742                 {
743                 }
744 
745                 bool operator()( DrawShapeSubsetting::IndexClassificator								eCurrElemClassification,
746                 				 sal_Int32																nCurrElemCount,
747 								 const DrawShapeSubsetting::IndexClassificatorVector::const_iterator&	rCurrElemBegin,
748 								 const DrawShapeSubsetting::IndexClassificatorVector::const_iterator&	rCurrElemEnd )
749                 {
750                     if( eCurrElemClassification == meClass &&
751                         nCurrElemCount == mnNodeIndex )
752                     {
753                         maLastBegin = rCurrElemBegin;
754                         maLastEnd = rCurrElemEnd;
755 
756                         return false; // abort iteration, we've
757                                       // already found what we've been
758                                       // looking for
759                     }
760 
761                     return true; // keep on truckin'
762                 }
763 
764                 DrawShapeSubsetting::IndexClassificatorVector::const_iterator getBeginElement() const
765                 {
766                     return maLastBegin;
767                 }
768 
769                 DrawShapeSubsetting::IndexClassificatorVector::const_iterator getEndElement() const
770                 {
771                     return maLastEnd;
772                 }
773 
774             private:
775                 sal_Int32														mnNodeIndex;
776                 DrawShapeSubsetting::IndexClassificatorVector::const_iterator	maLastBegin;
777                 DrawShapeSubsetting::IndexClassificatorVector::const_iterator	maLastEnd;
778                 DrawShapeSubsetting::IndexClassificator							meClass;
779             };
780 
781             DocTreeNode makeTreeNode( const DrawShapeSubsetting::IndexClassificatorVector::const_iterator& rBegin,
782                                       const DrawShapeSubsetting::IndexClassificatorVector::const_iterator& rStart,
783                                       const DrawShapeSubsetting::IndexClassificatorVector::const_iterator& rEnd,
784                                       DocTreeNode::NodeType												   eNodeType )
785             {
786                 return DocTreeNode( ::std::distance(rBegin,
787                                                     rStart),
788                                     ::std::distance(rBegin,
789                                                     rEnd),
790                                     eNodeType );
791             }
792         }
793 
794         DocTreeNode DrawShapeSubsetting::implGetTreeNode( const IndexClassificatorVector::const_iterator&	rBegin,
795                                                           const IndexClassificatorVector::const_iterator&	rEnd,
796                                                           sal_Int32											nNodeIndex,
797                                                           DocTreeNode::NodeType								eNodeType ) const
798         {
799             const IndexClassificator eRequestedClass(
800                 mapDocTreeNode( eNodeType ) );
801 
802             // create a nth element functor for the requested class of
803             // actions, and nNodeIndex as the target index
804             FindNthElementFunctor aFunctor( nNodeIndex,
805                                             eRequestedClass );
806 
807             // find given index in the given range
808             iterateActionClassifications( aFunctor, rBegin, rEnd );
809 
810             return makeTreeNode( maActionClassVector.begin(),
811                                  aFunctor.getBeginElement(),
812                                  aFunctor.getEndElement(),
813                                  eNodeType );
814         }
815 
816         DocTreeNode DrawShapeSubsetting::getTreeNode( sal_Int32				nNodeIndex,
817                                                       DocTreeNode::NodeType	eNodeType ) const
818         {
819             ensureInitializedNodeTree();
820 
821             return implGetTreeNode( maActionClassVector.begin(),
822                                     maActionClassVector.end(),
823                                     nNodeIndex,
824                                     eNodeType );
825         }
826 
827         sal_Int32 DrawShapeSubsetting::getNumberOfSubsetTreeNodes( const DocTreeNode&  		rParentNode,
828                                                                    DocTreeNode::NodeType	eNodeType ) const
829         {
830             ensureInitializedNodeTree();
831 
832             // convert from vector indices to vector iterators
833             const DrawShapeSubsetting::IndexClassificatorVector::const_iterator aBegin( maActionClassVector.begin() );
834             const DrawShapeSubsetting::IndexClassificatorVector::const_iterator aParentBegin( aBegin + rParentNode.getStartIndex() );
835             const DrawShapeSubsetting::IndexClassificatorVector::const_iterator aParentEnd( aBegin + rParentNode.getEndIndex() );
836 
837             return implGetNumberOfTreeNodes( aParentBegin,
838                                              aParentEnd,
839                                              eNodeType );
840         }
841 
842         DocTreeNode DrawShapeSubsetting::getSubsetTreeNode( const DocTreeNode& 		rParentNode,
843                                                             sal_Int32				nNodeIndex,
844                                                             DocTreeNode::NodeType	eNodeType ) const
845         {
846             ensureInitializedNodeTree();
847 
848             // convert from vector indices to vector iterators
849             const DrawShapeSubsetting::IndexClassificatorVector::const_iterator aBegin( maActionClassVector.begin() );
850             const DrawShapeSubsetting::IndexClassificatorVector::const_iterator aParentBegin( aBegin + rParentNode.getStartIndex() );
851             const DrawShapeSubsetting::IndexClassificatorVector::const_iterator aParentEnd( aBegin + rParentNode.getEndIndex() );
852 
853             return implGetTreeNode( aParentBegin,
854                                     aParentEnd,
855                                     nNodeIndex,
856                                     eNodeType );
857         }
858 
859         const VectorOfDocTreeNodes& DrawShapeSubsetting::getActiveSubsets() const
860         {
861             return maCurrentSubsets;
862         }
863 
864     }
865 }
866