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 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 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 198 private: 199 200 EPosition ImpCalcInternal( sal_Int32 nFlatIndex, bool bExclusive ) const; 201 202 // our frontend class (the one implementing the actual 203 // interface). That's not necessarily the one containing the impl 204 // pointer 205 uno::Reference< XAccessible > mxThis; 206 207 // implements our functionality, we're just an adapter (guarded by solar mutex) 208 mutable AccessibleEditableTextPara* mpTextParagraph; 209 210 uno::Reference< XAccessible > mxParagraph; 211 212 // a wrapper for the text forwarders (guarded by solar mutex) 213 mutable SvxEditSourceAdapter maEditSource; 214 215 // guard for maOffset 216 mutable ::osl::Mutex maMutex; 217 218 /// our current offset to the containing shape/cell (guarded by maMutex) 219 Point maOffset; 220 221 }; 222 223 //------------------------------------------------------------------------ 224 // 225 // AccessibleStaticTextBase_Impl implementation 226 // 227 //------------------------------------------------------------------------ 228 229 AccessibleStaticTextBase_Impl::AccessibleStaticTextBase_Impl() : 230 mxThis( NULL ), 231 mpTextParagraph( new AccessibleEditableTextPara(NULL) ), 232 mxParagraph( mpTextParagraph ), 233 maEditSource(), 234 maMutex(), 235 maOffset(0,0) 236 { 237 DBG_CTOR( AccessibleStaticTextBase_Impl, NULL ); 238 239 // TODO: this is still somewhat of a hack, all the more since 240 // now the maTextParagraph has an empty parent reference set 241 } 242 243 AccessibleStaticTextBase_Impl::~AccessibleStaticTextBase_Impl() 244 { 245 DBG_DTOR( AccessibleStaticTextBase_Impl, NULL ); 246 } 247 248 void AccessibleStaticTextBase_Impl::SetEditSource( ::std::auto_ptr< SvxEditSource > pEditSource ) SAL_THROW((uno::RuntimeException)) 249 { 250 DBG_CHKTHIS( AccessibleStaticTextBase_Impl, NULL ); 251 252 maEditSource.SetEditSource( pEditSource ); 253 if( mpTextParagraph ) 254 mpTextParagraph->SetEditSource( &maEditSource ); 255 } 256 257 void AccessibleStaticTextBase_Impl::SetOffset( const Point& rPoint ) 258 { 259 DBG_CHKTHIS( AccessibleStaticTextBase_Impl, NULL ); 260 261 // guard against non-atomic access to maOffset data structure 262 { 263 ::osl::MutexGuard aGuard( maMutex ); 264 maOffset = rPoint; 265 } 266 267 if( mpTextParagraph ) 268 mpTextParagraph->SetEEOffset( rPoint ); 269 270 // in all cases, check visibility afterwards. 271 UpdateChildren(); 272 } 273 274 void AccessibleStaticTextBase_Impl::UpdateChildren() 275 { 276 DBG_CHKTHIS( AccessibleStaticTextBase_Impl, NULL ); 277 278 // currently no children 279 } 280 281 void AccessibleStaticTextBase_Impl::Dispose() 282 { 283 DBG_CHKTHIS( AccessibleStaticTextBase_Impl, NULL ); 284 285 // we're the owner of the paragraph, so destroy it, too 286 if( mpTextParagraph ) 287 mpTextParagraph->Dispose(); 288 289 // drop references 290 mxParagraph = NULL; 291 mxThis = NULL; 292 mpTextParagraph = NULL; 293 } 294 295 #ifdef DBG_UTIL 296 void AccessibleStaticTextBase_Impl::CheckInvariants() const 297 { 298 // TODO 299 } 300 #endif 301 302 AccessibleEditableTextPara& AccessibleStaticTextBase_Impl::GetParagraph( sal_Int32 nPara ) const 303 { 304 DBG_CHKTHIS( AccessibleStaticTextBase_Impl, NULL ); 305 306 if( !mpTextParagraph ) 307 throw lang::DisposedException ( 308 ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("object has been already disposed")), mxThis ); 309 310 // TODO: Have a differnt method on AccessibleEditableTextPara 311 // that does not care about state changes 312 mpTextParagraph->SetParagraphIndex( nPara ); 313 314 return *mpTextParagraph; 315 } 316 317 sal_Int32 AccessibleStaticTextBase_Impl::GetParagraphCount() const 318 { 319 DBG_CHKTHIS( AccessibleStaticTextBase_Impl, NULL ); 320 321 if( !mpTextParagraph ) 322 return 0; 323 else 324 return mpTextParagraph->GetTextForwarder().GetParagraphCount(); 325 } 326 327 sal_Int32 AccessibleStaticTextBase_Impl::GetParagraphIndex() const 328 { 329 DBG_CHKTHIS( AccessibleStaticTextBase_Impl, NULL ); 330 331 sal_Int32 nIndex = -1; 332 if( mpTextParagraph ) 333 nIndex = mpTextParagraph->GetParagraphIndex(); 334 return nIndex; 335 } 336 337 sal_Int32 AccessibleStaticTextBase_Impl::GetLineCount( sal_Int32 nParagraph ) const 338 { 339 DBG_CHKTHIS( AccessibleStaticTextBase_Impl, NULL ); 340 341 sal_Int32 nIndex = 0; 342 if( mpTextParagraph ) 343 nIndex = mpTextParagraph->GetTextForwarder().GetLineCount( static_cast< sal_uInt16 >(nParagraph) ); 344 return nIndex; 345 } 346 347 sal_Int32 AccessibleStaticTextBase_Impl::Internal2Index( EPosition nEEIndex ) const 348 { 349 sal_Int32 aRes(0); 350 int i; 351 for(i=0; i<nEEIndex.nPara; ++i) 352 aRes += GetParagraph(i).getCharacterCount(); 353 354 return aRes + nEEIndex.nIndex; 355 } 356 357 void AccessibleStaticTextBase_Impl::CorrectTextSegment( TextSegment& aTextSegment, 358 int nPara ) const 359 { 360 // Keep 'invalid' values at the TextSegment 361 if( aTextSegment.SegmentStart != -1 && 362 aTextSegment.SegmentStart != -1 ) 363 { 364 // #112814# Correct TextSegment by paragraph offset 365 sal_Int32 nOffset(0); 366 int i; 367 for(i=0; i<nPara; ++i) 368 nOffset += GetParagraph(i).getCharacterCount(); 369 370 aTextSegment.SegmentStart += nOffset; 371 aTextSegment.SegmentEnd += nOffset; 372 } 373 } 374 375 EPosition AccessibleStaticTextBase_Impl::ImpCalcInternal( sal_Int32 nFlatIndex, bool bExclusive ) const 376 { 377 DBG_CHKTHIS( AccessibleStaticTextBase_Impl, NULL ); 378 379 if( nFlatIndex < 0 ) 380 throw lang::IndexOutOfBoundsException(::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("AccessibleStaticTextBase_Impl::Index2Internal: character index out of bounds")), 381 mxThis); 382 // gratuitously accepting larger indices here, AccessibleEditableTextPara will throw eventually 383 384 sal_Int32 nCurrPara, nCurrIndex, nParas, nCurrCount; 385 for( nCurrPara=0, nParas=GetParagraphCount(), nCurrCount=0, nCurrIndex=0; nCurrPara<nParas; ++nCurrPara ) 386 { 387 nCurrCount = GetParagraph( nCurrPara ).getCharacterCount(); 388 nCurrIndex += nCurrCount; 389 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 481 //------------------------------------------------------------------------ 482 // 483 // AccessibleStaticTextBase implementation 484 // 485 //------------------------------------------------------------------------ 486 487 AccessibleStaticTextBase::AccessibleStaticTextBase( ::std::auto_ptr< SvxEditSource > pEditSource ) : 488 mpImpl( new AccessibleStaticTextBase_Impl() ) 489 { 490 ::vos::OGuard aGuard( Application::GetSolarMutex() ); 491 492 SetEditSource( pEditSource ); 493 } 494 495 AccessibleStaticTextBase::~AccessibleStaticTextBase() 496 { 497 } 498 499 const SvxEditSource& AccessibleStaticTextBase::GetEditSource() const SAL_THROW((::com::sun::star::uno::RuntimeException)) 500 { 501 #ifdef DBG_UTIL 502 mpImpl->CheckInvariants(); 503 504 const SvxEditSource& aEditSource = mpImpl->GetEditSource(); 505 506 mpImpl->CheckInvariants(); 507 508 return aEditSource; 509 #else 510 return mpImpl->GetEditSource(); 511 #endif 512 } 513 514 void AccessibleStaticTextBase::SetEditSource( ::std::auto_ptr< SvxEditSource > pEditSource ) SAL_THROW((::com::sun::star::uno::RuntimeException)) 515 { 516 #ifdef DBG_UTIL 517 // precondition: solar mutex locked 518 DBG_TESTSOLARMUTEX(); 519 520 mpImpl->CheckInvariants(); 521 522 mpImpl->SetEditSource( pEditSource ); 523 524 mpImpl->CheckInvariants(); 525 #else 526 mpImpl->SetEditSource( pEditSource ); 527 #endif 528 } 529 530 void AccessibleStaticTextBase::SetEventSource( const uno::Reference< XAccessible >& rInterface ) 531 { 532 #ifdef DBG_UTIL 533 mpImpl->CheckInvariants(); 534 #endif 535 536 mpImpl->SetEventSource( rInterface ); 537 538 #ifdef DBG_UTIL 539 mpImpl->CheckInvariants(); 540 #endif 541 } 542 543 uno::Reference< XAccessible > AccessibleStaticTextBase::GetEventSource() const 544 { 545 #ifdef DBG_UTIL 546 mpImpl->CheckInvariants(); 547 548 uno::Reference< XAccessible > xRet( mpImpl->GetEventSource() ); 549 550 mpImpl->CheckInvariants(); 551 552 return xRet; 553 #else 554 return mpImpl->GetEventSource(); 555 #endif 556 } 557 558 void AccessibleStaticTextBase::SetOffset( const Point& rPoint ) 559 { 560 #ifdef DBG_UTIL 561 // precondition: solar mutex locked 562 DBG_TESTSOLARMUTEX(); 563 564 mpImpl->CheckInvariants(); 565 566 mpImpl->SetOffset( rPoint ); 567 568 mpImpl->CheckInvariants(); 569 #else 570 mpImpl->SetOffset( rPoint ); 571 #endif 572 } 573 574 Point AccessibleStaticTextBase::GetOffset() const 575 { 576 #ifdef DBG_UTIL 577 mpImpl->CheckInvariants(); 578 579 Point aPoint( mpImpl->GetOffset() ); 580 581 mpImpl->CheckInvariants(); 582 583 return aPoint; 584 #else 585 return mpImpl->GetOffset(); 586 #endif 587 } 588 589 void AccessibleStaticTextBase::UpdateChildren() SAL_THROW((::com::sun::star::uno::RuntimeException)) 590 { 591 #ifdef DBG_UTIL 592 // precondition: solar mutex locked 593 DBG_TESTSOLARMUTEX(); 594 595 mpImpl->CheckInvariants(); 596 597 mpImpl->UpdateChildren(); 598 599 mpImpl->CheckInvariants(); 600 #else 601 mpImpl->UpdateChildren(); 602 #endif 603 } 604 605 void AccessibleStaticTextBase::Dispose() 606 { 607 #ifdef DBG_UTIL 608 mpImpl->CheckInvariants(); 609 #endif 610 611 mpImpl->Dispose(); 612 613 #ifdef DBG_UTIL 614 mpImpl->CheckInvariants(); 615 #endif 616 } 617 618 // XAccessibleContext 619 sal_Int32 SAL_CALL AccessibleStaticTextBase::getAccessibleChildCount() throw (uno::RuntimeException) 620 { 621 // no children at all 622 return 0; 623 } 624 625 uno::Reference< XAccessible > SAL_CALL AccessibleStaticTextBase::getAccessibleChild( sal_Int32 /*i*/ ) throw (lang::IndexOutOfBoundsException, uno::RuntimeException) 626 { 627 // no children at all 628 return uno::Reference< XAccessible >(); 629 } 630 631 uno::Reference< XAccessible > SAL_CALL AccessibleStaticTextBase::getAccessibleAtPoint( const awt::Point& /*_aPoint*/ ) throw (uno::RuntimeException) 632 { 633 // no children at all 634 return uno::Reference< XAccessible >(); 635 } 636 637 // XAccessibleText 638 sal_Int32 SAL_CALL AccessibleStaticTextBase::getCaretPosition() throw (uno::RuntimeException) 639 { 640 ::vos::OGuard aGuard( Application::GetSolarMutex() ); 641 642 sal_Int32 i, nPos, nParas; 643 for( i=0, nPos=-1, nParas=mpImpl->GetParagraphCount(); i<nParas; ++i ) 644 { 645 if( (nPos=mpImpl->GetParagraph(i).getCaretPosition()) != -1 ) 646 return nPos; 647 } 648 649 return nPos; 650 } 651 652 sal_Bool SAL_CALL AccessibleStaticTextBase::setCaretPosition( sal_Int32 nIndex ) throw (lang::IndexOutOfBoundsException, uno::RuntimeException) 653 { 654 return setSelection(nIndex, nIndex); 655 } 656 657 sal_Unicode SAL_CALL AccessibleStaticTextBase::getCharacter( sal_Int32 nIndex ) throw (lang::IndexOutOfBoundsException, uno::RuntimeException) 658 { 659 ::vos::OGuard aGuard( Application::GetSolarMutex() ); 660 661 EPosition aPos( mpImpl->Index2Internal(nIndex) ); 662 663 return mpImpl->GetParagraph( aPos.nPara ).getCharacter( aPos.nIndex ); 664 } 665 666 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) 667 { 668 ::vos::OGuard aGuard( Application::GetSolarMutex() ); 669 670 EPosition aPos( mpImpl->Index2Internal(nIndex) ); 671 672 return mpImpl->GetParagraph( aPos.nPara ).getCharacterAttributes( aPos.nIndex, aRequestedAttributes ); 673 } 674 675 awt::Rectangle SAL_CALL AccessibleStaticTextBase::getCharacterBounds( sal_Int32 nIndex ) throw (lang::IndexOutOfBoundsException, uno::RuntimeException) 676 { 677 ::vos::OGuard aGuard( Application::GetSolarMutex() ); 678 679 // #108900# Allow ranges for nIndex, as one-past-the-end 680 // values are now legal, too. 681 EPosition aPos( mpImpl->Range2Internal(nIndex) ); 682 683 // #i70916# Text in spread sheet cells return the wrong extents 684 AccessibleEditableTextPara& rPara = mpImpl->GetParagraph( aPos.nPara ); 685 awt::Rectangle aParaBounds( rPara.getBounds() ); 686 awt::Rectangle aBounds( rPara.getCharacterBounds( aPos.nIndex ) ); 687 aBounds.X += aParaBounds.X; 688 aBounds.Y += aParaBounds.Y; 689 690 return aBounds; 691 } 692 693 sal_Int32 SAL_CALL AccessibleStaticTextBase::getCharacterCount() throw (uno::RuntimeException) 694 { 695 ::vos::OGuard aGuard( Application::GetSolarMutex() ); 696 697 sal_Int32 i, nCount, nParas; 698 for( i=0, nCount=0, nParas=mpImpl->GetParagraphCount(); i<nParas; ++i ) 699 nCount += mpImpl->GetParagraph(i).getCharacterCount(); 700 701 return nCount; 702 } 703 704 sal_Int32 SAL_CALL AccessibleStaticTextBase::getIndexAtPoint( const awt::Point& rPoint ) throw (uno::RuntimeException) 705 { 706 ::vos::OGuard aGuard( Application::GetSolarMutex() ); 707 708 const sal_Int32 nParas( mpImpl->GetParagraphCount() ); 709 sal_Int32 nIndex; 710 int i; 711 for( i=0; i<nParas; ++i ) 712 { 713 // TODO: maybe exploit the fact that paragraphs are 714 // ordered vertically for early exit 715 716 // #i70916# Text in spread sheet cells return the wrong extents 717 AccessibleEditableTextPara& rPara = mpImpl->GetParagraph( i ); 718 awt::Rectangle aParaBounds( rPara.getBounds() ); 719 awt::Point aPoint( rPoint ); 720 aPoint.X -= aParaBounds.X; 721 aPoint.Y -= aParaBounds.Y; 722 723 // #112814# Use correct index offset 724 if ( ( nIndex = rPara.getIndexAtPoint( aPoint ) ) != -1 ) 725 return mpImpl->Internal2Index( EPosition(sal::static_int_cast<sal_uInt16>(i), 726 sal::static_int_cast<sal_uInt16>(nIndex)) ); 727 } 728 729 return -1; 730 } 731 732 ::rtl::OUString SAL_CALL AccessibleStaticTextBase::getSelectedText() throw (uno::RuntimeException) 733 { 734 ::vos::OGuard aGuard( Application::GetSolarMutex() ); 735 736 sal_Int32 nStart( getSelectionStart() ); 737 sal_Int32 nEnd( getSelectionEnd() ); 738 739 // #104481# Return the empty string for 'no selection' 740 if( nStart < 0 || nEnd < 0 ) 741 return ::rtl::OUString(); 742 743 return getTextRange( nStart, nEnd ); 744 } 745 746 sal_Int32 SAL_CALL AccessibleStaticTextBase::getSelectionStart() throw (uno::RuntimeException) 747 { 748 ::vos::OGuard aGuard( Application::GetSolarMutex() ); 749 750 sal_Int32 i, nPos, nParas; 751 for( i=0, nPos=-1, nParas=mpImpl->GetParagraphCount(); i<nParas; ++i ) 752 { 753 if( (nPos=mpImpl->GetParagraph(i).getSelectionStart()) != -1 ) 754 return nPos; 755 } 756 757 return nPos; 758 } 759 760 sal_Int32 SAL_CALL AccessibleStaticTextBase::getSelectionEnd() throw (uno::RuntimeException) 761 { 762 ::vos::OGuard aGuard( Application::GetSolarMutex() ); 763 764 sal_Int32 i, nPos, nParas; 765 for( i=0, nPos=-1, nParas=mpImpl->GetParagraphCount(); i<nParas; ++i ) 766 { 767 if( (nPos=mpImpl->GetParagraph(i).getSelectionEnd()) != -1 ) 768 return nPos; 769 } 770 771 return nPos; 772 } 773 774 sal_Bool SAL_CALL AccessibleStaticTextBase::setSelection( sal_Int32 nStartIndex, sal_Int32 nEndIndex ) throw (lang::IndexOutOfBoundsException, uno::RuntimeException) 775 { 776 ::vos::OGuard aGuard( Application::GetSolarMutex() ); 777 778 EPosition aStartIndex( mpImpl->Range2Internal(nStartIndex) ); 779 EPosition aEndIndex( mpImpl->Range2Internal(nEndIndex) ); 780 781 return mpImpl->SetSelection( aStartIndex.nPara, aStartIndex.nIndex, 782 aEndIndex.nPara, aEndIndex.nIndex ); 783 } 784 785 ::rtl::OUString SAL_CALL AccessibleStaticTextBase::getText() throw (uno::RuntimeException) 786 { 787 ::vos::OGuard aGuard( Application::GetSolarMutex() ); 788 789 sal_Int32 i, nParas; 790 ::rtl::OUString aRes; 791 for( i=0, nParas=mpImpl->GetParagraphCount(); i<nParas; ++i ) 792 aRes += mpImpl->GetParagraph(i).getText(); 793 794 return aRes; 795 } 796 797 ::rtl::OUString SAL_CALL AccessibleStaticTextBase::getTextRange( sal_Int32 nStartIndex, sal_Int32 nEndIndex ) throw (lang::IndexOutOfBoundsException, uno::RuntimeException) 798 { 799 ::vos::OGuard aGuard( Application::GetSolarMutex() ); 800 801 if( nStartIndex > nEndIndex ) 802 ::std::swap(nStartIndex, nEndIndex); 803 804 EPosition aStartIndex( mpImpl->Range2Internal(nStartIndex) ); 805 EPosition aEndIndex( mpImpl->Range2Internal(nEndIndex) ); 806 807 // #102170# Special case: start and end paragraph are identical 808 if( aStartIndex.nPara == aEndIndex.nPara ) 809 { 810 return mpImpl->GetParagraph( aStartIndex.nPara ).getTextRange( aStartIndex.nIndex, aEndIndex.nIndex ); 811 } 812 else 813 { 814 sal_Int32 i( aStartIndex.nPara ); 815 ::rtl::OUString aRes( mpImpl->GetParagraph(i).getTextRange( aStartIndex.nIndex, 816 mpImpl->GetParagraph(i).getCharacterCount()-1) ); 817 ++i; 818 819 // paragraphs inbetween are fully included 820 for( ; i<aEndIndex.nPara; ++i ) 821 aRes += mpImpl->GetParagraph(i).getText(); 822 823 if( i<=aEndIndex.nPara ) 824 aRes += mpImpl->GetParagraph(i).getTextRange( 0, aEndIndex.nIndex ); 825 826 return aRes; 827 } 828 } 829 830 ::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) 831 { 832 ::vos::OGuard aGuard( Application::GetSolarMutex() ); 833 834 EPosition aPos( mpImpl->Range2Internal(nIndex) ); 835 836 ::com::sun::star::accessibility::TextSegment aResult; 837 838 if( AccessibleTextType::PARAGRAPH == aTextType ) 839 { 840 // #106393# Special casing one behind last paragraph is 841 // not necessary, since then, we return the content and 842 // boundary of that last paragraph. Range2Internal is 843 // tolerant against that, and returns the last paragraph 844 // in aPos.nPara. 845 846 // retrieve full text of the paragraph 847 aResult.SegmentText = mpImpl->GetParagraph( aPos.nPara ).getText(); 848 849 // #112814# Adapt the start index with the paragraph offset 850 aResult.SegmentStart = mpImpl->Internal2Index( EPosition( aPos.nPara, 0 ) ); 851 aResult.SegmentEnd = aResult.SegmentStart + aResult.SegmentText.getLength(); 852 } 853 else 854 { 855 // No special handling required, forward to wrapped class 856 aResult = mpImpl->GetParagraph( aPos.nPara ).getTextAtIndex( aPos.nIndex, aTextType ); 857 858 // #112814# Adapt the start index with the paragraph offset 859 mpImpl->CorrectTextSegment( aResult, aPos.nPara ); 860 } 861 862 return aResult; 863 } 864 865 ::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) 866 { 867 ::vos::OGuard aGuard( Application::GetSolarMutex() ); 868 869 EPosition aPos( mpImpl->Range2Internal(nIndex) ); 870 871 ::com::sun::star::accessibility::TextSegment aResult; 872 873 if( AccessibleTextType::PARAGRAPH == aTextType ) 874 { 875 if( aPos.nIndex == mpImpl->GetParagraph( aPos.nPara ).getCharacterCount() ) 876 { 877 // #103589# Special casing one behind the last paragraph 878 aResult.SegmentText = mpImpl->GetParagraph( aPos.nPara ).getText(); 879 880 // #112814# Adapt the start index with the paragraph offset 881 aResult.SegmentStart = mpImpl->Internal2Index( EPosition( aPos.nPara, 0 ) ); 882 } 883 else if( aPos.nPara > 0 ) 884 { 885 aResult.SegmentText = mpImpl->GetParagraph( aPos.nPara - 1 ).getText(); 886 887 // #112814# Adapt the start index with the paragraph offset 888 aResult.SegmentStart = mpImpl->Internal2Index( EPosition( aPos.nPara - 1, 0 ) ); 889 } 890 891 aResult.SegmentEnd = aResult.SegmentStart + aResult.SegmentText.getLength(); 892 } 893 else 894 { 895 // No special handling required, forward to wrapped class 896 aResult = mpImpl->GetParagraph( aPos.nPara ).getTextBeforeIndex( aPos.nIndex, aTextType ); 897 898 // #112814# Adapt the start index with the paragraph offset 899 mpImpl->CorrectTextSegment( aResult, aPos.nPara ); 900 } 901 902 return aResult; 903 } 904 905 ::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) 906 { 907 ::vos::OGuard aGuard( Application::GetSolarMutex() ); 908 909 EPosition aPos( mpImpl->Range2Internal(nIndex) ); 910 911 ::com::sun::star::accessibility::TextSegment aResult; 912 913 if( AccessibleTextType::PARAGRAPH == aTextType ) 914 { 915 // Special casing one behind the last paragraph is not 916 // necessary, this case is invalid here for 917 // getTextBehindIndex 918 if( aPos.nPara + 1 < mpImpl->GetParagraphCount() ) 919 { 920 aResult.SegmentText = mpImpl->GetParagraph( aPos.nPara + 1 ).getText(); 921 922 // #112814# Adapt the start index with the paragraph offset 923 aResult.SegmentStart = mpImpl->Internal2Index( EPosition( aPos.nPara + 1, 0 ) ); 924 aResult.SegmentEnd = aResult.SegmentStart + aResult.SegmentText.getLength(); 925 } 926 } 927 else 928 { 929 // No special handling required, forward to wrapped class 930 aResult = mpImpl->GetParagraph( aPos.nPara ).getTextBehindIndex( aPos.nIndex, aTextType ); 931 932 // #112814# Adapt the start index with the paragraph offset 933 mpImpl->CorrectTextSegment( aResult, aPos.nPara ); 934 } 935 936 return aResult; 937 } 938 939 sal_Bool SAL_CALL AccessibleStaticTextBase::copyText( sal_Int32 nStartIndex, sal_Int32 nEndIndex ) throw (lang::IndexOutOfBoundsException, uno::RuntimeException) 940 { 941 ::vos::OGuard aGuard( Application::GetSolarMutex() ); 942 943 if( nStartIndex > nEndIndex ) 944 ::std::swap(nStartIndex, nEndIndex); 945 946 EPosition aStartIndex( mpImpl->Range2Internal(nStartIndex) ); 947 EPosition aEndIndex( mpImpl->Range2Internal(nEndIndex) ); 948 949 return mpImpl->CopyText( aStartIndex.nPara, aStartIndex.nIndex, 950 aEndIndex.nPara, aEndIndex.nIndex ); 951 } 952 953 // XAccessibleTextAttributes 954 uno::Sequence< beans::PropertyValue > AccessibleStaticTextBase::getDefaultAttributes( const uno::Sequence< ::rtl::OUString >& RequestedAttributes ) throw (uno::RuntimeException) 955 { 956 // get the intersection of the default attributes of all paragraphs 957 958 ::vos::OGuard aGuard( Application::GetSolarMutex() ); 959 960 PropertyValueVector aDefAttrVec( mpImpl->GetParagraph( 0 ).getDefaultAttributes( RequestedAttributes ) ); 961 962 const sal_Int32 nParaCount = mpImpl->GetParagraphCount(); 963 for ( sal_Int32 nPara = 1; nPara < nParaCount; ++nPara ) 964 { 965 uno::Sequence< beans::PropertyValue > aSeq = mpImpl->GetParagraph( nPara ).getDefaultAttributes( RequestedAttributes ); 966 PropertyValueVector aIntersectionVec; 967 968 PropertyValueVector::const_iterator aEnd = aDefAttrVec.end(); 969 for ( PropertyValueVector::const_iterator aItr = aDefAttrVec.begin(); aItr != aEnd; ++aItr ) 970 { 971 const beans::PropertyValue* pItr = aSeq.getConstArray(); 972 const beans::PropertyValue* pEnd = pItr + aSeq.getLength(); 973 const beans::PropertyValue* pFind = ::std::find_if( pItr, pEnd, ::std::bind2nd( PropertyValueEqualFunctor(), boost::cref( *aItr ) ) ); 974 if ( pFind != pEnd ) 975 { 976 aIntersectionVec.push_back( *pFind ); 977 } 978 } 979 980 aDefAttrVec.swap( aIntersectionVec ); 981 982 if ( aDefAttrVec.empty() ) 983 { 984 break; 985 } 986 } 987 988 return aDefAttrVec.getAsConstList(); 989 } 990 991 uno::Sequence< beans::PropertyValue > SAL_CALL AccessibleStaticTextBase::getRunAttributes( sal_Int32 nIndex, const uno::Sequence< ::rtl::OUString >& RequestedAttributes ) throw (lang::IndexOutOfBoundsException, uno::RuntimeException) 992 { 993 // get those default attributes of the paragraph, which are not part 994 // of the intersection of all paragraphs and add them to the run attributes 995 996 ::vos::OGuard aGuard( Application::GetSolarMutex() ); 997 998 EPosition aPos( mpImpl->Index2Internal( nIndex ) ); 999 AccessibleEditableTextPara& rPara = mpImpl->GetParagraph( aPos.nPara ); 1000 uno::Sequence< beans::PropertyValue > aDefAttrSeq = rPara.getDefaultAttributes( RequestedAttributes ); 1001 uno::Sequence< beans::PropertyValue > aRunAttrSeq = rPara.getRunAttributes( aPos.nIndex, RequestedAttributes ); 1002 uno::Sequence< beans::PropertyValue > aIntersectionSeq = getDefaultAttributes( RequestedAttributes ); 1003 PropertyValueVector aDiffVec; 1004 1005 const beans::PropertyValue* pDefAttr = aDefAttrSeq.getConstArray(); 1006 const sal_Int32 nLength = aDefAttrSeq.getLength(); 1007 for ( sal_Int32 i = 0; i < nLength; ++i ) 1008 { 1009 const beans::PropertyValue* pItr = aIntersectionSeq.getConstArray(); 1010 const beans::PropertyValue* pEnd = pItr + aIntersectionSeq.getLength(); 1011 const beans::PropertyValue* pFind = ::std::find_if( pItr, pEnd, ::std::bind2nd( PropertyValueEqualFunctor(), boost::cref( pDefAttr[i] ) ) ); 1012 if ( pFind == pEnd && pDefAttr[i].Handle != 0) 1013 { 1014 aDiffVec.push_back( pDefAttr[i] ); 1015 } 1016 } 1017 1018 return ::comphelper::concatSequences( aRunAttrSeq, aDiffVec.getAsConstList() ); 1019 } 1020 1021 Rectangle AccessibleStaticTextBase::GetParagraphBoundingBox() const 1022 { 1023 return mpImpl->GetParagraphBoundingBox(); 1024 } 1025 1026 sal_Int32 AccessibleStaticTextBase::GetParagraphIndex() const 1027 { 1028 return mpImpl->GetParagraphIndex(); 1029 } 1030 1031 sal_Int32 AccessibleStaticTextBase::GetParagraphCount() const 1032 { 1033 return mpImpl->GetParagraphCount(); 1034 } 1035 1036 sal_Int32 AccessibleStaticTextBase::GetLineCount( sal_Int32 nParagraph ) const 1037 { 1038 return mpImpl->GetLineCount( nParagraph ); 1039 } 1040 1041 } // end of namespace accessibility 1042 1043 //------------------------------------------------------------------------ 1044