1 /************************************************************** 2 * 3 * Licensed to the Apache Software Foundation (ASF) under one 4 * or more contributor license agreements. See the NOTICE file 5 * distributed with this work for additional information 6 * regarding copyright ownership. The ASF licenses this file 7 * to you under the Apache License, Version 2.0 (the 8 * "License"); you may not use this file except in compliance 9 * with the License. You may obtain a copy of the License at 10 * 11 * http://www.apache.org/licenses/LICENSE-2.0 12 * 13 * Unless required by applicable law or agreed to in writing, 14 * software distributed under the License is distributed on an 15 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 * KIND, either express or implied. See the License for the 17 * specific language governing permissions and limitations 18 * under the License. 19 * 20 *************************************************************/ 21 22 23 24 // MARKER(update_precomp.py): autogen include statement, do not remove 25 #include "precompiled_editeng.hxx" 26 27 //------------------------------------------------------------------------ 28 // 29 // Global header 30 // 31 //------------------------------------------------------------------------ 32 33 #include <limits.h> 34 #include <vector> 35 #include <algorithm> 36 #include <boost/bind.hpp> 37 #include <vos/mutex.hxx> 38 #include <vcl/window.hxx> 39 #include <vcl/svapp.hxx> 40 #include <comphelper/sequenceasvector.hxx> 41 #include <com/sun/star/uno/Any.hxx> 42 #include <com/sun/star/uno/Reference.hxx> 43 #include <com/sun/star/awt/Point.hpp> 44 #include <com/sun/star/awt/Rectangle.hpp> 45 #include <com/sun/star/accessibility/AccessibleTextType.hpp> 46 47 //------------------------------------------------------------------------ 48 // 49 // Project-local header 50 // 51 //------------------------------------------------------------------------ 52 53 #include <editeng/editdata.hxx> 54 #include <editeng/unopracc.hxx> 55 #include "editeng/unoedprx.hxx" 56 #include <editeng/AccessibleStaticTextBase.hxx> 57 #include "editeng/AccessibleEditableTextPara.hxx" 58 59 60 using namespace ::com::sun::star; 61 using namespace ::com::sun::star::accessibility; 62 63 /* TODO: 64 ===== 65 66 - separate adapter functionality from AccessibleStaticText class 67 68 - refactor common loops into templates, using mem_fun 69 70 */ 71 72 namespace accessibility 73 { 74 typedef ::comphelper::SequenceAsVector< beans::PropertyValue > PropertyValueVector; 75 76 class PropertyValueEqualFunctor : public ::std::binary_function< beans::PropertyValue, beans::PropertyValue, bool > 77 { 78 public: 79 PropertyValueEqualFunctor() 80 {} 81 bool operator() ( const beans::PropertyValue& lhs, const beans::PropertyValue& rhs ) const 82 { 83 return ( lhs.Name == rhs.Name && lhs.Value == rhs.Value ); 84 } 85 }; 86 sal_Unicode cNewLine(0x0a); 87 //------------------------------------------------------------------------ 88 // 89 // Static Helper 90 // 91 //------------------------------------------------------------------------ 92 ESelection MakeSelection( sal_Int32 nStartPara, sal_Int32 nStartIndex, 93 sal_Int32 nEndPara, sal_Int32 nEndIndex ) 94 { 95 DBG_ASSERT(nStartPara >= 0 && nStartPara <= USHRT_MAX && 96 nStartIndex >= 0 && nStartIndex <= USHRT_MAX && 97 nEndPara >= 0 && nEndPara <= USHRT_MAX && 98 nEndIndex >= 0 && nEndIndex <= USHRT_MAX , 99 "AccessibleStaticTextBase_Impl::MakeSelection: index value overflow"); 100 101 return ESelection( static_cast< sal_uInt16 >(nStartPara), static_cast< sal_uInt16 >(nStartIndex), 102 static_cast< sal_uInt16 >(nEndPara), static_cast< sal_uInt16 >(nEndIndex) ); 103 } 104 105 //------------------------------------------------------------------------ 106 // 107 // AccessibleStaticTextBase_Impl declaration 108 // 109 //------------------------------------------------------------------------ 110 111 DBG_NAME( AccessibleStaticTextBase_Impl ); 112 113 /** AccessibleStaticTextBase_Impl 114 115 This class implements the AccessibleStaticTextBase 116 functionality, mainly by forwarding the calls to an aggregated 117 AccessibleEditableTextPara. As this is a therefore non-trivial 118 adapter, factoring out the common functionality from 119 AccessibleEditableTextPara might be a profitable future task. 120 */ 121 class AccessibleStaticTextBase_Impl 122 { 123 friend class AccessibleStaticTextBase; 124 public: 125 126 // receive pointer to our frontend class and view window 127 AccessibleStaticTextBase_Impl(); 128 ~AccessibleStaticTextBase_Impl(); 129 130 SvxEditSourceAdapter& GetEditSource() const SAL_THROW((uno::RuntimeException)) 131 { 132 DBG_CHKTHIS( AccessibleStaticTextBase_Impl, NULL ); 133 134 return maEditSource; 135 } 136 void SetEditSource( ::std::auto_ptr< SvxEditSource > pEditSource ) SAL_THROW((uno::RuntimeException)); 137 138 void SetEventSource( const uno::Reference< XAccessible >& rInterface ) 139 { 140 DBG_CHKTHIS( AccessibleStaticTextBase_Impl, NULL ); 141 142 mxThis = rInterface; 143 } 144 uno::Reference< XAccessible > GetEventSource() const 145 { 146 DBG_CHKTHIS( AccessibleStaticTextBase_Impl, NULL ); 147 148 return mxThis; 149 } 150 151 void SetOffset( const Point& ); 152 Point GetOffset() const 153 { 154 DBG_CHKTHIS( AccessibleStaticTextBase_Impl, NULL ); 155 156 ::osl::MutexGuard aGuard( maMutex ); Point aPoint( maOffset ); 157 return aPoint; 158 } 159 160 void UpdateChildren(); 161 void Dispose(); 162 163 #ifdef DBG_UTIL 164 void CheckInvariants() const; 165 #endif 166 167 AccessibleEditableTextPara& GetParagraph( sal_Int32 nPara ) const; 168 sal_Int32 GetParagraphCount() const; 169 sal_Int32 GetParagraphIndex() const; 170 sal_Int32 GetLineCount( sal_Int32 nParagraph ) const; 171 172 EPosition Index2Internal( sal_Int32 nFlatIndex ) const 173 { 174 DBG_CHKTHIS( AccessibleStaticTextBase_Impl, NULL ); 175 176 return ImpCalcInternal( nFlatIndex, false ); 177 } 178 179 EPosition Range2Internal( sal_Int32 nFlatIndex ) const 180 { 181 DBG_CHKTHIS( AccessibleStaticTextBase_Impl, NULL ); 182 183 return ImpCalcInternal( nFlatIndex, true ); 184 } 185 186 sal_Int32 Internal2Index( EPosition nEEIndex ) const; 187 188 void CorrectTextSegment( TextSegment& aTextSegment, 189 int nPara ) const; 190 191 sal_Bool SetSelection( sal_Int32 nStartPara, sal_Int32 nStartIndex, 192 sal_Int32 nEndPara, sal_Int32 nEndIndex ); 193 sal_Bool CopyText( sal_Int32 nStartPara, sal_Int32 nStartIndex, 194 sal_Int32 nEndPara, sal_Int32 nEndIndex ); 195 196 Rectangle GetParagraphBoundingBox() const; 197 sal_Bool RemoveLineBreakCount( sal_Int32& rIndex ); 198 199 private: 200 201 EPosition ImpCalcInternal( sal_Int32 nFlatIndex, bool bExclusive ) const; 202 203 // our frontend class (the one implementing the actual 204 // interface). That's not necessarily the one containing the impl 205 // pointer 206 uno::Reference< XAccessible > mxThis; 207 208 // implements our functionality, we're just an adapter (guarded by solar mutex) 209 mutable AccessibleEditableTextPara* mpTextParagraph; 210 211 uno::Reference< XAccessible > mxParagraph; 212 213 // a wrapper for the text forwarders (guarded by solar mutex) 214 mutable SvxEditSourceAdapter maEditSource; 215 216 // guard for maOffset 217 mutable ::osl::Mutex maMutex; 218 219 /// our current offset to the containing shape/cell (guarded by maMutex) 220 Point maOffset; 221 222 }; 223 224 //------------------------------------------------------------------------ 225 // 226 // AccessibleStaticTextBase_Impl implementation 227 // 228 //------------------------------------------------------------------------ 229 230 AccessibleStaticTextBase_Impl::AccessibleStaticTextBase_Impl() : 231 mxThis( NULL ), 232 mpTextParagraph( new AccessibleEditableTextPara(NULL) ), 233 mxParagraph( mpTextParagraph ), 234 maEditSource(), 235 maMutex(), 236 maOffset(0,0) 237 { 238 DBG_CTOR( AccessibleStaticTextBase_Impl, NULL ); 239 240 // TODO: this is still somewhat of a hack, all the more since 241 // now the maTextParagraph has an empty parent reference set 242 } 243 244 AccessibleStaticTextBase_Impl::~AccessibleStaticTextBase_Impl() 245 { 246 DBG_DTOR( AccessibleStaticTextBase_Impl, NULL ); 247 } 248 249 void AccessibleStaticTextBase_Impl::SetEditSource( ::std::auto_ptr< SvxEditSource > pEditSource ) SAL_THROW((uno::RuntimeException)) 250 { 251 DBG_CHKTHIS( AccessibleStaticTextBase_Impl, NULL ); 252 253 maEditSource.SetEditSource( pEditSource ); 254 if( mpTextParagraph ) 255 mpTextParagraph->SetEditSource( &maEditSource ); 256 } 257 258 void AccessibleStaticTextBase_Impl::SetOffset( const Point& rPoint ) 259 { 260 DBG_CHKTHIS( AccessibleStaticTextBase_Impl, NULL ); 261 262 // guard against non-atomic access to maOffset data structure 263 { 264 ::osl::MutexGuard aGuard( maMutex ); 265 maOffset = rPoint; 266 } 267 268 if( mpTextParagraph ) 269 mpTextParagraph->SetEEOffset( rPoint ); 270 271 // in all cases, check visibility afterwards. 272 UpdateChildren(); 273 } 274 275 void AccessibleStaticTextBase_Impl::UpdateChildren() 276 { 277 DBG_CHKTHIS( AccessibleStaticTextBase_Impl, NULL ); 278 279 // currently no children 280 } 281 282 void AccessibleStaticTextBase_Impl::Dispose() 283 { 284 DBG_CHKTHIS( AccessibleStaticTextBase_Impl, NULL ); 285 286 // we're the owner of the paragraph, so destroy it, too 287 if( mpTextParagraph ) 288 mpTextParagraph->Dispose(); 289 290 // drop references 291 mxParagraph = NULL; 292 mxThis = NULL; 293 mpTextParagraph = NULL; 294 } 295 296 #ifdef DBG_UTIL 297 void AccessibleStaticTextBase_Impl::CheckInvariants() const 298 { 299 // TODO 300 } 301 #endif 302 303 AccessibleEditableTextPara& AccessibleStaticTextBase_Impl::GetParagraph( sal_Int32 nPara ) const 304 { 305 DBG_CHKTHIS( AccessibleStaticTextBase_Impl, NULL ); 306 307 if( !mpTextParagraph ) 308 throw lang::DisposedException ( 309 ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("object has been already disposed")), mxThis ); 310 311 // TODO: Have a different method on AccessibleEditableTextPara 312 // that does not care about state changes 313 mpTextParagraph->SetParagraphIndex( nPara ); 314 315 return *mpTextParagraph; 316 } 317 318 sal_Int32 AccessibleStaticTextBase_Impl::GetParagraphCount() const 319 { 320 DBG_CHKTHIS( AccessibleStaticTextBase_Impl, NULL ); 321 322 if( !mpTextParagraph ) 323 return 0; 324 else 325 return mpTextParagraph->GetTextForwarder().GetParagraphCount(); 326 } 327 328 sal_Int32 AccessibleStaticTextBase_Impl::GetParagraphIndex() const 329 { 330 DBG_CHKTHIS( AccessibleStaticTextBase_Impl, NULL ); 331 332 sal_Int32 nIndex = -1; 333 if( mpTextParagraph ) 334 nIndex = mpTextParagraph->GetParagraphIndex(); 335 return nIndex; 336 } 337 338 sal_Int32 AccessibleStaticTextBase_Impl::GetLineCount( sal_Int32 nParagraph ) const 339 { 340 DBG_CHKTHIS( AccessibleStaticTextBase_Impl, NULL ); 341 342 sal_Int32 nIndex = 0; 343 if( mpTextParagraph ) 344 nIndex = mpTextParagraph->GetTextForwarder().GetLineCount( static_cast< sal_uInt16 >(nParagraph) ); 345 return nIndex; 346 } 347 348 sal_Int32 AccessibleStaticTextBase_Impl::Internal2Index( EPosition nEEIndex ) const 349 { 350 sal_Int32 aRes(0); 351 int i; 352 for(i=0; i<nEEIndex.nPara; ++i) 353 aRes += GetParagraph(i).getCharacterCount(); 354 355 return aRes + nEEIndex.nIndex; 356 } 357 358 void AccessibleStaticTextBase_Impl::CorrectTextSegment( TextSegment& aTextSegment, 359 int nPara ) const 360 { 361 // Keep 'invalid' values at the TextSegment 362 if( aTextSegment.SegmentStart != -1 && 363 aTextSegment.SegmentStart != -1 ) 364 { 365 // #112814# Correct TextSegment by paragraph offset 366 sal_Int32 nOffset(0); 367 int i; 368 for(i=0; i<nPara; ++i) 369 nOffset += GetParagraph(i).getCharacterCount(); 370 371 aTextSegment.SegmentStart += nOffset; 372 aTextSegment.SegmentEnd += nOffset; 373 } 374 } 375 376 EPosition AccessibleStaticTextBase_Impl::ImpCalcInternal( sal_Int32 nFlatIndex, bool bExclusive ) const 377 { 378 DBG_CHKTHIS( AccessibleStaticTextBase_Impl, NULL ); 379 380 if( nFlatIndex < 0 ) 381 throw lang::IndexOutOfBoundsException(::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("AccessibleStaticTextBase_Impl::Index2Internal: character index out of bounds")), 382 mxThis); 383 // gratuitously accepting larger indices here, AccessibleEditableTextPara will throw eventually 384 385 sal_Int32 nCurrPara, nCurrIndex, nParas, nCurrCount; 386 for( nCurrPara=0, nParas=GetParagraphCount(), nCurrCount=0, nCurrIndex=0; nCurrPara<nParas; ++nCurrPara ) 387 { 388 nCurrCount = GetParagraph( nCurrPara ).getCharacterCount(); 389 nCurrIndex += nCurrCount; 390 if( nCurrIndex >= nFlatIndex ) 391 { 392 // check overflow 393 DBG_ASSERT(nCurrPara >= 0 && nCurrPara <= USHRT_MAX && 394 nFlatIndex - nCurrIndex + nCurrCount >= 0 && nFlatIndex - nCurrIndex + nCurrCount <= USHRT_MAX , 395 "AccessibleStaticTextBase_Impl::Index2Internal: index value overflow"); 396 397 return EPosition( static_cast< sal_uInt16 >(nCurrPara), static_cast< sal_uInt16 >(nFlatIndex - nCurrIndex + nCurrCount) ); 398 } 399 } 400 401 // #102170# Allow one-past the end for ranges 402 if( bExclusive && nCurrIndex == nFlatIndex ) 403 { 404 // check overflow 405 DBG_ASSERT(nCurrPara >= 0 && nCurrPara <= USHRT_MAX && 406 nFlatIndex - nCurrIndex + nCurrCount >= 0 && nFlatIndex - nCurrIndex + nCurrCount <= USHRT_MAX , 407 "AccessibleStaticTextBase_Impl::Index2Internal: index value overflow"); 408 409 return EPosition( static_cast< sal_uInt16 >(nCurrPara-1), static_cast< sal_uInt16 >(nFlatIndex - nCurrIndex + nCurrCount) ); 410 } 411 412 // not found? Out of bounds 413 throw lang::IndexOutOfBoundsException(::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("AccessibleStaticTextBase_Impl::Index2Internal: character index out of bounds")), 414 mxThis); 415 } 416 417 sal_Bool AccessibleStaticTextBase_Impl::SetSelection( sal_Int32 nStartPara, sal_Int32 nStartIndex, 418 sal_Int32 nEndPara, sal_Int32 nEndIndex ) 419 { 420 DBG_CHKTHIS( AccessibleStaticTextBase_Impl, NULL ); 421 422 if( !mpTextParagraph ) 423 return sal_False; 424 425 try 426 { 427 SvxEditViewForwarder& rCacheVF = mpTextParagraph->GetEditViewForwarder( sal_True ); 428 return rCacheVF.SetSelection( MakeSelection(nStartPara, nStartIndex, nEndPara, nEndIndex) ); 429 } 430 catch( const uno::RuntimeException& ) 431 { 432 return sal_False; 433 } 434 } 435 436 sal_Bool AccessibleStaticTextBase_Impl::CopyText( sal_Int32 nStartPara, sal_Int32 nStartIndex, 437 sal_Int32 nEndPara, sal_Int32 nEndIndex ) 438 { 439 DBG_CHKTHIS( AccessibleStaticTextBase_Impl, NULL ); 440 441 if( !mpTextParagraph ) 442 return sal_False; 443 444 try 445 { 446 SvxEditViewForwarder& rCacheVF = mpTextParagraph->GetEditViewForwarder( sal_True ); 447 mpTextParagraph->GetTextForwarder(); // MUST be after GetEditViewForwarder(), see method docs 448 sal_Bool aRetVal; 449 450 // save current selection 451 ESelection aOldSelection; 452 453 rCacheVF.GetSelection( aOldSelection ); 454 rCacheVF.SetSelection( MakeSelection(nStartPara, nStartIndex, nEndPara, nEndIndex) ); 455 aRetVal = rCacheVF.Copy(); 456 rCacheVF.SetSelection( aOldSelection ); // restore 457 458 return aRetVal; 459 } 460 catch( const uno::RuntimeException& ) 461 { 462 return sal_False; 463 } 464 } 465 466 Rectangle AccessibleStaticTextBase_Impl::GetParagraphBoundingBox() const 467 { 468 Rectangle aRect; 469 if( mpTextParagraph ) 470 { 471 awt::Rectangle aAwtRect = mpTextParagraph->getBounds(); 472 aRect = Rectangle( Point( aAwtRect.X, aAwtRect.Y ), Size( aAwtRect.Width, aAwtRect.Height ) ); 473 } 474 else 475 { 476 aRect.SetEmpty(); 477 } 478 return aRect; 479 } 480 //the input argument is the index(including "\n" ) in the string. 481 //the function will calculate the actual index(not including "\n") in the string. 482 //and return true if the index is just at a "\n" 483 sal_Bool AccessibleStaticTextBase_Impl::RemoveLineBreakCount( sal_Int32& rIndex ) 484 { 485 // get the total char number inside the cell. 486 sal_Int32 i, nCount, nParas; 487 for( i=0, nCount=0, nParas=GetParagraphCount(); i<nParas; ++i ) 488 nCount += GetParagraph(i).getCharacterCount(); 489 nCount = nCount + (nParas-1); 490 if( nCount == 0 && rIndex == 0) return sal_False; 491 492 493 sal_Int32 nCurrPara, nCurrCount; 494 sal_Int32 nLineBreakPos = 0, nLineBreakCount = 0; 495 sal_Int32 nParaCount = GetParagraphCount(); 496 for ( nCurrCount = 0, nCurrPara = 0; nCurrPara < nParaCount; nCurrPara++ ) 497 { 498 nCurrCount += GetParagraph( nCurrPara ).getCharacterCount(); 499 nLineBreakPos = nCurrCount++; 500 if ( rIndex == nLineBreakPos ) 501 { 502 rIndex -= (++nLineBreakCount);//(++nLineBreakCount); 503 if ( rIndex < 0) 504 { 505 rIndex = 0; 506 } 507 //if the index is at the last position of the last paragraph 508 //there is no "\n" , so we should increase rIndex by 1 and return false. 509 if ( (nCurrPara+1) == nParaCount ) 510 { 511 rIndex++; 512 return sal_False; 513 } 514 else 515 { 516 return sal_True; 517 } 518 } 519 else if ( rIndex < nLineBreakPos ) 520 { 521 rIndex -= nLineBreakCount; 522 return sal_False; 523 } 524 else 525 { 526 nLineBreakCount++; 527 } 528 } 529 return sal_False; 530 } 531 //------------------------------------------------------------------------ 532 // 533 // AccessibleStaticTextBase implementation 534 // 535 //------------------------------------------------------------------------ 536 537 AccessibleStaticTextBase::AccessibleStaticTextBase( ::std::auto_ptr< SvxEditSource > pEditSource ) : 538 mpImpl( new AccessibleStaticTextBase_Impl() ) 539 { 540 ::vos::OGuard aGuard( Application::GetSolarMutex() ); 541 542 SetEditSource( pEditSource ); 543 } 544 545 AccessibleStaticTextBase::~AccessibleStaticTextBase() 546 { 547 } 548 549 const SvxEditSource& AccessibleStaticTextBase::GetEditSource() const SAL_THROW((::com::sun::star::uno::RuntimeException)) 550 { 551 #ifdef DBG_UTIL 552 mpImpl->CheckInvariants(); 553 554 const SvxEditSource& aEditSource = mpImpl->GetEditSource(); 555 556 mpImpl->CheckInvariants(); 557 558 return aEditSource; 559 #else 560 return mpImpl->GetEditSource(); 561 #endif 562 } 563 564 void AccessibleStaticTextBase::SetEditSource( ::std::auto_ptr< SvxEditSource > pEditSource ) SAL_THROW((::com::sun::star::uno::RuntimeException)) 565 { 566 #ifdef DBG_UTIL 567 // precondition: solar mutex locked 568 DBG_TESTSOLARMUTEX(); 569 570 mpImpl->CheckInvariants(); 571 572 mpImpl->SetEditSource( pEditSource ); 573 574 mpImpl->CheckInvariants(); 575 #else 576 mpImpl->SetEditSource( pEditSource ); 577 #endif 578 } 579 580 void AccessibleStaticTextBase::SetEventSource( const uno::Reference< XAccessible >& rInterface ) 581 { 582 #ifdef DBG_UTIL 583 mpImpl->CheckInvariants(); 584 #endif 585 586 mpImpl->SetEventSource( rInterface ); 587 588 #ifdef DBG_UTIL 589 mpImpl->CheckInvariants(); 590 #endif 591 } 592 593 uno::Reference< XAccessible > AccessibleStaticTextBase::GetEventSource() const 594 { 595 #ifdef DBG_UTIL 596 mpImpl->CheckInvariants(); 597 598 uno::Reference< XAccessible > xRet( mpImpl->GetEventSource() ); 599 600 mpImpl->CheckInvariants(); 601 602 return xRet; 603 #else 604 return mpImpl->GetEventSource(); 605 #endif 606 } 607 608 void AccessibleStaticTextBase::SetOffset( const Point& rPoint ) 609 { 610 #ifdef DBG_UTIL 611 // precondition: solar mutex locked 612 DBG_TESTSOLARMUTEX(); 613 614 mpImpl->CheckInvariants(); 615 616 mpImpl->SetOffset( rPoint ); 617 618 mpImpl->CheckInvariants(); 619 #else 620 mpImpl->SetOffset( rPoint ); 621 #endif 622 } 623 624 Point AccessibleStaticTextBase::GetOffset() const 625 { 626 #ifdef DBG_UTIL 627 mpImpl->CheckInvariants(); 628 629 Point aPoint( mpImpl->GetOffset() ); 630 631 mpImpl->CheckInvariants(); 632 633 return aPoint; 634 #else 635 return mpImpl->GetOffset(); 636 #endif 637 } 638 639 void AccessibleStaticTextBase::UpdateChildren() SAL_THROW((::com::sun::star::uno::RuntimeException)) 640 { 641 #ifdef DBG_UTIL 642 // precondition: solar mutex locked 643 DBG_TESTSOLARMUTEX(); 644 645 mpImpl->CheckInvariants(); 646 647 mpImpl->UpdateChildren(); 648 649 mpImpl->CheckInvariants(); 650 #else 651 mpImpl->UpdateChildren(); 652 #endif 653 } 654 655 void AccessibleStaticTextBase::Dispose() 656 { 657 #ifdef DBG_UTIL 658 mpImpl->CheckInvariants(); 659 #endif 660 661 mpImpl->Dispose(); 662 663 #ifdef DBG_UTIL 664 mpImpl->CheckInvariants(); 665 #endif 666 } 667 668 // XAccessibleContext 669 sal_Int32 SAL_CALL AccessibleStaticTextBase::getAccessibleChildCount() throw (uno::RuntimeException) 670 { 671 // no children at all 672 return 0; 673 } 674 675 uno::Reference< XAccessible > SAL_CALL AccessibleStaticTextBase::getAccessibleChild( sal_Int32 /*i*/ ) throw (lang::IndexOutOfBoundsException, uno::RuntimeException) 676 { 677 // no children at all 678 return uno::Reference< XAccessible >(); 679 } 680 681 uno::Reference< XAccessible > SAL_CALL AccessibleStaticTextBase::getAccessibleAtPoint( const awt::Point& /*_aPoint*/ ) throw (uno::RuntimeException) 682 { 683 // no children at all 684 return uno::Reference< XAccessible >(); 685 } 686 687 // XAccessibleText 688 sal_Int32 SAL_CALL AccessibleStaticTextBase::getCaretPosition() throw (uno::RuntimeException) 689 { 690 ::vos::OGuard aGuard( Application::GetSolarMutex() ); 691 692 sal_Int32 i, nPos, nParas; 693 for( i=0, nPos=-1, nParas=mpImpl->GetParagraphCount(); i<nParas; ++i ) 694 { 695 if( (nPos=mpImpl->GetParagraph(i).getCaretPosition()) != -1 ) 696 return nPos; 697 } 698 699 return nPos; 700 } 701 702 sal_Bool SAL_CALL AccessibleStaticTextBase::setCaretPosition( sal_Int32 nIndex ) throw (lang::IndexOutOfBoundsException, uno::RuntimeException) 703 { 704 return setSelection(nIndex, nIndex); 705 } 706 707 sal_Unicode SAL_CALL AccessibleStaticTextBase::getCharacter( sal_Int32 nIndex ) throw (lang::IndexOutOfBoundsException, uno::RuntimeException) 708 { 709 ::vos::OGuard aGuard( Application::GetSolarMutex() ); 710 711 EPosition aPos( mpImpl->Index2Internal(nIndex) ); 712 713 return mpImpl->GetParagraph( aPos.nPara ).getCharacter( aPos.nIndex ); 714 } 715 716 uno::Sequence< beans::PropertyValue > SAL_CALL AccessibleStaticTextBase::getCharacterAttributes( sal_Int32 nIndex, const ::com::sun::star::uno::Sequence< ::rtl::OUString >& aRequestedAttributes ) throw (lang::IndexOutOfBoundsException, uno::RuntimeException) 717 { 718 ::vos::OGuard aGuard( Application::GetSolarMutex() ); 719 //get the actual index without "\n" 720 mpImpl->RemoveLineBreakCount( nIndex ); 721 722 EPosition aPos( mpImpl->Index2Internal(nIndex) ); 723 724 return mpImpl->GetParagraph( aPos.nPara ).getCharacterAttributes( aPos.nIndex, aRequestedAttributes ); 725 } 726 727 awt::Rectangle SAL_CALL AccessibleStaticTextBase::getCharacterBounds( sal_Int32 nIndex ) throw (lang::IndexOutOfBoundsException, uno::RuntimeException) 728 { 729 ::vos::OGuard aGuard( Application::GetSolarMutex() ); 730 731 // #108900# Allow ranges for nIndex, as one-past-the-end 732 // values are now legal, too. 733 EPosition aPos( mpImpl->Range2Internal(nIndex) ); 734 735 // #i70916# Text in spread sheet cells return the wrong extents 736 AccessibleEditableTextPara& rPara = mpImpl->GetParagraph( aPos.nPara ); 737 awt::Rectangle aParaBounds( rPara.getBounds() ); 738 awt::Rectangle aBounds( rPara.getCharacterBounds( aPos.nIndex ) ); 739 aBounds.X += aParaBounds.X; 740 aBounds.Y += aParaBounds.Y; 741 742 return aBounds; 743 } 744 745 sal_Int32 SAL_CALL AccessibleStaticTextBase::getCharacterCount() throw (uno::RuntimeException) 746 { 747 ::vos::OGuard aGuard( Application::GetSolarMutex() ); 748 749 sal_Int32 i, nCount, nParas; 750 for( i=0, nCount=0, nParas=mpImpl->GetParagraphCount(); i<nParas; ++i ) 751 nCount += mpImpl->GetParagraph(i).getCharacterCount(); 752 //count on the number of "\n" which equals number of paragraphs decrease 1. 753 nCount = nCount + (nParas-1); 754 return nCount; 755 } 756 757 sal_Int32 SAL_CALL AccessibleStaticTextBase::getIndexAtPoint( const awt::Point& rPoint ) throw (uno::RuntimeException) 758 { 759 ::vos::OGuard aGuard( Application::GetSolarMutex() ); 760 761 const sal_Int32 nParas( mpImpl->GetParagraphCount() ); 762 sal_Int32 nIndex; 763 int i; 764 for( i=0; i<nParas; ++i ) 765 { 766 // TODO: maybe exploit the fact that paragraphs are 767 // ordered vertically for early exit 768 769 // #i70916# Text in spread sheet cells return the wrong extents 770 AccessibleEditableTextPara& rPara = mpImpl->GetParagraph( i ); 771 awt::Rectangle aParaBounds( rPara.getBounds() ); 772 awt::Point aPoint( rPoint ); 773 aPoint.X -= aParaBounds.X; 774 aPoint.Y -= aParaBounds.Y; 775 776 // #112814# Use correct index offset 777 if ( ( nIndex = rPara.getIndexAtPoint( aPoint ) ) != -1 ) 778 return mpImpl->Internal2Index( EPosition(sal::static_int_cast<sal_uInt16>(i), 779 sal::static_int_cast<sal_uInt16>(nIndex)) ); 780 } 781 782 return -1; 783 } 784 785 ::rtl::OUString SAL_CALL AccessibleStaticTextBase::getSelectedText() throw (uno::RuntimeException) 786 { 787 ::vos::OGuard aGuard( Application::GetSolarMutex() ); 788 789 sal_Int32 nStart( getSelectionStart() ); 790 sal_Int32 nEnd( getSelectionEnd() ); 791 792 // #104481# Return the empty string for 'no selection' 793 if( nStart < 0 || nEnd < 0 ) 794 return ::rtl::OUString(); 795 796 return getTextRange( nStart, nEnd ); 797 } 798 799 sal_Int32 SAL_CALL AccessibleStaticTextBase::getSelectionStart() throw (uno::RuntimeException) 800 { 801 ::vos::OGuard aGuard( Application::GetSolarMutex() ); 802 803 sal_Int32 i, nPos, nParas; 804 for( i=0, nPos=-1, nParas=mpImpl->GetParagraphCount(); i<nParas; ++i ) 805 { 806 if( (nPos=mpImpl->GetParagraph(i).getSelectionStart()) != -1 ) 807 return nPos; 808 } 809 810 return nPos; 811 } 812 813 sal_Int32 SAL_CALL AccessibleStaticTextBase::getSelectionEnd() throw (uno::RuntimeException) 814 { 815 ::vos::OGuard aGuard( Application::GetSolarMutex() ); 816 817 sal_Int32 i, nPos, nParas; 818 for( i=0, nPos=-1, nParas=mpImpl->GetParagraphCount(); i<nParas; ++i ) 819 { 820 if( (nPos=mpImpl->GetParagraph(i).getSelectionEnd()) != -1 ) 821 return nPos; 822 } 823 824 return nPos; 825 } 826 827 sal_Bool SAL_CALL AccessibleStaticTextBase::setSelection( sal_Int32 nStartIndex, sal_Int32 nEndIndex ) throw (lang::IndexOutOfBoundsException, uno::RuntimeException) 828 { 829 ::vos::OGuard aGuard( Application::GetSolarMutex() ); 830 831 EPosition aStartIndex( mpImpl->Range2Internal(nStartIndex) ); 832 EPosition aEndIndex( mpImpl->Range2Internal(nEndIndex) ); 833 834 return mpImpl->SetSelection( aStartIndex.nPara, aStartIndex.nIndex, 835 aEndIndex.nPara, aEndIndex.nIndex ); 836 } 837 838 ::rtl::OUString SAL_CALL AccessibleStaticTextBase::getText() throw (uno::RuntimeException) 839 { 840 ::vos::OGuard aGuard( Application::GetSolarMutex() ); 841 842 sal_Int32 i, nParas; 843 ::rtl::OUString aRes; 844 for( i=0, nParas=mpImpl->GetParagraphCount(); i<nParas; ++i ) 845 aRes += mpImpl->GetParagraph(i).getText(); 846 847 return aRes; 848 } 849 850 ::rtl::OUString SAL_CALL AccessibleStaticTextBase::getTextRange( sal_Int32 nStartIndex, sal_Int32 nEndIndex ) throw (lang::IndexOutOfBoundsException, uno::RuntimeException) 851 { 852 ::vos::OGuard aGuard( Application::GetSolarMutex() ); 853 854 if( nStartIndex > nEndIndex ) 855 ::std::swap(nStartIndex, nEndIndex); 856 //if startindex equals endindex we will get nothing. So return an empty string directly. 857 if ( nStartIndex == nEndIndex ) 858 { 859 ::rtl::OUString sEmptyStr; 860 return sEmptyStr; 861 } 862 sal_Bool bStart = mpImpl->RemoveLineBreakCount( nStartIndex ); 863 //if the start index is just at a "\n", we need to begin from the next char 864 if ( bStart ) 865 { 866 nStartIndex++; 867 } 868 //we need to find out whether the previous position of the current endindex is at "\n" or not 869 //if yes we need to mark it and add "\n" at the end of the result 870 sal_Int32 nTemp = nEndIndex - 1; 871 sal_Bool bEnd = mpImpl->RemoveLineBreakCount( nTemp ); 872 sal_Bool bTemp = mpImpl->RemoveLineBreakCount( nEndIndex ); 873 //if the below condition is true it indicates an empty paragraph with just a "\n" 874 //so we need to set one "\n" flag to avoid duplication. 875 if ( bStart && bEnd && ( nStartIndex == nEndIndex) ) 876 { 877 bEnd = sal_False; 878 } 879 //if the current endindex is at a "\n", we need to increase endindex by 1 to make sure 880 //the char before "\n" is included. Because string returned by this function will not include 881 //the char at the endindex. 882 if ( bTemp ) 883 { 884 nEndIndex++; 885 } 886 ::rtl::OUString aRes; 887 EPosition aStartIndex( mpImpl->Range2Internal(nStartIndex) ); 888 EPosition aEndIndex( mpImpl->Range2Internal(nEndIndex) ); 889 890 // #102170# Special case: start and end paragraph are identical 891 if( aStartIndex.nPara == aEndIndex.nPara ) 892 { 893 //we don't return the string directly now for that we have to do some further process for "\n" 894 aRes = mpImpl->GetParagraph( aStartIndex.nPara ).getTextRange( aStartIndex.nIndex, aEndIndex.nIndex ); 895 } 896 else 897 { 898 sal_Int32 i( aStartIndex.nPara ); 899 aRes = mpImpl->GetParagraph(i).getTextRange( aStartIndex.nIndex, 900 mpImpl->GetParagraph(i).getCharacterCount()/*-1*/); 901 ++i; 902 903 // paragraphs inbetween are fully included 904 for( ; i<aEndIndex.nPara; ++i ) 905 { 906 aRes += rtl::OUString(cNewLine); 907 aRes += mpImpl->GetParagraph(i).getText(); 908 } 909 910 if( i<=aEndIndex.nPara ) 911 { 912 //if the below condition is mathed it means the endindex is at mid of the last paragraph 913 //we need to add a "\n" before we add the last part of the string. 914 if ( !bEnd && aEndIndex.nIndex ) 915 { 916 aRes += rtl::OUString(cNewLine); 917 } 918 aRes += mpImpl->GetParagraph(i).getTextRange( 0, aEndIndex.nIndex ); 919 } 920 //return aRes; 921 } 922 //According the the flag we marked before, we have to add "\n" at the beginning 923 //or at the end of the result string. 924 if ( bStart ) 925 { 926 aRes = rtl::OUString(cNewLine) + aRes; 927 } 928 if ( bEnd ) 929 { 930 aRes += rtl::OUString(cNewLine); 931 } 932 return aRes; 933 } 934 935 ::com::sun::star::accessibility::TextSegment SAL_CALL AccessibleStaticTextBase::getTextAtIndex( sal_Int32 nIndex, sal_Int16 aTextType ) throw (::com::sun::star::lang::IndexOutOfBoundsException, ::com::sun::star::lang::IllegalArgumentException, ::com::sun::star::uno::RuntimeException) 936 { 937 ::vos::OGuard aGuard( Application::GetSolarMutex() ); 938 939 sal_Bool bLineBreak = mpImpl->RemoveLineBreakCount( nIndex ); 940 EPosition aPos( mpImpl->Range2Internal(nIndex) ); 941 942 ::com::sun::star::accessibility::TextSegment aResult; 943 944 if( AccessibleTextType::PARAGRAPH == aTextType ) 945 { 946 // #106393# Special casing one behind last paragraph is 947 // not necessary, since then, we return the content and 948 // boundary of that last paragraph. Range2Internal is 949 // tolerant against that, and returns the last paragraph 950 // in aPos.nPara. 951 952 // retrieve full text of the paragraph 953 aResult.SegmentText = mpImpl->GetParagraph( aPos.nPara ).getText(); 954 955 // #112814# Adapt the start index with the paragraph offset 956 aResult.SegmentStart = mpImpl->Internal2Index( EPosition( aPos.nPara, 0 ) ); 957 aResult.SegmentEnd = aResult.SegmentStart + aResult.SegmentText.getLength(); 958 } 959 else if ( AccessibleTextType::ATTRIBUTE_RUN == aTextType ) 960 { 961 SvxAccessibleTextAdapter& rTextForwarder = mpImpl->GetParagraph( aPos.nIndex ).GetTextForwarder(); 962 sal_uInt16 nStartIndex, nEndIndex; 963 if ( rTextForwarder.GetAttributeRun( nStartIndex, nEndIndex, aPos.nPara, aPos.nIndex, sal_True ) ) 964 { 965 aResult.SegmentText = getTextRange( nStartIndex, nEndIndex ); 966 aResult.SegmentStart = nStartIndex; 967 aResult.SegmentEnd = nEndIndex; 968 } 969 } 970 else 971 { 972 // No special handling required, forward to wrapped class 973 aResult = mpImpl->GetParagraph( aPos.nPara ).getTextAtIndex( aPos.nIndex, aTextType ); 974 975 // #112814# Adapt the start index with the paragraph offset 976 mpImpl->CorrectTextSegment( aResult, aPos.nPara ); 977 if ( bLineBreak ) 978 { 979 aResult.SegmentText = rtl::OUString(cNewLine); 980 } 981 } 982 983 return aResult; 984 } 985 986 ::com::sun::star::accessibility::TextSegment SAL_CALL AccessibleStaticTextBase::getTextBeforeIndex( sal_Int32 nIndex, sal_Int16 aTextType ) throw (::com::sun::star::lang::IndexOutOfBoundsException, ::com::sun::star::lang::IllegalArgumentException, ::com::sun::star::uno::RuntimeException) 987 { 988 ::vos::OGuard aGuard( Application::GetSolarMutex() ); 989 sal_Int32 nOldIdx = nIndex; 990 sal_Bool bLineBreak = mpImpl->RemoveLineBreakCount( nIndex ); 991 EPosition aPos( mpImpl->Range2Internal(nIndex) ); 992 993 ::com::sun::star::accessibility::TextSegment aResult; 994 995 if( AccessibleTextType::PARAGRAPH == aTextType ) 996 { 997 if( aPos.nIndex == mpImpl->GetParagraph( aPos.nPara ).getCharacterCount() ) 998 { 999 // #103589# Special casing one behind the last paragraph 1000 aResult.SegmentText = mpImpl->GetParagraph( aPos.nPara ).getText(); 1001 1002 // #112814# Adapt the start index with the paragraph offset 1003 aResult.SegmentStart = mpImpl->Internal2Index( EPosition( aPos.nPara, 0 ) ); 1004 } 1005 else if( aPos.nPara > 0 ) 1006 { 1007 aResult.SegmentText = mpImpl->GetParagraph( aPos.nPara - 1 ).getText(); 1008 1009 // #112814# Adapt the start index with the paragraph offset 1010 aResult.SegmentStart = mpImpl->Internal2Index( EPosition( aPos.nPara - 1, 0 ) ); 1011 } 1012 1013 aResult.SegmentEnd = aResult.SegmentStart + aResult.SegmentText.getLength(); 1014 } 1015 else 1016 { 1017 // No special handling required, forward to wrapped class 1018 aResult = mpImpl->GetParagraph( aPos.nPara ).getTextBeforeIndex( aPos.nIndex, aTextType ); 1019 1020 // #112814# Adapt the start index with the paragraph offset 1021 mpImpl->CorrectTextSegment( aResult, aPos.nPara ); 1022 if ( bLineBreak && (nOldIdx-1) >= 0) 1023 { 1024 aResult = getTextAtIndex( nOldIdx-1, aTextType ); 1025 } 1026 } 1027 1028 return aResult; 1029 } 1030 1031 ::com::sun::star::accessibility::TextSegment SAL_CALL AccessibleStaticTextBase::getTextBehindIndex( sal_Int32 nIndex, sal_Int16 aTextType ) throw (::com::sun::star::lang::IndexOutOfBoundsException, ::com::sun::star::lang::IllegalArgumentException, ::com::sun::star::uno::RuntimeException) 1032 { 1033 ::vos::OGuard aGuard( Application::GetSolarMutex() ); 1034 sal_Int32 nTemp = nIndex+1; 1035 sal_Bool bLineBreak = mpImpl->RemoveLineBreakCount( nTemp ); 1036 mpImpl->RemoveLineBreakCount( nIndex ); 1037 EPosition aPos( mpImpl->Range2Internal(nIndex) ); 1038 1039 ::com::sun::star::accessibility::TextSegment aResult; 1040 1041 if( AccessibleTextType::PARAGRAPH == aTextType ) 1042 { 1043 // Special casing one behind the last paragraph is not 1044 // necessary, this case is invalid here for 1045 // getTextBehindIndex 1046 if( aPos.nPara + 1 < mpImpl->GetParagraphCount() ) 1047 { 1048 aResult.SegmentText = mpImpl->GetParagraph( aPos.nPara + 1 ).getText(); 1049 1050 // #112814# Adapt the start index with the paragraph offset 1051 aResult.SegmentStart = mpImpl->Internal2Index( EPosition( aPos.nPara + 1, 0 ) ); 1052 aResult.SegmentEnd = aResult.SegmentStart + aResult.SegmentText.getLength(); 1053 } 1054 } 1055 else 1056 { 1057 // No special handling required, forward to wrapped class 1058 aResult = mpImpl->GetParagraph( aPos.nPara ).getTextBehindIndex( aPos.nIndex, aTextType ); 1059 1060 // #112814# Adapt the start index with the paragraph offset 1061 mpImpl->CorrectTextSegment( aResult, aPos.nPara ); 1062 if ( bLineBreak ) 1063 { 1064 aResult.SegmentText = rtl::OUString(cNewLine) + aResult.SegmentText; 1065 } 1066 } 1067 1068 return aResult; 1069 } 1070 1071 sal_Bool SAL_CALL AccessibleStaticTextBase::copyText( sal_Int32 nStartIndex, sal_Int32 nEndIndex ) throw (lang::IndexOutOfBoundsException, uno::RuntimeException) 1072 { 1073 ::vos::OGuard aGuard( Application::GetSolarMutex() ); 1074 1075 if( nStartIndex > nEndIndex ) 1076 ::std::swap(nStartIndex, nEndIndex); 1077 1078 EPosition aStartIndex( mpImpl->Range2Internal(nStartIndex) ); 1079 EPosition aEndIndex( mpImpl->Range2Internal(nEndIndex) ); 1080 1081 return mpImpl->CopyText( aStartIndex.nPara, aStartIndex.nIndex, 1082 aEndIndex.nPara, aEndIndex.nIndex ); 1083 } 1084 1085 // XAccessibleTextAttributes 1086 uno::Sequence< beans::PropertyValue > AccessibleStaticTextBase::getDefaultAttributes( const uno::Sequence< ::rtl::OUString >& RequestedAttributes ) throw (uno::RuntimeException) 1087 { 1088 // get the intersection of the default attributes of all paragraphs 1089 1090 ::vos::OGuard aGuard( Application::GetSolarMutex() ); 1091 1092 PropertyValueVector aDefAttrVec( mpImpl->GetParagraph( 0 ).getDefaultAttributes( RequestedAttributes ) ); 1093 1094 const sal_Int32 nParaCount = mpImpl->GetParagraphCount(); 1095 for ( sal_Int32 nPara = 1; nPara < nParaCount; ++nPara ) 1096 { 1097 uno::Sequence< beans::PropertyValue > aSeq = mpImpl->GetParagraph( nPara ).getDefaultAttributes( RequestedAttributes ); 1098 PropertyValueVector aIntersectionVec; 1099 1100 PropertyValueVector::const_iterator aEnd = aDefAttrVec.end(); 1101 for ( PropertyValueVector::const_iterator aItr = aDefAttrVec.begin(); aItr != aEnd; ++aItr ) 1102 { 1103 const beans::PropertyValue* pItr = aSeq.getConstArray(); 1104 const beans::PropertyValue* pEnd = pItr + aSeq.getLength(); 1105 const beans::PropertyValue* pFind = ::std::find_if( pItr, pEnd, ::std::bind2nd( PropertyValueEqualFunctor(), boost::cref( *aItr ) ) ); 1106 if ( pFind != pEnd ) 1107 { 1108 aIntersectionVec.push_back( *pFind ); 1109 } 1110 } 1111 1112 aDefAttrVec.swap( aIntersectionVec ); 1113 1114 if ( aDefAttrVec.empty() ) 1115 { 1116 break; 1117 } 1118 } 1119 1120 return aDefAttrVec.getAsConstList(); 1121 } 1122 1123 uno::Sequence< beans::PropertyValue > SAL_CALL AccessibleStaticTextBase::getRunAttributes( sal_Int32 nIndex, const uno::Sequence< ::rtl::OUString >& RequestedAttributes ) throw (lang::IndexOutOfBoundsException, uno::RuntimeException) 1124 { 1125 // get those default attributes of the paragraph, which are not part 1126 // of the intersection of all paragraphs and add them to the run attributes 1127 1128 ::vos::OGuard aGuard( Application::GetSolarMutex() ); 1129 1130 EPosition aPos( mpImpl->Index2Internal( nIndex ) ); 1131 AccessibleEditableTextPara& rPara = mpImpl->GetParagraph( aPos.nPara ); 1132 uno::Sequence< beans::PropertyValue > aDefAttrSeq = rPara.getDefaultAttributes( RequestedAttributes ); 1133 uno::Sequence< beans::PropertyValue > aRunAttrSeq = rPara.getRunAttributes( aPos.nIndex, RequestedAttributes ); 1134 uno::Sequence< beans::PropertyValue > aIntersectionSeq = getDefaultAttributes( RequestedAttributes ); 1135 PropertyValueVector aDiffVec; 1136 1137 const beans::PropertyValue* pDefAttr = aDefAttrSeq.getConstArray(); 1138 const sal_Int32 nLength = aDefAttrSeq.getLength(); 1139 for ( sal_Int32 i = 0; i < nLength; ++i ) 1140 { 1141 const beans::PropertyValue* pItr = aIntersectionSeq.getConstArray(); 1142 const beans::PropertyValue* pEnd = pItr + aIntersectionSeq.getLength(); 1143 const beans::PropertyValue* pFind = ::std::find_if( pItr, pEnd, ::std::bind2nd( PropertyValueEqualFunctor(), boost::cref( pDefAttr[i] ) ) ); 1144 if ( pFind == pEnd && pDefAttr[i].Handle != 0) 1145 { 1146 aDiffVec.push_back( pDefAttr[i] ); 1147 } 1148 } 1149 1150 return ::comphelper::concatSequences( aRunAttrSeq, aDiffVec.getAsConstList() ); 1151 } 1152 1153 Rectangle AccessibleStaticTextBase::GetParagraphBoundingBox() const 1154 { 1155 return mpImpl->GetParagraphBoundingBox(); 1156 } 1157 1158 sal_Int32 AccessibleStaticTextBase::GetParagraphIndex() const 1159 { 1160 return mpImpl->GetParagraphIndex(); 1161 } 1162 1163 sal_Int32 AccessibleStaticTextBase::GetParagraphCount() const 1164 { 1165 return mpImpl->GetParagraphCount(); 1166 } 1167 1168 sal_Int32 AccessibleStaticTextBase::GetLineCount( sal_Int32 nParagraph ) const 1169 { 1170 return mpImpl->GetLineCount( nParagraph ); 1171 } 1172 1173 } // end of namespace accessibility 1174 1175 //------------------------------------------------------------------------ 1176