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