tdf#162786, tdf#161947: Add support for setuptools and pip
[LibreOffice.git] / vcl / source / accessibility / textwindowaccessibility.cxx
blob2868e865b35ab4becd9005d6041b0313912af5cb
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 <sal/config.h>
22 #include <accessibility/textwindowaccessibility.hxx>
24 #include <sal/log.hxx>
26 #include <com/sun/star/accessibility/AccessibleEventId.hpp>
27 #include <com/sun/star/accessibility/AccessibleRelationType.hpp>
28 #include <com/sun/star/accessibility/AccessibleRole.hpp>
29 #include <com/sun/star/accessibility/AccessibleStateType.hpp>
30 #include <com/sun/star/awt/FontWeight.hpp>
31 #include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
32 #include <com/sun/star/lang/WrappedTargetRuntimeException.hpp>
33 #include <com/sun/star/i18n/Boundary.hpp>
34 #include <cppuhelper/exc_hlp.hxx>
35 #include <comphelper/accessiblecontexthelper.hxx>
36 #include <comphelper/accessibleeventnotifier.hxx>
37 #include <o3tl/safeint.hxx>
38 #include <unotools/accessiblerelationsethelper.hxx>
39 #include <utility>
40 #include <vcl/svapp.hxx>
41 #include <vcl/txtattr.hxx>
42 #include <vcl/unohelp.hxx>
43 #include <vcl/window.hxx>
44 #include <comphelper/diagnose_ex.hxx>
45 #include <comphelper/sequence.hxx>
47 #include <algorithm>
48 #include <numeric>
49 #include <vector>
51 namespace accessibility
53 void SfxListenerGuard::startListening(::SfxBroadcaster & rNotifier)
55 assert(m_pNotifier == nullptr && "called more than once");
56 m_pNotifier = &rNotifier;
57 m_rListener.StartListening(*m_pNotifier, DuplicateHandling::Prevent);
60 void SfxListenerGuard::endListening()
62 if (m_pNotifier != nullptr)
64 m_rListener.EndListening(*m_pNotifier);
65 m_pNotifier = nullptr;
69 void WindowListenerGuard::startListening(vcl::Window & rNotifier)
71 assert(m_pNotifier == nullptr && "called more than once");
72 m_pNotifier = &rNotifier;
73 m_pNotifier->AddEventListener(m_aListener);
76 void WindowListenerGuard::endListening()
78 if (m_pNotifier)
80 m_pNotifier->RemoveEventListener(m_aListener);
81 m_pNotifier = nullptr;
85 Paragraph::Paragraph(::rtl::Reference< Document > xDocument,
86 Paragraphs::size_type nNumber):
87 ParagraphBase(m_aMutex),
88 m_xDocument(std::move(xDocument)),
89 m_nNumber(nNumber),
90 m_nClientId(0)
92 m_aParagraphText = m_xDocument->retrieveParagraphText(this);
95 void
96 Paragraph::numberChanged(bool bIncremented)
98 if (bIncremented)
99 ++m_nNumber;
100 else
101 --m_nNumber;
104 void Paragraph::textChanged()
106 OUString aParagraphText = implGetText();
107 css::uno::Any aOldValue, aNewValue;
108 if ( implInitTextChangedEvent( m_aParagraphText, aParagraphText, aOldValue, aNewValue ) )
110 m_aParagraphText = aParagraphText;
111 notifyEvent(css::accessibility::AccessibleEventId::
112 TEXT_CHANGED,
113 aOldValue, aNewValue);
117 void Paragraph::notifyEvent(::sal_Int16 nEventId,
118 css::uno::Any const & rOldValue,
119 css::uno::Any const & rNewValue)
121 if (m_nClientId)
122 comphelper::AccessibleEventNotifier::addEvent( m_nClientId, css::accessibility::AccessibleEventObject(
123 getXWeak(),
124 nEventId, rNewValue, rOldValue, -1) );
127 // virtual
128 css::uno::Reference< css::accessibility::XAccessibleContext > SAL_CALL
129 Paragraph::getAccessibleContext()
131 checkDisposed();
132 return this;
135 // virtual
136 sal_Int64 SAL_CALL Paragraph::getAccessibleChildCount()
138 checkDisposed();
139 return 0;
142 // virtual
143 css::uno::Reference< css::accessibility::XAccessible > SAL_CALL
144 Paragraph::getAccessibleChild(sal_Int64)
146 checkDisposed();
147 throw css::lang::IndexOutOfBoundsException(
148 u"textwindowaccessibility.cxx:"
149 " Paragraph::getAccessibleChild"_ustr,
150 getXWeak());
153 // virtual
154 css::uno::Reference< css::accessibility::XAccessible > SAL_CALL
155 Paragraph::getAccessibleParent()
157 checkDisposed();
158 return m_xDocument->getAccessible();
161 // virtual
162 sal_Int64 SAL_CALL Paragraph::getAccessibleIndexInParent()
164 checkDisposed();
165 return m_xDocument->retrieveParagraphIndex(this);
168 // virtual
169 ::sal_Int16 SAL_CALL Paragraph::getAccessibleRole()
171 checkDisposed();
172 return css::accessibility::AccessibleRole::PARAGRAPH;
175 // virtual
176 OUString SAL_CALL Paragraph::getAccessibleDescription()
178 checkDisposed();
179 return OUString();
182 // virtual
183 OUString SAL_CALL Paragraph::getAccessibleName()
185 checkDisposed();
186 return OUString();
189 // virtual
190 css::uno::Reference< css::accessibility::XAccessibleRelationSet >
191 SAL_CALL Paragraph::getAccessibleRelationSet()
193 checkDisposed();
194 return m_xDocument->retrieveParagraphRelationSet( this );
197 // virtual
198 sal_Int64 SAL_CALL Paragraph::getAccessibleStateSet()
200 checkDisposed();
202 // FIXME Notification of changes (STATE_CHANGED) missing when
203 // m_rView.IsReadOnly() changes:
204 return m_xDocument->retrieveParagraphState(this);
207 // virtual
208 css::lang::Locale SAL_CALL Paragraph::getLocale()
210 checkDisposed();
211 return m_xDocument->retrieveLocale();
214 // virtual
215 sal_Bool SAL_CALL Paragraph::containsPoint(css::awt::Point const & rPoint)
217 checkDisposed();
218 css::awt::Rectangle aRect(m_xDocument->retrieveParagraphBounds(this,
219 false));
220 return rPoint.X >= 0 && rPoint.X < aRect.Width
221 && rPoint.Y >= 0 && rPoint.Y < aRect.Height;
224 // virtual
225 css::uno::Reference< css::accessibility::XAccessible > SAL_CALL
226 Paragraph::getAccessibleAtPoint(css::awt::Point const &)
228 checkDisposed();
229 return nullptr;
232 // virtual
233 css::awt::Rectangle SAL_CALL Paragraph::getBounds()
235 checkDisposed();
236 return m_xDocument->retrieveParagraphBounds(this, false);
239 // virtual
240 css::awt::Point SAL_CALL Paragraph::getLocation()
242 checkDisposed();
243 css::awt::Rectangle aRect(m_xDocument->retrieveParagraphBounds(this,
244 false));
245 return css::awt::Point(aRect.X, aRect.Y);
248 // virtual
249 css::awt::Point SAL_CALL Paragraph::getLocationOnScreen()
251 checkDisposed();
252 css::awt::Rectangle aRect(m_xDocument->retrieveParagraphBounds(this,
253 true));
254 return css::awt::Point(aRect.X, aRect.Y);
257 // virtual
258 css::awt::Size SAL_CALL Paragraph::getSize()
260 checkDisposed();
261 css::awt::Rectangle aRect(m_xDocument->retrieveParagraphBounds(this,
262 false));
263 return css::awt::Size(aRect.Width, aRect.Height);
266 // virtual
267 void SAL_CALL Paragraph::grabFocus()
269 checkDisposed();
270 VclPtr<vcl::Window> pWindow = m_xDocument->GetWindow();
271 if ( pWindow )
273 pWindow->GrabFocus();
277 m_xDocument->changeParagraphSelection(this, 0, 0);
279 catch (const css::lang::IndexOutOfBoundsException &)
281 TOOLS_INFO_EXCEPTION("accessibility", "Paragraph::grabFocus: caught unexpected");
285 // virtual
286 sal_Int32 SAL_CALL Paragraph::getForeground()
288 return 0; // TODO
291 // virtual
292 sal_Int32 SAL_CALL Paragraph::getBackground()
294 return 0; // TODO
297 // virtual
298 ::sal_Int32 SAL_CALL Paragraph::getCaretPosition()
300 checkDisposed();
301 return m_xDocument->retrieveParagraphCaretPosition(this);
304 // virtual
305 sal_Bool SAL_CALL Paragraph::setCaretPosition(::sal_Int32 nIndex)
307 checkDisposed();
308 m_xDocument->changeParagraphSelection(this, nIndex, nIndex);
309 return true;
312 // virtual
313 ::sal_Unicode SAL_CALL Paragraph::getCharacter(::sal_Int32 nIndex)
315 checkDisposed();
316 return OCommonAccessibleText::implGetCharacter(implGetText(), nIndex);
319 // virtual
320 css::uno::Sequence< css::beans::PropertyValue > SAL_CALL
321 Paragraph::getCharacterAttributes(::sal_Int32 nIndex, const css::uno::Sequence< OUString >& aRequestedAttributes)
323 checkDisposed();
324 return m_xDocument->retrieveCharacterAttributes( this, nIndex, aRequestedAttributes );
327 // virtual
328 css::awt::Rectangle SAL_CALL
329 Paragraph::getCharacterBounds(::sal_Int32 nIndex)
331 checkDisposed();
332 css::awt::Rectangle aBounds(m_xDocument->retrieveCharacterBounds(this, nIndex));
333 css::awt::Rectangle aParaBounds(m_xDocument->retrieveParagraphBounds(this, false));
334 aBounds.X -= aParaBounds.X;
335 aBounds.Y -= aParaBounds.Y;
336 return aBounds;
339 // virtual
340 ::sal_Int32 SAL_CALL Paragraph::getCharacterCount()
342 checkDisposed();
343 return implGetText().getLength();
346 // virtual
347 ::sal_Int32 SAL_CALL
348 Paragraph::getIndexAtPoint(css::awt::Point const & rPoint)
350 checkDisposed();
351 css::awt::Point aPoint(rPoint);
352 css::awt::Rectangle aParaBounds(m_xDocument->retrieveParagraphBounds(this, false));
353 aPoint.X += aParaBounds.X;
354 aPoint.Y += aParaBounds.Y;
355 return m_xDocument->retrieveCharacterIndex(this, aPoint);
358 // virtual
359 OUString SAL_CALL Paragraph::getSelectedText()
361 checkDisposed();
363 return OCommonAccessibleText::getSelectedText();
366 // virtual
367 ::sal_Int32 SAL_CALL Paragraph::getSelectionStart()
369 checkDisposed();
370 return OCommonAccessibleText::getSelectionStart();
373 // virtual
374 ::sal_Int32 SAL_CALL Paragraph::getSelectionEnd()
376 checkDisposed();
377 return OCommonAccessibleText::getSelectionEnd();
380 // virtual
381 sal_Bool SAL_CALL Paragraph::setSelection(::sal_Int32 nStartIndex,
382 ::sal_Int32 nEndIndex)
384 checkDisposed();
385 m_xDocument->changeParagraphSelection(this, nStartIndex, nEndIndex);
386 return true;
389 // virtual
390 OUString SAL_CALL Paragraph::getText()
392 checkDisposed();
393 return implGetText();
396 // virtual
397 OUString SAL_CALL Paragraph::getTextRange(::sal_Int32 nStartIndex,
398 ::sal_Int32 nEndIndex)
400 checkDisposed();
401 return OCommonAccessibleText::implGetTextRange(implGetText(), nStartIndex, nEndIndex);
404 // virtual
405 css::accessibility::TextSegment SAL_CALL Paragraph::getTextAtIndex( sal_Int32 nIndex, sal_Int16 aTextType )
407 checkDisposed();
408 return OCommonAccessibleText::getTextAtIndex(nIndex, aTextType);
411 // virtual
412 css::accessibility::TextSegment SAL_CALL Paragraph::getTextBeforeIndex( sal_Int32 nIndex, sal_Int16 aTextType )
414 checkDisposed();
415 return OCommonAccessibleText::getTextBeforeIndex(nIndex, aTextType);
418 // virtual
419 css::accessibility::TextSegment SAL_CALL Paragraph::getTextBehindIndex( sal_Int32 nIndex, sal_Int16 aTextType )
421 checkDisposed();
422 return OCommonAccessibleText::getTextBehindIndex(nIndex, aTextType);
425 // virtual
426 sal_Bool SAL_CALL Paragraph::copyText(::sal_Int32 nStartIndex,
427 ::sal_Int32 nEndIndex)
429 checkDisposed();
430 m_xDocument->copyParagraphText(this, nStartIndex, nEndIndex);
431 return true;
434 // virtual
435 sal_Bool SAL_CALL Paragraph::scrollSubstringTo( sal_Int32, sal_Int32, css::accessibility::AccessibleScrollType )
437 return false;
440 // virtual
441 sal_Bool SAL_CALL Paragraph::cutText(::sal_Int32 nStartIndex,
442 ::sal_Int32 nEndIndex)
444 checkDisposed();
445 m_xDocument->changeParagraphText(this, nStartIndex, nEndIndex, true, false,
446 OUString());
447 return true;
450 // virtual
451 sal_Bool SAL_CALL Paragraph::pasteText(::sal_Int32 nIndex)
453 checkDisposed();
454 m_xDocument->changeParagraphText(this, nIndex, nIndex, false, true,
455 OUString());
456 return true;
459 // virtual
460 sal_Bool SAL_CALL Paragraph::deleteText(::sal_Int32 nStartIndex,
461 ::sal_Int32 nEndIndex)
463 checkDisposed();
464 m_xDocument->changeParagraphText(this, nStartIndex, nEndIndex, false, false,
465 OUString());
466 return true;
469 // virtual
470 sal_Bool SAL_CALL Paragraph::insertText(OUString const & rText,
471 ::sal_Int32 nIndex)
473 checkDisposed();
474 m_xDocument->changeParagraphText(this, nIndex, nIndex, false, false, rText);
475 return true;
478 // virtual
479 sal_Bool SAL_CALL
480 Paragraph::replaceText(::sal_Int32 nStartIndex, ::sal_Int32 nEndIndex,
481 OUString const & rReplacement)
483 checkDisposed();
484 m_xDocument->changeParagraphText(this, nStartIndex, nEndIndex, false, false,
485 rReplacement);
486 return true;
489 // virtual
490 sal_Bool SAL_CALL Paragraph::setAttributes(
491 ::sal_Int32 nStartIndex, ::sal_Int32 nEndIndex,
492 css::uno::Sequence< css::beans::PropertyValue > const & rAttributeSet)
494 checkDisposed();
495 m_xDocument->changeParagraphAttributes(this, nStartIndex, nEndIndex,
496 rAttributeSet);
497 return true;
500 // virtual
501 sal_Bool SAL_CALL Paragraph::setText(OUString const & rText)
503 checkDisposed();
504 m_xDocument->changeParagraphText(this, rText);
505 return true;
508 // virtual
509 css::uno::Sequence< css::beans::PropertyValue > SAL_CALL
510 Paragraph::getDefaultAttributes(const css::uno::Sequence< OUString >&)
512 checkDisposed();
513 return {}; // default attributes are not supported by text engine
516 // virtual
517 css::uno::Sequence< css::beans::PropertyValue > SAL_CALL
518 Paragraph::getRunAttributes(::sal_Int32 Index, const css::uno::Sequence< OUString >& RequestedAttributes)
520 checkDisposed();
521 return m_xDocument->retrieveRunAttributes( this, Index, RequestedAttributes );
524 // virtual
525 ::sal_Int32 SAL_CALL Paragraph::getLineNumberAtIndex( ::sal_Int32 nIndex )
527 checkDisposed();
529 ::sal_Int32 nLineNo = -1;
530 m_xDocument->retrieveParagraphLineBoundary( this, nIndex, &nLineNo );
532 return nLineNo;
535 // virtual
536 css::accessibility::TextSegment SAL_CALL Paragraph::getTextAtLineNumber( ::sal_Int32 nLineNo )
538 checkDisposed();
540 css::i18n::Boundary aBoundary =
541 m_xDocument->retrieveParagraphBoundaryOfLine( this, nLineNo );
543 return css::accessibility::TextSegment( getTextRange(aBoundary.startPos, aBoundary.endPos),
544 aBoundary.startPos, aBoundary.endPos);
547 // virtual
548 css::accessibility::TextSegment SAL_CALL Paragraph::getTextAtLineWithCaret( )
550 checkDisposed();
552 sal_Int32 nLineNo = getNumberOfLineWithCaret();
554 try {
555 return ( nLineNo >= 0 ) ?
556 getTextAtLineNumber( nLineNo ) :
557 css::accessibility::TextSegment();
558 } catch (const css::lang::IndexOutOfBoundsException&) {
559 css::uno::Any anyEx = cppu::getCaughtException();
560 throw css::lang::WrappedTargetRuntimeException(
561 u"textwindowaccessibility.cxx:"
562 " Paragraph::getTextAtLineWithCaret"_ustr,
563 getXWeak(), anyEx );
567 // virtual
568 ::sal_Int32 SAL_CALL Paragraph::getNumberOfLineWithCaret( )
570 checkDisposed();
571 return m_xDocument->retrieveParagraphLineWithCursor(this);
575 // virtual
576 void SAL_CALL Paragraph::addAccessibleEventListener(
577 css::uno::Reference<
578 css::accessibility::XAccessibleEventListener > const & rListener)
580 if (!rListener.is())
581 return;
583 ::osl::ClearableMutexGuard aGuard(rBHelper.rMutex);
584 if (rBHelper.bDisposed || rBHelper.bInDispose)
586 aGuard.clear();
587 rListener->disposing(css::lang::EventObject(
588 getXWeak()));
590 else
592 if (!m_nClientId)
593 m_nClientId = comphelper::AccessibleEventNotifier::registerClient( );
594 comphelper::AccessibleEventNotifier::addEventListener( m_nClientId, rListener );
598 // virtual
599 void SAL_CALL Paragraph::removeAccessibleEventListener(
600 css::uno::Reference<
601 css::accessibility::XAccessibleEventListener > const & rListener)
603 comphelper::AccessibleEventNotifier::TClientId nId = 0;
605 osl::MutexGuard aGuard(rBHelper.rMutex);
606 if (rListener.is() && m_nClientId != 0
607 && comphelper::AccessibleEventNotifier::removeEventListener( m_nClientId, rListener ) == 0)
609 nId = m_nClientId;
610 m_nClientId = 0;
613 if (nId != 0)
615 // no listeners anymore
616 // -> revoke ourself. This may lead to the notifier thread dying (if we were the last client),
617 // and at least to us not firing any events anymore, in case somebody calls
618 // NotifyAccessibleEvent, again
619 comphelper::AccessibleEventNotifier::revokeClient(nId);
623 // virtual
624 void SAL_CALL Paragraph::disposing()
626 comphelper::AccessibleEventNotifier::TClientId nId = 0;
628 osl::MutexGuard aGuard(rBHelper.rMutex);
629 nId = m_nClientId;
630 m_nClientId = 0;
632 if (nId != 0)
633 comphelper::AccessibleEventNotifier::revokeClientNotifyDisposing(nId, *this);
636 // virtual
637 OUString Paragraph::implGetText()
639 return m_xDocument->retrieveParagraphText(this);
642 // virtual
643 css::lang::Locale Paragraph::implGetLocale()
645 return m_xDocument->retrieveLocale();
648 // virtual
649 void Paragraph::implGetSelection(::sal_Int32 & rStartIndex,
650 ::sal_Int32 & rEndIndex)
652 m_xDocument->retrieveParagraphSelection(this, &rStartIndex, &rEndIndex);
655 // virtual
656 void Paragraph::implGetParagraphBoundary( const OUString& rText,
657 css::i18n::Boundary& rBoundary,
658 ::sal_Int32 nIndex )
660 ::sal_Int32 nLength = rText.getLength();
662 if ( implIsValidIndex( nIndex, nLength ) )
664 rBoundary.startPos = 0;
665 rBoundary.endPos = nLength;
667 else
669 rBoundary.startPos = nIndex;
670 rBoundary.endPos = nIndex;
674 // virtual
675 void Paragraph::implGetLineBoundary( const OUString& rText,
676 css::i18n::Boundary& rBoundary,
677 ::sal_Int32 nIndex )
679 ::sal_Int32 nLength = rText.getLength();
681 if ( implIsValidIndex( nIndex, nLength ) || nIndex == nLength )
683 css::i18n::Boundary aBoundary =
684 m_xDocument->retrieveParagraphLineBoundary( this, nIndex, nullptr );
685 rBoundary.startPos = aBoundary.startPos;
686 rBoundary.endPos = aBoundary.endPos;
688 else
690 rBoundary.startPos = nIndex;
691 rBoundary.endPos = nIndex;
696 void Paragraph::checkDisposed()
698 ::osl::MutexGuard aGuard(rBHelper.rMutex);
699 if (!(rBHelper.bDisposed || rBHelper.bInDispose))
700 return;
701 throw css::lang::DisposedException(
702 OUString(), getXWeak());
705 Document::Document(vcl::Window* pWindow, ::TextEngine & rEngine,
706 ::TextView & rView)
707 : VCLXAccessibleComponent(pWindow),
708 m_rEngine(rEngine),
709 m_rView(rView),
710 m_aEngineListener(*this),
711 m_aViewListener(LINK(this, Document, WindowEventHandler)),
712 m_nVisibleBeginOffset(0),
713 m_nSelectionFirstPara(-1),
714 m_nSelectionFirstPos(-1),
715 m_nSelectionLastPara(-1),
716 m_nSelectionLastPos(-1),
717 m_bSelectionChangedNotification(false)
719 const sal_uInt32 nCount = m_rEngine.GetParagraphCount();
720 m_aParagraphs.reserve(nCount);
721 for (sal_uInt32 i = 0; i < nCount; ++i)
722 m_aParagraphs.emplace_back(m_rEngine.GetTextHeight(i));
723 m_nViewOffset = static_cast<::sal_Int32>(m_rView.GetStartDocPos().Y()); // XXX numeric overflow
724 m_nViewHeight = static_cast<::sal_Int32>(m_rView.GetWindow()->GetOutputSizePixel().Height());
725 // XXX numeric overflow
726 determineVisibleRange();
727 m_nFocused = m_aParagraphs.size();
728 m_aEngineListener.startListening(m_rEngine);
729 m_aViewListener.startListening(*m_rView.GetWindow());
732 css::uno::Reference<css::accessibility::XAccessible> Document::getAccessible() const
734 if (vcl::Window* pWindow = GetWindow())
735 return pWindow->GetAccessible();
736 return nullptr;
739 css::lang::Locale Document::retrieveLocale()
741 SolarMutexGuard aGuard;
742 return m_rEngine.GetLocale();
745 ::sal_Int32 Document::retrieveParagraphIndex(Paragraph const * pParagraph)
747 ::osl::MutexGuard aInternalGuard(GetMutex());
749 // If a client holds on to a Paragraph that is no longer visible, it can
750 // happen that this Paragraph lies outside the range from m_aVisibleBegin
751 // to m_aVisibleEnd. In that case, return -1 instead of a valid index:
752 auto nPara(pParagraph->getNumber());
753 return nPara < m_nVisibleBegin || nPara >= m_nVisibleEnd
754 ? -1 : static_cast< ::sal_Int32 >(nPara - m_nVisibleBegin);
755 // XXX numeric overflow
758 ::sal_Int64 Document::retrieveParagraphState(Paragraph const * pParagraph)
760 ::osl::MutexGuard aInternalGuard(GetMutex());
762 // If a client holds on to a Paragraph that is no longer visible, it can
763 // happen that this Paragraph lies outside the range from m_aVisibleBegin
764 // to m_aVisibleEnd. In that case, it is neither VISIBLE nor SHOWING:
765 ::sal_Int64 nState
766 = css::accessibility::AccessibleStateType::ENABLED
767 | css::accessibility::AccessibleStateType::SENSITIVE
768 | css::accessibility::AccessibleStateType::FOCUSABLE
769 | css::accessibility::AccessibleStateType::MULTI_LINE;
770 if (!m_rView.IsReadOnly())
771 nState |= css::accessibility::AccessibleStateType::EDITABLE;
772 auto nPara(pParagraph->getNumber());
773 if (nPara >= m_nVisibleBegin && nPara < m_nVisibleEnd)
775 nState
776 |= css::accessibility::AccessibleStateType::VISIBLE
777 | css::accessibility::AccessibleStateType::SHOWING;
778 if (nPara == m_nFocused)
779 nState |= css::accessibility::AccessibleStateType::FOCUSED;
781 return nState;
784 css::awt::Rectangle
785 Document::retrieveParagraphBounds(Paragraph const * pParagraph,
786 bool bAbsolute)
788 SolarMutexGuard aGuard;
789 ::osl::MutexGuard aInternalGuard(GetMutex());
791 // If a client holds on to a Paragraph that is no longer visible (as it
792 // scrolled out the top of the view), it can happen that this Paragraph
793 // lies before m_aVisibleBegin. In that case, calculate the vertical
794 // position of the Paragraph starting at paragraph 0, otherwise optimize
795 // and start at m_aVisibleBegin:
796 auto nPara(pParagraph->getNumber());
797 auto lAddHeight = [](const sal_Int32& rSum, const ParagraphInfo& rParagraph) {
798 return rSum + rParagraph.getHeight(); };
799 ::sal_Int32 nPos;
800 if (nPara < m_nVisibleBegin)
801 nPos = std::accumulate(m_aParagraphs.begin(), getIter(nPara), sal_Int32(0), lAddHeight);
802 else
803 nPos = std::accumulate(visibleBegin(), getIter(nPara), m_nViewOffset - m_nVisibleBeginOffset, lAddHeight);
805 Point aOrig(0, 0);
806 if (bAbsolute)
807 aOrig = Point(m_rView.GetWindow()->OutputToAbsoluteScreenPixel(aOrig));
809 return css::awt::Rectangle(
810 static_cast< ::sal_Int32 >(aOrig.X()),
811 static_cast< ::sal_Int32 >(aOrig.Y()) + nPos - m_nViewOffset,
812 m_rView.GetWindow()->GetOutputSizePixel().Width(), getIter(nPara)->getHeight());
813 // XXX numeric overflow (3x)
816 OUString
817 Document::retrieveParagraphText(Paragraph const * pParagraph)
819 SolarMutexGuard aGuard;
820 ::osl::MutexGuard aInternalGuard(GetMutex());
821 return m_rEngine.GetText(static_cast< ::sal_uInt32 >(pParagraph->getNumber()));
822 // numeric overflow cannot happen here
825 void Document::retrieveParagraphSelection(Paragraph const * pParagraph,
826 ::sal_Int32 * pBegin,
827 ::sal_Int32 * pEnd)
829 SolarMutexGuard aGuard;
830 ::osl::MutexGuard aInternalGuard(GetMutex());
831 ::TextSelection const & rSelection = m_rView.GetSelection();
832 Paragraphs::size_type nNumber = pParagraph->getNumber();
833 TextPaM aStartPaM( rSelection.GetStart() );
834 TextPaM aEndPaM( rSelection.GetEnd() );
835 TextPaM aMinPaM( std::min( aStartPaM, aEndPaM ) );
836 TextPaM aMaxPaM( std::max( aStartPaM, aEndPaM ) );
838 if ( nNumber >= aMinPaM.GetPara() && nNumber <= aMaxPaM.GetPara() )
840 *pBegin = nNumber > aMinPaM.GetPara() ? 0 : aMinPaM.GetIndex();
841 // XXX numeric overflow
842 *pEnd = nNumber < aMaxPaM.GetPara()
843 ? m_rEngine.GetText(static_cast< ::sal_uInt32 >(nNumber)).getLength()
844 : aMaxPaM.GetIndex();
845 // XXX numeric overflow (3x)
847 if ( aStartPaM > aEndPaM )
848 std::swap( *pBegin, *pEnd );
850 else
852 *pBegin = 0;
853 *pEnd = 0;
857 ::sal_Int32 Document::retrieveParagraphCaretPosition(Paragraph const * pParagraph)
859 SolarMutexGuard aGuard;
860 ::osl::MutexGuard aInternalGuard(GetMutex());
861 ::TextSelection const & rSelection = m_rView.GetSelection();
862 Paragraphs::size_type nNumber = pParagraph->getNumber();
863 TextPaM aEndPaM( rSelection.GetEnd() );
865 return aEndPaM.GetPara() == nNumber ? aEndPaM.GetIndex() : -1;
868 css::awt::Rectangle
869 Document::retrieveCharacterBounds(Paragraph const * pParagraph,
870 ::sal_Int32 nIndex)
872 SolarMutexGuard aGuard;
873 ::osl::MutexGuard aInternalGuard(GetMutex());
874 ::sal_uInt32 nNumber = static_cast< ::sal_uInt32 >(pParagraph->getNumber());
875 sal_Int32 nLength = m_rEngine.GetText(nNumber).getLength();
876 // XXX numeric overflow
877 if (nIndex < 0 || nIndex > nLength)
878 throw css::lang::IndexOutOfBoundsException(
879 u"textwindowaccessibility.cxx:"
880 " Document::retrieveCharacterAttributes"_ustr,
881 getXWeak());
882 css::awt::Rectangle aBounds( 0, 0, 0, 0 );
883 if ( nIndex == nLength )
885 aBounds
886 = vcl::unohelper::ConvertToAWTRect(m_rEngine.PaMtoEditCursor(::TextPaM(nNumber, nIndex)));
888 else
890 ::tools::Rectangle aLeft(
891 m_rEngine.PaMtoEditCursor(::TextPaM(nNumber, nIndex)));
892 // XXX numeric overflow
893 ::tools::Rectangle aRight(
894 m_rEngine.PaMtoEditCursor(::TextPaM(nNumber, nIndex + 1)));
895 // XXX numeric overflow (2x)
896 // FIXME If the vertical extends of the two cursors do not match, assume
897 // nIndex is the last character on the line; the bounding box will then
898 // extend to m_rEngine.GetMaxTextWidth():
899 ::sal_Int32 nWidth = (aLeft.Top() == aRight.Top()
900 && aLeft.Bottom() == aRight.Bottom())
901 ? static_cast< ::sal_Int32 >(aRight.Left() - aLeft.Left())
902 : static_cast< ::sal_Int32 >(m_rEngine.GetMaxTextWidth()
903 - aLeft.Left());
904 // XXX numeric overflow (4x)
905 aBounds = css::awt::Rectangle(static_cast< ::sal_Int32 >(aLeft.Left()),
906 static_cast< ::sal_Int32 >(aLeft.Top() - m_nViewOffset),
907 nWidth,
908 static_cast< ::sal_Int32 >(aLeft.Bottom()
909 - aLeft.Top()));
910 // XXX numeric overflow (4x)
912 return aBounds;
915 ::sal_Int32 Document::retrieveCharacterIndex(Paragraph const * pParagraph,
916 css::awt::Point const & rPoint)
918 SolarMutexGuard aGuard;
919 ::osl::MutexGuard aInternalGuard(GetMutex());
920 ::sal_uInt32 nNumber = static_cast< ::sal_uInt32 >(pParagraph->getNumber());
921 // XXX numeric overflow
922 ::TextPaM aPaM(m_rEngine.GetPaM(::Point(static_cast< tools::Long >(rPoint.X),
923 static_cast< tools::Long >(rPoint.Y))));
924 // XXX numeric overflow (2x)
925 return aPaM.GetPara() == nNumber ? aPaM.GetIndex() : -1;
926 // XXX numeric overflow
929 css::uno::Sequence< css::beans::PropertyValue >
930 Document::retrieveCharacterAttributes(
931 Paragraph const * pParagraph, ::sal_Int32 nIndex,
932 const css::uno::Sequence< OUString >& aRequestedAttributes)
934 SolarMutexGuard aGuard;
936 vcl::Font aFont = m_rEngine.GetFont();
937 const sal_Int32 AttributeCount = 9;
938 std::vector< css::beans::PropertyValue > aAttribs;
939 aAttribs.reserve(AttributeCount);
941 css::beans::PropertyValue aAttrib;
942 aAttrib.Handle = -1;
943 aAttrib.State = css::beans::PropertyState_DIRECT_VALUE;
945 //character background color
946 aAttrib.Name = "CharBackColor";
947 aAttrib.Value = mapFontColor( aFont.GetFillColor() );
948 aAttribs.push_back(aAttrib);
950 //character color
951 aAttrib.Name = "CharColor";
952 //aAttrib.Value = mapFontColor( aFont.GetColor() );
953 aAttrib.Value = mapFontColor( m_rEngine.GetTextColor() );
954 aAttribs.push_back(aAttrib);
956 //character font name
957 aAttrib.Name = "CharFontName";
958 aAttrib.Value <<= aFont.GetFamilyName();
959 aAttribs.push_back(aAttrib);
961 //character height
962 aAttrib.Name = "CharHeight";
963 aAttrib.Value <<= static_cast<sal_Int16>(aFont.GetFontHeight());
964 aAttribs.push_back(aAttrib);
966 //character posture
967 aAttrib.Name = "CharPosture";
968 aAttrib.Value <<= vcl::unohelper::ConvertFontSlant(aFont.GetItalic());
969 aAttribs.push_back(aAttrib);
971 //character relief
973 aAttrib.Name = "CharRelief";
974 aAttrib.Value = css::uno::Any( (sal_Int16)aFont.GetRelief() );
975 aAttribs.push_back(aAttrib);
978 //character strikeout
979 aAttrib.Name = "CharStrikeout";
980 aAttrib.Value <<= static_cast<sal_Int16>(aFont.GetStrikeout());
981 aAttribs.push_back(aAttrib);
983 //character underline
984 aAttrib.Name = "CharUnderline";
985 aAttrib.Value <<= static_cast<sal_Int16>(aFont.GetUnderline());
986 aAttribs.push_back(aAttrib);
988 //character weight
989 aAttrib.Name = "CharWeight";
990 aAttrib.Value <<= static_cast<float>(aFont.GetWeight());
991 aAttribs.push_back(aAttrib);
993 //character alignment
994 aAttrib.Name = "ParaAdjust";
995 aAttrib.Value <<= static_cast<sal_Int16>(m_rEngine.GetTextAlign());
996 aAttribs.push_back(aAttrib);
998 ::osl::MutexGuard aInternalGuard(GetMutex());
999 ::sal_uInt32 nNumber = static_cast< ::sal_uInt32 >(pParagraph->getNumber());
1000 // XXX numeric overflow
1001 // nIndex can be equal to getLength();
1002 if (nIndex < 0 || nIndex > m_rEngine.GetText(nNumber).getLength())
1003 throw css::lang::IndexOutOfBoundsException(
1004 u"textwindowaccessibility.cxx:"
1005 " Document::retrieveCharacterAttributes"_ustr,
1006 getXWeak());
1009 // retrieve run attributes
1010 tPropValMap aCharAttrSeq;
1011 retrieveRunAttributesImpl( pParagraph, nIndex, aRequestedAttributes, aCharAttrSeq );
1013 for (const css::beans::PropertyValue& rAttrib : aAttribs)
1015 aCharAttrSeq[ rAttrib.Name ] = rAttrib;
1018 const css::uno::Sequence< css::beans::PropertyValue > aRes = comphelper::mapValuesToSequence( aCharAttrSeq );
1020 // sort the attributes
1021 auto nLength = static_cast<size_t>(aRes.getLength());
1022 std::vector<sal_Int32> pIndices(nLength);
1023 std::iota(pIndices.begin(), pIndices.end(), 0);
1024 std::sort(pIndices.begin(), pIndices.end(),
1025 [&aRes](sal_Int32 a, sal_Int32 b) { return aRes[a].Name < aRes[b].Name; });
1027 // create sorted sequences according to index array
1028 std::vector<css::beans::PropertyValue> aNewValues;
1029 aNewValues.reserve(nLength);
1030 std::transform(pIndices.begin(), pIndices.end(), std::back_inserter(aNewValues),
1031 [&aRes](const sal_Int32 nIdx) -> const css::beans::PropertyValue& { return aRes[nIdx]; });
1033 return comphelper::containerToSequence(aNewValues);
1036 void Document::retrieveRunAttributesImpl(
1037 Paragraph const * pParagraph, ::sal_Int32 Index,
1038 const css::uno::Sequence< OUString >& RequestedAttributes,
1039 tPropValMap& rRunAttrSeq)
1041 ::sal_uInt32 nNumber = static_cast< ::sal_uInt32 >( pParagraph->getNumber() );
1042 ::TextPaM aPaM( nNumber, Index );
1043 // XXX numeric overflow
1044 ::TextAttribFontColor const * pColor
1045 = static_cast< ::TextAttribFontColor const * >(
1046 m_rEngine.FindAttrib( aPaM, TEXTATTR_FONTCOLOR ) );
1047 ::TextAttribFontWeight const * pWeight
1048 = static_cast< ::TextAttribFontWeight const * >(
1049 m_rEngine.FindAttrib( aPaM, TEXTATTR_FONTWEIGHT ) );
1050 tPropValMap aRunAttrSeq;
1051 if ( pColor )
1053 css::beans::PropertyValue aPropVal{
1054 "CharColor", -1,
1055 mapFontColor(pColor->GetColor()),
1056 css::beans::PropertyState_DIRECT_VALUE};
1057 aRunAttrSeq[ aPropVal.Name ] = aPropVal;
1059 if ( pWeight )
1061 css::beans::PropertyValue aPropVal{
1062 "CharWeight", -1,
1063 mapFontWeight(pWeight->getFontWeight()),
1064 css::beans::PropertyState_DIRECT_VALUE};
1065 aRunAttrSeq[ aPropVal.Name ] = aPropVal;
1067 if ( !RequestedAttributes.hasElements() )
1069 rRunAttrSeq = std::move(aRunAttrSeq);
1071 else
1073 for ( const OUString& rReqAttr : RequestedAttributes )
1075 tPropValMap::iterator aIter = aRunAttrSeq.find( rReqAttr );
1076 if ( aIter != aRunAttrSeq.end() )
1078 rRunAttrSeq[ (*aIter).first ] = (*aIter).second;
1084 css::uno::Sequence< css::beans::PropertyValue >
1085 Document::retrieveRunAttributes(
1086 Paragraph const * pParagraph, ::sal_Int32 Index,
1087 const css::uno::Sequence< OUString >& RequestedAttributes)
1089 SolarMutexGuard aGuard;
1090 ::osl::MutexGuard aInternalGuard( GetMutex() );
1091 ::sal_uInt32 nNumber = static_cast< ::sal_uInt32 >( pParagraph->getNumber() );
1092 // XXX numeric overflow
1093 if ( Index < 0 || Index >= m_rEngine.GetText(nNumber).getLength() )
1094 throw css::lang::IndexOutOfBoundsException(
1095 u"textwindowaccessibility.cxx:"
1096 " Document::retrieveRunAttributes"_ustr,
1097 getXWeak() );
1099 tPropValMap aRunAttrSeq;
1100 retrieveRunAttributesImpl( pParagraph, Index, RequestedAttributes, aRunAttrSeq );
1101 return comphelper::mapValuesToSequence( aRunAttrSeq );
1104 void Document::changeParagraphText(Paragraph const * pParagraph,
1105 OUString const & rText)
1107 SolarMutexGuard aGuard;
1109 ::osl::MutexGuard aInternalGuard(GetMutex());
1110 ::sal_uInt32 nNumber = static_cast< ::sal_uInt32 >(pParagraph->getNumber());
1111 // XXX numeric overflow
1112 changeParagraphText(nNumber, 0, m_rEngine.GetTextLen(nNumber), false,
1113 false, rText);
1117 void Document::changeParagraphText(Paragraph const * pParagraph,
1118 ::sal_Int32 nBegin, ::sal_Int32 nEnd,
1119 bool bCut, bool bPaste,
1120 OUString const & rText)
1122 SolarMutexGuard aGuard;
1124 ::osl::MutexGuard aInternalGuard(GetMutex());
1125 ::sal_uInt32 nNumber = static_cast< ::sal_uInt32 >(pParagraph->getNumber());
1126 // XXX numeric overflow
1127 if (nBegin < 0 || nBegin > nEnd
1128 || nEnd > m_rEngine.GetText(nNumber).getLength())
1129 throw css::lang::IndexOutOfBoundsException(
1130 u"textwindowaccessibility.cxx:"
1131 " Document::changeParagraphText"_ustr,
1132 getXWeak());
1133 changeParagraphText(nNumber, static_cast< ::sal_uInt16 >(nBegin),
1134 static_cast< ::sal_uInt16 >(nEnd), bCut, bPaste, rText);
1135 // XXX numeric overflow (2x)
1139 void Document::copyParagraphText(Paragraph const * pParagraph,
1140 ::sal_Int32 nBegin, ::sal_Int32 nEnd)
1142 SolarMutexGuard aGuard;
1144 ::osl::MutexGuard aInternalGuard(GetMutex());
1145 ::sal_uInt32 nNumber = static_cast< ::sal_uInt32 >(pParagraph->getNumber());
1146 // XXX numeric overflow
1147 if (nBegin < 0 || nBegin > nEnd
1148 || nEnd > m_rEngine.GetText(nNumber).getLength())
1149 throw css::lang::IndexOutOfBoundsException(
1150 u"textwindowaccessibility.cxx:"
1151 " Document::copyParagraphText"_ustr,
1152 getXWeak());
1153 m_rView.SetSelection(
1154 ::TextSelection(::TextPaM(nNumber, nBegin),
1155 ::TextPaM(nNumber, nEnd)));
1156 // XXX numeric overflow (2x)
1157 m_rView.Copy();
1161 void Document::changeParagraphAttributes(
1162 Paragraph const * pParagraph, ::sal_Int32 nBegin, ::sal_Int32 nEnd,
1163 css::uno::Sequence< css::beans::PropertyValue > const & rAttributeSet)
1165 SolarMutexGuard aGuard;
1167 ::osl::MutexGuard aInternalGuard(GetMutex());
1168 ::sal_uInt32 nNumber = static_cast< ::sal_uInt32 >(pParagraph->getNumber());
1169 // XXX numeric overflow
1170 if (nBegin < 0 || nBegin > nEnd
1171 || nEnd > m_rEngine.GetText(nNumber).getLength())
1172 throw css::lang::IndexOutOfBoundsException(
1173 u"textwindowaccessibility.cxx:"
1174 " Document::changeParagraphAttributes"_ustr,
1175 getXWeak());
1177 // FIXME The new attributes are added to any attributes already set,
1178 // they do not replace the old attributes as required by
1179 // XAccessibleEditableText.setAttributes:
1180 for (const auto& rAttr : rAttributeSet)
1181 if ( rAttr.Name == "CharColor" )
1182 m_rEngine.SetAttrib(::TextAttribFontColor(
1183 mapFontColor(rAttr.Value)),
1184 nNumber, nBegin, nEnd);
1185 // XXX numeric overflow (2x)
1186 else if ( rAttr.Name == "CharWeight" )
1187 m_rEngine.SetAttrib(::TextAttribFontWeight(
1188 mapFontWeight(rAttr.Value)),
1189 nNumber, nBegin, nEnd);
1190 // XXX numeric overflow (2x)
1194 void Document::changeParagraphSelection(Paragraph const * pParagraph,
1195 ::sal_Int32 nBegin, ::sal_Int32 nEnd)
1197 SolarMutexGuard aGuard;
1199 ::osl::MutexGuard aInternalGuard(GetMutex());
1200 ::sal_uInt32 nNumber = static_cast< ::sal_uInt32 >(pParagraph->getNumber());
1201 // XXX numeric overflow
1202 if (nBegin < 0 || nBegin > nEnd
1203 || nEnd > m_rEngine.GetText(nNumber).getLength())
1204 throw css::lang::IndexOutOfBoundsException(
1205 u"textwindowaccessibility.cxx:"
1206 " Document::changeParagraphSelection"_ustr,
1207 getXWeak());
1208 m_rView.SetSelection(
1209 ::TextSelection(::TextPaM(nNumber, nBegin),
1210 ::TextPaM(nNumber, nEnd)));
1211 // XXX numeric overflow (2x)
1215 css::i18n::Boundary
1216 Document::retrieveParagraphLineBoundary( Paragraph const * pParagraph,
1217 ::sal_Int32 nIndex, ::sal_Int32 *pLineNo )
1219 css::i18n::Boundary aBoundary;
1220 aBoundary.startPos = nIndex;
1221 aBoundary.endPos = nIndex;
1223 SolarMutexGuard aGuard;
1225 ::osl::MutexGuard aInternalGuard( GetMutex() );
1226 ::sal_uInt32 nNumber = static_cast< ::sal_uInt32 >( pParagraph->getNumber() );
1227 if ( nIndex < 0 || nIndex > m_rEngine.GetText( nNumber ).getLength() )
1228 throw css::lang::IndexOutOfBoundsException(
1229 u"textwindowaccessibility.cxx:"
1230 " Document::retrieveParagraphLineBoundary"_ustr,
1231 getXWeak() );
1232 ::sal_Int32 nLineStart = 0;
1233 ::sal_Int32 nLineEnd = 0;
1234 ::sal_uInt16 nLineCount = m_rEngine.GetLineCount( nNumber );
1235 for ( ::sal_uInt16 nLine = 0; nLine < nLineCount; ++nLine )
1237 nLineStart = nLineEnd;
1238 nLineEnd += m_rEngine.GetLineLen( nNumber, nLine );
1239 if ( nIndex >= nLineStart && ( ( nLine == nLineCount - 1 ) ? nIndex <= nLineEnd : nIndex < nLineEnd ) )
1241 aBoundary.startPos = nLineStart;
1242 aBoundary.endPos = nLineEnd;
1243 if( pLineNo )
1244 pLineNo[0] = nLine;
1245 break;
1250 return aBoundary;
1253 css::i18n::Boundary
1254 Document::retrieveParagraphBoundaryOfLine( Paragraph const * pParagraph,
1255 ::sal_Int32 nLineNo )
1257 css::i18n::Boundary aBoundary;
1258 aBoundary.startPos = 0;
1259 aBoundary.endPos = 0;
1261 SolarMutexGuard aGuard;
1263 ::osl::MutexGuard aInternalGuard( GetMutex() );
1264 ::sal_uInt32 nNumber = static_cast< ::sal_uInt32 >( pParagraph->getNumber() );
1265 if ( nLineNo >= m_rEngine.GetLineCount( nNumber ) )
1266 throw css::lang::IndexOutOfBoundsException(
1267 u"textwindowaccessibility.cxx:"
1268 " Document::retrieveParagraphBoundaryOfLine"_ustr,
1269 getXWeak() );
1270 ::sal_Int32 nLineStart = 0;
1271 ::sal_Int32 nLineEnd = 0;
1272 for ( ::sal_Int32 nLine = 0; nLine <= nLineNo; ++nLine )
1274 nLineStart = nLineEnd;
1275 nLineEnd += m_rEngine.GetLineLen( nNumber, nLine );
1278 aBoundary.startPos = nLineStart;
1279 aBoundary.endPos = nLineEnd;
1282 return aBoundary;
1285 sal_Int32 Document::retrieveParagraphLineWithCursor( Paragraph const * pParagraph )
1287 SolarMutexGuard aGuard;
1288 ::osl::MutexGuard aInternalGuard(GetMutex());
1289 ::TextSelection const & rSelection = m_rView.GetSelection();
1290 Paragraphs::size_type nNumber = pParagraph->getNumber();
1291 TextPaM aEndPaM( rSelection.GetEnd() );
1293 return aEndPaM.GetPara() == nNumber
1294 ? m_rView.GetLineNumberOfCursorInSelection() : -1;
1298 css::uno::Reference< css::accessibility::XAccessibleRelationSet >
1299 Document::retrieveParagraphRelationSet( Paragraph const * pParagraph )
1301 ::osl::MutexGuard aInternalGuard( GetMutex() );
1303 rtl::Reference<::utl::AccessibleRelationSetHelper> pRelationSetHelper = new ::utl::AccessibleRelationSetHelper();
1305 auto nPara(pParagraph->getNumber());
1307 if (nPara > m_nVisibleBegin && nPara < m_nVisibleEnd)
1309 css::uno::Sequence<css::uno::Reference<css::accessibility::XAccessible>> aSequence { getAccessibleChild(getIter(nPara - 1)) };
1310 css::accessibility::AccessibleRelation aRelation(css::accessibility::AccessibleRelationType_CONTENT_FLOWS_FROM, aSequence);
1311 pRelationSetHelper->AddRelation( aRelation );
1314 if (nPara >= m_nVisibleBegin && m_nVisibleEnd > 1 && nPara < m_nVisibleEnd - 1)
1316 css::uno::Sequence<css::uno::Reference<css::accessibility::XAccessible>> aSequence { getAccessibleChild(getIter(nPara + 1)) };
1317 css::accessibility::AccessibleRelation aRelation( css::accessibility::AccessibleRelationType_CONTENT_FLOWS_TO, aSequence );
1318 pRelationSetHelper->AddRelation( aRelation );
1321 return pRelationSetHelper;
1324 // virtual
1325 sal_Int64 SAL_CALL Document::getAccessibleChildCount()
1327 ::comphelper::OExternalLockGuard aGuard(this);
1328 return m_nVisibleEnd - m_nVisibleBegin;
1331 // virtual
1332 css::uno::Reference< css::accessibility::XAccessible > SAL_CALL
1333 Document::getAccessibleChild(sal_Int64 i)
1335 ::comphelper::OExternalLockGuard aGuard(this);
1336 if (i < 0 || o3tl::make_unsigned(i) >= m_nVisibleEnd - m_nVisibleBegin)
1337 throw css::lang::IndexOutOfBoundsException(
1338 u"textwindowaccessibility.cxx:"
1339 " Document::getAccessibleChild"_ustr,
1340 getXWeak());
1341 return getAccessibleChild(getIter(m_nVisibleBegin + i));
1344 // virtual
1345 ::sal_Int16 SAL_CALL Document::getAccessibleRole()
1347 return css::accessibility::AccessibleRole::TEXT_FRAME;
1350 // virtual
1351 css::uno::Reference< css::accessibility::XAccessible > SAL_CALL
1352 Document::getAccessibleAtPoint(css::awt::Point const & rPoint)
1354 ::comphelper::OExternalLockGuard aGuard(this);
1355 if (rPoint.X >= 0
1356 && rPoint.X < m_rView.GetWindow()->GetOutputSizePixel().Width()
1357 && rPoint.Y >= 0 && rPoint.Y < m_nViewHeight)
1359 ::sal_Int32 nOffset = m_nViewOffset + rPoint.Y; // XXX numeric overflow
1360 ::sal_Int32 nPos = m_nViewOffset - m_nVisibleBeginOffset;
1361 for (Paragraphs::iterator aIt(visibleBegin()), aEnd(visibleEnd()); aIt != aEnd; ++aIt)
1363 nPos += aIt->getHeight(); // XXX numeric overflow
1364 if (nOffset < nPos)
1365 return getAccessibleChild(aIt);
1368 return nullptr;
1370 void Document::FillAccessibleStateSet( sal_Int64& rStateSet )
1372 VCLXAccessibleComponent::FillAccessibleStateSet( rStateSet );
1373 if (!m_rView.IsReadOnly())
1374 rStateSet |= css::accessibility::AccessibleStateType::EDITABLE;
1377 void Document::FillAccessibleRelationSet( utl::AccessibleRelationSetHelper& rRelationSet )
1379 if( getAccessibleParent()->getAccessibleContext()->getAccessibleRole() == css::accessibility::AccessibleRole::SCROLL_PANE )
1381 css::uno::Sequence<css::uno::Reference<css::accessibility::XAccessible>> aSequence { getAccessibleParent() };
1382 rRelationSet.AddRelation( css::accessibility::AccessibleRelation( css::accessibility::AccessibleRelationType_MEMBER_OF, aSequence ) );
1384 else
1386 VCLXAccessibleComponent::FillAccessibleRelationSet(rRelationSet);
1389 // virtual
1390 void SAL_CALL Document::disposing()
1392 m_aEngineListener.endListening();
1393 m_aViewListener.endListening();
1394 disposeParagraphs();
1395 VCLXAccessibleComponent::disposing();
1398 // virtual
1399 void Document::Notify(::SfxBroadcaster &, ::SfxHint const & rHint)
1401 switch (rHint.GetId())
1403 case SfxHintId::TextParaInserted:
1404 case SfxHintId::TextParaRemoved:
1405 // SfxHintId::TextParaInserted and SfxHintId::TextParaRemoved are sent at
1406 // "unsafe" times (when the text engine has not yet re-formatted its
1407 // content), so that for example calling ::TextEngine::GetTextHeight
1408 // from within the code that handles SfxHintId::TextParaInserted causes
1409 // trouble within the text engine. Therefore, these hints are just
1410 // buffered until a following ::TextEngine::FormatDoc causes a
1411 // SfxHintId::TextFormatted to come in:
1412 case SfxHintId::TextFormatPara:
1413 // ::TextEngine::FormatDoc sends a sequence of
1414 // SfxHintId::TextFormatParas, followed by an optional
1415 // SfxHintId::TextHeightChanged, followed in all cases by one
1416 // SfxHintId::TextFormatted. Only the SfxHintId::TextFormatParas contain
1417 // the numbers of the affected paragraphs, but they are sent
1418 // before the changes are applied. Therefore, SfxHintId::TextFormatParas
1419 // are just buffered until another hint comes in:
1421 ::osl::MutexGuard aInternalGuard(GetMutex());
1422 if (!isAlive())
1423 break;
1425 const TextHint& rTextHint = static_cast<const TextHint&>(rHint);
1426 m_aParagraphNotifications.push(rTextHint);
1427 break;
1429 case SfxHintId::TextFormatted:
1430 case SfxHintId::TextHeightChanged:
1431 case SfxHintId::TextModified:
1433 ::osl::MutexGuard aInternalGuard(GetMutex());
1434 if (!isAlive())
1435 break;
1436 handleParagraphNotifications();
1437 break;
1439 case SfxHintId::TextViewScrolled:
1441 ::osl::MutexGuard aInternalGuard(GetMutex());
1442 if (!isAlive())
1443 break;
1444 handleParagraphNotifications();
1446 ::sal_Int32 nOffset = static_cast< ::sal_Int32 >(
1447 m_rView.GetStartDocPos().Y());
1448 // XXX numeric overflow
1449 if (nOffset != m_nViewOffset)
1451 m_nViewOffset = nOffset;
1453 Paragraphs::iterator aOldVisibleBegin(visibleBegin());
1454 Paragraphs::iterator aOldVisibleEnd(visibleEnd());
1456 determineVisibleRange();
1458 notifyVisibleRangeChanges(aOldVisibleBegin,
1459 aOldVisibleEnd,
1460 m_aParagraphs.end());
1462 break;
1464 case SfxHintId::TextViewSelectionChanged:
1465 case SfxHintId::TextViewCaretChanged:
1467 ::osl::MutexGuard aInternalGuard(GetMutex());
1468 if (!isAlive())
1469 break;
1471 if (m_aParagraphNotifications.empty())
1473 handleSelectionChangeNotification();
1475 else
1477 // SfxHintId::TextViewSelectionChanged is sometimes sent at
1478 // "unsafe" times (when the text engine has not yet re-
1479 // formatted its content), so that for example calling
1480 // ::TextEngine::GetTextHeight from within the code that
1481 // handles a previous SfxHintId::TextParaInserted causes
1482 // trouble within the text engine. Therefore, these
1483 // hints are just buffered (along with
1484 // SfxHintId::TextParaInserted/REMOVED/FORMATPARA) until a
1485 // following ::TextEngine::FormatDoc causes a
1486 // SfxHintId::TextFormatted to come in:
1487 m_bSelectionChangedNotification = true;
1489 break;
1491 default: break;
1495 IMPL_LINK(Document, WindowEventHandler, ::VclWindowEvent&, rEvent, void)
1497 switch (rEvent.GetId())
1499 case VclEventId::WindowResize:
1501 ::osl::MutexGuard aInternalGuard(GetMutex());
1502 if (!isAlive())
1503 break;
1505 ::sal_Int32 nHeight = static_cast< ::sal_Int32 >(
1506 m_rView.GetWindow()->GetOutputSizePixel().Height());
1507 // XXX numeric overflow
1508 if (nHeight != m_nViewHeight)
1510 m_nViewHeight = nHeight;
1512 Paragraphs::iterator aOldVisibleBegin(visibleBegin());
1513 Paragraphs::iterator aOldVisibleEnd(visibleEnd());
1515 determineVisibleRange();
1517 notifyVisibleRangeChanges(aOldVisibleBegin, aOldVisibleEnd,
1518 m_aParagraphs.end());
1520 break;
1522 case VclEventId::WindowGetFocus:
1524 ::osl::MutexGuard aInternalGuard(GetMutex());
1525 if (!isAlive())
1526 break;
1527 //to enable the PARAGRAPH to get focus for multiline edit
1528 sal_Int64 count = getAccessibleChildCount();
1529 bool bEmpty = m_nFocused == m_nVisibleEnd && count == 1;
1530 if (bEmpty || (m_nFocused >= m_nVisibleBegin && m_nFocused < m_nVisibleEnd))
1532 Paragraphs::iterator aTemp = bEmpty ? visibleBegin() : focused();
1533 ::rtl::Reference< Paragraph > xParagraph(getParagraph(aTemp));
1534 if (xParagraph.is())
1536 xParagraph->notifyEvent(
1537 css::accessibility::AccessibleEventId::
1538 STATE_CHANGED,
1539 css::uno::Any(),
1540 css::uno::Any(
1541 css::accessibility::AccessibleStateType::
1542 FOCUSED));
1545 break;
1547 case VclEventId::WindowLoseFocus:
1549 ::osl::MutexGuard aInternalGuard(GetMutex());
1550 if (!isAlive())
1551 break;
1552 //to enable the PARAGRAPH to get focus for multiline edit
1553 sal_Int64 count = getAccessibleChildCount();
1554 bool bEmpty = m_nFocused == m_nVisibleEnd && count == 1;
1555 if (bEmpty || (m_nFocused >= m_nVisibleBegin && m_nFocused < m_nVisibleEnd))
1557 Paragraphs::iterator aTemp = bEmpty ? visibleBegin() : focused();
1558 ::rtl::Reference< Paragraph > xParagraph(getParagraph(aTemp));
1559 if (xParagraph.is())
1560 xParagraph->notifyEvent(
1561 css::accessibility::AccessibleEventId::
1562 STATE_CHANGED,
1563 css::uno::Any(
1564 css::accessibility::AccessibleStateType::
1565 FOCUSED),
1566 css::uno::Any());
1568 break;
1570 default: break;
1574 ::rtl::Reference< Paragraph >
1575 Document::getParagraph(Paragraphs::iterator const & rIt)
1577 return rIt->getParagraph().get();
1580 css::uno::Reference< css::accessibility::XAccessible >
1581 Document::getAccessibleChild(Paragraphs::iterator const & rIt)
1583 rtl::Reference< Paragraph > xParagraph(rIt->getParagraph());
1584 if (!xParagraph.is())
1586 xParagraph = new Paragraph(this, rIt - m_aParagraphs.begin());
1587 rIt->setParagraph(xParagraph);
1589 return xParagraph;
1592 void Document::determineVisibleRange()
1594 auto const nEnd = m_aParagraphs.size();
1596 m_nVisibleBegin = nEnd;
1597 m_nVisibleEnd = nEnd;
1598 m_nVisibleBeginOffset = 0;
1600 ::sal_Int32 nPos = 0;
1601 for (Paragraphs::size_type i = 0; m_nVisibleEnd == nEnd && i != nEnd; ++i)
1603 ::sal_Int32 const nOldPos = nPos;
1604 nPos += m_aParagraphs[i].getHeight(); // XXX numeric overflow
1605 if (m_nVisibleBegin == nEnd)
1607 if (nPos >= m_nViewOffset)
1609 m_nVisibleBegin = i;
1610 m_nVisibleBeginOffset = m_nViewOffset - nOldPos;
1613 else
1615 if (nPos >= m_nViewOffset + m_nViewHeight) // XXX numeric overflow
1617 m_nVisibleEnd = i;
1622 SAL_WARN_IF(
1623 !((m_nVisibleBegin == m_aParagraphs.size() && m_nVisibleEnd == m_aParagraphs.size() && m_nVisibleBeginOffset == 0)
1624 || (m_nVisibleBegin < m_nVisibleEnd && m_nVisibleBeginOffset >= 0)),
1625 "accessibility",
1626 "invalid visible range");
1629 void Document::notifyVisibleRangeChanges(
1630 Paragraphs::iterator const & rOldVisibleBegin,
1631 Paragraphs::iterator const & rOldVisibleEnd,
1632 Paragraphs::iterator const & rInserted)
1634 // XXX Replace this code that determines which paragraphs have changed from
1635 // invisible to visible or vice versa with a better algorithm.
1636 auto aVisibleBegin = visibleBegin(), aVisibleEnd = visibleEnd();
1637 for (Paragraphs::iterator aIt(rOldVisibleBegin); aIt != rOldVisibleEnd;
1638 ++aIt)
1640 if (aIt != rInserted && (aIt < aVisibleBegin || aIt >= aVisibleEnd))
1641 NotifyAccessibleEvent(
1642 css::accessibility::AccessibleEventId::
1643 CHILD,
1644 css::uno::Any(getAccessibleChild(aIt)),
1645 css::uno::Any());
1647 for (Paragraphs::iterator aIt(aVisibleBegin); aIt != aVisibleEnd; ++aIt)
1649 if (aIt == rInserted
1650 || aIt < rOldVisibleBegin || aIt >= rOldVisibleEnd)
1651 NotifyAccessibleEvent(
1652 css::accessibility::AccessibleEventId::
1653 CHILD,
1654 css::uno::Any(),
1655 css::uno::Any(getAccessibleChild(aIt)));
1659 void
1660 Document::changeParagraphText(::sal_uInt32 nNumber, ::sal_uInt16 nBegin, ::sal_uInt16 nEnd,
1661 bool bCut, bool bPaste,
1662 OUString const & rText)
1664 m_rView.SetSelection(::TextSelection(::TextPaM(nNumber, nBegin),
1665 ::TextPaM(nNumber, nEnd)));
1666 if (bCut)
1667 m_rView.Cut();
1668 else if (nBegin != nEnd)
1669 m_rView.DeleteSelected();
1670 if (bPaste)
1671 m_rView.Paste();
1672 else if (!rText.isEmpty())
1673 m_rView.InsertText(rText);
1676 void Document::handleParagraphNotifications()
1678 // Recursion is possible, e.g. when SfxHintId::TextParaInserted is being handled,
1679 // and TextEngine::GetTextHeight is called for the paragraph being inserted; that
1680 // tries to handle the following SfxHintId::TextFormatPara notification at the
1681 // moment when the respective element hasn't yet been inserted into m_aParagraphs,
1682 // which could crash. Therefore, re-entry is not allowed. The handling is done in
1683 // a loop anyway, so it will process all of them in due order.
1684 // See also comments in Document::Notify.
1685 if (m_bInParagraphNotificationsHandler)
1686 return;
1687 m_bInParagraphNotificationsHandler = true;
1688 while (!m_aParagraphNotifications.empty())
1690 ::TextHint aHint(m_aParagraphNotifications.front());
1691 m_aParagraphNotifications.pop();
1692 switch (aHint.GetId())
1694 case SfxHintId::TextParaInserted:
1696 ::sal_uInt32 n = static_cast< ::sal_uInt32 >( aHint.GetValue() );
1697 assert(n <= m_aParagraphs.size() && "bad SfxHintId::TextParaInserted event");
1699 // Save the values of old offsets, and adjust the old values so that they
1700 // reflect the insertion of the new paragraph:
1701 Paragraphs::size_type nOldVisibleBegin = m_nVisibleBegin;
1702 Paragraphs::size_type nOldVisibleEnd = m_nVisibleEnd;
1703 Paragraphs::size_type nOldFocused = m_nFocused;
1704 if (n <= nOldVisibleBegin)
1705 ++nOldVisibleBegin; // XXX numeric overflow
1706 if (n <= nOldVisibleEnd)
1707 ++nOldVisibleEnd; // XXX numeric overflow
1708 if (n <= nOldFocused)
1709 ++nOldFocused; // XXX numeric overflow
1710 if (sal::static_int_cast<sal_Int32>(n) <= m_nSelectionFirstPara)
1711 ++m_nSelectionFirstPara; // XXX numeric overflow
1712 if (sal::static_int_cast<sal_Int32>(n) <= m_nSelectionLastPara)
1713 ++m_nSelectionLastPara; // XXX numeric overflow
1715 Paragraphs::iterator aIns(
1716 m_aParagraphs.insert(
1717 getIter(n),
1718 ParagraphInfo(static_cast< ::sal_Int32 >(
1719 m_rEngine.GetTextHeight(n)))));
1720 // XXX numeric overflow (2x)
1722 determineVisibleRange();
1723 m_nFocused = nOldFocused;
1725 for (Paragraphs::iterator aIt(aIns);;)
1727 ++aIt;
1728 if (aIt == m_aParagraphs.end())
1729 break;
1730 ::rtl::Reference< Paragraph > xParagraph(
1731 getParagraph(aIt));
1732 if (xParagraph.is())
1733 xParagraph->numberChanged(true);
1736 notifyVisibleRangeChanges(getIter(nOldVisibleBegin), getIter(nOldVisibleEnd), aIns);
1737 break;
1739 case SfxHintId::TextParaRemoved:
1741 ::sal_uInt32 n = static_cast< ::sal_uInt32 >( aHint.GetValue() );
1742 if (n == TEXT_PARA_ALL)
1744 for (Paragraphs::iterator aIt(visibleBegin()), aEnd(visibleEnd());
1745 aIt != aEnd; ++aIt)
1747 NotifyAccessibleEvent(
1748 css::accessibility::AccessibleEventId::
1749 CHILD,
1750 css::uno::Any(getAccessibleChild(aIt)),
1751 css::uno::Any());
1753 disposeParagraphs();
1754 m_aParagraphs.clear();
1755 determineVisibleRange();
1756 m_nSelectionFirstPara = -1;
1757 m_nSelectionFirstPos = -1;
1758 m_nSelectionLastPara = -1;
1759 m_nSelectionLastPos = -1;
1760 m_nFocused = m_aParagraphs.size();
1762 else
1764 assert(n < m_aParagraphs.size() && "Bad SfxHintId::TextParaRemoved event");
1766 Paragraphs::iterator aIt(getIter(n));
1768 // Save the values of old offsets, and adjust the old
1769 // values so that they reflect the removal of the paragraph:
1770 Paragraphs::size_type nOldVisibleBegin = m_nVisibleBegin;
1771 Paragraphs::size_type nOldVisibleEnd = m_nVisibleEnd;
1772 bool bWasVisible
1773 = nOldVisibleBegin <= n && n < nOldVisibleEnd;
1774 Paragraphs::size_type nOldFocused = m_nFocused;
1775 bool bWasFocused = n == m_nFocused;
1776 if (n < nOldVisibleBegin)
1777 --nOldVisibleBegin;
1778 if (n < nOldVisibleEnd)
1779 --nOldVisibleEnd;
1780 if (n < nOldFocused)
1781 --nOldFocused;
1782 if (sal::static_int_cast<sal_Int32>(n) < m_nSelectionFirstPara)
1783 --m_nSelectionFirstPara;
1784 else if (sal::static_int_cast<sal_Int32>(n) == m_nSelectionFirstPara)
1786 if (m_nSelectionFirstPara == m_nSelectionLastPara)
1788 m_nSelectionFirstPara = -1;
1789 m_nSelectionFirstPos = -1;
1790 m_nSelectionLastPara = -1;
1791 m_nSelectionLastPos = -1;
1793 else
1795 ++m_nSelectionFirstPara;
1796 m_nSelectionFirstPos = 0;
1799 if (sal::static_int_cast<sal_Int32>(n) < m_nSelectionLastPara)
1800 --m_nSelectionLastPara;
1801 else if (sal::static_int_cast<sal_Int32>(n) == m_nSelectionLastPara)
1803 assert(m_nSelectionFirstPara < m_nSelectionLastPara && "logic error");
1804 --m_nSelectionLastPara;
1805 m_nSelectionLastPos = 0x7FFFFFFF;
1808 css::uno::Reference< css::accessibility::XAccessible >
1809 xStrong;
1810 if (bWasVisible)
1811 xStrong = getAccessibleChild(aIt);
1812 unotools::WeakReference<Paragraph> xWeak(
1813 aIt->getParagraph());
1814 aIt = m_aParagraphs.erase(aIt);
1816 determineVisibleRange();
1817 m_nFocused = bWasFocused ? m_aParagraphs.size() : nOldFocused;
1819 for (; aIt != m_aParagraphs.end(); ++aIt)
1821 ::rtl::Reference< Paragraph > xParagraph(
1822 getParagraph(aIt));
1823 if (xParagraph.is())
1824 xParagraph->numberChanged(false);
1827 if (bWasVisible)
1828 NotifyAccessibleEvent(
1829 css::accessibility::AccessibleEventId::
1830 CHILD,
1831 css::uno::Any(xStrong),
1832 css::uno::Any());
1834 rtl::Reference< Paragraph > xComponent( xWeak.get() );
1835 if (xComponent.is())
1836 xComponent->dispose();
1838 notifyVisibleRangeChanges(
1839 getIter(nOldVisibleBegin),
1840 getIter(nOldVisibleEnd),
1841 m_aParagraphs.end());
1843 break;
1845 case SfxHintId::TextFormatPara:
1847 ::sal_uInt32 n = static_cast< ::sal_uInt32 >( aHint.GetValue() );
1848 assert(n < m_aParagraphs.size() && "Bad SfxHintId::TextFormatPara event");
1850 m_aParagraphs[static_cast< Paragraphs::size_type >(n)].
1851 changeHeight(static_cast< ::sal_Int32 >(
1852 m_rEngine.GetTextHeight(n)));
1853 // XXX numeric overflow
1854 Paragraphs::iterator aOldVisibleBegin(visibleBegin());
1855 Paragraphs::iterator aOldVisibleEnd(visibleEnd());
1856 determineVisibleRange();
1857 notifyVisibleRangeChanges(aOldVisibleBegin, aOldVisibleEnd,
1858 m_aParagraphs.end());
1860 if (n < m_aParagraphs.size())
1862 Paragraphs::iterator aIt(m_aParagraphs.begin() + n);
1863 ::rtl::Reference< Paragraph > xParagraph(getParagraph(aIt));
1864 if (xParagraph.is())
1865 xParagraph->textChanged();
1867 break;
1869 default:
1870 SAL_WARN("accessibility", "bad buffered hint");
1871 break;
1874 m_bInParagraphNotificationsHandler = false;
1875 if (m_bSelectionChangedNotification)
1877 m_bSelectionChangedNotification = false;
1878 handleSelectionChangeNotification();
1882 namespace
1885 enum class SelChangeType
1887 None, // no change, or invalid
1888 CaretMove, // neither old nor new have selection, and they are different
1889 NoSelToSel, // old has no selection but new has selection
1890 SelToNoSel, // old has selection but new has no selection
1891 // both old and new have selections
1892 NoParaChange, // only end index changed inside end para
1893 EndParaNoMoreBehind, // end para was behind start, but now is same or ahead
1894 AddedFollowingPara, // selection extended to following paragraph(s)
1895 ExcludedPreviousPara, // selection shrunk excluding previous paragraph(s)
1896 ExcludedFollowingPara, // selection shrunk excluding following paragraph(s)
1897 AddedPreviousPara, // selection extended to previous paragraph(s)
1898 EndParaBecameBehind // end para was ahead of start, but now is behind
1901 SelChangeType getSelChangeType(const TextPaM& Os, const TextPaM& Oe,
1902 const TextPaM& Ns, const TextPaM& Ne)
1904 if (Os == Oe) // no old selection
1906 if (Ns == Ne) // no new selection: only caret moves
1907 return Os != Ns ? SelChangeType::CaretMove : SelChangeType::None;
1908 else // old has no selection but new has selection
1909 return SelChangeType::NoSelToSel;
1911 else if (Ns == Ne) // old has selection; no new selection
1913 return SelChangeType::SelToNoSel;
1915 else if (Os == Ns) // both old and new have selections, and their starts are same
1917 const sal_Int32 Osp = Os.GetPara(), Oep = Oe.GetPara();
1918 const sal_Int32 Nsp = Ns.GetPara(), Nep = Ne.GetPara();
1919 if (Oep == Nep) // end of selection stays in the same paragraph
1921 //Send text_selection_change event on Nep
1922 return Oe.GetIndex() != Ne.GetIndex() ? SelChangeType::NoParaChange
1923 : SelChangeType::None;
1925 else if (Oep < Nep) // end of selection moved to a following paragraph
1927 //all the following examples like 1,2->1,3 means that old start select para is 1, old end select para is 2,
1928 // then press shift up, the new start select para is 1, new end select para is 3;
1929 //for example, 1, 2 -> 1, 3; 4,1 -> 4, 7; 4,1 -> 4, 2; 4,4->4,5
1930 if (Nep >= Nsp) // new end para not behind start
1932 // 1, 2 -> 1, 3; 4, 1 -> 4, 7; 4,4->4,5;
1933 if (Oep < Osp) // old end was behind start
1935 // 4,1 -> 4,7; 4,1 -> 4,4
1936 return SelChangeType::EndParaNoMoreBehind;
1938 else // old end para wasn't behind start
1940 // 1, 2 -> 1, 3; 4,4->4,5;
1941 return SelChangeType::AddedFollowingPara;
1944 else // new end para is still behind start
1946 // 4,1 -> 4,2,
1947 return SelChangeType::ExcludedPreviousPara;
1950 else // Oep > Nep => end of selection moved to a previous paragraph
1952 // 3,2 -> 3,1; 4,7 -> 4,1; 4, 7 -> 4,6; 4,4 -> 4,3
1953 if (Nep >= Nsp) // new end para is still not behind of start
1955 // 4,7 ->4,6
1956 return SelChangeType::ExcludedFollowingPara;
1958 else // new end para is behind start
1960 // 3,2 -> 3,1, 4,7 -> 4,1; 4,4->4,3
1961 if (Oep <= Osp) // it was not ahead already
1963 // 3,2 -> 3,1; 4,4->4,3
1964 return SelChangeType::AddedPreviousPara;
1966 else // it was ahead previously
1968 // 4,7 -> 4,1
1969 return SelChangeType::EndParaBecameBehind;
1974 return SelChangeType::None;
1977 } // namespace
1979 void Document::sendEvent(::sal_Int32 start, ::sal_Int32 end, ::sal_Int16 nEventId)
1981 size_t nAvailDistance = std::distance(m_aParagraphs.begin(), visibleEnd());
1983 Paragraphs::iterator aEnd(m_aParagraphs.begin());
1984 size_t nEndDistance = std::min<size_t>(end + 1, nAvailDistance);
1985 std::advance(aEnd, nEndDistance);
1987 Paragraphs::iterator aIt(m_aParagraphs.begin());
1988 size_t nStartDistance = std::min<size_t>(start, nAvailDistance);
1989 std::advance(aIt, nStartDistance);
1991 while (aIt < aEnd)
1993 ::rtl::Reference< Paragraph > xParagraph(getParagraph(aIt));
1994 if (xParagraph.is())
1995 xParagraph->notifyEvent(
1996 nEventId,
1997 css::uno::Any(), css::uno::Any());
1998 ++aIt;
2002 void Document::handleSelectionChangeNotification()
2004 ::TextSelection const & rSelection = m_rView.GetSelection();
2005 assert(rSelection.GetStart().GetPara() < m_aParagraphs.size() &&
2006 rSelection.GetEnd().GetPara() < m_aParagraphs.size() &&
2007 "bad SfxHintId::TextViewSelectionChanged event");
2008 ::sal_Int32 nNewFirstPara
2009 = static_cast< ::sal_Int32 >(rSelection.GetStart().GetPara());
2010 ::sal_Int32 nNewFirstPos = rSelection.GetStart().GetIndex();
2011 // XXX numeric overflow
2012 ::sal_Int32 nNewLastPara
2013 = static_cast< ::sal_Int32 >(rSelection.GetEnd().GetPara());
2014 ::sal_Int32 nNewLastPos = rSelection.GetEnd().GetIndex();
2015 // XXX numeric overflow
2017 // Lose focus:
2018 Paragraphs::iterator aIt(getIter(nNewLastPara));
2019 if (m_nFocused < m_aParagraphs.size() && focused() != aIt
2020 && m_nFocused >= m_nVisibleBegin && m_nFocused < m_nVisibleEnd)
2022 ::rtl::Reference< Paragraph > xParagraph(getParagraph(focused()));
2023 if (xParagraph.is())
2024 xParagraph->notifyEvent(
2025 css::accessibility::AccessibleEventId::
2026 STATE_CHANGED,
2027 css::uno::Any(
2028 css::accessibility::AccessibleStateType::FOCUSED),
2029 css::uno::Any());
2032 // Gain focus and update cursor position:
2033 if (aIt >= visibleBegin() && aIt < visibleEnd()
2034 && (aIt != focused()
2035 || nNewLastPara != m_nSelectionLastPara
2036 || nNewLastPos != m_nSelectionLastPos))
2038 ::rtl::Reference< Paragraph > xParagraph(getParagraph(aIt));
2039 if (xParagraph.is())
2041 //disable the first event when user types in empty field.
2042 sal_Int64 count = getAccessibleChildCount();
2043 bool bEmpty = count > 1;
2044 if (aIt != focused() && bEmpty)
2045 xParagraph->notifyEvent(
2046 css::accessibility::AccessibleEventId::
2047 STATE_CHANGED,
2048 css::uno::Any(),
2049 css::uno::Any(
2050 css::accessibility::AccessibleStateType::FOCUSED));
2051 if (nNewLastPara != m_nSelectionLastPara
2052 || nNewLastPos != m_nSelectionLastPos)
2053 xParagraph->notifyEvent(
2054 css::accessibility::AccessibleEventId::
2055 CARET_CHANGED,
2056 css::uno::Any( ::sal_Int32 (
2057 nNewLastPara == m_nSelectionLastPara
2058 ? m_nSelectionLastPos : 0)),
2059 css::uno::Any(nNewLastPos));
2062 m_nFocused = std::distance(m_aParagraphs.begin(), aIt);
2064 if (m_nSelectionFirstPara != -1)
2066 sal_Int32 nMin;
2067 sal_Int32 nMax;
2068 SelChangeType ret = getSelChangeType(TextPaM(m_nSelectionFirstPara, m_nSelectionFirstPos),
2069 TextPaM(m_nSelectionLastPara, m_nSelectionLastPos),
2070 rSelection.GetStart(), rSelection.GetEnd());
2071 switch (ret)
2073 case SelChangeType::None:
2074 //no event
2075 break;
2076 case SelChangeType::CaretMove:
2077 //only caret moved, already handled in above
2078 break;
2079 case SelChangeType::NoSelToSel:
2080 //old has no selection but new has selection
2081 nMin = std::min(nNewFirstPara, nNewLastPara);
2082 nMax = std::max(nNewFirstPara, nNewLastPara);
2083 sendEvent(nMin, nMax, css::accessibility::AccessibleEventId::SELECTION_CHANGED);
2084 sendEvent(nMin, nMax,
2085 css::accessibility::AccessibleEventId::TEXT_SELECTION_CHANGED);
2086 break;
2087 case SelChangeType::SelToNoSel:
2088 //old has selection but new has no selection.
2089 nMin = std::min(m_nSelectionFirstPara, m_nSelectionLastPara);
2090 nMax = std::max(m_nSelectionFirstPara, m_nSelectionLastPara);
2091 sendEvent(nMin, nMax, css::accessibility::AccessibleEventId::SELECTION_CHANGED);
2092 sendEvent(nMin, nMax,
2093 css::accessibility::AccessibleEventId::TEXT_SELECTION_CHANGED);
2094 break;
2095 case SelChangeType::NoParaChange:
2096 //Send text_selection_change event on Nep
2097 sendEvent(nNewLastPara, nNewLastPara,
2098 css::accessibility::AccessibleEventId::TEXT_SELECTION_CHANGED);
2099 break;
2100 case SelChangeType::EndParaNoMoreBehind:
2101 // 4, 1 -> 4, 7; 4,1 -> 4,4
2102 sendEvent(m_nSelectionLastPara, m_nSelectionFirstPara - 1,
2103 css::accessibility::AccessibleEventId::SELECTION_CHANGED);
2104 sendEvent(nNewFirstPara + 1, nNewLastPara,
2105 css::accessibility::AccessibleEventId::SELECTION_CHANGED);
2107 sendEvent(m_nSelectionLastPara, nNewLastPara,
2108 css::accessibility::AccessibleEventId::TEXT_SELECTION_CHANGED);
2109 break;
2110 case SelChangeType::AddedFollowingPara:
2111 // 1, 2 -> 1, 4; 4,4->4,5;
2112 sendEvent(m_nSelectionLastPara + 1, nNewLastPara,
2113 css::accessibility::AccessibleEventId::SELECTION_CHANGED);
2115 sendEvent(m_nSelectionLastPara, nNewLastPara,
2116 css::accessibility::AccessibleEventId::TEXT_SELECTION_CHANGED);
2117 break;
2118 case SelChangeType::ExcludedPreviousPara:
2119 // 4,1 -> 4,3,
2120 sendEvent(m_nSelectionLastPara + 1, nNewLastPara,
2121 css::accessibility::AccessibleEventId::SELECTION_CHANGED);
2123 sendEvent(m_nSelectionLastPara, nNewLastPara,
2124 css::accessibility::AccessibleEventId::TEXT_SELECTION_CHANGED);
2125 break;
2126 case SelChangeType::ExcludedFollowingPara:
2127 // 4,7 ->4,5;
2128 sendEvent(nNewLastPara + 1, m_nSelectionLastPara,
2129 css::accessibility::AccessibleEventId::SELECTION_CHANGED);
2131 sendEvent(nNewLastPara, m_nSelectionLastPara,
2132 css::accessibility::AccessibleEventId::TEXT_SELECTION_CHANGED);
2133 break;
2134 case SelChangeType::AddedPreviousPara:
2135 // 3,2 -> 3,1; 4,4->4,3
2136 sendEvent(nNewLastPara, m_nSelectionLastPara - 1,
2137 css::accessibility::AccessibleEventId::SELECTION_CHANGED);
2139 sendEvent(nNewLastPara, m_nSelectionLastPara,
2140 css::accessibility::AccessibleEventId::TEXT_SELECTION_CHANGED);
2141 break;
2142 case SelChangeType::EndParaBecameBehind:
2143 // 4,7 -> 4,1
2144 sendEvent(m_nSelectionFirstPara + 1, m_nSelectionLastPara,
2145 css::accessibility::AccessibleEventId::SELECTION_CHANGED);
2146 sendEvent(nNewLastPara, nNewFirstPara - 1,
2147 css::accessibility::AccessibleEventId::SELECTION_CHANGED);
2149 sendEvent(nNewLastPara, m_nSelectionLastPara,
2150 css::accessibility::AccessibleEventId::TEXT_SELECTION_CHANGED);
2151 break;
2155 m_nSelectionFirstPara = nNewFirstPara;
2156 m_nSelectionFirstPos = nNewFirstPos;
2157 m_nSelectionLastPara = nNewLastPara;
2158 m_nSelectionLastPos = nNewLastPos;
2161 void Document::disposeParagraphs()
2163 for (auto const& paragraph : m_aParagraphs)
2165 rtl::Reference< Paragraph > xComponent(
2166 paragraph.getParagraph().get() );
2167 if (xComponent.is())
2168 xComponent->dispose();
2172 // static
2173 css::uno::Any Document::mapFontColor(::Color const & rColor)
2175 return css::uno::Any(rColor.GetRGBColor());
2176 // FIXME keep transparency?
2179 // static
2180 ::Color Document::mapFontColor(css::uno::Any const & rColor)
2182 ::Color nColor;
2183 rColor >>= nColor;
2184 return nColor;
2187 // static
2188 css::uno::Any Document::mapFontWeight(::FontWeight nWeight)
2190 // Map from ::FontWeight to css::awt::FontWeight, depends on order of
2191 // elements in ::FontWeight (vcl/vclenum.hxx):
2192 static float const aWeight[]
2193 = { css::awt::FontWeight::DONTKNOW, // WEIGHT_DONTKNOW
2194 css::awt::FontWeight::THIN, // WEIGHT_THIN
2195 css::awt::FontWeight::ULTRALIGHT, // WEIGHT_ULTRALIGHT
2196 css::awt::FontWeight::LIGHT, // WEIGHT_LIGHT
2197 css::awt::FontWeight::SEMILIGHT, // WEIGHT_SEMILIGHT
2198 css::awt::FontWeight::NORMAL, // WEIGHT_NORMAL
2199 css::awt::FontWeight::NORMAL, // WEIGHT_MEDIUM
2200 css::awt::FontWeight::SEMIBOLD, // WEIGHT_SEMIBOLD
2201 css::awt::FontWeight::BOLD, // WEIGHT_BOLD
2202 css::awt::FontWeight::ULTRABOLD, // WEIGHT_ULTRABOLD
2203 css::awt::FontWeight::BLACK }; // WEIGHT_BLACK
2204 return css::uno::Any(aWeight[nWeight]);
2207 // static
2208 ::FontWeight Document::mapFontWeight(css::uno::Any const & rWeight)
2210 float nWeight = css::awt::FontWeight::NORMAL;
2211 rWeight >>= nWeight;
2212 return nWeight <= css::awt::FontWeight::DONTKNOW ? WEIGHT_DONTKNOW
2213 : nWeight <= css::awt::FontWeight::THIN ? WEIGHT_THIN
2214 : nWeight <= css::awt::FontWeight::ULTRALIGHT ? WEIGHT_ULTRALIGHT
2215 : nWeight <= css::awt::FontWeight::LIGHT ? WEIGHT_LIGHT
2216 : nWeight <= css::awt::FontWeight::SEMILIGHT ? WEIGHT_SEMILIGHT
2217 : nWeight <= css::awt::FontWeight::NORMAL ? WEIGHT_NORMAL
2218 : nWeight <= css::awt::FontWeight::SEMIBOLD ? WEIGHT_SEMIBOLD
2219 : nWeight <= css::awt::FontWeight::BOLD ? WEIGHT_BOLD
2220 : nWeight <= css::awt::FontWeight::ULTRABOLD ? WEIGHT_ULTRABOLD
2221 : WEIGHT_BLACK;
2226 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */