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_svx.hxx" 26 27 //------------------------------------------------------------------------ 28 // 29 // Global header 30 // 31 //------------------------------------------------------------------------ 32 33 #include <limits.h> 34 #include <memory> 35 #include <algorithm> 36 #include <deque> 37 #include <vos/mutex.hxx> 38 #include <com/sun/star/uno/Any.hxx> 39 #include <com/sun/star/uno/Reference.hxx> 40 #include <cppuhelper/weakref.hxx> 41 #include <com/sun/star/awt/Point.hpp> 42 #include <com/sun/star/awt/Rectangle.hpp> 43 #include <com/sun/star/lang/DisposedException.hpp> 44 #include <com/sun/star/accessibility/AccessibleEventId.hpp> 45 #include <com/sun/star/accessibility/XAccessible.hpp> 46 #include <com/sun/star/accessibility/XAccessibleContext.hpp> 47 #include <com/sun/star/accessibility/XAccessibleComponent.hpp> 48 #include <com/sun/star/accessibility/AccessibleStateType.hpp> 49 #include <comphelper/accessibleeventnotifier.hxx> 50 #include <unotools/accessiblestatesethelper.hxx> 51 #include <vcl/unohelp.hxx> 52 #include <vcl/svapp.hxx> 53 //add TEXT_SELECTION_CHANGED event 54 #ifndef _TEXTDATA_HXX 55 #include <svtools/textdata.hxx> 56 #endif 57 58 #include <sfx2/viewfrm.hxx> 59 #include <sfx2/viewsh.hxx> 60 //------------------------------------------------------------------------ 61 // 62 // Project-local header 63 // 64 //------------------------------------------------------------------------ 65 #include "AccessibleTextEventQueue.hxx" 66 #include <svx/AccessibleTextHelper.hxx> 67 #include <svx/unoshape.hxx> 68 #include "editeng/unolingu.hxx" 69 #include <editeng/unotext.hxx> 70 71 #include "editeng/unoedhlp.hxx" 72 #include "editeng/unopracc.hxx" 73 #include "editeng/AccessibleParaManager.hxx" 74 #include "editeng/AccessibleEditableTextPara.hxx" 75 #include <svx/svdmodel.hxx> 76 #include <svx/svdpntv.hxx> 77 #include "../table/cell.hxx" 78 #include "../table/accessiblecell.hxx" 79 #include <editeng/editdata.hxx> 80 #include <editeng/editeng.hxx> 81 #include <editeng/editview.hxx> 82 83 using namespace ::com::sun::star; 84 using namespace ::com::sun::star::accessibility; 85 86 namespace accessibility 87 { GetCurrentEditorWnd()88 Window* GetCurrentEditorWnd() 89 { 90 Window* pWin = NULL; 91 SfxViewFrame* pFrame = SfxViewFrame::Current(); 92 if (pFrame) 93 { 94 const SfxViewShell * pViewShell = pFrame->GetViewShell(); 95 if(pViewShell) 96 { 97 pWin = pViewShell->GetWindow(); 98 } 99 } 100 return pWin; 101 } 102 103 //------------------------------------------------------------------------ 104 // 105 // AccessibleTextHelper_Impl declaration 106 // 107 //------------------------------------------------------------------------ 108 DBG_NAME(AccessibleTextHelper_Impl)109 DBG_NAME( AccessibleTextHelper_Impl ) 110 111 template < typename first_type, typename second_type > 112 ::std::pair< first_type, second_type > makeSortedPair( first_type first, 113 second_type second ) 114 { 115 if( first > second ) 116 return ::std::make_pair( second, first ); 117 else 118 return ::std::make_pair( first, second ); 119 } 120 121 class AccessibleTextHelper_Impl : public SfxListener 122 { 123 124 public: 125 typedef ::std::vector< sal_Int16 > VectorOfStates; 126 127 // receive pointer to our frontend class and view window 128 AccessibleTextHelper_Impl(); 129 ~AccessibleTextHelper_Impl(); 130 131 // XAccessibleContext child handling methods 132 sal_Int32 SAL_CALL getAccessibleChildCount() SAL_THROW((uno::RuntimeException)); 133 uno::Reference< XAccessible > SAL_CALL getAccessibleChild( sal_Int32 i ) SAL_THROW((lang::IndexOutOfBoundsException, uno::RuntimeException)); 134 135 // XAccessibleEventBroadcaster child related methods 136 void SAL_CALL addEventListener( const uno::Reference< XAccessibleEventListener >& xListener ) SAL_THROW((uno::RuntimeException)); 137 void SAL_CALL removeEventListener( const uno::Reference< XAccessibleEventListener >& xListener ) SAL_THROW((uno::RuntimeException)); 138 139 // XAccessibleComponent child related methods 140 uno::Reference< XAccessible > SAL_CALL getAccessibleAtPoint( const awt::Point& aPoint ) SAL_THROW((uno::RuntimeException)); 141 142 SvxEditSourceAdapter& GetEditSource() const SAL_THROW((uno::RuntimeException)); 143 void SetEditSource( ::std::auto_ptr< SvxEditSource > pEditSource ) SAL_THROW((uno::RuntimeException)); 144 SetEventSource(const uno::Reference<XAccessible> & rInterface)145 void SetEventSource( const uno::Reference< XAccessible >& rInterface ) 146 { 147 DBG_CHKTHIS( AccessibleTextHelper_Impl, NULL ); 148 mxFrontEnd = rInterface; 149 } GetEventSource() const150 uno::Reference< XAccessible > GetEventSource() const 151 { 152 DBG_CHKTHIS( AccessibleTextHelper_Impl, NULL ); 153 return mxFrontEnd; 154 } 155 156 void SetOffset( const Point& ); GetOffset() const157 Point GetOffset() const 158 { 159 DBG_CHKTHIS( AccessibleTextHelper_Impl, NULL ); 160 ::osl::MutexGuard aGuard( maMutex ); Point aPoint( maOffset ); 161 return aPoint; 162 } 163 164 void SetStartIndex( sal_Int32 nOffset ); GetStartIndex() const165 sal_Int32 GetStartIndex() const 166 { 167 DBG_CHKTHIS( AccessibleTextHelper_Impl, NULL ); 168 // Strictly correct only with locked solar mutex, // but 169 // here we rely on the fact that sal_Int32 access is 170 // atomic 171 return mnStartIndex; 172 } 173 174 void SetAdditionalChildStates( const VectorOfStates& rChildStates ); 175 const VectorOfStates& GetAdditionalChildStates() const; 176 177 sal_Bool IsSelected() const; 178 179 void Dispose(); 180 181 // do NOT hold object mutex when calling this! Danger of deadlock 182 void FireEvent( const sal_Int16 nEventId, const uno::Any& rNewValue = uno::Any(), const uno::Any& rOldValue = uno::Any() ) const; 183 void FireEvent( const AccessibleEventObject& rEvent ) const; 184 185 void SetFocus( sal_Bool bHaveFocus ) SAL_THROW((::com::sun::star::uno::RuntimeException)); 186 sal_Bool HaveFocus() SAL_THROW((::com::sun::star::uno::RuntimeException)); 187 void SetChildFocus( sal_Int32 nChild, sal_Bool bHaveFocus ) SAL_THROW((::com::sun::star::uno::RuntimeException)); 188 void SetShapeFocus( sal_Bool bHaveFocus ) SAL_THROW((::com::sun::star::uno::RuntimeException)); 189 void ChangeChildFocus( sal_Int32 nNewChild ) SAL_THROW((::com::sun::star::uno::RuntimeException)); 190 191 #ifdef DBG_UTIL 192 void CheckInvariants() const; 193 #endif 194 195 // checks all children for visibility, throws away invisible ones 196 void UpdateVisibleChildren( bool bBroadcastEvents=true ); 197 198 // check all children for changes in posit�on and size 199 void UpdateBoundRect(); 200 201 // calls SetSelection on the forwarder and updates maLastSelection 202 // cache. 203 void UpdateSelection(); 204 205 private: 206 207 // Process event queue 208 void ProcessQueue(); 209 210 // syntactic sugar for FireEvent GotPropertyEvent(const uno::Any & rNewValue,const sal_Int16 nEventId) const211 void GotPropertyEvent( const uno::Any& rNewValue, const sal_Int16 nEventId ) const { FireEvent( nEventId, rNewValue ); } LostPropertyEvent(const uno::Any & rOldValue,const sal_Int16 nEventId) const212 void LostPropertyEvent( const uno::Any& rOldValue, const sal_Int16 nEventId ) const { FireEvent( nEventId, uno::Any(), rOldValue ); } 213 214 // shutdown usage of current edit source on myself and the children. 215 void ShutdownEditSource() SAL_THROW((uno::RuntimeException)); 216 217 void ParagraphsMoved( sal_Int32 nFirst, sal_Int32 nMiddle, sal_Int32 nLast ); 218 219 virtual void Notify( SfxBroadcaster& rBC, const SfxHint& rHint ); 220 getNotifierClientId() const221 int getNotifierClientId() const { return mnNotifierClientId; } 222 223 // lock solar mutex before 224 SvxTextForwarder& GetTextForwarder() const SAL_THROW((uno::RuntimeException)); 225 // lock solar mutex before 226 SvxViewForwarder& GetViewForwarder() const SAL_THROW((uno::RuntimeException)); 227 // lock solar mutex before 228 SvxEditViewForwarder& GetEditViewForwarder( sal_Bool bCreate = sal_False ) const SAL_THROW((uno::RuntimeException)); 229 230 // are we in edit mode? 231 sal_Bool IsActive() const SAL_THROW((uno::RuntimeException)); 232 233 // our frontend class (the one implementing the actual 234 // interface). That's not necessarily the one containing the impl 235 // pointer! 236 uno::Reference< XAccessible > mxFrontEnd; 237 238 // a wrapper for the text forwarders (guarded by solar mutex) 239 mutable SvxEditSourceAdapter maEditSource; 240 241 // store last selection (to correctly report selection changes, guarded by solar mutex) 242 ESelection maLastSelection; 243 244 // cache range of visible children (guarded by solar mutex) 245 sal_Int32 mnFirstVisibleChild; 246 sal_Int32 mnLastVisibleChild; 247 248 // offset to add to all our children (unguarded, relying on 249 // the fact that sal_Int32 access is atomic) 250 sal_Int32 mnStartIndex; 251 252 // the object handling our children (guarded by solar mutex) 253 ::accessibility::AccessibleParaManager maParaManager; 254 255 // number of not-yet-closed event frames (BEGIN/END sequences) (guarded by solar mutex) 256 sal_Int32 maEventOpenFrames; 257 258 // Queued events from Notify() (guarded by solar mutex) 259 AccessibleTextEventQueue maEventQueue; 260 261 // spin lock to prevent notify in notify (guarded by solar mutex) 262 sal_Bool mbInNotify; 263 264 // whether the object or it's children has the focus set (guarded by solar mutex) 265 sal_Bool mbGroupHasFocus; 266 267 // whether we (this object) has the focus set (guarded by solar mutex) 268 sal_Bool mbThisHasFocus; 269 270 mutable ::osl::Mutex maMutex; 271 272 /// our current offset to the containing shape/cell (guarded by maMutex) 273 Point maOffset; 274 275 /// client Id from AccessibleEventNotifier 276 int mnNotifierClientId; 277 }; 278 279 //------------------------------------------------------------------------ 280 // 281 // AccessibleTextHelper_Impl implementation 282 // 283 //------------------------------------------------------------------------ 284 AccessibleTextHelper_Impl()285 AccessibleTextHelper_Impl::AccessibleTextHelper_Impl() : 286 mxFrontEnd( NULL ), 287 maLastSelection( EE_PARA_NOT_FOUND,EE_PARA_NOT_FOUND,EE_PARA_NOT_FOUND,EE_PARA_NOT_FOUND ), 288 mnFirstVisibleChild( -1 ), 289 mnLastVisibleChild( -2 ), 290 mnStartIndex( 0 ), 291 maEventOpenFrames( 0 ), 292 mbInNotify( sal_False ), 293 mbGroupHasFocus( sal_False ), 294 mbThisHasFocus( sal_False ), 295 maOffset(0,0), 296 // well, that's strictly exception safe, though not really 297 // robust. We rely on the fact that this member is constructed 298 // last, and that the constructor body is empty, thus no 299 // chance for exceptions once the Id is fetched. Nevertheless, 300 // normally should employ RAII here... 301 mnNotifierClientId(::comphelper::AccessibleEventNotifier::registerClient()) 302 { 303 DBG_CTOR( AccessibleTextHelper_Impl, NULL ); 304 305 #ifdef DBG_UTIL 306 OSL_TRACE( "AccessibleTextHelper_Impl received ID: %d", mnNotifierClientId ); 307 #endif 308 } 309 ~AccessibleTextHelper_Impl()310 AccessibleTextHelper_Impl::~AccessibleTextHelper_Impl() 311 { 312 DBG_DTOR( AccessibleTextHelper_Impl, NULL ); 313 314 ::vos::OGuard aGuard( Application::GetSolarMutex() ); 315 316 try 317 { 318 // call Dispose here, too, since we've some resources not 319 // automatically freed otherwise 320 Dispose(); 321 } 322 catch( const uno::Exception& ) {} 323 } 324 GetTextForwarder() const325 SvxTextForwarder& AccessibleTextHelper_Impl::GetTextForwarder() const SAL_THROW((uno::RuntimeException)) 326 { 327 DBG_CHKTHIS( AccessibleTextHelper_Impl, NULL ); 328 329 if( !maEditSource.IsValid() ) 330 throw uno::RuntimeException(::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("Unknown edit source")), mxFrontEnd); 331 332 SvxTextForwarder* pTextForwarder = maEditSource.GetTextForwarder(); 333 334 if( !pTextForwarder ) 335 throw uno::RuntimeException(::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("Unable to fetch text forwarder, model might be dead")), mxFrontEnd); 336 337 if( pTextForwarder->IsValid() ) 338 return *pTextForwarder; 339 else 340 throw uno::RuntimeException(::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("Text forwarder is invalid, model might be dead")), mxFrontEnd); 341 } 342 GetViewForwarder() const343 SvxViewForwarder& AccessibleTextHelper_Impl::GetViewForwarder() const SAL_THROW((uno::RuntimeException)) 344 { 345 DBG_CHKTHIS( AccessibleTextHelper_Impl, NULL ); 346 347 if( !maEditSource.IsValid() ) 348 throw uno::RuntimeException(::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("Unknown edit source")), mxFrontEnd); 349 350 SvxViewForwarder* pViewForwarder = maEditSource.GetViewForwarder(); 351 352 if( !pViewForwarder ) 353 throw uno::RuntimeException(::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("Unable to fetch view forwarder, model might be dead")), mxFrontEnd); 354 355 if( pViewForwarder->IsValid() ) 356 return *pViewForwarder; 357 else 358 throw uno::RuntimeException(::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("View forwarder is invalid, model might be dead")), mxFrontEnd); 359 } 360 GetEditViewForwarder(sal_Bool bCreate) const361 SvxEditViewForwarder& AccessibleTextHelper_Impl::GetEditViewForwarder( sal_Bool bCreate ) const SAL_THROW((uno::RuntimeException)) 362 { 363 DBG_CHKTHIS( AccessibleTextHelper_Impl, NULL ); 364 365 if( !maEditSource.IsValid() ) 366 throw uno::RuntimeException(::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("Unknown edit source")), mxFrontEnd); 367 368 SvxEditViewForwarder* pViewForwarder = maEditSource.GetEditViewForwarder( bCreate ); 369 370 if( !pViewForwarder ) 371 { 372 if( bCreate ) 373 throw uno::RuntimeException(::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("Unable to fetch edit view forwarder, model might be dead")), mxFrontEnd); 374 else 375 throw uno::RuntimeException(::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("No edit view forwarder, object not in edit mode")), mxFrontEnd); 376 } 377 378 if( pViewForwarder->IsValid() ) 379 return *pViewForwarder; 380 else 381 { 382 if( bCreate ) 383 throw uno::RuntimeException(::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("View forwarder is invalid, model might be dead")), mxFrontEnd); 384 else 385 throw uno::RuntimeException(::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("View forwarder is invalid, object not in edit mode")), mxFrontEnd); 386 } 387 } 388 GetEditSource() const389 SvxEditSourceAdapter& AccessibleTextHelper_Impl::GetEditSource() const SAL_THROW((uno::RuntimeException)) 390 { 391 DBG_CHKTHIS( AccessibleTextHelper_Impl, NULL ); 392 393 if( maEditSource.IsValid() ) 394 return maEditSource; 395 else 396 throw uno::RuntimeException(::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("AccessibleTextHelper_Impl::GetEditSource: no edit source")), mxFrontEnd ); 397 } 398 IsSelected() const399 sal_Bool AccessibleTextHelper_Impl::IsSelected() const 400 { 401 DBG_CHKTHIS( AccessibleTextHelper_Impl, NULL ); 402 403 sal_Bool bRet = sal_False; 404 405 try 406 { 407 ESelection aSelection; 408 bRet = GetEditViewForwarder().GetSelection( aSelection ); 409 } 410 catch( const uno::Exception& ) {} 411 412 return bRet; 413 } 414 415 // functor for sending child events (no stand-alone function, they are maybe not inlined) 416 class AccessibleTextHelper_OffsetChildIndex : public ::std::unary_function< ::accessibility::AccessibleEditableTextPara&, void > 417 { 418 public: AccessibleTextHelper_OffsetChildIndex(sal_Int32 nDifference)419 AccessibleTextHelper_OffsetChildIndex( sal_Int32 nDifference ) : mnDifference(nDifference) {} operator ()(::accessibility::AccessibleEditableTextPara & rPara)420 void operator()( ::accessibility::AccessibleEditableTextPara& rPara ) 421 { 422 rPara.SetIndexInParent( rPara.GetIndexInParent() + mnDifference ); 423 } 424 425 private: 426 const sal_Int32 mnDifference; 427 }; 428 SetStartIndex(sal_Int32 nOffset)429 void AccessibleTextHelper_Impl::SetStartIndex( sal_Int32 nOffset ) 430 { 431 DBG_CHKTHIS( AccessibleTextHelper_Impl, NULL ); 432 433 sal_Int32 nOldOffset( mnStartIndex ); 434 435 mnStartIndex = nOffset; 436 437 if( nOldOffset != nOffset ) 438 { 439 // update children 440 AccessibleTextHelper_OffsetChildIndex aFunctor( nOffset - nOldOffset ); 441 442 ::std::for_each( maParaManager.begin(), maParaManager.end(), 443 AccessibleParaManager::WeakChildAdapter< AccessibleTextHelper_OffsetChildIndex > (aFunctor) ); 444 } 445 } 446 SetAdditionalChildStates(const VectorOfStates & rChildStates)447 void AccessibleTextHelper_Impl::SetAdditionalChildStates( const VectorOfStates& rChildStates ) 448 { 449 maParaManager.SetAdditionalChildStates( rChildStates ); 450 } 451 GetAdditionalChildStates() const452 const AccessibleTextHelper_Impl::VectorOfStates& AccessibleTextHelper_Impl::GetAdditionalChildStates() const 453 { 454 return maParaManager.GetAdditionalChildStates(); 455 } 456 SetChildFocus(sal_Int32 nChild,sal_Bool bHaveFocus)457 void AccessibleTextHelper_Impl::SetChildFocus( sal_Int32 nChild, sal_Bool bHaveFocus ) SAL_THROW((::com::sun::star::uno::RuntimeException)) 458 { 459 DBG_CHKTHIS( AccessibleTextHelper_Impl, NULL ); 460 461 if( bHaveFocus ) 462 { 463 if( mbThisHasFocus ) 464 SetShapeFocus( sal_False ); 465 466 maParaManager.SetFocus( nChild ); 467 468 // we just received the focus, also send caret event then 469 UpdateSelection(); 470 471 DBG_TRACE1("AccessibleTextHelper_Impl::SetChildFocus(): Paragraph %d received focus", nChild ); 472 } 473 else 474 { 475 maParaManager.SetFocus( -1 ); 476 477 DBG_TRACE1("AccessibleTextHelper_Impl::SetChildFocus(): Paragraph %d lost focus", nChild ); 478 479 if( mbGroupHasFocus ) 480 SetShapeFocus( sal_True ); 481 } 482 } 483 ChangeChildFocus(sal_Int32 nNewChild)484 void AccessibleTextHelper_Impl::ChangeChildFocus( sal_Int32 nNewChild ) SAL_THROW((::com::sun::star::uno::RuntimeException)) 485 { 486 DBG_CHKTHIS( AccessibleTextHelper_Impl, NULL ); 487 488 if( mbThisHasFocus ) 489 SetShapeFocus( sal_False ); 490 491 mbGroupHasFocus = sal_True; 492 maParaManager.SetFocus( nNewChild ); 493 494 DBG_TRACE1("AccessibleTextHelper_Impl::ChangeChildFocus(): Paragraph %d received focus", nNewChild ); 495 } 496 SetShapeFocus(sal_Bool bHaveFocus)497 void AccessibleTextHelper_Impl::SetShapeFocus( sal_Bool bHaveFocus ) SAL_THROW((::com::sun::star::uno::RuntimeException)) 498 { 499 DBG_CHKTHIS( AccessibleTextHelper_Impl, NULL ); 500 501 sal_Bool bOldFocus( mbThisHasFocus ); 502 503 mbThisHasFocus = bHaveFocus; 504 505 if( bOldFocus != bHaveFocus ) 506 { 507 if( bHaveFocus ) 508 { 509 if( mxFrontEnd.is() ) 510 { 511 AccessibleCell* pAccessibleCell = dynamic_cast< AccessibleCell* > ( mxFrontEnd.get() ); 512 if ( !pAccessibleCell ) 513 GotPropertyEvent( uno::makeAny(AccessibleStateType::FOCUSED), AccessibleEventId::STATE_CHANGED ); 514 else // the focus event on cell should be fired on table directly 515 { 516 AccessibleTableShape* pAccTable = pAccessibleCell->GetParentTable(); 517 if (pAccTable) 518 pAccTable->SetStateDirectly(AccessibleStateType::FOCUSED); 519 } 520 } 521 DBG_TRACE("AccessibleTextHelper_Impl::SetShapeFocus(): Parent object received focus" ); 522 } 523 else 524 { 525 // The focus state should be reset directly on table. 526 //LostPropertyEvent( uno::makeAny(AccessibleStateType::FOCUSED), AccessibleEventId::STATE_CHANGED ); 527 if( mxFrontEnd.is() ) 528 { 529 AccessibleCell* pAccessibleCell = dynamic_cast< AccessibleCell* > ( mxFrontEnd.get() ); 530 if ( !pAccessibleCell ) 531 LostPropertyEvent( uno::makeAny(AccessibleStateType::FOCUSED), AccessibleEventId::STATE_CHANGED ); 532 else 533 { 534 AccessibleTableShape* pAccTable = pAccessibleCell->GetParentTable(); 535 if (pAccTable) 536 pAccTable->ResetStateDirectly(AccessibleStateType::FOCUSED); 537 } 538 } 539 DBG_TRACE("AccessibleTextHelper_Impl::SetShapeFocus(): Parent object lost focus" ); 540 } 541 } 542 } 543 SetFocus(sal_Bool bHaveFocus)544 void AccessibleTextHelper_Impl::SetFocus( sal_Bool bHaveFocus ) SAL_THROW((::com::sun::star::uno::RuntimeException)) 545 { 546 DBG_CHKTHIS( AccessibleTextHelper_Impl, NULL ); 547 548 sal_Bool bOldFocus( mbGroupHasFocus ); 549 550 mbGroupHasFocus = bHaveFocus; 551 552 if( IsActive() ) 553 { 554 try 555 { 556 // find the one with the cursor and get/set focus accordingly 557 ESelection aSelection; 558 if( GetEditViewForwarder().GetSelection( aSelection ) ) 559 SetChildFocus( aSelection.nEndPara, bHaveFocus ); 560 } 561 catch( const uno::Exception& ) {} 562 } 563 else if( bOldFocus != bHaveFocus ) 564 { 565 SetShapeFocus( bHaveFocus ); 566 } 567 568 DBG_TRACE2("AccessibleTextHelper_Impl::SetFocus: focus changed, Object %d, state: %s", this, bHaveFocus ? "focused" : "not focused"); 569 } 570 HaveFocus()571 sal_Bool AccessibleTextHelper_Impl::HaveFocus() SAL_THROW((::com::sun::star::uno::RuntimeException)) 572 { 573 DBG_CHKTHIS( AccessibleTextHelper_Impl, NULL ); 574 575 // No locking of solar mutex here, since we rely on the fact 576 // that sal_Bool access is atomic 577 return mbThisHasFocus; 578 } 579 IsActive() const580 sal_Bool AccessibleTextHelper_Impl::IsActive() const SAL_THROW((uno::RuntimeException)) 581 { 582 DBG_CHKTHIS( AccessibleTextHelper_Impl, NULL ); 583 584 try 585 { 586 SvxEditSource& rEditSource = GetEditSource(); 587 SvxEditViewForwarder* pViewForwarder = rEditSource.GetEditViewForwarder(); 588 589 if( !pViewForwarder ) 590 return sal_False; 591 592 if( mxFrontEnd.is() ) 593 { 594 AccessibleCell* pAccessibleCell = dynamic_cast< AccessibleCell* > ( mxFrontEnd.get() ); 595 if ( pAccessibleCell ) 596 { 597 sdr::table::CellRef xCell = pAccessibleCell->getCellRef(); 598 if ( xCell.is() ) 599 return xCell->IsTextEditActive(); 600 } 601 } 602 if( pViewForwarder->IsValid() ) 603 return sal_True; 604 else 605 return sal_False; 606 } 607 catch( const uno::RuntimeException& ) 608 { 609 return sal_False; 610 } 611 } 612 UpdateSelection()613 void AccessibleTextHelper_Impl::UpdateSelection() 614 { 615 DBG_CHKTHIS( AccessibleTextHelper_Impl, NULL ); 616 617 try 618 { 619 ESelection aSelection; 620 if( GetEditViewForwarder().GetSelection( aSelection ) ) 621 { 622 if( !maLastSelection.IsEqual( aSelection ) && 623 aSelection.nEndPara < maParaManager.GetNum() ) 624 { 625 // #103998# Not that important, changed from assertion to trace 626 if( mbThisHasFocus ) 627 { 628 DBG_TRACE("AccessibleTextHelper_Impl::UpdateSelection(): Parent has focus!"); 629 } 630 631 sal_uInt32 nMaxValidParaIndex( GetTextForwarder().GetParagraphCount() - 1 ); 632 633 // notify all affected paragraphs (TODO: may be suboptimal, 634 // since some paragraphs might stay selected) 635 if( maLastSelection.nStartPara != EE_PARA_NOT_FOUND ) 636 { 637 // Did the caret move from one paragraph to another? 638 // #100530# no caret events if not focused. 639 if( mbGroupHasFocus && 640 maLastSelection.nEndPara != aSelection.nEndPara ) 641 { 642 if( maLastSelection.nEndPara < maParaManager.GetNum() ) 643 { 644 maParaManager.FireEvent( ::std::min( maLastSelection.nEndPara, nMaxValidParaIndex ), 645 ::std::min( maLastSelection.nEndPara, nMaxValidParaIndex )+1, 646 AccessibleEventId::CARET_CHANGED, 647 uno::makeAny(static_cast<sal_Int32>(-1)), 648 uno::makeAny(static_cast<sal_Int32>(maLastSelection.nEndPos)) ); 649 } 650 651 ChangeChildFocus( aSelection.nEndPara ); 652 653 DBG_TRACE3("AccessibleTextHelper_Impl::UpdateSelection(): focus changed, Object: %d, Paragraph: %d, Last paragraph: %d", 654 this, aSelection.nEndPara, maLastSelection.nEndPara); 655 } 656 } 657 658 // #100530# no caret events if not focused. 659 if( mbGroupHasFocus ) 660 { 661 uno::Any aOldCursor; 662 663 // #i13705# The old cursor can only contain valid 664 // values if it's the same paragraph! 665 if( maLastSelection.nStartPara != EE_PARA_NOT_FOUND && 666 maLastSelection.nEndPara == aSelection.nEndPara ) 667 { 668 aOldCursor <<= static_cast<sal_Int32>(maLastSelection.nEndPos); 669 } 670 else 671 { 672 aOldCursor <<= static_cast<sal_Int32>(-1); 673 } 674 675 maParaManager.FireEvent( aSelection.nEndPara, 676 aSelection.nEndPara+1, 677 AccessibleEventId::CARET_CHANGED, 678 uno::makeAny(static_cast<sal_Int32>(aSelection.nEndPos)), 679 aOldCursor ); 680 } 681 682 DBG_TRACE5("AccessibleTextHelper_Impl::UpdateSelection(): caret changed, Object: %d, New pos: %d, Old pos: %d, New para: %d, Old para: %d", 683 this, aSelection.nEndPos, maLastSelection.nEndPos, aSelection.nEndPara, maLastSelection.nEndPara); 684 685 // #108947# Sort new range before calling FireEvent 686 ::std::pair< xub_StrLen, xub_StrLen > sortedSelection( 687 makeSortedPair(::std::min( aSelection.nStartPara, nMaxValidParaIndex ), 688 ::std::min( aSelection.nEndPara, nMaxValidParaIndex ) ) ); 689 690 // #108947# Sort last range before calling FireEvent 691 ::std::pair< xub_StrLen, xub_StrLen > sortedLastSelection( 692 makeSortedPair(::std::min( maLastSelection.nStartPara, nMaxValidParaIndex ), 693 ::std::min( maLastSelection.nEndPara, nMaxValidParaIndex ) ) ); 694 695 // --> OD 2005-12-15 #i27299# 696 // event TEXT_SELECTION_CHANGED has to be submitted. 697 const sal_Int16 nTextSelChgEventId = 698 AccessibleEventId::TEXT_SELECTION_CHANGED; 699 // <-- 700 // #107037# notify selection change 701 if( maLastSelection.nStartPara == EE_PARA_NOT_FOUND ) 702 { 703 // last selection is undefined 704 // --> OD 2005-12-15 #i27299# - use method <ESelection::HasRange()> 705 if ( aSelection.HasRange() ) 706 // <-- 707 { 708 // selection was undefined, now is on 709 maParaManager.FireEvent( sortedSelection.first, 710 sortedSelection.second+1, 711 nTextSelChgEventId ); 712 } 713 } 714 else 715 { 716 // last selection is valid 717 // --> OD 2005-12-15 #i27299# - use method <ESelection::HasRange()> 718 if ( maLastSelection.HasRange() && 719 !aSelection.HasRange() ) 720 // <-- 721 { 722 // selection was on, now is empty 723 maParaManager.FireEvent( sortedLastSelection.first, 724 sortedLastSelection.second+1, 725 nTextSelChgEventId ); 726 } 727 // --> OD 2005-12-15 #i27299# - use method <ESelection::HasRange()> 728 else if( !maLastSelection.HasRange() && 729 aSelection.HasRange() ) 730 // <-- 731 { 732 // selection was empty, now is on 733 maParaManager.FireEvent( sortedSelection.first, 734 sortedSelection.second+1, 735 nTextSelChgEventId ); 736 } 737 // --> OD 2005-12-15 #i27299# 738 // - no event TEXT_SELECTION_CHANGED event, if new and 739 // last selection are empty. 740 else if ( maLastSelection.HasRange() && 741 aSelection.HasRange() ) 742 // <-- 743 { 744 // --> OD 2005-12-16 #i27299# 745 // - send event TEXT_SELECTION_CHANGED for difference 746 // between last and new selection. 747 // // selection was on, now is different: take union of ranges 748 // maParaManager.FireEvent( ::std::min(sortedSelection.first, 749 // sortedLastSelection.second), 750 // ::std::max(sortedSelection.first, 751 // sortedLastSelection.second)+1, 752 // nTextSelChgEventId ); 753 // use sorted last and new selection 754 ESelection aTmpLastSel( maLastSelection ); 755 aTmpLastSel.Adjust(); 756 ESelection aTmpSel( aSelection ); 757 aTmpSel.Adjust(); 758 // first submit event for new and changed selection 759 sal_uInt32 nPara = aTmpSel.nStartPara; 760 for ( ; nPara <= aTmpSel.nEndPara; ++nPara ) 761 { 762 if ( nPara < aTmpLastSel.nStartPara || 763 nPara > aTmpLastSel.nEndPara ) 764 { 765 // new selection on paragraph <nPara> 766 maParaManager.FireEvent( nPara, 767 nTextSelChgEventId ); 768 } 769 else 770 { 771 // check for changed selection on paragraph <nPara> 772 const xub_StrLen nParaStartPos = 773 nPara == aTmpSel.nStartPara 774 ? aTmpSel.nStartPos : 0; 775 const xub_StrLen nParaEndPos = 776 nPara == aTmpSel.nEndPara 777 ? aTmpSel.nEndPos : STRING_LEN; 778 const xub_StrLen nLastParaStartPos = 779 nPara == aTmpLastSel.nStartPara 780 ? aTmpLastSel.nStartPos : 0; 781 const xub_StrLen nLastParaEndPos = 782 nPara == aTmpLastSel.nEndPara 783 ? aTmpLastSel.nEndPos : STRING_LEN; 784 if ( nParaStartPos != nLastParaStartPos || 785 nParaEndPos != nLastParaEndPos ) 786 { 787 maParaManager.FireEvent( 788 nPara, nTextSelChgEventId ); 789 } 790 } 791 } 792 // second submit event for 'old' selections 793 nPara = aTmpLastSel.nStartPara; 794 for ( ; nPara <= aTmpLastSel.nEndPara; ++nPara ) 795 { 796 if ( nPara < aTmpSel.nStartPara || 797 nPara > aTmpSel.nEndPara ) 798 { 799 maParaManager.FireEvent( nPara, 800 nTextSelChgEventId ); 801 } 802 } 803 } 804 } 805 806 maLastSelection = aSelection; 807 } 808 } 809 } 810 // no selection? no update actions 811 catch( const uno::RuntimeException& ) {} 812 } 813 ShutdownEditSource()814 void AccessibleTextHelper_Impl::ShutdownEditSource() SAL_THROW((uno::RuntimeException)) 815 { 816 DBG_CHKTHIS( AccessibleTextHelper_Impl, NULL ); 817 818 // This should only be called with solar mutex locked, i.e. from the main office thread 819 820 // This here is somewhat clumsy: As soon as our children have 821 // a NULL EditSource (maParaManager.SetEditSource()), they 822 // enter the disposed state and cannot be reanimated. Thus, it 823 // is unavoidable and a hard requirement to let go and create 824 // from scratch each and every child. 825 826 // invalidate children 827 maParaManager.Dispose(); 828 maParaManager.SetNum(0); 829 830 // lost all children 831 if( mxFrontEnd.is() ) 832 FireEvent(AccessibleEventId::INVALIDATE_ALL_CHILDREN); 833 834 // quit listen on stale edit source 835 if( maEditSource.IsValid() ) 836 EndListening( maEditSource.GetBroadcaster() ); 837 838 maEditSource.SetEditSource( ::std::auto_ptr< SvxEditSource >(NULL) ); 839 } 840 SetEditSource(::std::auto_ptr<SvxEditSource> pEditSource)841 void AccessibleTextHelper_Impl::SetEditSource( ::std::auto_ptr< SvxEditSource > pEditSource ) SAL_THROW((uno::RuntimeException)) 842 { 843 DBG_CHKTHIS( AccessibleTextHelper_Impl, NULL ); 844 845 // This should only be called with solar mutex locked, i.e. from the main office thread 846 847 // shutdown old edit source 848 ShutdownEditSource(); 849 850 // set new edit source 851 maEditSource.SetEditSource( pEditSource ); 852 853 // init child vector to the current child count 854 if( maEditSource.IsValid() ) 855 { 856 maParaManager.SetNum( GetTextForwarder().GetParagraphCount() ); 857 858 // listen on new edit source 859 StartListening( maEditSource.GetBroadcaster() ); 860 861 UpdateVisibleChildren(); 862 } 863 } 864 SetOffset(const Point & rPoint)865 void AccessibleTextHelper_Impl::SetOffset( const Point& rPoint ) 866 { 867 DBG_CHKTHIS( AccessibleTextHelper_Impl, NULL ); 868 869 // guard against non-atomic access to maOffset data structure 870 { 871 ::osl::MutexGuard aGuard( maMutex ); 872 maOffset = rPoint; 873 } 874 875 maParaManager.SetEEOffset( rPoint ); 876 877 // in all cases, check visibility afterwards. 878 UpdateVisibleChildren(); 879 UpdateBoundRect(); 880 } 881 UpdateVisibleChildren(bool bBroadcastEvents)882 void AccessibleTextHelper_Impl::UpdateVisibleChildren( bool bBroadcastEvents ) 883 { 884 DBG_CHKTHIS( AccessibleTextHelper_Impl, NULL ); 885 886 try 887 { 888 SvxTextForwarder& rCacheTF = GetTextForwarder(); 889 SvxViewForwarder& rCacheVF = GetViewForwarder(); 890 891 Rectangle aViewArea = rCacheVF.GetVisArea(); 892 893 if( IsActive() ) 894 { 895 // maybe the edit view scrolls, adapt aViewArea 896 Rectangle aEditViewArea = GetEditViewForwarder().GetVisArea(); 897 aViewArea += aEditViewArea.TopLeft(); 898 899 // now determine intersection 900 aViewArea.Intersection( aEditViewArea ); 901 } 902 903 Rectangle aTmpBB, aParaBB; 904 sal_Bool bFirstChild = sal_True; 905 sal_Int32 nCurrPara; 906 sal_Int32 nParas=rCacheTF.GetParagraphCount(); 907 908 mnFirstVisibleChild = -1; 909 mnLastVisibleChild = -2; 910 911 for( nCurrPara=0; nCurrPara<nParas; ++nCurrPara ) 912 { 913 DBG_ASSERT(nCurrPara >= 0 && nCurrPara <= USHRT_MAX, 914 "AccessibleTextHelper_Impl::UpdateVisibleChildren: index value overflow"); 915 916 aTmpBB = rCacheTF.GetParaBounds( nCurrPara ); 917 918 // convert to screen coordinates 919 aParaBB = ::accessibility::AccessibleEditableTextPara::LogicToPixel( aTmpBB, rCacheTF.GetMapMode(), rCacheVF ); 920 921 // at least partially visible 922 if( bFirstChild ) 923 { 924 bFirstChild = sal_False; 925 mnFirstVisibleChild = nCurrPara; 926 } 927 928 mnLastVisibleChild = nCurrPara; 929 930 // child not yet created? 931 ::accessibility::AccessibleParaManager::WeakChild aChild( maParaManager.GetChild(nCurrPara) ); 932 if( aChild.second.Width == 0 && 933 aChild.second.Height == 0 && 934 mxFrontEnd.is() && 935 bBroadcastEvents ) 936 { 937 GotPropertyEvent( uno::makeAny( maParaManager.CreateChild( nCurrPara - mnFirstVisibleChild, 938 mxFrontEnd, GetEditSource(), nCurrPara ).first ), 939 AccessibleEventId::CHILD ); 940 } 941 } 942 } 943 catch( const uno::Exception& ) 944 { 945 DBG_ERROR("AccessibleTextHelper_Impl::UpdateVisibleChildren error while determining visible children"); 946 947 // something failed - currently no children 948 mnFirstVisibleChild = -1; 949 mnLastVisibleChild = -2; 950 maParaManager.SetNum(0); 951 952 // lost all children 953 if( bBroadcastEvents ) 954 FireEvent(AccessibleEventId::INVALIDATE_ALL_CHILDREN); 955 } 956 } 957 958 // functor for checking changes in paragraph bounding boxes (no stand-alone function, maybe not inlined) 959 class AccessibleTextHelper_UpdateChildBounds : public ::std::unary_function< const ::accessibility::AccessibleParaManager::WeakChild&, 960 ::accessibility::AccessibleParaManager::WeakChild > 961 { 962 public: AccessibleTextHelper_UpdateChildBounds(AccessibleTextHelper_Impl & rImpl)963 AccessibleTextHelper_UpdateChildBounds( AccessibleTextHelper_Impl& rImpl ) : mrImpl(rImpl) {} operator ()(const::accessibility::AccessibleParaManager::WeakChild & rChild)964 ::accessibility::AccessibleParaManager::WeakChild operator()( const ::accessibility::AccessibleParaManager::WeakChild& rChild ) 965 { 966 // retrieve hard reference from weak one 967 ::accessibility::AccessibleParaManager::WeakPara::HardRefType aHardRef( rChild.first.get() ); 968 969 if( aHardRef.is() ) 970 { 971 awt::Rectangle aNewRect = aHardRef->getBounds(); 972 const awt::Rectangle& aOldRect = rChild.second; 973 974 if( aNewRect.X != aOldRect.X || 975 aNewRect.Y != aOldRect.Y || 976 aNewRect.Width != aOldRect.Width || 977 aNewRect.Height != aOldRect.Height ) 978 { 979 // visible data changed 980 aHardRef->FireEvent( AccessibleEventId::BOUNDRECT_CHANGED ); 981 982 // update internal bounds 983 return ::accessibility::AccessibleParaManager::WeakChild( rChild.first, aNewRect ); 984 } 985 } 986 987 // identity transform 988 return rChild; 989 } 990 991 private: 992 AccessibleTextHelper_Impl& mrImpl; 993 }; 994 UpdateBoundRect()995 void AccessibleTextHelper_Impl::UpdateBoundRect() 996 { 997 DBG_CHKTHIS( AccessibleTextHelper_Impl, NULL ); 998 999 // send BOUNDRECT_CHANGED to affected children 1000 AccessibleTextHelper_UpdateChildBounds aFunctor( *this ); 1001 ::std::transform( maParaManager.begin(), maParaManager.end(), maParaManager.begin(), aFunctor ); 1002 } 1003 1004 #ifdef DBG_UTIL CheckInvariants() const1005 void AccessibleTextHelper_Impl::CheckInvariants() const 1006 { 1007 if( mnFirstVisibleChild >= 0 && 1008 mnFirstVisibleChild > mnLastVisibleChild ) 1009 { 1010 DBG_ERROR( "AccessibleTextHelper: range invalid" ); 1011 } 1012 } 1013 #endif 1014 1015 // functor for sending child events (no stand-alone function, they are maybe not inlined) 1016 class AccessibleTextHelper_LostChildEvent : public ::std::unary_function< const ::accessibility::AccessibleParaManager::WeakChild&, void > 1017 { 1018 public: AccessibleTextHelper_LostChildEvent(AccessibleTextHelper_Impl & rImpl)1019 AccessibleTextHelper_LostChildEvent( AccessibleTextHelper_Impl& rImpl ) : mrImpl(rImpl) {} operator ()(const::accessibility::AccessibleParaManager::WeakChild & rPara)1020 void operator()( const ::accessibility::AccessibleParaManager::WeakChild& rPara ) 1021 { 1022 // retrieve hard reference from weak one 1023 ::accessibility::AccessibleParaManager::WeakPara::HardRefType aHardRef( rPara.first.get() ); 1024 1025 if( aHardRef.is() ) 1026 mrImpl.FireEvent(AccessibleEventId::CHILD, uno::Any(), uno::makeAny( aHardRef.getRef() ) ); 1027 } 1028 1029 private: 1030 AccessibleTextHelper_Impl& mrImpl; 1031 }; 1032 ParagraphsMoved(sal_Int32 nFirst,sal_Int32 nMiddle,sal_Int32 nLast)1033 void AccessibleTextHelper_Impl::ParagraphsMoved( sal_Int32 nFirst, sal_Int32 nMiddle, sal_Int32 nLast ) 1034 { 1035 DBG_CHKTHIS( AccessibleTextHelper_Impl, NULL ); 1036 1037 const sal_Int32 nParas = GetTextForwarder().GetParagraphCount(); 1038 1039 /* rotate paragraphs 1040 * ================= 1041 * 1042 * Three cases: 1043 * 1044 * 1. 1045 * ... nParagraph ... nParam1 ... nParam2 ... 1046 * |______________[xxxxxxxxxxx] 1047 * becomes 1048 * [xxxxxxxxxxx]|______________ 1049 * 1050 * tail is 0 1051 * 1052 * 2. 1053 * ... nParam1 ... nParagraph ... nParam2 ... 1054 * [xxxxxxxxxxx|xxxxxxxxxxxxxx]____________ 1055 * becomes 1056 * ____________[xxxxxxxxxxx|xxxxxxxxxxxxxx] 1057 * 1058 * tail is nParagraph - nParam1 1059 * 1060 * 3. 1061 * ... nParam1 ... nParam2 ... nParagraph ... 1062 * [xxxxxxxxxxx]___________|____________ 1063 * becomes 1064 * ___________|____________[xxxxxxxxxxx] 1065 * 1066 * tail is nParam2 - nParam1 1067 */ 1068 1069 // sort nParagraph, nParam1 and nParam2 in ascending order, calc range 1070 if( nMiddle < nFirst ) 1071 { 1072 ::std::swap(nFirst, nMiddle); 1073 } 1074 else if( nMiddle < nLast ) 1075 { 1076 nLast = nLast + nMiddle - nFirst; 1077 } 1078 else 1079 { 1080 ::std::swap(nMiddle, nLast); 1081 nLast = nLast + nMiddle - nFirst; 1082 } 1083 1084 if( nFirst < nParas && nMiddle < nParas && nLast < nParas ) 1085 { 1086 // since we have no "paragraph index 1087 // changed" event on UAA, remove 1088 // [first,last] and insert again later (in 1089 // UpdateVisibleChildren) 1090 1091 // maParaManager.Rotate( nFirst, nMiddle, nLast ); 1092 1093 // send CHILD_EVENT to affected children 1094 ::accessibility::AccessibleParaManager::VectorOfChildren::const_iterator begin = maParaManager.begin(); 1095 ::accessibility::AccessibleParaManager::VectorOfChildren::const_iterator end = begin; 1096 1097 ::std::advance( begin, nFirst ); 1098 ::std::advance( end, nLast+1 ); 1099 1100 // TODO: maybe optimize here in the following way. If the 1101 // number of removed children exceeds a certain threshold, 1102 // use INVALIDATE_CHILDREN 1103 AccessibleTextHelper_LostChildEvent aFunctor( *this ); 1104 1105 ::std::for_each( begin, end, aFunctor ); 1106 1107 maParaManager.Release(nFirst, nLast+1); 1108 // should be no need for UpdateBoundRect, since all affected children are cleared. 1109 } 1110 } 1111 1112 // functor for sending child events (no stand-alone function, they are maybe not inlined) 1113 class AccessibleTextHelper_ChildrenTextChanged : public ::std::unary_function< ::accessibility::AccessibleEditableTextPara&, void > 1114 { 1115 public: operator ()(::accessibility::AccessibleEditableTextPara & rPara)1116 void operator()( ::accessibility::AccessibleEditableTextPara& rPara ) 1117 { 1118 rPara.TextChanged(); 1119 } 1120 }; 1121 1122 /** functor processing queue events 1123 1124 Reacts on TEXT_HINT_PARAINSERTED/REMOVED events and stores 1125 their content 1126 */ 1127 class AccessibleTextHelper_QueueFunctor : public ::std::unary_function< const SfxHint*, void > 1128 { 1129 public: AccessibleTextHelper_QueueFunctor()1130 AccessibleTextHelper_QueueFunctor() : 1131 mnParasChanged( 0 ), 1132 mnParaIndex(-1), 1133 mnHintId(-1) 1134 {} operator ()(const SfxHint * pEvent)1135 void operator()( const SfxHint* pEvent ) 1136 { 1137 if( pEvent && 1138 mnParasChanged != -1 ) 1139 { 1140 // determine hint type 1141 const TextHint* pTextHint = PTR_CAST( TextHint, pEvent ); 1142 const SvxEditSourceHint* pEditSourceHint = PTR_CAST( SvxEditSourceHint, pEvent ); 1143 1144 if( !pEditSourceHint && pTextHint && 1145 (pTextHint->GetId() == TEXT_HINT_PARAINSERTED || 1146 pTextHint->GetId() == TEXT_HINT_PARAREMOVED ) ) 1147 { 1148 if( pTextHint->GetValue() == EE_PARA_ALL ) 1149 { 1150 mnParasChanged = -1; 1151 } 1152 else 1153 { 1154 mnHintId = pTextHint->GetId(); 1155 mnParaIndex = pTextHint->GetValue(); 1156 ++mnParasChanged; 1157 } 1158 } 1159 } 1160 } 1161 1162 /** Query number of paragraphs changed during queue processing. 1163 1164 @return number of changed paragraphs, -1 for 1165 "every paragraph changed" 1166 */ GetNumberOfParasChanged()1167 int GetNumberOfParasChanged() { return mnParasChanged; } 1168 /** Query index of last added/removed paragraph 1169 1170 @return index of lastly added paragraphs, -1 for none 1171 added so far. 1172 */ GetParaIndex()1173 int GetParaIndex() { return mnParaIndex; } 1174 /** Query hint id of last interesting event 1175 1176 @return hint id of last interesting event (REMOVED/INSERTED). 1177 */ GetHintId()1178 int GetHintId() { return mnHintId; } 1179 1180 private: 1181 /** number of paragraphs changed during queue processing. -1 for 1182 "every paragraph changed" 1183 */ 1184 int mnParasChanged; 1185 /// index of paragraph added/removed last 1186 int mnParaIndex; 1187 /// TextHint ID (removed/inserted) of last interesting event 1188 int mnHintId; 1189 }; 1190 ProcessQueue()1191 void AccessibleTextHelper_Impl::ProcessQueue() 1192 { 1193 DBG_CHKTHIS( AccessibleTextHelper_Impl, NULL ); 1194 1195 // inspect queue for paragraph insert/remove events. If there 1196 // is exactly _one_ of those in the queue, and the number of 1197 // paragraphs has changed by exactly one, use that event to 1198 // determine a priori which paragraph was added/removed. This 1199 // is necessary, since I must sync right here with the 1200 // EditEngine state (number of paragraphs etc.), since I'm 1201 // potentially sending listener events right away. 1202 AccessibleTextHelper_QueueFunctor aFunctor; 1203 maEventQueue.ForEach( aFunctor ); 1204 1205 const sal_Int32 nNewParas( GetTextForwarder().GetParagraphCount() ); 1206 const sal_Int32 nCurrParas( maParaManager.GetNum() ); 1207 1208 // whether every paragraph already is updated (no need to 1209 // repeat that later on, e.g. for PARA_MOVED events) 1210 bool bEverythingUpdated( false ); 1211 1212 if( labs( nNewParas - nCurrParas ) == 1 && 1213 aFunctor.GetNumberOfParasChanged() == 1 ) 1214 { 1215 // #103483# Exactly one paragraph added/removed. This is 1216 // the normal case, optimize event handling here. 1217 1218 if( aFunctor.GetHintId() == TEXT_HINT_PARAINSERTED ) 1219 { 1220 // update num of paras 1221 maParaManager.SetNum( nNewParas ); 1222 1223 // release everything from the insertion position until the end 1224 maParaManager.Release(aFunctor.GetParaIndex(), nCurrParas); 1225 1226 // TODO: Clarify whether this behaviour _really_ saves 1227 // anybody anything! 1228 // update children, _don't_ broadcast 1229 UpdateVisibleChildren( false ); 1230 UpdateBoundRect(); 1231 1232 // send insert event 1233 // #109864# Enforce creation of this paragraph 1234 try 1235 { 1236 GotPropertyEvent( uno::makeAny( getAccessibleChild( aFunctor.GetParaIndex() - 1237 mnFirstVisibleChild + GetStartIndex() ) ), 1238 AccessibleEventId::CHILD ); 1239 } 1240 catch( const uno::Exception& ) 1241 { 1242 DBG_ERROR("AccessibleTextHelper_Impl::ProcessQueue: could not create new paragraph"); 1243 } 1244 } 1245 else if( aFunctor.GetHintId() == TEXT_HINT_PARAREMOVED ) 1246 { 1247 ::accessibility::AccessibleParaManager::VectorOfChildren::const_iterator begin = maParaManager.begin(); 1248 ::std::advance( begin, aFunctor.GetParaIndex() ); 1249 ::accessibility::AccessibleParaManager::VectorOfChildren::const_iterator end = begin; 1250 ::std::advance( end, 1 ); 1251 1252 // #i61812# remember para to be removed for later notification 1253 // AFTER the new state is applied (that after the para got removed) 1254 ::uno::Reference< XAccessible > xPara; 1255 ::accessibility::AccessibleParaManager::WeakPara::HardRefType aHardRef( begin->first.get() ); 1256 if( aHardRef.is() ) 1257 xPara = ::uno::Reference< XAccessible >( aHardRef.getRef(), ::uno::UNO_QUERY ); 1258 1259 // release everything from the remove position until the end 1260 maParaManager.Release(aFunctor.GetParaIndex(), nCurrParas); 1261 1262 // update num of paras 1263 maParaManager.SetNum( nNewParas ); 1264 1265 // TODO: Clarify whether this behaviour _really_ saves 1266 // anybody anything! 1267 // update children, _don't_ broadcast 1268 UpdateVisibleChildren( false ); 1269 UpdateBoundRect(); 1270 1271 // #i61812# notification for removed para 1272 if (xPara.is()) 1273 FireEvent(AccessibleEventId::CHILD, uno::Any(), uno::makeAny( xPara) ); 1274 } 1275 #ifdef DBG_UTIL 1276 else 1277 DBG_ERROR("AccessibleTextHelper_Impl::ProcessQueue() invalid hint id"); 1278 #endif 1279 } 1280 else if( nNewParas != nCurrParas ) 1281 { 1282 // release all paras 1283 maParaManager.Release(0, nCurrParas); 1284 1285 // update num of paras 1286 maParaManager.SetNum( nNewParas ); 1287 1288 // #109864# create from scratch, don't broadcast 1289 UpdateVisibleChildren( false ); 1290 UpdateBoundRect(); 1291 1292 // number of paragraphs somehow changed - but we have no 1293 // chance determining how. Thus, throw away everything and 1294 // create from scratch. 1295 // (child events should be broadcast after the changes are done...) 1296 FireEvent(AccessibleEventId::INVALIDATE_ALL_CHILDREN); 1297 1298 // no need for further updates later on 1299 bEverythingUpdated = true; 1300 } 1301 1302 while( !maEventQueue.IsEmpty() ) 1303 { 1304 ::std::auto_ptr< SfxHint > pHint( maEventQueue.PopFront() ); 1305 if( pHint.get() ) 1306 { 1307 const SfxHint& rHint = *(pHint.get()); 1308 1309 // determine hint type 1310 const SdrHint* pSdrHint = PTR_CAST( SdrHint, &rHint ); 1311 const SfxSimpleHint* pSimpleHint = PTR_CAST( SfxSimpleHint, &rHint ); 1312 const TextHint* pTextHint = PTR_CAST( TextHint, &rHint ); 1313 const SvxViewHint* pViewHint = PTR_CAST( SvxViewHint, &rHint ); 1314 const SvxEditSourceHint* pEditSourceHint = PTR_CAST( SvxEditSourceHint, &rHint ); 1315 1316 try 1317 { 1318 const sal_Int32 nParas = GetTextForwarder().GetParagraphCount(); 1319 1320 if( pEditSourceHint ) 1321 { 1322 switch( pEditSourceHint->GetId() ) 1323 { 1324 case EDITSOURCE_HINT_PARASMOVED: 1325 { 1326 DBG_ASSERT( pEditSourceHint->GetStartValue() < GetTextForwarder().GetParagraphCount() && 1327 pEditSourceHint->GetEndValue() < GetTextForwarder().GetParagraphCount(), 1328 "AccessibleTextHelper_Impl::NotifyHdl: Invalid notification"); 1329 1330 if( !bEverythingUpdated ) 1331 { 1332 ParagraphsMoved(pEditSourceHint->GetStartValue(), 1333 pEditSourceHint->GetValue(), 1334 pEditSourceHint->GetEndValue()); 1335 1336 // in all cases, check visibility afterwards. 1337 UpdateVisibleChildren(); 1338 } 1339 break; 1340 } 1341 1342 case EDITSOURCE_HINT_SELECTIONCHANGED: 1343 // notify listeners 1344 try 1345 { 1346 UpdateSelection(); 1347 } 1348 // maybe we're not in edit mode (this is not an error) 1349 catch( const uno::Exception& ) {} 1350 break; 1351 } 1352 } 1353 else if( pTextHint ) 1354 { 1355 switch( pTextHint->GetId() ) 1356 { 1357 case TEXT_HINT_MODIFIED: 1358 { 1359 // notify listeners 1360 sal_Int32 nPara( pTextHint->GetValue() ); 1361 1362 // #108900# Delegate change event to children 1363 AccessibleTextHelper_ChildrenTextChanged aNotifyChildrenFunctor; 1364 1365 if( nPara == static_cast<sal_Int32>(EE_PARA_ALL) ) 1366 { 1367 // #108900# Call every child 1368 ::std::for_each( maParaManager.begin(), maParaManager.end(), 1369 AccessibleParaManager::WeakChildAdapter< AccessibleTextHelper_ChildrenTextChanged > (aNotifyChildrenFunctor) ); 1370 } 1371 else 1372 if( nPara < nParas ) 1373 { 1374 // #108900# Call child at index nPara 1375 ::std::for_each( maParaManager.begin()+nPara, maParaManager.begin()+nPara+1, 1376 AccessibleParaManager::WeakChildAdapter< AccessibleTextHelper_ChildrenTextChanged > (aNotifyChildrenFunctor) ); 1377 } 1378 break; 1379 } 1380 1381 case TEXT_HINT_PARAINSERTED: 1382 // already happened above 1383 break; 1384 1385 case TEXT_HINT_PARAREMOVED: 1386 // already happened above 1387 break; 1388 1389 case TEXT_HINT_TEXTHEIGHTCHANGED: 1390 // visibility changed, done below 1391 break; 1392 1393 case TEXT_HINT_VIEWSCROLLED: 1394 // visibility changed, done below 1395 break; 1396 } 1397 1398 // in all cases, check visibility afterwards. 1399 UpdateVisibleChildren(); 1400 UpdateBoundRect(); 1401 } 1402 else if( pViewHint ) 1403 { 1404 switch( pViewHint->GetHintType() ) 1405 { 1406 case SvxViewHint::SVX_HINT_VIEWCHANGED: 1407 // just check visibility 1408 UpdateVisibleChildren(); 1409 UpdateBoundRect(); 1410 break; 1411 } 1412 } 1413 else if( pSdrHint ) 1414 { 1415 switch( pSdrHint->GetKind() ) 1416 { 1417 case HINT_BEGEDIT: 1418 { 1419 if(!IsActive()) 1420 { 1421 break; 1422 } 1423 // change children state 1424 maParaManager.SetActive(); 1425 1426 // per definition, edit mode text has the focus 1427 SetFocus( sal_True ); 1428 break; 1429 } 1430 1431 case HINT_ENDEDIT: 1432 { 1433 // focused child now looses focus 1434 ESelection aSelection; 1435 if( GetEditViewForwarder().GetSelection( aSelection ) ) 1436 SetChildFocus( aSelection.nEndPara, sal_False ); 1437 1438 // change children state 1439 maParaManager.SetActive( sal_False ); 1440 1441 maLastSelection = ESelection( EE_PARA_NOT_FOUND, EE_PARA_NOT_FOUND, 1442 EE_PARA_NOT_FOUND, EE_PARA_NOT_FOUND); 1443 break; 1444 } 1445 default: 1446 break; 1447 } 1448 } 1449 // it's VITAL to keep the SfxSimpleHint last! It's the base of some classes above! 1450 else if( pSimpleHint ) 1451 { 1452 switch( pSimpleHint->GetId() ) 1453 { 1454 case SFX_HINT_DYING: 1455 // edit source is dying under us, become defunc then 1456 try 1457 { 1458 // make edit source inaccessible 1459 // Note: cannot destroy it here, since we're called from there! 1460 ShutdownEditSource(); 1461 } 1462 catch( const uno::Exception& ) {} 1463 1464 break; 1465 } 1466 } 1467 } 1468 catch( const uno::Exception& ) 1469 { 1470 #ifdef DBG_UTIL 1471 OSL_TRACE("AccessibleTextHelper_Impl::ProcessQueue: Unhandled exception."); 1472 #endif 1473 } 1474 } 1475 } 1476 } 1477 Notify(SfxBroadcaster &,const SfxHint & rHint)1478 void AccessibleTextHelper_Impl::Notify( SfxBroadcaster& /*rBC*/, const SfxHint& rHint ) 1479 { 1480 DBG_CHKTHIS( AccessibleTextHelper_Impl, NULL ); 1481 1482 // precondition: solar mutex locked 1483 DBG_TESTSOLARMUTEX(); 1484 1485 // precondition: not in a recursion 1486 if( mbInNotify ) 1487 return; 1488 1489 mbInNotify = sal_True; 1490 1491 // determine hint type 1492 const SdrHint* pSdrHint = PTR_CAST( SdrHint, &rHint ); 1493 const SfxSimpleHint* pSimpleHint = PTR_CAST( SfxSimpleHint, &rHint ); 1494 const TextHint* pTextHint = PTR_CAST( TextHint, &rHint ); 1495 const SvxViewHint* pViewHint = PTR_CAST( SvxViewHint, &rHint ); 1496 const SvxEditSourceHint* pEditSourceHint = PTR_CAST( SvxEditSourceHint, &rHint ); 1497 1498 try 1499 { 1500 // Process notification event 1501 if( pEditSourceHint ) 1502 { 1503 maEventQueue.Append( *pEditSourceHint ); 1504 // --> OD 2005-12-19 #i27299# 1505 if( maEventOpenFrames == 0 ) 1506 ProcessQueue(); 1507 // <-- 1508 } 1509 else if( pTextHint ) 1510 { 1511 switch( pTextHint->GetId() ) 1512 { 1513 case TEXT_HINT_BLOCKNOTIFICATION_END: 1514 case TEXT_HINT_INPUT_END: 1515 --maEventOpenFrames; 1516 1517 if( maEventOpenFrames == 0 ) 1518 { 1519 // #103483# 1520 /* All information should have arrived 1521 * now, process queue. As stated in the 1522 * above bug, we can often avoid throwing 1523 * away all paragraphs by looking forward 1524 * in the event queue (searching for 1525 * PARAINSERT/REMOVE events). Furthermore, 1526 * processing the event queue only at the 1527 * end of an interaction cycle, ensures 1528 * that the EditEngine state and the 1529 * AccessibleText state are the same 1530 * (well, mostly. If there are _multiple_ 1531 * interaction cycles in the EE queues, it 1532 * can still happen that EE state is 1533 * different. That's so to say broken by 1534 * design with that delayed EE event 1535 * concept). 1536 */ 1537 ProcessQueue(); 1538 } 1539 break; 1540 1541 case TEXT_HINT_BLOCKNOTIFICATION_START: 1542 case TEXT_HINT_INPUT_START: 1543 ++maEventOpenFrames; 1544 // --> OD 2005-12-19 #i27299# - no FALLTROUGH 1545 // reason: event will not be processes, thus appending 1546 // the event isn't necessary. 1547 break; 1548 // <-- 1549 default: 1550 maEventQueue.Append( *pTextHint ); 1551 // --> OD 2005-12-19 #i27299# 1552 if( maEventOpenFrames == 0 ) 1553 ProcessQueue(); 1554 // <-- 1555 break; 1556 } 1557 } 1558 else if( pViewHint ) 1559 { 1560 maEventQueue.Append( *pViewHint ); 1561 1562 // process visibility right away, if not within an 1563 // open EE notification frame. Otherwise, event 1564 // processing would be delayed until next EE 1565 // notification sequence. 1566 if( maEventOpenFrames == 0 ) 1567 ProcessQueue(); 1568 } 1569 else if( pSdrHint ) 1570 { 1571 maEventQueue.Append( *pSdrHint ); 1572 1573 // process drawing layer events right away, if not 1574 // within an open EE notification frame. Otherwise, 1575 // event processing would be delayed until next EE 1576 // notification sequence. 1577 if( maEventOpenFrames == 0 ) 1578 ProcessQueue(); 1579 } 1580 // it's VITAL to keep the SfxSimpleHint last! It's the base of some classes above! 1581 else if( pSimpleHint ) 1582 { 1583 // handle this event _at once_, because after that, objects are invalid 1584 switch( pSimpleHint->GetId() ) 1585 { 1586 case SFX_HINT_DYING: 1587 // edit source is dying under us, become defunc then 1588 maEventQueue.Clear(); 1589 try 1590 { 1591 // make edit source inaccessible 1592 // Note: cannot destroy it here, since we're called from there! 1593 ShutdownEditSource(); 1594 } 1595 catch( const uno::Exception& ) {} 1596 1597 break; 1598 } 1599 } 1600 } 1601 catch( const uno::Exception& ) 1602 { 1603 #ifdef DBG_UTIL 1604 OSL_TRACE("AccessibleTextHelper_Impl::Notify: Unhandled exception."); 1605 #endif 1606 mbInNotify = sal_False; 1607 } 1608 1609 mbInNotify = sal_False; 1610 } 1611 Dispose()1612 void AccessibleTextHelper_Impl::Dispose() 1613 { 1614 DBG_CHKTHIS( AccessibleTextHelper_Impl, NULL ); 1615 1616 if( getNotifierClientId() != -1 ) 1617 { 1618 try 1619 { 1620 // #106234# Unregister from EventNotifier 1621 ::comphelper::AccessibleEventNotifier::revokeClient( getNotifierClientId() ); 1622 #ifdef DBG_UTIL 1623 OSL_TRACE( "AccessibleTextHelper_Impl disposed ID: %d", mnNotifierClientId ); 1624 #endif 1625 } 1626 catch( const uno::Exception& ) {} 1627 1628 mnNotifierClientId = -1; 1629 } 1630 1631 try 1632 { 1633 // dispose children 1634 maParaManager.Dispose(); 1635 } 1636 catch( const uno::Exception& ) {} 1637 1638 // quit listen on stale edit source 1639 if( maEditSource.IsValid() ) 1640 EndListening( maEditSource.GetBroadcaster() ); 1641 1642 // clear references 1643 maEditSource.SetEditSource( ::std::auto_ptr< SvxEditSource >(NULL) ); 1644 mxFrontEnd = NULL; 1645 } 1646 FireEvent(const sal_Int16 nEventId,const uno::Any & rNewValue,const uno::Any & rOldValue) const1647 void AccessibleTextHelper_Impl::FireEvent( const sal_Int16 nEventId, const uno::Any& rNewValue, const uno::Any& rOldValue ) const 1648 { 1649 DBG_CHKTHIS( AccessibleTextHelper_Impl, NULL ); 1650 1651 // -- object locked -- 1652 ::osl::ClearableMutexGuard aGuard( maMutex ); 1653 1654 AccessibleEventObject aEvent; 1655 1656 DBG_ASSERT(mxFrontEnd.is(), "AccessibleTextHelper::FireEvent: no event source set" ); 1657 1658 if( mxFrontEnd.is() ) 1659 aEvent = AccessibleEventObject(mxFrontEnd->getAccessibleContext(), nEventId, rNewValue, rOldValue); 1660 else 1661 aEvent = AccessibleEventObject(uno::Reference< uno::XInterface >(), nEventId, rNewValue, rOldValue); 1662 1663 // no locking necessary, FireEvent internally copies listeners 1664 // if someone removes/adds in between Further locking, 1665 // actually, might lead to deadlocks, since we're calling out 1666 // of this object 1667 aGuard.clear(); 1668 // -- until here -- 1669 1670 FireEvent(aEvent); 1671 } 1672 FireEvent(const AccessibleEventObject & rEvent) const1673 void AccessibleTextHelper_Impl::FireEvent( const AccessibleEventObject& rEvent ) const 1674 { 1675 DBG_CHKTHIS( AccessibleTextHelper_Impl, NULL ); 1676 1677 // #102261# Call global queue for focus events 1678 if( rEvent.EventId == AccessibleStateType::FOCUSED ) 1679 vcl::unohelper::NotifyAccessibleStateEventGlobally( rEvent ); 1680 1681 // #106234# Delegate to EventNotifier 1682 ::comphelper::AccessibleEventNotifier::addEvent( getNotifierClientId(), 1683 rEvent ); 1684 } 1685 1686 // XAccessibleContext getAccessibleChildCount()1687 sal_Int32 SAL_CALL AccessibleTextHelper_Impl::getAccessibleChildCount() SAL_THROW((uno::RuntimeException)) 1688 { 1689 DBG_CHKTHIS( AccessibleTextHelper_Impl, NULL ); 1690 1691 return mnLastVisibleChild - mnFirstVisibleChild + 1; 1692 } 1693 getAccessibleChild(sal_Int32 i)1694 uno::Reference< XAccessible > SAL_CALL AccessibleTextHelper_Impl::getAccessibleChild( sal_Int32 i ) SAL_THROW((lang::IndexOutOfBoundsException, uno::RuntimeException)) 1695 { 1696 DBG_CHKTHIS( AccessibleTextHelper_Impl, NULL ); 1697 1698 i -= GetStartIndex(); 1699 1700 if( 0 > i || i >= getAccessibleChildCount() || 1701 GetTextForwarder().GetParagraphCount() <= i ) 1702 { 1703 throw lang::IndexOutOfBoundsException(::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("Invalid child index")), mxFrontEnd); 1704 } 1705 1706 DBG_ASSERT(mxFrontEnd.is(), "AccessibleTextHelper_Impl::UpdateVisibleChildren: no frontend set"); 1707 1708 if( mxFrontEnd.is() ) 1709 return maParaManager.CreateChild( i, mxFrontEnd, GetEditSource(), mnFirstVisibleChild + i ).first; 1710 else 1711 return NULL; 1712 } 1713 addEventListener(const uno::Reference<XAccessibleEventListener> & xListener)1714 void SAL_CALL AccessibleTextHelper_Impl::addEventListener( const uno::Reference< XAccessibleEventListener >& xListener ) SAL_THROW((uno::RuntimeException)) 1715 { 1716 DBG_CHKTHIS( AccessibleTextHelper_Impl, NULL ); 1717 1718 if( getNotifierClientId() != -1 ) 1719 ::comphelper::AccessibleEventNotifier::addEventListener( getNotifierClientId(), xListener ); 1720 } 1721 removeEventListener(const uno::Reference<XAccessibleEventListener> & xListener)1722 void SAL_CALL AccessibleTextHelper_Impl::removeEventListener( const uno::Reference< XAccessibleEventListener >& xListener ) SAL_THROW((uno::RuntimeException)) 1723 { 1724 DBG_CHKTHIS( AccessibleTextHelper_Impl, NULL ); 1725 1726 if( getNotifierClientId() != -1 ) 1727 ::comphelper::AccessibleEventNotifier::removeEventListener( getNotifierClientId(), xListener ); 1728 } 1729 getAccessibleAtPoint(const awt::Point & _aPoint)1730 uno::Reference< XAccessible > SAL_CALL AccessibleTextHelper_Impl::getAccessibleAtPoint( const awt::Point& _aPoint ) SAL_THROW((uno::RuntimeException)) 1731 { 1732 DBG_CHKTHIS( AccessibleTextHelper_Impl, NULL ); 1733 1734 // make given position relative 1735 if( !mxFrontEnd.is() ) 1736 throw uno::RuntimeException(::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("AccessibleTextHelper_Impl::getAccessibleAt: frontend invalid")), mxFrontEnd ); 1737 1738 uno::Reference< XAccessibleContext > xFrontEndContext = mxFrontEnd->getAccessibleContext(); 1739 1740 if( !xFrontEndContext.is() ) 1741 throw uno::RuntimeException(::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("AccessibleTextHelper_Impl::getAccessibleAt: frontend invalid")), mxFrontEnd ); 1742 1743 uno::Reference< XAccessibleComponent > xFrontEndComponent( xFrontEndContext, uno::UNO_QUERY ); 1744 1745 if( !xFrontEndComponent.is() ) 1746 throw uno::RuntimeException(::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("AccessibleTextHelper_Impl::getAccessibleAt: frontend is no XAccessibleComponent")), 1747 mxFrontEnd ); 1748 1749 // #103862# No longer need to make given position relative 1750 Point aPoint( _aPoint.X, _aPoint.Y ); 1751 1752 // respect EditEngine offset to surrounding shape/cell 1753 aPoint -= GetOffset(); 1754 1755 // convert to EditEngine coordinate system 1756 SvxTextForwarder& rCacheTF = GetTextForwarder(); 1757 Point aLogPoint( GetViewForwarder().PixelToLogic( aPoint, rCacheTF.GetMapMode() ) ); 1758 1759 // iterate over all visible children (including those not yet created) 1760 sal_Int32 nChild; 1761 for( nChild=mnFirstVisibleChild; nChild <= mnLastVisibleChild; ++nChild ) 1762 { 1763 DBG_ASSERT(nChild >= 0 && nChild <= USHRT_MAX, 1764 "AccessibleTextHelper_Impl::getAccessibleAt: index value overflow"); 1765 1766 Rectangle aParaBounds( rCacheTF.GetParaBounds( nChild ) ); 1767 1768 if( aParaBounds.IsInside( aLogPoint ) ) 1769 return getAccessibleChild( nChild - mnFirstVisibleChild + GetStartIndex() ); 1770 } 1771 1772 // found none 1773 return NULL; 1774 } 1775 1776 //------------------------------------------------------------------------ 1777 // 1778 // AccessibleTextHelper implementation (simply forwards to impl) 1779 // 1780 //------------------------------------------------------------------------ 1781 AccessibleTextHelper(::std::auto_ptr<SvxEditSource> pEditSource)1782 AccessibleTextHelper::AccessibleTextHelper( ::std::auto_ptr< SvxEditSource > pEditSource ) : 1783 mpImpl( new AccessibleTextHelper_Impl() ) 1784 { 1785 ::vos::OGuard aGuard( Application::GetSolarMutex() ); 1786 1787 SetEditSource( pEditSource ); 1788 } 1789 ~AccessibleTextHelper()1790 AccessibleTextHelper::~AccessibleTextHelper() 1791 { 1792 } 1793 GetEditSource() const1794 const SvxEditSource& AccessibleTextHelper::GetEditSource() const SAL_THROW((uno::RuntimeException)) 1795 { 1796 #ifdef DBG_UTIL 1797 mpImpl->CheckInvariants(); 1798 1799 const SvxEditSource& aEditSource = mpImpl->GetEditSource(); 1800 1801 mpImpl->CheckInvariants(); 1802 1803 return aEditSource; 1804 #else 1805 return mpImpl->GetEditSource(); 1806 #endif 1807 } 1808 SetEditSource(::std::auto_ptr<SvxEditSource> pEditSource)1809 void AccessibleTextHelper::SetEditSource( ::std::auto_ptr< SvxEditSource > pEditSource ) SAL_THROW((uno::RuntimeException)) 1810 { 1811 #ifdef DBG_UTIL 1812 // precondition: solar mutex locked 1813 DBG_TESTSOLARMUTEX(); 1814 1815 mpImpl->CheckInvariants(); 1816 #endif 1817 1818 mpImpl->SetEditSource( pEditSource ); 1819 1820 #ifdef DBG_UTIL 1821 mpImpl->CheckInvariants(); 1822 #endif 1823 } 1824 SetEventSource(const uno::Reference<XAccessible> & rInterface)1825 void AccessibleTextHelper::SetEventSource( const uno::Reference< XAccessible >& rInterface ) 1826 { 1827 #ifdef DBG_UTIL 1828 mpImpl->CheckInvariants(); 1829 #endif 1830 1831 mpImpl->SetEventSource( rInterface ); 1832 1833 #ifdef DBG_UTIL 1834 mpImpl->CheckInvariants(); 1835 #endif 1836 } 1837 GetEventSource() const1838 uno::Reference< XAccessible > AccessibleTextHelper::GetEventSource() const 1839 { 1840 #ifdef DBG_UTIL 1841 mpImpl->CheckInvariants(); 1842 1843 uno::Reference< XAccessible > xRet( mpImpl->GetEventSource() ); 1844 1845 mpImpl->CheckInvariants(); 1846 1847 return xRet; 1848 #else 1849 return mpImpl->GetEventSource(); 1850 #endif 1851 } 1852 SetFocus(sal_Bool bHaveFocus)1853 void AccessibleTextHelper::SetFocus( sal_Bool bHaveFocus ) SAL_THROW((::com::sun::star::uno::RuntimeException)) 1854 { 1855 #ifdef DBG_UTIL 1856 // precondition: solar mutex locked 1857 DBG_TESTSOLARMUTEX(); 1858 1859 mpImpl->CheckInvariants(); 1860 #endif 1861 1862 mpImpl->SetFocus( bHaveFocus ); 1863 1864 #ifdef DBG_UTIL 1865 mpImpl->CheckInvariants(); 1866 #endif 1867 } 1868 HaveFocus()1869 sal_Bool AccessibleTextHelper::HaveFocus() SAL_THROW((::com::sun::star::uno::RuntimeException)) 1870 { 1871 #ifdef DBG_UTIL 1872 mpImpl->CheckInvariants(); 1873 1874 sal_Bool bRet( mpImpl->HaveFocus() ); 1875 1876 mpImpl->CheckInvariants(); 1877 1878 return bRet; 1879 #else 1880 return mpImpl->HaveFocus(); 1881 #endif 1882 } 1883 FireEvent(const sal_Int16 nEventId,const uno::Any & rNewValue,const uno::Any & rOldValue) const1884 void AccessibleTextHelper::FireEvent( const sal_Int16 nEventId, const uno::Any& rNewValue, const uno::Any& rOldValue ) const 1885 { 1886 #ifdef DBG_UTIL 1887 mpImpl->CheckInvariants(); 1888 #endif 1889 1890 mpImpl->FireEvent( nEventId, rNewValue, rOldValue ); 1891 1892 #ifdef DBG_UTIL 1893 mpImpl->CheckInvariants(); 1894 #endif 1895 } 1896 FireEvent(const AccessibleEventObject & rEvent) const1897 void AccessibleTextHelper::FireEvent( const AccessibleEventObject& rEvent ) const 1898 { 1899 #ifdef DBG_UTIL 1900 mpImpl->CheckInvariants(); 1901 #endif 1902 1903 mpImpl->FireEvent( rEvent ); 1904 1905 #ifdef DBG_UTIL 1906 mpImpl->CheckInvariants(); 1907 #endif 1908 } 1909 SetOffset(const Point & rPoint)1910 void AccessibleTextHelper::SetOffset( const Point& rPoint ) 1911 { 1912 #ifdef DBG_UTIL 1913 // precondition: solar mutex locked 1914 DBG_TESTSOLARMUTEX(); 1915 1916 mpImpl->CheckInvariants(); 1917 #endif 1918 1919 mpImpl->SetOffset( rPoint ); 1920 1921 #ifdef DBG_UTIL 1922 mpImpl->CheckInvariants(); 1923 #endif 1924 } 1925 GetOffset() const1926 Point AccessibleTextHelper::GetOffset() const 1927 { 1928 #ifdef DBG_UTIL 1929 mpImpl->CheckInvariants(); 1930 1931 Point aPoint( mpImpl->GetOffset() ); 1932 1933 mpImpl->CheckInvariants(); 1934 1935 return aPoint; 1936 #else 1937 return mpImpl->GetOffset(); 1938 #endif 1939 } 1940 SetStartIndex(sal_Int32 nOffset)1941 void AccessibleTextHelper::SetStartIndex( sal_Int32 nOffset ) 1942 { 1943 #ifdef DBG_UTIL 1944 // precondition: solar mutex locked 1945 DBG_TESTSOLARMUTEX(); 1946 1947 mpImpl->CheckInvariants(); 1948 #endif 1949 1950 mpImpl->SetStartIndex( nOffset ); 1951 1952 #ifdef DBG_UTIL 1953 mpImpl->CheckInvariants(); 1954 #endif 1955 } 1956 GetStartIndex() const1957 sal_Int32 AccessibleTextHelper::GetStartIndex() const 1958 { 1959 #ifdef DBG_UTIL 1960 mpImpl->CheckInvariants(); 1961 1962 sal_Int32 nOffset = mpImpl->GetStartIndex(); 1963 1964 mpImpl->CheckInvariants(); 1965 1966 return nOffset; 1967 #else 1968 return mpImpl->GetStartIndex(); 1969 #endif 1970 } 1971 SetAdditionalChildStates(const VectorOfStates & rChildStates)1972 void AccessibleTextHelper::SetAdditionalChildStates( const VectorOfStates& rChildStates ) 1973 { 1974 mpImpl->SetAdditionalChildStates( rChildStates ); 1975 } 1976 GetAdditionalChildStates() const1977 const AccessibleTextHelper::VectorOfStates& AccessibleTextHelper::GetAdditionalChildStates() const 1978 { 1979 return mpImpl->GetAdditionalChildStates(); 1980 } 1981 UpdateChildren()1982 void AccessibleTextHelper::UpdateChildren() SAL_THROW((::com::sun::star::uno::RuntimeException)) 1983 { 1984 #ifdef DBG_UTIL 1985 // precondition: solar mutex locked 1986 DBG_TESTSOLARMUTEX(); 1987 1988 mpImpl->CheckInvariants(); 1989 #endif 1990 1991 mpImpl->UpdateVisibleChildren(); 1992 mpImpl->UpdateBoundRect(); 1993 1994 mpImpl->UpdateSelection(); 1995 1996 #ifdef DBG_UTIL 1997 mpImpl->CheckInvariants(); 1998 #endif 1999 } 2000 Dispose()2001 void AccessibleTextHelper::Dispose() 2002 { 2003 // As Dispose calls ShutdownEditSource, which in turn 2004 // deregisters as listener on the edit source, have to lock 2005 // here 2006 ::vos::OGuard aGuard( Application::GetSolarMutex() ); 2007 2008 #ifdef DBG_UTIL 2009 mpImpl->CheckInvariants(); 2010 #endif 2011 2012 mpImpl->Dispose(); 2013 2014 #ifdef DBG_UTIL 2015 mpImpl->CheckInvariants(); 2016 #endif 2017 } 2018 IsSelected() const2019 sal_Bool AccessibleTextHelper::IsSelected() const 2020 { 2021 ::vos::OGuard aGuard( Application::GetSolarMutex() ); 2022 2023 #ifdef DBG_UTIL 2024 mpImpl->CheckInvariants(); 2025 2026 sal_Bool aRet = mpImpl->IsSelected(); 2027 2028 mpImpl->CheckInvariants(); 2029 2030 return aRet; 2031 #else 2032 return mpImpl->IsSelected(); 2033 #endif 2034 } 2035 2036 // XAccessibleContext GetChildCount()2037 sal_Int32 AccessibleTextHelper::GetChildCount() SAL_THROW((uno::RuntimeException)) 2038 { 2039 ::vos::OGuard aGuard( Application::GetSolarMutex() ); 2040 2041 #ifdef DBG_UTIL 2042 mpImpl->CheckInvariants(); 2043 2044 sal_Int32 nRet = mpImpl->getAccessibleChildCount(); 2045 2046 mpImpl->CheckInvariants(); 2047 2048 return nRet; 2049 #else 2050 return mpImpl->getAccessibleChildCount(); 2051 #endif 2052 } 2053 GetChild(sal_Int32 i)2054 uno::Reference< XAccessible > AccessibleTextHelper::GetChild( sal_Int32 i ) SAL_THROW((lang::IndexOutOfBoundsException, uno::RuntimeException)) 2055 { 2056 ::vos::OGuard aGuard( Application::GetSolarMutex() ); 2057 2058 #ifdef DBG_UTIL 2059 mpImpl->CheckInvariants(); 2060 2061 uno::Reference< XAccessible > xRet = mpImpl->getAccessibleChild( i ); 2062 2063 mpImpl->CheckInvariants(); 2064 2065 return xRet; 2066 #else 2067 return mpImpl->getAccessibleChild( i ); 2068 #endif 2069 } 2070 AddEventListener(const uno::Reference<XAccessibleEventListener> & xListener)2071 void AccessibleTextHelper::AddEventListener( const uno::Reference< XAccessibleEventListener >& xListener ) SAL_THROW((uno::RuntimeException)) 2072 { 2073 #ifdef DBG_UTIL 2074 mpImpl->CheckInvariants(); 2075 2076 mpImpl->addEventListener( xListener ); 2077 2078 mpImpl->CheckInvariants(); 2079 #else 2080 mpImpl->addEventListener( xListener ); 2081 #endif 2082 } 2083 RemoveEventListener(const uno::Reference<XAccessibleEventListener> & xListener)2084 void AccessibleTextHelper::RemoveEventListener( const uno::Reference< XAccessibleEventListener >& xListener ) SAL_THROW((uno::RuntimeException)) 2085 { 2086 #ifdef DBG_UTIL 2087 mpImpl->CheckInvariants(); 2088 2089 mpImpl->removeEventListener( xListener ); 2090 2091 mpImpl->CheckInvariants(); 2092 #else 2093 mpImpl->removeEventListener( xListener ); 2094 #endif 2095 } 2096 2097 // XAccessibleComponent GetAt(const awt::Point & aPoint)2098 uno::Reference< XAccessible > AccessibleTextHelper::GetAt( const awt::Point& aPoint ) SAL_THROW((uno::RuntimeException)) 2099 { 2100 ::vos::OGuard aGuard( Application::GetSolarMutex() ); 2101 2102 #ifdef DBG_UTIL 2103 mpImpl->CheckInvariants(); 2104 2105 uno::Reference< XAccessible > xChild = mpImpl->getAccessibleAtPoint( aPoint ); 2106 2107 mpImpl->CheckInvariants(); 2108 2109 return xChild; 2110 #else 2111 return mpImpl->getAccessibleAtPoint( aPoint ); 2112 #endif 2113 } 2114 2115 } // end of namespace accessibility 2116 2117 //------------------------------------------------------------------------ 2118