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
;
100 using namespace ::com::sun::star::container
;
102 using beans::PropertyValue
;
103 using beans::UnknownPropertyException
;
104 using beans::PropertyState_DIRECT_VALUE
;
110 namespace com::sun::star::text
{
114 constexpr OUStringLiteral sServiceName
= u
"com.sun.star.text.AccessibleParagraphView";
115 constexpr OUStringLiteral sImplementationName
= u
"com.sun.star.comp.Writer.SwAccessibleParagraphView";
117 OUString
const & SwAccessibleParagraph::GetString()
119 return GetPortionData().GetAccessibleString();
122 OUString
SwAccessibleParagraph::GetDescription()
124 return OUString(); // provide empty description for paragraphs
127 sal_Int32
SwAccessibleParagraph::GetCaretPos()
131 // get the selection's point, and test whether it's in our node
132 // #i27301# - consider adjusted method signature
133 SwPaM
* pCaret
= GetCursor( false ); // caret is first PaM in PaM-ring
135 if( pCaret
!= nullptr )
137 SwTextFrame
const*const pTextFrame(static_cast<SwTextFrame
const*>(GetFrame()));
140 // check whether the point points into 'our' node
141 SwPosition
* pPoint
= pCaret
->GetPoint();
142 if (sw::FrameContainsNode(*pTextFrame
, pPoint
->GetNodeIndex()))
144 // same node? Then check whether it's also within 'our' part
146 const TextFrameIndex nIndex
= pTextFrame
->MapModelToViewPos(*pPoint
);
147 if(!GetPortionData().IsValidCorePosition( nIndex
) ||
148 (GetPortionData().IsZeroCorePositionData()
149 && nIndex
== TextFrameIndex(0)))
151 bool bFormat
= pTextFrame
->HasPara();
158 if( GetPortionData().IsValidCorePosition( nIndex
) )
161 // consider that cursor/caret is in front of the list label
162 if ( pCaret
->IsInFrontOfLabel() )
168 nRet
= GetPortionData().GetAccessiblePosition( nIndex
);
171 OSL_ENSURE( nRet
>= 0, "invalid cursor?" );
172 OSL_ENSURE( nRet
<= GetPortionData().GetAccessibleString().
173 getLength(), "invalid cursor?" );
175 // else: in this paragraph, but in different frame
177 // else: not in this paragraph
179 // else: no cursor -> no caret
184 // #i27301# - new parameter <_bForSelection>
185 SwPaM
* SwAccessibleParagraph::GetCursor( const bool _bForSelection
)
187 // get the cursor shell; if we don't have any, we don't have a
188 // cursor/selection either
189 SwPaM
* pCursor
= nullptr;
190 SwCursorShell
* pCursorShell
= SwAccessibleParagraph::GetCursorShell();
191 // #i27301# - if cursor is retrieved for selection, the cursors for
192 // a table selection has to be returned.
193 if ( pCursorShell
!= nullptr &&
194 ( _bForSelection
|| !pCursorShell
->IsTableMode() ) )
196 SwFEShell
*pFESh
= dynamic_cast<SwFEShell
*>(pCursorShell
);
198 !(pFESh
->IsFrameSelected() || pFESh
->IsObjSelected() > 0) )
200 // get the selection, and test whether it affects our text node
201 pCursor
= pCursorShell
->GetCursor( false /* ??? */ );
208 bool SwAccessibleParagraph::IsHeading() const
210 SwTextFrame
const*const pFrame(static_cast<SwTextFrame
const*>(GetFrame()));
211 const SwTextNode
*pTextNd
= pFrame
->GetTextNodeForParaProps();
212 return pTextNd
->IsOutline();
215 void SwAccessibleParagraph::GetStates( sal_Int64
& rStateSet
)
217 SwAccessibleContext::GetStates( rStateSet
);
220 rStateSet
|= AccessibleStateType::MULTI_LINE
;
222 if (GetCursorShell())
225 rStateSet
|= AccessibleStateType::MULTI_SELECTABLE
;
227 rStateSet
|= AccessibleStateType::FOCUSABLE
;
230 // FOCUSED (simulates node index of cursor)
231 SwPaM
* pCaret
= GetCursor( false ); // #i27301# - consider adjusted method signature
232 SwTextFrame
const*const pFrame(static_cast<SwTextFrame
const*>(GetFrame()));
234 if (pCaret
!= nullptr &&
235 sw::FrameContainsNode(*pFrame
, pCaret
->GetPoint()->GetNodeIndex()) &&
236 m_nOldCaretPos
!= -1)
238 vcl::Window
*pWin
= GetWindow();
239 if( pWin
&& pWin
->HasFocus() )
240 rStateSet
|= AccessibleStateType::FOCUSED
;
241 ::rtl::Reference
< SwAccessibleContext
> xThis( this );
242 GetMap()->SetCursorContext( xThis
);
246 void SwAccessibleParagraph::InvalidateContent_( bool bVisibleDataFired
)
248 OUString
sOldText( GetString() );
252 const OUString sText
= GetString();
254 if( sText
!= sOldText
)
256 // The text is changed
257 AccessibleEventObject aEvent
;
258 aEvent
.EventId
= AccessibleEventId::TEXT_CHANGED
;
260 // determine exact changes between sOldText and sText
261 (void)comphelper::OCommonAccessibleText::implInitTextChangedEvent(sOldText
, sText
,
265 FireAccessibleEvent( aEvent
);
266 uno::Reference
< XAccessible
> xparent
= getAccessibleParent();
267 uno::Reference
< XAccessibleContext
> xAccContext(xparent
,uno::UNO_QUERY
);
268 if (xAccContext
.is() && xAccContext
->getAccessibleRole() == AccessibleRole::TABLE_CELL
)
270 SwAccessibleContext
* pPara
= static_cast< SwAccessibleContext
* >(xparent
.get());
273 AccessibleEventObject aParaEvent
;
274 aParaEvent
.EventId
= AccessibleEventId::VALUE_CHANGED
;
275 pPara
->FireAccessibleEvent(aParaEvent
);
279 else if( !bVisibleDataFired
)
281 FireVisibleDataEvent();
284 bool bNewIsHeading
= IsHeading();
285 //Get the real heading level, Heading1 ~ Heading10
286 m_nHeadingLevel
= GetRealHeadingLevel();
289 std::scoped_lock
aGuard( m_Mutex
);
290 bOldIsHeading
= m_bIsHeading
;
291 if( m_bIsHeading
!= bNewIsHeading
)
292 m_bIsHeading
= bNewIsHeading
;
295 if( bNewIsHeading
!= bOldIsHeading
)
297 // The role has changed
298 AccessibleEventObject aEvent
;
299 aEvent
.EventId
= AccessibleEventId::ROLE_CHANGED
;
301 FireAccessibleEvent( aEvent
);
304 if( sText
== sOldText
)
307 OUString
sNewDesc( GetDescription() );
310 std::scoped_lock
aGuard( m_Mutex
);
312 if( m_sDesc
!= sNewDesc
)
316 if( sNewDesc
!= sOldDesc
)
318 // The text is changed
319 AccessibleEventObject aEvent
;
320 aEvent
.EventId
= AccessibleEventId::DESCRIPTION_CHANGED
;
321 aEvent
.OldValue
<<= sOldDesc
;
322 aEvent
.NewValue
<<= sNewDesc
;
324 FireAccessibleEvent( aEvent
);
328 void SwAccessibleParagraph::InvalidateCursorPos_()
330 // The text is changed
331 sal_Int32 nNew
= GetCaretPos();
334 std::scoped_lock
aGuard( m_Mutex
);
335 nOld
= m_nOldCaretPos
;
336 m_nOldCaretPos
= nNew
;
340 // remember that object as the one that has the caret. This is
341 // necessary to notify that object if the cursor leaves it.
342 ::rtl::Reference
< SwAccessibleContext
> xThis( this );
343 GetMap()->SetCursorContext( xThis
);
346 vcl::Window
*pWin
= GetWindow();
350 // The cursor's node position is simulated by the focus!
351 if( pWin
&& pWin
->HasFocus() && -1 == nOld
)
352 FireStateChangedEvent( AccessibleStateType::FOCUSED
, true );
354 AccessibleEventObject aEvent
;
355 aEvent
.EventId
= AccessibleEventId::CARET_CHANGED
;
356 aEvent
.OldValue
<<= nOld
;
357 aEvent
.NewValue
<<= nNew
;
359 FireAccessibleEvent( aEvent
);
361 if( pWin
&& pWin
->HasFocus() && -1 == nNew
)
362 FireStateChangedEvent( AccessibleStateType::FOCUSED
, false );
363 //To send TEXT_SELECTION_CHANGED event
366 bool bCurSelection
= GetSelection(nStart
,nEnd
);
367 if(m_bLastHasSelection
|| bCurSelection
)
369 aEvent
.EventId
= AccessibleEventId::TEXT_SELECTION_CHANGED
;
370 aEvent
.OldValue
.clear();
371 aEvent
.NewValue
.clear();
372 FireAccessibleEvent(aEvent
);
374 m_bLastHasSelection
=bCurSelection
;
378 void SwAccessibleParagraph::InvalidateFocus_()
380 vcl::Window
*pWin
= GetWindow();
385 std::scoped_lock
aGuard( m_Mutex
);
386 nPos
= m_nOldCaretPos
;
388 OSL_ENSURE( nPos
!= -1, "focus object should be selected" );
390 FireStateChangedEvent( AccessibleStateType::FOCUSED
,
391 pWin
->HasFocus() && nPos
!= -1 );
395 SwAccessibleParagraph::SwAccessibleParagraph(
396 std::shared_ptr
<SwAccessibleMap
> const& pInitMap
,
397 const SwTextFrame
& rTextFrame
)
398 : SwAccessibleContext( pInitMap
, AccessibleRole::PARAGRAPH
, &rTextFrame
)
399 , m_nOldCaretPos( -1 )
400 , m_bIsHeading( false )
401 //Get the real heading level, Heading1 ~ Heading10
402 , m_nHeadingLevel (-1)
403 , m_aSelectionHelper( *this )
404 , mpParaChangeTrackInfo( new SwParaChangeTrackingInfo( rTextFrame
) ) // #i108125#
405 , m_bLastHasSelection(false) //To add TEXT_SELECTION_CHANGED event
407 StartListening(const_cast<SwTextFrame
&>(rTextFrame
));
408 m_bIsHeading
= IsHeading();
409 //Get the real heading level, Heading1 ~ Heading10
410 m_nHeadingLevel
= GetRealHeadingLevel();
411 SetName( OUString() ); // set an empty accessibility name for paragraphs
414 SwAccessibleParagraph::~SwAccessibleParagraph()
416 SolarMutexGuard aGuard
;
418 m_pPortionData
.reset();
419 m_pHyperTextData
.reset();
420 mpParaChangeTrackInfo
.reset(); // #i108125#
424 bool SwAccessibleParagraph::HasCursor()
426 std::scoped_lock
aGuard( m_Mutex
);
427 return m_nOldCaretPos
!= -1;
430 void SwAccessibleParagraph::UpdatePortionData()
432 // obtain the text frame
433 const SwTextFrame
* pFrame
= static_cast<const SwTextFrame
*>( GetFrame() );
434 OSL_ENSURE( pFrame
!= nullptr, "The text frame has vanished!" );
439 OSL_ENSURE( pFrame
->IsTextFrame(), "The text frame has mutated!" );
440 // build new portion data
441 m_pPortionData
.reset( new SwAccessiblePortionData(
442 pFrame
, GetMap()->GetShell()->GetViewOptions()) );
443 pFrame
->VisitPortions( *m_pPortionData
);
445 OSL_ENSURE( m_pPortionData
!= nullptr, "UpdatePortionData() failed" );
448 void SwAccessibleParagraph::ClearPortionData()
450 m_pPortionData
.reset();
451 m_pHyperTextData
.reset();
454 void SwAccessibleParagraph::ExecuteAtViewShell( sal_uInt16 nSlot
)
456 OSL_ENSURE( GetMap() != nullptr, "no map?" );
457 SwViewShell
* pViewShell
= GetMap()->GetShell();
459 OSL_ENSURE( pViewShell
!= nullptr, "View shell expected!" );
460 SfxViewShell
* pSfxShell
= pViewShell
->GetSfxViewShell();
462 OSL_ENSURE( pSfxShell
!= nullptr, "SfxViewShell shell expected!" );
466 SfxViewFrame
& rFrame
= pSfxShell
->GetViewFrame();
467 SfxDispatcher
*pDispatcher
= rFrame
.GetDispatcher();
468 OSL_ENSURE( pDispatcher
!= nullptr, "Dispatcher expected!" );
472 pDispatcher
->Execute( nSlot
);
475 rtl::Reference
<SwXTextPortion
> SwAccessibleParagraph::CreateUnoPortion(
476 sal_Int32 nStartIndex
,
477 sal_Int32 nEndIndex
)
479 OSL_ENSURE( (IsValidChar(nStartIndex
, GetString().getLength()) &&
480 (nEndIndex
== -1)) ||
481 IsValidRange(nStartIndex
, nEndIndex
, GetString().getLength()),
482 "please check parameters before calling this method" );
484 const TextFrameIndex nStart
= GetPortionData().GetCoreViewPosition(nStartIndex
);
485 const TextFrameIndex nEnd
= (nEndIndex
== -1)
486 ? (nStart
+ TextFrameIndex(1))
487 : GetPortionData().GetCoreViewPosition(nEndIndex
);
490 SwTextFrame
const*const pFrame(static_cast<SwTextFrame
const*>(GetFrame()));
491 SwPosition
aStartPos(pFrame
->MapViewToModelPos(nStart
));
492 auto pUnoCursor(const_cast<SwDoc
&>(pFrame
->GetDoc()).CreateUnoCursor(aStartPos
));
493 pUnoCursor
->SetMark();
494 *pUnoCursor
->GetMark() = pFrame
->MapViewToModelPos(nEnd
);
496 // create a (dummy) text portion to be returned
497 uno::Reference
<text::XText
> aEmpty
;
498 return new SwXTextPortion ( pUnoCursor
.get(), aEmpty
, PORTION_TEXT
);
501 // range checking for parameter
503 bool SwAccessibleParagraph::IsValidChar(
504 sal_Int32 nPos
, sal_Int32 nLength
)
506 return (nPos
>= 0) && (nPos
< nLength
);
509 bool SwAccessibleParagraph::IsValidPosition(
510 sal_Int32 nPos
, sal_Int32 nLength
)
512 return (nPos
>= 0) && (nPos
<= nLength
);
515 bool SwAccessibleParagraph::IsValidRange(
516 sal_Int32 nBegin
, sal_Int32 nEnd
, sal_Int32 nLength
)
518 return IsValidPosition(nBegin
, nLength
) && IsValidPosition(nEnd
, nLength
);
521 //the function is to check whether the position is in a redline range.
522 const SwRangeRedline
* SwAccessibleParagraph::GetRedlineAtIndex()
524 const SwRangeRedline
* pRedline
= nullptr;
525 SwPaM
* pCrSr
= GetCursor( true );
528 SwPosition
* pStart
= pCrSr
->Start();
529 pRedline
= pStart
->GetDoc().getIDocumentRedlineAccess().GetRedline(*pStart
, nullptr);
537 bool SwAccessibleParagraph::GetCharBoundary(
538 i18n::Boundary
& rBound
,
539 std::u16string_view text
,
542 if( GetPortionData().FillBoundaryIFDateField( rBound
, nPos
) )
546 o3tl::iterateCodePoints(text
, &nPosEnd
);
548 rBound
.startPos
= nPos
;
549 rBound
.endPos
= nPosEnd
;
554 bool SwAccessibleParagraph::GetWordBoundary(
555 i18n::Boundary
& rBound
,
556 const OUString
& rText
,
559 // now ask the Break-Iterator for the word
560 assert(g_pBreakIt
&& g_pBreakIt
->GetBreakIter().is());
562 // get locale for this position
563 SwTextFrame
const*const pFrame(static_cast<SwTextFrame
const*>(GetFrame()));
564 const TextFrameIndex nCorePos
= GetPortionData().GetCoreViewPosition(nPos
);
565 lang::Locale aLocale
= g_pBreakIt
->GetLocale(pFrame
->GetLangOfChar(nCorePos
, 0, true));
567 // which type of word are we interested in?
568 // (DICTIONARY_WORD includes punctuation, ANY_WORD doesn't.)
569 const sal_Int16 nWordType
= i18n::WordType::ANY_WORD
;
571 // get word boundary, as the Break-Iterator sees fit.
572 rBound
= g_pBreakIt
->GetBreakIter()->getWordBoundary(
573 rText
, nPos
, aLocale
, nWordType
, true );
578 bool SwAccessibleParagraph::GetSentenceBoundary(
579 i18n::Boundary
& rBound
,
580 const OUString
& rText
,
583 const sal_Unicode
* pStr
= rText
.getStr();
584 while( nPos
< rText
.getLength() && pStr
[nPos
] == u
' ' )
587 GetPortionData().GetSentenceBoundary( rBound
, nPos
);
591 bool SwAccessibleParagraph::GetLineBoundary(
592 i18n::Boundary
& rBound
,
593 std::u16string_view aText
,
596 if( sal_Int32(aText
.size()) == nPos
)
597 GetPortionData().GetLastLineBoundary( rBound
);
599 GetPortionData().GetLineBoundary( rBound
, nPos
);
603 bool SwAccessibleParagraph::GetParagraphBoundary(
604 i18n::Boundary
& rBound
,
605 std::u16string_view aText
)
608 rBound
.endPos
= aText
.size();
612 bool SwAccessibleParagraph::GetAttributeBoundary(
613 i18n::Boundary
& rBound
,
616 GetPortionData().GetAttributeBoundary( rBound
, nPos
);
620 bool SwAccessibleParagraph::GetGlyphBoundary(
621 i18n::Boundary
& rBound
,
622 const OUString
& rText
,
625 // ask the Break-Iterator for the glyph by moving one cell
626 // forward, and then one cell back
627 assert(g_pBreakIt
&& g_pBreakIt
->GetBreakIter().is());
629 // get locale for this position
630 SwTextFrame
const*const pFrame(static_cast<SwTextFrame
const*>(GetFrame()));
631 const TextFrameIndex nCorePos
= GetPortionData().GetCoreViewPosition(nPos
);
632 lang::Locale aLocale
= g_pBreakIt
->GetLocale(pFrame
->GetLangOfChar(nCorePos
, 0, true));
634 // get word boundary, as the Break-Iterator sees fit.
635 const sal_Int16 nIterMode
= i18n::CharacterIteratorMode::SKIPCELL
;
637 rBound
.endPos
= g_pBreakIt
->GetBreakIter()->nextCharacters(
638 rText
, nPos
, aLocale
, nIterMode
, 1, nDone
);
639 rBound
.startPos
= g_pBreakIt
->GetBreakIter()->previousCharacters(
640 rText
, rBound
.endPos
, aLocale
, nIterMode
, 1, nDone
);
641 bool bRet
= ((rBound
.startPos
<= nPos
) && (nPos
<= rBound
.endPos
));
642 OSL_ENSURE( rBound
.startPos
<= nPos
, "start pos too high" );
643 OSL_ENSURE( rBound
.endPos
>= nPos
, "end pos too low" );
648 bool SwAccessibleParagraph::GetTextBoundary(
649 i18n::Boundary
& rBound
,
650 const OUString
& rText
,
652 sal_Int16 nTextType
)
655 if( !( AccessibleTextType::LINE
== nTextType
656 ? IsValidPosition( nPos
, rText
.getLength() )
657 : IsValidChar( nPos
, rText
.getLength() ) ) )
658 throw lang::IndexOutOfBoundsException();
664 case AccessibleTextType::WORD
:
665 bRet
= GetWordBoundary(rBound
, rText
, nPos
);
668 case AccessibleTextType::SENTENCE
:
669 bRet
= GetSentenceBoundary( rBound
, rText
, nPos
);
672 case AccessibleTextType::PARAGRAPH
:
673 bRet
= GetParagraphBoundary( rBound
, rText
);
676 case AccessibleTextType::CHARACTER
:
677 bRet
= GetCharBoundary( rBound
, rText
, nPos
);
680 case AccessibleTextType::LINE
:
681 //Solve the problem of returning wrong LINE and PARAGRAPH
682 if((nPos
== rText
.getLength()) && nPos
> 0)
683 bRet
= GetLineBoundary( rBound
, rText
, nPos
- 1);
685 bRet
= GetLineBoundary( rBound
, rText
, nPos
);
688 case AccessibleTextType::ATTRIBUTE_RUN
:
689 bRet
= GetAttributeBoundary( rBound
, nPos
);
692 case AccessibleTextType::GLYPH
:
693 bRet
= GetGlyphBoundary( rBound
, rText
, nPos
);
697 throw lang::IllegalArgumentException( );
703 OUString SAL_CALL
SwAccessibleParagraph::getAccessibleDescription()
705 SolarMutexGuard aGuard
;
709 std::scoped_lock
aGuard2( m_Mutex
);
710 if( m_sDesc
.isEmpty() )
711 m_sDesc
= GetDescription();
716 lang::Locale SAL_CALL
SwAccessibleParagraph::getLocale()
718 SolarMutexGuard aGuard
;
720 const SwTextFrame
*pTextFrame
= GetFrame()->DynCastTextFrame();
723 throw uno::RuntimeException("no SwTextFrame", static_cast<cppu::OWeakObject
*>(this));
726 lang::Locale
aLoc(g_pBreakIt
->GetLocale(pTextFrame
->GetLangOfChar(TextFrameIndex(0), 0, true)));
731 // #i27138# - paragraphs are in relation CONTENT_FLOWS_FROM and/or CONTENT_FLOWS_TO
732 uno::Reference
<XAccessibleRelationSet
> SAL_CALL
SwAccessibleParagraph::getAccessibleRelationSet()
734 SolarMutexGuard aGuard
;
738 rtl::Reference
<utl::AccessibleRelationSetHelper
> pHelper
= new utl::AccessibleRelationSetHelper();
740 const SwTextFrame
* pTextFrame
= GetFrame()->DynCastTextFrame();
741 OSL_ENSURE( pTextFrame
,
742 "<SwAccessibleParagraph::getAccessibleRelationSet()> - missing text frame");
745 const SwContentFrame
* pPrevContentFrame( pTextFrame
->FindPrevCnt() );
746 if ( pPrevContentFrame
)
748 uno::Sequence
< uno::Reference
<XInterface
> > aSequence
{ GetMap()->GetContext( pPrevContentFrame
) };
749 AccessibleRelation
aAccRel( AccessibleRelationType::CONTENT_FLOWS_FROM
,
751 pHelper
->AddRelation( aAccRel
);
754 const SwContentFrame
* pNextContentFrame( pTextFrame
->FindNextCnt( true ) );
755 if ( pNextContentFrame
)
757 uno::Sequence
< uno::Reference
<XInterface
> > aSequence
{ GetMap()->GetContext( pNextContentFrame
) };
758 AccessibleRelation
aAccRel( AccessibleRelationType::CONTENT_FLOWS_TO
,
760 pHelper
->AddRelation( aAccRel
);
767 void SAL_CALL
SwAccessibleParagraph::grabFocus()
769 SolarMutexGuard aGuard
;
774 SwCursorShell
*pCursorSh
= GetCursorShell();
775 SwPaM
*pCursor
= GetCursor( false ); // #i27301# - consider new method signature
776 const SwTextFrame
*pTextFrame
= static_cast<const SwTextFrame
*>( GetFrame() );
778 if (pCursorSh
!= nullptr &&
779 ( pCursor
== nullptr ||
780 !sw::FrameContainsNode(*pTextFrame
, pCursor
->GetPoint()->GetNodeIndex()) ||
781 !pTextFrame
->IsInside(pTextFrame
->MapModelToViewPos(*pCursor
->GetPoint()))))
783 // create pam for selection
784 SwPosition
const aStartPos(pTextFrame
->MapViewToModelPos(pTextFrame
->GetOffset()));
785 SwPaM
aPaM( aStartPos
);
787 // set PaM at cursor shell
793 vcl::Window
* pWindow
= GetWindow();
795 if (pWindow
!= nullptr)
796 pWindow
->GrabFocus();
801 static bool lcl_GetBackgroundColor( Color
& rColor
,
802 const SwFrame
* pFrame
,
803 SwCursorShell
* pCursorSh
)
805 const SvxBrushItem
* pBackgroundBrush
= nullptr;
806 std::optional
<Color
> xSectionTOXColor
;
808 drawinglayer::attribute::SdrAllFillAttributesHelperPtr aFillAttributes
;
811 pFrame
->GetBackgroundBrush( aFillAttributes
, pBackgroundBrush
, xSectionTOXColor
, aDummyRect
, false, /*bConsiderTextBox=*/false ) )
813 if ( xSectionTOXColor
)
815 rColor
= *xSectionTOXColor
;
820 rColor
= pBackgroundBrush
->GetColor();
824 else if ( pCursorSh
)
826 rColor
= pCursorSh
->Imp()->GetRetoucheColor();
833 sal_Int32 SAL_CALL
SwAccessibleParagraph::getForeground()
837 Color aBackgroundCol
;
839 if ( lcl_GetBackgroundColor( aBackgroundCol
, GetFrame(), GetCursorShell() ) )
841 if ( aBackgroundCol
.IsDark() )
843 return sal_Int32(COL_WHITE
);
847 return sal_Int32(COL_BLACK
);
851 return SwAccessibleContext::getForeground();
854 sal_Int32 SAL_CALL
SwAccessibleParagraph::getBackground()
858 Color aBackgroundCol
;
860 if ( lcl_GetBackgroundColor( aBackgroundCol
, GetFrame(), GetCursorShell() ) )
862 return sal_Int32(aBackgroundCol
);
865 return SwAccessibleContext::getBackground();
868 OUString SAL_CALL
SwAccessibleParagraph::getImplementationName()
870 return sImplementationName
;
873 sal_Bool SAL_CALL
SwAccessibleParagraph::supportsService(
874 const OUString
& sTestServiceName
)
876 return cppu::supportsService(this, sTestServiceName
);
879 uno::Sequence
< OUString
> SAL_CALL
SwAccessibleParagraph::getSupportedServiceNames()
881 return { sServiceName
, sAccessibleServiceName
};
884 static uno::Sequence
< OUString
> const & getAttributeNames()
886 static uno::Sequence
< OUString
> const aNames
888 // Add the font name to attribute list
889 // sorted list of strings
890 UNO_NAME_CHAR_BACK_COLOR
,
892 UNO_NAME_CHAR_CONTOURED
,
893 UNO_NAME_CHAR_EMPHASIS
,
894 UNO_NAME_CHAR_ESCAPEMENT
,
895 UNO_NAME_CHAR_FONT_NAME
,
896 UNO_NAME_CHAR_HEIGHT
,
897 UNO_NAME_CHAR_POSTURE
,
898 UNO_NAME_CHAR_SHADOWED
,
899 UNO_NAME_CHAR_STRIKEOUT
,
900 UNO_NAME_CHAR_UNDERLINE
,
901 UNO_NAME_CHAR_UNDERLINE_COLOR
,
902 UNO_NAME_CHAR_WEIGHT
,
907 static uno::Sequence
< OUString
> const & getSupplementalAttributeNames()
909 static uno::Sequence
< OUString
> const aNames
911 // sorted list of strings
912 UNO_NAME_NUMBERING_LEVEL
,
913 UNO_NAME_NUMBERING_RULES
,
914 UNO_NAME_PARA_ADJUST
,
915 UNO_NAME_PARA_BOTTOM_MARGIN
,
916 UNO_NAME_PARA_FIRST_LINE_INDENT
,
917 UNO_NAME_PARA_LEFT_MARGIN
,
918 UNO_NAME_PARA_LINE_SPACING
,
919 UNO_NAME_PARA_RIGHT_MARGIN
,
927 uno::Any
SwAccessibleParagraph::queryInterface( const uno::Type
& rType
)
930 if ( rType
== cppu::UnoType
<XAccessibleText
>::get())
932 uno::Reference
<XAccessibleText
> aAccText
= static_cast<XAccessibleText
*>(*this); // resolve ambiguity
935 else if ( rType
== cppu::UnoType
<XAccessibleEditableText
>::get())
937 uno::Reference
<XAccessibleEditableText
> aAccEditText
= this;
938 aRet
<<= aAccEditText
;
940 else if ( rType
== cppu::UnoType
<XAccessibleSelection
>::get())
942 uno::Reference
<XAccessibleSelection
> aAccSel
= this;
945 else if ( rType
== cppu::UnoType
<XAccessibleHypertext
>::get())
947 uno::Reference
<XAccessibleHypertext
> aAccHyp
= this;
951 // add interface com::sun:star:accessibility::XAccessibleTextAttributes
952 else if ( rType
== cppu::UnoType
<XAccessibleTextAttributes
>::get())
954 uno::Reference
<XAccessibleTextAttributes
> aAccTextAttr
= this;
955 aRet
<<= aAccTextAttr
;
958 // add interface com::sun:star:accessibility::XAccessibleTextMarkup
959 else if ( rType
== cppu::UnoType
<XAccessibleTextMarkup
>::get())
961 uno::Reference
<XAccessibleTextMarkup
> aAccTextMarkup
= this;
962 aRet
<<= aAccTextMarkup
;
964 // add interface com::sun:star:accessibility::XAccessibleMultiLineText
965 else if ( rType
== cppu::UnoType
<XAccessibleMultiLineText
>::get())
967 uno::Reference
<XAccessibleMultiLineText
> aAccMultiLineText
= this;
968 aRet
<<= aAccMultiLineText
;
970 else if ( rType
== cppu::UnoType
<XAccessibleTextSelection
>::get())
972 uno::Reference
< css::accessibility::XAccessibleTextSelection
> aTextExtension
= this;
973 aRet
<<= aTextExtension
;
975 else if ( rType
== cppu::UnoType
<XAccessibleExtendedAttributes
>::get())
977 uno::Reference
<XAccessibleExtendedAttributes
> xAttr
= this;
982 aRet
= SwAccessibleContext::queryInterface(rType
);
989 uno::Sequence
< uno::Type
> SAL_CALL
SwAccessibleParagraph::getTypes()
991 // #i63870# - add type accessibility::XAccessibleTextAttributes
992 // #i89175# - add type accessibility::XAccessibleTextMarkup and
993 return cppu::OTypeCollection(
994 cppu::UnoType
<XAccessibleEditableText
>::get(),
995 cppu::UnoType
<XAccessibleTextAttributes
>::get(),
996 ::cppu::UnoType
<XAccessibleSelection
>::get(),
997 cppu::UnoType
<XAccessibleTextMarkup
>::get(),
998 cppu::UnoType
<XAccessibleMultiLineText
>::get(),
999 cppu::UnoType
<XAccessibleHypertext
>::get(),
1000 SwAccessibleContext::getTypes() ).getTypes();
1003 uno::Sequence
< sal_Int8
> SAL_CALL
SwAccessibleParagraph::getImplementationId()
1005 return css::uno::Sequence
<sal_Int8
>();
1010 sal_Int32
SwAccessibleParagraph::getCaretPosition()
1012 SolarMutexGuard aGuard
;
1016 sal_Int32 nRet
= GetCaretPos();
1018 std::scoped_lock
aOldCaretPosGuard( m_Mutex
);
1019 OSL_ENSURE( nRet
== m_nOldCaretPos
, "caret pos out of sync" );
1020 m_nOldCaretPos
= nRet
;
1024 ::rtl::Reference
< SwAccessibleContext
> xThis( this );
1025 GetMap()->SetCursorContext( xThis
);
1031 sal_Bool SAL_CALL
SwAccessibleParagraph::setCaretPosition( sal_Int32 nIndex
)
1033 SolarMutexGuard aGuard
;
1037 // parameter checking
1038 sal_Int32 nLength
= GetString().getLength();
1039 if ( ! IsValidPosition( nIndex
, nLength
) )
1041 throw lang::IndexOutOfBoundsException();
1047 SwCursorShell
* pCursorShell
= GetCursorShell();
1048 if( pCursorShell
!= nullptr )
1050 // create pam for selection
1051 SwTextFrame
const*const pFrame(static_cast<SwTextFrame
const*>(GetFrame()));
1052 TextFrameIndex
const nFrameIndex(GetPortionData().GetCoreViewPosition(nIndex
));
1053 SwPosition
aStartPos(pFrame
->MapViewToModelPos(nFrameIndex
));
1054 SwPaM
aPaM( aStartPos
);
1056 // set PaM at cursor shell
1057 bRet
= Select( aPaM
);
1063 sal_Unicode
SwAccessibleParagraph::getCharacter( sal_Int32 nIndex
)
1065 SolarMutexGuard aGuard
;
1069 OUString
sText( GetString() );
1071 // return character (if valid)
1072 if( !IsValidChar(nIndex
, sText
.getLength() ) )
1073 throw lang::IndexOutOfBoundsException();
1075 return sText
[nIndex
];
1078 css::uno::Sequence
< css::style::TabStop
> SwAccessibleParagraph::GetCurrentTabStop( sal_Int32 nIndex
)
1080 SolarMutexGuard aGuard
;
1084 /* #i12332# The position after the string needs special treatment.
1085 IsValidChar -> IsValidPosition
1087 if( ! (IsValidPosition( nIndex
, GetString().getLength() ) ) )
1088 throw lang::IndexOutOfBoundsException();
1091 bool bBehindText
= false;
1092 if ( nIndex
== GetString().getLength() )
1095 // get model position & prepare GetCharRect() arguments
1096 SwCursorMoveState aMoveState
;
1097 aMoveState
.m_bRealHeight
= true;
1098 aMoveState
.m_bRealWidth
= true;
1099 SwSpecialPos aSpecialPos
;
1100 SwTextFrame
const*const pFrame(static_cast<SwTextFrame
const*>(GetFrame()));
1102 /* #i12332# FillSpecialPos does not accept nIndex ==
1103 GetString().getLength(). In that case nPos is set to the
1104 length of the string in the core. This way GetCharRect
1105 returns the rectangle for a cursor at the end of the
1107 const TextFrameIndex nPos
= bBehindText
1108 ? TextFrameIndex(pFrame
->GetText().getLength())
1109 : GetPortionData().FillSpecialPos(nIndex
, aSpecialPos
, aMoveState
.m_pSpecialPos
);
1113 SwPosition
aPosition(pFrame
->MapViewToModelPos(nPos
));
1114 GetFrame()->GetCharRect( aCoreRect
, aPosition
, &aMoveState
);
1116 // already get the caret position
1117 css::uno::Sequence
< css::style::TabStop
> tabs
;
1118 const sal_Int32 nStrLen
= pFrame
->GetText().getLength();
1121 SwFrame
* pTFrame
= const_cast<SwFrame
*>(GetFrame());
1122 tabs
= pTFrame
->GetTabStopInfo(aCoreRect
.Left());
1125 if( tabs
.hasElements() )
1127 // translate core coordinates into accessibility coordinates
1128 vcl::Window
*pWin
= GetWindow();
1131 throw uno::RuntimeException("no Window", static_cast<cppu::OWeakObject
*>(this));
1134 SwRect
aTmpRect(0, 0, tabs
[0].Position
, 0);
1136 tools::Rectangle
aScreenRect( GetMap()->CoreToPixel( aTmpRect
));
1137 SwRect
aFrameLogBounds( GetBounds( *(GetMap()) ) ); // twip rel to doc root
1139 Point
aFramePixPos( GetMap()->CoreToPixel( aFrameLogBounds
).TopLeft() );
1140 aScreenRect
.Move( -aFramePixPos
.X(), -aFramePixPos
.Y() );
1142 tabs
.getArray()[0].Position
= aScreenRect
.GetWidth();
1152 const PropertyValue
* pValues
;
1153 explicit IndexCompare( const PropertyValue
* pVals
) : pValues(pVals
) {}
1154 bool operator() ( sal_Int32 a
, sal_Int32 b
) const
1156 return (pValues
[a
].Name
< pValues
[b
].Name
);
1162 OUString
SwAccessibleParagraph::GetFieldTypeNameAtIndex(sal_Int32 nIndex
)
1164 OUString strTypeName
;
1166 SwTextField
* pTextField
= nullptr;
1167 sal_Int32 nFieldIndex
= GetPortionData().GetFieldIndex(nIndex
);
1168 if (nFieldIndex
>= 0)
1170 SwTextFrame
const*const pFrame(static_cast<SwTextFrame
const*>(GetFrame()));
1171 sw::MergedAttrIter
iter(*pFrame
);
1172 while (SwTextAttr
const*const pHt
= iter
.NextAttr())
1174 if ((pHt
->Which() == RES_TXTATR_FIELD
1175 || pHt
->Which() == RES_TXTATR_ANNOTATION
1176 || pHt
->Which() == RES_TXTATR_INPUTFIELD
)
1177 && (nFieldIndex
-- == 0))
1179 pTextField
= const_cast<SwTextField
*>(
1180 static_txtattr_cast
<SwTextField
const*>(pHt
));
1183 else if (pHt
->Which() == RES_TXTATR_REFMARK
1184 && (nFieldIndex
-- == 0))
1186 strTypeName
= "set reference";
1192 const SwField
* pField
= pTextField
->GetFormatField().GetField();
1195 strTypeName
= SwFieldType::GetTypeStr(pField
->GetTypeId());
1196 const SwFieldIds nWhich
= pField
->GetTyp()->Which();
1198 sal_uInt32 subType
= 0;
1201 case SwFieldIds::DocStat
:
1202 subType
= static_cast<const SwDocStatField
*>(pField
)->GetSubType();
1204 case SwFieldIds::GetRef
:
1206 switch( pField
->GetSubType() )
1210 const SwGetRefField
* pRefField
= dynamic_cast<const SwGetRefField
*>(pField
);
1211 if ( pRefField
&& pRefField
->IsRefToHeadingCrossRefBookmark() )
1212 sEntry
= "Headings";
1213 else if ( pRefField
&& pRefField
->IsRefToNumItemCrossRefBookmark() )
1214 sEntry
= "Numbered Paragraphs";
1216 sEntry
= "Bookmarks";
1220 sEntry
= "Footnotes";
1223 sEntry
= "Endnotes";
1225 case REF_SETREFATTR
:
1226 sEntry
= "Insert Reference";
1228 case REF_SEQUENCEFLD
:
1229 sEntry
= static_cast<const SwGetRefField
*>(pField
)->GetSetRefName();
1233 strTypeName
= sEntry
;
1234 // <pField->GetFormat() >= 0> is always true as <pField->GetFormat()> is unsigned
1235 // if (pField->GetFormat() >= 0)
1237 sEntry
= aMgr
.GetFormatStr( pField
->GetTypeId(), pField
->GetFormat() );
1238 if (sEntry
.getLength() > 0)
1240 strTypeName
+= "-" + sEntry
;
1245 case SwFieldIds::DateTime
:
1246 subType
= static_cast<const SwDateTimeField
*>(pField
)->GetSubType();
1248 case SwFieldIds::JumpEdit
:
1250 const sal_uInt32 nFormat
= pField
->GetFormat();
1251 const sal_uInt16 nSize
= aMgr
.GetFormatCount(pField
->GetTypeId(), false);
1252 if (nFormat
< nSize
)
1254 sEntry
= aMgr
.GetFormatStr(pField
->GetTypeId(), nFormat
);
1255 if (sEntry
.getLength() > 0)
1257 strTypeName
+= "-" + sEntry
;
1262 case SwFieldIds::ExtUser
:
1263 subType
= static_cast<const SwExtUserField
*>(pField
)->GetSubType();
1265 case SwFieldIds::HiddenText
:
1266 case SwFieldIds::SetExp
:
1268 sEntry
= pField
->GetTyp()->GetName();
1269 if (sEntry
.getLength() > 0)
1271 strTypeName
+= "-" + sEntry
;
1275 case SwFieldIds::DocInfo
:
1276 subType
= pField
->GetSubType();
1279 case SwFieldIds::RefPageSet
:
1281 const SwRefPageSetField
* pRPld
= static_cast<const SwRefPageSetField
*>(pField
);
1282 bool bOn
= pRPld
->IsOn();
1285 strTypeName
+= "on";
1287 strTypeName
+= "off";
1290 case SwFieldIds::Author
:
1292 strTypeName
+= "-" + aMgr
.GetFormatStr(pField
->GetTypeId(), pField
->GetFormat() & 0xff);
1297 if (subType
> 0 || nWhich
== SwFieldIds::DocInfo
|| nWhich
== SwFieldIds::ExtUser
|| nWhich
== SwFieldIds::DocStat
)
1299 std::vector
<OUString
> aLst
;
1300 aMgr
.GetSubTypes(pField
->GetTypeId(), aLst
);
1301 if (subType
< aLst
.size())
1302 sEntry
= aLst
[subType
];
1303 if (sEntry
.getLength() > 0)
1305 if (nWhich
== SwFieldIds::DocInfo
)
1307 strTypeName
= sEntry
;
1308 sal_uInt16 nSize
= aMgr
.GetFormatCount(pField
->GetTypeId(), false);
1309 const sal_uInt16 nExSub
= pField
->GetSubType() & 0xff00;
1310 if (nSize
> 0 && nExSub
> 0)
1312 //Get extra subtype string
1314 sEntry
= aMgr
.GetFormatStr(pField
->GetTypeId(), nExSub
/0x0100-1);
1315 strTypeName
+= sEntry
;
1320 strTypeName
+= "-" + sEntry
;
1329 // #i63870# - re-implement method on behalf of methods
1330 // <_getDefaultAttributesImpl(..)> and <_getRunAttributesImpl(..)>
1331 uno::Sequence
<PropertyValue
> SwAccessibleParagraph::getCharacterAttributes(
1333 const uno::Sequence
< OUString
>& aRequestedAttributes
)
1336 SolarMutexGuard aGuard
;
1340 const OUString
& rText
= GetString();
1342 if (!IsValidPosition(nIndex
, rText
.getLength()))
1343 throw lang::IndexOutOfBoundsException();
1345 bool bSupplementalMode
= false;
1346 uno::Sequence
< OUString
> aNames
= aRequestedAttributes
;
1347 if (!aNames
.hasElements())
1349 bSupplementalMode
= true;
1350 aNames
= getAttributeNames();
1352 // retrieve default character attributes
1353 tAccParaPropValMap aDefAttrSeq
;
1354 _getDefaultAttributesImpl( aNames
, aDefAttrSeq
, true );
1356 // retrieved run character attributes
1357 tAccParaPropValMap aRunAttrSeq
;
1358 _getRunAttributesImpl( nIndex
, aNames
, aRunAttrSeq
);
1360 // merge default and run attributes
1361 std::vector
< PropertyValue
> aValues( aDefAttrSeq
.size() );
1363 for ( const auto& rDefEntry
: aDefAttrSeq
)
1365 tAccParaPropValMap::const_iterator aRunIter
=
1366 aRunAttrSeq
.find( rDefEntry
.first
);
1367 if ( aRunIter
!= aRunAttrSeq
.end() )
1369 aValues
[i
] = aRunIter
->second
;
1373 aValues
[i
] = rDefEntry
.second
;
1377 if( bSupplementalMode
)
1379 uno::Sequence
< OUString
> aSupplementalNames
= aRequestedAttributes
;
1380 if (!aSupplementalNames
.hasElements())
1381 aSupplementalNames
= getSupplementalAttributeNames();
1383 tAccParaPropValMap aSupplementalAttrSeq
;
1384 _getSupplementalAttributesImpl( aSupplementalNames
, aSupplementalAttrSeq
);
1386 aValues
.resize( aValues
.size() + aSupplementalAttrSeq
.size() );
1388 for ( const auto& rSupplementalEntry
: aSupplementalAttrSeq
)
1390 aValues
[i
] = rSupplementalEntry
.second
;
1394 _correctValues( nIndex
, aValues
);
1396 aValues
.emplace_back();
1398 OUString strTypeName
= GetFieldTypeNameAtIndex(nIndex
);
1399 if (!strTypeName
.isEmpty())
1401 aValues
.emplace_back();
1402 PropertyValue
& rValueFT
= aValues
.back();
1403 rValueFT
.Name
= "FieldType";
1404 rValueFT
.Value
<<= strTypeName
.toAsciiLowerCase();
1405 rValueFT
.Handle
= -1;
1406 rValueFT
.State
= PropertyState_DIRECT_VALUE
;
1409 //sort property values
1410 // build sorted index array
1411 sal_Int32 nLength
= aValues
.size();
1412 std::vector
<sal_Int32
> aIndices
;
1413 aIndices
.reserve(nLength
);
1414 for (i
= 0; i
< nLength
; ++i
)
1415 aIndices
.push_back(i
);
1416 std::sort(aIndices
.begin(), aIndices
.end(), IndexCompare(aValues
.data()));
1417 // create sorted sequences according to index array
1418 uno::Sequence
<PropertyValue
> aNewValues( nLength
);
1419 PropertyValue
* pNewValues
= aNewValues
.getArray();
1420 for (i
= 0; i
< nLength
; ++i
)
1422 pNewValues
[i
] = aValues
[aIndices
[i
]];
1427 return comphelper::containerToSequence(aValues
);
1430 static void SetPutRecursive(SfxItemSet
&targetSet
, const SfxItemSet
&sourceSet
)
1432 const SfxItemSet
*const pParentSet
= sourceSet
.GetParent();
1434 SetPutRecursive(targetSet
, *pParentSet
);
1435 targetSet
.Put(sourceSet
);
1439 void SwAccessibleParagraph::_getDefaultAttributesImpl(
1440 const uno::Sequence
< OUString
>& aRequestedAttributes
,
1441 tAccParaPropValMap
& rDefAttrSeq
,
1442 const bool bOnlyCharAttrs
)
1444 // retrieve default attributes
1445 SwTextFrame
const*const pFrame(static_cast<SwTextFrame
const*>(GetFrame()));
1446 const SwTextNode
*const pTextNode(pFrame
->GetTextNodeForParaProps());
1447 std::optional
<SfxItemSet
> pSet
;
1448 if ( !bOnlyCharAttrs
)
1450 pSet
.emplace( const_cast<SwAttrPool
&>(pTextNode
->GetDoc().GetAttrPool()),
1451 svl::Items
<RES_CHRATR_BEGIN
, RES_CHRATR_END
- 1,
1452 RES_PARATR_BEGIN
, RES_PARATR_END
- 1,
1453 RES_FRMATR_BEGIN
, RES_FRMATR_END
- 1> );
1457 pSet
.emplace( const_cast<SwAttrPool
&>(pTextNode
->GetDoc().GetAttrPool()),
1458 svl::Items
<RES_CHRATR_BEGIN
, RES_CHRATR_END
- 1> );
1460 // #i82637# - From the perspective of the a11y API the default character
1461 // attributes are the character attributes, which are set at the paragraph style
1462 // of the paragraph. The character attributes set at the automatic paragraph
1463 // style of the paragraph are treated as run attributes.
1464 // pTextNode->SwContentNode::GetAttr( *pSet );
1465 // get default paragraph attributes, if needed, and merge these into <pSet>
1466 if ( !bOnlyCharAttrs
)
1468 SfxItemSetFixed
<RES_PARATR_BEGIN
, RES_PARATR_END
- 1,
1469 RES_FRMATR_BEGIN
, RES_FRMATR_END
- 1>
1470 aParaSet( const_cast<SwAttrPool
&>(pTextNode
->GetDoc().GetAttrPool()) );
1471 pTextNode
->SwContentNode::GetAttr( aParaSet
);
1472 pSet
->Put( aParaSet
);
1474 // get default character attributes and merge these into <pSet>
1475 OSL_ENSURE( pTextNode
->GetTextColl(),
1476 "<SwAccessibleParagraph::_getDefaultAttributesImpl(..)> - missing paragraph style. Serious defect!" );
1477 if ( pTextNode
->GetTextColl() )
1479 SfxItemSetFixed
<RES_CHRATR_BEGIN
, RES_CHRATR_END
- 1>
1480 aCharSet( const_cast<SwAttrPool
&>(pTextNode
->GetDoc().GetAttrPool()) );
1481 SetPutRecursive( aCharSet
, pTextNode
->GetTextColl()->GetAttrSet() );
1482 pSet
->Put( aCharSet
);
1485 // build-up sequence containing the run attributes <rDefAttrSeq>
1486 tAccParaPropValMap aDefAttrSeq
;
1488 const SfxItemPropertyMap
& rPropMap
=
1489 aSwMapProvider
.GetPropertySet( PROPERTY_MAP_TEXT_CURSOR
)->getPropertyMap();
1490 for ( const auto pEntry
: rPropMap
.getPropertyEntries() )
1492 const SfxPoolItem
* pItem
= pSet
->GetItem( pEntry
->nWID
);
1496 pItem
->QueryValue( aVal
, pEntry
->nMemberId
);
1498 PropertyValue rPropVal
;
1499 rPropVal
.Name
= pEntry
->aName
;
1500 rPropVal
.Value
= aVal
;
1501 rPropVal
.Handle
= -1;
1502 rPropVal
.State
= beans::PropertyState_DEFAULT_VALUE
;
1504 aDefAttrSeq
[rPropVal
.Name
] = rPropVal
;
1509 // add property value entry for the paragraph style
1510 if ( !bOnlyCharAttrs
&& pTextNode
->GetTextColl() )
1512 if ( aDefAttrSeq
.find( UNO_NAME_PARA_STYLE_NAME
) == aDefAttrSeq
.end() )
1514 PropertyValue rPropVal
;
1515 rPropVal
.Name
= UNO_NAME_PARA_STYLE_NAME
;
1516 uno::Any
aVal( uno::Any( pTextNode
->GetTextColl()->GetName() ) );
1517 rPropVal
.Value
= aVal
;
1518 rPropVal
.Handle
= -1;
1519 rPropVal
.State
= beans::PropertyState_DEFAULT_VALUE
;
1521 aDefAttrSeq
[rPropVal
.Name
] = rPropVal
;
1526 // resolve value text::WritingMode2::PAGE of property value entry WritingMode
1527 if ( !bOnlyCharAttrs
&& GetFrame() )
1529 tAccParaPropValMap::iterator aIter
= aDefAttrSeq
.find( UNO_NAME_WRITING_MODE
);
1530 if ( aIter
!= aDefAttrSeq
.end() )
1532 PropertyValue
rPropVal( aIter
->second
);
1533 sal_Int16 nVal
= rPropVal
.Value
.get
<sal_Int16
>();
1534 if ( nVal
== text::WritingMode2::PAGE
)
1536 const SwFrame
* pUpperFrame( GetFrame()->GetUpper() );
1537 while ( pUpperFrame
)
1539 if ( pUpperFrame
->GetType() &
1540 ( SwFrameType::Page
| SwFrameType::Fly
| SwFrameType::Section
| SwFrameType::Tab
| SwFrameType::Cell
) )
1542 if ( pUpperFrame
->IsVertical() )
1544 nVal
= text::WritingMode2::TB_RL
;
1546 else if ( pUpperFrame
->IsRightToLeft() )
1548 nVal
= text::WritingMode2::RL_TB
;
1552 nVal
= text::WritingMode2::LR_TB
;
1554 rPropVal
.Value
<<= nVal
;
1555 aDefAttrSeq
[rPropVal
.Name
] = rPropVal
;
1559 if ( pUpperFrame
->IsFlyFrame() )
1561 pUpperFrame
= static_cast<const SwFlyFrame
*>(pUpperFrame
)->GetAnchorFrame();
1565 pUpperFrame
= pUpperFrame
->GetUpper();
1573 if ( !aRequestedAttributes
.hasElements() )
1575 rDefAttrSeq
= aDefAttrSeq
;
1579 for( const OUString
& rReqAttr
: aRequestedAttributes
)
1581 tAccParaPropValMap::const_iterator
const aIter
= aDefAttrSeq
.find( rReqAttr
);
1582 if ( aIter
!= aDefAttrSeq
.end() )
1584 rDefAttrSeq
[ aIter
->first
] = aIter
->second
;
1590 uno::Sequence
< PropertyValue
> SwAccessibleParagraph::getDefaultAttributes(
1591 const uno::Sequence
< OUString
>& aRequestedAttributes
)
1593 SolarMutexGuard aGuard
;
1597 tAccParaPropValMap aDefAttrSeq
;
1598 _getDefaultAttributesImpl( aRequestedAttributes
, aDefAttrSeq
);
1601 static constexpr OUStringLiteral sMMToPixelRatio
= u
"MMToPixelRatio";
1602 bool bProvideMMToPixelRatio( !aRequestedAttributes
.hasElements() ||
1603 (comphelper::findValue(aRequestedAttributes
, sMMToPixelRatio
) != -1) );
1605 uno::Sequence
< PropertyValue
> aValues( aDefAttrSeq
.size() +
1606 ( bProvideMMToPixelRatio
? 1 : 0 ) );
1607 auto pValues
= aValues
.getArray();
1608 std::transform(aDefAttrSeq
.begin(), aDefAttrSeq
.end(), pValues
,
1609 [](const auto& rEntry
) -> PropertyValue
{ return rEntry
.second
; });
1612 if ( bProvideMMToPixelRatio
)
1614 PropertyValue rPropVal
;
1615 rPropVal
.Name
= sMMToPixelRatio
;
1616 const Size
a100thMMSize( 1000, 1000 );
1617 const Size aPixelSize
= GetMap()->LogicToPixel( a100thMMSize
);
1618 const float fRatio
= (static_cast<float>(a100thMMSize
.Width())/100)/aPixelSize
.Width();
1619 rPropVal
.Value
<<= fRatio
;
1620 rPropVal
.Handle
= -1;
1621 rPropVal
.State
= beans::PropertyState_DEFAULT_VALUE
;
1622 pValues
[ aValues
.getLength() - 1 ] = rPropVal
;
1628 void SwAccessibleParagraph::_getRunAttributesImpl(
1629 const sal_Int32 nIndex
,
1630 const uno::Sequence
< OUString
>& aRequestedAttributes
,
1631 tAccParaPropValMap
& rRunAttrSeq
)
1633 // create PaM for character at position <nIndex>
1634 std::optional
<SwPaM
> pPaM
;
1635 const TextFrameIndex
nCorePos(GetPortionData().GetCoreViewPosition(nIndex
));
1636 SwTextFrame
const*const pFrame(static_cast<SwTextFrame
const*>(GetFrame()));
1637 SwPosition
const aModelPos(pFrame
->MapViewToModelPos(nCorePos
));
1638 SwTextNode
*const pTextNode(aModelPos
.GetNode().GetTextNode());
1640 SwPosition
const aEndPos(*pTextNode
,
1641 aModelPos
.GetContentIndex() == pTextNode
->Len()
1642 ? pTextNode
->Len() // ???
1643 : aModelPos
.GetContentIndex() + 1);
1644 pPaM
.emplace(aModelPos
, aEndPos
);
1647 // retrieve character attributes for the created PaM <pPaM>
1648 SfxItemSetFixed
<RES_CHRATR_BEGIN
, RES_CHRATR_END
-1> aSet( pPaM
->GetDoc().GetAttrPool() );
1650 // From the perspective of the a11y API the character attributes, which
1651 // are set at the automatic paragraph style of the paragraph, are treated
1652 // as run attributes.
1653 // SwXTextCursor::GetCursorAttr( *pPaM, aSet, sal_True, sal_True );
1654 // get character attributes from automatic paragraph style and merge these into <aSet>
1656 if ( pTextNode
->HasSwAttrSet() )
1658 SfxItemSetFixed
<RES_CHRATR_BEGIN
, RES_CHRATR_END
-1> aAutomaticParaStyleCharAttrs( pPaM
->GetDoc().GetAttrPool());
1659 aAutomaticParaStyleCharAttrs
.Put( *(pTextNode
->GetpSwAttrSet()), false );
1660 aSet
.Put( aAutomaticParaStyleCharAttrs
);
1663 // get character attributes at <pPaM> and merge these into <aSet>
1665 SfxItemSetFixed
<RES_CHRATR_BEGIN
, RES_CHRATR_END
-1> aCharAttrsAtPaM( pPaM
->GetDoc().GetAttrPool() );
1666 SwUnoCursorHelper::GetCursorAttr(*pPaM
, aCharAttrsAtPaM
, true);
1667 aSet
.Put( aCharAttrsAtPaM
);
1670 // build-up sequence containing the run attributes <rRunAttrSeq>
1672 tAccParaPropValMap aRunAttrSeq
;
1674 tAccParaPropValMap aDefAttrSeq
;
1675 uno::Sequence
< OUString
> aDummy
;
1676 _getDefaultAttributesImpl( aDummy
, aDefAttrSeq
, true ); // #i82637#
1678 const SfxItemPropertyMap
& rPropMap
=
1679 aSwMapProvider
.GetPropertySet( PROPERTY_MAP_TEXT_CURSOR
)->getPropertyMap();
1680 for ( const auto pEntry
: rPropMap
.getPropertyEntries() )
1682 const SfxPoolItem
* pItem( nullptr );
1683 // #i82637# - Found character attributes, whose value equals the value of
1684 // the corresponding default character attributes, are excluded.
1685 if ( aSet
.GetItemState( pEntry
->nWID
, true, &pItem
) == SfxItemState::SET
)
1688 pItem
->QueryValue( aVal
, pEntry
->nMemberId
);
1690 PropertyValue rPropVal
;
1691 rPropVal
.Name
= pEntry
->aName
;
1692 rPropVal
.Value
= aVal
;
1693 rPropVal
.Handle
= -1;
1694 rPropVal
.State
= PropertyState_DIRECT_VALUE
;
1696 tAccParaPropValMap::const_iterator aDefIter
=
1697 aDefAttrSeq
.find( rPropVal
.Name
);
1698 if ( aDefIter
== aDefAttrSeq
.end() ||
1699 rPropVal
.Value
!= aDefIter
->second
.Value
)
1701 aRunAttrSeq
[rPropVal
.Name
] = rPropVal
;
1707 if ( !aRequestedAttributes
.hasElements() )
1709 rRunAttrSeq
= aRunAttrSeq
;
1713 for( const OUString
& rReqAttr
: aRequestedAttributes
)
1715 tAccParaPropValMap::iterator aIter
= aRunAttrSeq
.find( rReqAttr
);
1716 if ( aIter
!= aRunAttrSeq
.end() )
1718 rRunAttrSeq
[ (*aIter
).first
] = (*aIter
).second
;
1725 uno::Sequence
< PropertyValue
> SwAccessibleParagraph::getRunAttributes(
1727 const uno::Sequence
< OUString
>& aRequestedAttributes
)
1729 SolarMutexGuard aGuard
;
1734 const OUString
& rText
= GetString();
1735 if (!IsValidPosition(nIndex
, rText
.getLength()))
1737 throw lang::IndexOutOfBoundsException();
1741 tAccParaPropValMap aRunAttrSeq
;
1742 _getRunAttributesImpl( nIndex
, aRequestedAttributes
, aRunAttrSeq
);
1744 return comphelper::mapValuesToSequence( aRunAttrSeq
);
1747 void SwAccessibleParagraph::_getSupplementalAttributesImpl(
1748 const uno::Sequence
< OUString
>& aRequestedAttributes
,
1749 tAccParaPropValMap
& rSupplementalAttrSeq
)
1751 SwTextFrame
const*const pFrame(static_cast<SwTextFrame
const*>(GetFrame()));
1752 const SwTextNode
*const pTextNode(pFrame
->GetTextNodeForParaProps());
1754 RES_PARATR_LINESPACING
, RES_PARATR_ADJUST
,
1755 RES_PARATR_TABSTOP
, RES_PARATR_TABSTOP
,
1756 RES_PARATR_NUMRULE
, RES_PARATR_NUMRULE
,
1757 RES_PARATR_LIST_BEGIN
, RES_PARATR_LIST_END
- 1,
1758 RES_MARGIN_FIRSTLINE
, RES_MARGIN_RIGHT
,
1759 RES_UL_SPACE
, RES_UL_SPACE
>
1760 aSet( const_cast<SwAttrPool
&>(pTextNode
->GetDoc().GetAttrPool()) );
1762 if ( pTextNode
->HasBullet() || pTextNode
->HasNumber() )
1764 aSet
.Put( pTextNode
->GetAttr(RES_PARATR_LIST_LEVEL
) );
1766 aSet
.Put( pTextNode
->SwContentNode::GetAttr(RES_UL_SPACE
) );
1767 aSet
.Put( pTextNode
->SwContentNode::GetAttr(RES_MARGIN_FIRSTLINE
) );
1768 aSet
.Put( pTextNode
->SwContentNode::GetAttr(RES_MARGIN_TEXTLEFT
) );
1769 aSet
.Put( pTextNode
->SwContentNode::GetAttr(RES_MARGIN_RIGHT
) );
1770 aSet
.Put( pTextNode
->SwContentNode::GetAttr(RES_PARATR_ADJUST
) );
1772 tAccParaPropValMap aSupplementalAttrSeq
;
1774 o3tl::span
<const SfxItemPropertyMapEntry
> pPropMap(
1775 aSwMapProvider
.GetPropertyMapEntries( PROPERTY_MAP_ACCESSIBILITY_TEXT_ATTRIBUTE
) );
1776 for (const auto & rEntry
: pPropMap
)
1778 const SfxPoolItem
* pItem
= aSet
.GetItem( rEntry
.nWID
);
1782 pItem
->QueryValue( aVal
, rEntry
.nMemberId
);
1784 PropertyValue rPropVal
;
1785 rPropVal
.Name
= rEntry
.aName
;
1786 rPropVal
.Value
= aVal
;
1787 rPropVal
.Handle
= -1;
1788 rPropVal
.State
= beans::PropertyState_DEFAULT_VALUE
;
1790 aSupplementalAttrSeq
[rPropVal
.Name
] = rPropVal
;
1795 for( const OUString
& rSupplementalAttr
: aRequestedAttributes
)
1797 tAccParaPropValMap::const_iterator
const aIter
= aSupplementalAttrSeq
.find( rSupplementalAttr
);
1798 if ( aIter
!= aSupplementalAttrSeq
.end() )
1800 rSupplementalAttrSeq
[ aIter
->first
] = aIter
->second
;
1805 void SwAccessibleParagraph::_correctValues( const sal_Int32 nIndex
,
1806 std::vector
< PropertyValue
>& rValues
)
1808 PropertyValue ChangeAttr
, ChangeAttrColor
;
1810 const SwRangeRedline
* pRedline
= GetRedlineAtIndex();
1814 const SwModuleOptions
*pOpt
= SW_MOD()->GetModuleConfig();
1815 AuthorCharAttr aChangeAttr
;
1818 switch( pRedline
->GetType())
1820 case RedlineType::Insert
:
1821 aChangeAttr
= pOpt
->GetInsertAuthorAttr();
1823 case RedlineType::Delete
:
1824 aChangeAttr
= pOpt
->GetDeletedAuthorAttr();
1826 case RedlineType::Format
:
1827 aChangeAttr
= pOpt
->GetFormatAuthorAttr();
1832 switch( aChangeAttr
.m_nItemId
)
1834 case SID_ATTR_CHAR_WEIGHT
:
1835 ChangeAttr
.Name
= UNO_NAME_CHAR_WEIGHT
;
1836 ChangeAttr
.Value
<<= awt::FontWeight::BOLD
;
1838 case SID_ATTR_CHAR_POSTURE
:
1839 ChangeAttr
.Name
= UNO_NAME_CHAR_POSTURE
;
1840 ChangeAttr
.Value
<<= awt::FontSlant_ITALIC
; //char posture
1842 case SID_ATTR_CHAR_STRIKEOUT
:
1843 ChangeAttr
.Name
= UNO_NAME_CHAR_STRIKEOUT
;
1844 ChangeAttr
.Value
<<= awt::FontStrikeout::SINGLE
; //char strikeout
1846 case SID_ATTR_CHAR_UNDERLINE
:
1847 ChangeAttr
.Name
= UNO_NAME_CHAR_UNDERLINE
;
1848 ChangeAttr
.Value
<<= aChangeAttr
.m_nAttr
; //underline line
1851 if( aChangeAttr
.m_nColor
!= COL_NONE_COLOR
)
1853 if( aChangeAttr
.m_nItemId
== SID_ATTR_BRUSH
)
1855 ChangeAttrColor
.Name
= UNO_NAME_CHAR_BACK_COLOR
;
1856 if( aChangeAttr
.m_nColor
== COL_TRANSPARENT
)//char backcolor
1857 ChangeAttrColor
.Value
<<= COL_BLUE
;
1859 ChangeAttrColor
.Value
<<= aChangeAttr
.m_nColor
;
1863 ChangeAttrColor
.Name
= UNO_NAME_CHAR_COLOR
;
1864 if( aChangeAttr
.m_nColor
== COL_TRANSPARENT
)//char color
1865 ChangeAttrColor
.Value
<<= COL_BLUE
;
1867 ChangeAttrColor
.Value
<<= aChangeAttr
.m_nColor
;
1872 // sw_redlinehide: this function only needs SwWrongList for 1 character,
1873 // and the end is excluded by InWrongWord(),
1874 // so it ought to work to just pick the wrong-list/node that contains
1875 // the character following the given nIndex
1876 SwTextFrame
const*const pFrame(static_cast<SwTextFrame
const*>(GetFrame()));
1877 TextFrameIndex
const nCorePos(GetPortionData().GetCoreViewPosition(nIndex
));
1878 std::pair
<SwTextNode
*, sal_Int32
> pos(pFrame
->MapViewToModel(nCorePos
));
1879 if (pos
.first
->Len() == pos
.second
1880 && nCorePos
!= TextFrameIndex(pFrame
->GetText().getLength()))
1882 pos
= pFrame
->MapViewToModel(nCorePos
+ TextFrameIndex(1)); // try this one instead
1883 assert(pos
.first
->Len() != pos
.second
);
1885 const SwTextNode
*const pTextNode(pos
.first
);
1887 sal_Int32 nValues
= rValues
.size();
1888 for (sal_Int32 i
= 0; i
< nValues
; ++i
)
1890 PropertyValue
& rValue
= rValues
[i
];
1892 if (rValue
.Name
== ChangeAttr
.Name
)
1894 rValue
.Value
= ChangeAttr
.Value
;
1898 if (rValue
.Name
== ChangeAttrColor
.Name
)
1900 rValue
.Value
= ChangeAttrColor
.Value
;
1905 if (rValue
.Name
== UNO_NAME_CHAR_BACK_COLOR
)
1907 uno::Any
&anyChar
= rValue
.Value
;
1908 sal_uInt32 crBack
= static_cast<sal_uInt32
>( reinterpret_cast<sal_uIntPtr
>(anyChar
.pReserved
));
1909 if (COL_AUTO
== Color(ColorTransparency
, crBack
))
1911 uno::Reference
<XAccessibleComponent
> xComponent(this);
1912 if (xComponent
.is())
1914 crBack
= static_cast<sal_uInt32
>(xComponent
->getBackground());
1916 rValue
.Value
<<= crBack
;
1922 if (rValue
.Name
== UNO_NAME_CHAR_COLOR
)
1924 if( GetPortionData().IsInGrayPortion( nIndex
) )
1925 rValue
.Value
<<= GetCursorShell()->GetViewOptions()->GetFieldShadingsColor();
1926 uno::Any
&anyChar
= rValue
.Value
;
1927 sal_uInt32 crChar
= static_cast<sal_uInt32
>( reinterpret_cast<sal_uIntPtr
>(anyChar
.pReserved
));
1929 if( COL_AUTO
== Color(ColorTransparency
, crChar
) )
1931 uno::Reference
<XAccessibleComponent
> xComponent(this);
1932 if (xComponent
.is())
1934 Color
cr(ColorTransparency
, xComponent
->getBackground());
1935 crChar
= sal_uInt32(cr
.IsDark() ? COL_WHITE
: COL_BLACK
);
1936 rValue
.Value
<<= crChar
;
1943 if (rValue
.Name
== UNO_NAME_CHAR_UNDERLINE
)
1946 SwCursorShell
* pCursorShell
= GetCursorShell();
1947 if( pCursorShell
!= nullptr && pCursorShell
->GetViewOptions() && pCursorShell
->GetViewOptions()->IsOnlineSpell())
1949 const SwWrongList
* pWrongList
= pTextNode
->GetWrong();
1950 if( nullptr != pWrongList
)
1952 sal_Int32 nBegin
= pos
.second
;
1954 if (pWrongList
->InWrongWord(nBegin
, nLen
) && !pTextNode
->IsSymbolAt(nBegin
))
1956 rValue
.Value
<<= sal_uInt16(LINESTYLE_WAVE
);
1964 if (rValue
.Name
== UNO_NAME_CHAR_UNDERLINE_COLOR
)
1967 SwCursorShell
* pCursorShell
= GetCursorShell();
1968 if( pCursorShell
!= nullptr && pCursorShell
->GetViewOptions() && pCursorShell
->GetViewOptions()->IsOnlineSpell())
1970 const SwWrongList
* pWrongList
= pTextNode
->GetWrong();
1971 if( nullptr != pWrongList
)
1973 sal_Int32 nBegin
= pos
.second
;
1975 if (pWrongList
->InWrongWord(nBegin
, nLen
) && !pTextNode
->IsSymbolAt(nBegin
))
1977 rValue
.Value
<<= sal_Int32(0x00ff0000);
1983 uno::Any
&anyChar
= rValue
.Value
;
1984 sal_uInt32 crUnderline
= static_cast<sal_uInt32
>( reinterpret_cast<sal_uIntPtr
>(anyChar
.pReserved
));
1985 if ( COL_AUTO
== Color(ColorTransparency
, crUnderline
) )
1987 uno::Reference
<XAccessibleComponent
> xComponent(this);
1988 if (xComponent
.is())
1990 Color
cr(ColorTransparency
, xComponent
->getBackground());
1991 crUnderline
= sal_uInt32(cr
.IsDark() ? COL_WHITE
: COL_BLACK
);
1992 rValue
.Value
<<= crUnderline
;
2000 if (rValue
.Name
== UNO_NAME_TABSTOPS
)
2002 css::uno::Sequence
< css::style::TabStop
> tabs
= GetCurrentTabStop( nIndex
);
2003 if( !tabs
.hasElements() )
2005 css::style::TabStop ts
;
2006 css::awt::Rectangle rc0
= getCharacterBounds(0);
2007 css::awt::Rectangle rc1
= getCharacterBounds(nIndex
);
2008 if( rc1
.X
- rc0
.X
>= 48 )
2009 ts
.Position
= (rc1
.X
- rc0
.X
) - (rc1
.X
- rc0
.X
- 48)% 47 + 47;
2012 ts
.DecimalChar
= ' ';
2014 ts
.Alignment
= css::style::TabAlign_LEFT
;
2017 rValue
.Value
<<= tabs
;
2021 //footnote & endnote
2022 if (rValue
.Name
== UNO_NAME_CHAR_ESCAPEMENT
)
2024 if ( GetPortionData().IsIndexInFootnode(nIndex
) )
2026 rValue
.Value
<<= sal_Int32(101);
2033 awt::Rectangle
SwAccessibleParagraph::getCharacterBounds(
2036 SolarMutexGuard aGuard
;
2040 // #i12332# The position after the string needs special treatment.
2041 // IsValidChar -> IsValidPosition
2042 if( ! (IsValidPosition( nIndex
, GetString().getLength() ) ) )
2043 throw lang::IndexOutOfBoundsException();
2046 bool bBehindText
= false;
2047 if ( nIndex
== GetString().getLength() )
2050 // get model position & prepare GetCharRect() arguments
2051 SwCursorMoveState aMoveState
;
2052 aMoveState
.m_bRealHeight
= true;
2053 aMoveState
.m_bRealWidth
= true;
2054 SwSpecialPos aSpecialPos
;
2055 SwTextFrame
const*const pFrame(static_cast<SwTextFrame
const*>(GetFrame()));
2057 /** #i12332# FillSpecialPos does not accept nIndex ==
2058 GetString().getLength(). In that case nPos is set to the
2059 length of the string in the core. This way GetCharRect
2060 returns the rectangle for a cursor at the end of the
2062 const TextFrameIndex nPos
= bBehindText
2063 ? TextFrameIndex(pFrame
->GetText().getLength())
2064 : GetPortionData().FillSpecialPos(nIndex
, aSpecialPos
, aMoveState
.m_pSpecialPos
);
2068 SwPosition
aPosition(pFrame
->MapViewToModelPos(nPos
));
2069 GetFrame()->GetCharRect( aCoreRect
, aPosition
, &aMoveState
);
2071 // translate core coordinates into accessibility coordinates
2072 vcl::Window
*pWin
= GetWindow();
2075 throw uno::RuntimeException("no Window", static_cast<cppu::OWeakObject
*>(this));
2078 tools::Rectangle
aScreenRect( GetMap()->CoreToPixel( aCoreRect
));
2079 SwRect
aFrameLogBounds( GetBounds( *(GetMap()) ) ); // twip rel to doc root
2081 Point
aFramePixPos( GetMap()->CoreToPixel( aFrameLogBounds
).TopLeft() );
2082 aScreenRect
.Move( -aFramePixPos
.getX(), -aFramePixPos
.getY() );
2084 // convert into AWT Rectangle
2085 return awt::Rectangle(
2086 aScreenRect
.Left(), aScreenRect
.Top(),
2087 aScreenRect
.GetWidth(), aScreenRect
.GetHeight() );
2090 sal_Int32
SwAccessibleParagraph::getCharacterCount()
2092 SolarMutexGuard aGuard
;
2096 return GetString().getLength();
2099 sal_Int32
SwAccessibleParagraph::getIndexAtPoint( const awt::Point
& rPoint
)
2101 SolarMutexGuard aGuard
;
2105 // construct Point (translate into layout coordinates)
2106 vcl::Window
*pWin
= GetWindow();
2109 throw uno::RuntimeException("no Window", static_cast<cppu::OWeakObject
*>(this));
2111 Point
aPoint( rPoint
.X
, rPoint
.Y
);
2112 SwRect
aLogBounds( GetBounds( *(GetMap()), GetFrame() ) ); // twip rel to doc root
2113 Point
aPixPos( GetMap()->CoreToPixel( aLogBounds
).TopLeft() );
2114 aPoint
.setX(aPoint
.getX() + aPixPos
.getX());
2115 aPoint
.setY(aPoint
.getY() + aPixPos
.getY());
2116 Point
aCorePoint( GetMap()->PixelToCore( aPoint
) );
2117 if( !aLogBounds
.Contains( aCorePoint
) )
2119 // #i12332# rPoint is may also be in rectangle returned by
2120 // getCharacterBounds(getCharacterCount()
2122 awt::Rectangle aRectEndPos
=
2123 getCharacterBounds(getCharacterCount());
2125 if (rPoint
.X
- aRectEndPos
.X
>= 0 &&
2126 rPoint
.X
- aRectEndPos
.X
< aRectEndPos
.Width
&&
2127 rPoint
.Y
- aRectEndPos
.Y
>= 0 &&
2128 rPoint
.Y
- aRectEndPos
.Y
< aRectEndPos
.Height
)
2129 return getCharacterCount();
2134 // ask core for position
2135 OSL_ENSURE( GetFrame() != nullptr, "The text frame has vanished!" );
2136 OSL_ENSURE( GetFrame()->IsTextFrame(), "The text frame has mutated!" );
2137 const SwTextFrame
* pFrame
= static_cast<const SwTextFrame
*>( GetFrame() );
2138 // construct SwPosition (where GetModelPositionForViewPoint() will put the result into)
2139 SwTextNode
* pNode
= const_cast<SwTextNode
*>(pFrame
->GetTextNodeFirst());
2140 SwPosition
aPos(*pNode
, 0);
2141 SwCursorMoveState aMoveState
;
2142 aMoveState
.m_bPosMatchesBounds
= true;
2143 const bool bSuccess
= pFrame
->GetModelPositionForViewPoint( &aPos
, aCorePoint
, &aMoveState
);
2145 TextFrameIndex nIndex
= pFrame
->MapModelToViewPos(aPos
);
2146 if (TextFrameIndex(0) < nIndex
)
2150 pFrame
->GetCharRect( aResultRect
, aPos
);
2151 bool bVert
= pFrame
->IsVertical();
2152 bool bR2L
= pFrame
->IsRightToLeft();
2154 if ( (!bVert
&& aResultRect
.Pos().getX() > aCorePoint
.getX()) ||
2155 ( bVert
&& aResultRect
.Pos().getY() > aCorePoint
.getY()) ||
2156 ( bR2L
&& aResultRect
.Right() < aCorePoint
.getX()) )
2158 SwPosition
aPosPrev(pFrame
->MapViewToModelPos(nIndex
- TextFrameIndex(1)));
2159 SwRect aResultRectPrev
;
2160 pFrame
->GetCharRect( aResultRectPrev
, aPosPrev
);
2161 if ( (!bVert
&& aResultRectPrev
.Pos().getX() < aCorePoint
.getX() && aResultRect
.Pos().getY() == aResultRectPrev
.Pos().getY()) ||
2162 ( bVert
&& aResultRectPrev
.Pos().getY() < aCorePoint
.getY() && aResultRect
.Pos().getX() == aResultRectPrev
.Pos().getX()) ||
2163 ( bR2L
&& aResultRectPrev
.Right() > aCorePoint
.getX() && aResultRect
.Pos().getY() == aResultRectPrev
.Pos().getY()) )
2171 ? GetPortionData().GetAccessiblePosition(nIndex
)
2175 OUString
SwAccessibleParagraph::getSelectedText()
2177 SolarMutexGuard aGuard
;
2181 sal_Int32 nStart
, nEnd
;
2182 bool bSelected
= GetSelection( nStart
, nEnd
);
2184 ? GetString().copy( nStart
, nEnd
- nStart
)
2188 sal_Int32
SwAccessibleParagraph::getSelectionStart()
2190 SolarMutexGuard aGuard
;
2194 sal_Int32 nStart
, nEnd
;
2195 GetSelection( nStart
, nEnd
);
2199 sal_Int32
SwAccessibleParagraph::getSelectionEnd()
2201 SolarMutexGuard aGuard
;
2205 sal_Int32 nStart
, nEnd
;
2206 GetSelection( nStart
, nEnd
);
2210 sal_Bool
SwAccessibleParagraph::setSelection( sal_Int32 nStartIndex
, sal_Int32 nEndIndex
)
2212 SolarMutexGuard aGuard
;
2216 // parameter checking
2217 sal_Int32 nLength
= GetString().getLength();
2218 if ( ! IsValidRange( nStartIndex
, nEndIndex
, nLength
) )
2220 throw lang::IndexOutOfBoundsException();
2226 SwCursorShell
* pCursorShell
= GetCursorShell();
2227 if( pCursorShell
!= nullptr )
2229 // create pam for selection
2230 SwTextFrame
const*const pFrame(static_cast<SwTextFrame
const*>(GetFrame()));
2231 TextFrameIndex
const nStart(GetPortionData().GetCoreViewPosition(nStartIndex
));
2232 TextFrameIndex
const nEnd(GetPortionData().GetCoreViewPosition(nEndIndex
));
2233 SwPaM
aPaM(pFrame
->MapViewToModelPos(nStart
));
2235 *aPaM
.GetPoint() = pFrame
->MapViewToModelPos(nEnd
);
2237 // set PaM at cursor shell
2238 bRet
= Select( aPaM
);
2244 OUString
SwAccessibleParagraph::getText()
2246 SolarMutexGuard aGuard
;
2253 OUString
SwAccessibleParagraph::getTextRange(
2254 sal_Int32 nStartIndex
, sal_Int32 nEndIndex
)
2256 SolarMutexGuard aGuard
;
2260 OUString
sText( GetString() );
2262 if ( !IsValidRange( nStartIndex
, nEndIndex
, sText
.getLength() ) )
2263 throw lang::IndexOutOfBoundsException();
2265 OrderRange( nStartIndex
, nEndIndex
);
2266 return sText
.copy(nStartIndex
, nEndIndex
-nStartIndex
);
2269 /*accessibility::*/TextSegment
SwAccessibleParagraph::getTextAtIndex( sal_Int32 nIndex
, sal_Int16 nTextType
)
2271 SolarMutexGuard aGuard
;
2275 /*accessibility::*/TextSegment aResult
;
2276 aResult
.SegmentStart
= -1;
2277 aResult
.SegmentEnd
= -1;
2279 const OUString rText
= GetString();
2280 // implement the silly specification that first position after
2281 // text must return an empty string, rather than throwing an
2282 // IndexOutOfBoundsException, except for LINE, where the last
2284 if( nIndex
== rText
.getLength() && AccessibleTextType::LINE
!= nTextType
)
2287 // with error checking
2288 i18n::Boundary aBound
;
2289 bool bWord
= GetTextBoundary( aBound
, rText
, nIndex
, nTextType
);
2291 OSL_ENSURE( aBound
.startPos
>= 0, "illegal boundary" );
2292 OSL_ENSURE( aBound
.startPos
<= aBound
.endPos
, "illegal boundary" );
2294 // return word (if present)
2297 aResult
.SegmentText
= rText
.copy( aBound
.startPos
, aBound
.endPos
- aBound
.startPos
);
2298 aResult
.SegmentStart
= aBound
.startPos
;
2299 aResult
.SegmentEnd
= aBound
.endPos
;
2305 /*accessibility::*/TextSegment
SwAccessibleParagraph::getTextBeforeIndex( sal_Int32 nIndex
, sal_Int16 nTextType
)
2307 SolarMutexGuard aGuard
;
2311 const OUString rText
= GetString();
2313 /*accessibility::*/TextSegment aResult
;
2314 aResult
.SegmentStart
= -1;
2315 aResult
.SegmentEnd
= -1;
2316 //If nIndex = 0, then nobefore text so return -1 directly.
2319 //Tab will be return when call WORDTYPE
2322 i18n::Boundary aBound
;
2323 if (nIndex
== rText
.getLength())
2324 aBound
.startPos
= aBound
.endPos
= nIndex
;
2327 bool bTmp
= GetTextBoundary( aBound
, rText
, nIndex
, nTextType
);
2330 aBound
.startPos
= aBound
.endPos
= nIndex
;
2333 // now skip to previous word
2334 if (nTextType
== AccessibleTextType::WORD
|| nTextType
== AccessibleTextType::SENTENCE
)
2336 i18n::Boundary preBound
= aBound
;
2337 while(preBound
.startPos
==aBound
.startPos
&& nIndex
> 0)
2339 nIndex
= min(nIndex
, preBound
.startPos
);
2340 if (nIndex
<= 0) break;
2341 rText
.iterateCodePoints(&nIndex
, -1);
2342 GetTextBoundary( preBound
, rText
, nIndex
, nTextType
);
2346 //Tab will be return when call WORDTYPE
2348 aResult
.SegmentText
= rText
.copy( preBound
.startPos
, preBound
.endPos
- preBound
.startPos
);
2349 aResult
.SegmentStart
= preBound
.startPos
;
2350 aResult
.SegmentEnd
= preBound
.endPos
;
2358 nIndex
= min(nIndex
, aBound
.startPos
);
2361 rText
.iterateCodePoints(&nIndex
, -1);
2362 bWord
= GetTextBoundary( aBound
, rText
, nIndex
, nTextType
);
2365 break; // exit if beginning of string is reached
2368 if (bWord
&& nIndex
<rText
.getLength())
2370 aResult
.SegmentText
= rText
.copy( aBound
.startPos
, aBound
.endPos
- aBound
.startPos
);
2371 aResult
.SegmentStart
= aBound
.startPos
;
2372 aResult
.SegmentEnd
= aBound
.endPos
;
2378 /*accessibility::*/TextSegment
SwAccessibleParagraph::getTextBehindIndex( sal_Int32 nIndex
, sal_Int16 nTextType
)
2380 SolarMutexGuard aGuard
;
2384 /*accessibility::*/TextSegment aResult
;
2385 aResult
.SegmentStart
= -1;
2386 aResult
.SegmentEnd
= -1;
2387 const OUString rText
= GetString();
2389 // implement the silly specification that first position after
2390 // text must return an empty string, rather than throwing an
2391 // IndexOutOfBoundsException
2392 if( nIndex
== rText
.getLength() )
2395 // get first word, then skip to next word
2396 i18n::Boundary aBound
;
2397 GetTextBoundary( aBound
, rText
, nIndex
, nTextType
);
2401 nIndex
= max( sal_Int32(nIndex
+1), aBound
.endPos
);
2402 if( nIndex
< rText
.getLength() )
2403 bWord
= GetTextBoundary( aBound
, rText
, nIndex
, nTextType
);
2405 break; // exit if end of string is reached
2410 aResult
.SegmentText
= rText
.copy( aBound
.startPos
, aBound
.endPos
- aBound
.startPos
);
2411 aResult
.SegmentStart
= aBound
.startPos
;
2412 aResult
.SegmentEnd
= aBound
.endPos
;
2416 sal_Bool bWord = sal_False;
2417 bWord = GetTextBoundary( aBound, rText, nIndex, nTextType );
2419 if (nTextType == AccessibleTextType::WORD)
2421 Boundary nexBound=aBound;
2423 // real current word
2424 if( nIndex <= aBound.endPos && nIndex >= aBound.startPos )
2426 while(nexBound.endPos==aBound.endPos&&nIndex<rText.getLength())
2428 // nIndex = max( (sal_Int32)(nIndex), nexBound.endPos) + 1;
2429 nIndex = max( (sal_Int32)(nIndex), nexBound.endPos) ;
2430 const sal_Unicode* pStr = rText.getStr();
2433 if( pStr[nIndex] == sal_Unicode(' ') )
2436 if( nIndex < rText.getLength() )
2438 bWord = GetTextBoundary( nexBound, rText, nIndex, nTextType );
2443 if (bWord && nIndex<rText.getLength())
2445 aResult.SegmentText = rText.copy( nexBound.startPos, nexBound.endPos - nexBound.startPos );
2446 aResult.SegmentStart = nexBound.startPos;
2447 aResult.SegmentEnd = nexBound.endPos;
2456 nIndex = max( (sal_Int32)(nIndex+1), aBound.endPos );
2457 if( nIndex < rText.getLength() )
2459 bWord = GetTextBoundary( aBound, rText, nIndex, nTextType );
2462 break; // exit if end of string is reached
2464 if (bWord && nIndex<rText.getLength())
2466 aResult.SegmentText = rText.copy( aBound.startPos, aBound.endPos - aBound.startPos );
2467 aResult.SegmentStart = aBound.startPos;
2468 aResult.SegmentEnd = aBound.endPos;
2475 sal_Bool
SwAccessibleParagraph::copyText( sal_Int32 nStartIndex
, sal_Int32 nEndIndex
)
2477 SolarMutexGuard aGuard
;
2481 // select and copy (through dispatch mechanism)
2482 setSelection( nStartIndex
, nEndIndex
);
2483 ExecuteAtViewShell( SID_COPY
);
2487 sal_Bool
SwAccessibleParagraph::scrollSubstringTo( sal_Int32 nStartIndex
,
2488 sal_Int32 nEndIndex
, AccessibleScrollType aScrollType
)
2490 SolarMutexGuard aGuard
;
2494 // parameter checking
2495 sal_Int32 nLength
= GetString().getLength();
2496 if ( ! IsValidRange( nStartIndex
, nEndIndex
, nLength
) )
2497 throw lang::IndexOutOfBoundsException();
2499 vcl::Window
*pWin
= GetWindow();
2501 throw uno::RuntimeException("no Window", static_cast<cppu::OWeakObject
*>(this));
2503 /* Start and end character bounds, in pixels, relative to the paragraph */
2504 awt::Rectangle startR
, endR
;
2505 startR
= getCharacterBounds(nStartIndex
);
2506 endR
= getCharacterBounds(nEndIndex
);
2508 /* Adjust points to fit the bounding box of both bounds. */
2509 Point
sP(std::min(startR
.X
, endR
.X
), startR
.Y
);
2510 Point
eP(std::max(startR
.X
+ startR
.Width
, endR
.X
+ endR
.Width
), endR
.Y
+ endR
.Height
);
2512 /* Offset the values relative to the view shell frame */
2513 SwRect
aFrameLogBounds( GetBounds( *(GetMap()) ) ); // twip rel to doc root
2514 Point
aFramePixPos( GetMap()->CoreToPixel( aFrameLogBounds
).TopLeft() );
2518 Point
startPoint(GetMap()->PixelToCore(sP
));
2519 Point
endPoint(GetMap()->PixelToCore(eP
));
2521 switch (aScrollType
)
2524 case AccessibleScrollType_SCROLL_TOP_LEFT
:
2526 case AccessibleScrollType_SCROLL_BOTTOM_RIGHT
:
2528 case AccessibleScrollType_SCROLL_TOP_EDGE
:
2530 case AccessibleScrollType_SCROLL_BOTTOM_EDGE
:
2532 case AccessibleScrollType_SCROLL_LEFT_EDGE
:
2534 case AccessibleScrollType_SCROLL_RIGHT_EDGE
:
2537 case AccessibleScrollType_SCROLL_ANYWHERE
:
2543 const SwRect
aRect(startPoint
, endPoint
);
2544 SwViewShell
* pViewShell
= GetMap()->GetShell();
2545 OSL_ENSURE( pViewShell
!= nullptr, "View shell expected!" );
2547 ScrollMDI(pViewShell
, aRect
, USHRT_MAX
, USHRT_MAX
);
2552 // XAccessibleEditableText
2554 sal_Bool
SwAccessibleParagraph::cutText( sal_Int32 nStartIndex
, sal_Int32 nEndIndex
)
2556 SolarMutexGuard aGuard
;
2560 if( !IsEditableState() )
2563 // select and cut (through dispatch mechanism)
2564 setSelection( nStartIndex
, nEndIndex
);
2565 ExecuteAtViewShell( SID_CUT
);
2569 sal_Bool
SwAccessibleParagraph::pasteText( sal_Int32 nIndex
)
2571 SolarMutexGuard aGuard
;
2575 if( !IsEditableState() )
2578 // select and paste (through dispatch mechanism)
2579 setSelection( nIndex
, nIndex
);
2580 ExecuteAtViewShell( SID_PASTE
);
2584 sal_Bool
SwAccessibleParagraph::deleteText( sal_Int32 nStartIndex
, sal_Int32 nEndIndex
)
2586 return replaceText( nStartIndex
, nEndIndex
, OUString() );
2589 sal_Bool
SwAccessibleParagraph::insertText( const OUString
& sText
, sal_Int32 nIndex
)
2591 return replaceText( nIndex
, nIndex
, sText
);
2594 sal_Bool
SwAccessibleParagraph::replaceText(
2595 sal_Int32 nStartIndex
, sal_Int32 nEndIndex
,
2596 const OUString
& sReplacement
)
2598 SolarMutexGuard aGuard
;
2602 const OUString
& rText
= GetString();
2604 if( !IsValidRange( nStartIndex
, nEndIndex
, rText
.getLength() ) )
2605 throw lang::IndexOutOfBoundsException();
2607 if( !IsEditableState() )
2610 // translate positions
2611 TextFrameIndex nStart
;
2612 TextFrameIndex nEnd
;
2613 bool bSuccess
= GetPortionData().GetEditableRange(
2614 nStartIndex
, nEndIndex
, nStart
, nEnd
);
2616 // edit only if the range is editable
2619 SwTextFrame
const*const pFrame(static_cast<SwTextFrame
const*>(GetFrame()));
2620 // create SwPosition for nStartIndex
2621 SwPosition
aStartPos(pFrame
->MapViewToModelPos(nStart
));
2623 // create SwPosition for nEndIndex
2624 SwPosition
aEndPos(pFrame
->MapViewToModelPos(nEnd
));
2626 // now create XTextRange as helper and set string
2627 const rtl::Reference
<SwXTextRange
> xRange(
2628 SwXTextRange::CreateXTextRange(
2629 const_cast<SwDoc
&>(pFrame
->GetDoc()), aStartPos
, &aEndPos
));
2630 xRange
->setString(sReplacement
);
2632 // delete portion data
2640 sal_Bool
SwAccessibleParagraph::setAttributes(
2641 sal_Int32 nStartIndex
,
2642 sal_Int32 nEndIndex
,
2643 const uno::Sequence
<PropertyValue
>& rAttributeSet
)
2645 SolarMutexGuard aGuard
;
2649 const OUString
& rText
= GetString();
2651 if( ! IsValidRange( nStartIndex
, nEndIndex
, rText
.getLength() ) )
2652 throw lang::IndexOutOfBoundsException();
2654 if( !IsEditableState() )
2657 // create a (dummy) text portion for the sole purpose of calling
2658 // setPropertyValue on it
2659 rtl::Reference
<SwXTextPortion
> xPortion
= CreateUnoPortion( nStartIndex
,
2662 // build sorted index array
2663 sal_Int32 nLength
= rAttributeSet
.getLength();
2664 const PropertyValue
* pPairs
= rAttributeSet
.getConstArray();
2665 std::vector
<sal_Int32
> aIndices(nLength
);
2666 std::iota(aIndices
.begin(), aIndices
.end(), 0);
2667 std::sort(aIndices
.begin(), aIndices
.end(), IndexCompare(pPairs
));
2669 // create sorted sequences according to index array
2670 uno::Sequence
< OUString
> aNames( nLength
);
2671 OUString
* pNames
= aNames
.getArray();
2672 uno::Sequence
< uno::Any
> aValues( nLength
);
2673 uno::Any
* pValues
= aValues
.getArray();
2674 for (sal_Int32 i
= 0; i
< nLength
; ++i
)
2676 const PropertyValue
& rVal
= pPairs
[aIndices
[i
]];
2677 pNames
[i
] = rVal
.Name
;
2678 pValues
[i
] = rVal
.Value
;
2682 // now set the values
2686 xPortion
->setPropertyValues( aNames
, aValues
);
2688 catch (const UnknownPropertyException
&)
2690 // error handling through return code!
2697 sal_Bool
SwAccessibleParagraph::setText( const OUString
& sText
)
2699 return replaceText(0, GetString().getLength(), sText
);
2702 // XAccessibleSelection
2704 void SwAccessibleParagraph::selectAccessibleChild(
2705 sal_Int64 nChildIndex
)
2709 m_aSelectionHelper
.selectAccessibleChild(nChildIndex
);
2712 sal_Bool
SwAccessibleParagraph::isAccessibleChildSelected(
2713 sal_Int64 nChildIndex
)
2717 return m_aSelectionHelper
.isAccessibleChildSelected(nChildIndex
);
2720 void SwAccessibleParagraph::clearAccessibleSelection( )
2725 void SwAccessibleParagraph::selectAllAccessibleChildren( )
2729 m_aSelectionHelper
.selectAllAccessibleChildren();
2732 sal_Int64
SwAccessibleParagraph::getSelectedAccessibleChildCount( )
2736 return m_aSelectionHelper
.getSelectedAccessibleChildCount();
2739 uno::Reference
<XAccessible
> SwAccessibleParagraph::getSelectedAccessibleChild(
2740 sal_Int64 nSelectedChildIndex
)
2744 return m_aSelectionHelper
.getSelectedAccessibleChild(nSelectedChildIndex
);
2747 // index has to be treated as global child index.
2748 void SwAccessibleParagraph::deselectAccessibleChild(
2749 sal_Int64 nChildIndex
)
2753 m_aSelectionHelper
.deselectAccessibleChild( nChildIndex
);
2756 // XAccessibleHypertext
2760 class SwHyperlinkIter_Impl
2762 SwTextFrame
const& m_rFrame
;
2763 sw::MergedAttrIter m_Iter
;
2764 TextFrameIndex m_nStt
;
2765 TextFrameIndex m_nEnd
;
2768 explicit SwHyperlinkIter_Impl(const SwTextFrame
& rTextFrame
);
2769 const SwTextAttr
*next(SwTextNode
const** ppNode
= nullptr);
2771 TextFrameIndex
startIdx() const { return m_nStt
; }
2772 TextFrameIndex
endIdx() const { return m_nEnd
; }
2777 SwHyperlinkIter_Impl::SwHyperlinkIter_Impl(const SwTextFrame
& rTextFrame
)
2778 : m_rFrame(rTextFrame
)
2779 , m_Iter(rTextFrame
)
2780 , m_nStt(rTextFrame
.GetOffset())
2782 const SwTextFrame
*const pFollFrame
= rTextFrame
.GetFollow();
2783 m_nEnd
= pFollFrame
? pFollFrame
->GetOffset() : TextFrameIndex(rTextFrame
.GetText().getLength());
2786 const SwTextAttr
*SwHyperlinkIter_Impl::next(SwTextNode
const** ppNode
)
2788 const SwTextAttr
*pAttr
= nullptr;
2794 SwTextNode
const* pNode(nullptr);
2795 while (SwTextAttr
const*const pHt
= m_Iter
.NextAttr(&pNode
))
2797 if (RES_TXTATR_INETFMT
== pHt
->Which())
2799 const TextFrameIndex
nHtStt(m_rFrame
.MapModelToView(pNode
, pHt
->GetStart()));
2800 const TextFrameIndex
nHtEnd(m_rFrame
.MapModelToView(pNode
, pHt
->GetAnyEnd()));
2801 if (nHtEnd
> nHtStt
&&
2802 ((nHtStt
>= m_nStt
&& nHtStt
< m_nEnd
) ||
2803 (nHtEnd
> m_nStt
&& nHtEnd
<= m_nEnd
)))
2818 sal_Int32 SAL_CALL
SwAccessibleParagraph::getHyperLinkCount()
2820 SolarMutexGuard aGuard
;
2824 sal_Int32 nCount
= 0;
2825 // #i77108# - provide hyperlinks also in editable documents.
2827 const SwTextFrame
*pTextFrame
= static_cast<const SwTextFrame
*>( GetFrame() );
2828 SwHyperlinkIter_Impl
aIter(*pTextFrame
);
2829 while( aIter
.next() )
2835 uno::Reference
< XAccessibleHyperlink
> SAL_CALL
2836 SwAccessibleParagraph::getHyperLink( sal_Int32 nLinkIndex
)
2838 SolarMutexGuard aGuard
;
2842 uno::Reference
< XAccessibleHyperlink
> xRet
;
2844 const SwTextFrame
*pTextFrame
= static_cast<const SwTextFrame
*>( GetFrame() );
2845 SwHyperlinkIter_Impl
aHIter(*pTextFrame
);
2846 SwTextNode
const* pNode(nullptr);
2847 SwTextAttr
* pHt
= const_cast<SwTextAttr
*>(aHIter
.next(&pNode
));
2848 for (sal_Int32 nTIndex
= 0; pHt
&& nTIndex
<= nLinkIndex
; ++nTIndex
)
2850 if( nTIndex
== nLinkIndex
)
2852 if (!m_pHyperTextData
)
2853 m_pHyperTextData
.reset( new SwAccessibleHyperTextData
);
2854 SwAccessibleHyperTextData::iterator aIter
=
2855 m_pHyperTextData
->find( pHt
);
2856 if (aIter
!= m_pHyperTextData
->end())
2858 xRet
= (*aIter
).second
;
2862 TextFrameIndex
const nHintStart(pTextFrame
->MapModelToView(pNode
, pHt
->GetStart()));
2863 TextFrameIndex
const nHintEnd(pTextFrame
->MapModelToView(pNode
, pHt
->GetAnyEnd()));
2864 const sal_Int32 nTmpHStt
= GetPortionData().GetAccessiblePosition(
2865 max(aHIter
.startIdx(), nHintStart
));
2866 const sal_Int32 nTmpHEnd
= GetPortionData().GetAccessiblePosition(
2867 min(aHIter
.endIdx(), nHintEnd
));
2868 xRet
= new SwAccessibleHyperlink(*pHt
,
2869 *this, nTmpHStt
, nTmpHEnd
);
2870 if (aIter
!= m_pHyperTextData
->end())
2872 (*aIter
).second
= xRet
;
2876 m_pHyperTextData
->emplace( pHt
, xRet
);
2882 // iterate next hyperlink
2883 pHt
= const_cast<SwTextAttr
*>(aHIter
.next(&pNode
));
2886 throw lang::IndexOutOfBoundsException();
2891 sal_Int32 SAL_CALL
SwAccessibleParagraph::getHyperLinkIndex( sal_Int32 nCharIndex
)
2893 SolarMutexGuard aGuard
;
2897 // parameter checking
2898 sal_Int32 nLength
= GetString().getLength();
2899 if ( ! IsValidPosition( nCharIndex
, nLength
) )
2901 throw lang::IndexOutOfBoundsException();
2904 sal_Int32 nRet
= -1;
2907 const SwTextFrame
*pTextFrame
= static_cast<const SwTextFrame
*>( GetFrame() );
2908 SwHyperlinkIter_Impl
aHIter(*pTextFrame
);
2910 const TextFrameIndex nIdx
= GetPortionData().GetCoreViewPosition(nCharIndex
);
2912 SwTextNode
const* pNode(nullptr);
2913 const SwTextAttr
*pHt
= aHIter
.next(&pNode
);
2914 while (pHt
&& (nIdx
< pTextFrame
->MapModelToView(pNode
, pHt
->GetStart())
2915 || nIdx
>= pTextFrame
->MapModelToView(pNode
, pHt
->GetAnyEnd())))
2917 pHt
= aHIter
.next(&pNode
);
2926 throw lang::IndexOutOfBoundsException();
2930 // #i71360#, #i108125# - adjustments for change tracking text markup
2931 sal_Int32 SAL_CALL
SwAccessibleParagraph::getTextMarkupCount( sal_Int32 nTextMarkupType
)
2935 std::unique_ptr
<SwTextMarkupHelper
> pTextMarkupHelper
;
2936 switch ( nTextMarkupType
)
2938 case text::TextMarkupType::TRACK_CHANGE_INSERTION
:
2939 case text::TextMarkupType::TRACK_CHANGE_DELETION
:
2940 case text::TextMarkupType::TRACK_CHANGE_FORMATCHANGE
:
2942 pTextMarkupHelper
.reset( new SwTextMarkupHelper(
2944 *(mpParaChangeTrackInfo
->getChangeTrackingTextMarkupList( nTextMarkupType
) )) );
2949 SwTextFrame
const*const pFrame(static_cast<SwTextFrame
const*>(GetFrame()));
2950 pTextMarkupHelper
.reset(new SwTextMarkupHelper(GetPortionData(), *pFrame
));
2954 return pTextMarkupHelper
->getTextMarkupCount( nTextMarkupType
);
2957 //MSAA Extension Implementation in app module
2958 sal_Bool SAL_CALL
SwAccessibleParagraph::scrollToPosition( const css::awt::Point
&, sal_Bool
)
2963 sal_Int32 SAL_CALL
SwAccessibleParagraph::getSelectedPortionCount( )
2967 sal_Int32 nSelected
= 0;
2968 SwPaM
* pCursor
= GetCursor( true );
2969 if( pCursor
!= nullptr )
2971 // get SwPosition for my node
2972 SwTextFrame
const*const pFrame(static_cast<SwTextFrame
const*>(GetFrame()));
2973 SwNodeOffset
nFirstNode(pFrame
->GetTextNodeFirst()->GetIndex());
2974 SwNodeOffset nLastNode
;
2975 if (sw::MergedPara
const*const pMerged
= pFrame
->GetMergedPara())
2977 nLastNode
= pMerged
->pLastNode
->GetIndex();
2981 nLastNode
= nFirstNode
;
2984 // iterate over ring
2985 for(SwPaM
& rTmpCursor
: pCursor
->GetRingContainer())
2987 // ignore, if no mark
2988 if( rTmpCursor
.HasMark() )
2990 // check whether frame's node(s) are 'inside' pCursor
2991 SwPosition
* pStart
= rTmpCursor
.Start();
2992 SwNodeOffset nStartIndex
= pStart
->GetNodeIndex();
2993 SwPosition
* pEnd
= rTmpCursor
.End();
2994 SwNodeOffset nEndIndex
= pEnd
->GetNodeIndex();
2995 if ((nStartIndex
<= nLastNode
) && (nFirstNode
<= nEndIndex
))
2999 // else: this PaM doesn't point to this paragraph
3001 // else: this PaM is collapsed and doesn't select anything
3008 sal_Int32 SAL_CALL
SwAccessibleParagraph::getSeletedPositionStart( sal_Int32 nSelectedPortionIndex
)
3010 SolarMutexGuard aGuard
;
3014 sal_Int32 nStart
=-1, nEnd
=-1;
3015 /*sal_Bool bSelected = */GetSelectionAtIndex(&nSelectedPortionIndex
, nStart
, nEnd
);
3019 sal_Int32 SAL_CALL
SwAccessibleParagraph::getSeletedPositionEnd( sal_Int32 nSelectedPortionIndex
)
3021 SolarMutexGuard aGuard
;
3025 sal_Int32 nStart
=-1, nEnd
=-1;
3026 /*sal_Bool bSelected = */GetSelectionAtIndex(&nSelectedPortionIndex
, nStart
, nEnd
);
3030 sal_Bool SAL_CALL
SwAccessibleParagraph::removeSelection( sal_Int32 selectionIndex
)
3034 if(selectionIndex
< 0) return false;
3036 sal_Int32 nSelected
= selectionIndex
;
3038 // get the selection, and test whether it affects our text node
3039 SwPaM
* pCursor
= GetCursor( true );
3041 if( pCursor
!= nullptr )
3045 // get SwPosition for my node
3046 SwTextFrame
const*const pFrame(static_cast<SwTextFrame
const*>(GetFrame()));
3047 SwNodeOffset
nFirstNode(pFrame
->GetTextNodeFirst()->GetIndex());
3048 SwNodeOffset nLastNode
;
3049 if (sw::MergedPara
const*const pMerged
= pFrame
->GetMergedPara())
3051 nLastNode
= pMerged
->pLastNode
->GetIndex();
3055 nLastNode
= nFirstNode
;
3058 // iterate over ring
3059 SwPaM
* pRingStart
= pCursor
;
3062 // ignore, if no mark
3063 if( pCursor
->HasMark() )
3065 // check whether frame's node(s) are 'inside' pCursor
3066 SwPosition
* pStart
= pCursor
->Start();
3067 SwNodeOffset nStartIndex
= pStart
->GetNodeIndex();
3068 SwPosition
* pEnd
= pCursor
->End();
3069 SwNodeOffset nEndIndex
= pEnd
->GetNodeIndex();
3070 if ((nStartIndex
<= nLastNode
) && (nFirstNode
<= nEndIndex
))
3072 if( nSelected
== 0 )
3074 pCursor
->MoveTo(nullptr);
3084 // else: this PaM is collapsed and doesn't select anything
3086 pCursor
= pCursor
->GetNext();
3088 while( !bRet
&& (pCursor
!= pRingStart
) );
3093 sal_Int32 SAL_CALL
SwAccessibleParagraph::addSelection( sal_Int32
, sal_Int32 startOffset
, sal_Int32 endOffset
)
3095 SolarMutexGuard aGuard
;
3099 // parameter checking
3100 sal_Int32 nLength
= GetString().getLength();
3101 if ( ! IsValidRange( startOffset
, endOffset
, nLength
) )
3103 throw lang::IndexOutOfBoundsException();
3106 sal_Int32 nSelectedCount
= getSelectedPortionCount();
3107 for ( sal_Int32 i
= nSelectedCount
; i
>= 0 ; i
--)
3109 sal_Int32 nStart
, nEnd
;
3110 bool bSelected
= GetSelectionAtIndex(&i
, nStart
, nEnd
);
3115 if (( startOffset
>=nStart
&& startOffset
<=nEnd
) || //startOffset in a selection
3116 ( endOffset
>=nStart
&& endOffset
<=nEnd
) || //endOffset in a selection
3117 ( startOffset
<= nStart
&& endOffset
>=nEnd
) || //start and end include the old selection
3118 ( startOffset
>= nStart
&& endOffset
<=nEnd
) )
3126 if (( startOffset
>=nEnd
&& startOffset
<=nStart
) || //startOffset in a selection
3127 ( endOffset
>=nEnd
&& endOffset
<=nStart
) || //endOffset in a selection
3128 ( startOffset
<= nStart
&& endOffset
>=nEnd
) || //start and end include the old selection
3129 ( startOffset
>= nStart
&& endOffset
<=nEnd
) )
3140 SwCursorShell
* pCursorShell
= GetCursorShell();
3141 if( pCursorShell
!= nullptr )
3143 // create pam for selection
3144 pCursorShell
->StartAction();
3145 SwTextFrame
const*const pFrame(static_cast<SwTextFrame
const*>(GetFrame()));
3146 SwPaM
* aPaM
= pCursorShell
->CreateCursor();
3148 *aPaM
->GetPoint() = pFrame
->MapViewToModelPos(GetPortionData().GetCoreViewPosition(startOffset
));
3149 *aPaM
->GetMark() = pFrame
->MapViewToModelPos(GetPortionData().GetCoreViewPosition(endOffset
));
3150 pCursorShell
->EndAction();
3156 /*accessibility::*/TextSegment SAL_CALL
3157 SwAccessibleParagraph::getTextMarkup( sal_Int32 nTextMarkupIndex
,
3158 sal_Int32 nTextMarkupType
)
3162 std::unique_ptr
<SwTextMarkupHelper
> pTextMarkupHelper
;
3163 switch ( nTextMarkupType
)
3165 case text::TextMarkupType::TRACK_CHANGE_INSERTION
:
3166 case text::TextMarkupType::TRACK_CHANGE_DELETION
:
3167 case text::TextMarkupType::TRACK_CHANGE_FORMATCHANGE
:
3169 pTextMarkupHelper
.reset( new SwTextMarkupHelper(
3171 *(mpParaChangeTrackInfo
->getChangeTrackingTextMarkupList( nTextMarkupType
) )) );
3176 SwTextFrame
const*const pFrame(static_cast<SwTextFrame
const*>(GetFrame()));
3177 pTextMarkupHelper
.reset(new SwTextMarkupHelper(GetPortionData(), *pFrame
));
3181 return pTextMarkupHelper
->getTextMarkup( nTextMarkupIndex
, nTextMarkupType
);
3184 uno::Sequence
< /*accessibility::*/TextSegment
> SAL_CALL
3185 SwAccessibleParagraph::getTextMarkupAtIndex( sal_Int32 nCharIndex
,
3186 sal_Int32 nTextMarkupType
)
3190 // parameter checking
3191 const sal_Int32 nLength
= GetString().getLength();
3192 if ( ! IsValidPosition( nCharIndex
, nLength
) )
3194 throw lang::IndexOutOfBoundsException();
3197 std::unique_ptr
<SwTextMarkupHelper
> pTextMarkupHelper
;
3198 switch ( nTextMarkupType
)
3200 case text::TextMarkupType::TRACK_CHANGE_INSERTION
:
3201 case text::TextMarkupType::TRACK_CHANGE_DELETION
:
3202 case text::TextMarkupType::TRACK_CHANGE_FORMATCHANGE
:
3204 pTextMarkupHelper
.reset( new SwTextMarkupHelper(
3206 *(mpParaChangeTrackInfo
->getChangeTrackingTextMarkupList( nTextMarkupType
) )) );
3211 SwTextFrame
const*const pFrame(static_cast<SwTextFrame
const*>(GetFrame()));
3212 pTextMarkupHelper
.reset(new SwTextMarkupHelper(GetPortionData(), *pFrame
));
3216 return pTextMarkupHelper
->getTextMarkupAtIndex( nCharIndex
, nTextMarkupType
);
3220 sal_Int32 SAL_CALL
SwAccessibleParagraph::getLineNumberAtIndex( sal_Int32 nIndex
)
3224 // parameter checking
3225 const sal_Int32 nLength
= GetString().getLength();
3226 if ( ! IsValidPosition( nIndex
, nLength
) )
3228 throw lang::IndexOutOfBoundsException();
3231 const sal_Int32 nLineNo
= GetPortionData().GetLineNo( nIndex
);
3235 /*accessibility::*/TextSegment SAL_CALL
3236 SwAccessibleParagraph::getTextAtLineNumber( sal_Int32 nLineNo
)
3240 // parameter checking
3242 nLineNo
>= GetPortionData().GetLineCount() )
3244 throw lang::IndexOutOfBoundsException();
3247 i18n::Boundary aLineBound
;
3248 GetPortionData().GetBoundaryOfLine( nLineNo
, aLineBound
);
3250 /*accessibility::*/TextSegment aTextAtLine
;
3251 const OUString rText
= GetString();
3252 aTextAtLine
.SegmentText
= rText
.copy( aLineBound
.startPos
,
3253 aLineBound
.endPos
- aLineBound
.startPos
);
3254 aTextAtLine
.SegmentStart
= aLineBound
.startPos
;
3255 aTextAtLine
.SegmentEnd
= aLineBound
.endPos
;
3260 /*accessibility::*/TextSegment SAL_CALL
SwAccessibleParagraph::getTextAtLineWithCaret()
3264 const sal_Int32 nLineNoOfCaret
= getNumberOfLineWithCaret();
3266 if ( nLineNoOfCaret
>= 0 &&
3267 nLineNoOfCaret
< GetPortionData().GetLineCount() )
3269 return getTextAtLineNumber( nLineNoOfCaret
);
3272 return /*accessibility::*/TextSegment();
3275 sal_Int32 SAL_CALL
SwAccessibleParagraph::getNumberOfLineWithCaret()
3279 const sal_Int32 nCaretPos
= getCaretPosition();
3280 const sal_Int32 nLength
= GetString().getLength();
3281 if ( !IsValidPosition( nCaretPos
, nLength
) )
3286 sal_Int32 nLineNo
= GetPortionData().GetLineNo( nCaretPos
);
3288 // special handling for cursor positioned at end of text line via End key
3289 if ( nCaretPos
!= 0 )
3291 i18n::Boundary aLineBound
;
3292 GetPortionData().GetBoundaryOfLine( nLineNo
, aLineBound
);
3293 if ( nCaretPos
== aLineBound
.startPos
)
3295 SwCursorShell
* pCursorShell
= SwAccessibleParagraph::GetCursorShell();
3296 if ( pCursorShell
!= nullptr )
3298 const awt::Rectangle aCharRect
= getCharacterBounds( nCaretPos
);
3300 const SwRect
& aCursorCoreRect
= pCursorShell
->GetCharRect();
3301 // translate core coordinates into accessibility coordinates
3302 vcl::Window
*pWin
= GetWindow();
3305 throw uno::RuntimeException("no Window", static_cast<cppu::OWeakObject
*>(this));
3308 tools::Rectangle
aScreenRect( GetMap()->CoreToPixel( aCursorCoreRect
));
3310 SwRect
aFrameLogBounds( GetBounds( *(GetMap()) ) ); // twip rel to doc root
3311 Point
aFramePixPos( GetMap()->CoreToPixel( aFrameLogBounds
).TopLeft() );
3312 aScreenRect
.Move( -aFramePixPos
.getX(), -aFramePixPos
.getY() );
3314 // convert into AWT Rectangle
3315 const awt::Rectangle
aCursorRect( aScreenRect
.Left(),
3317 aScreenRect
.GetWidth(),
3318 aScreenRect
.GetHeight() );
3320 if ( aCharRect
.X
!= aCursorRect
.X
||
3321 aCharRect
.Y
!= aCursorRect
.Y
)
3333 void SwAccessibleParagraph::Notify(SfxBroadcaster
&, const SfxHint
&)
3335 mpParaChangeTrackInfo
->reset();
3338 bool SwAccessibleParagraph::GetSelectionAtIndex(
3339 sal_Int32
* pSelection
, sal_Int32
& nStart
, sal_Int32
& nEnd
)
3341 if (pSelection
&& *pSelection
< 0) return false;
3347 // get the selection, and test whether it affects our text node
3348 SwPaM
* pCursor
= GetCursor( true );
3349 if( pCursor
!= nullptr )
3351 // get SwPosition for my node
3352 SwTextFrame
const*const pFrame(static_cast<SwTextFrame
const*>(GetFrame()));
3353 SwNodeOffset
nFirstNode(pFrame
->GetTextNodeFirst()->GetIndex());
3354 SwNodeOffset nLastNode
;
3355 if (sw::MergedPara
const*const pMerged
= pFrame
->GetMergedPara())
3357 nLastNode
= pMerged
->pLastNode
->GetIndex();
3361 nLastNode
= nFirstNode
;
3364 // iterate over ring
3365 for(SwPaM
& rTmpCursor
: pCursor
->GetRingContainer())
3367 // ignore, if no mark
3368 if( rTmpCursor
.HasMark() )
3370 // check whether frame's node(s) are 'inside' pCursor
3371 SwPosition
* pStart
= rTmpCursor
.Start();
3372 SwNodeOffset nStartIndex
= pStart
->GetNodeIndex();
3373 SwPosition
* pEnd
= rTmpCursor
.End();
3374 SwNodeOffset nEndIndex
= pEnd
->GetNodeIndex();
3375 if ((nStartIndex
<= nLastNode
) && (nFirstNode
<= nEndIndex
))
3377 if (!pSelection
|| *pSelection
== 0)
3379 // translate start and end positions
3382 sal_Int32 nLocalStart
= -1;
3383 if (nStartIndex
< nFirstNode
)
3385 // selection starts in previous node:
3386 // then our local selection starts with the paragraph
3391 assert(FrameContainsNode(*pFrame
, nStartIndex
));
3393 // selection starts in this node:
3394 // then check whether it's before or inside our part of
3395 // the paragraph, and if so, get the proper position
3396 const TextFrameIndex nCoreStart
=
3397 pFrame
->MapModelToViewPos(*pStart
);
3399 GetPortionData().GetFirstValidCorePosition() )
3403 else if( nCoreStart
<=
3404 GetPortionData().GetLastValidCorePosition() )
3407 !GetPortionData().IsValidCorePosition(
3410 "problem determining valid core position");
3413 GetPortionData().GetAccessiblePosition(
3419 sal_Int32 nLocalEnd
= -1;
3420 if (nLastNode
< nEndIndex
)
3422 // selection ends in following node:
3423 // then our local selection extends to the end
3424 nLocalEnd
= GetPortionData().GetAccessibleString().
3429 assert(FrameContainsNode(*pFrame
, nEndIndex
));
3431 // selection ends in this node: then select everything
3432 // before our part of the node
3433 const TextFrameIndex nCoreEnd
=
3434 pFrame
->MapModelToViewPos(*pEnd
);
3436 GetPortionData().GetLastValidCorePosition() )
3438 // selection extends beyond out part of this para
3439 nLocalEnd
= GetPortionData().GetAccessibleString().
3442 else if( nCoreEnd
>=
3443 GetPortionData().GetFirstValidCorePosition() )
3445 // selection is inside our part of this para
3447 !GetPortionData().IsValidCorePosition(
3450 "problem determining valid core position");
3452 nLocalEnd
= GetPortionData().GetAccessiblePosition(
3457 if( ( nLocalStart
!= -1 ) && ( nLocalEnd
!= -1 ) )
3459 nStart
= nLocalStart
;
3463 } // if hit the index
3469 // else: this PaM doesn't point to this paragraph
3471 // else: this PaM is collapsed and doesn't select anything
3476 // else: nocursor -> no selection
3478 if (pSelection
&& bRet
)
3480 sal_Int32 nCaretPos
= GetCaretPos();
3481 if( nStart
== nCaretPos
)
3482 std::swap( nStart
, nEnd
);
3487 sal_Int16 SAL_CALL
SwAccessibleParagraph::getAccessibleRole()
3491 //Get the real heading level, Heading1 ~ Heading10
3492 if (m_nHeadingLevel
> 0)
3494 return AccessibleRole::HEADING
;
3498 return AccessibleRole::PARAGRAPH
;
3502 //Get the real heading level, Heading1 ~ Heading10
3503 sal_Int32
SwAccessibleParagraph::GetRealHeadingLevel()
3505 uno::Reference
< css::beans::XPropertySet
> xPortion
= CreateUnoPortion( 0, 0 );
3506 uno::Any styleAny
= xPortion
->getPropertyValue( "ParaStyleName" );
3508 if (styleAny
>>= sValue
)
3510 sal_Int32 length
= sValue
.getLength();
3511 if (length
== 9 || length
== 10)
3513 if (sValue
.startsWith("Heading"))
3515 std::u16string_view intStr
= sValue
.subView(8);
3516 sal_Int32 headingLevel
= o3tl::toInt32(intStr
);
3517 return headingLevel
;
3524 uno::Any SAL_CALL
SwAccessibleParagraph::getExtendedAttributes()
3529 OUString
strHeading("heading-level:");
3530 if( m_nHeadingLevel
>= 0 )
3531 strHeading
+= OUString::number(m_nHeadingLevel
);
3532 // tdf#84102: expose the same attribute with the name "level"
3533 strHeading
+= ";level:";
3534 if( m_nHeadingLevel
>= 0 )
3535 strHeading
+= OUString::number(m_nHeadingLevel
);
3543 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */