LibreOffice Module svx (master) 1
AccessibleTextHelper.cxx
Go to the documentation of this file.
1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2/*
3 * This file is part of the LibreOffice project.
4 *
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
18 */
19
20#include <sal/config.h>
21
22#include <cstdlib>
23#include <memory>
24#include <mutex>
25#include <utility>
26#include <algorithm>
27#include <sal/log.hxx>
28#include <com/sun/star/uno/Any.hxx>
29#include <com/sun/star/uno/Reference.hxx>
30#include <com/sun/star/awt/Point.hpp>
31#include <com/sun/star/awt/Rectangle.hpp>
32#include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
33#include <com/sun/star/accessibility/AccessibleEventId.hpp>
34#include <com/sun/star/accessibility/XAccessible.hpp>
35#include <com/sun/star/accessibility/XAccessibleContext.hpp>
36#include <com/sun/star/accessibility/XAccessibleComponent.hpp>
37#include <com/sun/star/accessibility/AccessibleStateType.hpp>
39#include <vcl/svapp.hxx>
40#include <vcl/textdata.hxx>
41#include <vcl/unohelp.hxx>
42
43
44// Project-local header
45
46
49
50#include <editeng/unoedhlp.hxx>
51#include <editeng/unoedprx.hxx>
54#include <svx/svdmodel.hxx>
55#include <svx/svdpntv.hxx>
56#include <cell.hxx>
57#include "../table/accessiblecell.hxx"
58#include <editeng/editdata.hxx>
59#include <tools/debug.hxx>
61
62using namespace ::com::sun::star;
63using namespace ::com::sun::star::accessibility;
64
65namespace accessibility
66{
67
68// AccessibleTextHelper_Impl declaration
69
70 template < typename first_type, typename second_type >
71 static ::std::pair< first_type, second_type > makeSortedPair( first_type first,
72 second_type second )
73 {
74 if( first > second )
75 return ::std::make_pair( second, first );
76 else
77 return ::std::make_pair( first, second );
78 }
79
81 {
82 public:
83 typedef ::std::vector< sal_Int16 > VectorOfStates;
84
85 // receive pointer to our frontend class and view window
87 virtual ~AccessibleTextHelper_Impl() override;
88
89 // XAccessibleContext child handling methods
90 sal_Int64 getAccessibleChildCount() const;
91 uno::Reference< XAccessible > getAccessibleChild( sal_Int64 i );
92
93 // XAccessibleEventBroadcaster child related methods
94 void addAccessibleEventListener( const uno::Reference< XAccessibleEventListener >& xListener );
95 void removeAccessibleEventListener( const uno::Reference< XAccessibleEventListener >& xListener );
96
97 // XAccessibleComponent child related methods
98 uno::Reference< XAccessible > getAccessibleAtPoint( const awt::Point& aPoint );
99
100 SvxEditSourceAdapter& GetEditSource() const;
101
102 void SetEditSource( ::std::unique_ptr< SvxEditSource > && pEditSource );
103
104 void SetEventSource( const uno::Reference< XAccessible >& rInterface )
105 {
106 mxFrontEnd = rInterface;
107 }
108
109 void SetOffset( const Point& );
111 {
112 std::scoped_lock aGuard( maMutex ); Point aPoint( maOffset );
113 return aPoint;
114 }
115
116 void SetStartIndex( sal_Int32 nOffset );
117 sal_Int32 GetStartIndex() const
118 {
119 // Strictly correct only with locked solar mutex, // but
120 // here we rely on the fact that sal_Int32 access is
121 // atomic
122 return mnStartIndex;
123 }
124
125 void SetAdditionalChildStates( sal_Int64 nChildStates );
126
127 void Dispose();
128
129 // do NOT hold object mutex when calling this! Danger of deadlock
130 void FireEvent( const sal_Int16 nEventId, const uno::Any& rNewValue = uno::Any(), const uno::Any& rOldValue = uno::Any() ) const;
131 void FireEvent( const AccessibleEventObject& rEvent ) const;
132
133 void SetFocus( bool bHaveFocus );
134 bool HaveFocus() const
135 {
136 // No locking of solar mutex here, since we rely on the fact
137 // that sal_Bool access is atomic
138 return mbThisHasFocus;
139 }
140 void SetChildFocus( sal_Int32 nChild, bool bHaveFocus );
141 void SetShapeFocus( bool bHaveFocus );
142 void ChangeChildFocus( sal_Int32 nNewChild );
143
144#ifdef DBG_UTIL
145 void CheckInvariants() const;
146#endif
147
148 // checks all children for visibility, throws away invisible ones
149 void UpdateVisibleChildren( bool bBroadcastEvents=true );
150
151 // check all children for changes in position and size
152 void UpdateBoundRect();
153
154 // calls SetSelection on the forwarder and updates maLastSelection
155 // cache.
156 void UpdateSelection();
157
158 private:
159
160 // Process event queue
161 void ProcessQueue();
162
163 // syntactic sugar for FireEvent
164 void GotPropertyEvent( const uno::Any& rNewValue, const sal_Int16 nEventId ) const { FireEvent( nEventId, rNewValue ); }
165
166 // shutdown usage of current edit source on myself and the children.
167 void ShutdownEditSource();
168
169 void ParagraphsMoved( sal_Int32 nFirst, sal_Int32 nMiddle, sal_Int32 nLast );
170
171 virtual void Notify( SfxBroadcaster& rBC, const SfxHint& rHint ) override;
172
174
175 // lock solar mutex before
177 // lock solar mutex before
179 // lock solar mutex before
181
182 // are we in edit mode?
183 bool IsActive() const;
184
185 // our frontend class (the one implementing the actual
186 // interface). That's not necessarily the one containing the impl
187 // pointer!
188 uno::Reference< XAccessible > mxFrontEnd;
189
190 // a wrapper for the text forwarders (guarded by solar mutex)
191 mutable SvxEditSourceAdapter maEditSource;
192
193 // store last selection (to correctly report selection changes, guarded by solar mutex)
195
196 // cache range of visible children (guarded by solar mutex)
199
200 // offset to add to all our children (unguarded, relying on
201 // the fact that sal_Int32 access is atomic)
202 sal_Int32 mnStartIndex;
203
204 // the object handling our children (guarded by solar mutex)
205 ::accessibility::AccessibleParaManager maParaManager;
206
207 // Queued events from Notify() (guarded by solar mutex)
209
210 // spin lock to prevent notify in notify (guarded by solar mutex)
212
213 // whether the object or its children has the focus set (guarded by solar mutex)
215
216 // whether we (this object) has the focus set (guarded by solar mutex)
218
219 mutable std::mutex maMutex;
220
223
227 = std::numeric_limits<comphelper::AccessibleEventNotifier::TClientId>::max();
228 };
229
232 mnFirstVisibleChild( -1 ),
233 mnLastVisibleChild( -2 ),
234 mnStartIndex( 0 ),
235 mbInNotify( false ),
236 mbGroupHasFocus( false ),
237 mbThisHasFocus( false ),
238 maOffset(0,0),
239 // well, that's strictly exception safe, though not really
240 // robust. We rely on the fact that this member is constructed
241 // last, and that the constructor body is empty, thus no
242 // chance for exceptions once the Id is fetched. Nevertheless,
243 // normally should employ RAII here...
244 mnNotifierClientId(::comphelper::AccessibleEventNotifier::registerClient())
245 {
246 SAL_INFO("svx", "received ID: " << mnNotifierClientId );
247 }
248
250 {
251 SolarMutexGuard aGuard;
252
253 try
254 {
255 // call Dispose here, too, since we've some resources not
256 // automatically freed otherwise
257 Dispose();
258 }
259 catch( const uno::Exception& ) {}
260 }
261
263 {
264 if( !maEditSource.IsValid() )
265 throw uno::RuntimeException("Unknown edit source", mxFrontEnd);
266
267 SvxTextForwarder* pTextForwarder = maEditSource.GetTextForwarder();
268
269 if( !pTextForwarder )
270 throw uno::RuntimeException("Unable to fetch text forwarder, model might be dead", mxFrontEnd);
271
272 if( !pTextForwarder->IsValid() )
273 throw uno::RuntimeException("Text forwarder is invalid, model might be dead", mxFrontEnd);
274
275 return *pTextForwarder;
276 }
277
279 {
280 if( !maEditSource.IsValid() )
281 throw uno::RuntimeException("Unknown edit source", mxFrontEnd);
282
283 SvxViewForwarder* pViewForwarder = maEditSource.GetViewForwarder();
284
285 if( !pViewForwarder )
286 throw uno::RuntimeException("Unable to fetch view forwarder, model might be dead", mxFrontEnd);
287
288 if( !pViewForwarder->IsValid() )
289 throw uno::RuntimeException("View forwarder is invalid, model might be dead", mxFrontEnd);
290
291 return *pViewForwarder;
292 }
293
295 {
296 if( !maEditSource.IsValid() )
297 throw uno::RuntimeException("Unknown edit source", mxFrontEnd);
298
299 SvxEditViewForwarder* pViewForwarder = maEditSource.GetEditViewForwarder();
300
301 if( !pViewForwarder )
302 {
303 throw uno::RuntimeException("No edit view forwarder, object not in edit mode", mxFrontEnd);
304 }
305
306 if( !pViewForwarder->IsValid() )
307 {
308 throw uno::RuntimeException("View forwarder is invalid, object not in edit mode", mxFrontEnd);
309 }
310
311 return *pViewForwarder;
312 }
313
314 SvxEditSourceAdapter& AccessibleTextHelper_Impl::GetEditSource() const
315 {
316 if( !maEditSource.IsValid() )
317 throw uno::RuntimeException("AccessibleTextHelper_Impl::GetEditSource: no edit source", mxFrontEnd );
318 return maEditSource;
319 }
320
321 namespace {
322
323 // functor for sending child events (no stand-alone function, they are maybe not inlined)
324 class AccessibleTextHelper_OffsetChildIndex
325 {
326 public:
327 explicit AccessibleTextHelper_OffsetChildIndex( sal_Int32 nDifference ) : mnDifference(nDifference) {}
328 void operator()( ::accessibility::AccessibleEditableTextPara& rPara )
329 {
330 rPara.SetIndexInParent( rPara.GetIndexInParent() + mnDifference );
331 }
332
333 private:
334 const sal_Int32 mnDifference;
335 };
336
337 }
338
340 {
341 sal_Int32 nOldOffset( mnStartIndex );
342
343 mnStartIndex = nOffset;
344
345 if( nOldOffset != nOffset )
346 {
347 // update children
348 AccessibleTextHelper_OffsetChildIndex aFunctor( nOffset - nOldOffset );
349
350 ::std::for_each( maParaManager.begin(), maParaManager.end(),
351 AccessibleParaManager::WeakChildAdapter< AccessibleTextHelper_OffsetChildIndex > (aFunctor) );
352 }
353 }
354
356 {
357 maParaManager.SetAdditionalChildStates( nChildStates );
358 }
359
360 void AccessibleTextHelper_Impl::SetChildFocus( sal_Int32 nChild, bool bHaveFocus )
361 {
362 if( bHaveFocus )
363 {
364 if( mbThisHasFocus )
365 SetShapeFocus( false );
366
367 maParaManager.SetFocus( nChild );
368
369 // we just received the focus, also send caret event then
371
372 SAL_INFO("svx", "Paragraph " << nChild << " received focus");
373 }
374 else
375 {
376 maParaManager.SetFocus( -1 );
377
378 SAL_INFO("svx", "Paragraph " << nChild << " lost focus");
379
380 if( mbGroupHasFocus )
381 SetShapeFocus( true );
382 }
383 }
384
386 {
387 if( mbThisHasFocus )
388 SetShapeFocus( false );
389
390 mbGroupHasFocus = true;
391 maParaManager.SetFocus( nNewChild );
392
393 SAL_INFO("svx", "Paragraph " << nNewChild << " received focus");
394 }
395
397 {
398 bool bOldFocus( mbThisHasFocus );
399
400 mbThisHasFocus = bHaveFocus;
401
402 if( bOldFocus == bHaveFocus )
403 return;
404
405 if( bHaveFocus )
406 {
407 if( mxFrontEnd.is() )
408 {
409 AccessibleCell* pAccessibleCell = dynamic_cast< AccessibleCell* > ( mxFrontEnd.get() );
410 if ( !pAccessibleCell )
411 GotPropertyEvent( uno::Any(AccessibleStateType::FOCUSED), AccessibleEventId::STATE_CHANGED );
412 else // the focus event on cell should be fired on table directly
413 {
414 AccessibleTableShape* pAccTable = pAccessibleCell->GetParentTable();
415 if (pAccTable)
416 pAccTable->SetStateDirectly(AccessibleStateType::FOCUSED);
417 }
418 }
419 SAL_INFO("svx", "Parent object received focus" );
420 }
421 else
422 {
423 // The focus state should be reset directly on table.
424 //LostPropertyEvent( uno::makeAny(AccessibleStateType::FOCUSED), AccessibleEventId::STATE_CHANGED );
425 if( mxFrontEnd.is() )
426 {
427 AccessibleCell* pAccessibleCell = dynamic_cast< AccessibleCell* > ( mxFrontEnd.get() );
428 if ( !pAccessibleCell )
429 FireEvent( AccessibleEventId::STATE_CHANGED, uno::Any(), uno::Any(AccessibleStateType::FOCUSED) );
430 else
431 {
432 AccessibleTableShape* pAccTable = pAccessibleCell->GetParentTable();
433 if (pAccTable)
434 pAccTable->ResetStateDirectly(AccessibleStateType::FOCUSED);
435 }
436 }
437 SAL_INFO("svx", "Parent object lost focus" );
438 }
439 }
440
442 {
443 bool bOldFocus( mbGroupHasFocus );
444
445 mbGroupHasFocus = bHaveFocus;
446
447 if( IsActive() )
448 {
449 try
450 {
451 // find the one with the cursor and get/set focus accordingly
452 ESelection aSelection;
453 if( GetEditViewForwarder().GetSelection( aSelection ) )
454 SetChildFocus( aSelection.nEndPara, bHaveFocus );
455 }
456 catch( const uno::Exception& ) {}
457 }
458 else if( bOldFocus != bHaveFocus )
459 {
460 SetShapeFocus( bHaveFocus );
461 }
462
463 SAL_INFO("svx", "focus changed, Object " << this << ", state: " << (bHaveFocus ? "focused" : "not focused") );
464 }
465
467 {
468 try
469 {
470 SvxEditSource& rEditSource = GetEditSource();
471 SvxEditViewForwarder* pViewForwarder = rEditSource.GetEditViewForwarder();
472
473 if( !pViewForwarder )
474 return false;
475
476 if( mxFrontEnd.is() )
477 {
478 AccessibleCell* pAccessibleCell = dynamic_cast< AccessibleCell* > ( mxFrontEnd.get() );
479 if ( pAccessibleCell )
480 {
481 sdr::table::CellRef xCell = pAccessibleCell->getCellRef();
482 if ( xCell.is() )
483 return xCell->IsActiveCell();
484 }
485 }
486 return pViewForwarder->IsValid();
487 }
488 catch( const uno::RuntimeException& )
489 {
490 return false;
491 }
492 }
493
495 {
496 try
497 {
498 ESelection aSelection;
499 if( GetEditViewForwarder().GetSelection( aSelection ) )
500 {
501 if( maLastSelection != aSelection &&
502 aSelection.nEndPara < maParaManager.GetNum() )
503 {
504 // #103998# Not that important, changed from assertion to trace
505 if( mbThisHasFocus )
506 {
507 SAL_INFO("svx", "Parent has focus!");
508 }
509
510 sal_Int32 nMaxValidParaIndex( GetTextForwarder().GetParagraphCount() - 1 );
511
512 // notify all affected paragraphs (TODO: may be suboptimal,
513 // since some paragraphs might stay selected)
515 {
516 // Did the caret move from one paragraph to another?
517 // #100530# no caret events if not focused.
518 if( mbGroupHasFocus &&
519 maLastSelection.nEndPara != aSelection.nEndPara )
520 {
521 if( maLastSelection.nEndPara < maParaManager.GetNum() )
522 {
523 maParaManager.FireEvent( ::std::min( maLastSelection.nEndPara, nMaxValidParaIndex ),
524 ::std::min( maLastSelection.nEndPara, nMaxValidParaIndex )+1,
525 AccessibleEventId::CARET_CHANGED,
526 uno::Any(static_cast<sal_Int32>(-1)),
528 }
529
530 ChangeChildFocus( aSelection.nEndPara );
531
532 SAL_INFO(
533 "svx",
534 "focus changed, Object: " << this
535 << ", Paragraph: " << aSelection.nEndPara
536 << ", Last paragraph: "
538 }
539 }
540
541 // #100530# no caret events if not focused.
542 if( mbGroupHasFocus )
543 {
544 uno::Any aOldCursor;
545
546 // #i13705# The old cursor can only contain valid
547 // values if it's the same paragraph!
549 maLastSelection.nEndPara == aSelection.nEndPara )
550 {
551 aOldCursor <<= maLastSelection.nEndPos;
552 }
553 else
554 {
555 aOldCursor <<= static_cast<sal_Int32>(-1);
556 }
557
558 maParaManager.FireEvent( aSelection.nEndPara,
559 aSelection.nEndPara+1,
560 AccessibleEventId::CARET_CHANGED,
561 uno::Any(aSelection.nEndPos),
562 aOldCursor );
563 }
564
565 SAL_INFO(
566 "svx",
567 "caret changed, Object: " << this << ", New pos: "
568 << aSelection.nEndPos << ", Old pos: "
569 << maLastSelection.nEndPos << ", New para: "
570 << aSelection.nEndPara << ", Old para: "
572
573 // #108947# Sort new range before calling FireEvent
574 ::std::pair<sal_Int32, sal_Int32> sortedSelection(
575 makeSortedPair(::std::min( aSelection.nStartPara, nMaxValidParaIndex ),
576 ::std::min( aSelection.nEndPara, nMaxValidParaIndex ) ) );
577
578 // #108947# Sort last range before calling FireEvent
579 ::std::pair<sal_Int32, sal_Int32> sortedLastSelection(
580 makeSortedPair(::std::min( maLastSelection.nStartPara, nMaxValidParaIndex ),
581 ::std::min( maLastSelection.nEndPara, nMaxValidParaIndex ) ) );
582
583 // event TEXT_SELECTION_CHANGED has to be submitted. (#i27299#)
584 const sal_Int16 nTextSelChgEventId =
585 AccessibleEventId::TEXT_SELECTION_CHANGED;
586 // #107037# notify selection change
588 {
589 // last selection is undefined
590 // use method <ESelection::HasRange()> (#i27299#)
591 if ( aSelection.HasRange() )
592 {
593 // selection was undefined, now is on
594 maParaManager.FireEvent( sortedSelection.first,
595 sortedSelection.second+1,
596 nTextSelChgEventId );
597 }
598 }
599 else
600 {
601 // last selection is valid
602 // use method <ESelection::HasRange()> (#i27299#)
603 if ( maLastSelection.HasRange() &&
604 !aSelection.HasRange() )
605 {
606 // selection was on, now is empty
607 maParaManager.FireEvent( sortedLastSelection.first,
608 sortedLastSelection.second+1,
609 nTextSelChgEventId );
610 }
611 // use method <ESelection::HasRange()> (#i27299#)
612 else if( !maLastSelection.HasRange() &&
613 aSelection.HasRange() )
614 {
615 // selection was empty, now is on
616 maParaManager.FireEvent( sortedSelection.first,
617 sortedSelection.second+1,
618 nTextSelChgEventId );
619 }
620 // no event TEXT_SELECTION_CHANGED event, if new and
621 // last selection are empty. (#i27299#)
622 else if ( maLastSelection.HasRange() &&
623 aSelection.HasRange() )
624 {
625 // use sorted last and new selection
626 ESelection aTmpLastSel( maLastSelection );
627 aTmpLastSel.Adjust();
628 ESelection aTmpSel( aSelection );
629 aTmpSel.Adjust();
630 // first submit event for new and changed selection
631 sal_Int32 nPara = aTmpSel.nStartPara;
632 for ( ; nPara <= aTmpSel.nEndPara; ++nPara )
633 {
634 if ( nPara < aTmpLastSel.nStartPara ||
635 nPara > aTmpLastSel.nEndPara )
636 {
637 // new selection on paragraph <nPara>
638 maParaManager.FireEvent( nPara,
639 nTextSelChgEventId );
640 }
641 else
642 {
643 // check for changed selection on paragraph <nPara>
644 const sal_Int32 nParaStartPos =
645 nPara == aTmpSel.nStartPara
646 ? aTmpSel.nStartPos : 0;
647 const sal_Int32 nParaEndPos =
648 nPara == aTmpSel.nEndPara
649 ? aTmpSel.nEndPos : -1;
650 const sal_Int32 nLastParaStartPos =
651 nPara == aTmpLastSel.nStartPara
652 ? aTmpLastSel.nStartPos : 0;
653 const sal_Int32 nLastParaEndPos =
654 nPara == aTmpLastSel.nEndPara
655 ? aTmpLastSel.nEndPos : -1;
656 if ( nParaStartPos != nLastParaStartPos ||
657 nParaEndPos != nLastParaEndPos )
658 {
659 maParaManager.FireEvent(
660 nPara, nTextSelChgEventId );
661 }
662 }
663 }
664 // second submit event for 'old' selections
665 nPara = aTmpLastSel.nStartPara;
666 for ( ; nPara <= aTmpLastSel.nEndPara; ++nPara )
667 {
668 if ( nPara < aTmpSel.nStartPara ||
669 nPara > aTmpSel.nEndPara )
670 {
671 maParaManager.FireEvent( nPara,
672 nTextSelChgEventId );
673 }
674 }
675 }
676 }
677
678 maLastSelection = aSelection;
679 }
680 }
681 }
682 // no selection? no update actions
683 catch( const uno::RuntimeException& ) {}
684 }
685
687 {
688 // This should only be called with solar mutex locked, i.e. from the main office thread
689
690 // This here is somewhat clumsy: As soon as our children have
691 // a NULL EditSource (maParaManager.SetEditSource()), they
692 // enter the disposed state and cannot be reanimated. Thus, it
693 // is unavoidable and a hard requirement to let go and create
694 // from scratch each and every child.
695
696 // invalidate children
697 maParaManager.Dispose();
698 maParaManager.SetNum(0);
699
700 // lost all children
701 if( mxFrontEnd.is() )
702 FireEvent(AccessibleEventId::INVALIDATE_ALL_CHILDREN);
703
704 // quit listen on stale edit source
705 if( maEditSource.IsValid() )
706 EndListening( maEditSource.GetBroadcaster() );
707
708 maEditSource.SetEditSource( ::std::unique_ptr< SvxEditSource >() );
709 }
710
711 void AccessibleTextHelper_Impl::SetEditSource( ::std::unique_ptr< SvxEditSource > && pEditSource )
712 {
713 // This should only be called with solar mutex locked, i.e. from the main office thread
714
715 // shutdown old edit source
717
718 // set new edit source
719 maEditSource.SetEditSource( std::move(pEditSource) );
720
721 // init child vector to the current child count
722 if( maEditSource.IsValid() )
723 {
724 maParaManager.SetNum( GetTextForwarder().GetParagraphCount() );
725
726 // listen on new edit source
727 StartListening( maEditSource.GetBroadcaster() );
728
730 }
731 }
732
734 {
735 // guard against non-atomic access to maOffset data structure
736 {
737 std::scoped_lock aGuard( maMutex );
738 maOffset = rPoint;
739 }
740
741 maParaManager.SetEEOffset( rPoint );
742
743 // in all cases, check visibility afterwards.
746 }
747
749 {
750 try
751 {
753 sal_Int32 nParas=rCacheTF.GetParagraphCount();
754
755 // GetTextForwarder might have replaced everything, update
756 // paragraph count in case it's outdated
757 maParaManager.SetNum( nParas );
758
761
762 for( sal_Int32 nCurrPara=0; nCurrPara<nParas; ++nCurrPara )
763 {
764 if (nCurrPara == 0)
765 mnFirstVisibleChild = nCurrPara;
766 mnLastVisibleChild = nCurrPara;
767 if (mxFrontEnd.is() && bBroadcastEvents)
768 {
769 // child not yet created?
770 if (!maParaManager.HasCreatedChild(nCurrPara))
771 {
773 mxFrontEnd, GetEditSource(), nCurrPara ).first ),
774 AccessibleEventId::CHILD );
775 }
776 }
777 }
778 }
779 catch( const uno::Exception& )
780 {
781 OSL_FAIL("AccessibleTextHelper_Impl::UpdateVisibleChildren error while determining visible children");
782
783 // something failed - currently no children
786 maParaManager.SetNum(0);
787
788 // lost all children
789 if( bBroadcastEvents )
790 FireEvent(AccessibleEventId::INVALIDATE_ALL_CHILDREN);
791 }
792 }
793
795 {
796 // send BOUNDRECT_CHANGED to affected children
797 for(auto it = maParaManager.begin(); it != maParaManager.end(); ++it)
798 {
799 ::accessibility::AccessibleParaManager::WeakChild& rChild = *it;
800 // retrieve hard reference from weak one
801 auto aHardRef( rChild.first.get() );
802
803 if( aHardRef.is() )
804 {
805 awt::Rectangle aNewRect = aHardRef->getBounds();
806 const awt::Rectangle& aOldRect = rChild.second;
807
808 if( aNewRect.X != aOldRect.X ||
809 aNewRect.Y != aOldRect.Y ||
810 aNewRect.Width != aOldRect.Width ||
811 aNewRect.Height != aOldRect.Height )
812 {
813 // visible data changed
814 aHardRef->FireEvent( AccessibleEventId::BOUNDRECT_CHANGED );
815
816 // update internal bounds
817 rChild = ::accessibility::AccessibleParaManager::WeakChild( rChild.first, aNewRect );
818 }
819 }
820 }
821 }
822
823#ifdef DBG_UTIL
825 {
826 if( mnFirstVisibleChild >= 0 &&
828 {
829 OSL_FAIL( "AccessibleTextHelper: range invalid" );
830 }
831 }
832#endif
833
834 namespace {
835
836 // functor for sending child events (no stand-alone function, they are maybe not inlined)
837 class AccessibleTextHelper_LostChildEvent
838 {
839 public:
840 explicit AccessibleTextHelper_LostChildEvent( AccessibleTextHelper_Impl& rImpl ) : mrImpl(rImpl) {}
841 void operator()( const ::accessibility::AccessibleParaManager::WeakChild& rPara )
842 {
843 // retrieve hard reference from weak one
844 auto aHardRef( rPara.first.get() );
845
846 if( aHardRef.is() )
847 mrImpl.FireEvent(AccessibleEventId::CHILD, uno::Any(), uno::Any(css::uno::Reference<css::accessibility::XAccessible>(aHardRef)) );
848 }
849
850 private:
851 AccessibleTextHelper_Impl& mrImpl;
852 };
853
854 }
855
856 void AccessibleTextHelper_Impl::ParagraphsMoved( sal_Int32 nFirst, sal_Int32 nMiddle, sal_Int32 nLast )
857 {
858 const sal_Int32 nParas = GetTextForwarder().GetParagraphCount();
859
860 /* rotate paragraphs
861 * =================
862 *
863 * Three cases:
864 *
865 * 1.
866 * ... nParagraph ... nParam1 ... nParam2 ...
867 * |______________[xxxxxxxxxxx]
868 * becomes
869 * [xxxxxxxxxxx]|______________
870 *
871 * tail is 0
872 *
873 * 2.
874 * ... nParam1 ... nParagraph ... nParam2 ...
875 * [xxxxxxxxxxx|xxxxxxxxxxxxxx]____________
876 * becomes
877 * ____________[xxxxxxxxxxx|xxxxxxxxxxxxxx]
878 *
879 * tail is nParagraph - nParam1
880 *
881 * 3.
882 * ... nParam1 ... nParam2 ... nParagraph ...
883 * [xxxxxxxxxxx]___________|____________
884 * becomes
885 * ___________|____________[xxxxxxxxxxx]
886 *
887 * tail is nParam2 - nParam1
888 */
889
890 // sort nParagraph, nParam1 and nParam2 in ascending order, calc range
891 if( nMiddle < nFirst )
892 {
893 ::std::swap(nFirst, nMiddle);
894 }
895 else if( nMiddle < nLast )
896 {
897 nLast = nLast + nMiddle - nFirst;
898 }
899 else
900 {
901 ::std::swap(nMiddle, nLast);
902 nLast = nLast + nMiddle - nFirst;
903 }
904
905 if( !(nFirst < nParas && nMiddle < nParas && nLast < nParas) )
906 return;
907
908 // since we have no "paragraph index
909 // changed" event on UAA, remove
910 // [first,last] and insert again later (in
911 // UpdateVisibleChildren)
912
913 // maParaManager.Rotate( nFirst, nMiddle, nLast );
914
915 // send CHILD_EVENT to affected children
916 ::accessibility::AccessibleParaManager::VectorOfChildren::const_iterator begin = maParaManager.begin();
917 ::accessibility::AccessibleParaManager::VectorOfChildren::const_iterator end = begin;
918
919 ::std::advance( begin, nFirst );
920 ::std::advance( end, nLast+1 );
921
922 // TODO: maybe optimize here in the following way. If the
923 // number of removed children exceeds a certain threshold,
924 // use InvalidateFlags::Children
925 AccessibleTextHelper_LostChildEvent aFunctor( *this );
926
927 ::std::for_each( begin, end, aFunctor );
928
929 maParaManager.Release(nFirst, nLast+1);
930 // should be no need for UpdateBoundRect, since all affected children are cleared.
931 }
932
933 namespace {
934
935 // functor for sending child events (no stand-alone function, they are maybe not inlined)
936 class AccessibleTextHelper_ChildrenTextChanged
937 {
938 public:
939 void operator()( ::accessibility::AccessibleEditableTextPara& rPara )
940 {
941 rPara.TextChanged();
942 }
943 };
944
950 class AccessibleTextHelper_QueueFunctor
951 {
952 public:
953 AccessibleTextHelper_QueueFunctor() :
954 mnParasChanged( 0 ),
955 mnParaIndex(-1),
957 {}
958 void operator()( const SfxHint* pEvent )
959 {
960 if( !pEvent || mnParasChanged == -1 )
961 return;
962
963 // determine hint type
964 const TextHint* pTextHint = dynamic_cast<const TextHint*>( pEvent );
965 const SvxEditSourceHint* pEditSourceHint = dynamic_cast<const SvxEditSourceHint*>( pEvent );
966
967 if( !(!pEditSourceHint && pTextHint &&
968 (pTextHint->GetId() == SfxHintId::TextParaInserted ||
969 pTextHint->GetId() == SfxHintId::TextParaRemoved )) )
970 return;
971
972 if( pTextHint->GetValue() == EE_PARA_ALL )
973 {
974 mnParasChanged = -1;
975 }
976 else
977 {
978 mnHintId = pTextHint->GetId();
979 mnParaIndex = pTextHint->GetValue();
981 }
982 }
983
989 sal_Int32 GetNumberOfParasChanged() const { return mnParasChanged; }
995 sal_Int32 GetParaIndex() const { return mnParaIndex; }
1000 SfxHintId GetHintId() const { return mnHintId; }
1001
1002 private:
1008 sal_Int32 mnParaIndex;
1011 };
1012
1013 }
1014
1016 {
1017 // inspect queue for paragraph insert/remove events. If there
1018 // is exactly _one_ of those in the queue, and the number of
1019 // paragraphs has changed by exactly one, use that event to
1020 // determine a priori which paragraph was added/removed. This
1021 // is necessary, since I must sync right here with the
1022 // EditEngine state (number of paragraphs etc.), since I'm
1023 // potentially sending listener events right away.
1024 AccessibleTextHelper_QueueFunctor aFunctor;
1025 maEventQueue.ForEach( aFunctor );
1026
1027 const sal_Int32 nNewParas( GetTextForwarder().GetParagraphCount() );
1028 const sal_Int32 nCurrParas( maParaManager.GetNum() );
1029
1030 // whether every paragraph already is updated (no need to
1031 // repeat that later on, e.g. for PARA_MOVED events)
1032 bool bEverythingUpdated( false );
1033
1034 if( std::abs( nNewParas - nCurrParas ) == 1 &&
1035 aFunctor.GetNumberOfParasChanged() == 1 )
1036 {
1037 // #103483# Exactly one paragraph added/removed. This is
1038 // the normal case, optimize event handling here.
1039
1040 if( aFunctor.GetHintId() == SfxHintId::TextParaInserted )
1041 {
1042 // update num of paras
1043 maParaManager.SetNum( nNewParas );
1044
1045 // release everything from the insertion position until the end
1046 maParaManager.Release(aFunctor.GetParaIndex(), nCurrParas);
1047
1048 // TODO: Clarify whether this behaviour _really_ saves
1049 // anybody anything!
1050 // update children, _don't_ broadcast
1051 UpdateVisibleChildren( false );
1053
1054 // send insert event
1055 // #109864# Enforce creation of this paragraph
1056 try
1057 {
1058 GotPropertyEvent( uno::Any( getAccessibleChild( aFunctor.GetParaIndex() -
1060 AccessibleEventId::CHILD );
1061 }
1062 catch( const uno::Exception& )
1063 {
1064 OSL_FAIL("AccessibleTextHelper_Impl::ProcessQueue: could not create new paragraph");
1065 }
1066 }
1067 else if( aFunctor.GetHintId() == SfxHintId::TextParaRemoved )
1068 {
1069 ::accessibility::AccessibleParaManager::VectorOfChildren::const_iterator begin = maParaManager.begin();
1070 ::std::advance( begin, aFunctor.GetParaIndex() );
1071 ::accessibility::AccessibleParaManager::VectorOfChildren::const_iterator end = begin;
1072 ::std::advance( end, 1 );
1073
1074 // #i61812# remember para to be removed for later notification
1075 // AFTER the new state is applied (that after the para got removed)
1076 ::uno::Reference< XAccessible > xPara(begin->first.get());
1077
1078 // release everything from the remove position until the end
1079 maParaManager.Release(aFunctor.GetParaIndex(), nCurrParas);
1080
1081 // update num of paras
1082 maParaManager.SetNum( nNewParas );
1083
1084 // TODO: Clarify whether this behaviour _really_ saves
1085 // anybody anything!
1086 // update children, _don't_ broadcast
1087 UpdateVisibleChildren( false );
1089
1090 // #i61812# notification for removed para
1091 if (xPara.is())
1092 FireEvent(AccessibleEventId::CHILD, uno::Any(), uno::Any( xPara) );
1093 }
1094#ifdef DBG_UTIL
1095 else
1096 OSL_FAIL("AccessibleTextHelper_Impl::ProcessQueue() invalid hint id");
1097#endif
1098 }
1099 else if( nNewParas != nCurrParas )
1100 {
1101 // release all paras
1102 maParaManager.Release(0, nCurrParas);
1103
1104 // update num of paras
1105 maParaManager.SetNum( nNewParas );
1106
1107 // #109864# create from scratch, don't broadcast
1108 UpdateVisibleChildren( false );
1110
1111 // number of paragraphs somehow changed - but we have no
1112 // chance determining how. Thus, throw away everything and
1113 // create from scratch.
1114 // (child events should be broadcast after the changes are done...)
1115 FireEvent(AccessibleEventId::INVALIDATE_ALL_CHILDREN);
1116
1117 // no need for further updates later on
1118 bEverythingUpdated = true;
1119 }
1120
1121 bool bUpdatedBoundRectAndVisibleChildren(false);
1122
1123 while( !maEventQueue.IsEmpty() )
1124 {
1125 ::std::unique_ptr< SfxHint > pHint( maEventQueue.PopFront() );
1126 if (pHint)
1127 {
1128 const SfxHint& rHint = *pHint;
1129
1130 // Note, if you add events here, you need to update the AccessibleTextEventQueue::Append
1131 // code, because only the events we process here, are actually queued there.
1132
1133 try
1134 {
1135
1136 if (rHint.GetId() == SfxHintId::ThisIsAnSdrHint)
1137 {
1138 const SdrHint* pSdrHint = static_cast< const SdrHint* >( &rHint );
1139
1140 switch( pSdrHint->GetKind() )
1141 {
1143 {
1144 if(!IsActive())
1145 {
1146 break;
1147 }
1148 // change children state
1149 maParaManager.SetActive();
1150
1151 // per definition, edit mode text has the focus
1152 SetFocus( true );
1153 break;
1154 }
1155
1157 {
1158 // focused child now loses focus
1159 ESelection aSelection;
1160 if( GetEditViewForwarder().GetSelection( aSelection ) )
1161 SetChildFocus( aSelection.nEndPara, false );
1162
1163 // change children state
1164 maParaManager.SetActive( false );
1165
1168 break;
1169 }
1170 default:
1171 break;
1172 }
1173 }
1174 else if( const SvxEditSourceHint* pEditSourceHint = dynamic_cast<const SvxEditSourceHint*>( &rHint ) )
1175 {
1176 switch( pEditSourceHint->GetId() )
1177 {
1178 case SfxHintId::EditSourceParasMoved:
1179 {
1180 DBG_ASSERT( pEditSourceHint->GetStartValue() < GetTextForwarder().GetParagraphCount() &&
1181 pEditSourceHint->GetEndValue() < GetTextForwarder().GetParagraphCount(),
1182 "AccessibleTextHelper_Impl::NotifyHdl: Invalid notification");
1183
1184 if( !bEverythingUpdated )
1185 {
1186 ParagraphsMoved(pEditSourceHint->GetStartValue(),
1187 pEditSourceHint->GetValue(),
1188 pEditSourceHint->GetEndValue());
1189
1190 // in all cases, check visibility afterwards.
1192 }
1193 break;
1194 }
1195
1196 case SfxHintId::EditSourceSelectionChanged:
1197 // notify listeners
1198 try
1199 {
1201 }
1202 // maybe we're not in edit mode (this is not an error)
1203 catch( const uno::Exception& ) {}
1204 break;
1205 default: break;
1206 }
1207 }
1208 else if( const TextHint* pTextHint = dynamic_cast<const TextHint*>( &rHint ) )
1209 {
1210 const sal_Int32 nParas = GetTextForwarder().GetParagraphCount();
1211
1212 switch( pTextHint->GetId() )
1213 {
1214 case SfxHintId::TextModified:
1215 {
1216 // notify listeners
1217 sal_Int32 nPara( pTextHint->GetValue() );
1218
1219 // #108900# Delegate change event to children
1220 AccessibleTextHelper_ChildrenTextChanged aNotifyChildrenFunctor;
1221
1222 if( nPara == EE_PARA_ALL )
1223 {
1224 // #108900# Call every child
1225 ::std::for_each( maParaManager.begin(), maParaManager.end(),
1226 AccessibleParaManager::WeakChildAdapter< AccessibleTextHelper_ChildrenTextChanged > (aNotifyChildrenFunctor) );
1227 }
1228 else
1229 if( nPara < nParas )
1230 {
1231 // #108900# Call child at index nPara
1232 ::std::for_each( maParaManager.begin()+nPara, maParaManager.begin()+nPara+1,
1233 AccessibleParaManager::WeakChildAdapter< AccessibleTextHelper_ChildrenTextChanged > (aNotifyChildrenFunctor) );
1234 }
1235 break;
1236 }
1237
1238 case SfxHintId::TextParaInserted:
1239 // already happened above
1240 break;
1241
1242 case SfxHintId::TextParaRemoved:
1243 // already happened above
1244 break;
1245
1246 case SfxHintId::TextHeightChanged:
1247 // visibility changed, done below
1248 break;
1249
1250 case SfxHintId::TextViewScrolled:
1251 // visibility changed, done below
1252 break;
1253 default: break;
1254 }
1255
1256 // in all cases, check visibility afterwards.
1257 if (!bUpdatedBoundRectAndVisibleChildren)
1258 {
1261 bUpdatedBoundRectAndVisibleChildren = true;
1262 }
1263 }
1264 else if (rHint.GetId() == SfxHintId::SvxViewChanged)
1265 {
1266 // just check visibility
1267 if (!bUpdatedBoundRectAndVisibleChildren)
1268 {
1271 bUpdatedBoundRectAndVisibleChildren = true;
1272 }
1273 }
1274 // it's VITAL to keep the SfxSimpleHint last! It's the base of some classes above!
1275 else if( rHint.GetId() == SfxHintId::Dying)
1276 {
1277 // edit source is dying under us, become defunc then
1278 try
1279 {
1280 // make edit source inaccessible
1281 // Note: cannot destroy it here, since we're called from there!
1283 }
1284 catch( const uno::Exception& ) {}
1285 }
1286 }
1287 catch( const uno::Exception& )
1288 {
1290 }
1291 }
1292 }
1293 }
1294
1296 {
1297 // precondition: solar mutex locked
1299
1300 // precondition: not in a recursion
1301 if( mbInNotify )
1302 return;
1303
1304 mbInNotify = true;
1305
1306 try
1307 {
1308 // Process notification event, arranged in order of likelihood of
1309 // occurrence to avoid unnecessary dynamic_cast. Note that
1310 // SvxEditSourceHint is derived from TextHint, so has to be checked
1311 // before that.
1312 if (rHint.GetId() == SfxHintId::ThisIsAnSdrHint)
1313 {
1314 const SdrHint* pSdrHint = static_cast< const SdrHint* >( &rHint );
1315 // process drawing layer events right away, if not
1316 // within an open EE notification frame. Otherwise,
1317 // event processing would be delayed until next EE
1318 // notification sequence.
1319 maEventQueue.Append( *pSdrHint );
1320 }
1321 else if (rHint.GetId() == SfxHintId::SvxViewChanged)
1322 {
1323 const SvxViewChangedHint* pViewHint = static_cast<const SvxViewChangedHint*>(&rHint);
1324 // process visibility right away, if not within an
1325 // open EE notification frame. Otherwise, event
1326 // processing would be delayed until next EE
1327 // notification sequence.
1328 maEventQueue.Append( *pViewHint );
1329 }
1330 else if( const SvxEditSourceHint* pEditSourceHint = dynamic_cast<const SvxEditSourceHint*>( &rHint ) )
1331 {
1332 // EditEngine should emit TEXT_SELECTION_CHANGED events (#i27299#)
1333 maEventQueue.Append( *pEditSourceHint );
1334 }
1335 else if( const TextHint* pTextHint = dynamic_cast<const TextHint*>( &rHint ) )
1336 {
1337 // EditEngine should emit TEXT_SELECTION_CHANGED events (#i27299#)
1338 if(pTextHint->GetId() == SfxHintId::TextProcessNotifications)
1339 ProcessQueue();
1340 else
1341 maEventQueue.Append( *pTextHint );
1342 }
1343 // it's VITAL to keep the SfxHint last! It's the base of the classes above!
1344 else if( rHint.GetId() == SfxHintId::Dying )
1345 {
1346 // handle this event _at once_, because after that, objects are invalid
1347 // edit source is dying under us, become defunc then
1349 try
1350 {
1351 // make edit source inaccessible
1352 // Note: cannot destroy it here, since we're called from there!
1354 }
1355 catch( const uno::Exception& ) {}
1356 }
1357 }
1358 catch( const uno::Exception& )
1359 {
1361 mbInNotify = false;
1362 }
1363
1364 mbInNotify = false;
1365 }
1366
1368 {
1370 {
1371 try
1372 {
1373 // #106234# Unregister from EventNotifier
1375 SAL_INFO("svx", "disposed ID: " << mnNotifierClientId );
1376 }
1377 catch( const uno::Exception& ) {}
1378
1380 }
1381
1382 try
1383 {
1384 // dispose children
1385 maParaManager.Dispose();
1386 }
1387 catch( const uno::Exception& ) {}
1388
1389 // quit listen on stale edit source
1390 if( maEditSource.IsValid() )
1391 EndListening( maEditSource.GetBroadcaster() );
1392
1393 // clear references
1394 maEditSource.SetEditSource( ::std::unique_ptr< SvxEditSource >() );
1395 mxFrontEnd = nullptr;
1396 }
1397
1398 void AccessibleTextHelper_Impl::FireEvent( const sal_Int16 nEventId, const uno::Any& rNewValue, const uno::Any& rOldValue ) const
1399 {
1400 // -- object locked --
1401 AccessibleEventObject aEvent;
1402 {
1403 std::scoped_lock aGuard(maMutex);
1404
1405 DBG_ASSERT(mxFrontEnd.is(), "AccessibleTextHelper::FireEvent: no event source set");
1406
1407 if (mxFrontEnd.is())
1408 aEvent = AccessibleEventObject(mxFrontEnd->getAccessibleContext(), nEventId,
1409 rNewValue, rOldValue, -1);
1410 else
1411 aEvent = AccessibleEventObject(uno::Reference<uno::XInterface>(), nEventId,
1412 rNewValue, rOldValue, -1);
1413
1414 // no locking necessary, FireEvent internally copies listeners
1415 // if someone removes/adds in between Further locking,
1416 // actually, might lead to deadlocks, since we're calling out
1417 // of this object
1418 }
1419 // -- until here --
1420
1422 }
1423
1424 void AccessibleTextHelper_Impl::FireEvent( const AccessibleEventObject& rEvent ) const
1425 {
1426 // #106234# Delegate to EventNotifier
1429 }
1430
1431 // XAccessibleContext
1433 {
1435 }
1436
1437 uno::Reference< XAccessible > AccessibleTextHelper_Impl::getAccessibleChild( sal_Int64 i )
1438 {
1439 i -= GetStartIndex();
1440
1441 if( 0 > i || i >= getAccessibleChildCount() ||
1442 GetTextForwarder().GetParagraphCount() <= i )
1443 {
1444 throw lang::IndexOutOfBoundsException("Invalid child index", mxFrontEnd);
1445 }
1446
1447 DBG_ASSERT(mxFrontEnd.is(), "AccessibleTextHelper_Impl::UpdateVisibleChildren: no frontend set");
1448
1449 if( mxFrontEnd.is() )
1450 return maParaManager.CreateChild( i, mxFrontEnd, GetEditSource(), mnFirstVisibleChild + i ).first;
1451 else
1452 return nullptr;
1453 }
1454
1455 void AccessibleTextHelper_Impl::addAccessibleEventListener( const uno::Reference< XAccessibleEventListener >& xListener )
1456 {
1459 }
1460
1461 void AccessibleTextHelper_Impl::removeAccessibleEventListener( const uno::Reference< XAccessibleEventListener >& xListener )
1462 {
1464 return;
1465
1466 const sal_Int32 nListenerCount = ::comphelper::AccessibleEventNotifier::removeEventListener( getNotifierClientId(), xListener );
1467 if ( !nListenerCount )
1468 {
1469 // no listeners anymore
1470 // -> revoke ourself. This may lead to the notifier thread dying (if we were the last client),
1471 // and at least to us not firing any events anymore, in case somebody calls
1472 // NotifyAccessibleEvent, again
1476 }
1477 }
1478
1479 uno::Reference< XAccessible > AccessibleTextHelper_Impl::getAccessibleAtPoint( const awt::Point& _aPoint )
1480 {
1481 // make given position relative
1482 if( !mxFrontEnd.is() )
1483 throw uno::RuntimeException("AccessibleTextHelper_Impl::getAccessibleAt: frontend invalid", mxFrontEnd );
1484
1485 uno::Reference< XAccessibleContext > xFrontEndContext = mxFrontEnd->getAccessibleContext();
1486
1487 if( !xFrontEndContext.is() )
1488 throw uno::RuntimeException("AccessibleTextHelper_Impl::getAccessibleAt: frontend invalid", mxFrontEnd );
1489
1490 uno::Reference< XAccessibleComponent > xFrontEndComponent( xFrontEndContext, uno::UNO_QUERY_THROW );
1491
1492 // #103862# No longer need to make given position relative
1493 Point aPoint( _aPoint.X, _aPoint.Y );
1494
1495 // respect EditEngine offset to surrounding shape/cell
1496 aPoint -= GetOffset();
1497
1498 // convert to EditEngine coordinate system
1499 SvxTextForwarder& rCacheTF = GetTextForwarder();
1500 Point aLogPoint( GetViewForwarder().PixelToLogic( aPoint, rCacheTF.GetMapMode() ) );
1501
1502 // iterate over all visible children (including those not yet created)
1503 sal_Int64 nChild;
1504 for( nChild=mnFirstVisibleChild; nChild <= mnLastVisibleChild; ++nChild )
1505 {
1506 DBG_ASSERT(nChild >= 0,
1507 "AccessibleTextHelper_Impl::getAccessibleAt: index value overflow");
1508
1509 tools::Rectangle aParaBounds( rCacheTF.GetParaBounds( nChild ) );
1510
1511 if( aParaBounds.Contains( aLogPoint ) )
1513 }
1514
1515 // found none
1516 return nullptr;
1517 }
1518
1519
1520 // AccessibleTextHelper implementation (simply forwards to impl)
1521
1522 AccessibleTextHelper::AccessibleTextHelper( ::std::unique_ptr< SvxEditSource > && pEditSource ) :
1523 mpImpl( new AccessibleTextHelper_Impl() )
1524 {
1525 SolarMutexGuard aGuard;
1526
1527 SetEditSource( std::move(pEditSource) );
1528 }
1529
1531 {
1532 }
1533
1535 {
1536#ifdef DBG_UTIL
1537 mpImpl->CheckInvariants();
1538
1539 const SvxEditSource& aEditSource = mpImpl->GetEditSource();
1540
1541 mpImpl->CheckInvariants();
1542
1543 return aEditSource;
1544#else
1545 return mpImpl->GetEditSource();
1546#endif
1547 }
1548
1549 void AccessibleTextHelper::SetEditSource( ::std::unique_ptr< SvxEditSource > && pEditSource )
1550 {
1551#ifdef DBG_UTIL
1552 // precondition: solar mutex locked
1554
1555 mpImpl->CheckInvariants();
1556#endif
1557
1558 mpImpl->SetEditSource( std::move(pEditSource) );
1559
1560#ifdef DBG_UTIL
1561 mpImpl->CheckInvariants();
1562#endif
1563 }
1564
1565 void AccessibleTextHelper::SetEventSource( const uno::Reference< XAccessible >& rInterface )
1566 {
1567#ifdef DBG_UTIL
1568 mpImpl->CheckInvariants();
1569#endif
1570
1571 mpImpl->SetEventSource( rInterface );
1572
1573#ifdef DBG_UTIL
1574 mpImpl->CheckInvariants();
1575#endif
1576 }
1577
1578 void AccessibleTextHelper::SetFocus( bool bHaveFocus )
1579 {
1580#ifdef DBG_UTIL
1581 // precondition: solar mutex locked
1583
1584 mpImpl->CheckInvariants();
1585#endif
1586
1587 mpImpl->SetFocus( bHaveFocus );
1588
1589#ifdef DBG_UTIL
1590 mpImpl->CheckInvariants();
1591#endif
1592 }
1593
1595 {
1596#ifdef DBG_UTIL
1597 mpImpl->CheckInvariants();
1598
1599 bool bRet( mpImpl->HaveFocus() );
1600
1601 mpImpl->CheckInvariants();
1602
1603 return bRet;
1604#else
1605 return mpImpl->HaveFocus();
1606#endif
1607 }
1608
1610 {
1611#ifdef DBG_UTIL
1612 // precondition: solar mutex locked
1614
1615 mpImpl->CheckInvariants();
1616#endif
1617
1618 mpImpl->SetOffset( rPoint );
1619
1620#ifdef DBG_UTIL
1621 mpImpl->CheckInvariants();
1622#endif
1623 }
1624
1625 void AccessibleTextHelper::SetStartIndex( sal_Int32 nOffset )
1626 {
1627#ifdef DBG_UTIL
1628 // precondition: solar mutex locked
1630
1631 mpImpl->CheckInvariants();
1632#endif
1633
1634 mpImpl->SetStartIndex( nOffset );
1635
1636#ifdef DBG_UTIL
1637 mpImpl->CheckInvariants();
1638#endif
1639 }
1640
1642 {
1643#ifdef DBG_UTIL
1644 mpImpl->CheckInvariants();
1645
1646 sal_Int32 nOffset = mpImpl->GetStartIndex();
1647
1648 mpImpl->CheckInvariants();
1649
1650 return nOffset;
1651#else
1652 return mpImpl->GetStartIndex();
1653#endif
1654 }
1655
1657 {
1658 mpImpl->SetAdditionalChildStates( nChildStates );
1659 }
1660
1662 {
1663#ifdef DBG_UTIL
1664 // precondition: solar mutex locked
1666
1667 mpImpl->CheckInvariants();
1668#endif
1669
1670 mpImpl->UpdateVisibleChildren();
1671 mpImpl->UpdateBoundRect();
1672
1673 mpImpl->UpdateSelection();
1674
1675#ifdef DBG_UTIL
1676 mpImpl->CheckInvariants();
1677#endif
1678 }
1679
1681 {
1682 // As Dispose calls ShutdownEditSource, which in turn
1683 // deregisters as listener on the edit source, have to lock
1684 // here
1685 SolarMutexGuard aGuard;
1686
1687#ifdef DBG_UTIL
1688 mpImpl->CheckInvariants();
1689#endif
1690
1691 mpImpl->Dispose();
1692
1693#ifdef DBG_UTIL
1694 mpImpl->CheckInvariants();
1695#endif
1696 }
1697
1698 // XAccessibleContext
1700 {
1701 SolarMutexGuard aGuard;
1702
1703#ifdef DBG_UTIL
1704 mpImpl->CheckInvariants();
1705
1706 sal_Int64 nRet = mpImpl->getAccessibleChildCount();
1707
1708 mpImpl->CheckInvariants();
1709
1710 return nRet;
1711#else
1712 return mpImpl->getAccessibleChildCount();
1713#endif
1714 }
1715
1716 uno::Reference< XAccessible > AccessibleTextHelper::GetChild( sal_Int64 i )
1717 {
1718 SolarMutexGuard aGuard;
1719
1720#ifdef DBG_UTIL
1721 mpImpl->CheckInvariants();
1722
1723 uno::Reference< XAccessible > xRet = mpImpl->getAccessibleChild( i );
1724
1725 mpImpl->CheckInvariants();
1726
1727 return xRet;
1728#else
1729 return mpImpl->getAccessibleChild( i );
1730#endif
1731 }
1732
1733 void AccessibleTextHelper::AddEventListener( const uno::Reference< XAccessibleEventListener >& xListener )
1734 {
1735#ifdef DBG_UTIL
1736 mpImpl->CheckInvariants();
1737
1738 mpImpl->addAccessibleEventListener( xListener );
1739
1740 mpImpl->CheckInvariants();
1741#else
1742 mpImpl->addAccessibleEventListener( xListener );
1743#endif
1744 }
1745
1746 void AccessibleTextHelper::RemoveEventListener( const uno::Reference< XAccessibleEventListener >& xListener )
1747 {
1748#ifdef DBG_UTIL
1749 mpImpl->CheckInvariants();
1750
1751 mpImpl->removeAccessibleEventListener( xListener );
1752
1753 mpImpl->CheckInvariants();
1754#else
1755 mpImpl->removeAccessibleEventListener( xListener );
1756#endif
1757 }
1758
1759 // XAccessibleComponent
1760 uno::Reference< XAccessible > AccessibleTextHelper::GetAt( const awt::Point& aPoint )
1761 {
1762 SolarMutexGuard aGuard;
1763
1764#ifdef DBG_UTIL
1765 mpImpl->CheckInvariants();
1766
1767 uno::Reference< XAccessible > xChild = mpImpl->getAccessibleAtPoint( aPoint );
1768
1769 mpImpl->CheckInvariants();
1770
1771 return xChild;
1772#else
1773 return mpImpl->getAccessibleAtPoint( aPoint );
1774#endif
1775 }
1776
1777} // end of namespace accessibility
1778
1779
1780/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
const sal_Int32 mnDifference
SfxHintId mnHintId
TextHint ID (removed/inserted) of last interesting event.
sal_Int32 mnParasChanged
number of paragraphs changed during queue processing.
AccessibleTextHelper_Impl & mrImpl
sal_Int32 mnParaIndex
index of paragraph added/removed last
sal_Int32 mnStartIndex
AnyEventRef aEvent
SdrHintKind GetKind() const
Definition: svdmodel.hxx:133
SfxHintId GetId() const
void StartListening(SfxBroadcaster &rBroadcaster, DuplicateHandling eDuplicateHanding=DuplicateHandling::Unexpected)
void EndListening(SfxBroadcaster &rBroadcaster, bool bRemoveAllDuplicates=false)
sal_Int32 GetEndValue() const
sal_Int32 GetStartValue() const
virtual SvxEditViewForwarder * GetEditViewForwarder(bool bCreate=false)
virtual bool IsValid() const=0
virtual MapMode GetMapMode() const=0
virtual tools::Rectangle GetParaBounds(sal_Int32 nPara) const=0
virtual sal_Int32 GetParagraphCount() const=0
virtual bool IsValid() const=0
sal_Int32 GetValue() const
const sdr::table::CellRef & getCellRef() const
AccessibleTableShape * GetParentTable()
This class handles the notification events for the AccessibleTextHelper class.
void Append(const SdrHint &rHint)
Append event to end of queue.
::std::unique_ptr< SfxHint > PopFront()
Pop first queue element.
bool IsEmpty() const
Query whether queue is empty.
void ForEach(Functor &rFunctor) const
Apply functor to every queue member.
void SetEventSource(const uno::Reference< XAccessible > &rInterface)
virtual void Notify(SfxBroadcaster &rBC, const SfxHint &rHint) override
void UpdateVisibleChildren(bool bBroadcastEvents=true)
void GotPropertyEvent(const uno::Any &rNewValue, const sal_Int16 nEventId) const
uno::Reference< XAccessible > getAccessibleChild(sal_Int64 i)
void SetAdditionalChildStates(sal_Int64 nChildStates)
void SetChildFocus(sal_Int32 nChild, bool bHaveFocus)
uno::Reference< XAccessible > getAccessibleAtPoint(const awt::Point &aPoint)
SvxEditViewForwarder & GetEditViewForwarder() const
SvxEditSourceAdapter & GetEditSource() const
comphelper::AccessibleEventNotifier::TClientId getNotifierClientId() const
void removeAccessibleEventListener(const uno::Reference< XAccessibleEventListener > &xListener)
void FireEvent(const sal_Int16 nEventId, const uno::Any &rNewValue=uno::Any(), const uno::Any &rOldValue=uno::Any()) const
comphelper::AccessibleEventNotifier::TClientId mnNotifierClientId
client Id from AccessibleEventNotifier
::accessibility::AccessibleParaManager maParaManager
void addAccessibleEventListener(const uno::Reference< XAccessibleEventListener > &xListener)
void ParagraphsMoved(sal_Int32 nFirst, sal_Int32 nMiddle, sal_Int32 nLast)
Point maOffset
our current offset to the containing shape/cell (guarded by maMutex)
static constexpr comphelper::AccessibleEventNotifier::TClientId snNotifierClientRevoked
void SetEditSource(::std::unique_ptr< SvxEditSource > &&pEditSource)
void AddEventListener(const css::uno::Reference< css::accessibility::XAccessibleEventListener > &xListener)
Implements addEventListener.
css::uno::Reference< css::accessibility::XAccessible > GetAt(const css::awt::Point &aPoint)
Implements getAccessibleAt.
const SvxEditSource & GetEditSource() const
Query the current edit source.
css::uno::Reference< css::accessibility::XAccessible > GetChild(sal_Int64 i)
Implements getAccessibleChild.
void SetStartIndex(sal_Int32 nOffset)
Set offset the object adds to all children's indices.
bool HaveFocus()
Query the focus state of the surrounding object.
void UpdateChildren()
Update the visible children.
void Dispose()
Drop all references and enter disposed state.
AccessibleTextHelper(::std::unique_ptr< SvxEditSource > &&pEditSource)
Create accessible text object for given edit source.
void RemoveEventListener(const css::uno::Reference< css::accessibility::XAccessibleEventListener > &xListener)
Implements removeEventListener.
void SetAdditionalChildStates(sal_Int64 rChildStates)
Sets a bitset of additional accessible states.
void SetEventSource(const css::uno::Reference< css::accessibility::XAccessible > &rInterface)
Set the event source.
void SetFocus(bool bHaveFocus=true)
Set the focus state of the accessibility object.
void SetEditSource(::std::unique_ptr< SvxEditSource > &&pEditSource)
Set the current edit source.
sal_Int32 GetStartIndex() const
Query offset the object adds to all children's indices.
void SetOffset(const Point &rPoint)
Set offset of EditEngine/Outliner from parent.
const std::unique_ptr< AccessibleTextHelper_Impl > mpImpl
@dyn
sal_Int64 GetChildCount() const
Implements getAccessibleChildCount.
static sal_Int32 addEventListener(const TClientId _nClient, const css::uno::Reference< css::accessibility::XAccessibleEventListener > &_rxListener)
static void addEvent(const TClientId _nClient, const css::accessibility::AccessibleEventObject &_rEvent)
static sal_Int32 removeEventListener(const TClientId _nClient, const css::uno::Reference< css::accessibility::XAccessibleEventListener > &_rxListener)
static void revokeClient(const TClientId _nClient)
bool Contains(const Point &rPOINT) const
#define DBG_ASSERT(sCon, aError)
#define DBG_TESTSOLARMUTEX()
#define DBG_UNHANDLED_EXCEPTION(...)
#define EE_PARA_ALL
#define EE_PARA_NOT_FOUND
#define EE_INDEX_NOT_FOUND
SfxHintId
#define SAL_INFO(area, stream)
NONE
static ::std::pair< first_type, second_type > makeSortedPair(first_type first, second_type second)
class SvxPropertySetInfoPool
int i
constexpr OUStringLiteral first
enumrange< T >::Iterator begin(enumrange< T >)
end
sal_Int16 nId
bool HasRange() const
sal_Int32 nStartPara
sal_Int32 nEndPos
sal_Int32 nStartPos
void Adjust()
sal_Int32 nEndPara
void GetSelection(struct ESelection &rSel, SvxTextForwarder const *pForwarder) noexcept