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