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