1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
27 #include <unotextrange.hxx>
28 #include <unocrsrhelper.hxx>
29 #include <crstate.hxx>
32 #include <viewopt.hxx>
33 #include <vcl/svapp.hxx>
34 #include <vcl/window.hxx>
35 #include <sal/log.hxx>
36 #include <com/sun/star/accessibility/AccessibleRole.hpp>
37 #include <com/sun/star/accessibility/AccessibleScrollType.hpp>
38 #include <com/sun/star/accessibility/AccessibleStateType.hpp>
39 #include <com/sun/star/accessibility/AccessibleTextType.hpp>
40 #include <com/sun/star/accessibility/AccessibleEventId.hpp>
41 #include <com/sun/star/i18n/Boundary.hpp>
42 #include <com/sun/star/i18n/CharacterIteratorMode.hpp>
43 #include <com/sun/star/i18n/WordType.hpp>
44 #include <com/sun/star/i18n/XBreakIterator.hpp>
45 #include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
46 #include <com/sun/star/beans/UnknownPropertyException.hpp>
47 #include <breakit.hxx>
48 #include "accpara.hxx"
49 #include "accportions.hxx"
50 #include <sfx2/viewsh.hxx>
51 #include <sfx2/viewfrm.hxx>
52 #include <sfx2/dispatch.hxx>
53 #include <unocrsr.hxx>
54 #include <unoport.hxx>
56 #include <IDocumentRedlineAccess.hxx>
57 #include "acchyperlink.hxx"
58 #include "acchypertextdata.hxx"
59 #include <unotools/accessiblerelationsethelper.hxx>
60 #include <com/sun/star/accessibility/AccessibleRelationType.hpp>
61 #include <comphelper/accessibletexthelper.hxx>
63 #include <docufld.hxx>
67 #include <com/sun/star/beans/XPropertySet.hpp>
68 #include <swmodule.hxx>
69 #include <redline.hxx>
70 #include <com/sun/star/awt/FontWeight.hpp>
71 #include <com/sun/star/awt/FontStrikeout.hpp>
72 #include <com/sun/star/awt/FontSlant.hpp>
74 #include <editeng/brushitem.hxx>
75 #include <editeng/unoprnms.hxx>
76 #include <editeng/lrspitem.hxx>
77 #include <editeng/ulspitem.hxx>
78 #include <swatrset.hxx>
79 #include <unosett.hxx>
81 #include <unoprnms.hxx>
82 #include <com/sun/star/text/WritingMode2.hpp>
83 #include <viewimp.hxx>
84 #include "textmarkuphelper.hxx"
85 #include "parachangetrackinginfo.hxx"
86 #include <com/sun/star/text/TextMarkupType.hpp>
87 #include <cppuhelper/supportsservice.hxx>
88 #include <cppuhelper/typeprovider.hxx>
89 #include <svx/colorwindow.hxx>
90 #include <o3tl/string_view.hxx>
91 #include <editeng/editids.hrc>
95 #include "../../uibase/inc/fldmgr.hxx"
96 #include <fldbas.hxx> // SwField
98 using namespace ::com::sun::star
;
99 using namespace ::com::sun::star::accessibility
;
101 using beans::PropertyValue
;
102 using beans::UnknownPropertyException
;
103 using beans::PropertyState_DIRECT_VALUE
;
109 namespace com::sun::star::text
{
113 constexpr OUString sServiceName
= u
"com.sun.star.text.AccessibleParagraphView"_ustr
;
114 constexpr OUStringLiteral sImplementationName
= u
"com.sun.star.comp.Writer.SwAccessibleParagraphView";
116 const SwTextFrame
* SwAccessibleParagraph::GetTextFrame() const
118 return static_cast<const SwTextFrame
*>(GetFrame());
121 OUString
const & SwAccessibleParagraph::GetString()
123 return GetPortionData().GetAccessibleString();
126 const OUString
& SwAccessibleParagraph::GetDescription()
128 return EMPTY_OUSTRING
; // provide empty description for paragraphs
131 sal_Int32
SwAccessibleParagraph::GetCaretPos()
135 // get the selection's point, and test whether it's in our node
136 // #i27301# - consider adjusted method signature
137 SwPaM
* pCaret
= GetCursor( false ); // caret is first PaM in PaM-ring
139 if( pCaret
!= nullptr )
141 const SwTextFrame
* const pTextFrame
= GetTextFrame();
144 // check whether the point points into 'our' node
145 SwPosition
* pPoint
= pCaret
->GetPoint();
146 if (sw::FrameContainsNode(*pTextFrame
, pPoint
->GetNodeIndex()))
148 // same node? Then check whether it's also within 'our' part
150 const TextFrameIndex nIndex
= pTextFrame
->MapModelToViewPos(*pPoint
);
151 if(!GetPortionData().IsValidCorePosition( nIndex
) ||
152 (GetPortionData().IsZeroCorePositionData()
153 && nIndex
== TextFrameIndex(0)))
155 bool bFormat
= pTextFrame
->HasPara();
162 if( GetPortionData().IsValidCorePosition( nIndex
) )
165 // consider that cursor/caret is in front of the list label
166 if ( pCaret
->IsInFrontOfLabel() )
172 nRet
= GetPortionData().GetAccessiblePosition( nIndex
);
175 OSL_ENSURE( nRet
>= 0, "invalid cursor?" );
176 OSL_ENSURE( nRet
<= GetPortionData().GetAccessibleString().
177 getLength(), "invalid cursor?" );
179 // else: in this paragraph, but in different frame
181 // else: not in this paragraph
183 // else: no cursor -> no caret
188 // #i27301# - new parameter <_bForSelection>
189 SwPaM
* SwAccessibleParagraph::GetCursor( const bool _bForSelection
)
191 // get the cursor shell; if we don't have any, we don't have a
192 // cursor/selection either
193 SwPaM
* pCursor
= nullptr;
194 SwCursorShell
* pCursorShell
= SwAccessibleParagraph::GetCursorShell();
195 // #i27301# - if cursor is retrieved for selection, the cursors for
196 // a table selection has to be returned.
197 if ( pCursorShell
!= nullptr &&
198 ( _bForSelection
|| !pCursorShell
->IsTableMode() ) )
200 SwFEShell
*pFESh
= dynamic_cast<SwFEShell
*>(pCursorShell
);
202 !(pFESh
->IsFrameSelected() || pFESh
->GetSelectedObjCount() > 0) )
204 // get the selection, and test whether it affects our text node
205 pCursor
= pCursorShell
->GetCursor( false /* ??? */ );
212 bool SwAccessibleParagraph::IsHeading() const
214 const SwTextFrame
* const pFrame
= GetTextFrame();
215 const SwTextNode
*pTextNd
= pFrame
->GetTextNodeForParaProps();
216 return pTextNd
->IsOutline();
219 void SwAccessibleParagraph::GetStates( sal_Int64
& rStateSet
)
221 SwAccessibleContext::GetStates( rStateSet
);
224 rStateSet
|= AccessibleStateType::MULTI_LINE
;
226 if (GetCursorShell())
229 rStateSet
|= AccessibleStateType::MULTI_SELECTABLE
;
231 rStateSet
|= AccessibleStateType::FOCUSABLE
;
234 // FOCUSED (simulates node index of cursor)
235 SwPaM
* pCaret
= GetCursor( false ); // #i27301# - consider adjusted method signature
236 const SwTextFrame
* const pFrame
= GetTextFrame();
238 if (pCaret
!= nullptr &&
239 sw::FrameContainsNode(*pFrame
, pCaret
->GetPoint()->GetNodeIndex()) &&
242 vcl::Window
*pWin
= GetWindow();
243 if( pWin
&& pWin
->HasFocus() )
244 rStateSet
|= AccessibleStateType::FOCUSED
;
245 ::rtl::Reference
< SwAccessibleContext
> xThis( this );
246 GetMap()->SetCursorContext( xThis
);
250 void SwAccessibleParagraph::InvalidateContent_( bool bVisibleDataFired
)
252 OUString
sOldText( GetString() );
256 const OUString sText
= GetString();
258 if( sText
!= sOldText
)
260 // The text is changed
261 AccessibleEventObject aEvent
;
262 aEvent
.EventId
= AccessibleEventId::TEXT_CHANGED
;
264 // determine exact changes between sOldText and sText
265 (void)comphelper::OCommonAccessibleText::implInitTextChangedEvent(sOldText
, sText
,
269 FireAccessibleEvent( aEvent
);
270 uno::Reference
< XAccessible
> xparent
= getAccessibleParent();
271 uno::Reference
< XAccessibleContext
> xAccContext(xparent
,uno::UNO_QUERY
);
272 if (xAccContext
.is() && xAccContext
->getAccessibleRole() == AccessibleRole::TABLE_CELL
)
274 SwAccessibleContext
* pPara
= static_cast< SwAccessibleContext
* >(xparent
.get());
277 AccessibleEventObject aParaEvent
;
278 aParaEvent
.EventId
= AccessibleEventId::VALUE_CHANGED
;
279 pPara
->FireAccessibleEvent(aParaEvent
);
283 else if( !bVisibleDataFired
)
285 FireVisibleDataEvent();
288 bool bNewIsBlockQuote
= IsBlockQuote();
289 bool bNewIsHeading
= IsHeading();
290 //Get the real heading level, Heading1 ~ Heading10
291 m_nHeadingLevel
= GetRealHeadingLevel();
292 bool bOldIsBlockQuote
;
295 std::scoped_lock
aGuard( m_Mutex
);
296 bOldIsBlockQuote
= m_bIsBlockQuote
;
297 bOldIsHeading
= m_bIsHeading
;
298 m_bIsBlockQuote
= bNewIsBlockQuote
;
299 if( m_bIsHeading
!= bNewIsHeading
)
300 m_bIsHeading
= bNewIsHeading
;
303 if (bNewIsBlockQuote
!= bOldIsBlockQuote
|| bNewIsHeading
!= bOldIsHeading
)
305 // The role has changed
306 AccessibleEventObject aEvent
;
307 aEvent
.EventId
= AccessibleEventId::ROLE_CHANGED
;
309 FireAccessibleEvent( aEvent
);
312 if( sText
== sOldText
)
315 OUString
sNewDesc( GetDescription() );
318 std::scoped_lock
aGuard( m_Mutex
);
320 if( m_sDesc
!= sNewDesc
)
324 if( sNewDesc
!= sOldDesc
)
326 // The text is changed
327 AccessibleEventObject aEvent
;
328 aEvent
.EventId
= AccessibleEventId::DESCRIPTION_CHANGED
;
329 aEvent
.OldValue
<<= sOldDesc
;
330 aEvent
.NewValue
<<= sNewDesc
;
332 FireAccessibleEvent( aEvent
);
336 void SwAccessibleParagraph::InvalidateCursorPos_()
338 // The text is changed
339 sal_Int32 nNew
= GetCaretPos();
342 std::scoped_lock
aGuard( m_Mutex
);
343 nOld
= m_nOldCaretPos
;
344 m_nOldCaretPos
= nNew
;
348 // remember that object as the one that has the caret. This is
349 // necessary to notify that object if the cursor leaves it.
350 ::rtl::Reference
< SwAccessibleContext
> xThis( this );
351 GetMap()->SetCursorContext( xThis
);
354 vcl::Window
*pWin
= GetWindow();
358 // The cursor's node position is simulated by the focus!
359 if( pWin
&& pWin
->HasFocus() && -1 == nOld
)
360 FireStateChangedEvent( AccessibleStateType::FOCUSED
, true );
362 AccessibleEventObject aEvent
;
363 aEvent
.EventId
= AccessibleEventId::CARET_CHANGED
;
364 aEvent
.OldValue
<<= nOld
;
365 aEvent
.NewValue
<<= nNew
;
367 FireAccessibleEvent( aEvent
);
369 if( pWin
&& pWin
->HasFocus() && -1 == nNew
)
370 FireStateChangedEvent( AccessibleStateType::FOCUSED
, false );
371 //To send TEXT_SELECTION_CHANGED event
374 bool bCurSelection
= GetSelection(nStart
,nEnd
);
375 if(m_bLastHasSelection
|| bCurSelection
)
377 aEvent
.EventId
= AccessibleEventId::TEXT_SELECTION_CHANGED
;
378 aEvent
.OldValue
.clear();
379 aEvent
.NewValue
.clear();
380 FireAccessibleEvent(aEvent
);
382 m_bLastHasSelection
=bCurSelection
;
386 void SwAccessibleParagraph::InvalidateFocus_()
388 vcl::Window
*pWin
= GetWindow();
393 std::scoped_lock
aGuard( m_Mutex
);
394 nPos
= m_nOldCaretPos
;
396 OSL_ENSURE( nPos
!= -1, "focus object should be selected" );
398 FireStateChangedEvent( AccessibleStateType::FOCUSED
,
399 pWin
->HasFocus() && nPos
!= -1 );
403 SwAccessibleParagraph::SwAccessibleParagraph(
404 std::shared_ptr
<SwAccessibleMap
> const& pInitMap
,
405 const SwTextFrame
& rTextFrame
)
406 : SwAccessibleParagraph_BASE(pInitMap
, AccessibleRole::PARAGRAPH
, &rTextFrame
)
407 , m_nOldCaretPos( -1 )
408 , m_bIsBlockQuote(false)
409 , m_bIsHeading( false )
410 //Get the real heading level, Heading1 ~ Heading10
411 , m_nHeadingLevel (-1)
412 , m_aSelectionHelper( *this )
413 , mpParaChangeTrackInfo( new SwParaChangeTrackingInfo( rTextFrame
) ) // #i108125#
414 , m_bLastHasSelection(false) //To add TEXT_SELECTION_CHANGED event
416 StartListening(const_cast<SwTextFrame
&>(rTextFrame
));
417 m_bIsBlockQuote
= IsBlockQuote();
418 m_bIsHeading
= IsHeading();
419 //Get the real heading level, Heading1 ~ Heading10
420 m_nHeadingLevel
= GetRealHeadingLevel();
421 SetName( OUString() ); // set an empty accessibility name for paragraphs
424 SwAccessibleParagraph::~SwAccessibleParagraph()
426 SolarMutexGuard aGuard
;
428 m_pPortionData
.reset();
429 m_pHyperTextData
.reset();
430 mpParaChangeTrackInfo
.reset(); // #i108125#
434 bool SwAccessibleParagraph::HasCursor()
436 std::scoped_lock
aGuard( m_Mutex
);
437 return m_nOldCaretPos
!= -1;
440 void SwAccessibleParagraph::UpdatePortionData()
442 // obtain the text frame
443 const SwTextFrame
* pFrame
= GetTextFrame();
444 OSL_ENSURE( pFrame
!= nullptr, "The text frame has vanished!" );
449 OSL_ENSURE( pFrame
->IsTextFrame(), "The text frame has mutated!" );
450 // build new portion data
451 m_pPortionData
.reset( new SwAccessiblePortionData(
452 pFrame
, GetMap()->GetShell()->GetViewOptions()) );
453 pFrame
->VisitPortions( *m_pPortionData
);
455 OSL_ENSURE( m_pPortionData
!= nullptr, "UpdatePortionData() failed" );
458 void SwAccessibleParagraph::ClearPortionData()
460 m_pPortionData
.reset();
461 m_pHyperTextData
.reset();
464 void SwAccessibleParagraph::ExecuteAtViewShell( sal_uInt16 nSlot
)
466 OSL_ENSURE( GetMap() != nullptr, "no map?" );
467 SwViewShell
* pViewShell
= GetMap()->GetShell();
469 assert(pViewShell
!= nullptr && "View shell expected!");
470 SfxViewShell
* pSfxShell
= pViewShell
->GetSfxViewShell();
472 OSL_ENSURE( pSfxShell
!= nullptr, "SfxViewShell shell expected!" );
476 SfxViewFrame
& rFrame
= pSfxShell
->GetViewFrame();
477 SfxDispatcher
*pDispatcher
= rFrame
.GetDispatcher();
478 OSL_ENSURE( pDispatcher
!= nullptr, "Dispatcher expected!" );
482 pDispatcher
->Execute( nSlot
);
485 rtl::Reference
<SwXTextPortion
> SwAccessibleParagraph::CreateUnoPortion(
486 sal_Int32 nStartIndex
,
487 sal_Int32 nEndIndex
)
489 OSL_ENSURE( (IsValidChar(nStartIndex
, GetString().getLength()) &&
490 (nEndIndex
== -1)) ||
491 IsValidRange(nStartIndex
, nEndIndex
, GetString().getLength()),
492 "please check parameters before calling this method" );
494 const TextFrameIndex nStart
= GetPortionData().GetCoreViewPosition(nStartIndex
);
495 const TextFrameIndex nEnd
= (nEndIndex
== -1)
496 ? (nStart
+ TextFrameIndex(1))
497 : GetPortionData().GetCoreViewPosition(nEndIndex
);
500 const SwTextFrame
* const pFrame
= GetTextFrame();
501 SwPosition
aStartPos(pFrame
->MapViewToModelPos(nStart
));
502 auto pUnoCursor(const_cast<SwDoc
&>(pFrame
->GetDoc()).CreateUnoCursor(aStartPos
));
503 pUnoCursor
->SetMark();
504 *pUnoCursor
->GetMark() = pFrame
->MapViewToModelPos(nEnd
);
506 // create a (dummy) text portion to be returned
507 uno::Reference
<SwXText
> aEmpty
;
508 return new SwXTextPortion ( pUnoCursor
.get(), aEmpty
, PORTION_TEXT
);
511 // range checking for parameter
513 bool SwAccessibleParagraph::IsValidChar(
514 sal_Int32 nPos
, sal_Int32 nLength
)
516 return (nPos
>= 0) && (nPos
< nLength
);
519 bool SwAccessibleParagraph::IsValidPosition(
520 sal_Int32 nPos
, sal_Int32 nLength
)
522 return (nPos
>= 0) && (nPos
<= nLength
);
525 bool SwAccessibleParagraph::IsValidRange(
526 sal_Int32 nBegin
, sal_Int32 nEnd
, sal_Int32 nLength
)
528 return IsValidPosition(nBegin
, nLength
) && IsValidPosition(nEnd
, nLength
);
531 //the function is to check whether the position is in a redline range.
532 const SwRangeRedline
* SwAccessibleParagraph::GetRedlineAtIndex()
534 const SwRangeRedline
* pRedline
= nullptr;
535 SwPaM
* pCrSr
= GetCursor( true );
538 SwPosition
* pStart
= pCrSr
->Start();
539 pRedline
= pStart
->GetDoc().getIDocumentRedlineAccess().GetRedline(*pStart
, nullptr);
547 bool SwAccessibleParagraph::GetCharBoundary(
548 i18n::Boundary
& rBound
,
549 std::u16string_view text
,
552 if( GetPortionData().FillBoundaryIFDateField( rBound
, nPos
) )
556 o3tl::iterateCodePoints(text
, &nPosEnd
);
558 rBound
.startPos
= nPos
;
559 rBound
.endPos
= nPosEnd
;
564 bool SwAccessibleParagraph::GetWordBoundary(
565 i18n::Boundary
& rBound
,
566 const OUString
& rText
,
569 // now ask the Break-Iterator for the word
570 assert(g_pBreakIt
&& g_pBreakIt
->GetBreakIter().is());
572 // get locale for this position
573 const SwTextFrame
* const pFrame
= GetTextFrame();
574 const TextFrameIndex nCorePos
= GetPortionData().GetCoreViewPosition(nPos
);
575 lang::Locale aLocale
= g_pBreakIt
->GetLocale(pFrame
->GetLangOfChar(nCorePos
, 0, true));
577 // which type of word are we interested in?
578 // (DICTIONARY_WORD includes punctuation, ANY_WORD doesn't.)
579 const sal_Int16 nWordType
= i18n::WordType::ANY_WORD
;
581 // get word boundary, as the Break-Iterator sees fit.
582 rBound
= g_pBreakIt
->GetBreakIter()->getWordBoundary(
583 rText
, nPos
, aLocale
, nWordType
, true );
588 bool SwAccessibleParagraph::GetSentenceBoundary(
589 i18n::Boundary
& rBound
,
590 const OUString
& rText
,
593 const sal_Unicode
* pStr
= rText
.getStr();
594 while( nPos
< rText
.getLength() && pStr
[nPos
] == u
' ' )
597 GetPortionData().GetSentenceBoundary( rBound
, nPos
);
601 bool SwAccessibleParagraph::GetLineBoundary(
602 i18n::Boundary
& rBound
,
603 std::u16string_view aText
,
606 if( sal_Int32(aText
.size()) == nPos
)
607 GetPortionData().GetLastLineBoundary( rBound
);
609 GetPortionData().GetLineBoundary( rBound
, nPos
);
613 bool SwAccessibleParagraph::GetParagraphBoundary(
614 i18n::Boundary
& rBound
,
615 std::u16string_view aText
)
618 rBound
.endPos
= aText
.size();
622 bool SwAccessibleParagraph::GetAttributeBoundary(
623 i18n::Boundary
& rBound
,
626 GetPortionData().GetAttributeBoundary( rBound
, nPos
);
630 bool SwAccessibleParagraph::GetGlyphBoundary(
631 i18n::Boundary
& rBound
,
632 const OUString
& rText
,
635 // ask the Break-Iterator for the glyph by moving one cell
636 // forward, and then one cell back
637 assert(g_pBreakIt
&& g_pBreakIt
->GetBreakIter().is());
639 // get locale for this position
640 const SwTextFrame
* const pFrame
= GetTextFrame();
641 const TextFrameIndex nCorePos
= GetPortionData().GetCoreViewPosition(nPos
);
642 lang::Locale aLocale
= g_pBreakIt
->GetLocale(pFrame
->GetLangOfChar(nCorePos
, 0, true));
644 // get word boundary, as the Break-Iterator sees fit.
645 const sal_Int16 nIterMode
= i18n::CharacterIteratorMode::SKIPCELL
;
647 rBound
.endPos
= g_pBreakIt
->GetBreakIter()->nextCharacters(
648 rText
, nPos
, aLocale
, nIterMode
, 1, nDone
);
649 rBound
.startPos
= g_pBreakIt
->GetBreakIter()->previousCharacters(
650 rText
, rBound
.endPos
, aLocale
, nIterMode
, 1, nDone
);
651 bool bRet
= ((rBound
.startPos
<= nPos
) && (nPos
<= rBound
.endPos
));
652 OSL_ENSURE( rBound
.startPos
<= nPos
, "start pos too high" );
653 OSL_ENSURE( rBound
.endPos
>= nPos
, "end pos too low" );
658 bool SwAccessibleParagraph::GetTextBoundary(
659 i18n::Boundary
& rBound
,
660 const OUString
& rText
,
662 sal_Int16 nTextType
)
665 if( !( AccessibleTextType::LINE
== nTextType
666 ? IsValidPosition( nPos
, rText
.getLength() )
667 : IsValidChar( nPos
, rText
.getLength() ) ) )
668 throw lang::IndexOutOfBoundsException();
674 case AccessibleTextType::WORD
:
675 bRet
= GetWordBoundary(rBound
, rText
, nPos
);
678 case AccessibleTextType::SENTENCE
:
679 bRet
= GetSentenceBoundary( rBound
, rText
, nPos
);
682 case AccessibleTextType::PARAGRAPH
:
683 bRet
= GetParagraphBoundary( rBound
, rText
);
686 case AccessibleTextType::CHARACTER
:
687 bRet
= GetCharBoundary( rBound
, rText
, nPos
);
690 case AccessibleTextType::LINE
:
691 //Solve the problem of returning wrong LINE and PARAGRAPH
692 if((nPos
== rText
.getLength()) && nPos
> 0)
693 bRet
= GetLineBoundary( rBound
, rText
, nPos
- 1);
695 bRet
= GetLineBoundary( rBound
, rText
, nPos
);
698 case AccessibleTextType::ATTRIBUTE_RUN
:
699 bRet
= GetAttributeBoundary( rBound
, nPos
);
702 case AccessibleTextType::GLYPH
:
703 bRet
= GetGlyphBoundary( rBound
, rText
, nPos
);
707 throw lang::IllegalArgumentException( );
713 OUString SAL_CALL
SwAccessibleParagraph::getAccessibleDescription()
715 SolarMutexGuard aGuard
;
719 std::scoped_lock
aGuard2( m_Mutex
);
720 if( m_sDesc
.isEmpty() )
721 m_sDesc
= GetDescription();
726 lang::Locale SAL_CALL
SwAccessibleParagraph::getLocale()
728 SolarMutexGuard aGuard
;
730 const SwTextFrame
*pTextFrame
= GetFrame()->DynCastTextFrame();
733 throw uno::RuntimeException(u
"no SwTextFrame"_ustr
, getXWeak());
736 lang::Locale
aLoc(g_pBreakIt
->GetLocale(pTextFrame
->GetLangOfChar(TextFrameIndex(0), 0, true)));
741 // #i27138# - paragraphs are in relation CONTENT_FLOWS_FROM and/or CONTENT_FLOWS_TO
742 uno::Reference
<XAccessibleRelationSet
> SAL_CALL
SwAccessibleParagraph::getAccessibleRelationSet()
744 SolarMutexGuard aGuard
;
748 rtl::Reference
<utl::AccessibleRelationSetHelper
> pHelper
= new utl::AccessibleRelationSetHelper();
750 const SwTextFrame
* pTextFrame
= GetFrame()->DynCastTextFrame();
751 OSL_ENSURE( pTextFrame
,
752 "<SwAccessibleParagraph::getAccessibleRelationSet()> - missing text frame");
755 const SwContentFrame
* pPrevContentFrame( pTextFrame
->FindPrevCnt() );
756 if ( pPrevContentFrame
)
758 uno::Sequence
<uno::Reference
<XAccessible
>> aSequence
{ GetMap()->GetContext(pPrevContentFrame
) };
759 AccessibleRelation
aAccRel(AccessibleRelationType_CONTENT_FLOWS_FROM
, aSequence
);
760 pHelper
->AddRelation( aAccRel
);
763 const SwContentFrame
* pNextContentFrame( pTextFrame
->FindNextCnt( true ) );
764 if ( pNextContentFrame
)
766 uno::Sequence
<uno::Reference
<XAccessible
>> aSequence
{ GetMap()->GetContext(pNextContentFrame
) };
767 AccessibleRelation
aAccRel(AccessibleRelationType_CONTENT_FLOWS_TO
, aSequence
);
768 pHelper
->AddRelation( aAccRel
);
775 void SAL_CALL
SwAccessibleParagraph::grabFocus()
777 SolarMutexGuard aGuard
;
782 SwCursorShell
*pCursorSh
= GetCursorShell();
783 SwPaM
*pCursor
= GetCursor( false ); // #i27301# - consider new method signature
784 const SwTextFrame
* pTextFrame
= GetTextFrame();
786 if (pCursorSh
!= nullptr &&
787 ( pCursor
== nullptr ||
788 !sw::FrameContainsNode(*pTextFrame
, pCursor
->GetPoint()->GetNodeIndex()) ||
789 !pTextFrame
->IsInside(pTextFrame
->MapModelToViewPos(*pCursor
->GetPoint()))))
791 // create pam for selection
792 SwPosition
const aStartPos(pTextFrame
->MapViewToModelPos(pTextFrame
->GetOffset()));
793 SwPaM
aPaM( aStartPos
);
795 // set PaM at cursor shell
801 vcl::Window
* pWindow
= GetWindow();
803 if (pWindow
!= nullptr)
804 pWindow
->GrabFocus();
809 static bool lcl_GetBackgroundColor( Color
& rColor
,
810 const SwFrame
* pFrame
,
811 SwCursorShell
* pCursorSh
)
813 const SvxBrushItem
* pBackgroundBrush
= nullptr;
814 std::optional
<Color
> xSectionTOXColor
;
816 drawinglayer::attribute::SdrAllFillAttributesHelperPtr aFillAttributes
;
819 pFrame
->GetBackgroundBrush( aFillAttributes
, pBackgroundBrush
, xSectionTOXColor
, aDummyRect
, false, /*bConsiderTextBox=*/false ) )
821 if ( xSectionTOXColor
)
823 rColor
= *xSectionTOXColor
;
828 rColor
= pBackgroundBrush
->GetColor();
832 else if ( pCursorSh
)
834 rColor
= pCursorSh
->Imp()->GetRetoucheColor();
841 sal_Int32 SAL_CALL
SwAccessibleParagraph::getForeground()
845 Color aBackgroundCol
;
847 if ( lcl_GetBackgroundColor( aBackgroundCol
, GetFrame(), GetCursorShell() ) )
849 if ( aBackgroundCol
.IsDark() )
851 return sal_Int32(COL_WHITE
);
855 return sal_Int32(COL_BLACK
);
859 return SwAccessibleContext::getForeground();
862 sal_Int32 SAL_CALL
SwAccessibleParagraph::getBackground()
866 Color aBackgroundCol
;
868 if ( lcl_GetBackgroundColor( aBackgroundCol
, GetFrame(), GetCursorShell() ) )
870 return sal_Int32(aBackgroundCol
);
873 return SwAccessibleContext::getBackground();
876 OUString SAL_CALL
SwAccessibleParagraph::getImplementationName()
878 return sImplementationName
;
881 sal_Bool SAL_CALL
SwAccessibleParagraph::supportsService(
882 const OUString
& sTestServiceName
)
884 return cppu::supportsService(this, sTestServiceName
);
887 uno::Sequence
< OUString
> SAL_CALL
SwAccessibleParagraph::getSupportedServiceNames()
889 return { sServiceName
, sAccessibleServiceName
};
892 static uno::Sequence
< OUString
> const & getAttributeNames()
894 static uno::Sequence
< OUString
> const aNames
896 // Add the font name to attribute list
897 // sorted list of strings
898 UNO_NAME_CHAR_BACK_COLOR
,
900 UNO_NAME_CHAR_CONTOURED
,
901 UNO_NAME_CHAR_EMPHASIS
,
902 UNO_NAME_CHAR_ESCAPEMENT
,
903 UNO_NAME_CHAR_FONT_NAME
,
904 UNO_NAME_CHAR_HEIGHT
,
905 UNO_NAME_CHAR_POSTURE
,
906 UNO_NAME_CHAR_SHADOWED
,
907 UNO_NAME_CHAR_STRIKEOUT
,
908 UNO_NAME_CHAR_UNDERLINE
,
909 UNO_NAME_CHAR_UNDERLINE_COLOR
,
910 UNO_NAME_CHAR_WEIGHT
,
915 static uno::Sequence
< OUString
> const & getSupplementalAttributeNames()
917 static uno::Sequence
< OUString
> const aNames
919 // sorted list of strings
920 UNO_NAME_NUMBERING_LEVEL
,
922 UNO_NAME_NUMBERING_RULES
,
923 UNO_NAME_PARA_ADJUST
,
924 UNO_NAME_PARA_BOTTOM_MARGIN
,
925 UNO_NAME_PARA_FIRST_LINE_INDENT
,
926 UNO_NAME_PARA_LEFT_MARGIN
,
927 UNO_NAME_PARA_LINE_SPACING
,
928 UNO_NAME_PARA_RIGHT_MARGIN
,
936 sal_Int32
SwAccessibleParagraph::getCaretPosition()
938 SolarMutexGuard aGuard
;
942 sal_Int32 nRet
= GetCaretPos();
944 std::scoped_lock
aOldCaretPosGuard( m_Mutex
);
945 OSL_ENSURE( nRet
== m_nOldCaretPos
, "caret pos out of sync" );
946 m_nOldCaretPos
= nRet
;
950 ::rtl::Reference
< SwAccessibleContext
> xThis( this );
951 GetMap()->SetCursorContext( xThis
);
957 sal_Bool SAL_CALL
SwAccessibleParagraph::setCaretPosition( sal_Int32 nIndex
)
959 SolarMutexGuard aGuard
;
963 // parameter checking
964 sal_Int32 nLength
= GetString().getLength();
965 if ( ! IsValidPosition( nIndex
, nLength
) )
967 throw lang::IndexOutOfBoundsException();
973 SwCursorShell
* pCursorShell
= GetCursorShell();
974 if( pCursorShell
!= nullptr )
976 // create pam for selection
977 const SwTextFrame
* const pFrame
= GetTextFrame();
978 TextFrameIndex
const nFrameIndex(GetPortionData().GetCoreViewPosition(nIndex
));
979 SwPosition
aStartPos(pFrame
->MapViewToModelPos(nFrameIndex
));
980 SwPaM
aPaM( aStartPos
);
982 // set PaM at cursor shell
983 bRet
= Select( aPaM
);
989 sal_Unicode
SwAccessibleParagraph::getCharacter( sal_Int32 nIndex
)
991 SolarMutexGuard aGuard
;
995 OUString
sText( GetString() );
997 // return character (if valid)
998 if( !IsValidChar(nIndex
, sText
.getLength() ) )
999 throw lang::IndexOutOfBoundsException();
1001 return sText
[nIndex
];
1004 css::uno::Sequence
< css::style::TabStop
> SwAccessibleParagraph::GetCurrentTabStop( sal_Int32 nIndex
)
1006 SolarMutexGuard aGuard
;
1010 /* #i12332# The position after the string needs special treatment.
1011 IsValidChar -> IsValidPosition
1013 if( ! (IsValidPosition( nIndex
, GetString().getLength() ) ) )
1014 throw lang::IndexOutOfBoundsException();
1017 bool bBehindText
= false;
1018 if ( nIndex
== GetString().getLength() )
1021 // get model position & prepare GetCharRect() arguments
1022 SwCursorMoveState aMoveState
;
1023 aMoveState
.m_bRealHeight
= true;
1024 aMoveState
.m_bRealWidth
= true;
1025 SwSpecialPos aSpecialPos
;
1026 const SwTextFrame
* const pFrame
= GetTextFrame();
1028 /* #i12332# FillSpecialPos does not accept nIndex ==
1029 GetString().getLength(). In that case nPos is set to the
1030 length of the string in the core. This way GetCharRect
1031 returns the rectangle for a cursor at the end of the
1033 const TextFrameIndex nPos
= bBehindText
1034 ? TextFrameIndex(pFrame
->GetText().getLength())
1035 : GetPortionData().FillSpecialPos(nIndex
, aSpecialPos
, aMoveState
.m_pSpecialPos
);
1039 SwPosition
aPosition(pFrame
->MapViewToModelPos(nPos
));
1040 GetFrame()->GetCharRect( aCoreRect
, aPosition
, &aMoveState
);
1042 // already get the caret position
1043 css::uno::Sequence
< css::style::TabStop
> tabs
;
1044 const sal_Int32 nStrLen
= pFrame
->GetText().getLength();
1047 SwFrame
* pTFrame
= const_cast<SwFrame
*>(GetFrame());
1048 tabs
= pTFrame
->GetTabStopInfo(aCoreRect
.Left());
1051 if( tabs
.hasElements() )
1053 // translate core coordinates into accessibility coordinates
1054 vcl::Window
*pWin
= GetWindow();
1057 throw uno::RuntimeException(u
"no Window"_ustr
, getXWeak());
1060 SwRect
aTmpRect(0, 0, tabs
[0].Position
, 0);
1062 tools::Rectangle
aScreenRect( GetMap()->CoreToPixel( aTmpRect
));
1063 SwRect
aFrameLogBounds( GetBounds( *(GetMap()) ) ); // twip rel to doc root
1065 Point
aFramePixPos( GetMap()->CoreToPixel( aFrameLogBounds
).TopLeft() );
1066 aScreenRect
.Move( -aFramePixPos
.X(), -aFramePixPos
.Y() );
1068 tabs
.getArray()[0].Position
= aScreenRect
.GetWidth();
1078 const PropertyValue
* pValues
;
1079 explicit IndexCompare( const PropertyValue
* pVals
) : pValues(pVals
) {}
1080 bool operator() ( sal_Int32 a
, sal_Int32 b
) const
1082 return (pValues
[a
].Name
< pValues
[b
].Name
);
1088 OUString
SwAccessibleParagraph::GetFieldTypeNameAtIndex(sal_Int32 nIndex
)
1090 OUString strTypeName
;
1092 SwTextField
* pTextField
= nullptr;
1093 sal_Int32 nFieldIndex
= GetPortionData().GetFieldIndex(nIndex
);
1094 if (nFieldIndex
>= 0)
1096 const SwTextFrame
* const pFrame
= GetTextFrame();
1097 sw::MergedAttrIter
iter(*pFrame
);
1098 while (SwTextAttr
const*const pHt
= iter
.NextAttr())
1100 if ((pHt
->Which() == RES_TXTATR_FIELD
1101 || pHt
->Which() == RES_TXTATR_ANNOTATION
1102 || pHt
->Which() == RES_TXTATR_INPUTFIELD
)
1103 && (nFieldIndex
-- == 0))
1105 pTextField
= const_cast<SwTextField
*>(
1106 static_txtattr_cast
<SwTextField
const*>(pHt
));
1109 else if (pHt
->Which() == RES_TXTATR_REFMARK
1110 && (nFieldIndex
-- == 0))
1112 strTypeName
= "set reference";
1118 const SwField
* pField
= pTextField
->GetFormatField().GetField();
1121 strTypeName
= SwFieldType::GetTypeStr(pField
->GetTypeId());
1122 const SwFieldIds nWhich
= pField
->GetTyp()->Which();
1124 sal_uInt32 subType
= 0;
1127 case SwFieldIds::DocStat
:
1128 subType
= static_cast<const SwDocStatField
*>(pField
)->GetSubType();
1130 case SwFieldIds::GetRef
:
1132 switch( pField
->GetSubType() )
1136 const SwGetRefField
* pRefField
= dynamic_cast<const SwGetRefField
*>(pField
);
1137 if ( pRefField
&& pRefField
->IsRefToHeadingCrossRefBookmark() )
1138 sEntry
= "Headings";
1139 else if ( pRefField
&& pRefField
->IsRefToNumItemCrossRefBookmark() )
1140 sEntry
= "Numbered Paragraphs";
1142 sEntry
= "Bookmarks";
1146 sEntry
= "Footnotes";
1149 sEntry
= "Endnotes";
1151 case REF_SETREFATTR
:
1152 sEntry
= "Insert Reference";
1154 case REF_SEQUENCEFLD
:
1155 sEntry
= static_cast<const SwGetRefField
*>(pField
)->GetSetRefName();
1158 sEntry
= "StyleRef";
1162 strTypeName
= sEntry
;
1163 // <pField->GetFormat() >= 0> is always true as <pField->GetFormat()> is unsigned
1164 // if (pField->GetFormat() >= 0)
1166 sEntry
= aMgr
.GetFormatStr( pField
->GetTypeId(), pField
->GetFormat() );
1167 if (sEntry
.getLength() > 0)
1169 strTypeName
+= "-" + sEntry
;
1174 case SwFieldIds::DateTime
:
1175 subType
= static_cast<const SwDateTimeField
*>(pField
)->GetSubType();
1177 case SwFieldIds::JumpEdit
:
1179 const sal_uInt32 nFormat
= pField
->GetFormat();
1180 const sal_uInt16 nSize
= aMgr
.GetFormatCount(pField
->GetTypeId(), false);
1181 if (nFormat
< nSize
)
1183 sEntry
= aMgr
.GetFormatStr(pField
->GetTypeId(), nFormat
);
1184 if (sEntry
.getLength() > 0)
1186 strTypeName
+= "-" + sEntry
;
1191 case SwFieldIds::ExtUser
:
1192 subType
= static_cast<const SwExtUserField
*>(pField
)->GetSubType();
1194 case SwFieldIds::HiddenText
:
1195 case SwFieldIds::SetExp
:
1197 sEntry
= pField
->GetTyp()->GetName();
1198 if (sEntry
.getLength() > 0)
1200 strTypeName
+= "-" + sEntry
;
1204 case SwFieldIds::DocInfo
:
1205 subType
= pField
->GetSubType();
1208 case SwFieldIds::RefPageSet
:
1210 const SwRefPageSetField
* pRPld
= static_cast<const SwRefPageSetField
*>(pField
);
1211 bool bOn
= pRPld
->IsOn();
1214 strTypeName
+= "on";
1216 strTypeName
+= "off";
1219 case SwFieldIds::Author
:
1221 strTypeName
+= "-" + aMgr
.GetFormatStr(pField
->GetTypeId(), pField
->GetFormat() & 0xff);
1226 if (subType
> 0 || nWhich
== SwFieldIds::DocInfo
|| nWhich
== SwFieldIds::ExtUser
|| nWhich
== SwFieldIds::DocStat
)
1228 std::vector
<OUString
> aLst
;
1229 aMgr
.GetSubTypes(pField
->GetTypeId(), aLst
);
1230 if (subType
< aLst
.size())
1231 sEntry
= aLst
[subType
];
1232 if (sEntry
.getLength() > 0)
1234 if (nWhich
== SwFieldIds::DocInfo
)
1236 strTypeName
= sEntry
;
1237 sal_uInt16 nSize
= aMgr
.GetFormatCount(pField
->GetTypeId(), false);
1238 const sal_uInt16 nExSub
= pField
->GetSubType() & 0xff00;
1239 if (nSize
> 0 && nExSub
> 0)
1241 //Get extra subtype string
1243 sEntry
= aMgr
.GetFormatStr(pField
->GetTypeId(), nExSub
/0x0100-1);
1244 strTypeName
+= sEntry
;
1249 strTypeName
+= "-" + sEntry
;
1258 // #i63870# - re-implement method on behalf of methods
1259 // <_getDefaultAttributesImpl(..)> and <_getRunAttributesImpl(..)>
1260 uno::Sequence
<PropertyValue
> SwAccessibleParagraph::getCharacterAttributes(
1262 const uno::Sequence
< OUString
>& aRequestedAttributes
)
1265 SolarMutexGuard aGuard
;
1269 const OUString
& rText
= GetString();
1271 if (!IsValidPosition(nIndex
, rText
.getLength()))
1272 throw lang::IndexOutOfBoundsException();
1274 bool bSupplementalMode
= false;
1275 uno::Sequence
< OUString
> aNames
= aRequestedAttributes
;
1276 if (!aNames
.hasElements())
1278 bSupplementalMode
= true;
1279 aNames
= getAttributeNames();
1281 // retrieve default character attributes
1282 tAccParaPropValMap aDefAttrSeq
;
1283 _getDefaultAttributesImpl( aNames
, aDefAttrSeq
, true );
1285 // retrieved run character attributes
1286 tAccParaPropValMap aRunAttrSeq
;
1287 _getRunAttributesImpl( nIndex
, aNames
, aRunAttrSeq
);
1289 // this allows to request one or more supplemental attributes, only
1290 bSupplementalMode
= bSupplementalMode
|| aDefAttrSeq
.empty() || aRunAttrSeq
.empty();
1292 // merge default and run attributes
1293 std::vector
< PropertyValue
> aValues( aDefAttrSeq
.size() );
1295 for ( const auto& rDefEntry
: aDefAttrSeq
)
1297 tAccParaPropValMap::const_iterator aRunIter
=
1298 aRunAttrSeq
.find( rDefEntry
.first
);
1299 if ( aRunIter
!= aRunAttrSeq
.end() )
1301 aValues
[i
] = aRunIter
->second
;
1305 aValues
[i
] = rDefEntry
.second
;
1309 if( bSupplementalMode
)
1311 uno::Sequence
< OUString
> aSupplementalNames
= aRequestedAttributes
;
1312 if (!aSupplementalNames
.hasElements())
1313 aSupplementalNames
= getSupplementalAttributeNames();
1315 tAccParaPropValMap aSupplementalAttrSeq
;
1316 _getSupplementalAttributesImpl( aSupplementalNames
, aSupplementalAttrSeq
);
1318 aValues
.resize( aValues
.size() + aSupplementalAttrSeq
.size() );
1320 for ( const auto& rSupplementalEntry
: aSupplementalAttrSeq
)
1322 aValues
[i
] = rSupplementalEntry
.second
;
1326 _correctValues( nIndex
, aValues
);
1328 aValues
.emplace_back();
1330 OUString strTypeName
= GetFieldTypeNameAtIndex(nIndex
);
1331 if (!strTypeName
.isEmpty())
1333 aValues
.emplace_back();
1334 PropertyValue
& rValueFT
= aValues
.back();
1335 rValueFT
.Name
= "FieldType";
1336 rValueFT
.Value
<<= strTypeName
.toAsciiLowerCase();
1337 rValueFT
.Handle
= -1;
1338 rValueFT
.State
= PropertyState_DIRECT_VALUE
;
1341 //sort property values
1342 // build sorted index array
1343 sal_Int32 nLength
= aValues
.size();
1344 std::vector
<sal_Int32
> aIndices
;
1345 aIndices
.reserve(nLength
);
1346 for (i
= 0; i
< nLength
; ++i
)
1347 aIndices
.push_back(i
);
1348 std::sort(aIndices
.begin(), aIndices
.end(), IndexCompare(aValues
.data()));
1349 // create sorted sequences according to index array
1350 uno::Sequence
<PropertyValue
> aNewValues( nLength
);
1351 PropertyValue
* pNewValues
= aNewValues
.getArray();
1352 for (i
= 0; i
< nLength
; ++i
)
1354 pNewValues
[i
] = aValues
[aIndices
[i
]];
1359 return comphelper::containerToSequence(aValues
);
1362 static void SetPutRecursive(SfxItemSet
&targetSet
, const SfxItemSet
&sourceSet
)
1364 const SfxItemSet
*const pParentSet
= sourceSet
.GetParent();
1366 SetPutRecursive(targetSet
, *pParentSet
);
1367 targetSet
.Put(sourceSet
);
1371 void SwAccessibleParagraph::_getDefaultAttributesImpl(
1372 const uno::Sequence
< OUString
>& aRequestedAttributes
,
1373 tAccParaPropValMap
& rDefAttrSeq
,
1374 const bool bOnlyCharAttrs
)
1376 // retrieve default attributes
1377 const SwTextFrame
* const pFrame
= GetTextFrame();
1378 const SwTextNode
*const pTextNode(pFrame
->GetTextNodeForParaProps());
1379 std::optional
<SfxItemSet
> pSet
;
1380 if ( !bOnlyCharAttrs
)
1382 pSet
.emplace( const_cast<SwAttrPool
&>(pTextNode
->GetDoc().GetAttrPool()),
1383 svl::Items
<RES_CHRATR_BEGIN
, RES_CHRATR_END
- 1,
1384 RES_PARATR_BEGIN
, RES_PARATR_END
- 1,
1385 RES_FRMATR_BEGIN
, RES_FRMATR_END
- 1> );
1389 pSet
.emplace( const_cast<SwAttrPool
&>(pTextNode
->GetDoc().GetAttrPool()),
1390 svl::Items
<RES_CHRATR_BEGIN
, RES_CHRATR_END
- 1> );
1392 // #i82637# - From the perspective of the a11y API the default character
1393 // attributes are the character attributes, which are set at the paragraph style
1394 // of the paragraph. The character attributes set at the automatic paragraph
1395 // style of the paragraph are treated as run attributes.
1396 // pTextNode->SwContentNode::GetAttr( *pSet );
1397 // get default paragraph attributes, if needed, and merge these into <pSet>
1398 if ( !bOnlyCharAttrs
)
1400 SfxItemSetFixed
<RES_PARATR_BEGIN
, RES_PARATR_END
- 1,
1401 RES_FRMATR_BEGIN
, RES_FRMATR_END
- 1>
1402 aParaSet( const_cast<SwAttrPool
&>(pTextNode
->GetDoc().GetAttrPool()) );
1403 pTextNode
->SwContentNode::GetAttr( aParaSet
);
1404 pSet
->Put( aParaSet
);
1406 // get default character attributes and merge these into <pSet>
1407 OSL_ENSURE( pTextNode
->GetTextColl(),
1408 "<SwAccessibleParagraph::_getDefaultAttributesImpl(..)> - missing paragraph style. Serious defect!" );
1409 if ( pTextNode
->GetTextColl() )
1411 SfxItemSetFixed
<RES_CHRATR_BEGIN
, RES_CHRATR_END
- 1>
1412 aCharSet( const_cast<SwAttrPool
&>(pTextNode
->GetDoc().GetAttrPool()) );
1413 SetPutRecursive( aCharSet
, pTextNode
->GetTextColl()->GetAttrSet() );
1414 pSet
->Put( aCharSet
);
1417 // build-up sequence containing the run attributes <rDefAttrSeq>
1418 tAccParaPropValMap aDefAttrSeq
;
1420 const SfxItemPropertyMap
& rPropMap
=
1421 aSwMapProvider
.GetPropertySet( PROPERTY_MAP_TEXT_CURSOR
)->getPropertyMap();
1422 for ( const auto pEntry
: rPropMap
.getPropertyEntries() )
1424 const SfxPoolItem
* pItem
= pSet
->GetItem( pEntry
->nWID
);
1428 pItem
->QueryValue( aVal
, pEntry
->nMemberId
);
1430 PropertyValue rPropVal
;
1431 rPropVal
.Name
= pEntry
->aName
;
1432 rPropVal
.Value
= std::move(aVal
);
1433 rPropVal
.Handle
= -1;
1434 rPropVal
.State
= beans::PropertyState_DEFAULT_VALUE
;
1436 aDefAttrSeq
[rPropVal
.Name
] = rPropVal
;
1441 // add property value entry for the paragraph style
1442 if ( !bOnlyCharAttrs
&& pTextNode
->GetTextColl() )
1444 if ( aDefAttrSeq
.find( UNO_NAME_PARA_STYLE_NAME
) == aDefAttrSeq
.end() )
1446 PropertyValue rPropVal
;
1447 rPropVal
.Name
= UNO_NAME_PARA_STYLE_NAME
;
1448 rPropVal
.Value
<<= pTextNode
->GetTextColl()->GetName();
1449 rPropVal
.Handle
= -1;
1450 rPropVal
.State
= beans::PropertyState_DEFAULT_VALUE
;
1452 aDefAttrSeq
[rPropVal
.Name
] = rPropVal
;
1457 // resolve value text::WritingMode2::PAGE of property value entry WritingMode
1458 if ( !bOnlyCharAttrs
&& GetFrame() )
1460 tAccParaPropValMap::iterator aIter
= aDefAttrSeq
.find( UNO_NAME_WRITING_MODE
);
1461 if ( aIter
!= aDefAttrSeq
.end() )
1463 PropertyValue
rPropVal( aIter
->second
);
1464 sal_Int16 nVal
= rPropVal
.Value
.get
<sal_Int16
>();
1465 if ( nVal
== text::WritingMode2::PAGE
)
1467 const SwFrame
* pUpperFrame( GetFrame()->GetUpper() );
1468 while ( pUpperFrame
)
1470 if ( pUpperFrame
->GetType() &
1471 ( SwFrameType::Page
| SwFrameType::Fly
| SwFrameType::Section
| SwFrameType::Tab
| SwFrameType::Cell
) )
1473 if ( pUpperFrame
->IsVertical() )
1475 nVal
= text::WritingMode2::TB_RL
;
1477 else if ( pUpperFrame
->IsRightToLeft() )
1479 nVal
= text::WritingMode2::RL_TB
;
1483 nVal
= text::WritingMode2::LR_TB
;
1485 rPropVal
.Value
<<= nVal
;
1486 aDefAttrSeq
[rPropVal
.Name
] = rPropVal
;
1490 if ( pUpperFrame
->IsFlyFrame() )
1492 pUpperFrame
= static_cast<const SwFlyFrame
*>(pUpperFrame
)->GetAnchorFrame();
1496 pUpperFrame
= pUpperFrame
->GetUpper();
1504 if ( !aRequestedAttributes
.hasElements() )
1506 rDefAttrSeq
= std::move(aDefAttrSeq
);
1510 for( const OUString
& rReqAttr
: aRequestedAttributes
)
1512 tAccParaPropValMap::const_iterator
const aIter
= aDefAttrSeq
.find( rReqAttr
);
1513 if ( aIter
!= aDefAttrSeq
.end() )
1515 rDefAttrSeq
[ aIter
->first
] = aIter
->second
;
1521 uno::Sequence
< PropertyValue
> SwAccessibleParagraph::getDefaultAttributes(
1522 const uno::Sequence
< OUString
>& aRequestedAttributes
)
1524 SolarMutexGuard aGuard
;
1528 tAccParaPropValMap aDefAttrSeq
;
1529 _getDefaultAttributesImpl( aRequestedAttributes
, aDefAttrSeq
);
1532 static constexpr OUString sMMToPixelRatio
= u
"MMToPixelRatio"_ustr
;
1533 bool bProvideMMToPixelRatio( !aRequestedAttributes
.hasElements() ||
1534 (comphelper::findValue(aRequestedAttributes
, sMMToPixelRatio
) != -1) );
1536 uno::Sequence
< PropertyValue
> aValues( aDefAttrSeq
.size() +
1537 ( bProvideMMToPixelRatio
? 1 : 0 ) );
1538 auto pValues
= aValues
.getArray();
1539 std::transform(aDefAttrSeq
.begin(), aDefAttrSeq
.end(), pValues
,
1540 [](const auto& rEntry
) -> PropertyValue
{ return rEntry
.second
; });
1543 if ( bProvideMMToPixelRatio
)
1545 PropertyValue rPropVal
;
1546 rPropVal
.Name
= sMMToPixelRatio
;
1547 const Size
a100thMMSize( 1000, 1000 );
1548 const Size aPixelSize
= GetMap()->LogicToPixel( a100thMMSize
);
1549 const float fRatio
= (static_cast<float>(a100thMMSize
.Width())/100)/aPixelSize
.Width();
1550 rPropVal
.Value
<<= fRatio
;
1551 rPropVal
.Handle
= -1;
1552 rPropVal
.State
= beans::PropertyState_DEFAULT_VALUE
;
1553 pValues
[ aValues
.getLength() - 1 ] = std::move(rPropVal
);
1559 void SwAccessibleParagraph::_getRunAttributesImpl(
1560 const sal_Int32 nIndex
,
1561 const uno::Sequence
< OUString
>& aRequestedAttributes
,
1562 tAccParaPropValMap
& rRunAttrSeq
)
1564 // create PaM for character at position <nIndex>
1565 std::optional
<SwPaM
> pPaM
;
1566 const TextFrameIndex
nCorePos(GetPortionData().GetCoreViewPosition(nIndex
));
1567 const SwTextFrame
* const pFrame
= GetTextFrame();
1568 SwPosition
const aModelPos(pFrame
->MapViewToModelPos(nCorePos
));
1569 SwTextNode
*const pTextNode(aModelPos
.GetNode().GetTextNode());
1571 SwPosition
const aEndPos(*pTextNode
,
1572 aModelPos
.GetContentIndex() == pTextNode
->Len()
1573 ? pTextNode
->Len() // ???
1574 : aModelPos
.GetContentIndex() + 1);
1575 pPaM
.emplace(aModelPos
, aEndPos
);
1578 // retrieve character attributes for the created PaM <pPaM>
1579 SfxItemSetFixed
<RES_CHRATR_BEGIN
, RES_CHRATR_END
-1> aSet( pPaM
->GetDoc().GetAttrPool() );
1581 // From the perspective of the a11y API the character attributes, which
1582 // are set at the automatic paragraph style of the paragraph, are treated
1583 // as run attributes.
1584 // SwXTextCursor::GetCursorAttr( *pPaM, aSet, sal_True, sal_True );
1585 // get character attributes from automatic paragraph style and merge these into <aSet>
1587 if ( pTextNode
->HasSwAttrSet() )
1589 SfxItemSetFixed
<RES_CHRATR_BEGIN
, RES_CHRATR_END
-1> aAutomaticParaStyleCharAttrs( pPaM
->GetDoc().GetAttrPool());
1590 aAutomaticParaStyleCharAttrs
.Put( *(pTextNode
->GetpSwAttrSet()), false );
1591 aSet
.Put( aAutomaticParaStyleCharAttrs
);
1594 // get character attributes at <pPaM> and merge these into <aSet>
1596 SfxItemSetFixed
<RES_CHRATR_BEGIN
, RES_CHRATR_END
-1> aCharAttrsAtPaM( pPaM
->GetDoc().GetAttrPool() );
1597 SwUnoCursorHelper::GetCursorAttr(*pPaM
, aCharAttrsAtPaM
, true);
1598 aSet
.Put( aCharAttrsAtPaM
);
1601 // build-up sequence containing the run attributes <rRunAttrSeq>
1603 tAccParaPropValMap aRunAttrSeq
;
1605 tAccParaPropValMap aDefAttrSeq
;
1606 uno::Sequence
< OUString
> aDummy
;
1607 _getDefaultAttributesImpl( aDummy
, aDefAttrSeq
, true ); // #i82637#
1609 const SfxItemPropertyMap
& rPropMap
=
1610 aSwMapProvider
.GetPropertySet( PROPERTY_MAP_TEXT_CURSOR
)->getPropertyMap();
1611 for ( const auto pEntry
: rPropMap
.getPropertyEntries() )
1613 const SfxPoolItem
* pItem( nullptr );
1614 // #i82637# - Found character attributes, whose value equals the value of
1615 // the corresponding default character attributes, are excluded.
1616 if ( aSet
.GetItemState( pEntry
->nWID
, true, &pItem
) == SfxItemState::SET
)
1619 pItem
->QueryValue( aVal
, pEntry
->nMemberId
);
1621 PropertyValue rPropVal
;
1622 rPropVal
.Name
= pEntry
->aName
;
1623 rPropVal
.Value
= std::move(aVal
);
1624 rPropVal
.Handle
= -1;
1625 rPropVal
.State
= PropertyState_DIRECT_VALUE
;
1627 tAccParaPropValMap::const_iterator aDefIter
=
1628 aDefAttrSeq
.find( rPropVal
.Name
);
1629 if ( aDefIter
== aDefAttrSeq
.end() ||
1630 rPropVal
.Value
!= aDefIter
->second
.Value
)
1632 aRunAttrSeq
[rPropVal
.Name
] = rPropVal
;
1638 if ( !aRequestedAttributes
.hasElements() )
1640 rRunAttrSeq
= std::move(aRunAttrSeq
);
1644 for( const OUString
& rReqAttr
: aRequestedAttributes
)
1646 tAccParaPropValMap::iterator aIter
= aRunAttrSeq
.find( rReqAttr
);
1647 if ( aIter
!= aRunAttrSeq
.end() )
1649 rRunAttrSeq
[ (*aIter
).first
] = (*aIter
).second
;
1656 uno::Sequence
< PropertyValue
> SwAccessibleParagraph::getRunAttributes(
1658 const uno::Sequence
< OUString
>& aRequestedAttributes
)
1660 SolarMutexGuard aGuard
;
1665 const OUString
& rText
= GetString();
1666 if (!IsValidPosition(nIndex
, rText
.getLength()))
1668 throw lang::IndexOutOfBoundsException();
1672 tAccParaPropValMap aRunAttrSeq
;
1673 _getRunAttributesImpl( nIndex
, aRequestedAttributes
, aRunAttrSeq
);
1675 return comphelper::mapValuesToSequence( aRunAttrSeq
);
1678 void SwAccessibleParagraph::_getSupplementalAttributesImpl(
1679 const uno::Sequence
< OUString
>& aRequestedAttributes
,
1680 tAccParaPropValMap
& rSupplementalAttrSeq
)
1682 const SwTextFrame
* const pFrame
= GetTextFrame();
1683 const SwTextNode
*const pTextNode(pFrame
->GetTextNodeForParaProps());
1685 RES_PARATR_LINESPACING
, RES_PARATR_ADJUST
,
1686 RES_PARATR_TABSTOP
, RES_PARATR_TABSTOP
,
1687 RES_PARATR_NUMRULE
, RES_PARATR_NUMRULE
,
1688 RES_PARATR_LIST_BEGIN
, RES_PARATR_LIST_END
- 1,
1689 RES_MARGIN_FIRSTLINE
, RES_MARGIN_RIGHT
,
1690 RES_UL_SPACE
, RES_UL_SPACE
>
1691 aSet( const_cast<SwAttrPool
&>(pTextNode
->GetDoc().GetAttrPool()) );
1693 if ( pTextNode
->HasBullet() || pTextNode
->HasNumber() )
1695 aSet
.Put( pTextNode
->GetAttr(RES_PARATR_LIST_LEVEL
) );
1696 aSet
.Put( pTextNode
->GetAttr(RES_PARATR_LIST_ISCOUNTED
) );
1698 aSet
.Put( pTextNode
->SwContentNode::GetAttr(RES_UL_SPACE
) );
1699 aSet
.Put( pTextNode
->SwContentNode::GetAttr(RES_MARGIN_FIRSTLINE
) );
1700 aSet
.Put( pTextNode
->SwContentNode::GetAttr(RES_MARGIN_TEXTLEFT
) );
1701 aSet
.Put( pTextNode
->SwContentNode::GetAttr(RES_MARGIN_RIGHT
) );
1702 aSet
.Put( pTextNode
->SwContentNode::GetAttr(RES_PARATR_ADJUST
) );
1704 tAccParaPropValMap aSupplementalAttrSeq
;
1706 std::span
<const SfxItemPropertyMapEntry
> pPropMap(
1707 aSwMapProvider
.GetPropertyMapEntries( PROPERTY_MAP_ACCESSIBILITY_TEXT_ATTRIBUTE
) );
1708 for (const auto & rEntry
: pPropMap
)
1710 // For a paragraph, list level property is not set but when queried the returned default
1711 // value is 0, exactly the same value of top level list item; that prevents using
1712 // list level property for discerning simple paragraph from list item;
1713 // the following check allows not to return the list level property at all
1714 // when we are dealing with a simple paragraph
1715 if ((rEntry
.nWID
== RES_PARATR_LIST_LEVEL
|| rEntry
.nWID
== RES_PARATR_LIST_ISCOUNTED
) &&
1716 !aSet
.HasItem( rEntry
.nWID
))
1719 const SfxPoolItem
* pItem
= aSet
.GetItem( rEntry
.nWID
);
1723 pItem
->QueryValue( aVal
, rEntry
.nMemberId
);
1725 PropertyValue rPropVal
;
1726 rPropVal
.Name
= rEntry
.aName
;
1727 rPropVal
.Value
= std::move(aVal
);
1728 rPropVal
.Handle
= -1;
1729 rPropVal
.State
= beans::PropertyState_DEFAULT_VALUE
;
1731 aSupplementalAttrSeq
[rPropVal
.Name
] = rPropVal
;
1736 for( const OUString
& rSupplementalAttr
: aRequestedAttributes
)
1738 tAccParaPropValMap::const_iterator
const aIter
= aSupplementalAttrSeq
.find( rSupplementalAttr
);
1739 if ( aIter
!= aSupplementalAttrSeq
.end() )
1741 rSupplementalAttrSeq
[ aIter
->first
] = aIter
->second
;
1746 void SwAccessibleParagraph::_correctValues( const sal_Int32 nIndex
,
1747 std::vector
< PropertyValue
>& rValues
)
1749 PropertyValue ChangeAttr
, ChangeAttrColor
;
1751 const SwRangeRedline
* pRedline
= GetRedlineAtIndex();
1755 const SwModuleOptions
* pOpt
= SwModule::get()->GetModuleConfig();
1756 AuthorCharAttr aChangeAttr
;
1759 switch( pRedline
->GetType())
1761 case RedlineType::Insert
:
1762 aChangeAttr
= pOpt
->GetInsertAuthorAttr();
1764 case RedlineType::Delete
:
1765 aChangeAttr
= pOpt
->GetDeletedAuthorAttr();
1767 case RedlineType::Format
:
1768 aChangeAttr
= pOpt
->GetFormatAuthorAttr();
1773 switch( aChangeAttr
.m_nItemId
)
1775 case SID_ATTR_CHAR_WEIGHT
:
1776 ChangeAttr
.Name
= UNO_NAME_CHAR_WEIGHT
;
1777 ChangeAttr
.Value
<<= awt::FontWeight::BOLD
;
1779 case SID_ATTR_CHAR_POSTURE
:
1780 ChangeAttr
.Name
= UNO_NAME_CHAR_POSTURE
;
1781 ChangeAttr
.Value
<<= awt::FontSlant_ITALIC
; //char posture
1783 case SID_ATTR_CHAR_STRIKEOUT
:
1784 ChangeAttr
.Name
= UNO_NAME_CHAR_STRIKEOUT
;
1785 ChangeAttr
.Value
<<= awt::FontStrikeout::SINGLE
; //char strikeout
1787 case SID_ATTR_CHAR_UNDERLINE
:
1788 ChangeAttr
.Name
= UNO_NAME_CHAR_UNDERLINE
;
1789 ChangeAttr
.Value
<<= aChangeAttr
.m_nAttr
; //underline line
1792 if( aChangeAttr
.m_nColor
!= COL_NONE_COLOR
)
1794 if( aChangeAttr
.m_nItemId
== SID_ATTR_BRUSH
)
1796 ChangeAttrColor
.Name
= UNO_NAME_CHAR_BACK_COLOR
;
1797 if( aChangeAttr
.m_nColor
== COL_TRANSPARENT
)//char backcolor
1798 ChangeAttrColor
.Value
<<= COL_BLUE
;
1800 ChangeAttrColor
.Value
<<= aChangeAttr
.m_nColor
;
1804 ChangeAttrColor
.Name
= UNO_NAME_CHAR_COLOR
;
1805 if( aChangeAttr
.m_nColor
== COL_TRANSPARENT
)//char color
1806 ChangeAttrColor
.Value
<<= COL_BLUE
;
1808 ChangeAttrColor
.Value
<<= aChangeAttr
.m_nColor
;
1813 // sw_redlinehide: this function only needs SwWrongList for 1 character,
1814 // and the end is excluded by InWrongWord(),
1815 // so it ought to work to just pick the wrong-list/node that contains
1816 // the character following the given nIndex
1817 const SwTextFrame
* const pFrame
= GetTextFrame();
1818 TextFrameIndex
const nCorePos(GetPortionData().GetCoreViewPosition(nIndex
));
1819 std::pair
<SwTextNode
*, sal_Int32
> pos(pFrame
->MapViewToModel(nCorePos
));
1820 if (pos
.first
->Len() == pos
.second
1821 && nCorePos
!= TextFrameIndex(pFrame
->GetText().getLength()))
1823 pos
= pFrame
->MapViewToModel(nCorePos
+ TextFrameIndex(1)); // try this one instead
1824 assert(pos
.first
->Len() != pos
.second
);
1827 sal_Int32 nValues
= rValues
.size();
1828 for (sal_Int32 i
= 0; i
< nValues
; ++i
)
1830 PropertyValue
& rValue
= rValues
[i
];
1832 if (rValue
.Name
== ChangeAttr
.Name
)
1834 rValue
.Value
= ChangeAttr
.Value
;
1838 if (rValue
.Name
== ChangeAttrColor
.Name
)
1840 rValue
.Value
= ChangeAttrColor
.Value
;
1845 if (rValue
.Name
== UNO_NAME_CHAR_BACK_COLOR
)
1847 uno::Any
&anyChar
= rValue
.Value
;
1849 anyChar
>>= backColor
;
1850 if (COL_AUTO
== backColor
)
1852 uno::Reference
<XAccessibleComponent
> xComponent(this);
1853 if (xComponent
.is())
1855 sal_uInt32 crBack
= static_cast<sal_uInt32
>(xComponent
->getBackground());
1856 rValue
.Value
<<= crBack
;
1863 if (rValue
.Name
== UNO_NAME_CHAR_COLOR
)
1865 if( GetPortionData().IsInGrayPortion( nIndex
) )
1866 rValue
.Value
<<= GetCursorShell()->GetViewOptions()->GetFieldShadingsColor();
1867 uno::Any
&anyChar
= rValue
.Value
;
1869 anyChar
>>= charColor
;
1871 if( COL_AUTO
== charColor
)
1873 uno::Reference
<XAccessibleComponent
> xComponent(this);
1874 if (xComponent
.is())
1876 Color
cr(ColorTransparency
, xComponent
->getBackground());
1877 sal_uInt32 crChar
= sal_uInt32(cr
.IsDark() ? COL_WHITE
: COL_BLACK
);
1878 rValue
.Value
<<= crChar
;
1885 if (rValue
.Name
== UNO_NAME_CHAR_UNDERLINE_COLOR
)
1887 uno::Any
&anyChar
= rValue
.Value
;
1888 Color underlineColor
;
1889 anyChar
>>= underlineColor
;
1890 if ( COL_AUTO
== underlineColor
)
1892 uno::Reference
<XAccessibleComponent
> xComponent(this);
1893 if (xComponent
.is())
1895 Color
cr(ColorTransparency
, xComponent
->getBackground());
1896 underlineColor
= cr
.IsDark() ? COL_WHITE
: COL_BLACK
;
1897 rValue
.Value
<<= underlineColor
;
1905 if (rValue
.Name
== UNO_NAME_TABSTOPS
)
1907 css::uno::Sequence
< css::style::TabStop
> tabs
= GetCurrentTabStop( nIndex
);
1908 if( !tabs
.hasElements() )
1910 css::style::TabStop ts
;
1911 css::awt::Rectangle rc0
= getCharacterBounds(0);
1912 css::awt::Rectangle rc1
= getCharacterBounds(nIndex
);
1913 if( rc1
.X
- rc0
.X
>= 48 )
1914 ts
.Position
= (rc1
.X
- rc0
.X
) - (rc1
.X
- rc0
.X
- 48)% 47 + 47;
1917 ts
.DecimalChar
= ' ';
1919 ts
.Alignment
= css::style::TabAlign_LEFT
;
1922 rValue
.Value
<<= tabs
;
1926 //footnote & endnote
1927 if (rValue
.Name
== UNO_NAME_CHAR_ESCAPEMENT
)
1929 if ( GetPortionData().IsIndexInFootnode(nIndex
) )
1931 rValue
.Value
<<= sal_Int32(101);
1938 awt::Rectangle
SwAccessibleParagraph::getCharacterBounds(
1941 SolarMutexGuard aGuard
;
1945 // #i12332# The position after the string needs special treatment.
1946 // IsValidChar -> IsValidPosition
1947 if( ! (IsValidPosition( nIndex
, GetString().getLength() ) ) )
1948 throw lang::IndexOutOfBoundsException();
1951 bool bBehindText
= false;
1952 if ( nIndex
== GetString().getLength() )
1955 // get model position & prepare GetCharRect() arguments
1956 SwCursorMoveState aMoveState
;
1957 aMoveState
.m_bRealHeight
= true;
1958 aMoveState
.m_bRealWidth
= true;
1959 SwSpecialPos aSpecialPos
;
1960 const SwTextFrame
* const pFrame
= GetTextFrame();
1962 /** #i12332# FillSpecialPos does not accept nIndex ==
1963 GetString().getLength(). In that case nPos is set to the
1964 length of the string in the core. This way GetCharRect
1965 returns the rectangle for a cursor at the end of the
1967 const TextFrameIndex nPos
= bBehindText
1968 ? TextFrameIndex(pFrame
->GetText().getLength())
1969 : GetPortionData().FillSpecialPos(nIndex
, aSpecialPos
, aMoveState
.m_pSpecialPos
);
1973 SwPosition
aPosition(pFrame
->MapViewToModelPos(nPos
));
1974 GetFrame()->GetCharRect( aCoreRect
, aPosition
, &aMoveState
);
1976 // translate core coordinates into accessibility coordinates
1977 vcl::Window
*pWin
= GetWindow();
1980 throw uno::RuntimeException(u
"no Window"_ustr
, getXWeak());
1983 tools::Rectangle
aScreenRect( GetMap()->CoreToPixel( aCoreRect
));
1984 SwRect
aFrameLogBounds( GetBounds( *(GetMap()) ) ); // twip rel to doc root
1986 Point
aFramePixPos( GetMap()->CoreToPixel( aFrameLogBounds
).TopLeft() );
1987 aScreenRect
.Move( -aFramePixPos
.getX(), -aFramePixPos
.getY() );
1989 // convert into AWT Rectangle
1990 return awt::Rectangle(
1991 aScreenRect
.Left(), aScreenRect
.Top(),
1992 aScreenRect
.GetWidth(), aScreenRect
.GetHeight() );
1995 sal_Int32
SwAccessibleParagraph::getCharacterCount()
1997 SolarMutexGuard aGuard
;
2001 return GetString().getLength();
2004 sal_Int32
SwAccessibleParagraph::getIndexAtPoint( const awt::Point
& rPoint
)
2006 SolarMutexGuard aGuard
;
2010 // construct Point (translate into layout coordinates)
2011 vcl::Window
*pWin
= GetWindow();
2014 throw uno::RuntimeException(u
"no Window"_ustr
, getXWeak());
2016 Point
aPoint( rPoint
.X
, rPoint
.Y
);
2017 SwRect
aLogBounds( GetBounds( *(GetMap()), GetFrame() ) ); // twip rel to doc root
2018 Point
aPixPos( GetMap()->CoreToPixel( aLogBounds
).TopLeft() );
2019 aPoint
.setX(aPoint
.getX() + aPixPos
.getX());
2020 aPoint
.setY(aPoint
.getY() + aPixPos
.getY());
2021 Point
aCorePoint( GetMap()->PixelToCore( aPoint
) );
2022 if( !aLogBounds
.Contains( aCorePoint
) )
2024 // #i12332# rPoint is may also be in rectangle returned by
2025 // getCharacterBounds(getCharacterCount()
2027 awt::Rectangle aRectEndPos
=
2028 getCharacterBounds(getCharacterCount());
2030 if (rPoint
.X
- aRectEndPos
.X
>= 0 &&
2031 rPoint
.X
- aRectEndPos
.X
< aRectEndPos
.Width
&&
2032 rPoint
.Y
- aRectEndPos
.Y
>= 0 &&
2033 rPoint
.Y
- aRectEndPos
.Y
< aRectEndPos
.Height
)
2034 return getCharacterCount();
2039 // ask core for position
2040 OSL_ENSURE( GetFrame() != nullptr, "The text frame has vanished!" );
2041 OSL_ENSURE( GetFrame()->IsTextFrame(), "The text frame has mutated!" );
2042 const SwTextFrame
* pFrame
= GetTextFrame();
2043 // construct SwPosition (where GetModelPositionForViewPoint() will put the result into)
2044 SwTextNode
* pNode
= const_cast<SwTextNode
*>(pFrame
->GetTextNodeFirst());
2045 SwPosition
aPos(*pNode
, 0);
2046 SwCursorMoveState aMoveState
;
2047 aMoveState
.m_bPosMatchesBounds
= true;
2048 const bool bSuccess
= pFrame
->GetModelPositionForViewPoint( &aPos
, aCorePoint
, &aMoveState
);
2050 TextFrameIndex nIndex
= pFrame
->MapModelToViewPos(aPos
);
2051 if (TextFrameIndex(0) < nIndex
)
2055 pFrame
->GetCharRect( aResultRect
, aPos
);
2056 bool bVert
= pFrame
->IsVertical();
2057 bool bR2L
= pFrame
->IsRightToLeft();
2059 if ( (!bVert
&& aResultRect
.Pos().getX() > aCorePoint
.getX()) ||
2060 ( bVert
&& aResultRect
.Pos().getY() > aCorePoint
.getY()) ||
2061 ( bR2L
&& aResultRect
.Right() < aCorePoint
.getX()) )
2063 SwPosition
aPosPrev(pFrame
->MapViewToModelPos(nIndex
- TextFrameIndex(1)));
2064 SwRect aResultRectPrev
;
2065 pFrame
->GetCharRect( aResultRectPrev
, aPosPrev
);
2066 if ( (!bVert
&& aResultRectPrev
.Pos().getX() < aCorePoint
.getX() && aResultRect
.Pos().getY() == aResultRectPrev
.Pos().getY()) ||
2067 ( bVert
&& aResultRectPrev
.Pos().getY() < aCorePoint
.getY() && aResultRect
.Pos().getX() == aResultRectPrev
.Pos().getX()) ||
2068 ( bR2L
&& aResultRectPrev
.Right() > aCorePoint
.getX() && aResultRect
.Pos().getY() == aResultRectPrev
.Pos().getY()) )
2076 ? GetPortionData().GetAccessiblePosition(nIndex
)
2080 OUString
SwAccessibleParagraph::getSelectedText()
2082 SolarMutexGuard aGuard
;
2086 sal_Int32 nStart
, nEnd
;
2087 bool bSelected
= GetSelection( nStart
, nEnd
);
2089 ? GetString().copy( nStart
, nEnd
- nStart
)
2093 sal_Int32
SwAccessibleParagraph::getSelectionStart()
2095 SolarMutexGuard aGuard
;
2099 sal_Int32 nStart
, nEnd
;
2100 GetSelection( nStart
, nEnd
);
2104 sal_Int32
SwAccessibleParagraph::getSelectionEnd()
2106 SolarMutexGuard aGuard
;
2110 sal_Int32 nStart
, nEnd
;
2111 GetSelection( nStart
, nEnd
);
2115 sal_Bool
SwAccessibleParagraph::setSelection( sal_Int32 nStartIndex
, sal_Int32 nEndIndex
)
2117 SolarMutexGuard aGuard
;
2121 // parameter checking
2122 sal_Int32 nLength
= GetString().getLength();
2123 if ( ! IsValidRange( nStartIndex
, nEndIndex
, nLength
) )
2125 throw lang::IndexOutOfBoundsException();
2131 SwCursorShell
* pCursorShell
= GetCursorShell();
2132 if( pCursorShell
!= nullptr )
2134 // create pam for selection
2135 const SwTextFrame
* const pFrame
= GetTextFrame();
2136 TextFrameIndex
const nStart(GetPortionData().GetCoreViewPosition(nStartIndex
));
2137 TextFrameIndex
const nEnd(GetPortionData().GetCoreViewPosition(nEndIndex
));
2138 SwPaM
aPaM(pFrame
->MapViewToModelPos(nStart
));
2140 *aPaM
.GetPoint() = pFrame
->MapViewToModelPos(nEnd
);
2142 // set PaM at cursor shell
2143 bRet
= Select( aPaM
);
2149 OUString
SwAccessibleParagraph::getText()
2151 SolarMutexGuard aGuard
;
2158 OUString
SwAccessibleParagraph::getTextRange(
2159 sal_Int32 nStartIndex
, sal_Int32 nEndIndex
)
2161 SolarMutexGuard aGuard
;
2165 OUString
sText( GetString() );
2167 if ( !IsValidRange( nStartIndex
, nEndIndex
, sText
.getLength() ) )
2168 throw lang::IndexOutOfBoundsException();
2170 OrderRange( nStartIndex
, nEndIndex
);
2171 return sText
.copy(nStartIndex
, nEndIndex
-nStartIndex
);
2174 /*accessibility::*/TextSegment
SwAccessibleParagraph::getTextAtIndex( sal_Int32 nIndex
, sal_Int16 nTextType
)
2176 SolarMutexGuard aGuard
;
2180 /*accessibility::*/TextSegment aResult
;
2181 aResult
.SegmentStart
= -1;
2182 aResult
.SegmentEnd
= -1;
2184 const OUString rText
= GetString();
2185 // implement the silly specification that first position after
2186 // text must return an empty string, rather than throwing an
2187 // IndexOutOfBoundsException, except for LINE, where the last
2189 if( nIndex
== rText
.getLength() && AccessibleTextType::LINE
!= nTextType
)
2192 // with error checking
2193 i18n::Boundary aBound
;
2194 bool bWord
= GetTextBoundary( aBound
, rText
, nIndex
, nTextType
);
2196 OSL_ENSURE( aBound
.startPos
>= 0, "illegal boundary" );
2197 OSL_ENSURE( aBound
.startPos
<= aBound
.endPos
, "illegal boundary" );
2199 // return word (if present)
2202 aResult
.SegmentText
= rText
.copy( aBound
.startPos
, aBound
.endPos
- aBound
.startPos
);
2203 aResult
.SegmentStart
= aBound
.startPos
;
2204 aResult
.SegmentEnd
= aBound
.endPos
;
2210 /*accessibility::*/TextSegment
SwAccessibleParagraph::getTextBeforeIndex( sal_Int32 nIndex
, sal_Int16 nTextType
)
2212 SolarMutexGuard aGuard
;
2216 const OUString rText
= GetString();
2218 /*accessibility::*/TextSegment aResult
;
2219 aResult
.SegmentStart
= -1;
2220 aResult
.SegmentEnd
= -1;
2221 //If nIndex = 0, then nobefore text so return -1 directly.
2224 //Tab will be return when call WORDTYPE
2227 i18n::Boundary aBound
;
2228 if (nIndex
== rText
.getLength())
2229 aBound
.startPos
= aBound
.endPos
= nIndex
;
2232 bool bTmp
= GetTextBoundary( aBound
, rText
, nIndex
, nTextType
);
2235 aBound
.startPos
= aBound
.endPos
= nIndex
;
2238 // now skip to previous word
2239 if (nTextType
== AccessibleTextType::WORD
|| nTextType
== AccessibleTextType::SENTENCE
)
2241 i18n::Boundary preBound
= aBound
;
2242 while(preBound
.startPos
==aBound
.startPos
&& nIndex
> 0)
2244 nIndex
= min(nIndex
, preBound
.startPos
);
2245 if (nIndex
<= 0) break;
2246 rText
.iterateCodePoints(&nIndex
, -1);
2247 GetTextBoundary( preBound
, rText
, nIndex
, nTextType
);
2251 //Tab will be return when call WORDTYPE
2253 aResult
.SegmentText
= rText
.copy( preBound
.startPos
, preBound
.endPos
- preBound
.startPos
);
2254 aResult
.SegmentStart
= preBound
.startPos
;
2255 aResult
.SegmentEnd
= preBound
.endPos
;
2263 nIndex
= min(nIndex
, aBound
.startPos
);
2266 rText
.iterateCodePoints(&nIndex
, -1);
2267 bWord
= GetTextBoundary( aBound
, rText
, nIndex
, nTextType
);
2270 break; // exit if beginning of string is reached
2273 if (bWord
&& nIndex
<rText
.getLength())
2275 aResult
.SegmentText
= rText
.copy( aBound
.startPos
, aBound
.endPos
- aBound
.startPos
);
2276 aResult
.SegmentStart
= aBound
.startPos
;
2277 aResult
.SegmentEnd
= aBound
.endPos
;
2283 /*accessibility::*/TextSegment
SwAccessibleParagraph::getTextBehindIndex( sal_Int32 nIndex
, sal_Int16 nTextType
)
2285 SolarMutexGuard aGuard
;
2289 /*accessibility::*/TextSegment aResult
;
2290 aResult
.SegmentStart
= -1;
2291 aResult
.SegmentEnd
= -1;
2292 const OUString rText
= GetString();
2294 // implement the silly specification that first position after
2295 // text must return an empty string, rather than throwing an
2296 // IndexOutOfBoundsException
2297 if( nIndex
== rText
.getLength() )
2300 // get first word, then skip to next word
2301 i18n::Boundary aBound
;
2302 GetTextBoundary( aBound
, rText
, nIndex
, nTextType
);
2306 nIndex
= max( sal_Int32(nIndex
+1), aBound
.endPos
);
2307 if( nIndex
< rText
.getLength() )
2308 bWord
= GetTextBoundary( aBound
, rText
, nIndex
, nTextType
);
2310 break; // exit if end of string is reached
2315 aResult
.SegmentText
= rText
.copy( aBound
.startPos
, aBound
.endPos
- aBound
.startPos
);
2316 aResult
.SegmentStart
= aBound
.startPos
;
2317 aResult
.SegmentEnd
= aBound
.endPos
;
2321 sal_Bool bWord = sal_False;
2322 bWord = GetTextBoundary( aBound, rText, nIndex, nTextType );
2324 if (nTextType == AccessibleTextType::WORD)
2326 Boundary nexBound=aBound;
2328 // real current word
2329 if( nIndex <= aBound.endPos && nIndex >= aBound.startPos )
2331 while(nexBound.endPos==aBound.endPos&&nIndex<rText.getLength())
2333 // nIndex = max( (sal_Int32)(nIndex), nexBound.endPos) + 1;
2334 nIndex = max( (sal_Int32)(nIndex), nexBound.endPos) ;
2335 const sal_Unicode* pStr = rText.getStr();
2338 if( pStr[nIndex] == sal_Unicode(' ') )
2341 if( nIndex < rText.getLength() )
2343 bWord = GetTextBoundary( nexBound, rText, nIndex, nTextType );
2348 if (bWord && nIndex<rText.getLength())
2350 aResult.SegmentText = rText.copy( nexBound.startPos, nexBound.endPos - nexBound.startPos );
2351 aResult.SegmentStart = nexBound.startPos;
2352 aResult.SegmentEnd = nexBound.endPos;
2361 nIndex = max( (sal_Int32)(nIndex+1), aBound.endPos );
2362 if( nIndex < rText.getLength() )
2364 bWord = GetTextBoundary( aBound, rText, nIndex, nTextType );
2367 break; // exit if end of string is reached
2369 if (bWord && nIndex<rText.getLength())
2371 aResult.SegmentText = rText.copy( aBound.startPos, aBound.endPos - aBound.startPos );
2372 aResult.SegmentStart = aBound.startPos;
2373 aResult.SegmentEnd = aBound.endPos;
2380 sal_Bool
SwAccessibleParagraph::copyText( sal_Int32 nStartIndex
, sal_Int32 nEndIndex
)
2382 SolarMutexGuard aGuard
;
2386 // select and copy (through dispatch mechanism)
2387 setSelection( nStartIndex
, nEndIndex
);
2388 ExecuteAtViewShell( SID_COPY
);
2392 sal_Bool
SwAccessibleParagraph::scrollSubstringTo( sal_Int32 nStartIndex
,
2393 sal_Int32 nEndIndex
, AccessibleScrollType aScrollType
)
2395 SolarMutexGuard aGuard
;
2399 // parameter checking
2400 sal_Int32 nLength
= GetString().getLength();
2401 if ( ! IsValidRange( nStartIndex
, nEndIndex
, nLength
) )
2402 throw lang::IndexOutOfBoundsException();
2404 vcl::Window
*pWin
= GetWindow();
2406 throw uno::RuntimeException(u
"no Window"_ustr
, getXWeak());
2408 /* Start and end character bounds, in pixels, relative to the paragraph */
2409 awt::Rectangle startR
, endR
;
2410 startR
= getCharacterBounds(nStartIndex
);
2411 endR
= getCharacterBounds(nEndIndex
);
2413 /* Adjust points to fit the bounding box of both bounds. */
2414 Point
sP(std::min(startR
.X
, endR
.X
), startR
.Y
);
2415 Point
eP(std::max(startR
.X
+ startR
.Width
, endR
.X
+ endR
.Width
), endR
.Y
+ endR
.Height
);
2417 /* Offset the values relative to the view shell frame */
2418 SwRect
aFrameLogBounds( GetBounds( *(GetMap()) ) ); // twip rel to doc root
2419 Point
aFramePixPos( GetMap()->CoreToPixel( aFrameLogBounds
).TopLeft() );
2423 Point
startPoint(GetMap()->PixelToCore(sP
));
2424 Point
endPoint(GetMap()->PixelToCore(eP
));
2426 switch (aScrollType
)
2429 case AccessibleScrollType_SCROLL_TOP_LEFT
:
2431 case AccessibleScrollType_SCROLL_BOTTOM_RIGHT
:
2433 case AccessibleScrollType_SCROLL_TOP_EDGE
:
2435 case AccessibleScrollType_SCROLL_BOTTOM_EDGE
:
2437 case AccessibleScrollType_SCROLL_LEFT_EDGE
:
2439 case AccessibleScrollType_SCROLL_RIGHT_EDGE
:
2442 case AccessibleScrollType_SCROLL_ANYWHERE
:
2448 const SwRect
aRect(startPoint
, endPoint
);
2449 SwViewShell
* pViewShell
= GetMap()->GetShell();
2450 OSL_ENSURE( pViewShell
!= nullptr, "View shell expected!" );
2452 ScrollMDI(pViewShell
, aRect
, USHRT_MAX
, USHRT_MAX
);
2457 // XAccessibleEditableText
2459 sal_Bool
SwAccessibleParagraph::cutText( sal_Int32 nStartIndex
, sal_Int32 nEndIndex
)
2461 SolarMutexGuard aGuard
;
2465 if( !IsEditableState() )
2468 // select and cut (through dispatch mechanism)
2469 setSelection( nStartIndex
, nEndIndex
);
2470 ExecuteAtViewShell( SID_CUT
);
2474 sal_Bool
SwAccessibleParagraph::pasteText( sal_Int32 nIndex
)
2476 SolarMutexGuard aGuard
;
2480 if( !IsEditableState() )
2483 // select and paste (through dispatch mechanism)
2484 setSelection( nIndex
, nIndex
);
2485 ExecuteAtViewShell( SID_PASTE
);
2489 sal_Bool
SwAccessibleParagraph::deleteText( sal_Int32 nStartIndex
, sal_Int32 nEndIndex
)
2491 return replaceText( nStartIndex
, nEndIndex
, OUString() );
2494 sal_Bool
SwAccessibleParagraph::insertText( const OUString
& sText
, sal_Int32 nIndex
)
2496 return replaceText( nIndex
, nIndex
, sText
);
2499 sal_Bool
SwAccessibleParagraph::replaceText(
2500 sal_Int32 nStartIndex
, sal_Int32 nEndIndex
,
2501 const OUString
& sReplacement
)
2503 SolarMutexGuard aGuard
;
2507 const OUString
& rText
= GetString();
2509 if( !IsValidRange( nStartIndex
, nEndIndex
, rText
.getLength() ) )
2510 throw lang::IndexOutOfBoundsException();
2512 if( !IsEditableState() )
2515 // translate positions
2516 TextFrameIndex nStart
;
2517 TextFrameIndex nEnd
;
2518 bool bSuccess
= GetPortionData().GetEditableRange(
2519 nStartIndex
, nEndIndex
, nStart
, nEnd
);
2521 // edit only if the range is editable
2524 const SwTextFrame
* const pFrame
= GetTextFrame();
2525 // create SwPosition for nStartIndex
2526 SwPosition
aStartPos(pFrame
->MapViewToModelPos(nStart
));
2528 // create SwPosition for nEndIndex
2529 SwPosition
aEndPos(pFrame
->MapViewToModelPos(nEnd
));
2531 // now create XTextRange as helper and set string
2532 const rtl::Reference
<SwXTextRange
> xRange(
2533 SwXTextRange::CreateXTextRange(
2534 const_cast<SwDoc
&>(pFrame
->GetDoc()), aStartPos
, &aEndPos
));
2535 xRange
->setString(sReplacement
);
2537 // delete portion data
2545 sal_Bool
SwAccessibleParagraph::setAttributes(
2546 sal_Int32 nStartIndex
,
2547 sal_Int32 nEndIndex
,
2548 const uno::Sequence
<PropertyValue
>& rAttributeSet
)
2550 SolarMutexGuard aGuard
;
2554 const OUString
& rText
= GetString();
2556 if( ! IsValidRange( nStartIndex
, nEndIndex
, rText
.getLength() ) )
2557 throw lang::IndexOutOfBoundsException();
2559 if( !IsEditableState() )
2562 // create a (dummy) text portion for the sole purpose of calling
2563 // setPropertyValue on it
2564 rtl::Reference
<SwXTextPortion
> xPortion
= CreateUnoPortion( nStartIndex
,
2567 // build sorted index array
2568 sal_Int32 nLength
= rAttributeSet
.getLength();
2569 const PropertyValue
* pPairs
= rAttributeSet
.getConstArray();
2570 std::vector
<sal_Int32
> aIndices(nLength
);
2571 std::iota(aIndices
.begin(), aIndices
.end(), 0);
2572 std::sort(aIndices
.begin(), aIndices
.end(), IndexCompare(pPairs
));
2574 // create sorted sequences according to index array
2575 uno::Sequence
< OUString
> aNames( nLength
);
2576 OUString
* pNames
= aNames
.getArray();
2577 uno::Sequence
< uno::Any
> aValues( nLength
);
2578 uno::Any
* pValues
= aValues
.getArray();
2579 for (sal_Int32 i
= 0; i
< nLength
; ++i
)
2581 const PropertyValue
& rVal
= pPairs
[aIndices
[i
]];
2582 pNames
[i
] = rVal
.Name
;
2583 pValues
[i
] = rVal
.Value
;
2587 // now set the values
2591 xPortion
->setPropertyValues( aNames
, aValues
);
2593 catch (const UnknownPropertyException
&)
2595 // error handling through return code!
2602 sal_Bool
SwAccessibleParagraph::setText( const OUString
& sText
)
2604 return replaceText(0, GetString().getLength(), sText
);
2607 // XAccessibleSelection
2609 void SwAccessibleParagraph::selectAccessibleChild(
2610 sal_Int64 nChildIndex
)
2614 m_aSelectionHelper
.selectAccessibleChild(nChildIndex
);
2617 sal_Bool
SwAccessibleParagraph::isAccessibleChildSelected(
2618 sal_Int64 nChildIndex
)
2622 return m_aSelectionHelper
.isAccessibleChildSelected(nChildIndex
);
2625 void SwAccessibleParagraph::clearAccessibleSelection( )
2630 void SwAccessibleParagraph::selectAllAccessibleChildren( )
2634 m_aSelectionHelper
.selectAllAccessibleChildren();
2637 sal_Int64
SwAccessibleParagraph::getSelectedAccessibleChildCount( )
2641 return m_aSelectionHelper
.getSelectedAccessibleChildCount();
2644 uno::Reference
<XAccessible
> SwAccessibleParagraph::getSelectedAccessibleChild(
2645 sal_Int64 nSelectedChildIndex
)
2649 return m_aSelectionHelper
.getSelectedAccessibleChild(nSelectedChildIndex
);
2652 // index has to be treated as global child index.
2653 void SwAccessibleParagraph::deselectAccessibleChild(
2654 sal_Int64 nChildIndex
)
2658 m_aSelectionHelper
.deselectAccessibleChild( nChildIndex
);
2661 // XAccessibleHypertext
2665 class SwHyperlinkIter_Impl
2667 SwTextFrame
const& m_rFrame
;
2668 sw::MergedAttrIter m_Iter
;
2669 TextFrameIndex m_nStart
;
2670 TextFrameIndex m_nEnd
;
2673 explicit SwHyperlinkIter_Impl(const SwTextFrame
& rTextFrame
);
2674 const SwTextAttr
*next(SwTextNode
const** ppNode
= nullptr);
2676 TextFrameIndex
startIdx() const { return m_nStart
; }
2677 TextFrameIndex
endIdx() const { return m_nEnd
; }
2682 SwHyperlinkIter_Impl::SwHyperlinkIter_Impl(const SwTextFrame
& rTextFrame
)
2683 : m_rFrame(rTextFrame
)
2684 , m_Iter(rTextFrame
)
2685 , m_nStart(rTextFrame
.GetOffset())
2687 const SwTextFrame
*const pFollFrame
= rTextFrame
.GetFollow();
2688 m_nEnd
= pFollFrame
? pFollFrame
->GetOffset() : TextFrameIndex(rTextFrame
.GetText().getLength());
2691 const SwTextAttr
*SwHyperlinkIter_Impl::next(SwTextNode
const** ppNode
)
2693 const SwTextAttr
*pAttr
= nullptr;
2699 SwTextNode
const* pNode(nullptr);
2700 while (SwTextAttr
const*const pHt
= m_Iter
.NextAttr(&pNode
))
2702 if (RES_TXTATR_INETFMT
== pHt
->Which())
2704 const TextFrameIndex
nHtStart(m_rFrame
.MapModelToView(pNode
, pHt
->GetStart()));
2705 const TextFrameIndex
nHtEnd(m_rFrame
.MapModelToView(pNode
, pHt
->GetAnyEnd()));
2706 if (nHtEnd
> nHtStart
&&
2707 ((nHtStart
>= m_nStart
&& nHtStart
< m_nEnd
) ||
2708 (nHtEnd
> m_nStart
&& nHtEnd
<= m_nEnd
)))
2723 sal_Int32 SAL_CALL
SwAccessibleParagraph::getHyperLinkCount()
2725 SolarMutexGuard aGuard
;
2729 sal_Int32 nCount
= 0;
2730 // #i77108# - provide hyperlinks also in editable documents.
2732 const SwTextFrame
* pTextFrame
= GetTextFrame();
2733 SwHyperlinkIter_Impl
aIter(*pTextFrame
);
2734 while( aIter
.next() )
2740 uno::Reference
< XAccessibleHyperlink
> SAL_CALL
2741 SwAccessibleParagraph::getHyperLink( sal_Int32 nLinkIndex
)
2743 SolarMutexGuard aGuard
;
2747 const SwTextFrame
* pTextFrame
= GetTextFrame();
2748 SwHyperlinkIter_Impl
aHIter(*pTextFrame
);
2749 SwTextNode
const* pNode(nullptr);
2750 const SwTextAttr
* pHt
= aHIter
.next(&pNode
);
2751 for (sal_Int32 nTIndex
= 0; pHt
&& nTIndex
< nLinkIndex
; ++nTIndex
)
2752 pHt
= aHIter
.next(&pNode
);
2755 throw lang::IndexOutOfBoundsException();
2757 rtl::Reference
<SwAccessibleHyperlink
> xRet
;
2758 if (!m_pHyperTextData
)
2759 m_pHyperTextData
.reset( new SwAccessibleHyperTextData
);
2760 SwAccessibleHyperTextData::iterator aIter
= m_pHyperTextData
->find(pHt
);
2761 if (aIter
!= m_pHyperTextData
->end())
2763 xRet
= (*aIter
).second
;
2767 TextFrameIndex
const nHintStart(pTextFrame
->MapModelToView(pNode
, pHt
->GetStart()));
2768 TextFrameIndex
const nHintEnd(pTextFrame
->MapModelToView(pNode
, pHt
->GetAnyEnd()));
2769 const sal_Int32 nTmpHStt
= GetPortionData().GetAccessiblePosition(
2770 max(aHIter
.startIdx(), nHintStart
));
2771 const sal_Int32 nTmpHEnd
= GetPortionData().GetAccessiblePosition(
2772 min(aHIter
.endIdx(), nHintEnd
));
2773 xRet
= new SwAccessibleHyperlink(*pHt
,
2774 *this, nTmpHStt
, nTmpHEnd
);
2775 if (aIter
!= m_pHyperTextData
->end())
2777 (*aIter
).second
= xRet
.get();
2781 m_pHyperTextData
->emplace( pHt
, xRet
);
2787 sal_Int32 SAL_CALL
SwAccessibleParagraph::getHyperLinkIndex( sal_Int32 nCharIndex
)
2789 SolarMutexGuard aGuard
;
2793 // parameter checking
2794 sal_Int32 nLength
= GetString().getLength();
2795 if ( ! IsValidPosition( nCharIndex
, nLength
) )
2797 throw lang::IndexOutOfBoundsException();
2800 sal_Int32 nRet
= -1;
2803 const SwTextFrame
* pTextFrame
= GetTextFrame();
2804 SwHyperlinkIter_Impl
aHIter(*pTextFrame
);
2806 const TextFrameIndex nIdx
= GetPortionData().GetCoreViewPosition(nCharIndex
);
2808 SwTextNode
const* pNode(nullptr);
2809 const SwTextAttr
*pHt
= aHIter
.next(&pNode
);
2810 while (pHt
&& (nIdx
< pTextFrame
->MapModelToView(pNode
, pHt
->GetStart())
2811 || nIdx
>= pTextFrame
->MapModelToView(pNode
, pHt
->GetAnyEnd())))
2813 pHt
= aHIter
.next(&pNode
);
2822 throw lang::IndexOutOfBoundsException();
2826 // #i71360#, #i108125# - adjustments for change tracking text markup
2827 sal_Int32 SAL_CALL
SwAccessibleParagraph::getTextMarkupCount( sal_Int32 nTextMarkupType
)
2831 std::unique_ptr
<SwTextMarkupHelper
> pTextMarkupHelper
;
2832 switch ( nTextMarkupType
)
2834 case text::TextMarkupType::TRACK_CHANGE_INSERTION
:
2835 case text::TextMarkupType::TRACK_CHANGE_DELETION
:
2836 case text::TextMarkupType::TRACK_CHANGE_FORMATCHANGE
:
2838 pTextMarkupHelper
.reset( new SwTextMarkupHelper(
2840 *(mpParaChangeTrackInfo
->getChangeTrackingTextMarkupList( nTextMarkupType
) )) );
2845 const SwTextFrame
* const pFrame
= GetTextFrame();
2846 pTextMarkupHelper
.reset(new SwTextMarkupHelper(GetPortionData(), *pFrame
));
2850 return pTextMarkupHelper
->getTextMarkupCount( nTextMarkupType
);
2853 //MSAA Extension Implementation in app module
2854 sal_Bool SAL_CALL
SwAccessibleParagraph::scrollToPosition( const css::awt::Point
&, sal_Bool
)
2859 sal_Int32 SAL_CALL
SwAccessibleParagraph::getSelectedPortionCount( )
2863 sal_Int32 nSelected
= 0;
2864 SwPaM
* pCursor
= GetCursor( true );
2865 if( pCursor
!= nullptr )
2867 // get SwPosition for my node
2868 const SwTextFrame
* const pFrame
= GetTextFrame();
2869 SwNodeOffset
nFirstNode(pFrame
->GetTextNodeFirst()->GetIndex());
2870 SwNodeOffset nLastNode
;
2871 if (sw::MergedPara
const*const pMerged
= pFrame
->GetMergedPara())
2873 nLastNode
= pMerged
->pLastNode
->GetIndex();
2877 nLastNode
= nFirstNode
;
2880 // iterate over ring
2881 for(SwPaM
& rTmpCursor
: pCursor
->GetRingContainer())
2883 // ignore, if no mark
2884 if( rTmpCursor
.HasMark() )
2886 // check whether frame's node(s) are 'inside' pCursor
2887 SwPosition
* pStart
= rTmpCursor
.Start();
2888 SwNodeOffset nStartIndex
= pStart
->GetNodeIndex();
2889 SwPosition
* pEnd
= rTmpCursor
.End();
2890 SwNodeOffset nEndIndex
= pEnd
->GetNodeIndex();
2891 if ((nStartIndex
<= nLastNode
) && (nFirstNode
<= nEndIndex
))
2895 // else: this PaM doesn't point to this paragraph
2897 // else: this PaM is collapsed and doesn't select anything
2904 sal_Int32 SAL_CALL
SwAccessibleParagraph::getSeletedPositionStart( sal_Int32 nSelectedPortionIndex
)
2906 SolarMutexGuard aGuard
;
2910 sal_Int32 nStart
=-1, nEnd
=-1;
2911 /*sal_Bool bSelected = */GetSelectionAtIndex(&nSelectedPortionIndex
, nStart
, nEnd
);
2915 sal_Int32 SAL_CALL
SwAccessibleParagraph::getSeletedPositionEnd( sal_Int32 nSelectedPortionIndex
)
2917 SolarMutexGuard aGuard
;
2921 sal_Int32 nStart
=-1, nEnd
=-1;
2922 /*sal_Bool bSelected = */GetSelectionAtIndex(&nSelectedPortionIndex
, nStart
, nEnd
);
2926 sal_Bool SAL_CALL
SwAccessibleParagraph::removeSelection( sal_Int32 selectionIndex
)
2930 if(selectionIndex
< 0) return false;
2932 sal_Int32 nSelected
= selectionIndex
;
2934 // get the selection, and test whether it affects our text node
2935 SwPaM
* pCursor
= GetCursor( true );
2937 if( pCursor
!= nullptr )
2941 // get SwPosition for my node
2942 const SwTextFrame
* const pFrame
= GetTextFrame();
2943 SwNodeOffset
nFirstNode(pFrame
->GetTextNodeFirst()->GetIndex());
2944 SwNodeOffset nLastNode
;
2945 if (sw::MergedPara
const*const pMerged
= pFrame
->GetMergedPara())
2947 nLastNode
= pMerged
->pLastNode
->GetIndex();
2951 nLastNode
= nFirstNode
;
2954 // iterate over ring
2955 SwPaM
* pRingStart
= pCursor
;
2958 // ignore, if no mark
2959 if( pCursor
->HasMark() )
2961 // check whether frame's node(s) are 'inside' pCursor
2962 SwPosition
* pStart
= pCursor
->Start();
2963 SwNodeOffset nStartIndex
= pStart
->GetNodeIndex();
2964 SwPosition
* pEnd
= pCursor
->End();
2965 SwNodeOffset nEndIndex
= pEnd
->GetNodeIndex();
2966 if ((nStartIndex
<= nLastNode
) && (nFirstNode
<= nEndIndex
))
2968 if( nSelected
== 0 )
2970 pCursor
->MoveTo(nullptr);
2980 // else: this PaM is collapsed and doesn't select anything
2982 pCursor
= pCursor
->GetNext();
2984 while( !bRet
&& (pCursor
!= pRingStart
) );
2989 sal_Int32 SAL_CALL
SwAccessibleParagraph::addSelection( sal_Int32
, sal_Int32 startOffset
, sal_Int32 endOffset
)
2991 SolarMutexGuard aGuard
;
2995 // parameter checking
2996 sal_Int32 nLength
= GetString().getLength();
2997 if ( ! IsValidRange( startOffset
, endOffset
, nLength
) )
2999 throw lang::IndexOutOfBoundsException();
3002 sal_Int32 nSelectedCount
= getSelectedPortionCount();
3003 for ( sal_Int32 i
= nSelectedCount
; i
>= 0 ; i
--)
3005 sal_Int32 nStart
, nEnd
;
3006 bool bSelected
= GetSelectionAtIndex(&i
, nStart
, nEnd
);
3011 if (( startOffset
>=nStart
&& startOffset
<=nEnd
) || //startOffset in a selection
3012 ( endOffset
>=nStart
&& endOffset
<=nEnd
) || //endOffset in a selection
3013 ( startOffset
<= nStart
&& endOffset
>=nEnd
) || //start and end include the old selection
3014 ( startOffset
>= nStart
&& endOffset
<=nEnd
) )
3022 if (( startOffset
>=nEnd
&& startOffset
<=nStart
) || //startOffset in a selection
3023 ( endOffset
>=nEnd
&& endOffset
<=nStart
) || //endOffset in a selection
3024 ( startOffset
<= nStart
&& endOffset
>=nEnd
) || //start and end include the old selection
3025 ( startOffset
>= nStart
&& endOffset
<=nEnd
) )
3036 SwCursorShell
* pCursorShell
= GetCursorShell();
3037 if( pCursorShell
!= nullptr )
3039 // create pam for selection
3040 pCursorShell
->StartAction();
3041 const SwTextFrame
* const pFrame
= GetTextFrame();
3042 SwPaM
* aPaM
= pCursorShell
->CreateCursor();
3044 *aPaM
->GetPoint() = pFrame
->MapViewToModelPos(GetPortionData().GetCoreViewPosition(startOffset
));
3045 *aPaM
->GetMark() = pFrame
->MapViewToModelPos(GetPortionData().GetCoreViewPosition(endOffset
));
3046 pCursorShell
->EndAction();
3052 /*accessibility::*/TextSegment SAL_CALL
3053 SwAccessibleParagraph::getTextMarkup( sal_Int32 nTextMarkupIndex
,
3054 sal_Int32 nTextMarkupType
)
3058 std::unique_ptr
<SwTextMarkupHelper
> pTextMarkupHelper
;
3059 switch ( nTextMarkupType
)
3061 case text::TextMarkupType::TRACK_CHANGE_INSERTION
:
3062 case text::TextMarkupType::TRACK_CHANGE_DELETION
:
3063 case text::TextMarkupType::TRACK_CHANGE_FORMATCHANGE
:
3065 pTextMarkupHelper
.reset( new SwTextMarkupHelper(
3067 *(mpParaChangeTrackInfo
->getChangeTrackingTextMarkupList( nTextMarkupType
) )) );
3072 const SwTextFrame
* const pFrame
= GetTextFrame();
3073 pTextMarkupHelper
.reset(new SwTextMarkupHelper(GetPortionData(), *pFrame
));
3077 return pTextMarkupHelper
->getTextMarkup( nTextMarkupIndex
, nTextMarkupType
);
3080 uno::Sequence
< /*accessibility::*/TextSegment
> SAL_CALL
3081 SwAccessibleParagraph::getTextMarkupAtIndex( sal_Int32 nCharIndex
,
3082 sal_Int32 nTextMarkupType
)
3086 // parameter checking
3087 const sal_Int32 nLength
= GetString().getLength();
3088 if ( ! IsValidPosition( nCharIndex
, nLength
) )
3090 throw lang::IndexOutOfBoundsException();
3093 std::unique_ptr
<SwTextMarkupHelper
> pTextMarkupHelper
;
3094 switch ( nTextMarkupType
)
3096 case text::TextMarkupType::TRACK_CHANGE_INSERTION
:
3097 case text::TextMarkupType::TRACK_CHANGE_DELETION
:
3098 case text::TextMarkupType::TRACK_CHANGE_FORMATCHANGE
:
3100 pTextMarkupHelper
.reset( new SwTextMarkupHelper(
3102 *(mpParaChangeTrackInfo
->getChangeTrackingTextMarkupList( nTextMarkupType
) )) );
3107 const SwTextFrame
* const pFrame
= GetTextFrame();
3108 pTextMarkupHelper
.reset(new SwTextMarkupHelper(GetPortionData(), *pFrame
));
3112 return pTextMarkupHelper
->getTextMarkupAtIndex( nCharIndex
, nTextMarkupType
);
3116 sal_Int32 SAL_CALL
SwAccessibleParagraph::getLineNumberAtIndex( sal_Int32 nIndex
)
3120 // parameter checking
3121 const sal_Int32 nLength
= GetString().getLength();
3122 if ( ! IsValidPosition( nIndex
, nLength
) )
3124 throw lang::IndexOutOfBoundsException();
3127 const sal_Int32 nLineNo
= GetPortionData().GetLineNo( nIndex
);
3131 /*accessibility::*/TextSegment SAL_CALL
3132 SwAccessibleParagraph::getTextAtLineNumber( sal_Int32 nLineNo
)
3136 // parameter checking
3138 nLineNo
>= GetPortionData().GetLineCount() )
3140 throw lang::IndexOutOfBoundsException();
3143 i18n::Boundary aLineBound
;
3144 GetPortionData().GetBoundaryOfLine( nLineNo
, aLineBound
);
3146 /*accessibility::*/TextSegment aTextAtLine
;
3147 const OUString rText
= GetString();
3148 aTextAtLine
.SegmentText
= rText
.copy( aLineBound
.startPos
,
3149 aLineBound
.endPos
- aLineBound
.startPos
);
3150 aTextAtLine
.SegmentStart
= aLineBound
.startPos
;
3151 aTextAtLine
.SegmentEnd
= aLineBound
.endPos
;
3156 /*accessibility::*/TextSegment SAL_CALL
SwAccessibleParagraph::getTextAtLineWithCaret()
3160 const sal_Int32 nLineNoOfCaret
= getNumberOfLineWithCaret();
3162 if ( nLineNoOfCaret
>= 0 &&
3163 nLineNoOfCaret
< GetPortionData().GetLineCount() )
3165 return getTextAtLineNumber( nLineNoOfCaret
);
3168 return /*accessibility::*/TextSegment();
3171 sal_Int32 SAL_CALL
SwAccessibleParagraph::getNumberOfLineWithCaret()
3175 const sal_Int32 nCaretPos
= getCaretPosition();
3176 const sal_Int32 nLength
= GetString().getLength();
3177 if ( !IsValidPosition( nCaretPos
, nLength
) )
3182 sal_Int32 nLineNo
= GetPortionData().GetLineNo( nCaretPos
);
3184 // special handling for cursor positioned at end of text line via End key
3185 if ( nCaretPos
!= 0 )
3187 i18n::Boundary aLineBound
;
3188 GetPortionData().GetBoundaryOfLine( nLineNo
, aLineBound
);
3189 if ( nCaretPos
== aLineBound
.startPos
)
3191 SwCursorShell
* pCursorShell
= SwAccessibleParagraph::GetCursorShell();
3192 if ( pCursorShell
!= nullptr )
3194 const awt::Rectangle aCharRect
= getCharacterBounds( nCaretPos
);
3196 const SwRect
& aCursorCoreRect
= pCursorShell
->GetCharRect();
3197 // translate core coordinates into accessibility coordinates
3198 vcl::Window
*pWin
= GetWindow();
3201 throw uno::RuntimeException(u
"no Window"_ustr
, getXWeak());
3204 tools::Rectangle
aScreenRect( GetMap()->CoreToPixel( aCursorCoreRect
));
3206 SwRect
aFrameLogBounds( GetBounds( *(GetMap()) ) ); // twip rel to doc root
3207 Point
aFramePixPos( GetMap()->CoreToPixel( aFrameLogBounds
).TopLeft() );
3208 aScreenRect
.Move( -aFramePixPos
.getX(), -aFramePixPos
.getY() );
3210 // convert into AWT Rectangle
3211 const awt::Rectangle
aCursorRect( aScreenRect
.Left(),
3213 aScreenRect
.GetWidth(),
3214 aScreenRect
.GetHeight() );
3216 if ( aCharRect
.X
!= aCursorRect
.X
||
3217 aCharRect
.Y
!= aCursorRect
.Y
)
3229 void SwAccessibleParagraph::Notify(SfxBroadcaster
&, const SfxHint
&)
3231 mpParaChangeTrackInfo
->reset();
3234 bool SwAccessibleParagraph::GetSelectionAtIndex(
3235 sal_Int32
* pSelection
, sal_Int32
& nStart
, sal_Int32
& nEnd
)
3237 if (pSelection
&& *pSelection
< 0) return false;
3243 // get the selection, and test whether it affects our text node
3244 SwPaM
* pCursor
= GetCursor( true );
3245 if( pCursor
!= nullptr )
3247 // get SwPosition for my node
3248 const SwTextFrame
* const pFrame
= GetTextFrame();
3249 SwNodeOffset
nFirstNode(pFrame
->GetTextNodeFirst()->GetIndex());
3250 SwNodeOffset nLastNode
;
3251 if (sw::MergedPara
const*const pMerged
= pFrame
->GetMergedPara())
3253 nLastNode
= pMerged
->pLastNode
->GetIndex();
3257 nLastNode
= nFirstNode
;
3260 // iterate over ring
3261 for(SwPaM
& rTmpCursor
: pCursor
->GetRingContainer())
3263 // ignore, if no mark
3264 if( rTmpCursor
.HasMark() )
3266 // check whether frame's node(s) are 'inside' pCursor
3267 SwPosition
* pStart
= rTmpCursor
.Start();
3268 SwNodeOffset nStartIndex
= pStart
->GetNodeIndex();
3269 SwPosition
* pEnd
= rTmpCursor
.End();
3270 SwNodeOffset nEndIndex
= pEnd
->GetNodeIndex();
3271 if ((nStartIndex
<= nLastNode
) && (nFirstNode
<= nEndIndex
))
3273 if (!pSelection
|| *pSelection
== 0)
3275 // translate start and end positions
3278 sal_Int32 nLocalStart
= -1;
3279 if (nStartIndex
< nFirstNode
)
3281 // selection starts in previous node:
3282 // then our local selection starts with the paragraph
3287 assert(FrameContainsNode(*pFrame
, nStartIndex
));
3289 // selection starts in this node:
3290 // then check whether it's before or inside our part of
3291 // the paragraph, and if so, get the proper position
3292 const TextFrameIndex nCoreStart
=
3293 pFrame
->MapModelToViewPos(*pStart
);
3295 GetPortionData().GetFirstValidCorePosition() )
3299 else if( nCoreStart
<=
3300 GetPortionData().GetLastValidCorePosition() )
3303 !GetPortionData().IsValidCorePosition(
3306 "problem determining valid core position");
3309 GetPortionData().GetAccessiblePosition(
3315 sal_Int32 nLocalEnd
= -1;
3316 if (nLastNode
< nEndIndex
)
3318 // selection ends in following node:
3319 // then our local selection extends to the end
3320 nLocalEnd
= GetPortionData().GetAccessibleString().
3325 assert(FrameContainsNode(*pFrame
, nEndIndex
));
3327 // selection ends in this node: then select everything
3328 // before our part of the node
3329 const TextFrameIndex nCoreEnd
=
3330 pFrame
->MapModelToViewPos(*pEnd
);
3332 GetPortionData().GetLastValidCorePosition() )
3334 // selection extends beyond out part of this para
3335 nLocalEnd
= GetPortionData().GetAccessibleString().
3338 else if( nCoreEnd
>=
3339 GetPortionData().GetFirstValidCorePosition() )
3341 // selection is inside our part of this para
3343 !GetPortionData().IsValidCorePosition(
3346 "problem determining valid core position");
3348 nLocalEnd
= GetPortionData().GetAccessiblePosition(
3353 if( ( nLocalStart
!= -1 ) && ( nLocalEnd
!= -1 ) )
3355 nStart
= nLocalStart
;
3359 } // if hit the index
3365 // else: this PaM doesn't point to this paragraph
3367 // else: this PaM is collapsed and doesn't select anything
3372 // else: nocursor -> no selection
3374 if (pSelection
&& bRet
)
3376 sal_Int32 nCaretPos
= GetCaretPos();
3377 if( nStart
== nCaretPos
)
3378 std::swap( nStart
, nEnd
);
3383 sal_Int16 SAL_CALL
SwAccessibleParagraph::getAccessibleRole()
3385 std::scoped_lock
aGuard( m_Mutex
);
3387 //Get the real heading level, Heading1 ~ Heading10
3388 if (m_nHeadingLevel
> 0)
3389 return AccessibleRole::HEADING
;
3390 if (m_bIsBlockQuote
)
3391 return AccessibleRole::BLOCK_QUOTE
;
3393 return AccessibleRole::PARAGRAPH
;
3396 //Get the real heading level, Heading1 ~ Heading10
3397 sal_Int32
SwAccessibleParagraph::GetRealHeadingLevel()
3399 uno::Reference
< css::beans::XPropertySet
> xPortion
= CreateUnoPortion( 0, 0 );
3400 uno::Any styleAny
= xPortion
->getPropertyValue( u
"ParaStyleName"_ustr
);
3402 if (styleAny
>>= sValue
)
3404 sal_Int32 length
= sValue
.getLength();
3405 if (length
== 9 || length
== 10)
3407 if (sValue
.startsWith("Heading"))
3409 std::u16string_view intStr
= sValue
.subView(8);
3410 sal_Int32 headingLevel
= o3tl::toInt32(intStr
);
3411 return headingLevel
;
3418 bool SwAccessibleParagraph::IsBlockQuote()
3420 uno::Reference
<css::beans::XPropertySet
> xPortion
= CreateUnoPortion(0, 0);
3421 uno::Any aStyleAny
= xPortion
->getPropertyValue(u
"ParaStyleName"_ustr
);
3423 if (aStyleAny
>>= sValue
)
3424 return sValue
== "Quotations";
3428 uno::Any SAL_CALL
SwAccessibleParagraph::getExtendedAttributes()
3432 OUString strHeading
;
3433 if (m_nHeadingLevel
>= 0)
3435 // report heading level using the "level" object attribute as specified in ARIA,
3436 // maps to attributes of the same name for AT-SPI, IAccessible2, UIA
3437 // https://www.w3.org/TR/core-aam-1.2/#ariaLevelHeading
3438 strHeading
= "level:" + OUString::number(m_nHeadingLevel
) + ";";
3441 return uno::Any(strHeading
);
3444 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */