Version 7.6.3.2-android, tag libreoffice-7.6.3.2-android
[LibreOffice.git] / sw / source / core / access / accpara.cxx
blobc0599dbe36959f655acefb74539e5e31530f5045
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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 .
20 #include <memory>
21 #include <numeric>
22 #include <txtfrm.hxx>
23 #include <flyfrm.hxx>
24 #include <mdiexp.hxx>
25 #include <ndtxt.hxx>
26 #include <pam.hxx>
27 #include <unotextrange.hxx>
28 #include <unocrsrhelper.hxx>
29 #include <crstate.hxx>
30 #include <accmap.hxx>
31 #include <fesh.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>
55 #include <doc.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>
62 #include <algorithm>
63 #include <docufld.hxx>
64 #include <txtfld.hxx>
65 #include <fmtfld.hxx>
66 #include <modcfg.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>
73 #include <wrong.hxx>
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>
80 #include <unomap.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>
93 #include <reffld.hxx>
94 #include <flddat.hxx>
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;
106 using std::max;
107 using std::min;
108 using std::sort;
110 namespace com::sun::star::text {
111 class XText;
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()
129 sal_Int32 nRet = -1;
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()));
138 assert(pTextFrame);
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
145 // of the paragraph
146 const TextFrameIndex nIndex = pTextFrame->MapModelToViewPos(*pPoint);
147 if(!GetPortionData().IsValidCorePosition( nIndex ) ||
148 (GetPortionData().IsZeroCorePositionData()
149 && nIndex == TextFrameIndex(0)))
151 bool bFormat = pTextFrame->HasPara();
152 if(bFormat)
154 ClearPortionData();
155 UpdatePortionData();
158 if( GetPortionData().IsValidCorePosition( nIndex ) )
160 // Yes, it's us!
161 // consider that cursor/caret is in front of the list label
162 if ( pCaret->IsInFrontOfLabel() )
164 nRet = 0;
166 else
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
181 return nRet;
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);
197 if( !pFESh ||
198 !(pFESh->IsFrameSelected() || pFESh->IsObjSelected() > 0) )
200 // get the selection, and test whether it affects our text node
201 pCursor = pCursorShell->GetCursor( false /* ??? */ );
205 return pCursor;
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 );
219 // MULTILINE
220 rStateSet |= AccessibleStateType::MULTI_LINE;
222 if (GetCursorShell())
224 // MULTISELECTABLE
225 rStateSet |= AccessibleStateType::MULTI_SELECTABLE;
226 // FOCUSABLE
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()));
233 assert(pFrame);
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() );
250 ClearPortionData();
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,
262 aEvent.OldValue,
263 aEvent.NewValue);
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());
271 if(pPara)
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();
287 bool bOldIsHeading;
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 )
305 return;
307 OUString sNewDesc( GetDescription() );
308 OUString sOldDesc;
310 std::scoped_lock aGuard( m_Mutex );
311 sOldDesc = m_sDesc;
312 if( m_sDesc != sNewDesc )
313 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();
332 sal_Int32 nOld;
334 std::scoped_lock aGuard( m_Mutex );
335 nOld = m_nOldCaretPos;
336 m_nOldCaretPos = nNew;
338 if( -1 != 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();
347 if( nOld == nNew )
348 return;
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
364 sal_Int32 nStart=0;
365 sal_Int32 nEnd =0;
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();
381 if( pWin )
383 sal_Int32 nPos;
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#
421 EndListeningAll();
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!" );
435 if (!pFrame)
436 ClearPortionData();
437 else
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!" );
463 if( !pSfxShell )
464 return;
466 SfxViewFrame& rFrame = pSfxShell->GetViewFrame();
467 SfxDispatcher *pDispatcher = rFrame.GetDispatcher();
468 OSL_ENSURE( pDispatcher != nullptr, "Dispatcher expected!" );
469 if( !pDispatcher )
470 return;
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);
489 // create UNO cursor
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 );
526 if ( pCrSr )
528 SwPosition* pStart = pCrSr->Start();
529 pRedline = pStart->GetDoc().getIDocumentRedlineAccess().GetRedline(*pStart, nullptr);
532 return pRedline;
535 // text boundaries
537 bool SwAccessibleParagraph::GetCharBoundary(
538 i18n::Boundary& rBound,
539 std::u16string_view text,
540 sal_Int32 nPos )
542 if( GetPortionData().FillBoundaryIFDateField( rBound, nPos) )
543 return true;
545 auto nPosEnd = nPos;
546 o3tl::iterateCodePoints(text, &nPosEnd);
548 rBound.startPos = nPos;
549 rBound.endPos = nPosEnd;
551 return true;
554 bool SwAccessibleParagraph::GetWordBoundary(
555 i18n::Boundary& rBound,
556 const OUString& rText,
557 sal_Int32 nPos )
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 );
575 return true;
578 bool SwAccessibleParagraph::GetSentenceBoundary(
579 i18n::Boundary& rBound,
580 const OUString& rText,
581 sal_Int32 nPos )
583 const sal_Unicode* pStr = rText.getStr();
584 while( nPos < rText.getLength() && pStr[nPos] == u' ' )
585 nPos++;
587 GetPortionData().GetSentenceBoundary( rBound, nPos );
588 return true;
591 bool SwAccessibleParagraph::GetLineBoundary(
592 i18n::Boundary& rBound,
593 std::u16string_view aText,
594 sal_Int32 nPos )
596 if( sal_Int32(aText.size()) == nPos )
597 GetPortionData().GetLastLineBoundary( rBound );
598 else
599 GetPortionData().GetLineBoundary( rBound, nPos );
600 return true;
603 bool SwAccessibleParagraph::GetParagraphBoundary(
604 i18n::Boundary& rBound,
605 std::u16string_view aText )
607 rBound.startPos = 0;
608 rBound.endPos = aText.size();
609 return true;
612 bool SwAccessibleParagraph::GetAttributeBoundary(
613 i18n::Boundary& rBound,
614 sal_Int32 nPos )
616 GetPortionData().GetAttributeBoundary( rBound, nPos );
617 return true;
620 bool SwAccessibleParagraph::GetGlyphBoundary(
621 i18n::Boundary& rBound,
622 const OUString& rText,
623 sal_Int32 nPos )
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;
636 sal_Int32 nDone = 0;
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" );
645 return bRet;
648 bool SwAccessibleParagraph::GetTextBoundary(
649 i18n::Boundary& rBound,
650 const OUString& rText,
651 sal_Int32 nPos,
652 sal_Int16 nTextType )
654 // error checking
655 if( !( AccessibleTextType::LINE == nTextType
656 ? IsValidPosition( nPos, rText.getLength() )
657 : IsValidChar( nPos, rText.getLength() ) ) )
658 throw lang::IndexOutOfBoundsException();
660 bool bRet;
662 switch( nTextType )
664 case AccessibleTextType::WORD:
665 bRet = GetWordBoundary(rBound, rText, nPos);
666 break;
668 case AccessibleTextType::SENTENCE:
669 bRet = GetSentenceBoundary( rBound, rText, nPos );
670 break;
672 case AccessibleTextType::PARAGRAPH:
673 bRet = GetParagraphBoundary( rBound, rText );
674 break;
676 case AccessibleTextType::CHARACTER:
677 bRet = GetCharBoundary( rBound, rText, nPos );
678 break;
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);
684 else
685 bRet = GetLineBoundary( rBound, rText, nPos );
686 break;
688 case AccessibleTextType::ATTRIBUTE_RUN:
689 bRet = GetAttributeBoundary( rBound, nPos );
690 break;
692 case AccessibleTextType::GLYPH:
693 bRet = GetGlyphBoundary( rBound, rText, nPos );
694 break;
696 default:
697 throw lang::IllegalArgumentException( );
700 return bRet;
703 OUString SAL_CALL SwAccessibleParagraph::getAccessibleDescription()
705 SolarMutexGuard aGuard;
707 ThrowIfDisposed();
709 std::scoped_lock aGuard2( m_Mutex );
710 if( m_sDesc.isEmpty() )
711 m_sDesc = GetDescription();
713 return m_sDesc;
716 lang::Locale SAL_CALL SwAccessibleParagraph::getLocale()
718 SolarMutexGuard aGuard;
720 const SwTextFrame *pTextFrame = GetFrame()->DynCastTextFrame();
721 if( !pTextFrame )
723 throw uno::RuntimeException("no SwTextFrame", static_cast<cppu::OWeakObject*>(this));
726 lang::Locale aLoc(g_pBreakIt->GetLocale(pTextFrame->GetLangOfChar(TextFrameIndex(0), 0, true)));
728 return aLoc;
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;
736 ThrowIfDisposed();
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");
743 if ( pTextFrame )
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,
750 aSequence );
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,
759 aSequence );
760 pHelper->AddRelation( aAccRel );
764 return pHelper;
767 void SAL_CALL SwAccessibleParagraph::grabFocus()
769 SolarMutexGuard aGuard;
771 ThrowIfDisposed();
773 // get cursor shell
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
788 Select( aPaM );
792 // ->#i13955#
793 vcl::Window * pWindow = GetWindow();
795 if (pWindow != nullptr)
796 pWindow->GrabFocus();
797 // <-#i13955#
800 // #i71385#
801 static bool lcl_GetBackgroundColor( Color & rColor,
802 const SwFrame* pFrame,
803 SwCursorShell* pCursorSh )
805 const SvxBrushItem* pBackgroundBrush = nullptr;
806 std::optional<Color> xSectionTOXColor;
807 SwRect aDummyRect;
808 drawinglayer::attribute::SdrAllFillAttributesHelperPtr aFillAttributes;
810 if ( pFrame &&
811 pFrame->GetBackgroundBrush( aFillAttributes, pBackgroundBrush, xSectionTOXColor, aDummyRect, false, /*bConsiderTextBox=*/false ) )
813 if ( xSectionTOXColor )
815 rColor = *xSectionTOXColor;
816 return true;
818 else
820 rColor = pBackgroundBrush->GetColor();
821 return true;
824 else if ( pCursorSh )
826 rColor = pCursorSh->Imp()->GetRetoucheColor();
827 return true;
830 return false;
833 sal_Int32 SAL_CALL SwAccessibleParagraph::getForeground()
835 SolarMutexGuard g;
837 Color aBackgroundCol;
839 if ( lcl_GetBackgroundColor( aBackgroundCol, GetFrame(), GetCursorShell() ) )
841 if ( aBackgroundCol.IsDark() )
843 return sal_Int32(COL_WHITE);
845 else
847 return sal_Int32(COL_BLACK);
851 return SwAccessibleContext::getForeground();
854 sal_Int32 SAL_CALL SwAccessibleParagraph::getBackground()
856 SolarMutexGuard g;
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,
891 UNO_NAME_CHAR_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,
904 return aNames;
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,
920 UNO_NAME_TABSTOPS,
922 return aNames;
925 // XInterface
927 uno::Any SwAccessibleParagraph::queryInterface( const uno::Type& rType )
929 uno::Any aRet;
930 if ( rType == cppu::UnoType<XAccessibleText>::get())
932 uno::Reference<XAccessibleText> aAccText = static_cast<XAccessibleText *>(*this); // resolve ambiguity
933 aRet <<= aAccText;
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;
943 aRet <<= aAccSel;
945 else if ( rType == cppu::UnoType<XAccessibleHypertext>::get())
947 uno::Reference<XAccessibleHypertext> aAccHyp = this;
948 aRet <<= aAccHyp;
950 // #i63870#
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;
957 // #i89175#
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;
978 aRet <<= xAttr;
980 else
982 aRet = SwAccessibleContext::queryInterface(rType);
985 return aRet;
988 // XTypeProvider
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>();
1008 // XAccessibleText
1010 sal_Int32 SwAccessibleParagraph::getCaretPosition()
1012 SolarMutexGuard aGuard;
1014 ThrowIfDisposed();
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;
1022 if( -1 != nRet )
1024 ::rtl::Reference < SwAccessibleContext > xThis( this );
1025 GetMap()->SetCursorContext( xThis );
1028 return nRet;
1031 sal_Bool SAL_CALL SwAccessibleParagraph::setCaretPosition( sal_Int32 nIndex )
1033 SolarMutexGuard aGuard;
1035 ThrowIfDisposed();
1037 // parameter checking
1038 sal_Int32 nLength = GetString().getLength();
1039 if ( ! IsValidPosition( nIndex, nLength ) )
1041 throw lang::IndexOutOfBoundsException();
1044 bool bRet = false;
1046 // get cursor shell
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 );
1060 return bRet;
1063 sal_Unicode SwAccessibleParagraph::getCharacter( sal_Int32 nIndex )
1065 SolarMutexGuard aGuard;
1067 ThrowIfDisposed();
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;
1082 ThrowIfDisposed();
1084 /* #i12332# The position after the string needs special treatment.
1085 IsValidChar -> IsValidPosition
1087 if( ! (IsValidPosition( nIndex, GetString().getLength() ) ) )
1088 throw lang::IndexOutOfBoundsException();
1090 /* #i12332# */
1091 bool bBehindText = false;
1092 if ( nIndex == GetString().getLength() )
1093 bBehindText = true;
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
1106 paragraph. */
1107 const TextFrameIndex nPos = bBehindText
1108 ? TextFrameIndex(pFrame->GetText().getLength())
1109 : GetPortionData().FillSpecialPos(nIndex, aSpecialPos, aMoveState.m_pSpecialPos );
1111 // call GetCharRect
1112 SwRect aCoreRect;
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();
1119 if( nStrLen > 0 )
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();
1129 if (!pWin)
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();
1145 return tabs;
1148 namespace {
1150 struct IndexCompare
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;
1165 SwFieldMgr aMgr;
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));
1181 break;
1183 else if (pHt->Which() == RES_TXTATR_REFMARK
1184 && (nFieldIndex-- == 0))
1186 strTypeName = "set reference";
1190 if (pTextField)
1192 const SwField* pField = pTextField->GetFormatField().GetField();
1193 if (pField)
1195 strTypeName = SwFieldType::GetTypeStr(pField->GetTypeId());
1196 const SwFieldIds nWhich = pField->GetTyp()->Which();
1197 OUString sEntry;
1198 sal_uInt32 subType = 0;
1199 switch (nWhich)
1201 case SwFieldIds::DocStat:
1202 subType = static_cast<const SwDocStatField*>(pField)->GetSubType();
1203 break;
1204 case SwFieldIds::GetRef:
1206 switch( pField->GetSubType() )
1208 case REF_BOOKMARK:
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";
1215 else
1216 sEntry = "Bookmarks";
1218 break;
1219 case REF_FOOTNOTE:
1220 sEntry = "Footnotes";
1221 break;
1222 case REF_ENDNOTE:
1223 sEntry = "Endnotes";
1224 break;
1225 case REF_SETREFATTR:
1226 sEntry = "Insert Reference";
1227 break;
1228 case REF_SEQUENCEFLD:
1229 sEntry = static_cast<const SwGetRefField*>(pField)->GetSetRefName();
1230 break;
1232 //Get format string
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;
1244 break;
1245 case SwFieldIds::DateTime:
1246 subType = static_cast<const SwDateTimeField*>(pField)->GetSubType();
1247 break;
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;
1261 break;
1262 case SwFieldIds::ExtUser:
1263 subType = static_cast<const SwExtUserField*>(pField)->GetSubType();
1264 break;
1265 case SwFieldIds::HiddenText:
1266 case SwFieldIds::SetExp:
1268 sEntry = pField->GetTyp()->GetName();
1269 if (sEntry.getLength() > 0)
1271 strTypeName += "-" + sEntry;
1274 break;
1275 case SwFieldIds::DocInfo:
1276 subType = pField->GetSubType();
1277 subType &= 0x00ff;
1278 break;
1279 case SwFieldIds::RefPageSet:
1281 const SwRefPageSetField* pRPld = static_cast<const SwRefPageSetField*>(pField);
1282 bool bOn = pRPld->IsOn();
1283 strTypeName += "-";
1284 if (bOn)
1285 strTypeName += "on";
1286 else
1287 strTypeName += "off";
1289 break;
1290 case SwFieldIds::Author:
1292 strTypeName += "-" + aMgr.GetFormatStr(pField->GetTypeId(), pField->GetFormat() & 0xff);
1294 break;
1295 default: break;
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
1313 strTypeName += "-";
1314 sEntry = aMgr.GetFormatStr(pField->GetTypeId(), nExSub/0x0100-1);
1315 strTypeName += sEntry;
1318 else
1320 strTypeName += "-" + sEntry;
1326 return strTypeName;
1329 // #i63870# - re-implement method on behalf of methods
1330 // <_getDefaultAttributesImpl(..)> and <_getRunAttributesImpl(..)>
1331 uno::Sequence<PropertyValue> SwAccessibleParagraph::getCharacterAttributes(
1332 sal_Int32 nIndex,
1333 const uno::Sequence< OUString >& aRequestedAttributes )
1336 SolarMutexGuard aGuard;
1338 ThrowIfDisposed();
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() );
1362 sal_Int32 i = 0;
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;
1371 else
1373 aValues[i] = rDefEntry.second;
1375 ++i;
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;
1391 ++i;
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]];
1424 return aNewValues;
1427 return comphelper::containerToSequence(aValues);
1430 static void SetPutRecursive(SfxItemSet &targetSet, const SfxItemSet &sourceSet)
1432 const SfxItemSet *const pParentSet = sourceSet.GetParent();
1433 if (pParentSet)
1434 SetPutRecursive(targetSet, *pParentSet);
1435 targetSet.Put(sourceSet);
1438 // #i63870#
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> );
1455 else
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 );
1493 if ( pItem )
1495 uno::Any aVal;
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;
1508 // #i72800#
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;
1525 // #i73371#
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;
1550 else
1552 nVal = text::WritingMode2::LR_TB;
1554 rPropVal.Value <<= nVal;
1555 aDefAttrSeq[rPropVal.Name] = rPropVal;
1556 break;
1559 if ( pUpperFrame->IsFlyFrame() )
1561 pUpperFrame = static_cast<const SwFlyFrame*>(pUpperFrame)->GetAnchorFrame();
1563 else
1565 pUpperFrame = pUpperFrame->GetUpper();
1573 if ( !aRequestedAttributes.hasElements() )
1575 rDefAttrSeq = aDefAttrSeq;
1577 else
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;
1595 ThrowIfDisposed();
1597 tAccParaPropValMap aDefAttrSeq;
1598 _getDefaultAttributesImpl( aRequestedAttributes, aDefAttrSeq );
1600 // #i92233#
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; });
1611 // #i92233#
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;
1625 return aValues;
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() );
1649 // #i82637#
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 )
1687 uno::Any aVal;
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;
1711 else
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(
1726 sal_Int32 nIndex,
1727 const uno::Sequence< OUString >& aRequestedAttributes )
1729 SolarMutexGuard aGuard;
1731 ThrowIfDisposed();
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());
1753 SfxItemSetFixed<
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 );
1779 if ( pItem )
1781 uno::Any aVal;
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();
1811 if ( pRedline )
1814 const SwModuleOptions *pOpt = SW_MOD()->GetModuleConfig();
1815 AuthorCharAttr aChangeAttr;
1816 if ( pOpt )
1818 switch( pRedline->GetType())
1820 case RedlineType::Insert:
1821 aChangeAttr = pOpt->GetInsertAuthorAttr();
1822 break;
1823 case RedlineType::Delete:
1824 aChangeAttr = pOpt->GetDeletedAuthorAttr();
1825 break;
1826 case RedlineType::Format:
1827 aChangeAttr = pOpt->GetFormatAuthorAttr();
1828 break;
1829 default: break;
1832 switch( aChangeAttr.m_nItemId )
1834 case SID_ATTR_CHAR_WEIGHT:
1835 ChangeAttr.Name = UNO_NAME_CHAR_WEIGHT;
1836 ChangeAttr.Value <<= awt::FontWeight::BOLD;
1837 break;
1838 case SID_ATTR_CHAR_POSTURE:
1839 ChangeAttr.Name = UNO_NAME_CHAR_POSTURE;
1840 ChangeAttr.Value <<= awt::FontSlant_ITALIC; //char posture
1841 break;
1842 case SID_ATTR_CHAR_STRIKEOUT:
1843 ChangeAttr.Name = UNO_NAME_CHAR_STRIKEOUT;
1844 ChangeAttr.Value <<= awt::FontStrikeout::SINGLE; //char strikeout
1845 break;
1846 case SID_ATTR_CHAR_UNDERLINE:
1847 ChangeAttr.Name = UNO_NAME_CHAR_UNDERLINE;
1848 ChangeAttr.Value <<= aChangeAttr.m_nAttr; //underline line
1849 break;
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;
1858 else
1859 ChangeAttrColor.Value <<= aChangeAttr.m_nColor;
1861 else
1863 ChangeAttrColor.Name = UNO_NAME_CHAR_COLOR;
1864 if( aChangeAttr.m_nColor == COL_TRANSPARENT )//char color
1865 ChangeAttrColor.Value <<= COL_BLUE;
1866 else
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;
1895 continue;
1898 if (rValue.Name == ChangeAttrColor.Name )
1900 rValue.Value = ChangeAttrColor.Value;
1901 continue;
1904 //back color
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;
1918 continue;
1921 //char color
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;
1939 continue;
1942 // UnderLine
1943 if (rValue.Name == UNO_NAME_CHAR_UNDERLINE)
1945 //misspelled word
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;
1953 sal_Int32 nLen = 1;
1954 if (pWrongList->InWrongWord(nBegin, nLen) && !pTextNode->IsSymbolAt(nBegin))
1956 rValue.Value <<= sal_uInt16(LINESTYLE_WAVE);
1960 continue;
1963 // UnderLineColor
1964 if (rValue.Name == UNO_NAME_CHAR_UNDERLINE_COLOR)
1966 //misspelled word
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;
1974 sal_Int32 nLen = 1;
1975 if (pWrongList->InWrongWord(nBegin, nLen) && !pTextNode->IsSymbolAt(nBegin))
1977 rValue.Value <<= sal_Int32(0x00ff0000);
1978 continue;
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;
1996 continue;
1999 //tab stop
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;
2010 else
2011 ts.Position = 48;
2012 ts.DecimalChar = ' ';
2013 ts.FillChar = ' ';
2014 ts.Alignment = css::style::TabAlign_LEFT;
2015 tabs = { ts };
2017 rValue.Value <<= tabs;
2018 continue;
2021 //footnote & endnote
2022 if (rValue.Name == UNO_NAME_CHAR_ESCAPEMENT)
2024 if ( GetPortionData().IsIndexInFootnode(nIndex) )
2026 rValue.Value <<= sal_Int32(101);
2028 continue;
2033 awt::Rectangle SwAccessibleParagraph::getCharacterBounds(
2034 sal_Int32 nIndex )
2036 SolarMutexGuard aGuard;
2038 ThrowIfDisposed();
2040 // #i12332# The position after the string needs special treatment.
2041 // IsValidChar -> IsValidPosition
2042 if( ! (IsValidPosition( nIndex, GetString().getLength() ) ) )
2043 throw lang::IndexOutOfBoundsException();
2045 // #i12332#
2046 bool bBehindText = false;
2047 if ( nIndex == GetString().getLength() )
2048 bBehindText = true;
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
2061 paragraph. */
2062 const TextFrameIndex nPos = bBehindText
2063 ? TextFrameIndex(pFrame->GetText().getLength())
2064 : GetPortionData().FillSpecialPos(nIndex, aSpecialPos, aMoveState.m_pSpecialPos );
2066 // call GetCharRect
2067 SwRect aCoreRect;
2068 SwPosition aPosition(pFrame->MapViewToModelPos(nPos));
2069 GetFrame()->GetCharRect( aCoreRect, aPosition, &aMoveState );
2071 // translate core coordinates into accessibility coordinates
2072 vcl::Window *pWin = GetWindow();
2073 if (!pWin)
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;
2094 ThrowIfDisposed();
2096 return GetString().getLength();
2099 sal_Int32 SwAccessibleParagraph::getIndexAtPoint( const awt::Point& rPoint )
2101 SolarMutexGuard aGuard;
2103 ThrowIfDisposed();
2105 // construct Point (translate into layout coordinates)
2106 vcl::Window *pWin = GetWindow();
2107 if (!pWin)
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();
2131 return -1;
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)
2148 assert(bSuccess);
2149 SwRect aResultRect;
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()) )
2165 --nIndex;
2170 return bSuccess
2171 ? GetPortionData().GetAccessiblePosition(nIndex)
2172 : -1;
2175 OUString SwAccessibleParagraph::getSelectedText()
2177 SolarMutexGuard aGuard;
2179 ThrowIfDisposed();
2181 sal_Int32 nStart, nEnd;
2182 bool bSelected = GetSelection( nStart, nEnd );
2183 return bSelected
2184 ? GetString().copy( nStart, nEnd - nStart )
2185 : OUString();
2188 sal_Int32 SwAccessibleParagraph::getSelectionStart()
2190 SolarMutexGuard aGuard;
2192 ThrowIfDisposed();
2194 sal_Int32 nStart, nEnd;
2195 GetSelection( nStart, nEnd );
2196 return nStart;
2199 sal_Int32 SwAccessibleParagraph::getSelectionEnd()
2201 SolarMutexGuard aGuard;
2203 ThrowIfDisposed();
2205 sal_Int32 nStart, nEnd;
2206 GetSelection( nStart, nEnd );
2207 return nEnd;
2210 sal_Bool SwAccessibleParagraph::setSelection( sal_Int32 nStartIndex, sal_Int32 nEndIndex )
2212 SolarMutexGuard aGuard;
2214 ThrowIfDisposed();
2216 // parameter checking
2217 sal_Int32 nLength = GetString().getLength();
2218 if ( ! IsValidRange( nStartIndex, nEndIndex, nLength ) )
2220 throw lang::IndexOutOfBoundsException();
2223 bool bRet = false;
2225 // get cursor shell
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));
2234 aPaM.SetMark();
2235 *aPaM.GetPoint() = pFrame->MapViewToModelPos(nEnd);
2237 // set PaM at cursor shell
2238 bRet = Select( aPaM );
2241 return bRet;
2244 OUString SwAccessibleParagraph::getText()
2246 SolarMutexGuard aGuard;
2248 ThrowIfDisposed();
2250 return GetString();
2253 OUString SwAccessibleParagraph::getTextRange(
2254 sal_Int32 nStartIndex, sal_Int32 nEndIndex )
2256 SolarMutexGuard aGuard;
2258 ThrowIfDisposed();
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;
2273 ThrowIfDisposed();
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
2283 // line is returned
2284 if( nIndex == rText.getLength() && AccessibleTextType::LINE != nTextType )
2285 return aResult;
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)
2295 if ( bWord )
2297 aResult.SegmentText = rText.copy( aBound.startPos, aBound.endPos - aBound.startPos );
2298 aResult.SegmentStart = aBound.startPos;
2299 aResult.SegmentEnd = aBound.endPos;
2302 return aResult;
2305 /*accessibility::*/TextSegment SwAccessibleParagraph::getTextBeforeIndex( sal_Int32 nIndex, sal_Int16 nTextType )
2307 SolarMutexGuard aGuard;
2309 ThrowIfDisposed();
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.
2317 if( nIndex == 0 )
2318 return aResult;
2319 //Tab will be return when call WORDTYPE
2321 // get starting pos
2322 i18n::Boundary aBound;
2323 if (nIndex == rText.getLength())
2324 aBound.startPos = aBound.endPos = nIndex;
2325 else
2327 bool bTmp = GetTextBoundary( aBound, rText, nIndex, nTextType );
2329 if ( ! bTmp )
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 );
2344 //if (nIndex>0)
2345 if (nIndex>=0)
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;
2353 else
2355 bool bWord = false;
2356 while( !bWord )
2358 nIndex = min(nIndex, aBound.startPos);
2359 if (nIndex > 0)
2361 rText.iterateCodePoints(&nIndex, -1);
2362 bWord = GetTextBoundary( aBound, rText, nIndex, nTextType );
2364 else
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;
2375 return aResult;
2378 /*accessibility::*/TextSegment SwAccessibleParagraph::getTextBehindIndex( sal_Int32 nIndex, sal_Int16 nTextType )
2380 SolarMutexGuard aGuard;
2382 ThrowIfDisposed();
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() )
2393 return aResult;
2395 // get first word, then skip to next word
2396 i18n::Boundary aBound;
2397 GetTextBoundary( aBound, rText, nIndex, nTextType );
2398 bool bWord = false;
2399 while( !bWord )
2401 nIndex = max( sal_Int32(nIndex+1), aBound.endPos );
2402 if( nIndex < rText.getLength() )
2403 bWord = GetTextBoundary( aBound, rText, nIndex, nTextType );
2404 else
2405 break; // exit if end of string is reached
2408 if ( bWord )
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();
2431 if (pStr)
2433 if( pStr[nIndex] == sal_Unicode(' ') )
2434 nIndex++;
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;
2451 else
2453 bWord = sal_False;
2454 while( !bWord )
2456 nIndex = max( (sal_Int32)(nIndex+1), aBound.endPos );
2457 if( nIndex < rText.getLength() )
2459 bWord = GetTextBoundary( aBound, rText, nIndex, nTextType );
2461 else
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;
2472 return aResult;
2475 sal_Bool SwAccessibleParagraph::copyText( sal_Int32 nStartIndex, sal_Int32 nEndIndex )
2477 SolarMutexGuard aGuard;
2479 ThrowIfDisposed();
2481 // select and copy (through dispatch mechanism)
2482 setSelection( nStartIndex, nEndIndex );
2483 ExecuteAtViewShell( SID_COPY );
2484 return true;
2487 sal_Bool SwAccessibleParagraph::scrollSubstringTo( sal_Int32 nStartIndex,
2488 sal_Int32 nEndIndex, AccessibleScrollType aScrollType )
2490 SolarMutexGuard aGuard;
2492 ThrowIfDisposed();
2494 // parameter checking
2495 sal_Int32 nLength = GetString().getLength();
2496 if ( ! IsValidRange( nStartIndex, nEndIndex, nLength ) )
2497 throw lang::IndexOutOfBoundsException();
2499 vcl::Window *pWin = GetWindow();
2500 if ( ! pWin )
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() );
2515 sP += aFramePixPos;
2516 eP += aFramePixPos;
2518 Point startPoint(GetMap()->PixelToCore(sP));
2519 Point endPoint(GetMap()->PixelToCore(eP));
2521 switch (aScrollType)
2523 #ifdef notyet
2524 case AccessibleScrollType_SCROLL_TOP_LEFT:
2525 break;
2526 case AccessibleScrollType_SCROLL_BOTTOM_RIGHT:
2527 break;
2528 case AccessibleScrollType_SCROLL_TOP_EDGE:
2529 break;
2530 case AccessibleScrollType_SCROLL_BOTTOM_EDGE:
2531 break;
2532 case AccessibleScrollType_SCROLL_LEFT_EDGE:
2533 break;
2534 case AccessibleScrollType_SCROLL_RIGHT_EDGE:
2535 break;
2536 #endif
2537 case AccessibleScrollType_SCROLL_ANYWHERE:
2538 break;
2539 default:
2540 return false;
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);
2549 return true;
2552 // XAccessibleEditableText
2554 sal_Bool SwAccessibleParagraph::cutText( sal_Int32 nStartIndex, sal_Int32 nEndIndex )
2556 SolarMutexGuard aGuard;
2558 ThrowIfDisposed();
2560 if( !IsEditableState() )
2561 return false;
2563 // select and cut (through dispatch mechanism)
2564 setSelection( nStartIndex, nEndIndex );
2565 ExecuteAtViewShell( SID_CUT );
2566 return true;
2569 sal_Bool SwAccessibleParagraph::pasteText( sal_Int32 nIndex )
2571 SolarMutexGuard aGuard;
2573 ThrowIfDisposed();
2575 if( !IsEditableState() )
2576 return false;
2578 // select and paste (through dispatch mechanism)
2579 setSelection( nIndex, nIndex );
2580 ExecuteAtViewShell( SID_PASTE );
2581 return true;
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;
2600 ThrowIfDisposed();
2602 const OUString& rText = GetString();
2604 if( !IsValidRange( nStartIndex, nEndIndex, rText.getLength() ) )
2605 throw lang::IndexOutOfBoundsException();
2607 if( !IsEditableState() )
2608 return false;
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
2617 if( bSuccess )
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
2633 ClearPortionData();
2636 return bSuccess;
2640 sal_Bool SwAccessibleParagraph::setAttributes(
2641 sal_Int32 nStartIndex,
2642 sal_Int32 nEndIndex,
2643 const uno::Sequence<PropertyValue>& rAttributeSet )
2645 SolarMutexGuard aGuard;
2647 ThrowIfDisposed();
2649 const OUString& rText = GetString();
2651 if( ! IsValidRange( nStartIndex, nEndIndex, rText.getLength() ) )
2652 throw lang::IndexOutOfBoundsException();
2654 if( !IsEditableState() )
2655 return false;
2657 // create a (dummy) text portion for the sole purpose of calling
2658 // setPropertyValue on it
2659 rtl::Reference<SwXTextPortion> xPortion = CreateUnoPortion( nStartIndex,
2660 nEndIndex );
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;
2680 aIndices.clear();
2682 // now set the values
2683 bool bRet = true;
2686 xPortion->setPropertyValues( aNames, aValues );
2688 catch (const UnknownPropertyException&)
2690 // error handling through return code!
2691 bRet = false;
2694 return bRet;
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 )
2707 ThrowIfDisposed();
2709 m_aSelectionHelper.selectAccessibleChild(nChildIndex);
2712 sal_Bool SwAccessibleParagraph::isAccessibleChildSelected(
2713 sal_Int64 nChildIndex )
2715 ThrowIfDisposed();
2717 return m_aSelectionHelper.isAccessibleChildSelected(nChildIndex);
2720 void SwAccessibleParagraph::clearAccessibleSelection( )
2722 ThrowIfDisposed();
2725 void SwAccessibleParagraph::selectAllAccessibleChildren( )
2727 ThrowIfDisposed();
2729 m_aSelectionHelper.selectAllAccessibleChildren();
2732 sal_Int64 SwAccessibleParagraph::getSelectedAccessibleChildCount( )
2734 ThrowIfDisposed();
2736 return m_aSelectionHelper.getSelectedAccessibleChildCount();
2739 uno::Reference<XAccessible> SwAccessibleParagraph::getSelectedAccessibleChild(
2740 sal_Int64 nSelectedChildIndex )
2742 ThrowIfDisposed();
2744 return m_aSelectionHelper.getSelectedAccessibleChild(nSelectedChildIndex);
2747 // index has to be treated as global child index.
2748 void SwAccessibleParagraph::deselectAccessibleChild(
2749 sal_Int64 nChildIndex )
2751 ThrowIfDisposed();
2753 m_aSelectionHelper.deselectAccessibleChild( nChildIndex );
2756 // XAccessibleHypertext
2758 namespace {
2760 class SwHyperlinkIter_Impl
2762 SwTextFrame const& m_rFrame;
2763 sw::MergedAttrIter m_Iter;
2764 TextFrameIndex m_nStt;
2765 TextFrameIndex m_nEnd;
2767 public:
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;
2789 if (ppNode)
2791 *ppNode = 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)))
2805 pAttr = pHt;
2806 if (ppNode)
2808 *ppNode = pNode;
2810 break;
2815 return pAttr;
2818 sal_Int32 SAL_CALL SwAccessibleParagraph::getHyperLinkCount()
2820 SolarMutexGuard aGuard;
2822 ThrowIfDisposed();
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() )
2830 nCount++;
2832 return nCount;
2835 uno::Reference< XAccessibleHyperlink > SAL_CALL
2836 SwAccessibleParagraph::getHyperLink( sal_Int32 nLinkIndex )
2838 SolarMutexGuard aGuard;
2840 ThrowIfDisposed();
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 )
2851 { // found
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;
2860 if (!xRet.is())
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;
2874 else
2876 m_pHyperTextData->emplace( pHt, xRet );
2879 break;
2882 // iterate next hyperlink
2883 pHt = const_cast<SwTextAttr*>(aHIter.next(&pNode));
2885 if( !xRet.is() )
2886 throw lang::IndexOutOfBoundsException();
2888 return xRet;
2891 sal_Int32 SAL_CALL SwAccessibleParagraph::getHyperLinkIndex( sal_Int32 nCharIndex )
2893 SolarMutexGuard aGuard;
2895 ThrowIfDisposed();
2897 // parameter checking
2898 sal_Int32 nLength = GetString().getLength();
2899 if ( ! IsValidPosition( nCharIndex, nLength ) )
2901 throw lang::IndexOutOfBoundsException();
2904 sal_Int32 nRet = -1;
2905 // #i77108#
2907 const SwTextFrame *pTextFrame = static_cast<const SwTextFrame*>( GetFrame() );
2908 SwHyperlinkIter_Impl aHIter(*pTextFrame);
2910 const TextFrameIndex nIdx = GetPortionData().GetCoreViewPosition(nCharIndex);
2911 sal_Int32 nPos = 0;
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);
2918 nPos++;
2921 if( pHt )
2922 nRet = nPos;
2925 if (nRet == -1)
2926 throw lang::IndexOutOfBoundsException();
2927 return nRet;
2930 // #i71360#, #i108125# - adjustments for change tracking text markup
2931 sal_Int32 SAL_CALL SwAccessibleParagraph::getTextMarkupCount( sal_Int32 nTextMarkupType )
2933 SolarMutexGuard g;
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(
2943 GetPortionData(),
2944 *(mpParaChangeTrackInfo->getChangeTrackingTextMarkupList( nTextMarkupType ) )) );
2946 break;
2947 default:
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 )
2960 return false;
2963 sal_Int32 SAL_CALL SwAccessibleParagraph::getSelectedPortionCount( )
2965 SolarMutexGuard g;
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();
2979 else
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))
2997 nSelected++;
2999 // else: this PaM doesn't point to this paragraph
3001 // else: this PaM is collapsed and doesn't select anything
3004 return nSelected;
3008 sal_Int32 SAL_CALL SwAccessibleParagraph::getSeletedPositionStart( sal_Int32 nSelectedPortionIndex )
3010 SolarMutexGuard aGuard;
3012 ThrowIfDisposed();
3014 sal_Int32 nStart=-1, nEnd=-1;
3015 /*sal_Bool bSelected = */GetSelectionAtIndex(&nSelectedPortionIndex, nStart, nEnd );
3016 return nStart;
3019 sal_Int32 SAL_CALL SwAccessibleParagraph::getSeletedPositionEnd( sal_Int32 nSelectedPortionIndex )
3021 SolarMutexGuard aGuard;
3023 ThrowIfDisposed();
3025 sal_Int32 nStart=-1, nEnd=-1;
3026 /*sal_Bool bSelected = */GetSelectionAtIndex(&nSelectedPortionIndex, nStart, nEnd );
3027 return nEnd;
3030 sal_Bool SAL_CALL SwAccessibleParagraph::removeSelection( sal_Int32 selectionIndex )
3032 SolarMutexGuard g;
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 )
3043 bool bRet = false;
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();
3053 else
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);
3075 delete pCursor;
3076 bRet = true;
3078 else
3080 nSelected--;
3084 // else: this PaM is collapsed and doesn't select anything
3085 if(!bRet)
3086 pCursor = pCursor->GetNext();
3088 while( !bRet && (pCursor != pRingStart) );
3090 return true;
3093 sal_Int32 SAL_CALL SwAccessibleParagraph::addSelection( sal_Int32, sal_Int32 startOffset, sal_Int32 endOffset)
3095 SolarMutexGuard aGuard;
3097 ThrowIfDisposed();
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 );
3111 if(bSelected)
3113 if(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) )
3120 removeSelection(i);
3124 else
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) )
3132 removeSelection(i);
3139 // get cursor shell
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();
3147 aPaM->SetMark();
3148 *aPaM->GetPoint() = pFrame->MapViewToModelPos(GetPortionData().GetCoreViewPosition(startOffset));
3149 *aPaM->GetMark() = pFrame->MapViewToModelPos(GetPortionData().GetCoreViewPosition(endOffset));
3150 pCursorShell->EndAction();
3153 return 0;
3156 /*accessibility::*/TextSegment SAL_CALL
3157 SwAccessibleParagraph::getTextMarkup( sal_Int32 nTextMarkupIndex,
3158 sal_Int32 nTextMarkupType )
3160 SolarMutexGuard g;
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(
3170 GetPortionData(),
3171 *(mpParaChangeTrackInfo->getChangeTrackingTextMarkupList( nTextMarkupType ) )) );
3173 break;
3174 default:
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 )
3188 SolarMutexGuard g;
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(
3205 GetPortionData(),
3206 *(mpParaChangeTrackInfo->getChangeTrackingTextMarkupList( nTextMarkupType ) )) );
3208 break;
3209 default:
3211 SwTextFrame const*const pFrame(static_cast<SwTextFrame const*>(GetFrame()));
3212 pTextMarkupHelper.reset(new SwTextMarkupHelper(GetPortionData(), *pFrame));
3216 return pTextMarkupHelper->getTextMarkupAtIndex( nCharIndex, nTextMarkupType );
3219 // #i89175#
3220 sal_Int32 SAL_CALL SwAccessibleParagraph::getLineNumberAtIndex( sal_Int32 nIndex )
3222 SolarMutexGuard g;
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 );
3232 return nLineNo;
3235 /*accessibility::*/TextSegment SAL_CALL
3236 SwAccessibleParagraph::getTextAtLineNumber( sal_Int32 nLineNo )
3238 SolarMutexGuard g;
3240 // parameter checking
3241 if ( nLineNo < 0 ||
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;
3257 return aTextAtLine;
3260 /*accessibility::*/TextSegment SAL_CALL SwAccessibleParagraph::getTextAtLineWithCaret()
3262 SolarMutexGuard g;
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()
3277 SolarMutexGuard g;
3279 const sal_Int32 nCaretPos = getCaretPosition();
3280 const sal_Int32 nLength = GetString().getLength();
3281 if ( !IsValidPosition( nCaretPos, nLength ) )
3283 return -1;
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();
3303 if (!pWin)
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(),
3316 aScreenRect.Top(),
3317 aScreenRect.GetWidth(),
3318 aScreenRect.GetHeight() );
3320 if ( aCharRect.X != aCursorRect.X ||
3321 aCharRect.Y != aCursorRect.Y )
3323 --nLineNo;
3329 return nLineNo;
3332 // #i108125#
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;
3343 bool bRet = false;
3344 nStart = -1;
3345 nEnd = -1;
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();
3359 else
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
3381 // start position
3382 sal_Int32 nLocalStart = -1;
3383 if (nStartIndex < nFirstNode)
3385 // selection starts in previous node:
3386 // then our local selection starts with the paragraph
3387 nLocalStart = 0;
3389 else
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);
3398 if( nCoreStart <
3399 GetPortionData().GetFirstValidCorePosition() )
3401 nLocalStart = 0;
3403 else if( nCoreStart <=
3404 GetPortionData().GetLastValidCorePosition() )
3406 SAL_WARN_IF(
3407 !GetPortionData().IsValidCorePosition(
3408 nCoreStart),
3409 "sw.a11y",
3410 "problem determining valid core position");
3412 nLocalStart =
3413 GetPortionData().GetAccessiblePosition(
3414 nCoreStart );
3418 // end position
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().
3425 getLength();
3427 else
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);
3435 if( nCoreEnd >
3436 GetPortionData().GetLastValidCorePosition() )
3438 // selection extends beyond out part of this para
3439 nLocalEnd = GetPortionData().GetAccessibleString().
3440 getLength();
3442 else if( nCoreEnd >=
3443 GetPortionData().GetFirstValidCorePosition() )
3445 // selection is inside our part of this para
3446 SAL_WARN_IF(
3447 !GetPortionData().IsValidCorePosition(
3448 nCoreEnd),
3449 "sw.a11y",
3450 "problem determining valid core position");
3452 nLocalEnd = GetPortionData().GetAccessiblePosition(
3453 nCoreEnd );
3457 if( ( nLocalStart != -1 ) && ( nLocalEnd != -1 ) )
3459 nStart = nLocalStart;
3460 nEnd = nLocalEnd;
3461 bRet = true;
3463 } // if hit the index
3464 else
3466 --*pSelection;
3469 // else: this PaM doesn't point to this paragraph
3471 // else: this PaM is collapsed and doesn't select anything
3472 if(bRet)
3473 break;
3476 // else: nocursor -> no selection
3478 if (pSelection && bRet)
3480 sal_Int32 nCaretPos = GetCaretPos();
3481 if( nStart == nCaretPos )
3482 std::swap( nStart, nEnd );
3484 return bRet;
3487 sal_Int16 SAL_CALL SwAccessibleParagraph::getAccessibleRole()
3489 SolarMutexGuard g;
3491 //Get the real heading level, Heading1 ~ Heading10
3492 if (m_nHeadingLevel > 0)
3494 return AccessibleRole::HEADING;
3496 else
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" );
3507 OUString sValue;
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;
3521 return -1;
3524 uno::Any SAL_CALL SwAccessibleParagraph::getExtendedAttributes()
3526 SolarMutexGuard g;
3528 uno::Any Ret;
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);
3536 strHeading += ";";
3538 Ret <<= strHeading;
3540 return Ret;
3543 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */