lok: vcl: fix multiple floatwin removal case more robustly.
[LibreOffice.git] / accessibility / source / extended / textwindowaccessibility.cxx
blob9823a1853aaca2db77d9ccd52943e26be50a69c0
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/lang/IndexOutOfBoundsException.hpp>
24 #include <com/sun/star/lang/WrappedTargetRuntimeException.hpp>
25 #include <com/sun/star/i18n/Boundary.hpp>
26 #include <cppuhelper/exc_hlp.hxx>
27 #include <extended/textwindowaccessibility.hxx>
28 #include <comphelper/accessibleeventnotifier.hxx>
29 #include <unotools/accessiblerelationsethelper.hxx>
30 #include <unotools/accessiblestatesethelper.hxx>
31 #include <vcl/window.hxx>
32 #include <toolkit/helper/convert.hxx>
33 #include <comphelper/sequence.hxx>
35 #include <algorithm>
36 #include <memory>
37 #include <vector>
39 namespace accessibility
41 void SfxListenerGuard::startListening(::SfxBroadcaster & rNotifier)
43 assert(m_pNotifier == nullptr && "called more than once");
44 m_pNotifier = &rNotifier;
45 m_rListener.StartListening(*m_pNotifier, DuplicateHandling::Prevent);
48 void SfxListenerGuard::endListening()
50 if (m_pNotifier != nullptr)
52 m_rListener.EndListening(*m_pNotifier);
53 m_pNotifier = nullptr;
57 void WindowListenerGuard::startListening(vcl::Window & rNotifier)
59 assert(m_pNotifier == nullptr && "called more than once");
60 m_pNotifier = &rNotifier;
61 m_pNotifier->AddEventListener(m_aListener);
64 void WindowListenerGuard::endListening()
66 if (m_pNotifier)
68 m_pNotifier->RemoveEventListener(m_aListener);
69 m_pNotifier = nullptr;
73 Paragraph::Paragraph(::rtl::Reference< Document > const & rDocument,
74 Paragraphs::size_type nNumber):
75 ParagraphBase(m_aMutex),
76 m_xDocument(rDocument),
77 m_nNumber(nNumber),
78 m_nClientId(0)
80 m_aParagraphText = m_xDocument->retrieveParagraphText(this);
83 void
84 Paragraph::numberChanged(bool bIncremented)
86 if (bIncremented)
87 ++m_nNumber;
88 else
89 --m_nNumber;
92 void Paragraph::textChanged()
94 OUString aParagraphText = implGetText();
95 css::uno::Any aOldValue, aNewValue;
96 if ( implInitTextChangedEvent( m_aParagraphText, aParagraphText, aOldValue, aNewValue ) )
98 m_aParagraphText = aParagraphText;
99 notifyEvent(css::accessibility::AccessibleEventId::
100 TEXT_CHANGED,
101 aOldValue, aNewValue);
105 void Paragraph::notifyEvent(::sal_Int16 nEventId,
106 css::uno::Any const & rOldValue,
107 css::uno::Any const & rNewValue)
109 if (m_nClientId)
110 comphelper::AccessibleEventNotifier::addEvent( m_nClientId, css::accessibility::AccessibleEventObject(
111 static_cast< ::cppu::OWeakObject * >(this),
112 nEventId, rNewValue, rOldValue) );
115 // virtual
116 css::uno::Reference< css::accessibility::XAccessibleContext > SAL_CALL
117 Paragraph::getAccessibleContext()
119 checkDisposed();
120 return this;
123 // virtual
124 ::sal_Int32 SAL_CALL Paragraph::getAccessibleChildCount()
126 checkDisposed();
127 return 0;
130 // virtual
131 css::uno::Reference< css::accessibility::XAccessible > SAL_CALL
132 Paragraph::getAccessibleChild(::sal_Int32)
134 checkDisposed();
135 throw css::lang::IndexOutOfBoundsException(
136 "textwindowaccessibility.cxx:"
137 " Paragraph::getAccessibleChild",
138 static_cast< css::uno::XWeak * >(this));
141 // virtual
142 css::uno::Reference< css::accessibility::XAccessible > SAL_CALL
143 Paragraph::getAccessibleParent()
145 checkDisposed();
146 return m_xDocument->getAccessible();
149 // virtual
150 ::sal_Int32 SAL_CALL Paragraph::getAccessibleIndexInParent()
152 checkDisposed();
153 return m_xDocument->retrieveParagraphIndex(this);
156 // virtual
157 ::sal_Int16 SAL_CALL Paragraph::getAccessibleRole()
159 checkDisposed();
160 return css::accessibility::AccessibleRole::PARAGRAPH;
163 // virtual
164 OUString SAL_CALL Paragraph::getAccessibleDescription()
166 checkDisposed();
167 return OUString();
170 // virtual
171 OUString SAL_CALL Paragraph::getAccessibleName()
173 checkDisposed();
174 return OUString();
177 // virtual
178 css::uno::Reference< css::accessibility::XAccessibleRelationSet >
179 SAL_CALL Paragraph::getAccessibleRelationSet()
181 checkDisposed();
182 return m_xDocument->retrieveParagraphRelationSet( this );
185 // virtual
186 css::uno::Reference< css::accessibility::XAccessibleStateSet >
187 SAL_CALL Paragraph::getAccessibleStateSet()
189 checkDisposed();
191 // FIXME Notification of changes (STATE_CHANGED) missing when
192 // m_rView.IsReadOnly() changes:
193 return new ::utl::AccessibleStateSetHelper(
194 m_xDocument->retrieveParagraphState(this));
197 // virtual
198 css::lang::Locale SAL_CALL Paragraph::getLocale()
200 checkDisposed();
201 return m_xDocument->retrieveLocale();
204 // virtual
205 sal_Bool SAL_CALL Paragraph::containsPoint(css::awt::Point const & rPoint)
207 checkDisposed();
208 css::awt::Rectangle aRect(m_xDocument->retrieveParagraphBounds(this,
209 false));
210 return rPoint.X >= 0 && rPoint.X < aRect.Width
211 && rPoint.Y >= 0 && rPoint.Y < aRect.Height;
214 // virtual
215 css::uno::Reference< css::accessibility::XAccessible > SAL_CALL
216 Paragraph::getAccessibleAtPoint(css::awt::Point const &)
218 checkDisposed();
219 return nullptr;
222 // virtual
223 css::awt::Rectangle SAL_CALL Paragraph::getBounds()
225 checkDisposed();
226 return m_xDocument->retrieveParagraphBounds(this, false);
229 // virtual
230 css::awt::Point SAL_CALL Paragraph::getLocation()
232 checkDisposed();
233 css::awt::Rectangle aRect(m_xDocument->retrieveParagraphBounds(this,
234 false));
235 return css::awt::Point(aRect.X, aRect.Y);
238 // virtual
239 css::awt::Point SAL_CALL Paragraph::getLocationOnScreen()
241 checkDisposed();
242 css::awt::Rectangle aRect(m_xDocument->retrieveParagraphBounds(this,
243 true));
244 return css::awt::Point(aRect.X, aRect.Y);
247 // virtual
248 css::awt::Size SAL_CALL Paragraph::getSize()
250 checkDisposed();
251 css::awt::Rectangle aRect(m_xDocument->retrieveParagraphBounds(this,
252 false));
253 return css::awt::Size(aRect.Width, aRect.Height);
256 // virtual
257 void SAL_CALL Paragraph::grabFocus()
259 checkDisposed();
260 VclPtr<vcl::Window> pWindow = m_xDocument->GetWindow();
261 if ( pWindow )
263 pWindow->GrabFocus();
267 m_xDocument->changeParagraphSelection(this, 0, 0);
269 catch (const css::lang::IndexOutOfBoundsException & rEx)
271 SAL_INFO("accessibility",
272 "textwindowaccessibility.cxx: Paragraph::grabFocus: caught unexpected "
273 << rEx);
277 // virtual
278 css::util::Color SAL_CALL Paragraph::getForeground()
280 return 0; // TODO
283 // virtual
284 css::util::Color SAL_CALL Paragraph::getBackground()
286 return 0; // TODO
289 // virtual
290 ::sal_Int32 SAL_CALL Paragraph::getCaretPosition()
292 checkDisposed();
293 return m_xDocument->retrieveParagraphCaretPosition(this);
296 // virtual
297 sal_Bool SAL_CALL Paragraph::setCaretPosition(::sal_Int32 nIndex)
299 checkDisposed();
300 m_xDocument->changeParagraphSelection(this, nIndex, nIndex);
301 return true;
304 // virtual
305 ::sal_Unicode SAL_CALL Paragraph::getCharacter(::sal_Int32 nIndex)
307 checkDisposed();
308 return OCommonAccessibleText::implGetCharacter(implGetText(), nIndex);
311 // virtual
312 css::uno::Sequence< css::beans::PropertyValue > SAL_CALL
313 Paragraph::getCharacterAttributes(::sal_Int32 nIndex, const css::uno::Sequence< OUString >& aRequestedAttributes)
315 checkDisposed();
316 return m_xDocument->retrieveCharacterAttributes( this, nIndex, aRequestedAttributes );
319 // virtual
320 css::awt::Rectangle SAL_CALL
321 Paragraph::getCharacterBounds(::sal_Int32 nIndex)
323 checkDisposed();
324 css::awt::Rectangle aBounds(m_xDocument->retrieveCharacterBounds(this, nIndex));
325 css::awt::Rectangle aParaBounds(m_xDocument->retrieveParagraphBounds(this, false));
326 aBounds.X -= aParaBounds.X;
327 aBounds.Y -= aParaBounds.Y;
328 return aBounds;
331 // virtual
332 ::sal_Int32 SAL_CALL Paragraph::getCharacterCount()
334 checkDisposed();
335 return implGetText().getLength();
338 // virtual
339 ::sal_Int32 SAL_CALL
340 Paragraph::getIndexAtPoint(css::awt::Point const & rPoint)
342 checkDisposed();
343 css::awt::Point aPoint(rPoint);
344 css::awt::Rectangle aParaBounds(m_xDocument->retrieveParagraphBounds(this, false));
345 aPoint.X += aParaBounds.X;
346 aPoint.Y += aParaBounds.Y;
347 return m_xDocument->retrieveCharacterIndex(this, aPoint);
350 // virtual
351 OUString SAL_CALL Paragraph::getSelectedText()
353 checkDisposed();
355 return OCommonAccessibleText::getSelectedText();
358 // virtual
359 ::sal_Int32 SAL_CALL Paragraph::getSelectionStart()
361 checkDisposed();
362 return OCommonAccessibleText::getSelectionStart();
365 // virtual
366 ::sal_Int32 SAL_CALL Paragraph::getSelectionEnd()
368 checkDisposed();
369 return OCommonAccessibleText::getSelectionEnd();
372 // virtual
373 sal_Bool SAL_CALL Paragraph::setSelection(::sal_Int32 nStartIndex,
374 ::sal_Int32 nEndIndex)
376 checkDisposed();
377 m_xDocument->changeParagraphSelection(this, nStartIndex, nEndIndex);
378 return true;
381 // virtual
382 OUString SAL_CALL Paragraph::getText()
384 checkDisposed();
385 return implGetText();
388 // virtual
389 OUString SAL_CALL Paragraph::getTextRange(::sal_Int32 nStartIndex,
390 ::sal_Int32 nEndIndex)
392 checkDisposed();
393 return OCommonAccessibleText::implGetTextRange(implGetText(), nStartIndex, nEndIndex);
396 // virtual
397 css::accessibility::TextSegment SAL_CALL Paragraph::getTextAtIndex( sal_Int32 nIndex, sal_Int16 aTextType )
399 checkDisposed();
400 return OCommonAccessibleText::getTextAtIndex(nIndex, aTextType);
403 // virtual
404 css::accessibility::TextSegment SAL_CALL Paragraph::getTextBeforeIndex( sal_Int32 nIndex, sal_Int16 aTextType )
406 checkDisposed();
407 return OCommonAccessibleText::getTextBeforeIndex(nIndex, aTextType);
410 // virtual
411 css::accessibility::TextSegment SAL_CALL Paragraph::getTextBehindIndex( sal_Int32 nIndex, sal_Int16 aTextType )
413 checkDisposed();
414 return OCommonAccessibleText::getTextBehindIndex(nIndex, aTextType);
417 // virtual
418 sal_Bool SAL_CALL Paragraph::copyText(::sal_Int32 nStartIndex,
419 ::sal_Int32 nEndIndex)
421 checkDisposed();
422 m_xDocument->copyParagraphText(this, nStartIndex, nEndIndex);
423 return true;
426 // virtual
427 sal_Bool SAL_CALL Paragraph::cutText(::sal_Int32 nStartIndex,
428 ::sal_Int32 nEndIndex)
430 checkDisposed();
431 m_xDocument->changeParagraphText(this, nStartIndex, nEndIndex, true, false,
432 OUString());
433 return true;
436 // virtual
437 sal_Bool SAL_CALL Paragraph::pasteText(::sal_Int32 nIndex)
439 checkDisposed();
440 m_xDocument->changeParagraphText(this, nIndex, nIndex, false, true,
441 OUString());
442 return true;
445 // virtual
446 sal_Bool SAL_CALL Paragraph::deleteText(::sal_Int32 nStartIndex,
447 ::sal_Int32 nEndIndex)
449 checkDisposed();
450 m_xDocument->changeParagraphText(this, nStartIndex, nEndIndex, false, false,
451 OUString());
452 return true;
455 // virtual
456 sal_Bool SAL_CALL Paragraph::insertText(OUString const & rText,
457 ::sal_Int32 nIndex)
459 checkDisposed();
460 m_xDocument->changeParagraphText(this, nIndex, nIndex, false, false, rText);
461 return true;
464 // virtual
465 sal_Bool SAL_CALL
466 Paragraph::replaceText(::sal_Int32 nStartIndex, ::sal_Int32 nEndIndex,
467 OUString const & rReplacement)
469 checkDisposed();
470 m_xDocument->changeParagraphText(this, nStartIndex, nEndIndex, false, false,
471 rReplacement);
472 return true;
475 // virtual
476 sal_Bool SAL_CALL Paragraph::setAttributes(
477 ::sal_Int32 nStartIndex, ::sal_Int32 nEndIndex,
478 css::uno::Sequence< css::beans::PropertyValue > const & rAttributeSet)
480 checkDisposed();
481 m_xDocument->changeParagraphAttributes(this, nStartIndex, nEndIndex,
482 rAttributeSet);
483 return true;
486 // virtual
487 sal_Bool SAL_CALL Paragraph::setText(OUString const & rText)
489 checkDisposed();
490 m_xDocument->changeParagraphText(this, rText);
491 return true;
494 // virtual
495 css::uno::Sequence< css::beans::PropertyValue > SAL_CALL
496 Paragraph::getDefaultAttributes(const css::uno::Sequence< OUString >&)
498 checkDisposed();
499 return {}; // default attributes are not supported by text engine
502 // virtual
503 css::uno::Sequence< css::beans::PropertyValue > SAL_CALL
504 Paragraph::getRunAttributes(::sal_Int32 Index, const css::uno::Sequence< OUString >& RequestedAttributes)
506 checkDisposed();
507 return m_xDocument->retrieveRunAttributes( this, Index, RequestedAttributes );
510 // virtual
511 ::sal_Int32 SAL_CALL Paragraph::getLineNumberAtIndex( ::sal_Int32 nIndex )
513 checkDisposed();
515 ::sal_Int32 nLineNo = -1;
516 m_xDocument->retrieveParagraphLineBoundary( this, nIndex, &nLineNo );
518 return nLineNo;
521 // virtual
522 css::accessibility::TextSegment SAL_CALL Paragraph::getTextAtLineNumber( ::sal_Int32 nLineNo )
524 checkDisposed();
526 css::i18n::Boundary aBoundary =
527 m_xDocument->retrieveParagraphBoundaryOfLine( this, nLineNo );
529 return css::accessibility::TextSegment( getTextRange(aBoundary.startPos, aBoundary.endPos),
530 aBoundary.startPos, aBoundary.endPos);
533 // virtual
534 css::accessibility::TextSegment SAL_CALL Paragraph::getTextAtLineWithCaret( )
536 checkDisposed();
538 sal_Int32 nLineNo = getNumberOfLineWithCaret();
540 try {
541 return ( nLineNo >= 0 ) ?
542 getTextAtLineNumber( nLineNo ) :
543 css::accessibility::TextSegment();
544 } catch (const css::lang::IndexOutOfBoundsException&) {
545 css::uno::Any anyEx = cppu::getCaughtException();
546 throw css::lang::WrappedTargetRuntimeException(
547 "textwindowaccessibility.cxx:"
548 " Paragraph::getTextAtLineWithCaret",
549 static_cast< css::uno::XWeak * >( this ), anyEx );
553 // virtual
554 ::sal_Int32 SAL_CALL Paragraph::getNumberOfLineWithCaret( )
556 checkDisposed();
557 return m_xDocument->retrieveParagraphLineWithCursor(this);
561 // virtual
562 void SAL_CALL Paragraph::addAccessibleEventListener(
563 css::uno::Reference<
564 css::accessibility::XAccessibleEventListener > const & rListener)
566 if (rListener.is())
568 ::osl::ClearableMutexGuard aGuard(rBHelper.rMutex);
569 if (rBHelper.bDisposed || rBHelper.bInDispose)
571 aGuard.clear();
572 rListener->disposing(css::lang::EventObject(
573 static_cast< ::cppu::OWeakObject * >(this)));
575 else
577 if (!m_nClientId)
578 m_nClientId = comphelper::AccessibleEventNotifier::registerClient( );
579 comphelper::AccessibleEventNotifier::addEventListener( m_nClientId, rListener );
584 // virtual
585 void SAL_CALL Paragraph::removeAccessibleEventListener(
586 css::uno::Reference<
587 css::accessibility::XAccessibleEventListener > const & rListener)
589 comphelper::AccessibleEventNotifier::TClientId nId = 0;
591 ::osl::ClearableMutexGuard aGuard(rBHelper.rMutex);
592 if (rListener.is() && m_nClientId != 0
593 && comphelper::AccessibleEventNotifier::removeEventListener( m_nClientId, rListener ) == 0)
595 nId = m_nClientId;
596 m_nClientId = 0;
599 if (nId != 0)
601 // no listeners anymore
602 // -> revoke ourself. This may lead to the notifier thread dying (if we were the last client),
603 // and at least to us not firing any events anymore, in case somebody calls
604 // NotifyAccessibleEvent, again
605 comphelper::AccessibleEventNotifier::revokeClient(nId);
609 // virtual
610 void SAL_CALL Paragraph::disposing()
612 comphelper::AccessibleEventNotifier::TClientId nId = 0;
614 ::osl::ClearableMutexGuard aGuard(rBHelper.rMutex);
615 nId = m_nClientId;
616 m_nClientId = 0;
618 if (nId != 0)
619 comphelper::AccessibleEventNotifier::revokeClientNotifyDisposing(nId, *this);
622 // virtual
623 OUString Paragraph::implGetText()
625 return m_xDocument->retrieveParagraphText(this);
628 // virtual
629 css::lang::Locale Paragraph::implGetLocale()
631 return m_xDocument->retrieveLocale();
634 // virtual
635 void Paragraph::implGetSelection(::sal_Int32 & rStartIndex,
636 ::sal_Int32 & rEndIndex)
638 m_xDocument->retrieveParagraphSelection(this, &rStartIndex, &rEndIndex);
641 // virtual
642 void Paragraph::implGetParagraphBoundary( const OUString& rText,
643 css::i18n::Boundary& rBoundary,
644 ::sal_Int32 nIndex )
646 ::sal_Int32 nLength = rText.getLength();
648 if ( implIsValidIndex( nIndex, nLength ) )
650 rBoundary.startPos = 0;
651 rBoundary.endPos = nLength;
653 else
655 rBoundary.startPos = nIndex;
656 rBoundary.endPos = nIndex;
660 // virtual
661 void Paragraph::implGetLineBoundary( const OUString& rText,
662 css::i18n::Boundary& rBoundary,
663 ::sal_Int32 nIndex )
665 ::sal_Int32 nLength = rText.getLength();
667 if ( implIsValidIndex( nIndex, nLength ) || nIndex == nLength )
669 css::i18n::Boundary aBoundary =
670 m_xDocument->retrieveParagraphLineBoundary( this, nIndex, nullptr );
671 rBoundary.startPos = aBoundary.startPos;
672 rBoundary.endPos = aBoundary.endPos;
674 else
676 rBoundary.startPos = nIndex;
677 rBoundary.endPos = nIndex;
682 void Paragraph::checkDisposed()
684 ::osl::MutexGuard aGuard(rBHelper.rMutex);
685 if (!(rBHelper.bDisposed || rBHelper.bInDispose))
686 return;
687 throw css::lang::DisposedException(
688 OUString(), static_cast< css::uno::XWeak * >(this));
691 Document::Document(::VCLXWindow * pVclXWindow, ::TextEngine & rEngine,
692 ::TextView & rView):
693 VCLXAccessibleComponent(pVclXWindow),
694 m_xAccessible(pVclXWindow),
695 m_rEngine(rEngine),
696 m_rView(rView),
697 m_aEngineListener(*this),
698 m_aViewListener(LINK(this, Document, WindowEventHandler)),
699 m_nViewOffset(0),
700 m_nViewHeight(0),
701 m_nVisibleBeginOffset(0),
702 m_nSelectionFirstPara(-1),
703 m_nSelectionFirstPos(-1),
704 m_nSelectionLastPara(-1),
705 m_nSelectionLastPos(-1),
706 m_bSelectionChangedNotification(false)
709 css::lang::Locale Document::retrieveLocale()
711 SolarMutexGuard aGuard;
712 return m_rEngine.GetLocale();
715 ::sal_Int32 Document::retrieveParagraphIndex(Paragraph const * pParagraph)
717 ::osl::MutexGuard aInternalGuard(GetMutex());
719 // If a client holds on to a Paragraph that is no longer visible, it can
720 // happen that this Paragraph lies outside the range from m_aVisibleBegin
721 // to m_aVisibleEnd. In that case, return -1 instead of a valid index:
722 Paragraphs::iterator aPara(m_xParagraphs->begin()
723 + pParagraph->getNumber());
724 return aPara < m_aVisibleBegin || aPara >= m_aVisibleEnd
725 ? -1 : static_cast< ::sal_Int32 >(aPara - m_aVisibleBegin);
726 // XXX numeric overflow
729 ::sal_Int64 Document::retrieveParagraphState(Paragraph const * pParagraph)
731 ::osl::MutexGuard aInternalGuard(GetMutex());
733 // If a client holds on to a Paragraph that is no longer visible, it can
734 // happen that this Paragraph lies outside the range from m_aVisibleBegin
735 // to m_aVisibleEnd. In that case, it is neither VISIBLE nor SHOWING:
736 ::sal_Int64 nState
737 = (static_cast< ::sal_Int64 >(1)
738 << css::accessibility::AccessibleStateType::ENABLED)
739 | (static_cast< ::sal_Int64 >(1)
740 << css::accessibility::AccessibleStateType::SENSITIVE)
741 | (static_cast< ::sal_Int64 >(1)
742 << css::accessibility::AccessibleStateType::FOCUSABLE)
743 | (static_cast< ::sal_Int64 >(1)
744 << css::accessibility::AccessibleStateType::MULTI_LINE);
745 if (!m_rView.IsReadOnly())
746 nState |= (static_cast< ::sal_Int64 >(1)
747 << css::accessibility::AccessibleStateType::EDITABLE);
748 Paragraphs::iterator aPara(m_xParagraphs->begin()
749 + pParagraph->getNumber());
750 if (aPara >= m_aVisibleBegin && aPara < m_aVisibleEnd)
752 nState
753 |= (static_cast< ::sal_Int64 >(1)
754 << css::accessibility::AccessibleStateType::VISIBLE)
755 | (static_cast< ::sal_Int64 >(1)
756 << css::accessibility::AccessibleStateType::SHOWING);
757 if (aPara == m_aFocused)
758 nState |= (static_cast< ::sal_Int64 >(1)
759 << css::accessibility::AccessibleStateType::FOCUSED);
761 return nState;
764 css::awt::Rectangle
765 Document::retrieveParagraphBounds(Paragraph const * pParagraph,
766 bool bAbsolute)
768 SolarMutexGuard aGuard;
769 ::osl::MutexGuard aInternalGuard(GetMutex());
771 // If a client holds on to a Paragraph that is no longer visible (as it
772 // scrolled out the top of the view), it can happen that this Paragraph
773 // lies before m_aVisibleBegin. In that case, calculate the vertical
774 // position of the Paragraph starting at paragraph 0, otherwise optimize
775 // and start at m_aVisibleBegin:
776 Paragraphs::iterator aPara(m_xParagraphs->begin()
777 + pParagraph->getNumber());
778 ::sal_Int32 nPos;
779 Paragraphs::iterator aIt;
780 if (aPara < m_aVisibleBegin)
782 nPos = 0;
783 aIt = m_xParagraphs->begin();
785 else
787 nPos = m_nViewOffset - m_nVisibleBeginOffset;
788 aIt = m_aVisibleBegin;
790 for (; aIt != aPara; ++aIt)
791 nPos += aIt->getHeight();
793 Point aOrig(0, 0);
794 if (bAbsolute)
795 aOrig = m_rView.GetWindow()->OutputToAbsoluteScreenPixel(aOrig);
797 return css::awt::Rectangle(
798 static_cast< ::sal_Int32 >(aOrig.X()),
799 static_cast< ::sal_Int32 >(aOrig.Y()) + nPos - m_nViewOffset,
800 m_rView.GetWindow()->GetOutputSizePixel().Width(), aPara->getHeight());
801 // XXX numeric overflow (3x)
804 OUString
805 Document::retrieveParagraphText(Paragraph const * pParagraph)
807 SolarMutexGuard aGuard;
808 ::osl::MutexGuard aInternalGuard(GetMutex());
809 return m_rEngine.GetText(static_cast< ::sal_uInt32 >(pParagraph->getNumber()));
810 // numeric overflow cannot happen here
813 void Document::retrieveParagraphSelection(Paragraph const * pParagraph,
814 ::sal_Int32 * pBegin,
815 ::sal_Int32 * pEnd)
817 SolarMutexGuard aGuard;
818 ::osl::MutexGuard aInternalGuard(GetMutex());
819 ::TextSelection const & rSelection = m_rView.GetSelection();
820 Paragraphs::size_type nNumber = pParagraph->getNumber();
821 TextPaM aStartPaM( rSelection.GetStart() );
822 TextPaM aEndPaM( rSelection.GetEnd() );
823 TextPaM aMinPaM( std::min( aStartPaM, aEndPaM ) );
824 TextPaM aMaxPaM( std::max( aStartPaM, aEndPaM ) );
826 if ( nNumber >= aMinPaM.GetPara() && nNumber <= aMaxPaM.GetPara() )
828 *pBegin = nNumber > aMinPaM.GetPara() ? 0 : aMinPaM.GetIndex();
829 // XXX numeric overflow
830 *pEnd = nNumber < aMaxPaM.GetPara()
831 ? m_rEngine.GetText(static_cast< ::sal_uLong >(nNumber)).getLength()
832 : aMaxPaM.GetIndex();
833 // XXX numeric overflow (3x)
835 if ( aStartPaM > aEndPaM )
836 std::swap( *pBegin, *pEnd );
838 else
840 *pBegin = 0;
841 *pEnd = 0;
845 ::sal_Int32 Document::retrieveParagraphCaretPosition(Paragraph const * pParagraph)
847 SolarMutexGuard aGuard;
848 ::osl::MutexGuard aInternalGuard(GetMutex());
849 ::TextSelection const & rSelection = m_rView.GetSelection();
850 Paragraphs::size_type nNumber = pParagraph->getNumber();
851 TextPaM aEndPaM( rSelection.GetEnd() );
853 return aEndPaM.GetPara() == nNumber ? aEndPaM.GetIndex() : -1;
856 css::awt::Rectangle
857 Document::retrieveCharacterBounds(Paragraph const * pParagraph,
858 ::sal_Int32 nIndex)
860 SolarMutexGuard aGuard;
861 ::osl::MutexGuard aInternalGuard(GetMutex());
862 ::sal_uLong nNumber = static_cast< ::sal_uLong >(pParagraph->getNumber());
863 sal_Int32 nLength = m_rEngine.GetText(nNumber).getLength();
864 // XXX numeric overflow
865 if (nIndex < 0 || nIndex > nLength)
866 throw css::lang::IndexOutOfBoundsException(
867 "textwindowaccessibility.cxx:"
868 " Document::retrieveCharacterAttributes",
869 static_cast< css::uno::XWeak * >(this));
870 css::awt::Rectangle aBounds( 0, 0, 0, 0 );
871 if ( nIndex == nLength )
873 aBounds = AWTRectangle(
874 m_rEngine.PaMtoEditCursor(::TextPaM(nNumber, nIndex)));
876 else
878 ::tools::Rectangle aLeft(
879 m_rEngine.PaMtoEditCursor(::TextPaM(nNumber, nIndex)));
880 // XXX numeric overflow
881 ::tools::Rectangle aRight(
882 m_rEngine.PaMtoEditCursor(::TextPaM(nNumber, nIndex + 1)));
883 // XXX numeric overflow (2x)
884 // FIXME If the vertical extends of the two cursors do not match, assume
885 // nIndex is the last character on the line; the bounding box will then
886 // extend to m_rEnginge.GetMaxTextWidth():
887 ::sal_Int32 nWidth = (aLeft.Top() == aRight.Top()
888 && aLeft.Bottom() == aRight.Bottom())
889 ? static_cast< ::sal_Int32 >(aRight.Left() - aLeft.Left())
890 : static_cast< ::sal_Int32 >(m_rEngine.GetMaxTextWidth()
891 - aLeft.Left());
892 // XXX numeric overflow (4x)
893 aBounds = css::awt::Rectangle(static_cast< ::sal_Int32 >(aLeft.Left()),
894 static_cast< ::sal_Int32 >(aLeft.Top() - m_nViewOffset),
895 nWidth,
896 static_cast< ::sal_Int32 >(aLeft.Bottom()
897 - aLeft.Top()));
898 // XXX numeric overflow (4x)
900 return aBounds;
903 ::sal_Int32 Document::retrieveCharacterIndex(Paragraph const * pParagraph,
904 css::awt::Point const & rPoint)
906 SolarMutexGuard aGuard;
907 ::osl::MutexGuard aInternalGuard(GetMutex());
908 ::sal_uLong nNumber = static_cast< ::sal_uLong >(pParagraph->getNumber());
909 // XXX numeric overflow
910 ::TextPaM aPaM(m_rEngine.GetPaM(::Point(static_cast< long >(rPoint.X),
911 static_cast< long >(rPoint.Y))));
912 // XXX numeric overflow (2x)
913 return aPaM.GetPara() == nNumber ? aPaM.GetIndex() : -1;
914 // XXX numeric overflow
917 struct IndexCompare
919 const css::beans::PropertyValue* pValues;
920 explicit IndexCompare(const css::beans::PropertyValue* pVals)
921 : pValues(pVals)
924 bool operator() ( sal_Int32 a, sal_Int32 b ) const
926 return pValues[a].Name < pValues[b].Name;
930 css::uno::Sequence< css::beans::PropertyValue >
931 Document::retrieveCharacterAttributes(
932 Paragraph const * pParagraph, ::sal_Int32 nIndex,
933 const css::uno::Sequence< OUString >& aRequestedAttributes)
935 SolarMutexGuard aGuard;
937 vcl::Font aFont = m_rEngine.GetFont();
938 const sal_Int32 AttributeCount = 9;
939 sal_Int32 i = 0;
940 css::uno::Sequence< css::beans::PropertyValue > aAttribs( AttributeCount );
942 //character background color
943 aAttribs[i].Name = "CharBackColor";
944 aAttribs[i].Handle = -1;
945 aAttribs[i].Value = mapFontColor( aFont.GetFillColor() );
946 aAttribs[i].State = css::beans::PropertyState_DIRECT_VALUE;
947 i++;
949 //character color
950 aAttribs[i].Name = "CharColor";
951 aAttribs[i].Handle = -1;
952 //aAttribs[i].Value = mapFontColor( aFont.GetColor() );
953 aAttribs[i].Value = mapFontColor( m_rEngine.GetTextColor() );
954 aAttribs[i].State = css::beans::PropertyState_DIRECT_VALUE;
955 i++;
957 //character font name
958 aAttribs[i].Name = "CharFontName";
959 aAttribs[i].Handle = -1;
960 aAttribs[i].Value <<= aFont.GetFamilyName();
961 aAttribs[i].State = css::beans::PropertyState_DIRECT_VALUE;
962 i++;
964 //character height
965 aAttribs[i].Name = "CharHeight";
966 aAttribs[i].Handle = -1;
967 aAttribs[i].Value <<= static_cast<sal_Int16>(aFont.GetFontHeight());
968 aAttribs[i].State = css::beans::PropertyState_DIRECT_VALUE;
969 i++;
971 //character posture
972 aAttribs[i].Name = "CharPosture";
973 aAttribs[i].Handle = -1;
974 aAttribs[i].Value <<= static_cast<sal_Int16>(aFont.GetItalic());
975 aAttribs[i].State = css::beans::PropertyState_DIRECT_VALUE;
976 i++;
978 //character relief
980 aAttribs[i].Name = "CharRelief";
981 aAttribs[i].Handle = -1;
982 aAttribs[i].Value = css::uno::Any( (sal_Int16)aFont.GetRelief() );
983 aAttribs[i].State = css::beans::PropertyState_DIRECT_VALUE;
984 i++;
987 //character strikeout
988 aAttribs[i].Name = "CharStrikeout";
989 aAttribs[i].Handle = -1;
990 aAttribs[i].Value <<= static_cast<sal_Int16>(aFont.GetStrikeout());
991 aAttribs[i].State = css::beans::PropertyState_DIRECT_VALUE;
992 i++;
994 //character underline
995 aAttribs[i].Name = "CharUnderline";
996 aAttribs[i].Handle = -1;
997 aAttribs[i].Value <<= static_cast<sal_Int16>(aFont.GetUnderline());
998 aAttribs[i].State = css::beans::PropertyState_DIRECT_VALUE;
999 i++;
1001 //character weight
1002 aAttribs[i].Name = "CharWeight";
1003 aAttribs[i].Handle = -1;
1004 aAttribs[i].Value <<= static_cast<float>(aFont.GetWeight());
1005 aAttribs[i].State = css::beans::PropertyState_DIRECT_VALUE;
1006 i++;
1008 //character alignment
1009 aAttribs[i].Name = "ParaAdjust";
1010 aAttribs[i].Handle = -1;
1011 aAttribs[i].Value <<= static_cast<sal_Int16>(m_rEngine.GetTextAlign());
1012 aAttribs[i].State = css::beans::PropertyState_DIRECT_VALUE;
1013 i++;
1015 ::osl::MutexGuard aInternalGuard(GetMutex());
1016 ::sal_uLong nNumber = static_cast< ::sal_uLong >(pParagraph->getNumber());
1017 // XXX numeric overflow
1018 // nIndex can be equal to getLength();
1019 if (nIndex < 0 || nIndex > m_rEngine.GetText(nNumber).getLength())
1020 throw css::lang::IndexOutOfBoundsException(
1021 "textwindowaccessibility.cxx:"
1022 " Document::retrieveCharacterAttributes",
1023 static_cast< css::uno::XWeak * >(this));
1026 // retrieve run attributes
1027 tPropValMap aCharAttrSeq;
1028 retrieveRunAttributesImpl( pParagraph, nIndex, aRequestedAttributes, aCharAttrSeq );
1030 css::beans::PropertyValue* pValues = aAttribs.getArray();
1031 for (i = 0; i < AttributeCount; i++,pValues++)
1033 aCharAttrSeq[ pValues->Name ] = *pValues;
1036 css::uno::Sequence< css::beans::PropertyValue > aRes = comphelper::mapValuesToSequence( aCharAttrSeq );
1038 // sort the attributes
1039 sal_Int32 nLength = aRes.getLength();
1040 const css::beans::PropertyValue* pPairs = aRes.getConstArray();
1041 std::unique_ptr<sal_Int32[]> pIndices( new sal_Int32[nLength] );
1042 for( i = 0; i < nLength; i++ )
1043 pIndices[i] = i;
1044 std::sort( &pIndices[0], &pIndices[nLength], IndexCompare(pPairs) );
1045 // create sorted sequences according to index array
1046 css::uno::Sequence< css::beans::PropertyValue > aNewValues( nLength );
1047 css::beans::PropertyValue* pNewValues = aNewValues.getArray();
1048 for( i = 0; i < nLength; i++ )
1050 pNewValues[i] = pPairs[pIndices[i]];
1053 return aNewValues;
1056 void Document::retrieveRunAttributesImpl(
1057 Paragraph const * pParagraph, ::sal_Int32 Index,
1058 const css::uno::Sequence< OUString >& RequestedAttributes,
1059 tPropValMap& rRunAttrSeq)
1061 ::sal_uLong nNumber = static_cast< ::sal_uLong >( pParagraph->getNumber() );
1062 ::TextPaM aPaM( nNumber, Index );
1063 // XXX numeric overflow
1064 ::TextAttribFontColor const * pColor
1065 = static_cast< ::TextAttribFontColor const * >(
1066 m_rEngine.FindAttrib( aPaM, TEXTATTR_FONTCOLOR ) );
1067 ::TextAttribFontWeight const * pWeight
1068 = static_cast< ::TextAttribFontWeight const * >(
1069 m_rEngine.FindAttrib( aPaM, TEXTATTR_FONTWEIGHT ) );
1070 tPropValMap aRunAttrSeq;
1071 if ( pColor )
1073 css::beans::PropertyValue aPropVal;
1074 aPropVal.Name = "CharColor";
1075 aPropVal.Handle = -1;
1076 aPropVal.Value = mapFontColor( pColor->GetColor() );
1077 aPropVal.State = css::beans::PropertyState_DIRECT_VALUE;
1078 aRunAttrSeq[ aPropVal.Name ] = aPropVal;
1080 if ( pWeight )
1082 css::beans::PropertyValue aPropVal;
1083 aPropVal.Name = "CharWeight";
1084 aPropVal.Handle = -1;
1085 aPropVal.Value = mapFontWeight( pWeight->getFontWeight() );
1086 aPropVal.State = css::beans::PropertyState_DIRECT_VALUE;
1087 aRunAttrSeq[ aPropVal.Name ] = aPropVal;
1089 if ( RequestedAttributes.getLength() == 0 )
1091 rRunAttrSeq = aRunAttrSeq;
1093 else
1095 const OUString* pReqAttrs = RequestedAttributes.getConstArray();
1096 const ::sal_Int32 nLength = RequestedAttributes.getLength();
1097 for ( ::sal_Int32 i = 0; i < nLength; ++i )
1099 tPropValMap::iterator aIter = aRunAttrSeq.find( pReqAttrs[i] );
1100 if ( aIter != aRunAttrSeq.end() )
1102 rRunAttrSeq[ (*aIter).first ] = (*aIter).second;
1108 css::uno::Sequence< css::beans::PropertyValue >
1109 Document::retrieveRunAttributes(
1110 Paragraph const * pParagraph, ::sal_Int32 Index,
1111 const css::uno::Sequence< OUString >& RequestedAttributes)
1113 SolarMutexGuard aGuard;
1114 ::osl::MutexGuard aInternalGuard( GetMutex() );
1115 ::sal_uLong nNumber = static_cast< ::sal_uLong >( pParagraph->getNumber() );
1116 // XXX numeric overflow
1117 if ( Index < 0 || Index >= m_rEngine.GetText(nNumber).getLength() )
1118 throw css::lang::IndexOutOfBoundsException(
1119 "textwindowaccessibility.cxx:"
1120 " Document::retrieveRunAttributes",
1121 static_cast< css::uno::XWeak * >( this ) );
1123 tPropValMap aRunAttrSeq;
1124 retrieveRunAttributesImpl( pParagraph, Index, RequestedAttributes, aRunAttrSeq );
1125 return comphelper::mapValuesToSequence( aRunAttrSeq );
1128 void Document::changeParagraphText(Paragraph const * pParagraph,
1129 OUString const & rText)
1131 SolarMutexGuard aGuard;
1133 ::osl::MutexGuard aInternalGuard(GetMutex());
1134 ::sal_uLong nNumber = static_cast< ::sal_uLong >(pParagraph->getNumber());
1135 // XXX numeric overflow
1136 changeParagraphText(nNumber, 0, m_rEngine.GetTextLen(nNumber), false,
1137 false, rText);
1141 void Document::changeParagraphText(Paragraph const * pParagraph,
1142 ::sal_Int32 nBegin, ::sal_Int32 nEnd,
1143 bool bCut, bool bPaste,
1144 OUString const & rText)
1146 SolarMutexGuard aGuard;
1148 ::osl::MutexGuard aInternalGuard(GetMutex());
1149 ::sal_uLong nNumber = static_cast< ::sal_uLong >(pParagraph->getNumber());
1150 // XXX numeric overflow
1151 if (nBegin < 0 || nBegin > nEnd
1152 || nEnd > m_rEngine.GetText(nNumber).getLength())
1153 throw css::lang::IndexOutOfBoundsException(
1154 "textwindowaccessibility.cxx:"
1155 " Document::changeParagraphText",
1156 static_cast< css::uno::XWeak * >(this));
1157 changeParagraphText(nNumber, static_cast< ::sal_uInt16 >(nBegin),
1158 static_cast< ::sal_uInt16 >(nEnd), bCut, bPaste, rText);
1159 // XXX numeric overflow (2x)
1163 void Document::copyParagraphText(Paragraph const * pParagraph,
1164 ::sal_Int32 nBegin, ::sal_Int32 nEnd)
1166 SolarMutexGuard aGuard;
1168 ::osl::MutexGuard aInternalGuard(GetMutex());
1169 ::sal_uLong nNumber = static_cast< ::sal_uLong >(pParagraph->getNumber());
1170 // XXX numeric overflow
1171 if (nBegin < 0 || nBegin > nEnd
1172 || nEnd > m_rEngine.GetText(nNumber).getLength())
1173 throw css::lang::IndexOutOfBoundsException(
1174 "textwindowaccessibility.cxx:"
1175 " Document::copyParagraphText",
1176 static_cast< css::uno::XWeak * >(this));
1177 m_rView.SetSelection(
1178 ::TextSelection(::TextPaM(nNumber, nBegin),
1179 ::TextPaM(nNumber, nEnd)));
1180 // XXX numeric overflow (2x)
1181 m_rView.Copy();
1185 void Document::changeParagraphAttributes(
1186 Paragraph const * pParagraph, ::sal_Int32 nBegin, ::sal_Int32 nEnd,
1187 css::uno::Sequence< css::beans::PropertyValue > const & rAttributeSet)
1189 SolarMutexGuard aGuard;
1191 ::osl::MutexGuard aInternalGuard(GetMutex());
1192 ::sal_uLong nNumber = static_cast< ::sal_uLong >(pParagraph->getNumber());
1193 // XXX numeric overflow
1194 if (nBegin < 0 || nBegin > nEnd
1195 || nEnd > m_rEngine.GetText(nNumber).getLength())
1196 throw css::lang::IndexOutOfBoundsException(
1197 "textwindowaccessibility.cxx:"
1198 " Document::changeParagraphAttributes",
1199 static_cast< css::uno::XWeak * >(this));
1201 // FIXME The new attributes are added to any attributes already set,
1202 // they do not replace the old attributes as required by
1203 // XAccessibleEditableText.setAttributes:
1204 for (::sal_Int32 i = 0; i < rAttributeSet.getLength(); ++i)
1205 if ( rAttributeSet[i].Name == "CharColor" )
1206 m_rEngine.SetAttrib(::TextAttribFontColor(
1207 mapFontColor(rAttributeSet[i].Value)),
1208 nNumber, nBegin, nEnd);
1209 // XXX numeric overflow (2x)
1210 else if ( rAttributeSet[i].Name == "CharWeight" )
1211 m_rEngine.SetAttrib(::TextAttribFontWeight(
1212 mapFontWeight(rAttributeSet[i].Value)),
1213 nNumber, nBegin, nEnd);
1214 // XXX numeric overflow (2x)
1218 void Document::changeParagraphSelection(Paragraph const * pParagraph,
1219 ::sal_Int32 nBegin, ::sal_Int32 nEnd)
1221 SolarMutexGuard aGuard;
1223 ::osl::MutexGuard aInternalGuard(GetMutex());
1224 ::sal_uLong nNumber = static_cast< ::sal_uLong >(pParagraph->getNumber());
1225 // XXX numeric overflow
1226 if (nBegin < 0 || nBegin > nEnd
1227 || nEnd > m_rEngine.GetText(nNumber).getLength())
1228 throw css::lang::IndexOutOfBoundsException(
1229 "textwindowaccessibility.cxx:"
1230 " Document::changeParagraphSelection",
1231 static_cast< css::uno::XWeak * >(this));
1232 m_rView.SetSelection(
1233 ::TextSelection(::TextPaM(nNumber, nBegin),
1234 ::TextPaM(nNumber, nEnd)));
1235 // XXX numeric overflow (2x)
1239 css::i18n::Boundary
1240 Document::retrieveParagraphLineBoundary( Paragraph const * pParagraph,
1241 ::sal_Int32 nIndex, ::sal_Int32 *pLineNo )
1243 css::i18n::Boundary aBoundary;
1244 aBoundary.startPos = nIndex;
1245 aBoundary.endPos = nIndex;
1247 SolarMutexGuard aGuard;
1249 ::osl::MutexGuard aInternalGuard( GetMutex() );
1250 ::sal_uLong nNumber = static_cast< ::sal_uLong >( pParagraph->getNumber() );
1251 if ( nIndex < 0 || nIndex > m_rEngine.GetText( nNumber ).getLength() )
1252 throw css::lang::IndexOutOfBoundsException(
1253 "textwindowaccessibility.cxx:"
1254 " Document::retrieveParagraphLineBoundary",
1255 static_cast< css::uno::XWeak * >( this ) );
1256 ::sal_Int32 nLineStart = 0;
1257 ::sal_Int32 nLineEnd = 0;
1258 ::sal_uInt16 nLineCount = m_rEngine.GetLineCount( nNumber );
1259 for ( ::sal_uInt16 nLine = 0; nLine < nLineCount; ++nLine )
1261 nLineStart = nLineEnd;
1262 nLineEnd += m_rEngine.GetLineLen( nNumber, nLine );
1263 if ( nIndex >= nLineStart && ( ( nLine == nLineCount - 1 ) ? nIndex <= nLineEnd : nIndex < nLineEnd ) )
1265 aBoundary.startPos = nLineStart;
1266 aBoundary.endPos = nLineEnd;
1267 if( pLineNo )
1268 pLineNo[0] = nLine;
1269 break;
1274 return aBoundary;
1277 css::i18n::Boundary
1278 Document::retrieveParagraphBoundaryOfLine( Paragraph const * pParagraph,
1279 ::sal_Int32 nLineNo )
1281 css::i18n::Boundary aBoundary;
1282 aBoundary.startPos = 0;
1283 aBoundary.endPos = 0;
1285 SolarMutexGuard aGuard;
1287 ::osl::MutexGuard aInternalGuard( GetMutex() );
1288 ::sal_uLong nNumber = static_cast< ::sal_uLong >( pParagraph->getNumber() );
1289 if ( nLineNo >= m_rEngine.GetLineCount( nNumber ) )
1290 throw css::lang::IndexOutOfBoundsException(
1291 "textwindowaccessibility.cxx:"
1292 " Document::retrieveParagraphBoundaryOfLine",
1293 static_cast< css::uno::XWeak * >( this ) );
1294 ::sal_Int32 nLineStart = 0;
1295 ::sal_Int32 nLineEnd = 0;
1296 for ( ::sal_Int32 nLine = 0; nLine <= nLineNo; ++nLine )
1298 nLineStart = nLineEnd;
1299 nLineEnd += m_rEngine.GetLineLen( nNumber, nLine );
1302 aBoundary.startPos = nLineStart;
1303 aBoundary.endPos = nLineEnd;
1306 return aBoundary;
1309 sal_Int32 Document::retrieveParagraphLineWithCursor( Paragraph const * pParagraph )
1311 SolarMutexGuard aGuard;
1312 ::osl::MutexGuard aInternalGuard(GetMutex());
1313 ::TextSelection const & rSelection = m_rView.GetSelection();
1314 Paragraphs::size_type nNumber = pParagraph->getNumber();
1315 TextPaM aEndPaM( rSelection.GetEnd() );
1317 return aEndPaM.GetPara() == nNumber
1318 ? m_rView.GetLineNumberOfCursorInSelection() : -1;
1322 css::uno::Reference< css::accessibility::XAccessibleRelationSet >
1323 Document::retrieveParagraphRelationSet( Paragraph const * pParagraph )
1325 ::osl::MutexGuard aInternalGuard( GetMutex() );
1327 ::utl::AccessibleRelationSetHelper* pRelationSetHelper = new ::utl::AccessibleRelationSetHelper();
1328 css::uno::Reference< css::accessibility::XAccessibleRelationSet > xSet = pRelationSetHelper;
1330 Paragraphs::iterator aPara( m_xParagraphs->begin() + pParagraph->getNumber() );
1332 if ( aPara > m_aVisibleBegin && aPara < m_aVisibleEnd )
1334 css::uno::Sequence< css::uno::Reference< css::uno::XInterface > > aSequence { getAccessibleChild( aPara - 1 ) };
1335 css::accessibility::AccessibleRelation aRelation( css::accessibility::AccessibleRelationType::CONTENT_FLOWS_FROM, aSequence );
1336 pRelationSetHelper->AddRelation( aRelation );
1339 if ( aPara >= m_aVisibleBegin && aPara < m_aVisibleEnd -1 )
1341 css::uno::Sequence< css::uno::Reference< css::uno::XInterface > > aSequence { getAccessibleChild( aPara + 1 ) };
1342 css::accessibility::AccessibleRelation aRelation( css::accessibility::AccessibleRelationType::CONTENT_FLOWS_TO, aSequence );
1343 pRelationSetHelper->AddRelation( aRelation );
1346 return xSet;
1349 // virtual
1350 ::sal_Int32 SAL_CALL Document::getAccessibleChildCount()
1352 ::comphelper::OExternalLockGuard aGuard(this);
1353 init();
1354 return m_aVisibleEnd - m_aVisibleBegin;
1357 // virtual
1358 css::uno::Reference< css::accessibility::XAccessible > SAL_CALL
1359 Document::getAccessibleChild(::sal_Int32 i)
1361 ::comphelper::OExternalLockGuard aGuard(this);
1362 init();
1363 if (i < 0 || i >= m_aVisibleEnd - m_aVisibleBegin)
1364 throw css::lang::IndexOutOfBoundsException(
1365 "textwindowaccessibility.cxx:"
1366 " Document::getAccessibleChild",
1367 static_cast< css::uno::XWeak * >(this));
1368 return getAccessibleChild(m_aVisibleBegin
1369 + static_cast< Paragraphs::size_type >(i));
1372 // virtual
1373 ::sal_Int16 SAL_CALL Document::getAccessibleRole()
1375 return css::accessibility::AccessibleRole::TEXT_FRAME;
1378 // virtual
1379 css::uno::Reference< css::accessibility::XAccessible > SAL_CALL
1380 Document::getAccessibleAtPoint(css::awt::Point const & rPoint)
1382 ::comphelper::OExternalLockGuard aGuard(this);
1383 init();
1384 if (rPoint.X >= 0
1385 && rPoint.X < m_rView.GetWindow()->GetOutputSizePixel().Width()
1386 && rPoint.Y >= 0 && rPoint.Y < m_nViewHeight)
1388 ::sal_Int32 nOffset = m_nViewOffset + rPoint.Y; // XXX numeric overflow
1389 ::sal_Int32 nPos = m_nViewOffset - m_nVisibleBeginOffset;
1390 for (Paragraphs::iterator aIt(m_aVisibleBegin); aIt != m_aVisibleEnd;
1391 ++aIt)
1393 nPos += aIt->getHeight(); // XXX numeric overflow
1394 if (nOffset < nPos)
1395 return getAccessibleChild(aIt);
1398 return nullptr;
1400 void Document::FillAccessibleStateSet( utl::AccessibleStateSetHelper& rStateSet )
1402 VCLXAccessibleComponent::FillAccessibleStateSet( rStateSet );
1403 if (!m_rView.IsReadOnly())
1404 rStateSet.AddState( css::accessibility::AccessibleStateType::EDITABLE );
1407 void Document::FillAccessibleRelationSet( utl::AccessibleRelationSetHelper& rRelationSet )
1409 if( getAccessibleParent()->getAccessibleContext()->getAccessibleRole() == css::accessibility::AccessibleRole::SCROLL_PANE )
1411 css::uno::Sequence< css::uno::Reference< css::uno::XInterface > > aSequence { getAccessibleParent() };
1412 rRelationSet.AddRelation( css::accessibility::AccessibleRelation( css::accessibility::AccessibleRelationType::MEMBER_OF, aSequence ) );
1414 else
1416 VCLXAccessibleComponent::FillAccessibleRelationSet(rRelationSet);
1419 // virtual
1420 void SAL_CALL Document::disposing()
1422 m_aEngineListener.endListening();
1423 m_aViewListener.endListening();
1424 if (m_xParagraphs != nullptr)
1425 disposeParagraphs();
1426 VCLXAccessibleComponent::disposing();
1429 // virtual
1430 void Document::Notify(::SfxBroadcaster &, ::SfxHint const & rHint)
1432 const TextHint* pTextHint = dynamic_cast<const TextHint*>(&rHint);
1433 if (pTextHint)
1435 ::TextHint const & rTextHint = *pTextHint;
1436 switch (rTextHint.GetId())
1438 case SfxHintId::TextParaInserted:
1439 case SfxHintId::TextParaRemoved:
1440 // SfxHintId::TextParaInserted and SfxHintId::TextParaRemoved are sent at
1441 // "unsafe" times (when the text engine has not yet re-formatted its
1442 // content), so that for example calling ::TextEngine::GetTextHeight
1443 // from within the code that handles SfxHintId::TextParaInserted causes
1444 // trouble within the text engine. Therefore, these hints are just
1445 // buffered until a following ::TextEngine::FormatDoc causes a
1446 // SfxHintId::TextFormatted to come in:
1447 case SfxHintId::TextFormatPara:
1448 // ::TextEngine::FormatDoc sends a sequence of
1449 // SfxHintId::TextFormatParas, followed by an optional
1450 // SfxHintId::TextHeightChanged, followed in all cases by one
1451 // SfxHintId::TextFormatted. Only the SfxHintId::TextFormatParas contain
1452 // the numbers of the affected paragraphs, but they are sent
1453 // before the changes are applied. Therefore, SfxHintId::TextFormatParas
1454 // are just buffered until another hint comes in:
1456 ::osl::MutexGuard aInternalGuard(GetMutex());
1457 if (!isAlive())
1458 break;
1460 m_aParagraphNotifications.push(rTextHint);
1461 break;
1463 case SfxHintId::TextFormatted:
1464 case SfxHintId::TextHeightChanged:
1465 case SfxHintId::TextModified:
1467 ::osl::MutexGuard aInternalGuard(GetMutex());
1468 if (!isAlive())
1469 break;
1470 handleParagraphNotifications();
1471 break;
1473 case SfxHintId::TextViewScrolled:
1475 ::osl::MutexGuard aInternalGuard(GetMutex());
1476 if (!isAlive())
1477 break;
1478 handleParagraphNotifications();
1480 ::sal_Int32 nOffset = static_cast< ::sal_Int32 >(
1481 m_rView.GetStartDocPos().Y());
1482 // XXX numeric overflow
1483 if (nOffset != m_nViewOffset)
1485 m_nViewOffset = nOffset;
1487 Paragraphs::iterator aOldVisibleBegin(
1488 m_aVisibleBegin);
1489 Paragraphs::iterator aOldVisibleEnd(m_aVisibleEnd);
1491 determineVisibleRange();
1493 notifyVisibleRangeChanges(aOldVisibleBegin,
1494 aOldVisibleEnd,
1495 m_xParagraphs->end());
1497 break;
1499 case SfxHintId::TextViewSelectionChanged:
1500 case SfxHintId::TextViewCaretChanged:
1502 ::osl::MutexGuard aInternalGuard(GetMutex());
1503 if (!isAlive())
1504 break;
1506 if (m_aParagraphNotifications.empty())
1508 handleSelectionChangeNotification();
1510 else
1512 // SfxHintId::TextViewSelectionChanged is sometimes sent at
1513 // "unsafe" times (when the text engine has not yet re-
1514 // formatted its content), so that for example calling
1515 // ::TextEngine::GetTextHeight from within the code that
1516 // handles a previous SfxHintId::TextParaInserted causes
1517 // trouble within the text engine. Therefore, these
1518 // hints are just buffered (along with
1519 // SfxHintId::TextParaInserted/REMOVED/FORMATPARA) until a
1520 // following ::TextEngine::FormatDoc causes a
1521 // SfxHintId::TextFormatted to come in:
1522 m_bSelectionChangedNotification = true;
1524 break;
1526 default: break;
1531 IMPL_LINK(Document, WindowEventHandler, ::VclWindowEvent&, rEvent, void)
1533 switch (rEvent.GetId())
1535 case VclEventId::WindowResize:
1537 ::osl::MutexGuard aInternalGuard(GetMutex());
1538 if (!isAlive())
1539 break;
1541 ::sal_Int32 nHeight = static_cast< ::sal_Int32 >(
1542 m_rView.GetWindow()->GetOutputSizePixel().Height());
1543 // XXX numeric overflow
1544 if (nHeight != m_nViewHeight)
1546 m_nViewHeight = nHeight;
1548 Paragraphs::iterator aOldVisibleBegin(m_aVisibleBegin);
1549 Paragraphs::iterator aOldVisibleEnd(m_aVisibleEnd);
1551 determineVisibleRange();
1553 notifyVisibleRangeChanges(aOldVisibleBegin, aOldVisibleEnd,
1554 m_xParagraphs->end());
1556 break;
1558 case VclEventId::WindowGetFocus:
1560 ::osl::MutexGuard aInternalGuard(GetMutex());
1561 if (!isAlive())
1562 break;
1563 //to enable the PARAGRAPH to get focus for multiline edit
1564 ::sal_Int32 count = getAccessibleChildCount();
1565 bool bEmpty = m_aFocused == m_aVisibleEnd && count == 1;
1566 if ((m_aFocused >= m_aVisibleBegin && m_aFocused < m_aVisibleEnd) || bEmpty)
1568 Paragraphs::iterator aTemp = bEmpty ? m_aVisibleBegin : m_aFocused;
1569 ::rtl::Reference< Paragraph > xParagraph(getParagraph(aTemp));
1570 if (xParagraph.is())
1572 xParagraph->notifyEvent(
1573 css::accessibility::AccessibleEventId::
1574 STATE_CHANGED,
1575 css::uno::Any(),
1576 css::uno::Any(
1577 css::accessibility::AccessibleStateType::
1578 FOCUSED));
1581 break;
1583 case VclEventId::WindowLoseFocus:
1585 ::osl::MutexGuard aInternalGuard(GetMutex());
1586 if (!isAlive())
1587 break;
1588 //to enable the PARAGRAPH to get focus for multiline edit
1589 ::sal_Int32 count = getAccessibleChildCount();
1590 bool bEmpty = m_aFocused == m_aVisibleEnd && count == 1;
1591 if ((m_aFocused >= m_aVisibleBegin && m_aFocused < m_aVisibleEnd) || bEmpty)
1593 Paragraphs::iterator aTemp = bEmpty ? m_aVisibleBegin : m_aFocused;
1594 ::rtl::Reference< Paragraph > xParagraph(getParagraph(aTemp));
1595 if (xParagraph.is())
1596 xParagraph->notifyEvent(
1597 css::accessibility::AccessibleEventId::
1598 STATE_CHANGED,
1599 css::uno::Any(
1600 css::accessibility::AccessibleStateType::
1601 FOCUSED),
1602 css::uno::Any());
1604 break;
1606 default: break;
1610 void Document::init()
1612 if (m_xParagraphs == nullptr)
1614 const sal_uInt32 nCount = m_rEngine.GetParagraphCount();
1615 m_xParagraphs.reset(new Paragraphs);
1616 m_xParagraphs->reserve(static_cast< Paragraphs::size_type >(nCount));
1617 // numeric overflow is harmless here
1618 for (sal_uInt32 i = 0; i < nCount; ++i)
1619 m_xParagraphs->push_back(ParagraphInfo(static_cast< ::sal_Int32 >(
1620 m_rEngine.GetTextHeight(i))));
1621 // XXX numeric overflow
1622 m_nViewOffset = static_cast< ::sal_Int32 >(
1623 m_rView.GetStartDocPos().Y()); // XXX numeric overflow
1624 m_nViewHeight = static_cast< ::sal_Int32 >(
1625 m_rView.GetWindow()->GetOutputSizePixel().Height());
1626 // XXX numeric overflow
1627 determineVisibleRange();
1628 m_nSelectionFirstPara = -1;
1629 m_nSelectionFirstPos = -1;
1630 m_nSelectionLastPara = -1;
1631 m_nSelectionLastPos = -1;
1632 m_aFocused = m_xParagraphs->end();
1633 m_bSelectionChangedNotification = false;
1634 m_aEngineListener.startListening(m_rEngine);
1635 m_aViewListener.startListening(*m_rView.GetWindow());
1639 ::rtl::Reference< Paragraph >
1640 Document::getParagraph(Paragraphs::iterator const & rIt)
1642 return static_cast< Paragraph * >(
1643 css::uno::Reference< css::accessibility::XAccessible >(
1644 rIt->getParagraph()).get());
1647 css::uno::Reference< css::accessibility::XAccessible >
1648 Document::getAccessibleChild(Paragraphs::iterator const & rIt)
1650 css::uno::Reference< css::accessibility::XAccessible > xParagraph(
1651 rIt->getParagraph());
1652 if (!xParagraph.is())
1654 xParagraph = new Paragraph(this, rIt - m_xParagraphs->begin());
1655 rIt->setParagraph(xParagraph);
1657 return xParagraph;
1660 void Document::determineVisibleRange()
1662 Paragraphs::iterator const aEnd = m_xParagraphs->end();
1664 m_aVisibleBegin = aEnd;
1665 m_aVisibleEnd = aEnd;
1666 m_nVisibleBeginOffset = 0;
1668 ::sal_Int32 nPos = 0;
1669 for (Paragraphs::iterator aIt = m_xParagraphs->begin(); m_aVisibleEnd == aEnd && aIt != aEnd; ++aIt)
1671 ::sal_Int32 const nOldPos = nPos;
1672 nPos += aIt->getHeight(); // XXX numeric overflow
1673 if (m_aVisibleBegin == aEnd)
1675 if (nPos >= m_nViewOffset)
1677 m_aVisibleBegin = aIt;
1678 m_nVisibleBeginOffset = m_nViewOffset - nOldPos;
1681 else
1683 if (nPos >= m_nViewOffset + m_nViewHeight) // XXX numeric overflow
1685 m_aVisibleEnd = aIt;
1690 SAL_WARN_IF(
1691 !((m_aVisibleBegin == m_xParagraphs->end() && m_aVisibleEnd == m_xParagraphs->end() && m_nVisibleBeginOffset == 0)
1692 || (m_aVisibleBegin < m_aVisibleEnd && m_nVisibleBeginOffset >= 0)),
1693 "accessibility",
1694 "invalid visible range");
1697 void Document::notifyVisibleRangeChanges(
1698 Paragraphs::iterator const & rOldVisibleBegin,
1699 Paragraphs::iterator const & rOldVisibleEnd,
1700 Paragraphs::iterator const & rInserted)
1702 // XXX Replace this code that determines which paragraphs have changed from
1703 // invisible to visible or vice versa with a better algorithm.
1704 for (Paragraphs::iterator aIt(rOldVisibleBegin); aIt != rOldVisibleEnd;
1705 ++aIt)
1707 if (aIt != rInserted
1708 && (aIt < m_aVisibleBegin || aIt >= m_aVisibleEnd))
1709 NotifyAccessibleEvent(
1710 css::accessibility::AccessibleEventId::
1711 CHILD,
1712 css::uno::Any(getAccessibleChild(aIt)),
1713 css::uno::Any());
1715 for (Paragraphs::iterator aIt(m_aVisibleBegin); aIt != m_aVisibleEnd;
1716 ++aIt)
1718 if (aIt == rInserted
1719 || aIt < rOldVisibleBegin || aIt >= rOldVisibleEnd)
1720 NotifyAccessibleEvent(
1721 css::accessibility::AccessibleEventId::
1722 CHILD,
1723 css::uno::Any(),
1724 css::uno::Any(getAccessibleChild(aIt)));
1728 void
1729 Document::changeParagraphText(::sal_uLong nNumber, ::sal_uInt16 nBegin, ::sal_uInt16 nEnd,
1730 bool bCut, bool bPaste,
1731 OUString const & rText)
1733 m_rView.SetSelection(::TextSelection(::TextPaM(nNumber, nBegin),
1734 ::TextPaM(nNumber, nEnd)));
1735 if (bCut)
1736 m_rView.Cut();
1737 else if (nBegin != nEnd)
1738 m_rView.DeleteSelected();
1739 if (bPaste)
1740 m_rView.Paste();
1741 else if (!rText.isEmpty())
1742 m_rView.InsertText(rText);
1745 void Document::handleParagraphNotifications()
1747 while (!m_aParagraphNotifications.empty())
1749 ::TextHint aHint(m_aParagraphNotifications.front());
1750 m_aParagraphNotifications.pop();
1751 switch (aHint.GetId())
1753 case SfxHintId::TextParaInserted:
1755 ::sal_uLong n = aHint.GetValue();
1756 assert(n <= m_xParagraphs->size() && "bad SfxHintId::TextParaInserted event");
1758 // Save the values of old iterators (the iterators themselves
1759 // will get invalidated), and adjust the old values so that they
1760 // reflect the insertion of the new paragraph:
1761 Paragraphs::size_type nOldVisibleBegin
1762 = m_aVisibleBegin - m_xParagraphs->begin();
1763 Paragraphs::size_type nOldVisibleEnd
1764 = m_aVisibleEnd - m_xParagraphs->begin();
1765 Paragraphs::size_type nOldFocused
1766 = m_aFocused - m_xParagraphs->begin();
1767 if (n <= nOldVisibleBegin)
1768 ++nOldVisibleBegin; // XXX numeric overflow
1769 if (n <= nOldVisibleEnd)
1770 ++nOldVisibleEnd; // XXX numeric overflow
1771 if (n <= nOldFocused)
1772 ++nOldFocused; // XXX numeric overflow
1773 if (sal::static_int_cast<sal_Int32>(n) <= m_nSelectionFirstPara)
1774 ++m_nSelectionFirstPara; // XXX numeric overflow
1775 if (sal::static_int_cast<sal_Int32>(n) <= m_nSelectionLastPara)
1776 ++m_nSelectionLastPara; // XXX numeric overflow
1778 Paragraphs::iterator aIns(
1779 m_xParagraphs->insert(
1780 m_xParagraphs->begin() + n,
1781 ParagraphInfo(static_cast< ::sal_Int32 >(
1782 m_rEngine.GetTextHeight(n)))));
1783 // XXX numeric overflow (2x)
1785 determineVisibleRange();
1786 m_aFocused = m_xParagraphs->begin() + nOldFocused;
1788 for (Paragraphs::iterator aIt(aIns);;)
1790 ++aIt;
1791 if (aIt == m_xParagraphs->end())
1792 break;
1793 ::rtl::Reference< Paragraph > xParagraph(
1794 getParagraph(aIt));
1795 if (xParagraph.is())
1796 xParagraph->numberChanged(true);
1799 notifyVisibleRangeChanges(
1800 m_xParagraphs->begin() + nOldVisibleBegin,
1801 m_xParagraphs->begin() + nOldVisibleEnd, aIns);
1802 break;
1804 case SfxHintId::TextParaRemoved:
1806 ::sal_uLong n = aHint.GetValue();
1807 if (n == TEXT_PARA_ALL)
1809 for (Paragraphs::iterator aIt(m_aVisibleBegin);
1810 aIt != m_aVisibleEnd; ++aIt)
1812 NotifyAccessibleEvent(
1813 css::accessibility::AccessibleEventId::
1814 CHILD,
1815 css::uno::Any(getAccessibleChild(aIt)),
1816 css::uno::Any());
1818 disposeParagraphs();
1819 m_xParagraphs->clear();
1820 determineVisibleRange();
1821 m_nSelectionFirstPara = -1;
1822 m_nSelectionFirstPos = -1;
1823 m_nSelectionLastPara = -1;
1824 m_nSelectionLastPos = -1;
1825 m_aFocused = m_xParagraphs->end();
1827 else
1829 assert(n < m_xParagraphs->size() && "Bad SfxHintId::TextParaRemoved event");
1831 Paragraphs::iterator aIt(m_xParagraphs->begin() + n);
1832 // numeric overflow cannot occur
1834 // Save the values of old iterators (the iterators
1835 // themselves will get invalidated), and adjust the old
1836 // values so that they reflect the removal of the paragraph:
1837 Paragraphs::size_type nOldVisibleBegin
1838 = m_aVisibleBegin - m_xParagraphs->begin();
1839 Paragraphs::size_type nOldVisibleEnd
1840 = m_aVisibleEnd - m_xParagraphs->begin();
1841 bool bWasVisible
1842 = nOldVisibleBegin <= n && n < nOldVisibleEnd;
1843 Paragraphs::size_type nOldFocused
1844 = m_aFocused - m_xParagraphs->begin();
1845 bool bWasFocused = aIt == m_aFocused;
1846 if (n < nOldVisibleBegin)
1847 --nOldVisibleBegin;
1848 if (n < nOldVisibleEnd)
1849 --nOldVisibleEnd;
1850 if (n < nOldFocused)
1851 --nOldFocused;
1852 if (sal::static_int_cast<sal_Int32>(n) < m_nSelectionFirstPara)
1853 --m_nSelectionFirstPara;
1854 else if (sal::static_int_cast<sal_Int32>(n) == m_nSelectionFirstPara)
1856 if (m_nSelectionFirstPara == m_nSelectionLastPara)
1858 m_nSelectionFirstPara = -1;
1859 m_nSelectionFirstPos = -1;
1860 m_nSelectionLastPara = -1;
1861 m_nSelectionLastPos = -1;
1863 else
1865 ++m_nSelectionFirstPara;
1866 m_nSelectionFirstPos = 0;
1869 if (sal::static_int_cast<sal_Int32>(n) < m_nSelectionLastPara)
1870 --m_nSelectionLastPara;
1871 else if (sal::static_int_cast<sal_Int32>(n) == m_nSelectionLastPara)
1873 assert(m_nSelectionFirstPara < m_nSelectionLastPara && "logic error");
1874 --m_nSelectionLastPara;
1875 m_nSelectionLastPos = 0x7FFFFFFF;
1878 css::uno::Reference< css::accessibility::XAccessible >
1879 xStrong;
1880 if (bWasVisible)
1881 xStrong = getAccessibleChild(aIt);
1882 css::uno::WeakReference<
1883 css::accessibility::XAccessible > xWeak(
1884 aIt->getParagraph());
1885 aIt = m_xParagraphs->erase(aIt);
1887 determineVisibleRange();
1888 m_aFocused = bWasFocused ? m_xParagraphs->end()
1889 : m_xParagraphs->begin() + nOldFocused;
1891 for (; aIt != m_xParagraphs->end(); ++aIt)
1893 ::rtl::Reference< Paragraph > xParagraph(
1894 getParagraph(aIt));
1895 if (xParagraph.is())
1896 xParagraph->numberChanged(false);
1899 if (bWasVisible)
1900 NotifyAccessibleEvent(
1901 css::accessibility::AccessibleEventId::
1902 CHILD,
1903 css::uno::Any(xStrong),
1904 css::uno::Any());
1906 css::uno::Reference< css::lang::XComponent > xComponent(
1907 xWeak.get(), css::uno::UNO_QUERY);
1908 if (xComponent.is())
1909 xComponent->dispose();
1911 notifyVisibleRangeChanges(
1912 m_xParagraphs->begin() + nOldVisibleBegin,
1913 m_xParagraphs->begin() + nOldVisibleEnd,
1914 m_xParagraphs->end());
1916 break;
1918 case SfxHintId::TextFormatPara:
1920 ::sal_uLong n = aHint.GetValue();
1921 assert(n < m_xParagraphs->size() && "Bad SfxHintId::TextFormatPara event");
1923 (*m_xParagraphs)[static_cast< Paragraphs::size_type >(n)].
1924 changeHeight(static_cast< ::sal_Int32 >(
1925 m_rEngine.GetTextHeight(n)));
1926 // XXX numeric overflow
1927 Paragraphs::iterator aOldVisibleBegin(m_aVisibleBegin);
1928 Paragraphs::iterator aOldVisibleEnd(m_aVisibleEnd);
1929 determineVisibleRange();
1930 notifyVisibleRangeChanges(aOldVisibleBegin, aOldVisibleEnd,
1931 m_xParagraphs->end());
1933 if (n < m_xParagraphs->size())
1935 Paragraphs::iterator aIt(m_xParagraphs->begin() + n);
1936 ::rtl::Reference< Paragraph > xParagraph(getParagraph(aIt));
1937 if (xParagraph.is())
1938 xParagraph->textChanged();
1940 break;
1942 default:
1943 SAL_WARN("accessibility", "bad buffered hint");
1944 break;
1947 if (m_bSelectionChangedNotification)
1949 m_bSelectionChangedNotification = false;
1950 handleSelectionChangeNotification();
1954 namespace
1957 enum class SelChangeType
1959 None, // no change, or invalid
1960 CaretMove, // neither old nor new have selection, and they are different
1961 NoSelToSel, // old has no selection but new has selection
1962 SelToNoSel, // old has selection but new has no selection
1963 // both old and new have selections
1964 NoParaChange, // only end index changed inside end para
1965 EndParaNoMoreBehind, // end para was behind start, but now is same or ahead
1966 AddedFollowingPara, // selection extended to following paragraph(s)
1967 ExcludedPreviousPara, // selection shrunk excluding previous paragraph(s)
1968 ExcludedFollowingPara, // selection shrunk excluding following paragraph(s)
1969 AddedPreviousPara, // selection extended to previous paragraph(s)
1970 EndParaBecameBehind // end para was ahead of start, but now is behind
1973 SelChangeType getSelChangeType(const TextPaM& Os, const TextPaM& Oe,
1974 const TextPaM& Ns, const TextPaM& Ne)
1976 if (Os == Oe) // no old selection
1978 if (Ns == Ne) // no new selection: only caret moves
1979 return Os != Ns ? SelChangeType::CaretMove : SelChangeType::None;
1980 else // old has no selection but new has selection
1981 return SelChangeType::NoSelToSel;
1983 else if (Ns == Ne) // old has selection; no new selection
1985 return SelChangeType::SelToNoSel;
1987 else if (Os == Ns) // both old and new have selections, and their starts are same
1989 const sal_Int32 Osp = Os.GetPara(), Oep = Oe.GetPara();
1990 const sal_Int32 Nsp = Ns.GetPara(), Nep = Ne.GetPara();
1991 if (Oep == Nep) // end of selection stays in the same paragraph
1993 //Send text_selection_change event on Nep
1994 return Oe.GetIndex() != Ne.GetIndex() ? SelChangeType::NoParaChange
1995 : SelChangeType::None;
1997 else if (Oep < Nep) // end of selection moved to a following paragraph
1999 //all the following examples like 1,2->1,3 means that old start select para is 1, old end select para is 2,
2000 // then press shift up, the new start select para is 1, new end select para is 3;
2001 //for example, 1, 2 -> 1, 3; 4,1 -> 4, 7; 4,1 -> 4, 2; 4,4->4,5
2002 if (Nep >= Nsp) // new end para not behind start
2004 // 1, 2 -> 1, 3; 4, 1 -> 4, 7; 4,4->4,5;
2005 if (Oep < Osp) // old end was behind start
2007 // 4,1 -> 4,7; 4,1 -> 4,4
2008 return SelChangeType::EndParaNoMoreBehind;
2010 else // old end para wasn't behind start
2012 // 1, 2 -> 1, 3; 4,4->4,5;
2013 return SelChangeType::AddedFollowingPara;
2016 else // new end para is still behind start
2018 // 4,1 -> 4,2,
2019 return SelChangeType::ExcludedPreviousPara;
2022 else // Oep > Nep => end of selection moved to a previous paragraph
2024 // 3,2 -> 3,1; 4,7 -> 4,1; 4, 7 -> 4,6; 4,4 -> 4,3
2025 if (Nep >= Nsp) // new end para is still not behind of start
2027 // 4,7 ->4,6
2028 return SelChangeType::ExcludedFollowingPara;
2030 else // new end para is behind start
2032 // 3,2 -> 3,1, 4,7 -> 4,1; 4,4->4,3
2033 if (Oep <= Osp) // it was not ahead already
2035 // 3,2 -> 3,1; 4,4->4,3
2036 return SelChangeType::AddedPreviousPara;
2038 else // it was ahead previously
2040 // 4,7 -> 4,1
2041 return SelChangeType::EndParaBecameBehind;
2046 return SelChangeType::None;
2049 } // namespace
2051 void Document::sendEvent(::sal_Int32 start, ::sal_Int32 end, ::sal_Int16 nEventId)
2053 size_t nAvailDistance = std::distance(m_xParagraphs->begin(), m_aVisibleEnd);
2055 Paragraphs::iterator aEnd(m_xParagraphs->begin());
2056 size_t nEndDistance = std::min<size_t>(end + 1, nAvailDistance);
2057 std::advance(aEnd, nEndDistance);
2059 Paragraphs::iterator aIt(m_xParagraphs->begin());
2060 size_t nStartDistance = std::min<size_t>(start, nAvailDistance);
2061 std::advance(aIt, nStartDistance);
2063 while (aIt < aEnd)
2065 ::rtl::Reference< Paragraph > xParagraph(getParagraph(aIt));
2066 if (xParagraph.is())
2067 xParagraph->notifyEvent(
2068 nEventId,
2069 css::uno::Any(), css::uno::Any());
2070 ++aIt;
2074 void Document::handleSelectionChangeNotification()
2076 ::TextSelection const & rSelection = m_rView.GetSelection();
2077 assert(rSelection.GetStart().GetPara() < m_xParagraphs->size() &&
2078 rSelection.GetEnd().GetPara() < m_xParagraphs->size() &&
2079 "bad SfxHintId::TextViewSelectionChanged event");
2080 ::sal_Int32 nNewFirstPara
2081 = static_cast< ::sal_Int32 >(rSelection.GetStart().GetPara());
2082 ::sal_Int32 nNewFirstPos = rSelection.GetStart().GetIndex();
2083 // XXX numeric overflow
2084 ::sal_Int32 nNewLastPara
2085 = static_cast< ::sal_Int32 >(rSelection.GetEnd().GetPara());
2086 ::sal_Int32 nNewLastPos = rSelection.GetEnd().GetIndex();
2087 // XXX numeric overflow
2089 // Lose focus:
2090 Paragraphs::iterator aIt(m_xParagraphs->begin() + nNewLastPara);
2091 if (m_aFocused != m_xParagraphs->end() && m_aFocused != aIt
2092 && m_aFocused >= m_aVisibleBegin && m_aFocused < m_aVisibleEnd)
2094 ::rtl::Reference< Paragraph > xParagraph(getParagraph(m_aFocused));
2095 if (xParagraph.is())
2096 xParagraph->notifyEvent(
2097 css::accessibility::AccessibleEventId::
2098 STATE_CHANGED,
2099 css::uno::Any(
2100 css::accessibility::AccessibleStateType::FOCUSED),
2101 css::uno::Any());
2104 // Gain focus and update cursor position:
2105 if (aIt >= m_aVisibleBegin && aIt < m_aVisibleEnd
2106 && (aIt != m_aFocused
2107 || nNewLastPara != m_nSelectionLastPara
2108 || nNewLastPos != m_nSelectionLastPos))
2110 ::rtl::Reference< Paragraph > xParagraph(getParagraph(aIt));
2111 if (xParagraph.is())
2113 //disable the first event when user types in empty field.
2114 ::sal_Int32 count = getAccessibleChildCount();
2115 bool bEmpty = count > 1;
2116 //if (aIt != m_aFocused)
2117 if (aIt != m_aFocused && bEmpty)
2118 xParagraph->notifyEvent(
2119 css::accessibility::AccessibleEventId::
2120 STATE_CHANGED,
2121 css::uno::Any(),
2122 css::uno::Any(
2123 css::accessibility::AccessibleStateType::FOCUSED));
2124 if (nNewLastPara != m_nSelectionLastPara
2125 || nNewLastPos != m_nSelectionLastPos)
2126 xParagraph->notifyEvent(
2127 css::accessibility::AccessibleEventId::
2128 CARET_CHANGED,
2129 css::uno::makeAny< ::sal_Int32 >(
2130 nNewLastPara == m_nSelectionLastPara
2131 ? m_nSelectionLastPos : 0),
2132 css::uno::Any(nNewLastPos));
2135 m_aFocused = aIt;
2137 if (m_nSelectionFirstPara != -1)
2139 sal_Int32 nMin;
2140 sal_Int32 nMax;
2141 SelChangeType ret = getSelChangeType(TextPaM(m_nSelectionFirstPara, m_nSelectionFirstPos),
2142 TextPaM(m_nSelectionLastPara, m_nSelectionLastPos),
2143 rSelection.GetStart(), rSelection.GetEnd());
2144 switch (ret)
2146 case SelChangeType::None:
2147 //no event
2148 break;
2149 case SelChangeType::CaretMove:
2150 //only caret moved, already handled in above
2151 break;
2152 case SelChangeType::NoSelToSel:
2153 //old has no selection but new has selection
2154 nMin = std::min(nNewFirstPara, nNewLastPara);
2155 nMax = std::max(nNewFirstPara, nNewLastPara);
2156 sendEvent(nMin, nMax, css::accessibility::AccessibleEventId::SELECTION_CHANGED);
2157 sendEvent(nMin, nMax,
2158 css::accessibility::AccessibleEventId::TEXT_SELECTION_CHANGED);
2159 break;
2160 case SelChangeType::SelToNoSel:
2161 //old has selection but new has no selection.
2162 nMin = std::min(m_nSelectionFirstPara, m_nSelectionLastPara);
2163 nMax = std::max(m_nSelectionFirstPara, m_nSelectionLastPara);
2164 sendEvent(nMin, nMax, css::accessibility::AccessibleEventId::SELECTION_CHANGED);
2165 sendEvent(nMin, nMax,
2166 css::accessibility::AccessibleEventId::TEXT_SELECTION_CHANGED);
2167 break;
2168 case SelChangeType::NoParaChange:
2169 //Send text_selection_change event on Nep
2170 sendEvent(nNewLastPara, nNewLastPara,
2171 css::accessibility::AccessibleEventId::TEXT_SELECTION_CHANGED);
2172 break;
2173 case SelChangeType::EndParaNoMoreBehind:
2174 // 4, 1 -> 4, 7; 4,1 -> 4,4
2175 sendEvent(m_nSelectionLastPara, m_nSelectionFirstPara - 1,
2176 css::accessibility::AccessibleEventId::SELECTION_CHANGED);
2177 sendEvent(nNewFirstPara + 1, nNewLastPara,
2178 css::accessibility::AccessibleEventId::SELECTION_CHANGED);
2180 sendEvent(m_nSelectionLastPara, nNewLastPara,
2181 css::accessibility::AccessibleEventId::TEXT_SELECTION_CHANGED);
2182 break;
2183 case SelChangeType::AddedFollowingPara:
2184 // 1, 2 -> 1, 4; 4,4->4,5;
2185 sendEvent(m_nSelectionLastPara + 1, nNewLastPara,
2186 css::accessibility::AccessibleEventId::SELECTION_CHANGED);
2188 sendEvent(m_nSelectionLastPara, nNewLastPara,
2189 css::accessibility::AccessibleEventId::TEXT_SELECTION_CHANGED);
2190 break;
2191 case SelChangeType::ExcludedPreviousPara:
2192 // 4,1 -> 4,3,
2193 sendEvent(m_nSelectionLastPara + 1, nNewLastPara,
2194 css::accessibility::AccessibleEventId::SELECTION_CHANGED);
2196 sendEvent(m_nSelectionLastPara, nNewLastPara,
2197 css::accessibility::AccessibleEventId::TEXT_SELECTION_CHANGED);
2198 break;
2199 case SelChangeType::ExcludedFollowingPara:
2200 // 4,7 ->4,5;
2201 sendEvent(nNewLastPara + 1, m_nSelectionLastPara,
2202 css::accessibility::AccessibleEventId::SELECTION_CHANGED);
2204 sendEvent(nNewLastPara, m_nSelectionLastPara,
2205 css::accessibility::AccessibleEventId::TEXT_SELECTION_CHANGED);
2206 break;
2207 case SelChangeType::AddedPreviousPara:
2208 // 3,2 -> 3,1; 4,4->4,3
2209 sendEvent(nNewLastPara, m_nSelectionLastPara - 1,
2210 css::accessibility::AccessibleEventId::SELECTION_CHANGED);
2212 sendEvent(nNewLastPara, m_nSelectionLastPara,
2213 css::accessibility::AccessibleEventId::TEXT_SELECTION_CHANGED);
2214 break;
2215 case SelChangeType::EndParaBecameBehind:
2216 // 4,7 -> 4,1
2217 sendEvent(m_nSelectionFirstPara + 1, m_nSelectionLastPara,
2218 css::accessibility::AccessibleEventId::SELECTION_CHANGED);
2219 sendEvent(nNewLastPara, nNewFirstPara - 1,
2220 css::accessibility::AccessibleEventId::SELECTION_CHANGED);
2222 sendEvent(nNewLastPara, m_nSelectionLastPara,
2223 css::accessibility::AccessibleEventId::TEXT_SELECTION_CHANGED);
2224 break;
2228 m_nSelectionFirstPara = nNewFirstPara;
2229 m_nSelectionFirstPos = nNewFirstPos;
2230 m_nSelectionLastPara = nNewLastPara;
2231 m_nSelectionLastPos = nNewLastPos;
2234 void Document::disposeParagraphs()
2236 for (auto const& paragraph : *m_xParagraphs)
2238 css::uno::Reference< css::lang::XComponent > xComponent(
2239 paragraph.getParagraph().get(), css::uno::UNO_QUERY);
2240 if (xComponent.is())
2241 xComponent->dispose();
2245 // static
2246 css::uno::Any Document::mapFontColor(::Color const & rColor)
2248 return css::uno::makeAny(rColor.GetRGBColor());
2249 // FIXME keep transparency?
2252 // static
2253 ::Color Document::mapFontColor(css::uno::Any const & rColor)
2255 ::Color nColor;
2256 rColor >>= nColor;
2257 return nColor;
2260 // static
2261 css::uno::Any Document::mapFontWeight(::FontWeight nWeight)
2263 // Map from ::FontWeight to css::awt::FontWeight, depends on order of
2264 // elements in ::FontWeight (vcl/vclenum.hxx):
2265 static float const aWeight[]
2266 = { css::awt::FontWeight::DONTKNOW, // WEIGHT_DONTKNOW
2267 css::awt::FontWeight::THIN, // WEIGHT_THIN
2268 css::awt::FontWeight::ULTRALIGHT, // WEIGHT_ULTRALIGHT
2269 css::awt::FontWeight::LIGHT, // WEIGHT_LIGHT
2270 css::awt::FontWeight::SEMILIGHT, // WEIGHT_SEMILIGHT
2271 css::awt::FontWeight::NORMAL, // WEIGHT_NORMAL
2272 css::awt::FontWeight::NORMAL, // WEIGHT_MEDIUM
2273 css::awt::FontWeight::SEMIBOLD, // WEIGHT_SEMIBOLD
2274 css::awt::FontWeight::BOLD, // WEIGHT_BOLD
2275 css::awt::FontWeight::ULTRABOLD, // WEIGHT_ULTRABOLD
2276 css::awt::FontWeight::BLACK }; // WEIGHT_BLACK
2277 return css::uno::Any(aWeight[nWeight]);
2280 // static
2281 ::FontWeight Document::mapFontWeight(css::uno::Any const & rWeight)
2283 float nWeight = css::awt::FontWeight::NORMAL;
2284 rWeight >>= nWeight;
2285 return nWeight <= css::awt::FontWeight::DONTKNOW ? WEIGHT_DONTKNOW
2286 : nWeight <= css::awt::FontWeight::THIN ? WEIGHT_THIN
2287 : nWeight <= css::awt::FontWeight::ULTRALIGHT ? WEIGHT_ULTRALIGHT
2288 : nWeight <= css::awt::FontWeight::LIGHT ? WEIGHT_LIGHT
2289 : nWeight <= css::awt::FontWeight::SEMILIGHT ? WEIGHT_SEMILIGHT
2290 : nWeight <= css::awt::FontWeight::NORMAL ? WEIGHT_NORMAL
2291 : nWeight <= css::awt::FontWeight::SEMIBOLD ? WEIGHT_SEMIBOLD
2292 : nWeight <= css::awt::FontWeight::BOLD ? WEIGHT_BOLD
2293 : nWeight <= css::awt::FontWeight::ULTRABOLD ? WEIGHT_ULTRABOLD
2294 : WEIGHT_BLACK;
2299 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */