LanguageTool: don't crash if REST protocol isn't set
[LibreOffice.git] / accessibility / source / extended / textwindowaccessibility.cxx
blob4b37b1c78dd770b3d58b62a85a563bbbdf6d6eff
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>
21 #include <sal/log.hxx>
23 #include <com/sun/star/accessibility/AccessibleEventId.hpp>
24 #include <com/sun/star/accessibility/AccessibleRelationType.hpp>
25 #include <com/sun/star/accessibility/AccessibleRole.hpp>
26 #include <com/sun/star/accessibility/AccessibleStateType.hpp>
27 #include <com/sun/star/awt/FontWeight.hpp>
28 #include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
29 #include <com/sun/star/lang/WrappedTargetRuntimeException.hpp>
30 #include <com/sun/star/i18n/Boundary.hpp>
31 #include <cppuhelper/exc_hlp.hxx>
32 #include <extended/textwindowaccessibility.hxx>
33 #include <comphelper/accessibleeventnotifier.hxx>
34 #include <unotools/accessiblerelationsethelper.hxx>
35 #include <unotools/accessiblestatesethelper.hxx>
36 #include <vcl/svapp.hxx>
37 #include <vcl/txtattr.hxx>
38 #include <vcl/window.hxx>
39 #include <tools/diagnose_ex.h>
40 #include <toolkit/helper/convert.hxx>
41 #include <comphelper/sequence.hxx>
43 #include <algorithm>
44 #include <memory>
45 #include <numeric>
46 #include <vector>
48 namespace accessibility
50 void SfxListenerGuard::startListening(::SfxBroadcaster & rNotifier)
52 assert(m_pNotifier == nullptr && "called more than once");
53 m_pNotifier = &rNotifier;
54 m_rListener.StartListening(*m_pNotifier, DuplicateHandling::Prevent);
57 void SfxListenerGuard::endListening()
59 if (m_pNotifier != nullptr)
61 m_rListener.EndListening(*m_pNotifier);
62 m_pNotifier = nullptr;
66 void WindowListenerGuard::startListening(vcl::Window & rNotifier)
68 assert(m_pNotifier == nullptr && "called more than once");
69 m_pNotifier = &rNotifier;
70 m_pNotifier->AddEventListener(m_aListener);
73 void WindowListenerGuard::endListening()
75 if (m_pNotifier)
77 m_pNotifier->RemoveEventListener(m_aListener);
78 m_pNotifier = nullptr;
82 Paragraph::Paragraph(::rtl::Reference< Document > const & rDocument,
83 Paragraphs::size_type nNumber):
84 ParagraphBase(m_aMutex),
85 m_xDocument(rDocument),
86 m_nNumber(nNumber),
87 m_nClientId(0)
89 m_aParagraphText = m_xDocument->retrieveParagraphText(this);
92 void
93 Paragraph::numberChanged(bool bIncremented)
95 if (bIncremented)
96 ++m_nNumber;
97 else
98 --m_nNumber;
101 void Paragraph::textChanged()
103 OUString aParagraphText = implGetText();
104 css::uno::Any aOldValue, aNewValue;
105 if ( implInitTextChangedEvent( m_aParagraphText, aParagraphText, aOldValue, aNewValue ) )
107 m_aParagraphText = aParagraphText;
108 notifyEvent(css::accessibility::AccessibleEventId::
109 TEXT_CHANGED,
110 aOldValue, aNewValue);
114 void Paragraph::notifyEvent(::sal_Int16 nEventId,
115 css::uno::Any const & rOldValue,
116 css::uno::Any const & rNewValue)
118 if (m_nClientId)
119 comphelper::AccessibleEventNotifier::addEvent( m_nClientId, css::accessibility::AccessibleEventObject(
120 static_cast< ::cppu::OWeakObject * >(this),
121 nEventId, rNewValue, rOldValue) );
124 // virtual
125 css::uno::Reference< css::accessibility::XAccessibleContext > SAL_CALL
126 Paragraph::getAccessibleContext()
128 checkDisposed();
129 return this;
132 // virtual
133 ::sal_Int32 SAL_CALL Paragraph::getAccessibleChildCount()
135 checkDisposed();
136 return 0;
139 // virtual
140 css::uno::Reference< css::accessibility::XAccessible > SAL_CALL
141 Paragraph::getAccessibleChild(::sal_Int32)
143 checkDisposed();
144 throw css::lang::IndexOutOfBoundsException(
145 "textwindowaccessibility.cxx:"
146 " Paragraph::getAccessibleChild",
147 static_cast< css::uno::XWeak * >(this));
150 // virtual
151 css::uno::Reference< css::accessibility::XAccessible > SAL_CALL
152 Paragraph::getAccessibleParent()
154 checkDisposed();
155 return m_xDocument->getAccessible();
158 // virtual
159 ::sal_Int32 SAL_CALL Paragraph::getAccessibleIndexInParent()
161 checkDisposed();
162 return m_xDocument->retrieveParagraphIndex(this);
165 // virtual
166 ::sal_Int16 SAL_CALL Paragraph::getAccessibleRole()
168 checkDisposed();
169 return css::accessibility::AccessibleRole::PARAGRAPH;
172 // virtual
173 OUString SAL_CALL Paragraph::getAccessibleDescription()
175 checkDisposed();
176 return OUString();
179 // virtual
180 OUString SAL_CALL Paragraph::getAccessibleName()
182 checkDisposed();
183 return OUString();
186 // virtual
187 css::uno::Reference< css::accessibility::XAccessibleRelationSet >
188 SAL_CALL Paragraph::getAccessibleRelationSet()
190 checkDisposed();
191 return m_xDocument->retrieveParagraphRelationSet( this );
194 // virtual
195 css::uno::Reference< css::accessibility::XAccessibleStateSet >
196 SAL_CALL Paragraph::getAccessibleStateSet()
198 checkDisposed();
200 // FIXME Notification of changes (STATE_CHANGED) missing when
201 // m_rView.IsReadOnly() changes:
202 return new ::utl::AccessibleStateSetHelper(
203 m_xDocument->retrieveParagraphState(this));
206 // virtual
207 css::lang::Locale SAL_CALL Paragraph::getLocale()
209 checkDisposed();
210 return m_xDocument->retrieveLocale();
213 // virtual
214 sal_Bool SAL_CALL Paragraph::containsPoint(css::awt::Point const & rPoint)
216 checkDisposed();
217 css::awt::Rectangle aRect(m_xDocument->retrieveParagraphBounds(this,
218 false));
219 return rPoint.X >= 0 && rPoint.X < aRect.Width
220 && rPoint.Y >= 0 && rPoint.Y < aRect.Height;
223 // virtual
224 css::uno::Reference< css::accessibility::XAccessible > SAL_CALL
225 Paragraph::getAccessibleAtPoint(css::awt::Point const &)
227 checkDisposed();
228 return nullptr;
231 // virtual
232 css::awt::Rectangle SAL_CALL Paragraph::getBounds()
234 checkDisposed();
235 return m_xDocument->retrieveParagraphBounds(this, false);
238 // virtual
239 css::awt::Point SAL_CALL Paragraph::getLocation()
241 checkDisposed();
242 css::awt::Rectangle aRect(m_xDocument->retrieveParagraphBounds(this,
243 false));
244 return css::awt::Point(aRect.X, aRect.Y);
247 // virtual
248 css::awt::Point SAL_CALL Paragraph::getLocationOnScreen()
250 checkDisposed();
251 css::awt::Rectangle aRect(m_xDocument->retrieveParagraphBounds(this,
252 true));
253 return css::awt::Point(aRect.X, aRect.Y);
256 // virtual
257 css::awt::Size SAL_CALL Paragraph::getSize()
259 checkDisposed();
260 css::awt::Rectangle aRect(m_xDocument->retrieveParagraphBounds(this,
261 false));
262 return css::awt::Size(aRect.Width, aRect.Height);
265 // virtual
266 void SAL_CALL Paragraph::grabFocus()
268 checkDisposed();
269 VclPtr<vcl::Window> pWindow = m_xDocument->GetWindow();
270 if ( pWindow )
272 pWindow->GrabFocus();
276 m_xDocument->changeParagraphSelection(this, 0, 0);
278 catch (const css::lang::IndexOutOfBoundsException &)
280 TOOLS_INFO_EXCEPTION("accessibility", "Paragraph::grabFocus: caught unexpected");
284 // virtual
285 sal_Int32 SAL_CALL Paragraph::getForeground()
287 return 0; // TODO
290 // virtual
291 sal_Int32 SAL_CALL Paragraph::getBackground()
293 return 0; // TODO
296 // virtual
297 ::sal_Int32 SAL_CALL Paragraph::getCaretPosition()
299 checkDisposed();
300 return m_xDocument->retrieveParagraphCaretPosition(this);
303 // virtual
304 sal_Bool SAL_CALL Paragraph::setCaretPosition(::sal_Int32 nIndex)
306 checkDisposed();
307 m_xDocument->changeParagraphSelection(this, nIndex, nIndex);
308 return true;
311 // virtual
312 ::sal_Unicode SAL_CALL Paragraph::getCharacter(::sal_Int32 nIndex)
314 checkDisposed();
315 return OCommonAccessibleText::implGetCharacter(implGetText(), nIndex);
318 // virtual
319 css::uno::Sequence< css::beans::PropertyValue > SAL_CALL
320 Paragraph::getCharacterAttributes(::sal_Int32 nIndex, const css::uno::Sequence< OUString >& aRequestedAttributes)
322 checkDisposed();
323 return m_xDocument->retrieveCharacterAttributes( this, nIndex, aRequestedAttributes );
326 // virtual
327 css::awt::Rectangle SAL_CALL
328 Paragraph::getCharacterBounds(::sal_Int32 nIndex)
330 checkDisposed();
331 css::awt::Rectangle aBounds(m_xDocument->retrieveCharacterBounds(this, nIndex));
332 css::awt::Rectangle aParaBounds(m_xDocument->retrieveParagraphBounds(this, false));
333 aBounds.X -= aParaBounds.X;
334 aBounds.Y -= aParaBounds.Y;
335 return aBounds;
338 // virtual
339 ::sal_Int32 SAL_CALL Paragraph::getCharacterCount()
341 checkDisposed();
342 return implGetText().getLength();
345 // virtual
346 ::sal_Int32 SAL_CALL
347 Paragraph::getIndexAtPoint(css::awt::Point const & rPoint)
349 checkDisposed();
350 css::awt::Point aPoint(rPoint);
351 css::awt::Rectangle aParaBounds(m_xDocument->retrieveParagraphBounds(this, false));
352 aPoint.X += aParaBounds.X;
353 aPoint.Y += aParaBounds.Y;
354 return m_xDocument->retrieveCharacterIndex(this, aPoint);
357 // virtual
358 OUString SAL_CALL Paragraph::getSelectedText()
360 checkDisposed();
362 return OCommonAccessibleText::getSelectedText();
365 // virtual
366 ::sal_Int32 SAL_CALL Paragraph::getSelectionStart()
368 checkDisposed();
369 return OCommonAccessibleText::getSelectionStart();
372 // virtual
373 ::sal_Int32 SAL_CALL Paragraph::getSelectionEnd()
375 checkDisposed();
376 return OCommonAccessibleText::getSelectionEnd();
379 // virtual
380 sal_Bool SAL_CALL Paragraph::setSelection(::sal_Int32 nStartIndex,
381 ::sal_Int32 nEndIndex)
383 checkDisposed();
384 m_xDocument->changeParagraphSelection(this, nStartIndex, nEndIndex);
385 return true;
388 // virtual
389 OUString SAL_CALL Paragraph::getText()
391 checkDisposed();
392 return implGetText();
395 // virtual
396 OUString SAL_CALL Paragraph::getTextRange(::sal_Int32 nStartIndex,
397 ::sal_Int32 nEndIndex)
399 checkDisposed();
400 return OCommonAccessibleText::implGetTextRange(implGetText(), nStartIndex, nEndIndex);
403 // virtual
404 css::accessibility::TextSegment SAL_CALL Paragraph::getTextAtIndex( sal_Int32 nIndex, sal_Int16 aTextType )
406 checkDisposed();
407 return OCommonAccessibleText::getTextAtIndex(nIndex, aTextType);
410 // virtual
411 css::accessibility::TextSegment SAL_CALL Paragraph::getTextBeforeIndex( sal_Int32 nIndex, sal_Int16 aTextType )
413 checkDisposed();
414 return OCommonAccessibleText::getTextBeforeIndex(nIndex, aTextType);
417 // virtual
418 css::accessibility::TextSegment SAL_CALL Paragraph::getTextBehindIndex( sal_Int32 nIndex, sal_Int16 aTextType )
420 checkDisposed();
421 return OCommonAccessibleText::getTextBehindIndex(nIndex, aTextType);
424 // virtual
425 sal_Bool SAL_CALL Paragraph::copyText(::sal_Int32 nStartIndex,
426 ::sal_Int32 nEndIndex)
428 checkDisposed();
429 m_xDocument->copyParagraphText(this, nStartIndex, nEndIndex);
430 return true;
433 // virtual
434 sal_Bool SAL_CALL Paragraph::scrollSubstringTo( sal_Int32, sal_Int32, css::accessibility::AccessibleScrollType )
436 return false;
439 // virtual
440 sal_Bool SAL_CALL Paragraph::cutText(::sal_Int32 nStartIndex,
441 ::sal_Int32 nEndIndex)
443 checkDisposed();
444 m_xDocument->changeParagraphText(this, nStartIndex, nEndIndex, true, false,
445 OUString());
446 return true;
449 // virtual
450 sal_Bool SAL_CALL Paragraph::pasteText(::sal_Int32 nIndex)
452 checkDisposed();
453 m_xDocument->changeParagraphText(this, nIndex, nIndex, false, true,
454 OUString());
455 return true;
458 // virtual
459 sal_Bool SAL_CALL Paragraph::deleteText(::sal_Int32 nStartIndex,
460 ::sal_Int32 nEndIndex)
462 checkDisposed();
463 m_xDocument->changeParagraphText(this, nStartIndex, nEndIndex, false, false,
464 OUString());
465 return true;
468 // virtual
469 sal_Bool SAL_CALL Paragraph::insertText(OUString const & rText,
470 ::sal_Int32 nIndex)
472 checkDisposed();
473 m_xDocument->changeParagraphText(this, nIndex, nIndex, false, false, rText);
474 return true;
477 // virtual
478 sal_Bool SAL_CALL
479 Paragraph::replaceText(::sal_Int32 nStartIndex, ::sal_Int32 nEndIndex,
480 OUString const & rReplacement)
482 checkDisposed();
483 m_xDocument->changeParagraphText(this, nStartIndex, nEndIndex, false, false,
484 rReplacement);
485 return true;
488 // virtual
489 sal_Bool SAL_CALL Paragraph::setAttributes(
490 ::sal_Int32 nStartIndex, ::sal_Int32 nEndIndex,
491 css::uno::Sequence< css::beans::PropertyValue > const & rAttributeSet)
493 checkDisposed();
494 m_xDocument->changeParagraphAttributes(this, nStartIndex, nEndIndex,
495 rAttributeSet);
496 return true;
499 // virtual
500 sal_Bool SAL_CALL Paragraph::setText(OUString const & rText)
502 checkDisposed();
503 m_xDocument->changeParagraphText(this, rText);
504 return true;
507 // virtual
508 css::uno::Sequence< css::beans::PropertyValue > SAL_CALL
509 Paragraph::getDefaultAttributes(const css::uno::Sequence< OUString >&)
511 checkDisposed();
512 return {}; // default attributes are not supported by text engine
515 // virtual
516 css::uno::Sequence< css::beans::PropertyValue > SAL_CALL
517 Paragraph::getRunAttributes(::sal_Int32 Index, const css::uno::Sequence< OUString >& RequestedAttributes)
519 checkDisposed();
520 return m_xDocument->retrieveRunAttributes( this, Index, RequestedAttributes );
523 // virtual
524 ::sal_Int32 SAL_CALL Paragraph::getLineNumberAtIndex( ::sal_Int32 nIndex )
526 checkDisposed();
528 ::sal_Int32 nLineNo = -1;
529 m_xDocument->retrieveParagraphLineBoundary( this, nIndex, &nLineNo );
531 return nLineNo;
534 // virtual
535 css::accessibility::TextSegment SAL_CALL Paragraph::getTextAtLineNumber( ::sal_Int32 nLineNo )
537 checkDisposed();
539 css::i18n::Boundary aBoundary =
540 m_xDocument->retrieveParagraphBoundaryOfLine( this, nLineNo );
542 return css::accessibility::TextSegment( getTextRange(aBoundary.startPos, aBoundary.endPos),
543 aBoundary.startPos, aBoundary.endPos);
546 // virtual
547 css::accessibility::TextSegment SAL_CALL Paragraph::getTextAtLineWithCaret( )
549 checkDisposed();
551 sal_Int32 nLineNo = getNumberOfLineWithCaret();
553 try {
554 return ( nLineNo >= 0 ) ?
555 getTextAtLineNumber( nLineNo ) :
556 css::accessibility::TextSegment();
557 } catch (const css::lang::IndexOutOfBoundsException&) {
558 css::uno::Any anyEx = cppu::getCaughtException();
559 throw css::lang::WrappedTargetRuntimeException(
560 "textwindowaccessibility.cxx:"
561 " Paragraph::getTextAtLineWithCaret",
562 static_cast< css::uno::XWeak * >( this ), anyEx );
566 // virtual
567 ::sal_Int32 SAL_CALL Paragraph::getNumberOfLineWithCaret( )
569 checkDisposed();
570 return m_xDocument->retrieveParagraphLineWithCursor(this);
574 // virtual
575 void SAL_CALL Paragraph::addAccessibleEventListener(
576 css::uno::Reference<
577 css::accessibility::XAccessibleEventListener > const & rListener)
579 if (!rListener.is())
580 return;
582 ::osl::ClearableMutexGuard aGuard(rBHelper.rMutex);
583 if (rBHelper.bDisposed || rBHelper.bInDispose)
585 aGuard.clear();
586 rListener->disposing(css::lang::EventObject(
587 static_cast< ::cppu::OWeakObject * >(this)));
589 else
591 if (!m_nClientId)
592 m_nClientId = comphelper::AccessibleEventNotifier::registerClient( );
593 comphelper::AccessibleEventNotifier::addEventListener( m_nClientId, rListener );
597 // virtual
598 void SAL_CALL Paragraph::removeAccessibleEventListener(
599 css::uno::Reference<
600 css::accessibility::XAccessibleEventListener > const & rListener)
602 comphelper::AccessibleEventNotifier::TClientId nId = 0;
604 osl::MutexGuard aGuard(rBHelper.rMutex);
605 if (rListener.is() && m_nClientId != 0
606 && comphelper::AccessibleEventNotifier::removeEventListener( m_nClientId, rListener ) == 0)
608 nId = m_nClientId;
609 m_nClientId = 0;
612 if (nId != 0)
614 // no listeners anymore
615 // -> revoke ourself. This may lead to the notifier thread dying (if we were the last client),
616 // and at least to us not firing any events anymore, in case somebody calls
617 // NotifyAccessibleEvent, again
618 comphelper::AccessibleEventNotifier::revokeClient(nId);
622 // virtual
623 void SAL_CALL Paragraph::disposing()
625 comphelper::AccessibleEventNotifier::TClientId nId = 0;
627 osl::MutexGuard aGuard(rBHelper.rMutex);
628 nId = m_nClientId;
629 m_nClientId = 0;
631 if (nId != 0)
632 comphelper::AccessibleEventNotifier::revokeClientNotifyDisposing(nId, *this);
635 // virtual
636 OUString Paragraph::implGetText()
638 return m_xDocument->retrieveParagraphText(this);
641 // virtual
642 css::lang::Locale Paragraph::implGetLocale()
644 return m_xDocument->retrieveLocale();
647 // virtual
648 void Paragraph::implGetSelection(::sal_Int32 & rStartIndex,
649 ::sal_Int32 & rEndIndex)
651 m_xDocument->retrieveParagraphSelection(this, &rStartIndex, &rEndIndex);
654 // virtual
655 void Paragraph::implGetParagraphBoundary( const OUString& rText,
656 css::i18n::Boundary& rBoundary,
657 ::sal_Int32 nIndex )
659 ::sal_Int32 nLength = rText.getLength();
661 if ( implIsValidIndex( nIndex, nLength ) )
663 rBoundary.startPos = 0;
664 rBoundary.endPos = nLength;
666 else
668 rBoundary.startPos = nIndex;
669 rBoundary.endPos = nIndex;
673 // virtual
674 void Paragraph::implGetLineBoundary( const OUString& rText,
675 css::i18n::Boundary& rBoundary,
676 ::sal_Int32 nIndex )
678 ::sal_Int32 nLength = rText.getLength();
680 if ( implIsValidIndex( nIndex, nLength ) || nIndex == nLength )
682 css::i18n::Boundary aBoundary =
683 m_xDocument->retrieveParagraphLineBoundary( this, nIndex, nullptr );
684 rBoundary.startPos = aBoundary.startPos;
685 rBoundary.endPos = aBoundary.endPos;
687 else
689 rBoundary.startPos = nIndex;
690 rBoundary.endPos = nIndex;
695 void Paragraph::checkDisposed()
697 ::osl::MutexGuard aGuard(rBHelper.rMutex);
698 if (!(rBHelper.bDisposed || rBHelper.bInDispose))
699 return;
700 throw css::lang::DisposedException(
701 OUString(), static_cast< css::uno::XWeak * >(this));
704 Document::Document(::VCLXWindow * pVclXWindow, ::TextEngine & rEngine,
705 ::TextView & rView):
706 VCLXAccessibleComponent(pVclXWindow),
707 m_xAccessible(pVclXWindow),
708 m_rEngine(rEngine),
709 m_rView(rView),
710 m_aEngineListener(*this),
711 m_aViewListener(LINK(this, Document, WindowEventHandler)),
712 m_nViewOffset(0),
713 m_nViewHeight(0),
714 m_nVisibleBeginOffset(0),
715 m_nSelectionFirstPara(-1),
716 m_nSelectionFirstPos(-1),
717 m_nSelectionLastPara(-1),
718 m_nSelectionLastPos(-1),
719 m_bSelectionChangedNotification(false)
722 css::lang::Locale Document::retrieveLocale()
724 SolarMutexGuard aGuard;
725 return m_rEngine.GetLocale();
728 ::sal_Int32 Document::retrieveParagraphIndex(Paragraph const * pParagraph)
730 ::osl::MutexGuard aInternalGuard(GetMutex());
732 // If a client holds on to a Paragraph that is no longer visible, it can
733 // happen that this Paragraph lies outside the range from m_aVisibleBegin
734 // to m_aVisibleEnd. In that case, return -1 instead of a valid index:
735 Paragraphs::iterator aPara(m_xParagraphs->begin()
736 + pParagraph->getNumber());
737 return aPara < m_aVisibleBegin || aPara >= m_aVisibleEnd
738 ? -1 : static_cast< ::sal_Int32 >(aPara - m_aVisibleBegin);
739 // XXX numeric overflow
742 ::sal_Int64 Document::retrieveParagraphState(Paragraph const * pParagraph)
744 ::osl::MutexGuard aInternalGuard(GetMutex());
746 // If a client holds on to a Paragraph that is no longer visible, it can
747 // happen that this Paragraph lies outside the range from m_aVisibleBegin
748 // to m_aVisibleEnd. In that case, it is neither VISIBLE nor SHOWING:
749 ::sal_Int64 nState
750 = (static_cast< ::sal_Int64 >(1)
751 << css::accessibility::AccessibleStateType::ENABLED)
752 | (static_cast< ::sal_Int64 >(1)
753 << css::accessibility::AccessibleStateType::SENSITIVE)
754 | (static_cast< ::sal_Int64 >(1)
755 << css::accessibility::AccessibleStateType::FOCUSABLE)
756 | (static_cast< ::sal_Int64 >(1)
757 << css::accessibility::AccessibleStateType::MULTI_LINE);
758 if (!m_rView.IsReadOnly())
759 nState |= (static_cast< ::sal_Int64 >(1)
760 << css::accessibility::AccessibleStateType::EDITABLE);
761 Paragraphs::iterator aPara(m_xParagraphs->begin()
762 + pParagraph->getNumber());
763 if (aPara >= m_aVisibleBegin && aPara < m_aVisibleEnd)
765 nState
766 |= (static_cast< ::sal_Int64 >(1)
767 << css::accessibility::AccessibleStateType::VISIBLE)
768 | (static_cast< ::sal_Int64 >(1)
769 << css::accessibility::AccessibleStateType::SHOWING);
770 if (aPara == m_aFocused)
771 nState |= (static_cast< ::sal_Int64 >(1)
772 << css::accessibility::AccessibleStateType::FOCUSED);
774 return nState;
777 css::awt::Rectangle
778 Document::retrieveParagraphBounds(Paragraph const * pParagraph,
779 bool bAbsolute)
781 SolarMutexGuard aGuard;
782 ::osl::MutexGuard aInternalGuard(GetMutex());
784 // If a client holds on to a Paragraph that is no longer visible (as it
785 // scrolled out the top of the view), it can happen that this Paragraph
786 // lies before m_aVisibleBegin. In that case, calculate the vertical
787 // position of the Paragraph starting at paragraph 0, otherwise optimize
788 // and start at m_aVisibleBegin:
789 Paragraphs::iterator aPara(m_xParagraphs->begin()
790 + pParagraph->getNumber());
791 auto lAddHeight = [](const sal_Int32& rSum, const ParagraphInfo& rParagraph) {
792 return rSum + rParagraph.getHeight(); };
793 ::sal_Int32 nPos;
794 if (aPara < m_aVisibleBegin)
795 nPos = std::accumulate(m_xParagraphs->begin(), aPara, sal_Int32(0), lAddHeight);
796 else
797 nPos = std::accumulate(m_aVisibleBegin, aPara, m_nViewOffset - m_nVisibleBeginOffset, lAddHeight);
799 Point aOrig(0, 0);
800 if (bAbsolute)
801 aOrig = m_rView.GetWindow()->OutputToAbsoluteScreenPixel(aOrig);
803 return css::awt::Rectangle(
804 static_cast< ::sal_Int32 >(aOrig.X()),
805 static_cast< ::sal_Int32 >(aOrig.Y()) + nPos - m_nViewOffset,
806 m_rView.GetWindow()->GetOutputSizePixel().Width(), aPara->getHeight());
807 // XXX numeric overflow (3x)
810 OUString
811 Document::retrieveParagraphText(Paragraph const * pParagraph)
813 SolarMutexGuard aGuard;
814 ::osl::MutexGuard aInternalGuard(GetMutex());
815 return m_rEngine.GetText(static_cast< ::sal_uInt32 >(pParagraph->getNumber()));
816 // numeric overflow cannot happen here
819 void Document::retrieveParagraphSelection(Paragraph const * pParagraph,
820 ::sal_Int32 * pBegin,
821 ::sal_Int32 * pEnd)
823 SolarMutexGuard aGuard;
824 ::osl::MutexGuard aInternalGuard(GetMutex());
825 ::TextSelection const & rSelection = m_rView.GetSelection();
826 Paragraphs::size_type nNumber = pParagraph->getNumber();
827 TextPaM aStartPaM( rSelection.GetStart() );
828 TextPaM aEndPaM( rSelection.GetEnd() );
829 TextPaM aMinPaM( std::min( aStartPaM, aEndPaM ) );
830 TextPaM aMaxPaM( std::max( aStartPaM, aEndPaM ) );
832 if ( nNumber >= aMinPaM.GetPara() && nNumber <= aMaxPaM.GetPara() )
834 *pBegin = nNumber > aMinPaM.GetPara() ? 0 : aMinPaM.GetIndex();
835 // XXX numeric overflow
836 *pEnd = nNumber < aMaxPaM.GetPara()
837 ? m_rEngine.GetText(static_cast< ::sal_uInt32 >(nNumber)).getLength()
838 : aMaxPaM.GetIndex();
839 // XXX numeric overflow (3x)
841 if ( aStartPaM > aEndPaM )
842 std::swap( *pBegin, *pEnd );
844 else
846 *pBegin = 0;
847 *pEnd = 0;
851 ::sal_Int32 Document::retrieveParagraphCaretPosition(Paragraph const * pParagraph)
853 SolarMutexGuard aGuard;
854 ::osl::MutexGuard aInternalGuard(GetMutex());
855 ::TextSelection const & rSelection = m_rView.GetSelection();
856 Paragraphs::size_type nNumber = pParagraph->getNumber();
857 TextPaM aEndPaM( rSelection.GetEnd() );
859 return aEndPaM.GetPara() == nNumber ? aEndPaM.GetIndex() : -1;
862 css::awt::Rectangle
863 Document::retrieveCharacterBounds(Paragraph const * pParagraph,
864 ::sal_Int32 nIndex)
866 SolarMutexGuard aGuard;
867 ::osl::MutexGuard aInternalGuard(GetMutex());
868 ::sal_uInt32 nNumber = static_cast< ::sal_uInt32 >(pParagraph->getNumber());
869 sal_Int32 nLength = m_rEngine.GetText(nNumber).getLength();
870 // XXX numeric overflow
871 if (nIndex < 0 || nIndex > nLength)
872 throw css::lang::IndexOutOfBoundsException(
873 "textwindowaccessibility.cxx:"
874 " Document::retrieveCharacterAttributes",
875 static_cast< css::uno::XWeak * >(this));
876 css::awt::Rectangle aBounds( 0, 0, 0, 0 );
877 if ( nIndex == nLength )
879 aBounds = AWTRectangle(
880 m_rEngine.PaMtoEditCursor(::TextPaM(nNumber, nIndex)));
882 else
884 ::tools::Rectangle aLeft(
885 m_rEngine.PaMtoEditCursor(::TextPaM(nNumber, nIndex)));
886 // XXX numeric overflow
887 ::tools::Rectangle aRight(
888 m_rEngine.PaMtoEditCursor(::TextPaM(nNumber, nIndex + 1)));
889 // XXX numeric overflow (2x)
890 // FIXME If the vertical extends of the two cursors do not match, assume
891 // nIndex is the last character on the line; the bounding box will then
892 // extend to m_rEngine.GetMaxTextWidth():
893 ::sal_Int32 nWidth = (aLeft.Top() == aRight.Top()
894 && aLeft.Bottom() == aRight.Bottom())
895 ? static_cast< ::sal_Int32 >(aRight.Left() - aLeft.Left())
896 : static_cast< ::sal_Int32 >(m_rEngine.GetMaxTextWidth()
897 - aLeft.Left());
898 // XXX numeric overflow (4x)
899 aBounds = css::awt::Rectangle(static_cast< ::sal_Int32 >(aLeft.Left()),
900 static_cast< ::sal_Int32 >(aLeft.Top() - m_nViewOffset),
901 nWidth,
902 static_cast< ::sal_Int32 >(aLeft.Bottom()
903 - aLeft.Top()));
904 // XXX numeric overflow (4x)
906 return aBounds;
909 ::sal_Int32 Document::retrieveCharacterIndex(Paragraph const * pParagraph,
910 css::awt::Point const & rPoint)
912 SolarMutexGuard aGuard;
913 ::osl::MutexGuard aInternalGuard(GetMutex());
914 ::sal_uInt32 nNumber = static_cast< ::sal_uInt32 >(pParagraph->getNumber());
915 // XXX numeric overflow
916 ::TextPaM aPaM(m_rEngine.GetPaM(::Point(static_cast< tools::Long >(rPoint.X),
917 static_cast< tools::Long >(rPoint.Y))));
918 // XXX numeric overflow (2x)
919 return aPaM.GetPara() == nNumber ? aPaM.GetIndex() : -1;
920 // XXX numeric overflow
923 css::uno::Sequence< css::beans::PropertyValue >
924 Document::retrieveCharacterAttributes(
925 Paragraph const * pParagraph, ::sal_Int32 nIndex,
926 const css::uno::Sequence< OUString >& aRequestedAttributes)
928 SolarMutexGuard aGuard;
930 vcl::Font aFont = m_rEngine.GetFont();
931 const sal_Int32 AttributeCount = 9;
932 std::vector< css::beans::PropertyValue > aAttribs;
933 aAttribs.reserve(AttributeCount);
935 css::beans::PropertyValue aAttrib;
936 aAttrib.Handle = -1;
937 aAttrib.State = css::beans::PropertyState_DIRECT_VALUE;
939 //character background color
940 aAttrib.Name = "CharBackColor";
941 aAttrib.Value = mapFontColor( aFont.GetFillColor() );
942 aAttribs.push_back(aAttrib);
944 //character color
945 aAttrib.Name = "CharColor";
946 //aAttrib.Value = mapFontColor( aFont.GetColor() );
947 aAttrib.Value = mapFontColor( m_rEngine.GetTextColor() );
948 aAttribs.push_back(aAttrib);
950 //character font name
951 aAttrib.Name = "CharFontName";
952 aAttrib.Value <<= aFont.GetFamilyName();
953 aAttribs.push_back(aAttrib);
955 //character height
956 aAttrib.Name = "CharHeight";
957 aAttrib.Value <<= static_cast<sal_Int16>(aFont.GetFontHeight());
958 aAttribs.push_back(aAttrib);
960 //character posture
961 aAttrib.Name = "CharPosture";
962 aAttrib.Value <<= static_cast<sal_Int16>(aFont.GetItalic());
963 aAttribs.push_back(aAttrib);
965 //character relief
967 aAttrib.Name = "CharRelief";
968 aAttrib.Value = css::uno::Any( (sal_Int16)aFont.GetRelief() );
969 aAttribs.push_back(aAttrib);
972 //character strikeout
973 aAttrib.Name = "CharStrikeout";
974 aAttrib.Value <<= static_cast<sal_Int16>(aFont.GetStrikeout());
975 aAttribs.push_back(aAttrib);
977 //character underline
978 aAttrib.Name = "CharUnderline";
979 aAttrib.Value <<= static_cast<sal_Int16>(aFont.GetUnderline());
980 aAttribs.push_back(aAttrib);
982 //character weight
983 aAttrib.Name = "CharWeight";
984 aAttrib.Value <<= static_cast<float>(aFont.GetWeight());
985 aAttribs.push_back(aAttrib);
987 //character alignment
988 aAttrib.Name = "ParaAdjust";
989 aAttrib.Value <<= static_cast<sal_Int16>(m_rEngine.GetTextAlign());
990 aAttribs.push_back(aAttrib);
992 ::osl::MutexGuard aInternalGuard(GetMutex());
993 ::sal_uInt32 nNumber = static_cast< ::sal_uInt32 >(pParagraph->getNumber());
994 // XXX numeric overflow
995 // nIndex can be equal to getLength();
996 if (nIndex < 0 || nIndex > m_rEngine.GetText(nNumber).getLength())
997 throw css::lang::IndexOutOfBoundsException(
998 "textwindowaccessibility.cxx:"
999 " Document::retrieveCharacterAttributes",
1000 static_cast< css::uno::XWeak * >(this));
1003 // retrieve run attributes
1004 tPropValMap aCharAttrSeq;
1005 retrieveRunAttributesImpl( pParagraph, nIndex, aRequestedAttributes, aCharAttrSeq );
1007 for (const css::beans::PropertyValue& rAttrib : aAttribs)
1009 aCharAttrSeq[ rAttrib.Name ] = rAttrib;
1012 const css::uno::Sequence< css::beans::PropertyValue > aRes = comphelper::mapValuesToSequence( aCharAttrSeq );
1014 // sort the attributes
1015 auto nLength = static_cast<size_t>(aRes.getLength());
1016 std::unique_ptr<sal_Int32[]> pIndices( new sal_Int32[nLength] );
1017 std::iota(&pIndices[0], &pIndices[nLength], 0);
1018 std::sort(&pIndices[0], &pIndices[nLength],
1019 [&aRes](sal_Int32 a, sal_Int32 b) { return aRes[a].Name < aRes[b].Name; });
1021 // create sorted sequences according to index array
1022 std::vector<css::beans::PropertyValue> aNewValues;
1023 aNewValues.reserve(nLength);
1024 std::transform(&pIndices[0], &pIndices[nLength], std::back_inserter(aNewValues),
1025 [&aRes](const sal_Int32 nIdx) { return aRes[nIdx]; });
1027 return comphelper::containerToSequence(aNewValues);
1030 void Document::retrieveRunAttributesImpl(
1031 Paragraph const * pParagraph, ::sal_Int32 Index,
1032 const css::uno::Sequence< OUString >& RequestedAttributes,
1033 tPropValMap& rRunAttrSeq)
1035 ::sal_uInt32 nNumber = static_cast< ::sal_uInt32 >( pParagraph->getNumber() );
1036 ::TextPaM aPaM( nNumber, Index );
1037 // XXX numeric overflow
1038 ::TextAttribFontColor const * pColor
1039 = static_cast< ::TextAttribFontColor const * >(
1040 m_rEngine.FindAttrib( aPaM, TEXTATTR_FONTCOLOR ) );
1041 ::TextAttribFontWeight const * pWeight
1042 = static_cast< ::TextAttribFontWeight const * >(
1043 m_rEngine.FindAttrib( aPaM, TEXTATTR_FONTWEIGHT ) );
1044 tPropValMap aRunAttrSeq;
1045 if ( pColor )
1047 css::beans::PropertyValue aPropVal;
1048 aPropVal.Name = "CharColor";
1049 aPropVal.Handle = -1;
1050 aPropVal.Value = mapFontColor( pColor->GetColor() );
1051 aPropVal.State = css::beans::PropertyState_DIRECT_VALUE;
1052 aRunAttrSeq[ aPropVal.Name ] = aPropVal;
1054 if ( pWeight )
1056 css::beans::PropertyValue aPropVal;
1057 aPropVal.Name = "CharWeight";
1058 aPropVal.Handle = -1;
1059 aPropVal.Value = mapFontWeight( pWeight->getFontWeight() );
1060 aPropVal.State = css::beans::PropertyState_DIRECT_VALUE;
1061 aRunAttrSeq[ aPropVal.Name ] = aPropVal;
1063 if ( !RequestedAttributes.hasElements() )
1065 rRunAttrSeq = aRunAttrSeq;
1067 else
1069 for ( const OUString& rReqAttr : RequestedAttributes )
1071 tPropValMap::iterator aIter = aRunAttrSeq.find( rReqAttr );
1072 if ( aIter != aRunAttrSeq.end() )
1074 rRunAttrSeq[ (*aIter).first ] = (*aIter).second;
1080 css::uno::Sequence< css::beans::PropertyValue >
1081 Document::retrieveRunAttributes(
1082 Paragraph const * pParagraph, ::sal_Int32 Index,
1083 const css::uno::Sequence< OUString >& RequestedAttributes)
1085 SolarMutexGuard aGuard;
1086 ::osl::MutexGuard aInternalGuard( GetMutex() );
1087 ::sal_uInt32 nNumber = static_cast< ::sal_uInt32 >( pParagraph->getNumber() );
1088 // XXX numeric overflow
1089 if ( Index < 0 || Index >= m_rEngine.GetText(nNumber).getLength() )
1090 throw css::lang::IndexOutOfBoundsException(
1091 "textwindowaccessibility.cxx:"
1092 " Document::retrieveRunAttributes",
1093 static_cast< css::uno::XWeak * >( this ) );
1095 tPropValMap aRunAttrSeq;
1096 retrieveRunAttributesImpl( pParagraph, Index, RequestedAttributes, aRunAttrSeq );
1097 return comphelper::mapValuesToSequence( aRunAttrSeq );
1100 void Document::changeParagraphText(Paragraph const * pParagraph,
1101 OUString const & rText)
1103 SolarMutexGuard aGuard;
1105 ::osl::MutexGuard aInternalGuard(GetMutex());
1106 ::sal_uInt32 nNumber = static_cast< ::sal_uInt32 >(pParagraph->getNumber());
1107 // XXX numeric overflow
1108 changeParagraphText(nNumber, 0, m_rEngine.GetTextLen(nNumber), false,
1109 false, rText);
1113 void Document::changeParagraphText(Paragraph const * pParagraph,
1114 ::sal_Int32 nBegin, ::sal_Int32 nEnd,
1115 bool bCut, bool bPaste,
1116 OUString const & rText)
1118 SolarMutexGuard aGuard;
1120 ::osl::MutexGuard aInternalGuard(GetMutex());
1121 ::sal_uInt32 nNumber = static_cast< ::sal_uInt32 >(pParagraph->getNumber());
1122 // XXX numeric overflow
1123 if (nBegin < 0 || nBegin > nEnd
1124 || nEnd > m_rEngine.GetText(nNumber).getLength())
1125 throw css::lang::IndexOutOfBoundsException(
1126 "textwindowaccessibility.cxx:"
1127 " Document::changeParagraphText",
1128 static_cast< css::uno::XWeak * >(this));
1129 changeParagraphText(nNumber, static_cast< ::sal_uInt16 >(nBegin),
1130 static_cast< ::sal_uInt16 >(nEnd), bCut, bPaste, rText);
1131 // XXX numeric overflow (2x)
1135 void Document::copyParagraphText(Paragraph const * pParagraph,
1136 ::sal_Int32 nBegin, ::sal_Int32 nEnd)
1138 SolarMutexGuard aGuard;
1140 ::osl::MutexGuard aInternalGuard(GetMutex());
1141 ::sal_uInt32 nNumber = static_cast< ::sal_uInt32 >(pParagraph->getNumber());
1142 // XXX numeric overflow
1143 if (nBegin < 0 || nBegin > nEnd
1144 || nEnd > m_rEngine.GetText(nNumber).getLength())
1145 throw css::lang::IndexOutOfBoundsException(
1146 "textwindowaccessibility.cxx:"
1147 " Document::copyParagraphText",
1148 static_cast< css::uno::XWeak * >(this));
1149 m_rView.SetSelection(
1150 ::TextSelection(::TextPaM(nNumber, nBegin),
1151 ::TextPaM(nNumber, nEnd)));
1152 // XXX numeric overflow (2x)
1153 m_rView.Copy();
1157 void Document::changeParagraphAttributes(
1158 Paragraph const * pParagraph, ::sal_Int32 nBegin, ::sal_Int32 nEnd,
1159 css::uno::Sequence< css::beans::PropertyValue > const & rAttributeSet)
1161 SolarMutexGuard aGuard;
1163 ::osl::MutexGuard aInternalGuard(GetMutex());
1164 ::sal_uInt32 nNumber = static_cast< ::sal_uInt32 >(pParagraph->getNumber());
1165 // XXX numeric overflow
1166 if (nBegin < 0 || nBegin > nEnd
1167 || nEnd > m_rEngine.GetText(nNumber).getLength())
1168 throw css::lang::IndexOutOfBoundsException(
1169 "textwindowaccessibility.cxx:"
1170 " Document::changeParagraphAttributes",
1171 static_cast< css::uno::XWeak * >(this));
1173 // FIXME The new attributes are added to any attributes already set,
1174 // they do not replace the old attributes as required by
1175 // XAccessibleEditableText.setAttributes:
1176 for (const auto& rAttr : rAttributeSet)
1177 if ( rAttr.Name == "CharColor" )
1178 m_rEngine.SetAttrib(::TextAttribFontColor(
1179 mapFontColor(rAttr.Value)),
1180 nNumber, nBegin, nEnd);
1181 // XXX numeric overflow (2x)
1182 else if ( rAttr.Name == "CharWeight" )
1183 m_rEngine.SetAttrib(::TextAttribFontWeight(
1184 mapFontWeight(rAttr.Value)),
1185 nNumber, nBegin, nEnd);
1186 // XXX numeric overflow (2x)
1190 void Document::changeParagraphSelection(Paragraph const * pParagraph,
1191 ::sal_Int32 nBegin, ::sal_Int32 nEnd)
1193 SolarMutexGuard aGuard;
1195 ::osl::MutexGuard aInternalGuard(GetMutex());
1196 ::sal_uInt32 nNumber = static_cast< ::sal_uInt32 >(pParagraph->getNumber());
1197 // XXX numeric overflow
1198 if (nBegin < 0 || nBegin > nEnd
1199 || nEnd > m_rEngine.GetText(nNumber).getLength())
1200 throw css::lang::IndexOutOfBoundsException(
1201 "textwindowaccessibility.cxx:"
1202 " Document::changeParagraphSelection",
1203 static_cast< css::uno::XWeak * >(this));
1204 m_rView.SetSelection(
1205 ::TextSelection(::TextPaM(nNumber, nBegin),
1206 ::TextPaM(nNumber, nEnd)));
1207 // XXX numeric overflow (2x)
1211 css::i18n::Boundary
1212 Document::retrieveParagraphLineBoundary( Paragraph const * pParagraph,
1213 ::sal_Int32 nIndex, ::sal_Int32 *pLineNo )
1215 css::i18n::Boundary aBoundary;
1216 aBoundary.startPos = nIndex;
1217 aBoundary.endPos = nIndex;
1219 SolarMutexGuard aGuard;
1221 ::osl::MutexGuard aInternalGuard( GetMutex() );
1222 ::sal_uInt32 nNumber = static_cast< ::sal_uInt32 >( pParagraph->getNumber() );
1223 if ( nIndex < 0 || nIndex > m_rEngine.GetText( nNumber ).getLength() )
1224 throw css::lang::IndexOutOfBoundsException(
1225 "textwindowaccessibility.cxx:"
1226 " Document::retrieveParagraphLineBoundary",
1227 static_cast< css::uno::XWeak * >( this ) );
1228 ::sal_Int32 nLineStart = 0;
1229 ::sal_Int32 nLineEnd = 0;
1230 ::sal_uInt16 nLineCount = m_rEngine.GetLineCount( nNumber );
1231 for ( ::sal_uInt16 nLine = 0; nLine < nLineCount; ++nLine )
1233 nLineStart = nLineEnd;
1234 nLineEnd += m_rEngine.GetLineLen( nNumber, nLine );
1235 if ( nIndex >= nLineStart && ( ( nLine == nLineCount - 1 ) ? nIndex <= nLineEnd : nIndex < nLineEnd ) )
1237 aBoundary.startPos = nLineStart;
1238 aBoundary.endPos = nLineEnd;
1239 if( pLineNo )
1240 pLineNo[0] = nLine;
1241 break;
1246 return aBoundary;
1249 css::i18n::Boundary
1250 Document::retrieveParagraphBoundaryOfLine( Paragraph const * pParagraph,
1251 ::sal_Int32 nLineNo )
1253 css::i18n::Boundary aBoundary;
1254 aBoundary.startPos = 0;
1255 aBoundary.endPos = 0;
1257 SolarMutexGuard aGuard;
1259 ::osl::MutexGuard aInternalGuard( GetMutex() );
1260 ::sal_uInt32 nNumber = static_cast< ::sal_uInt32 >( pParagraph->getNumber() );
1261 if ( nLineNo >= m_rEngine.GetLineCount( nNumber ) )
1262 throw css::lang::IndexOutOfBoundsException(
1263 "textwindowaccessibility.cxx:"
1264 " Document::retrieveParagraphBoundaryOfLine",
1265 static_cast< css::uno::XWeak * >( this ) );
1266 ::sal_Int32 nLineStart = 0;
1267 ::sal_Int32 nLineEnd = 0;
1268 for ( ::sal_Int32 nLine = 0; nLine <= nLineNo; ++nLine )
1270 nLineStart = nLineEnd;
1271 nLineEnd += m_rEngine.GetLineLen( nNumber, nLine );
1274 aBoundary.startPos = nLineStart;
1275 aBoundary.endPos = nLineEnd;
1278 return aBoundary;
1281 sal_Int32 Document::retrieveParagraphLineWithCursor( Paragraph const * pParagraph )
1283 SolarMutexGuard aGuard;
1284 ::osl::MutexGuard aInternalGuard(GetMutex());
1285 ::TextSelection const & rSelection = m_rView.GetSelection();
1286 Paragraphs::size_type nNumber = pParagraph->getNumber();
1287 TextPaM aEndPaM( rSelection.GetEnd() );
1289 return aEndPaM.GetPara() == nNumber
1290 ? m_rView.GetLineNumberOfCursorInSelection() : -1;
1294 css::uno::Reference< css::accessibility::XAccessibleRelationSet >
1295 Document::retrieveParagraphRelationSet( Paragraph const * pParagraph )
1297 ::osl::MutexGuard aInternalGuard( GetMutex() );
1299 rtl::Reference<::utl::AccessibleRelationSetHelper> pRelationSetHelper = new ::utl::AccessibleRelationSetHelper();
1301 Paragraphs::iterator aPara( m_xParagraphs->begin() + pParagraph->getNumber() );
1303 if ( aPara > m_aVisibleBegin && aPara < m_aVisibleEnd )
1305 css::uno::Sequence< css::uno::Reference< css::uno::XInterface > > aSequence { getAccessibleChild( aPara - 1 ) };
1306 css::accessibility::AccessibleRelation aRelation( css::accessibility::AccessibleRelationType::CONTENT_FLOWS_FROM, aSequence );
1307 pRelationSetHelper->AddRelation( aRelation );
1310 if ( aPara >= m_aVisibleBegin && aPara < m_aVisibleEnd -1 )
1312 css::uno::Sequence< css::uno::Reference< css::uno::XInterface > > aSequence { getAccessibleChild( aPara + 1 ) };
1313 css::accessibility::AccessibleRelation aRelation( css::accessibility::AccessibleRelationType::CONTENT_FLOWS_TO, aSequence );
1314 pRelationSetHelper->AddRelation( aRelation );
1317 return pRelationSetHelper;
1320 // virtual
1321 ::sal_Int32 SAL_CALL Document::getAccessibleChildCount()
1323 ::comphelper::OExternalLockGuard aGuard(this);
1324 init();
1325 return m_aVisibleEnd - m_aVisibleBegin;
1328 // virtual
1329 css::uno::Reference< css::accessibility::XAccessible > SAL_CALL
1330 Document::getAccessibleChild(::sal_Int32 i)
1332 ::comphelper::OExternalLockGuard aGuard(this);
1333 init();
1334 if (i < 0 || i >= m_aVisibleEnd - m_aVisibleBegin)
1335 throw css::lang::IndexOutOfBoundsException(
1336 "textwindowaccessibility.cxx:"
1337 " Document::getAccessibleChild",
1338 static_cast< css::uno::XWeak * >(this));
1339 return getAccessibleChild(m_aVisibleBegin
1340 + static_cast< Paragraphs::size_type >(i));
1343 // virtual
1344 ::sal_Int16 SAL_CALL Document::getAccessibleRole()
1346 return css::accessibility::AccessibleRole::TEXT_FRAME;
1349 // virtual
1350 css::uno::Reference< css::accessibility::XAccessible > SAL_CALL
1351 Document::getAccessibleAtPoint(css::awt::Point const & rPoint)
1353 ::comphelper::OExternalLockGuard aGuard(this);
1354 init();
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(m_aVisibleBegin); aIt != m_aVisibleEnd;
1362 ++aIt)
1364 nPos += aIt->getHeight(); // XXX numeric overflow
1365 if (nOffset < nPos)
1366 return getAccessibleChild(aIt);
1369 return nullptr;
1371 void Document::FillAccessibleStateSet( utl::AccessibleStateSetHelper& rStateSet )
1373 VCLXAccessibleComponent::FillAccessibleStateSet( rStateSet );
1374 if (!m_rView.IsReadOnly())
1375 rStateSet.AddState( css::accessibility::AccessibleStateType::EDITABLE );
1378 void Document::FillAccessibleRelationSet( utl::AccessibleRelationSetHelper& rRelationSet )
1380 if( getAccessibleParent()->getAccessibleContext()->getAccessibleRole() == css::accessibility::AccessibleRole::SCROLL_PANE )
1382 css::uno::Sequence< css::uno::Reference< css::uno::XInterface > > aSequence { getAccessibleParent() };
1383 rRelationSet.AddRelation( css::accessibility::AccessibleRelation( css::accessibility::AccessibleRelationType::MEMBER_OF, aSequence ) );
1385 else
1387 VCLXAccessibleComponent::FillAccessibleRelationSet(rRelationSet);
1390 // virtual
1391 void SAL_CALL Document::disposing()
1393 m_aEngineListener.endListening();
1394 m_aViewListener.endListening();
1395 if (m_xParagraphs != nullptr)
1396 disposeParagraphs();
1397 VCLXAccessibleComponent::disposing();
1400 // virtual
1401 void Document::Notify(::SfxBroadcaster &, ::SfxHint const & rHint)
1403 const TextHint* pTextHint = dynamic_cast<const TextHint*>(&rHint);
1404 if (!pTextHint)
1405 return;
1407 ::TextHint const & rTextHint = *pTextHint;
1408 switch (rTextHint.GetId())
1410 case SfxHintId::TextParaInserted:
1411 case SfxHintId::TextParaRemoved:
1412 // SfxHintId::TextParaInserted and SfxHintId::TextParaRemoved are sent at
1413 // "unsafe" times (when the text engine has not yet re-formatted its
1414 // content), so that for example calling ::TextEngine::GetTextHeight
1415 // from within the code that handles SfxHintId::TextParaInserted causes
1416 // trouble within the text engine. Therefore, these hints are just
1417 // buffered until a following ::TextEngine::FormatDoc causes a
1418 // SfxHintId::TextFormatted to come in:
1419 case SfxHintId::TextFormatPara:
1420 // ::TextEngine::FormatDoc sends a sequence of
1421 // SfxHintId::TextFormatParas, followed by an optional
1422 // SfxHintId::TextHeightChanged, followed in all cases by one
1423 // SfxHintId::TextFormatted. Only the SfxHintId::TextFormatParas contain
1424 // the numbers of the affected paragraphs, but they are sent
1425 // before the changes are applied. Therefore, SfxHintId::TextFormatParas
1426 // are just buffered until another hint comes in:
1428 ::osl::MutexGuard aInternalGuard(GetMutex());
1429 if (!isAlive())
1430 break;
1432 m_aParagraphNotifications.push(rTextHint);
1433 break;
1435 case SfxHintId::TextFormatted:
1436 case SfxHintId::TextHeightChanged:
1437 case SfxHintId::TextModified:
1439 ::osl::MutexGuard aInternalGuard(GetMutex());
1440 if (!isAlive())
1441 break;
1442 handleParagraphNotifications();
1443 break;
1445 case SfxHintId::TextViewScrolled:
1447 ::osl::MutexGuard aInternalGuard(GetMutex());
1448 if (!isAlive())
1449 break;
1450 handleParagraphNotifications();
1452 ::sal_Int32 nOffset = static_cast< ::sal_Int32 >(
1453 m_rView.GetStartDocPos().Y());
1454 // XXX numeric overflow
1455 if (nOffset != m_nViewOffset)
1457 m_nViewOffset = nOffset;
1459 Paragraphs::iterator aOldVisibleBegin(
1460 m_aVisibleBegin);
1461 Paragraphs::iterator aOldVisibleEnd(m_aVisibleEnd);
1463 determineVisibleRange();
1465 notifyVisibleRangeChanges(aOldVisibleBegin,
1466 aOldVisibleEnd,
1467 m_xParagraphs->end());
1469 break;
1471 case SfxHintId::TextViewSelectionChanged:
1472 case SfxHintId::TextViewCaretChanged:
1474 ::osl::MutexGuard aInternalGuard(GetMutex());
1475 if (!isAlive())
1476 break;
1478 if (m_aParagraphNotifications.empty())
1480 handleSelectionChangeNotification();
1482 else
1484 // SfxHintId::TextViewSelectionChanged is sometimes sent at
1485 // "unsafe" times (when the text engine has not yet re-
1486 // formatted its content), so that for example calling
1487 // ::TextEngine::GetTextHeight from within the code that
1488 // handles a previous SfxHintId::TextParaInserted causes
1489 // trouble within the text engine. Therefore, these
1490 // hints are just buffered (along with
1491 // SfxHintId::TextParaInserted/REMOVED/FORMATPARA) until a
1492 // following ::TextEngine::FormatDoc causes a
1493 // SfxHintId::TextFormatted to come in:
1494 m_bSelectionChangedNotification = true;
1496 break;
1498 default: break;
1502 IMPL_LINK(Document, WindowEventHandler, ::VclWindowEvent&, rEvent, void)
1504 switch (rEvent.GetId())
1506 case VclEventId::WindowResize:
1508 ::osl::MutexGuard aInternalGuard(GetMutex());
1509 if (!isAlive())
1510 break;
1512 ::sal_Int32 nHeight = static_cast< ::sal_Int32 >(
1513 m_rView.GetWindow()->GetOutputSizePixel().Height());
1514 // XXX numeric overflow
1515 if (nHeight != m_nViewHeight)
1517 m_nViewHeight = nHeight;
1519 Paragraphs::iterator aOldVisibleBegin(m_aVisibleBegin);
1520 Paragraphs::iterator aOldVisibleEnd(m_aVisibleEnd);
1522 determineVisibleRange();
1524 notifyVisibleRangeChanges(aOldVisibleBegin, aOldVisibleEnd,
1525 m_xParagraphs->end());
1527 break;
1529 case VclEventId::WindowGetFocus:
1531 ::osl::MutexGuard aInternalGuard(GetMutex());
1532 if (!isAlive())
1533 break;
1534 //to enable the PARAGRAPH to get focus for multiline edit
1535 ::sal_Int32 count = getAccessibleChildCount();
1536 bool bEmpty = m_aFocused == m_aVisibleEnd && count == 1;
1537 if ((m_aFocused >= m_aVisibleBegin && m_aFocused < m_aVisibleEnd) || bEmpty)
1539 Paragraphs::iterator aTemp = bEmpty ? m_aVisibleBegin : m_aFocused;
1540 ::rtl::Reference< Paragraph > xParagraph(getParagraph(aTemp));
1541 if (xParagraph.is())
1543 xParagraph->notifyEvent(
1544 css::accessibility::AccessibleEventId::
1545 STATE_CHANGED,
1546 css::uno::Any(),
1547 css::uno::Any(
1548 css::accessibility::AccessibleStateType::
1549 FOCUSED));
1552 break;
1554 case VclEventId::WindowLoseFocus:
1556 ::osl::MutexGuard aInternalGuard(GetMutex());
1557 if (!isAlive())
1558 break;
1559 //to enable the PARAGRAPH to get focus for multiline edit
1560 ::sal_Int32 count = getAccessibleChildCount();
1561 bool bEmpty = m_aFocused == m_aVisibleEnd && count == 1;
1562 if ((m_aFocused >= m_aVisibleBegin && m_aFocused < m_aVisibleEnd) || bEmpty)
1564 Paragraphs::iterator aTemp = bEmpty ? m_aVisibleBegin : m_aFocused;
1565 ::rtl::Reference< Paragraph > xParagraph(getParagraph(aTemp));
1566 if (xParagraph.is())
1567 xParagraph->notifyEvent(
1568 css::accessibility::AccessibleEventId::
1569 STATE_CHANGED,
1570 css::uno::Any(
1571 css::accessibility::AccessibleStateType::
1572 FOCUSED),
1573 css::uno::Any());
1575 break;
1577 default: break;
1581 void Document::init()
1583 if (m_xParagraphs != nullptr)
1584 return;
1586 const ::sal_uInt32 nCount = m_rEngine.GetParagraphCount();
1587 m_xParagraphs.reset(new Paragraphs);
1588 m_xParagraphs->reserve(static_cast< Paragraphs::size_type >(nCount));
1589 // numeric overflow is harmless here
1590 for (::sal_uInt32 i = 0; i < nCount; ++i)
1591 m_xParagraphs->push_back(ParagraphInfo(static_cast< ::sal_Int32 >(
1592 m_rEngine.GetTextHeight(i))));
1593 // XXX numeric overflow
1594 m_nViewOffset = static_cast< ::sal_Int32 >(
1595 m_rView.GetStartDocPos().Y()); // XXX numeric overflow
1596 m_nViewHeight = static_cast< ::sal_Int32 >(
1597 m_rView.GetWindow()->GetOutputSizePixel().Height());
1598 // XXX numeric overflow
1599 determineVisibleRange();
1600 m_nSelectionFirstPara = -1;
1601 m_nSelectionFirstPos = -1;
1602 m_nSelectionLastPara = -1;
1603 m_nSelectionLastPos = -1;
1604 m_aFocused = m_xParagraphs->end();
1605 m_bSelectionChangedNotification = false;
1606 m_aEngineListener.startListening(m_rEngine);
1607 m_aViewListener.startListening(*m_rView.GetWindow());
1610 ::rtl::Reference< Paragraph >
1611 Document::getParagraph(Paragraphs::iterator const & rIt)
1613 return static_cast< Paragraph * >(
1614 css::uno::Reference< css::accessibility::XAccessible >(
1615 rIt->getParagraph()).get());
1618 css::uno::Reference< css::accessibility::XAccessible >
1619 Document::getAccessibleChild(Paragraphs::iterator const & rIt)
1621 css::uno::Reference< css::accessibility::XAccessible > xParagraph(
1622 rIt->getParagraph());
1623 if (!xParagraph.is())
1625 xParagraph = new Paragraph(this, rIt - m_xParagraphs->begin());
1626 rIt->setParagraph(xParagraph);
1628 return xParagraph;
1631 void Document::determineVisibleRange()
1633 Paragraphs::iterator const aEnd = m_xParagraphs->end();
1635 m_aVisibleBegin = aEnd;
1636 m_aVisibleEnd = aEnd;
1637 m_nVisibleBeginOffset = 0;
1639 ::sal_Int32 nPos = 0;
1640 for (Paragraphs::iterator aIt = m_xParagraphs->begin(); m_aVisibleEnd == aEnd && aIt != aEnd; ++aIt)
1642 ::sal_Int32 const nOldPos = nPos;
1643 nPos += aIt->getHeight(); // XXX numeric overflow
1644 if (m_aVisibleBegin == aEnd)
1646 if (nPos >= m_nViewOffset)
1648 m_aVisibleBegin = aIt;
1649 m_nVisibleBeginOffset = m_nViewOffset - nOldPos;
1652 else
1654 if (nPos >= m_nViewOffset + m_nViewHeight) // XXX numeric overflow
1656 m_aVisibleEnd = aIt;
1661 SAL_WARN_IF(
1662 !((m_aVisibleBegin == m_xParagraphs->end() && m_aVisibleEnd == m_xParagraphs->end() && m_nVisibleBeginOffset == 0)
1663 || (m_aVisibleBegin < m_aVisibleEnd && m_nVisibleBeginOffset >= 0)),
1664 "accessibility",
1665 "invalid visible range");
1668 void Document::notifyVisibleRangeChanges(
1669 Paragraphs::iterator const & rOldVisibleBegin,
1670 Paragraphs::iterator const & rOldVisibleEnd,
1671 Paragraphs::iterator const & rInserted)
1673 // XXX Replace this code that determines which paragraphs have changed from
1674 // invisible to visible or vice versa with a better algorithm.
1675 for (Paragraphs::iterator aIt(rOldVisibleBegin); aIt != rOldVisibleEnd;
1676 ++aIt)
1678 if (aIt != rInserted
1679 && (aIt < m_aVisibleBegin || aIt >= m_aVisibleEnd))
1680 NotifyAccessibleEvent(
1681 css::accessibility::AccessibleEventId::
1682 CHILD,
1683 css::uno::Any(getAccessibleChild(aIt)),
1684 css::uno::Any());
1686 for (Paragraphs::iterator aIt(m_aVisibleBegin); aIt != m_aVisibleEnd;
1687 ++aIt)
1689 if (aIt == rInserted
1690 || aIt < rOldVisibleBegin || aIt >= rOldVisibleEnd)
1691 NotifyAccessibleEvent(
1692 css::accessibility::AccessibleEventId::
1693 CHILD,
1694 css::uno::Any(),
1695 css::uno::Any(getAccessibleChild(aIt)));
1699 void
1700 Document::changeParagraphText(::sal_uInt32 nNumber, ::sal_uInt16 nBegin, ::sal_uInt16 nEnd,
1701 bool bCut, bool bPaste,
1702 OUString const & rText)
1704 m_rView.SetSelection(::TextSelection(::TextPaM(nNumber, nBegin),
1705 ::TextPaM(nNumber, nEnd)));
1706 if (bCut)
1707 m_rView.Cut();
1708 else if (nBegin != nEnd)
1709 m_rView.DeleteSelected();
1710 if (bPaste)
1711 m_rView.Paste();
1712 else if (!rText.isEmpty())
1713 m_rView.InsertText(rText);
1716 void Document::handleParagraphNotifications()
1718 while (!m_aParagraphNotifications.empty())
1720 ::TextHint aHint(m_aParagraphNotifications.front());
1721 m_aParagraphNotifications.pop();
1722 switch (aHint.GetId())
1724 case SfxHintId::TextParaInserted:
1726 ::sal_uInt32 n = static_cast< ::sal_uInt32 >( aHint.GetValue() );
1727 assert(n <= m_xParagraphs->size() && "bad SfxHintId::TextParaInserted event");
1729 // Save the values of old iterators (the iterators themselves
1730 // will get invalidated), and adjust the old values so that they
1731 // reflect the insertion of the new paragraph:
1732 Paragraphs::size_type nOldVisibleBegin
1733 = m_aVisibleBegin - m_xParagraphs->begin();
1734 Paragraphs::size_type nOldVisibleEnd
1735 = m_aVisibleEnd - m_xParagraphs->begin();
1736 Paragraphs::size_type nOldFocused
1737 = m_aFocused - m_xParagraphs->begin();
1738 if (n <= nOldVisibleBegin)
1739 ++nOldVisibleBegin; // XXX numeric overflow
1740 if (n <= nOldVisibleEnd)
1741 ++nOldVisibleEnd; // XXX numeric overflow
1742 if (n <= nOldFocused)
1743 ++nOldFocused; // XXX numeric overflow
1744 if (sal::static_int_cast<sal_Int32>(n) <= m_nSelectionFirstPara)
1745 ++m_nSelectionFirstPara; // XXX numeric overflow
1746 if (sal::static_int_cast<sal_Int32>(n) <= m_nSelectionLastPara)
1747 ++m_nSelectionLastPara; // XXX numeric overflow
1749 Paragraphs::iterator aIns(
1750 m_xParagraphs->insert(
1751 m_xParagraphs->begin() + n,
1752 ParagraphInfo(static_cast< ::sal_Int32 >(
1753 m_rEngine.GetTextHeight(n)))));
1754 // XXX numeric overflow (2x)
1756 determineVisibleRange();
1757 m_aFocused = m_xParagraphs->begin() + nOldFocused;
1759 for (Paragraphs::iterator aIt(aIns);;)
1761 ++aIt;
1762 if (aIt == m_xParagraphs->end())
1763 break;
1764 ::rtl::Reference< Paragraph > xParagraph(
1765 getParagraph(aIt));
1766 if (xParagraph.is())
1767 xParagraph->numberChanged(true);
1770 notifyVisibleRangeChanges(
1771 m_xParagraphs->begin() + nOldVisibleBegin,
1772 m_xParagraphs->begin() + nOldVisibleEnd, aIns);
1773 break;
1775 case SfxHintId::TextParaRemoved:
1777 ::sal_uInt32 n = static_cast< ::sal_uInt32 >( aHint.GetValue() );
1778 if (n == TEXT_PARA_ALL)
1780 for (Paragraphs::iterator aIt(m_aVisibleBegin);
1781 aIt != m_aVisibleEnd; ++aIt)
1783 NotifyAccessibleEvent(
1784 css::accessibility::AccessibleEventId::
1785 CHILD,
1786 css::uno::Any(getAccessibleChild(aIt)),
1787 css::uno::Any());
1789 disposeParagraphs();
1790 m_xParagraphs->clear();
1791 determineVisibleRange();
1792 m_nSelectionFirstPara = -1;
1793 m_nSelectionFirstPos = -1;
1794 m_nSelectionLastPara = -1;
1795 m_nSelectionLastPos = -1;
1796 m_aFocused = m_xParagraphs->end();
1798 else
1800 assert(n < m_xParagraphs->size() && "Bad SfxHintId::TextParaRemoved event");
1802 Paragraphs::iterator aIt(m_xParagraphs->begin() + n);
1803 // numeric overflow cannot occur
1805 // Save the values of old iterators (the iterators
1806 // themselves will get invalidated), and adjust the old
1807 // values so that they reflect the removal of the paragraph:
1808 Paragraphs::size_type nOldVisibleBegin
1809 = m_aVisibleBegin - m_xParagraphs->begin();
1810 Paragraphs::size_type nOldVisibleEnd
1811 = m_aVisibleEnd - m_xParagraphs->begin();
1812 bool bWasVisible
1813 = nOldVisibleBegin <= n && n < nOldVisibleEnd;
1814 Paragraphs::size_type nOldFocused
1815 = m_aFocused - m_xParagraphs->begin();
1816 bool bWasFocused = aIt == m_aFocused;
1817 if (n < nOldVisibleBegin)
1818 --nOldVisibleBegin;
1819 if (n < nOldVisibleEnd)
1820 --nOldVisibleEnd;
1821 if (n < nOldFocused)
1822 --nOldFocused;
1823 if (sal::static_int_cast<sal_Int32>(n) < m_nSelectionFirstPara)
1824 --m_nSelectionFirstPara;
1825 else if (sal::static_int_cast<sal_Int32>(n) == m_nSelectionFirstPara)
1827 if (m_nSelectionFirstPara == m_nSelectionLastPara)
1829 m_nSelectionFirstPara = -1;
1830 m_nSelectionFirstPos = -1;
1831 m_nSelectionLastPara = -1;
1832 m_nSelectionLastPos = -1;
1834 else
1836 ++m_nSelectionFirstPara;
1837 m_nSelectionFirstPos = 0;
1840 if (sal::static_int_cast<sal_Int32>(n) < m_nSelectionLastPara)
1841 --m_nSelectionLastPara;
1842 else if (sal::static_int_cast<sal_Int32>(n) == m_nSelectionLastPara)
1844 assert(m_nSelectionFirstPara < m_nSelectionLastPara && "logic error");
1845 --m_nSelectionLastPara;
1846 m_nSelectionLastPos = 0x7FFFFFFF;
1849 css::uno::Reference< css::accessibility::XAccessible >
1850 xStrong;
1851 if (bWasVisible)
1852 xStrong = getAccessibleChild(aIt);
1853 css::uno::WeakReference<
1854 css::accessibility::XAccessible > xWeak(
1855 aIt->getParagraph());
1856 aIt = m_xParagraphs->erase(aIt);
1858 determineVisibleRange();
1859 m_aFocused = bWasFocused ? m_xParagraphs->end()
1860 : m_xParagraphs->begin() + nOldFocused;
1862 for (; aIt != m_xParagraphs->end(); ++aIt)
1864 ::rtl::Reference< Paragraph > xParagraph(
1865 getParagraph(aIt));
1866 if (xParagraph.is())
1867 xParagraph->numberChanged(false);
1870 if (bWasVisible)
1871 NotifyAccessibleEvent(
1872 css::accessibility::AccessibleEventId::
1873 CHILD,
1874 css::uno::Any(xStrong),
1875 css::uno::Any());
1877 css::uno::Reference< css::lang::XComponent > xComponent(
1878 xWeak.get(), css::uno::UNO_QUERY);
1879 if (xComponent.is())
1880 xComponent->dispose();
1882 notifyVisibleRangeChanges(
1883 m_xParagraphs->begin() + nOldVisibleBegin,
1884 m_xParagraphs->begin() + nOldVisibleEnd,
1885 m_xParagraphs->end());
1887 break;
1889 case SfxHintId::TextFormatPara:
1891 ::sal_uInt32 n = static_cast< ::sal_uInt32 >( aHint.GetValue() );
1892 assert(n < m_xParagraphs->size() && "Bad SfxHintId::TextFormatPara event");
1894 (*m_xParagraphs)[static_cast< Paragraphs::size_type >(n)].
1895 changeHeight(static_cast< ::sal_Int32 >(
1896 m_rEngine.GetTextHeight(n)));
1897 // XXX numeric overflow
1898 Paragraphs::iterator aOldVisibleBegin(m_aVisibleBegin);
1899 Paragraphs::iterator aOldVisibleEnd(m_aVisibleEnd);
1900 determineVisibleRange();
1901 notifyVisibleRangeChanges(aOldVisibleBegin, aOldVisibleEnd,
1902 m_xParagraphs->end());
1904 if (n < m_xParagraphs->size())
1906 Paragraphs::iterator aIt(m_xParagraphs->begin() + n);
1907 ::rtl::Reference< Paragraph > xParagraph(getParagraph(aIt));
1908 if (xParagraph.is())
1909 xParagraph->textChanged();
1911 break;
1913 default:
1914 SAL_WARN("accessibility", "bad buffered hint");
1915 break;
1918 if (m_bSelectionChangedNotification)
1920 m_bSelectionChangedNotification = false;
1921 handleSelectionChangeNotification();
1925 namespace
1928 enum class SelChangeType
1930 None, // no change, or invalid
1931 CaretMove, // neither old nor new have selection, and they are different
1932 NoSelToSel, // old has no selection but new has selection
1933 SelToNoSel, // old has selection but new has no selection
1934 // both old and new have selections
1935 NoParaChange, // only end index changed inside end para
1936 EndParaNoMoreBehind, // end para was behind start, but now is same or ahead
1937 AddedFollowingPara, // selection extended to following paragraph(s)
1938 ExcludedPreviousPara, // selection shrunk excluding previous paragraph(s)
1939 ExcludedFollowingPara, // selection shrunk excluding following paragraph(s)
1940 AddedPreviousPara, // selection extended to previous paragraph(s)
1941 EndParaBecameBehind // end para was ahead of start, but now is behind
1944 SelChangeType getSelChangeType(const TextPaM& Os, const TextPaM& Oe,
1945 const TextPaM& Ns, const TextPaM& Ne)
1947 if (Os == Oe) // no old selection
1949 if (Ns == Ne) // no new selection: only caret moves
1950 return Os != Ns ? SelChangeType::CaretMove : SelChangeType::None;
1951 else // old has no selection but new has selection
1952 return SelChangeType::NoSelToSel;
1954 else if (Ns == Ne) // old has selection; no new selection
1956 return SelChangeType::SelToNoSel;
1958 else if (Os == Ns) // both old and new have selections, and their starts are same
1960 const sal_Int32 Osp = Os.GetPara(), Oep = Oe.GetPara();
1961 const sal_Int32 Nsp = Ns.GetPara(), Nep = Ne.GetPara();
1962 if (Oep == Nep) // end of selection stays in the same paragraph
1964 //Send text_selection_change event on Nep
1965 return Oe.GetIndex() != Ne.GetIndex() ? SelChangeType::NoParaChange
1966 : SelChangeType::None;
1968 else if (Oep < Nep) // end of selection moved to a following paragraph
1970 //all the following examples like 1,2->1,3 means that old start select para is 1, old end select para is 2,
1971 // then press shift up, the new start select para is 1, new end select para is 3;
1972 //for example, 1, 2 -> 1, 3; 4,1 -> 4, 7; 4,1 -> 4, 2; 4,4->4,5
1973 if (Nep >= Nsp) // new end para not behind start
1975 // 1, 2 -> 1, 3; 4, 1 -> 4, 7; 4,4->4,5;
1976 if (Oep < Osp) // old end was behind start
1978 // 4,1 -> 4,7; 4,1 -> 4,4
1979 return SelChangeType::EndParaNoMoreBehind;
1981 else // old end para wasn't behind start
1983 // 1, 2 -> 1, 3; 4,4->4,5;
1984 return SelChangeType::AddedFollowingPara;
1987 else // new end para is still behind start
1989 // 4,1 -> 4,2,
1990 return SelChangeType::ExcludedPreviousPara;
1993 else // Oep > Nep => end of selection moved to a previous paragraph
1995 // 3,2 -> 3,1; 4,7 -> 4,1; 4, 7 -> 4,6; 4,4 -> 4,3
1996 if (Nep >= Nsp) // new end para is still not behind of start
1998 // 4,7 ->4,6
1999 return SelChangeType::ExcludedFollowingPara;
2001 else // new end para is behind start
2003 // 3,2 -> 3,1, 4,7 -> 4,1; 4,4->4,3
2004 if (Oep <= Osp) // it was not ahead already
2006 // 3,2 -> 3,1; 4,4->4,3
2007 return SelChangeType::AddedPreviousPara;
2009 else // it was ahead previously
2011 // 4,7 -> 4,1
2012 return SelChangeType::EndParaBecameBehind;
2017 return SelChangeType::None;
2020 } // namespace
2022 void Document::sendEvent(::sal_Int32 start, ::sal_Int32 end, ::sal_Int16 nEventId)
2024 size_t nAvailDistance = std::distance(m_xParagraphs->begin(), m_aVisibleEnd);
2026 Paragraphs::iterator aEnd(m_xParagraphs->begin());
2027 size_t nEndDistance = std::min<size_t>(end + 1, nAvailDistance);
2028 std::advance(aEnd, nEndDistance);
2030 Paragraphs::iterator aIt(m_xParagraphs->begin());
2031 size_t nStartDistance = std::min<size_t>(start, nAvailDistance);
2032 std::advance(aIt, nStartDistance);
2034 while (aIt < aEnd)
2036 ::rtl::Reference< Paragraph > xParagraph(getParagraph(aIt));
2037 if (xParagraph.is())
2038 xParagraph->notifyEvent(
2039 nEventId,
2040 css::uno::Any(), css::uno::Any());
2041 ++aIt;
2045 void Document::handleSelectionChangeNotification()
2047 ::TextSelection const & rSelection = m_rView.GetSelection();
2048 assert(rSelection.GetStart().GetPara() < m_xParagraphs->size() &&
2049 rSelection.GetEnd().GetPara() < m_xParagraphs->size() &&
2050 "bad SfxHintId::TextViewSelectionChanged event");
2051 ::sal_Int32 nNewFirstPara
2052 = static_cast< ::sal_Int32 >(rSelection.GetStart().GetPara());
2053 ::sal_Int32 nNewFirstPos = rSelection.GetStart().GetIndex();
2054 // XXX numeric overflow
2055 ::sal_Int32 nNewLastPara
2056 = static_cast< ::sal_Int32 >(rSelection.GetEnd().GetPara());
2057 ::sal_Int32 nNewLastPos = rSelection.GetEnd().GetIndex();
2058 // XXX numeric overflow
2060 // Lose focus:
2061 Paragraphs::iterator aIt(m_xParagraphs->begin() + nNewLastPara);
2062 if (m_aFocused != m_xParagraphs->end() && m_aFocused != aIt
2063 && m_aFocused >= m_aVisibleBegin && m_aFocused < m_aVisibleEnd)
2065 ::rtl::Reference< Paragraph > xParagraph(getParagraph(m_aFocused));
2066 if (xParagraph.is())
2067 xParagraph->notifyEvent(
2068 css::accessibility::AccessibleEventId::
2069 STATE_CHANGED,
2070 css::uno::Any(
2071 css::accessibility::AccessibleStateType::FOCUSED),
2072 css::uno::Any());
2075 // Gain focus and update cursor position:
2076 if (aIt >= m_aVisibleBegin && aIt < m_aVisibleEnd
2077 && (aIt != m_aFocused
2078 || nNewLastPara != m_nSelectionLastPara
2079 || nNewLastPos != m_nSelectionLastPos))
2081 ::rtl::Reference< Paragraph > xParagraph(getParagraph(aIt));
2082 if (xParagraph.is())
2084 //disable the first event when user types in empty field.
2085 ::sal_Int32 count = getAccessibleChildCount();
2086 bool bEmpty = count > 1;
2087 //if (aIt != m_aFocused)
2088 if (aIt != m_aFocused && bEmpty)
2089 xParagraph->notifyEvent(
2090 css::accessibility::AccessibleEventId::
2091 STATE_CHANGED,
2092 css::uno::Any(),
2093 css::uno::Any(
2094 css::accessibility::AccessibleStateType::FOCUSED));
2095 if (nNewLastPara != m_nSelectionLastPara
2096 || nNewLastPos != m_nSelectionLastPos)
2097 xParagraph->notifyEvent(
2098 css::accessibility::AccessibleEventId::
2099 CARET_CHANGED,
2100 css::uno::makeAny< ::sal_Int32 >(
2101 nNewLastPara == m_nSelectionLastPara
2102 ? m_nSelectionLastPos : 0),
2103 css::uno::Any(nNewLastPos));
2106 m_aFocused = aIt;
2108 if (m_nSelectionFirstPara != -1)
2110 sal_Int32 nMin;
2111 sal_Int32 nMax;
2112 SelChangeType ret = getSelChangeType(TextPaM(m_nSelectionFirstPara, m_nSelectionFirstPos),
2113 TextPaM(m_nSelectionLastPara, m_nSelectionLastPos),
2114 rSelection.GetStart(), rSelection.GetEnd());
2115 switch (ret)
2117 case SelChangeType::None:
2118 //no event
2119 break;
2120 case SelChangeType::CaretMove:
2121 //only caret moved, already handled in above
2122 break;
2123 case SelChangeType::NoSelToSel:
2124 //old has no selection but new has selection
2125 nMin = std::min(nNewFirstPara, nNewLastPara);
2126 nMax = std::max(nNewFirstPara, nNewLastPara);
2127 sendEvent(nMin, nMax, css::accessibility::AccessibleEventId::SELECTION_CHANGED);
2128 sendEvent(nMin, nMax,
2129 css::accessibility::AccessibleEventId::TEXT_SELECTION_CHANGED);
2130 break;
2131 case SelChangeType::SelToNoSel:
2132 //old has selection but new has no selection.
2133 nMin = std::min(m_nSelectionFirstPara, m_nSelectionLastPara);
2134 nMax = std::max(m_nSelectionFirstPara, m_nSelectionLastPara);
2135 sendEvent(nMin, nMax, css::accessibility::AccessibleEventId::SELECTION_CHANGED);
2136 sendEvent(nMin, nMax,
2137 css::accessibility::AccessibleEventId::TEXT_SELECTION_CHANGED);
2138 break;
2139 case SelChangeType::NoParaChange:
2140 //Send text_selection_change event on Nep
2141 sendEvent(nNewLastPara, nNewLastPara,
2142 css::accessibility::AccessibleEventId::TEXT_SELECTION_CHANGED);
2143 break;
2144 case SelChangeType::EndParaNoMoreBehind:
2145 // 4, 1 -> 4, 7; 4,1 -> 4,4
2146 sendEvent(m_nSelectionLastPara, m_nSelectionFirstPara - 1,
2147 css::accessibility::AccessibleEventId::SELECTION_CHANGED);
2148 sendEvent(nNewFirstPara + 1, nNewLastPara,
2149 css::accessibility::AccessibleEventId::SELECTION_CHANGED);
2151 sendEvent(m_nSelectionLastPara, nNewLastPara,
2152 css::accessibility::AccessibleEventId::TEXT_SELECTION_CHANGED);
2153 break;
2154 case SelChangeType::AddedFollowingPara:
2155 // 1, 2 -> 1, 4; 4,4->4,5;
2156 sendEvent(m_nSelectionLastPara + 1, nNewLastPara,
2157 css::accessibility::AccessibleEventId::SELECTION_CHANGED);
2159 sendEvent(m_nSelectionLastPara, nNewLastPara,
2160 css::accessibility::AccessibleEventId::TEXT_SELECTION_CHANGED);
2161 break;
2162 case SelChangeType::ExcludedPreviousPara:
2163 // 4,1 -> 4,3,
2164 sendEvent(m_nSelectionLastPara + 1, nNewLastPara,
2165 css::accessibility::AccessibleEventId::SELECTION_CHANGED);
2167 sendEvent(m_nSelectionLastPara, nNewLastPara,
2168 css::accessibility::AccessibleEventId::TEXT_SELECTION_CHANGED);
2169 break;
2170 case SelChangeType::ExcludedFollowingPara:
2171 // 4,7 ->4,5;
2172 sendEvent(nNewLastPara + 1, m_nSelectionLastPara,
2173 css::accessibility::AccessibleEventId::SELECTION_CHANGED);
2175 sendEvent(nNewLastPara, m_nSelectionLastPara,
2176 css::accessibility::AccessibleEventId::TEXT_SELECTION_CHANGED);
2177 break;
2178 case SelChangeType::AddedPreviousPara:
2179 // 3,2 -> 3,1; 4,4->4,3
2180 sendEvent(nNewLastPara, m_nSelectionLastPara - 1,
2181 css::accessibility::AccessibleEventId::SELECTION_CHANGED);
2183 sendEvent(nNewLastPara, m_nSelectionLastPara,
2184 css::accessibility::AccessibleEventId::TEXT_SELECTION_CHANGED);
2185 break;
2186 case SelChangeType::EndParaBecameBehind:
2187 // 4,7 -> 4,1
2188 sendEvent(m_nSelectionFirstPara + 1, m_nSelectionLastPara,
2189 css::accessibility::AccessibleEventId::SELECTION_CHANGED);
2190 sendEvent(nNewLastPara, nNewFirstPara - 1,
2191 css::accessibility::AccessibleEventId::SELECTION_CHANGED);
2193 sendEvent(nNewLastPara, m_nSelectionLastPara,
2194 css::accessibility::AccessibleEventId::TEXT_SELECTION_CHANGED);
2195 break;
2199 m_nSelectionFirstPara = nNewFirstPara;
2200 m_nSelectionFirstPos = nNewFirstPos;
2201 m_nSelectionLastPara = nNewLastPara;
2202 m_nSelectionLastPos = nNewLastPos;
2205 void Document::disposeParagraphs()
2207 for (auto const& paragraph : *m_xParagraphs)
2209 css::uno::Reference< css::lang::XComponent > xComponent(
2210 paragraph.getParagraph().get(), css::uno::UNO_QUERY);
2211 if (xComponent.is())
2212 xComponent->dispose();
2216 // static
2217 css::uno::Any Document::mapFontColor(::Color const & rColor)
2219 return css::uno::makeAny(rColor.GetRGBColor());
2220 // FIXME keep transparency?
2223 // static
2224 ::Color Document::mapFontColor(css::uno::Any const & rColor)
2226 ::Color nColor;
2227 rColor >>= nColor;
2228 return nColor;
2231 // static
2232 css::uno::Any Document::mapFontWeight(::FontWeight nWeight)
2234 // Map from ::FontWeight to css::awt::FontWeight, depends on order of
2235 // elements in ::FontWeight (vcl/vclenum.hxx):
2236 static float const aWeight[]
2237 = { css::awt::FontWeight::DONTKNOW, // WEIGHT_DONTKNOW
2238 css::awt::FontWeight::THIN, // WEIGHT_THIN
2239 css::awt::FontWeight::ULTRALIGHT, // WEIGHT_ULTRALIGHT
2240 css::awt::FontWeight::LIGHT, // WEIGHT_LIGHT
2241 css::awt::FontWeight::SEMILIGHT, // WEIGHT_SEMILIGHT
2242 css::awt::FontWeight::NORMAL, // WEIGHT_NORMAL
2243 css::awt::FontWeight::NORMAL, // WEIGHT_MEDIUM
2244 css::awt::FontWeight::SEMIBOLD, // WEIGHT_SEMIBOLD
2245 css::awt::FontWeight::BOLD, // WEIGHT_BOLD
2246 css::awt::FontWeight::ULTRABOLD, // WEIGHT_ULTRABOLD
2247 css::awt::FontWeight::BLACK }; // WEIGHT_BLACK
2248 return css::uno::Any(aWeight[nWeight]);
2251 // static
2252 ::FontWeight Document::mapFontWeight(css::uno::Any const & rWeight)
2254 float nWeight = css::awt::FontWeight::NORMAL;
2255 rWeight >>= nWeight;
2256 return nWeight <= css::awt::FontWeight::DONTKNOW ? WEIGHT_DONTKNOW
2257 : nWeight <= css::awt::FontWeight::THIN ? WEIGHT_THIN
2258 : nWeight <= css::awt::FontWeight::ULTRALIGHT ? WEIGHT_ULTRALIGHT
2259 : nWeight <= css::awt::FontWeight::LIGHT ? WEIGHT_LIGHT
2260 : nWeight <= css::awt::FontWeight::SEMILIGHT ? WEIGHT_SEMILIGHT
2261 : nWeight <= css::awt::FontWeight::NORMAL ? WEIGHT_NORMAL
2262 : nWeight <= css::awt::FontWeight::SEMIBOLD ? WEIGHT_SEMIBOLD
2263 : nWeight <= css::awt::FontWeight::BOLD ? WEIGHT_BOLD
2264 : nWeight <= css::awt::FontWeight::ULTRABOLD ? WEIGHT_ULTRABOLD
2265 : WEIGHT_BLACK;
2270 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */