diff options
author | Jens-Heiner Rechtien <hr@openoffice.org> | 2007-06-27 14:34:57 +0000 |
---|---|---|
committer | Jens-Heiner Rechtien <hr@openoffice.org> | 2007-06-27 14:34:57 +0000 |
commit | dac3b388798e78803a5df77894bfd30cddeb77c3 (patch) | |
tree | 709b607632424a1cd6635efe3196358eb7b03ac5 | |
parent | 92f08522ccd89ff43186061fc4aad89f397ce4be (diff) |
INTEGRATION: CWS a11ysep (1.1.2); FILE ADDED
2007/06/27 14:03:19 fs 1.1.2.6: manual resync: merge the changes SRC680m211->SRC680m217 (which happened in the old incarnation of this file in module svtools) herein
2007/02/28 07:26:19 fs 1.1.2.5: #i10000#
2006/10/05 19:33:55 fs 1.1.2.4: #i10000# post-resync
2006/10/04 19:58:03 fs 1.1.2.3: MANUAL RESYNC m130->m185:
file was changed in MWS, but has been moved herein in CWS
2005/09/28 11:36:47 fs 1.1.2.2: manual resync (files have been moved herein from another location): licence change
2005/03/07 08:29:23 fs 1.1.2.1: #i44293# moved implementations herein from svtools module
-rw-r--r-- | accessibility/source/extended/textwindowaccessibility.cxx | 2203 |
1 files changed, 2203 insertions, 0 deletions
diff --git a/accessibility/source/extended/textwindowaccessibility.cxx b/accessibility/source/extended/textwindowaccessibility.cxx new file mode 100644 index 000000000000..375fbb6f2ec0 --- /dev/null +++ b/accessibility/source/extended/textwindowaccessibility.cxx @@ -0,0 +1,2203 @@ +/************************************************************************* + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: textwindowaccessibility.cxx,v $ + * + * $Revision: 1.2 $ + * + * last change: $Author: hr $ $Date: 2007-06-27 15:34:57 $ + * + * The Contents of this file are made available subject to + * the terms of GNU Lesser General Public License Version 2.1. + * + * + * GNU Lesser General Public License Version 2.1 + * ============================================= + * Copyright 2005 by Sun Microsystems, Inc. + * 901 San Antonio Road, Palo Alto, CA 94303, USA + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_accessibility.hxx" + +#ifndef _TOOLKIT_AWT_VCLXACCESSIBLECOMPONENT_HXX_ +#include <accessibility/extended/textwindowaccessibility.hxx> +#endif + +#ifndef COMPHELPER_ACCESSIBLE_EVENT_NOTIFIER +#include "comphelper/accessibleeventnotifier.hxx" +#endif +#ifndef _UTL_ACCESSIBLERELATIONSETHELPER_HXX_ +#include "unotools/accessiblerelationsethelper.hxx" +#endif +#ifndef _UTL_ACCESSIBLESTATESETHELPER_HXX_ +#include <unotools/accessiblestatesethelper.hxx> +#endif +#ifndef _SV_WINDOW_HXX +#include <vcl/window.hxx> +#endif + +#ifndef _TOOLKIT_HELPER_CONVERT_HXX_ +#include <toolkit/helper/convert.hxx> +#endif + +#include <algorithm> +#include <vector> +#include <hash_map> + +namespace css = ::com::sun::star; + +namespace accessibility +{ + +// Both ::osl::Mutex and ParagraphBase implement acquire and release, and thus +// ::rtl::Reference< Paragraph > does not work. So ParagraphImpl was factored +// out and ::rtl::Reference< ParagraphImpl > is used instead. +class Paragraph: private ::osl::Mutex, public ParagraphImpl +{ +public: + inline Paragraph(::rtl::Reference< Document > const & rDocument, + Paragraphs::size_type nNumber): + ParagraphImpl(rDocument, nNumber, *this) {} +}; + +void SfxListenerGuard::startListening(::SfxBroadcaster & rNotifier) +{ + OSL_ENSURE(m_pNotifier == 0, "called more than once"); + m_pNotifier = &rNotifier; + m_rListener.StartListening(*m_pNotifier, true); +} + +void SfxListenerGuard::endListening() +{ + if (m_pNotifier != 0) + { + m_rListener.EndListening(*m_pNotifier); + m_pNotifier = 0; + } +} + +void WindowListenerGuard::startListening(::Window & rNotifier) +{ + OSL_ENSURE(m_pNotifier == 0, "called more than once"); + m_pNotifier = &rNotifier; + m_pNotifier->AddEventListener(m_aListener); +} + +void WindowListenerGuard::endListening() +{ + if (m_pNotifier != 0) + { + m_pNotifier->RemoveEventListener(m_aListener); + m_pNotifier = 0; + } +} + +ParagraphImpl::ParagraphImpl(::rtl::Reference< Document > const & rDocument, + Paragraphs::size_type nNumber, + ::osl::Mutex & rMutex): + ParagraphBase(rMutex), + m_xDocument(rDocument), + m_nNumber(nNumber), + m_nClientId(0) +{ + calculateFirstSentence(); + m_aParagraphText = m_xDocument->retrieveParagraphText(this); +} + +void +ParagraphImpl::numberChanged(bool bIncremented) +{ + Paragraphs::size_type nOld = m_nNumber; + if (bIncremented) + ++m_nNumber; + else + --m_nNumber; + + notifyEvent(::css::accessibility::AccessibleEventId:: + NAME_CHANGED, + ::css::uno::makeAny(calculateName(nOld)), + ::css::uno::makeAny(calculateName(m_nNumber))); +} + +void ParagraphImpl::textChanged() +{ + ::rtl::OUString aParagraphText = implGetText(); + ::css::uno::Any aOldValue, aNewValue; + if ( implInitTextChangedEvent( m_aParagraphText, aParagraphText, aOldValue, aNewValue ) ) + { + m_aParagraphText = aParagraphText; + notifyEvent(::css::accessibility::AccessibleEventId:: + TEXT_CHANGED, + aOldValue, aNewValue); + } + + ::rtl::OUString aOld(m_aFirstSentence); + calculateFirstSentence(); + if (m_aFirstSentence != aOld) + notifyEvent(::css::accessibility::AccessibleEventId:: + DESCRIPTION_CHANGED, + ::css::uno::makeAny(calculateDescription(aOld)), + ::css::uno::makeAny(calculateDescription( + m_aFirstSentence))); +} + +void ParagraphImpl::notifyEvent(::sal_Int16 nEventId, + ::css::uno::Any const & rOldValue, + ::css::uno::Any const & rNewValue) +{ + if (m_nClientId) + comphelper::AccessibleEventNotifier::addEvent( m_nClientId, ::css::accessibility::AccessibleEventObject( + static_cast< ::cppu::OWeakObject * >(this), + nEventId, rNewValue, rOldValue) ); +} + +// virtual +::css::uno::Reference< ::css::accessibility::XAccessibleContext > SAL_CALL +ParagraphImpl::getAccessibleContext() throw (::css::uno::RuntimeException) +{ + checkDisposed(); + return this; +} + +// virtual +::sal_Int32 SAL_CALL ParagraphImpl::getAccessibleChildCount() + throw (::css::uno::RuntimeException) +{ + checkDisposed(); + return 0; +} + +// virtual +::css::uno::Reference< ::css::accessibility::XAccessible > SAL_CALL +ParagraphImpl::getAccessibleChild(::sal_Int32) + throw (::css::lang::IndexOutOfBoundsException, + ::css::uno::RuntimeException) +{ + checkDisposed(); + throw ::css::lang::IndexOutOfBoundsException( + ::rtl::OUString( + RTL_CONSTASCII_USTRINGPARAM( + "svtools/textwindowaccessibility.cxx:" + " ParagraphImpl::getAccessibleChild")), + static_cast< ::css::uno::XWeak * >(this)); +} + +// virtual +::css::uno::Reference< ::css::accessibility::XAccessible > SAL_CALL +ParagraphImpl::getAccessibleParent() + throw (::css::uno::RuntimeException) +{ + checkDisposed(); + return m_xDocument->getAccessible(); +} + +// virtual +::sal_Int32 SAL_CALL ParagraphImpl::getAccessibleIndexInParent() + throw (::css::uno::RuntimeException) +{ + checkDisposed(); + return m_xDocument->retrieveParagraphIndex(this); +} + +// virtual +::sal_Int16 SAL_CALL ParagraphImpl::getAccessibleRole() + throw (::css::uno::RuntimeException) +{ + checkDisposed(); + return ::css::accessibility::AccessibleRole::PARAGRAPH; +} + +// virtual +::rtl::OUString SAL_CALL ParagraphImpl::getAccessibleDescription() + throw (::css::uno::RuntimeException) +{ + checkDisposed(); + return calculateDescription(m_aFirstSentence); +} + +// virtual +::rtl::OUString SAL_CALL ParagraphImpl::getAccessibleName() + throw (::css::uno::RuntimeException) +{ + checkDisposed(); + return calculateName(m_xDocument->retrieveParagraphNumber(this)); +} + +// virtual +::css::uno::Reference< ::css::accessibility::XAccessibleRelationSet > +SAL_CALL ParagraphImpl::getAccessibleRelationSet() + throw (::css::uno::RuntimeException) +{ + checkDisposed(); + return m_xDocument->retrieveParagraphRelationSet( this ); +} + +// virtual +::css::uno::Reference< ::css::accessibility::XAccessibleStateSet > +SAL_CALL ParagraphImpl::getAccessibleStateSet() + throw (::css::uno::RuntimeException) +{ + checkDisposed(); + + // FIXME Notification of changes (STATE_CHANGED) missing when + // m_rView.IsReadOnly() changes: + return new ::utl::AccessibleStateSetHelper( + m_xDocument->retrieveParagraphState(this)); +} + +// virtual +::css::lang::Locale SAL_CALL ParagraphImpl::getLocale() + throw (::css::accessibility::IllegalAccessibleComponentStateException, + ::css::uno::RuntimeException) +{ + checkDisposed(); + return m_xDocument->retrieveLocale(); +} + +// virtual +::sal_Bool SAL_CALL ParagraphImpl::containsPoint(::css::awt::Point const & rPoint) + throw (::css::uno::RuntimeException) +{ + checkDisposed(); + ::css::awt::Rectangle aRect(m_xDocument->retrieveParagraphBounds(this, + false)); + return rPoint.X >= 0 && rPoint.X < aRect.Width + && rPoint.Y >= 0 && rPoint.Y < aRect.Height; +} + +// virtual +::css::uno::Reference< ::css::accessibility::XAccessible > SAL_CALL +ParagraphImpl::getAccessibleAtPoint(::css::awt::Point const &) + throw (::css::uno::RuntimeException) +{ + checkDisposed(); + return 0; +} + +// virtual +::css::awt::Rectangle SAL_CALL ParagraphImpl::getBounds() + throw (::css::uno::RuntimeException) +{ + checkDisposed(); + return m_xDocument->retrieveParagraphBounds(this, false); +} + +// virtual +::css::awt::Point SAL_CALL ParagraphImpl::getLocation() + throw (::css::uno::RuntimeException) +{ + checkDisposed(); + ::css::awt::Rectangle aRect(m_xDocument->retrieveParagraphBounds(this, + false)); + return ::css::awt::Point(aRect.X, aRect.Y); +} + +// virtual +::css::awt::Point SAL_CALL ParagraphImpl::getLocationOnScreen() + throw (::css::uno::RuntimeException) +{ + checkDisposed(); + ::css::awt::Rectangle aRect(m_xDocument->retrieveParagraphBounds(this, + true)); + return ::css::awt::Point(aRect.X, aRect.Y); +} + +// virtual +::css::awt::Size SAL_CALL ParagraphImpl::getSize() + throw (::css::uno::RuntimeException) +{ + checkDisposed(); + ::css::awt::Rectangle aRect(m_xDocument->retrieveParagraphBounds(this, + false)); + return ::css::awt::Size(aRect.Width, aRect.Height); +} + +// virtual +void SAL_CALL ParagraphImpl::grabFocus() throw (::css::uno::RuntimeException) +{ + checkDisposed(); + try + { + m_xDocument->changeParagraphSelection(this, 0, 0); + } + catch (::css::lang::IndexOutOfBoundsException & rEx) + { + OSL_TRACE( + "svtools/textwindowaccessibility.cxx: ParagraphImpl::grabFocus:" + " caught unexpected %s\n", + ::rtl::OUStringToOString(rEx.Message, RTL_TEXTENCODING_UTF8). + getStr()); + } +} + +// virtual +::css::uno::Any SAL_CALL ParagraphImpl::getAccessibleKeyBinding() + throw (::css::uno::RuntimeException) +{ + checkDisposed(); + return ::css::uno::Any(); +} + +// virtual +::css::util::Color SAL_CALL ParagraphImpl::getForeground() + throw (::css::uno::RuntimeException) +{ + return 0; // TODO +} + +// virtual +::css::util::Color SAL_CALL ParagraphImpl::getBackground() + throw (::css::uno::RuntimeException) +{ + return 0; // TODO +} + +// virtual +::sal_Int32 SAL_CALL ParagraphImpl::getCaretPosition() + throw (::css::uno::RuntimeException) +{ + checkDisposed(); + return m_xDocument->retrieveParagraphCaretPosition(this); +} + +// virtual +::sal_Bool SAL_CALL ParagraphImpl::setCaretPosition(::sal_Int32 nIndex) + throw (::css::lang::IndexOutOfBoundsException, + ::css::uno::RuntimeException) +{ + checkDisposed(); + m_xDocument->changeParagraphSelection(this, nIndex, nIndex); + return true; +} + +// virtual +::sal_Unicode SAL_CALL ParagraphImpl::getCharacter(::sal_Int32 nIndex) + throw (::css::lang::IndexOutOfBoundsException, + ::css::uno::RuntimeException) +{ + checkDisposed(); + return OCommonAccessibleText::getCharacter(nIndex); +} + +// virtual +::css::uno::Sequence< ::css::beans::PropertyValue > SAL_CALL +ParagraphImpl::getCharacterAttributes(::sal_Int32 nIndex, const ::com::sun::star::uno::Sequence< ::rtl::OUString >& aRequestedAttributes) + throw (::css::lang::IndexOutOfBoundsException, + ::css::uno::RuntimeException) +{ + checkDisposed(); + return m_xDocument->retrieveCharacterAttributes( this, nIndex, aRequestedAttributes ); +} + +// virtual +::css::awt::Rectangle SAL_CALL +ParagraphImpl::getCharacterBounds(::sal_Int32 nIndex) + throw (::css::lang::IndexOutOfBoundsException, + ::css::uno::RuntimeException) +{ + checkDisposed(); + ::css::awt::Rectangle aBounds(m_xDocument->retrieveCharacterBounds(this, nIndex)); + ::css::awt::Rectangle aParaBounds(m_xDocument->retrieveParagraphBounds(this, false)); + aBounds.X -= aParaBounds.X; + aBounds.Y -= aParaBounds.Y; + return aBounds; +} + +// virtual +::sal_Int32 SAL_CALL ParagraphImpl::getCharacterCount() + throw (::css::uno::RuntimeException) +{ + checkDisposed(); + return OCommonAccessibleText::getCharacterCount(); +} + +// virtual +::sal_Int32 SAL_CALL +ParagraphImpl::getIndexAtPoint(::css::awt::Point const & rPoint) + throw (::css::uno::RuntimeException) +{ + checkDisposed(); + ::css::awt::Point aPoint(rPoint); + ::css::awt::Rectangle aParaBounds(m_xDocument->retrieveParagraphBounds(this, false)); + aPoint.X += aParaBounds.X; + aPoint.Y += aParaBounds.Y; + return m_xDocument->retrieveCharacterIndex(this, aPoint); +} + +// virtual +::rtl::OUString SAL_CALL ParagraphImpl::getSelectedText() + throw (::css::uno::RuntimeException) +{ + checkDisposed(); + + return OCommonAccessibleText::getSelectedText(); +} + +// virtual +::sal_Int32 SAL_CALL ParagraphImpl::getSelectionStart() + throw (::css::uno::RuntimeException) +{ + checkDisposed(); + return OCommonAccessibleText::getSelectionStart(); +} + +// virtual +::sal_Int32 SAL_CALL ParagraphImpl::getSelectionEnd() + throw (::css::uno::RuntimeException) +{ + checkDisposed(); + return OCommonAccessibleText::getSelectionEnd(); +} + +// virtual +::sal_Bool SAL_CALL ParagraphImpl::setSelection(::sal_Int32 nStartIndex, + ::sal_Int32 nEndIndex) + throw (::css::lang::IndexOutOfBoundsException, + ::css::uno::RuntimeException) +{ + checkDisposed(); + m_xDocument->changeParagraphSelection(this, nStartIndex, nEndIndex); + return true; +} + +// virtual +::rtl::OUString SAL_CALL ParagraphImpl::getText() + throw (::css::uno::RuntimeException) +{ + checkDisposed(); + return OCommonAccessibleText::getText(); +} + +// virtual +::rtl::OUString SAL_CALL ParagraphImpl::getTextRange(::sal_Int32 nStartIndex, + ::sal_Int32 nEndIndex) + throw (::css::lang::IndexOutOfBoundsException, + ::css::uno::RuntimeException) +{ + checkDisposed(); + return OCommonAccessibleText::getTextRange(nStartIndex, nEndIndex); +} + +// virtual +::com::sun::star::accessibility::TextSegment SAL_CALL ParagraphImpl::getTextAtIndex( sal_Int32 nIndex, sal_Int16 aTextType ) throw (::com::sun::star::lang::IndexOutOfBoundsException, ::com::sun::star::lang::IllegalArgumentException, ::com::sun::star::uno::RuntimeException) +{ + checkDisposed(); + return OCommonAccessibleText::getTextAtIndex(nIndex, aTextType); +} + +// virtual +::com::sun::star::accessibility::TextSegment SAL_CALL ParagraphImpl::getTextBeforeIndex( sal_Int32 nIndex, sal_Int16 aTextType ) throw (::com::sun::star::lang::IndexOutOfBoundsException, ::com::sun::star::lang::IllegalArgumentException, ::com::sun::star::uno::RuntimeException) +{ + checkDisposed(); + return OCommonAccessibleText::getTextBeforeIndex(nIndex, aTextType); +} + +// virtual +::com::sun::star::accessibility::TextSegment SAL_CALL ParagraphImpl::getTextBehindIndex( sal_Int32 nIndex, sal_Int16 aTextType ) throw (::com::sun::star::lang::IndexOutOfBoundsException, ::com::sun::star::lang::IllegalArgumentException, ::com::sun::star::uno::RuntimeException) +{ + checkDisposed(); + return OCommonAccessibleText::getTextBehindIndex(nIndex, aTextType); +} + +// virtual +::sal_Bool SAL_CALL ParagraphImpl::copyText(::sal_Int32 nStartIndex, + ::sal_Int32 nEndIndex) + throw (::css::lang::IndexOutOfBoundsException, + ::css::uno::RuntimeException) +{ + checkDisposed(); + m_xDocument->copyParagraphText(this, nStartIndex, nEndIndex); + return true; +} + +// virtual +::sal_Bool SAL_CALL ParagraphImpl::cutText(::sal_Int32 nStartIndex, + ::sal_Int32 nEndIndex) + throw (::css::lang::IndexOutOfBoundsException, + ::css::uno::RuntimeException) +{ + checkDisposed(); + m_xDocument->changeParagraphText(this, nStartIndex, nEndIndex, true, false, + ::rtl::OUString()); + return true; +} + +// virtual +::sal_Bool SAL_CALL ParagraphImpl::pasteText(::sal_Int32 nIndex) + throw (::css::lang::IndexOutOfBoundsException, + ::css::uno::RuntimeException) +{ + checkDisposed(); + m_xDocument->changeParagraphText(this, nIndex, nIndex, false, true, + ::rtl::OUString()); + return true; +} + +// virtual +::sal_Bool SAL_CALL ParagraphImpl::deleteText(::sal_Int32 nStartIndex, + ::sal_Int32 nEndIndex) + throw (::css::lang::IndexOutOfBoundsException, + ::css::uno::RuntimeException) +{ + checkDisposed(); + m_xDocument->changeParagraphText(this, nStartIndex, nEndIndex, false, false, + ::rtl::OUString()); + return true; +} + +// virtual +::sal_Bool SAL_CALL ParagraphImpl::insertText(::rtl::OUString const & rText, + ::sal_Int32 nIndex) + throw (::css::lang::IndexOutOfBoundsException, + ::css::uno::RuntimeException) +{ + checkDisposed(); + m_xDocument->changeParagraphText(this, nIndex, nIndex, false, false, rText); + return true; +} + +// virtual +::sal_Bool SAL_CALL +ParagraphImpl::replaceText(::sal_Int32 nStartIndex, ::sal_Int32 nEndIndex, + ::rtl::OUString const & rReplacement) + throw (::css::lang::IndexOutOfBoundsException, + ::css::uno::RuntimeException) +{ + checkDisposed(); + m_xDocument->changeParagraphText(this, nStartIndex, nEndIndex, false, false, + rReplacement); + return true; +} + +// virtual +::sal_Bool SAL_CALL ParagraphImpl::setAttributes( + ::sal_Int32 nStartIndex, ::sal_Int32 nEndIndex, + ::css::uno::Sequence< ::css::beans::PropertyValue > const & rAttributeSet) + throw (::css::lang::IndexOutOfBoundsException, + ::css::uno::RuntimeException) +{ + checkDisposed(); + m_xDocument->changeParagraphAttributes(this, nStartIndex, nEndIndex, + rAttributeSet); + return true; +} + +// virtual +::sal_Bool SAL_CALL ParagraphImpl::setText(::rtl::OUString const & rText) + throw (::css::uno::RuntimeException) +{ + checkDisposed(); + m_xDocument->changeParagraphText(this, rText); + return true; +} + +// virtual +::css::uno::Sequence< ::css::beans::PropertyValue > SAL_CALL +ParagraphImpl::getDefaultAttributes(const ::css::uno::Sequence< ::rtl::OUString >& RequestedAttributes) + throw (::css::uno::RuntimeException) +{ + checkDisposed(); + return m_xDocument->retrieveDefaultAttributes( this, RequestedAttributes ); +} + +// virtual +::css::uno::Sequence< ::css::beans::PropertyValue > SAL_CALL +ParagraphImpl::getRunAttributes(::sal_Int32 Index, const ::css::uno::Sequence< ::rtl::OUString >& RequestedAttributes) + throw (::css::lang::IndexOutOfBoundsException, + ::css::uno::RuntimeException) +{ + checkDisposed(); + return m_xDocument->retrieveRunAttributes( this, Index, RequestedAttributes ); +} + +// virtual +void SAL_CALL ParagraphImpl::addEventListener( + ::css::uno::Reference< + ::css::accessibility::XAccessibleEventListener > const & rListener) + throw (::css::uno::RuntimeException) +{ + if (rListener.is()) + { + ::osl::ClearableMutexGuard aGuard(rBHelper.rMutex); + if (rBHelper.bDisposed || rBHelper.bInDispose) + { + aGuard.clear(); + rListener->disposing(::css::lang::EventObject( + static_cast< ::cppu::OWeakObject * >(this))); + } + else + { + if (!m_nClientId) + m_nClientId = comphelper::AccessibleEventNotifier::registerClient( ); + comphelper::AccessibleEventNotifier::addEventListener( m_nClientId, rListener ); + } + } +} + +// virtual +void SAL_CALL ParagraphImpl::removeEventListener( + ::css::uno::Reference< + ::css::accessibility::XAccessibleEventListener > const & rListener) + throw (::css::uno::RuntimeException) +{ + comphelper::AccessibleEventNotifier::TClientId nId = 0; + { + ::osl::ClearableMutexGuard aGuard(rBHelper.rMutex); + if (rListener.is() && m_nClientId != 0 + && comphelper::AccessibleEventNotifier::removeEventListener( m_nClientId, rListener ) == 0) + { + nId = m_nClientId; + m_nClientId = 0; + } + } + if (nId != 0) + { + // no listeners anymore + // -> revoke ourself. This may lead to the notifier thread dying (if we were the last client), + // and at least to us not firing any events anymore, in case somebody calls + // NotifyAccessibleEvent, again + comphelper::AccessibleEventNotifier::revokeClient(nId); + } +} + +// virtual +void SAL_CALL ParagraphImpl::disposing() +{ + comphelper::AccessibleEventNotifier::TClientId nId = 0; + { + ::osl::ClearableMutexGuard aGuard(rBHelper.rMutex); + nId = m_nClientId; + m_nClientId = 0; + } + if (nId != 0) + comphelper::AccessibleEventNotifier::revokeClientNotifyDisposing(nId, *this); +} + +// virtual +::rtl::OUString ParagraphImpl::implGetText() +{ + return m_xDocument->retrieveParagraphText(this); +} + +// virtual +::css::lang::Locale ParagraphImpl::implGetLocale() +{ + return m_xDocument->retrieveLocale(); +} + +// virtual +void ParagraphImpl::implGetSelection(::sal_Int32 & rStartIndex, + ::sal_Int32 & rEndIndex) +{ + m_xDocument->retrieveParagraphSelection(this, &rStartIndex, &rEndIndex); +} + +// virtual +void ParagraphImpl::implGetParagraphBoundary( ::css::i18n::Boundary& rBoundary, + ::sal_Int32 nIndex ) +{ + ::rtl::OUString sText( implGetText() ); + ::sal_Int32 nLength = sText.getLength(); + + if ( implIsValidIndex( nIndex, nLength ) ) + { + rBoundary.startPos = 0; + rBoundary.endPos = nLength; + } + else + { + rBoundary.startPos = nIndex; + rBoundary.endPos = nIndex; + } +} + +// virtual +void ParagraphImpl::implGetLineBoundary( ::css::i18n::Boundary& rBoundary, + ::sal_Int32 nIndex ) +{ + ::rtl::OUString sText( implGetText() ); + ::sal_Int32 nLength = sText.getLength(); + + if ( implIsValidIndex( nIndex, nLength ) || nIndex == nLength ) + { + ::css::i18n::Boundary aBoundary = + m_xDocument->retrieveParagraphLineBoundary( this, nIndex ); + rBoundary.startPos = aBoundary.startPos; + rBoundary.endPos = aBoundary.endPos; + } + else + { + rBoundary.startPos = nIndex; + rBoundary.endPos = nIndex; + } +} + +void ParagraphImpl::checkDisposed() +{ + ::osl::MutexGuard aGuard(rBHelper.rMutex); + if (!(rBHelper.bDisposed || rBHelper.bInDispose)) + return; + throw ::css::lang::DisposedException( + ::rtl::OUString(), static_cast< ::css::uno::XWeak * >(this)); +} + +void ParagraphImpl::calculateFirstSentence() +{ + try + { + m_aFirstSentence = OCommonAccessibleText::getTextAtIndex( + 0, ::css::accessibility::AccessibleTextType::SENTENCE).SegmentText; + } + catch (::css::lang::IndexOutOfBoundsException &) + { + m_aFirstSentence = ::rtl::OUString(); + } +} + +::rtl::OUString +ParagraphImpl::calculateDescription(::rtl::OUString const & rFirstSentence) +{ + // FIXME Localize description (e.g., "Paragraph: XXX")? OBR to clarify. + return rFirstSentence; +} + +::rtl::OUString ParagraphImpl::calculateName(Paragraphs::size_type) +{ + // FIXME Localize name (e.g., "Paragraph NNN")? OBR to clarify. + return ::rtl::OUString::valueOf( + static_cast< ::sal_Int64 >(m_xDocument->retrieveParagraphNumber(this)) + + 1); // XXX numeric overflow +} + +Document::Document(::VCLXWindow * pVclXWindow, ::TextEngine & rEngine, + ::TextView & rView, bool bCompoundControlChild): + VCLXAccessibleComponent(pVclXWindow), + m_xAccessible(pVclXWindow), + m_rEngine(rEngine), + m_rView(rView), + m_aEngineListener(*this), + m_aViewListener(LINK(this, Document, WindowEventHandler)), + m_bCompoundControlChild(bCompoundControlChild) +{} + +::css::lang::Locale Document::retrieveLocale() +{ + ::osl::Guard< ::comphelper::IMutex > aExternalGuard(getExternalLock()); + return m_rEngine.GetLocale(); +} + +Paragraphs::size_type +Document::retrieveParagraphNumber(ParagraphImpl const * pParagraph) +{ + ::osl::MutexGuard aInternalGuard(GetMutex()); + return pParagraph->getNumber(); +} + +::sal_Int32 Document::retrieveParagraphIndex(ParagraphImpl const * pParagraph) +{ + ::osl::MutexGuard aInternalGuard(GetMutex()); + + // If a client holds on to a Paragraph that is no longer visible, it can + // happen that this Paragraph lies outside the range from m_aVisibleBegin + // to m_aVisibleEnd. In that case, return -1 instead of a valid index: + Paragraphs::iterator aPara(m_xParagraphs->begin() + + pParagraph->getNumber()); + return aPara < m_aVisibleBegin || aPara >= m_aVisibleEnd + ? -1 : static_cast< ::sal_Int32 >(aPara - m_aVisibleBegin); + // XXX numeric overflow +} + +::sal_Int64 Document::retrieveParagraphState(ParagraphImpl const * pParagraph) +{ + ::osl::MutexGuard aInternalGuard(GetMutex()); + + // If a client holds on to a Paragraph that is no longer visible, it can + // happen that this Paragraph lies outside the range from m_aVisibleBegin + // to m_aVisibleEnd. In that case, it is neither VISIBLE nor SHOWING: + ::sal_Int64 nState + = (static_cast< ::sal_Int64 >(1) + << ::css::accessibility::AccessibleStateType::ENABLED) + | (static_cast< ::sal_Int64 >(1) + << ::css::accessibility::AccessibleStateType::SENSITIVE) + | (static_cast< ::sal_Int64 >(1) + << ::css::accessibility::AccessibleStateType::FOCUSABLE) + | (static_cast< ::sal_Int64 >(1) + << ::css::accessibility::AccessibleStateType::MULTI_LINE); + if (!m_rView.IsReadOnly()) + nState |= (static_cast< ::sal_Int64 >(1) + << ::css::accessibility::AccessibleStateType::EDITABLE); + Paragraphs::iterator aPara(m_xParagraphs->begin() + + pParagraph->getNumber()); + if (aPara >= m_aVisibleBegin && aPara < m_aVisibleEnd) + { + nState + |= (static_cast< ::sal_Int64 >(1) + << ::css::accessibility::AccessibleStateType::VISIBLE) + | (static_cast< ::sal_Int64 >(1) + << ::css::accessibility::AccessibleStateType::SHOWING); + if (aPara == m_aFocused) + nState |= (static_cast< ::sal_Int64 >(1) + << ::css::accessibility::AccessibleStateType::FOCUSED); + } + return nState; +}; + +::css::awt::Rectangle +Document::retrieveParagraphBounds(ParagraphImpl const * pParagraph, + bool bAbsolute) +{ + ::osl::Guard< ::comphelper::IMutex > aExternalGuard(getExternalLock()); + ::osl::MutexGuard aInternalGuard(GetMutex()); + + // If a client holds on to a Paragraph that is no longer visible (as it + // scrolled out the top of the view), it can happen that this Paragraph + // lies before m_aVisibleBegin. In that case, calculate the vertical + // position of the Paragraph starting at paragraph 0, otherwise optimize + // and start at m_aVisibleBegin: + Paragraphs::iterator aPara(m_xParagraphs->begin() + + pParagraph->getNumber()); + ::sal_Int32 nPos; + Paragraphs::iterator aIt; + if (aPara < m_aVisibleBegin) + { + nPos = 0; + aIt = m_xParagraphs->begin(); + } + else + { + nPos = m_nViewOffset - m_nVisibleBeginOffset; + aIt = m_aVisibleBegin; + } + for (; aIt != aPara; ++aIt) + nPos += aIt->getHeight(); + + Point aOrig(0, 0); + if (bAbsolute) + aOrig = m_rView.GetWindow()->OutputToAbsoluteScreenPixel(aOrig); + + return ::css::awt::Rectangle( + static_cast< ::sal_Int32 >(aOrig.X()), + static_cast< ::sal_Int32 >(aOrig.Y()) + nPos, + m_rView.GetWindow()->GetOutputSizePixel().Width(), aPara->getHeight()); + // XXX numeric overflow (3x) +} + +::rtl::OUString +Document::retrieveParagraphText(ParagraphImpl const * pParagraph) +{ + ::osl::Guard< ::comphelper::IMutex > aExternalGuard(getExternalLock()); + ::osl::MutexGuard aInternalGuard(GetMutex()); + return m_rEngine.GetText(static_cast< ::ULONG >(pParagraph->getNumber())); + // numeric overflow cannot happen here +} + +void Document::retrieveParagraphSelection(ParagraphImpl const * pParagraph, + ::sal_Int32 * pBegin, + ::sal_Int32 * pEnd) +{ + ::osl::Guard< ::comphelper::IMutex > aExternalGuard(getExternalLock()); + ::osl::MutexGuard aInternalGuard(GetMutex()); + ::TextSelection const & rSelection = m_rView.GetSelection(); + Paragraphs::size_type nNumber = pParagraph->getNumber(); + TextPaM aStartPaM( rSelection.GetStart() ); + TextPaM aEndPaM( rSelection.GetEnd() ); + TextPaM aMinPaM( ::std::min( aStartPaM, aEndPaM ) ); + TextPaM aMaxPaM( ::std::max( aStartPaM, aEndPaM ) ); + + if ( nNumber >= aMinPaM.GetPara() && nNumber <= aMaxPaM.GetPara() ) + { + *pBegin = nNumber > aMinPaM.GetPara() + ? 0 + : static_cast< ::sal_Int32 >( aMinPaM.GetIndex() ); + // XXX numeric overflow + *pEnd = nNumber < aMaxPaM.GetPara() + ? static_cast< ::sal_Int32 >( m_rEngine.GetText(static_cast< ::ULONG >(nNumber)).Len() ) + : static_cast< ::sal_Int32 >( aMaxPaM.GetIndex() ); + // XXX numeric overflow (3x) + + if ( aStartPaM > aEndPaM ) + ::std::swap( *pBegin, *pEnd ); + } + else + { + *pBegin = 0; + *pEnd = 0; + } +} + +::sal_Int32 Document::retrieveParagraphCaretPosition(ParagraphImpl const * pParagraph) +{ + ::osl::Guard< ::comphelper::IMutex > aExternalGuard(getExternalLock()); + ::osl::MutexGuard aInternalGuard(GetMutex()); + ::TextSelection const & rSelection = m_rView.GetSelection(); + Paragraphs::size_type nNumber = pParagraph->getNumber(); + TextPaM aEndPaM( rSelection.GetEnd() ); + + return aEndPaM.GetPara() == nNumber + ? static_cast< ::sal_Int32 >(aEndPaM.GetIndex()) : -1; +} + +::css::awt::Rectangle +Document::retrieveCharacterBounds(ParagraphImpl const * pParagraph, + ::sal_Int32 nIndex) +{ + ::osl::Guard< ::comphelper::IMutex > aExternalGuard(getExternalLock()); + ::osl::MutexGuard aInternalGuard(GetMutex()); + ::ULONG nNumber = static_cast< ::ULONG >(pParagraph->getNumber()); + sal_Int32 nLength = m_rEngine.GetText(nNumber).Len(); + // XXX numeric overflow + if (nIndex < 0 || nIndex > nLength) + throw ::css::lang::IndexOutOfBoundsException( + ::rtl::OUString( + RTL_CONSTASCII_USTRINGPARAM( + "svtools/textwindowaccessibility.cxx:" + " Document::retrieveCharacterAttributes")), + static_cast< ::css::uno::XWeak * >(this)); + ::css::awt::Rectangle aBounds( 0, 0, 0, 0 ); + if ( nIndex == nLength ) + { + aBounds = AWTRectangle( + m_rEngine.PaMtoEditCursor(::TextPaM(nNumber, + static_cast< ::USHORT >(nIndex)))); + } + else + { + ::Rectangle aLeft( + m_rEngine.PaMtoEditCursor(::TextPaM(nNumber, + static_cast< ::USHORT >(nIndex)))); + // XXX numeric overflow + ::Rectangle aRight( + m_rEngine.PaMtoEditCursor(::TextPaM(nNumber, + static_cast< ::USHORT >(nIndex) + + 1))); + // XXX numeric overflow (2x) + // FIXME If the vertical extends of the two cursors do not match, assume + // nIndex is the last character on the line; the bounding box will then + // extend to m_rEnginge.GetMaxTextWidth(): + ::sal_Int32 nWidth = (aLeft.Top() == aRight.Top() + && aLeft.Bottom() == aRight.Bottom()) + ? static_cast< ::sal_Int32 >(aRight.Left() - aLeft.Left()) + : static_cast< ::sal_Int32 >(m_rEngine.GetMaxTextWidth() + - aLeft.Left()); + // XXX numeric overflow (4x) + aBounds = ::css::awt::Rectangle(static_cast< ::sal_Int32 >(aLeft.Left()), + static_cast< ::sal_Int32 >(aLeft.Top()), + nWidth, + static_cast< ::sal_Int32 >(aLeft.Bottom() + - aLeft.Top())); + // XXX numeric overflow (4x) + } + return aBounds; +} + +::sal_Int32 Document::retrieveCharacterIndex(ParagraphImpl const * pParagraph, + ::css::awt::Point const & rPoint) +{ + ::osl::Guard< ::comphelper::IMutex > aExternalGuard(getExternalLock()); + ::osl::MutexGuard aInternalGuard(GetMutex()); + ::ULONG nNumber = static_cast< ::ULONG >(pParagraph->getNumber()); + // XXX numeric overflow + ::TextPaM aPaM(m_rEngine.GetPaM(::Point(static_cast< long >(rPoint.X), + static_cast< long >(rPoint.Y)))); + // XXX numeric overflow (2x) + return aPaM.GetPara() == nNumber + ? static_cast< ::sal_Int32 >(aPaM.GetIndex()) : -1; + // XXX numeric overflow +} + +::css::uno::Sequence< ::css::beans::PropertyValue > +Document::retrieveCharacterAttributes( + ParagraphImpl const * pParagraph, ::sal_Int32 nIndex, + const ::css::uno::Sequence< ::rtl::OUString >& aRequestedAttributes) +{ + ::osl::Guard< ::comphelper::IMutex > aExternalGuard(getExternalLock()); + ::osl::MutexGuard aInternalGuard(GetMutex()); + ::ULONG nNumber = static_cast< ::ULONG >(pParagraph->getNumber()); + // XXX numeric overflow + if (nIndex < 0 || nIndex >= m_rEngine.GetText(nNumber).Len()) + throw ::css::lang::IndexOutOfBoundsException( + ::rtl::OUString( + RTL_CONSTASCII_USTRINGPARAM( + "svtools/textwindowaccessibility.cxx:" + " Document::retrieveCharacterAttributes")), + static_cast< ::css::uno::XWeak * >(this)); + + // retrieve default attributes + tPropValMap aCharAttrSeq; + retrieveDefaultAttributesImpl( pParagraph, aRequestedAttributes, aCharAttrSeq ); + + // retrieve run attributes + tPropValMap aRunAttrSeq; + retrieveRunAttributesImpl( pParagraph, nIndex, aRequestedAttributes, aRunAttrSeq ); + + // merge default and run attributes + for ( tPropValMap::const_iterator aRunIter = aRunAttrSeq.begin(); + aRunIter != aRunAttrSeq.end(); + ++aRunIter ) + { + aCharAttrSeq[ aRunIter->first ] = aRunIter->second; + } + + return convertHashMapToSequence( aCharAttrSeq ); +} + +void Document::retrieveDefaultAttributesImpl( + ParagraphImpl const * pParagraph, + const ::css::uno::Sequence< ::rtl::OUString >& RequestedAttributes, + tPropValMap& rDefAttrSeq) +{ + // default attributes are not supported by text engine + (void) pParagraph; + (void) RequestedAttributes; + (void) rDefAttrSeq; +} + +::css::uno::Sequence< ::css::beans::PropertyValue > +Document::retrieveDefaultAttributes( + ParagraphImpl const * pParagraph, + const ::css::uno::Sequence< ::rtl::OUString >& RequestedAttributes) +{ + ::osl::Guard< ::comphelper::IMutex > aExternalGuard( getExternalLock() ); + ::osl::MutexGuard aInternalGuard( GetMutex() ); + + tPropValMap aDefAttrSeq; + retrieveDefaultAttributesImpl( pParagraph, RequestedAttributes, aDefAttrSeq ); + return convertHashMapToSequence( aDefAttrSeq ); +} + +// static +::css::uno::Sequence< ::css::beans::PropertyValue > +Document::convertHashMapToSequence(tPropValMap& rAttrSeq) +{ + ::css::uno::Sequence< ::css::beans::PropertyValue > aValues( rAttrSeq.size() ); + ::css::beans::PropertyValue* pValues = aValues.getArray(); + ::sal_Int32 i = 0; + for ( tPropValMap::const_iterator aIter = rAttrSeq.begin(); + aIter != rAttrSeq.end(); + ++aIter ) + { + pValues[i] = aIter->second; + ++i; + } + return aValues; +} + +void Document::retrieveRunAttributesImpl( + ParagraphImpl const * pParagraph, ::sal_Int32 Index, + const ::css::uno::Sequence< ::rtl::OUString >& RequestedAttributes, + tPropValMap& rRunAttrSeq) +{ + ::ULONG nNumber = static_cast< ::ULONG >( pParagraph->getNumber() ); + ::TextPaM aPaM( nNumber, static_cast< ::USHORT >( Index ) ); + // XXX numeric overflow + // FIXME TEXTATTR_HYPERLINK ignored: + ::TextAttribFontColor const * pColor + = static_cast< ::TextAttribFontColor const * >( + m_rEngine.FindAttrib( aPaM, TEXTATTR_FONTCOLOR ) ); + ::TextAttribFontWeight const * pWeight + = static_cast< ::TextAttribFontWeight const * >( + m_rEngine.FindAttrib( aPaM, TEXTATTR_FONTWEIGHT ) ); + tPropValMap aRunAttrSeq; + if ( pColor ) + { + ::css::beans::PropertyValue aPropVal; + aPropVal.Name = + ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "CharColor" ) ); + aPropVal.Handle = -1; + aPropVal.Value = mapFontColor( pColor->GetColor() ); + aPropVal.State = ::css::beans::PropertyState_DIRECT_VALUE; + aRunAttrSeq[ aPropVal.Name ] = aPropVal; + } + if ( pWeight ) + { + ::css::beans::PropertyValue aPropVal; + aPropVal.Name = + ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "CharWeight" ) ); + aPropVal.Handle = -1; + aPropVal.Value = mapFontWeight( pWeight->getFontWeight() ); + aPropVal.State = ::css::beans::PropertyState_DIRECT_VALUE; + aRunAttrSeq[ aPropVal.Name ] = aPropVal; + } + if ( RequestedAttributes.getLength() == 0 ) + { + rRunAttrSeq = aRunAttrSeq; + } + else + { + const ::rtl::OUString* pReqAttrs = RequestedAttributes.getConstArray(); + const ::sal_Int32 nLength = RequestedAttributes.getLength(); + for ( ::sal_Int32 i = 0; i < nLength; ++i ) + { + tPropValMap::iterator aIter = aRunAttrSeq.find( pReqAttrs[i] ); + if ( aIter != aRunAttrSeq.end() ) + { + rRunAttrSeq[ (*aIter).first ] = (*aIter).second; + } + } + } +} + +::css::uno::Sequence< ::css::beans::PropertyValue > +Document::retrieveRunAttributes( + ParagraphImpl const * pParagraph, ::sal_Int32 Index, + const ::css::uno::Sequence< ::rtl::OUString >& RequestedAttributes) +{ + ::osl::Guard< ::comphelper::IMutex > aExternalGuard( getExternalLock() ); + ::osl::MutexGuard aInternalGuard( GetMutex() ); + ::ULONG nNumber = static_cast< ::ULONG >( pParagraph->getNumber() ); + // XXX numeric overflow + if ( Index < 0 || Index >= m_rEngine.GetText(nNumber).Len() ) + throw ::css::lang::IndexOutOfBoundsException( + ::rtl::OUString( + RTL_CONSTASCII_USTRINGPARAM( + "svtools/textwindowaccessibility.cxx:" + " Document::retrieveRunAttributes") ), + static_cast< ::css::uno::XWeak * >( this ) ); + + tPropValMap aRunAttrSeq; + retrieveRunAttributesImpl( pParagraph, Index, RequestedAttributes, aRunAttrSeq ); + return convertHashMapToSequence( aRunAttrSeq ); +} + +void Document::changeParagraphText(ParagraphImpl * pParagraph, + ::rtl::OUString const & rText) +{ + ::osl::Guard< ::comphelper::IMutex > aExternalGuard(getExternalLock()); + { + ::osl::MutexGuard aInternalGuard(GetMutex()); + ::ULONG nNumber = static_cast< ::ULONG >(pParagraph->getNumber()); + // XXX numeric overflow + changeParagraphText(nNumber, 0, m_rEngine.GetTextLen(nNumber), false, + false, rText); + } +} + +void Document::changeParagraphText(ParagraphImpl * pParagraph, + ::sal_Int32 nBegin, ::sal_Int32 nEnd, + bool bCut, bool bPaste, + ::rtl::OUString const & rText) +{ + ::osl::Guard< ::comphelper::IMutex > aExternalGuard(getExternalLock()); + { + ::osl::MutexGuard aInternalGuard(GetMutex()); + ::ULONG nNumber = static_cast< ::ULONG >(pParagraph->getNumber()); + // XXX numeric overflow + if (nBegin < 0 || nBegin > nEnd + || nEnd > m_rEngine.GetText(nNumber).Len()) + throw ::css::lang::IndexOutOfBoundsException( + ::rtl::OUString( + RTL_CONSTASCII_USTRINGPARAM( + "svtools/textwindowaccessibility.cxx:" + " Document::changeParagraphText")), + static_cast< ::css::uno::XWeak * >(this)); + changeParagraphText(nNumber, static_cast< ::USHORT >(nBegin), + static_cast< ::USHORT >(nEnd), bCut, bPaste, rText); + // XXX numeric overflow (2x) + } +} + +void Document::copyParagraphText(ParagraphImpl const * pParagraph, + ::sal_Int32 nBegin, ::sal_Int32 nEnd) +{ + ::osl::Guard< ::comphelper::IMutex > aExternalGuard(getExternalLock()); + { + ::osl::MutexGuard aInternalGuard(GetMutex()); + ::ULONG nNumber = static_cast< ::ULONG >(pParagraph->getNumber()); + // XXX numeric overflow + if (nBegin < 0 || nBegin > nEnd + || nEnd > m_rEngine.GetText(nNumber).Len()) + throw ::css::lang::IndexOutOfBoundsException( + ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM( + "svtools/textwindowaccessibility.cxx:" + " Document::copyParagraphText")), + static_cast< ::css::uno::XWeak * >(this)); + m_rView.SetSelection( + ::TextSelection(::TextPaM(nNumber, static_cast< ::USHORT >(nBegin)), + ::TextPaM(nNumber, static_cast< ::USHORT >(nEnd)))); + // XXX numeric overflow (2x) + m_rView.Copy(); + } +} + +void Document::changeParagraphAttributes( + ParagraphImpl * pParagraph, ::sal_Int32 nBegin, ::sal_Int32 nEnd, + ::css::uno::Sequence< ::css::beans::PropertyValue > const & rAttributeSet) +{ + ::osl::Guard< ::comphelper::IMutex > aExternalGuard(getExternalLock()); + { + ::osl::MutexGuard aInternalGuard(GetMutex()); + ::ULONG nNumber = static_cast< ::ULONG >(pParagraph->getNumber()); + // XXX numeric overflow + if (nBegin < 0 || nBegin > nEnd + || nEnd > m_rEngine.GetText(nNumber).Len()) + throw ::css::lang::IndexOutOfBoundsException( + ::rtl::OUString( + RTL_CONSTASCII_USTRINGPARAM( + "svtools/textwindowaccessibility.cxx:" + " Document::changeParagraphAttributes")), + static_cast< ::css::uno::XWeak * >(this)); + + // FIXME The new attributes are added to any attributes already set, + // they do not replace the old attributes as required by + // XAccessibleEditableText.setAttributes: + for (::sal_Int32 i = 0; i < rAttributeSet.getLength(); ++i) + if (rAttributeSet[i].Name.equalsAsciiL( + RTL_CONSTASCII_STRINGPARAM("CharColor"))) + m_rEngine.SetAttrib(::TextAttribFontColor( + mapFontColor(rAttributeSet[i].Value)), + nNumber, static_cast< ::USHORT >(nBegin), + static_cast< ::USHORT >(nEnd)); + // XXX numeric overflow (2x) + else if (rAttributeSet[i].Name.equalsAsciiL( + RTL_CONSTASCII_STRINGPARAM("CharWeight"))) + m_rEngine.SetAttrib(::TextAttribFontWeight( + mapFontWeight(rAttributeSet[i].Value)), + nNumber, static_cast< ::USHORT >(nBegin), + static_cast< ::USHORT >(nEnd)); + // XXX numeric overflow (2x) + } +} + +void Document::changeParagraphSelection(ParagraphImpl * pParagraph, + ::sal_Int32 nBegin, ::sal_Int32 nEnd) +{ + ::osl::Guard< ::comphelper::IMutex > aExternalGuard(getExternalLock()); + { + ::osl::MutexGuard aInternalGuard(GetMutex()); + ::ULONG nNumber = static_cast< ::ULONG >(pParagraph->getNumber()); + // XXX numeric overflow + if (nBegin < 0 || nBegin > nEnd + || nEnd > m_rEngine.GetText(nNumber).Len()) + throw ::css::lang::IndexOutOfBoundsException( + ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM( + "svtools/textwindowaccessibility.cxx:" + " Document::changeParagraphSelection")), + static_cast< ::css::uno::XWeak * >(this)); + m_rView.SetSelection( + ::TextSelection(::TextPaM(nNumber, static_cast< ::USHORT >(nBegin)), + ::TextPaM(nNumber, static_cast< ::USHORT >(nEnd)))); + // XXX numeric overflow (2x) + } +} + +::css::i18n::Boundary +Document::retrieveParagraphLineBoundary( ParagraphImpl const * pParagraph, + ::sal_Int32 nIndex ) +{ + ::css::i18n::Boundary aBoundary; + aBoundary.startPos = nIndex; + aBoundary.endPos = nIndex; + + ::osl::Guard< ::comphelper::IMutex > aExternalGuard( getExternalLock() ); + { + ::osl::MutexGuard aInternalGuard( GetMutex() ); + ::ULONG nNumber = static_cast< ::ULONG >( pParagraph->getNumber() ); + if ( nIndex < 0 || nIndex > m_rEngine.GetText( nNumber ).Len() ) + throw ::css::lang::IndexOutOfBoundsException( + ::rtl::OUString( + RTL_CONSTASCII_USTRINGPARAM( + "svtools/textwindowaccessibility.cxx:" + " Document::retrieveParagraphLineBoundary" ) ), + static_cast< ::css::uno::XWeak * >( this ) ); + ::sal_Int32 nLineStart = 0; + ::sal_Int32 nLineEnd = 0; + ::USHORT nLineCount = m_rEngine.GetLineCount( nNumber ); + for ( ::USHORT nLine = 0; nLine < nLineCount; ++nLine ) + { + ::sal_Int32 nLineLength = static_cast< ::sal_Int32 >( + m_rEngine.GetLineLen( nNumber, nLine ) ); + nLineStart = nLineEnd; + nLineEnd += nLineLength; + if ( nIndex >= nLineStart && ( ( nLine == nLineCount - 1 ) ? nIndex <= nLineEnd : nIndex < nLineEnd ) ) + { + aBoundary.startPos = nLineStart; + aBoundary.endPos = nLineEnd; + break; + } + } + } + + return aBoundary; +} + +::css::uno::Reference< ::css::accessibility::XAccessibleRelationSet > +Document::retrieveParagraphRelationSet( ParagraphImpl const * pParagraph ) +{ + ::osl::MutexGuard aInternalGuard( GetMutex() ); + + ::utl::AccessibleRelationSetHelper* pRelationSetHelper = new ::utl::AccessibleRelationSetHelper(); + ::css::uno::Reference< ::css::accessibility::XAccessibleRelationSet > xSet = pRelationSetHelper; + + Paragraphs::iterator aPara( m_xParagraphs->begin() + pParagraph->getNumber() ); + + if ( aPara > m_aVisibleBegin && aPara < m_aVisibleEnd ) + { + ::css::uno::Sequence< ::css::uno::Reference< ::css::uno::XInterface > > aSequence(1); + aSequence[0] = getAccessibleChild( aPara - 1 ); + ::css::accessibility::AccessibleRelation aRelation( ::css::accessibility::AccessibleRelationType::CONTENT_FLOWS_FROM, aSequence ); + pRelationSetHelper->AddRelation( aRelation ); + } + + if ( aPara >= m_aVisibleBegin && aPara < m_aVisibleEnd -1 ) + { + ::css::uno::Sequence< ::css::uno::Reference< ::css::uno::XInterface > > aSequence(1); + aSequence[0] = getAccessibleChild( aPara + 1 ); + ::css::accessibility::AccessibleRelation aRelation( ::css::accessibility::AccessibleRelationType::CONTENT_FLOWS_TO, aSequence ); + pRelationSetHelper->AddRelation( aRelation ); + } + + return xSet; +} + +void Document::ProcessWindowEvent( const VclWindowEvent& rVclWindowEvent ) +{ + switch ( rVclWindowEvent.GetId() ) + { + case VCLEVENT_WINDOW_GETFOCUS: + case VCLEVENT_WINDOW_LOSEFOCUS: + { + // #107179# if our parent is a compound control (e.g. MultiLineEdit), + // suppress the window focus events here + if ( !m_bCompoundControlChild ) + VCLXAccessibleComponent::ProcessWindowEvent( rVclWindowEvent ); + } + break; + default: + VCLXAccessibleComponent::ProcessWindowEvent( rVclWindowEvent ); + } +} + +// virtual +::sal_Int32 SAL_CALL Document::getAccessibleChildCount() + throw (::css::uno::RuntimeException) +{ + ::comphelper::OExternalLockGuard aGuard(this); + init(); + return m_aVisibleEnd - m_aVisibleBegin; +} + +// virtual +::css::uno::Reference< ::css::accessibility::XAccessible > SAL_CALL +Document::getAccessibleChild(::sal_Int32 i) + throw (::css::lang::IndexOutOfBoundsException, + ::css::uno::RuntimeException) +{ + ::comphelper::OExternalLockGuard aGuard(this); + init(); + if (i < 0 || i >= m_aVisibleEnd - m_aVisibleBegin) + throw ::css::lang::IndexOutOfBoundsException( + ::rtl::OUString( + RTL_CONSTASCII_USTRINGPARAM( + "svtools/textwindowaccessibility.cxx:" + " Document::getAccessibleChild")), + static_cast< ::css::uno::XWeak * >(this)); + return getAccessibleChild(m_aVisibleBegin + + static_cast< Paragraphs::size_type >(i)); +} + +// virtual +::sal_Int16 SAL_CALL Document::getAccessibleRole() + throw (::css::uno::RuntimeException) +{ + return ::css::accessibility::AccessibleRole::TEXT_FRAME; +} + +// virtual +::css::uno::Reference< ::css::accessibility::XAccessible > SAL_CALL +Document::getAccessibleAtPoint(::css::awt::Point const & rPoint) + throw (::css::uno::RuntimeException) +{ + ::comphelper::OExternalLockGuard aGuard(this); + init(); + if (rPoint.X >= 0 + && rPoint.X < m_rView.GetWindow()->GetOutputSizePixel().Width() + && rPoint.Y >= 0 && rPoint.Y < m_nViewHeight) + { + ::sal_Int32 nOffset = m_nViewOffset + rPoint.Y; // XXX numeric overflow + ::sal_Int32 nPos = m_nViewOffset - m_nVisibleBeginOffset; + for (Paragraphs::iterator aIt(m_aVisibleBegin); aIt != m_aVisibleEnd; + ++aIt) + { + nPos += aIt->getHeight(); // XXX numeric overflow + if (nOffset < nPos) + return getAccessibleChild(aIt); + } + } + return 0; +} + +// virtual +void SAL_CALL Document::disposing() +{ + m_aEngineListener.endListening(); + m_aViewListener.endListening(); + if (m_xParagraphs.get() != 0) + disposeParagraphs(); + VCLXAccessibleComponent::disposing(); +} + +// virtual +void Document::Notify(::SfxBroadcaster &, ::SfxHint const & rHint) +{ + if (rHint.ISA(::TextHint)) + { + ::TextHint const & rTextHint + = static_cast< ::TextHint const & >(rHint); + switch (rTextHint.GetId()) + { + case TEXT_HINT_PARAINSERTED: + case TEXT_HINT_PARAREMOVED: + // TEXT_HINT_PARAINSERTED and TEXT_HINT_PARAREMOVED are sent at + // "unsafe" times (when the text engine has not yet re-formatted its + // content), so that for example calling ::TextEngine::GetTextHeight + // from within the code that handles TEXT_HINT_PARAINSERTED causes + // trouble within the text engine. Therefore, these hints are just + // buffered until a following ::TextEngine::FormatDoc causes a + // TEXT_HINT_TEXTFORMATTED to come in: + case TEXT_HINT_FORMATPARA: + // ::TextEngine::FormatDoc sends a sequence of + // TEXT_HINT_FORMATPARAs, followed by an optional + // TEXT_HINT_TEXTHEIGHTCHANGED, followed in all cases by one + // TEXT_HINT_TEXTFORMATTED. Only the TEXT_HINT_FORMATPARAs contain + // the the numbers of the affected paragraphs, but they are sent + // before the changes are applied. Therefore, TEXT_HINT_FORMATPARAs + // are just buffered until another hint comes in: + { + ::osl::MutexGuard aInternalGuard(GetMutex()); + if (!isAlive()) + break; + + m_aParagraphNotifications.push(rTextHint); + break; + } + case TEXT_HINT_TEXTFORMATTED: + case TEXT_HINT_TEXTHEIGHTCHANGED: + case TEXT_HINT_MODIFIED: + { + ::osl::MutexGuard aInternalGuard(GetMutex()); + if (!isAlive()) + break; + handleParagraphNotifications(); + break; + } + case TEXT_HINT_VIEWSCROLLED: + { + ::osl::MutexGuard aInternalGuard(GetMutex()); + if (!isAlive()) + break; + handleParagraphNotifications(); + + ::sal_Int32 nOffset = static_cast< ::sal_Int32 >( + m_rView.GetStartDocPos().Y()); + // XXX numeric overflow + if (nOffset != m_nViewOffset) + { + m_nViewOffset = nOffset; + + Paragraphs::iterator aOldVisibleBegin( + m_aVisibleBegin); + Paragraphs::iterator aOldVisibleEnd(m_aVisibleEnd); + + determineVisibleRange(); + + notifyVisibleRangeChanges(aOldVisibleBegin, + aOldVisibleEnd, + m_xParagraphs->end()); + } + break; + } + case TEXT_HINT_VIEWSELECTIONCHANGED: + { + ::osl::MutexGuard aInternalGuard(GetMutex()); + if (!isAlive()) + break; + + if (m_aParagraphNotifications.empty()) + { + handleSelectionChangeNotification(); + } + else + { + // TEXT_HINT_VIEWSELECTIONCHANGED is sometimes sent at + // "unsafe" times (when the text engine has not yet re- + // formatted its content), so that for example calling + // ::TextEngine::GetTextHeight from within the code that + // handles a previous TEXT_HINT_PARAINSERTED causes + // trouble within the text engine. Therefore, these + // hints are just buffered (along with + // TEXT_HINT_PARAINSERTED/REMOVED/FORMATPARA) until a + // following ::TextEngine::FormatDoc causes a + // TEXT_HINT_TEXTFORMATTED to come in: + m_bSelectionChangedNotification = true; + } + break; + } + } + } +} + +IMPL_LINK(Document, WindowEventHandler, ::VclSimpleEvent *, pEvent) +{ + switch (pEvent->GetId()) + { + case VCLEVENT_WINDOW_RESIZE: + { + ::osl::MutexGuard aInternalGuard(GetMutex()); + if (!isAlive()) + break; + + ::sal_Int32 nHeight = static_cast< ::sal_Int32 >( + m_rView.GetWindow()->GetOutputSizePixel().Height()); + // XXX numeric overflow + if (nHeight != m_nViewHeight) + { + m_nViewHeight = nHeight; + + Paragraphs::iterator aOldVisibleBegin(m_aVisibleBegin); + Paragraphs::iterator aOldVisibleEnd(m_aVisibleEnd); + + determineVisibleRange(); + + notifyVisibleRangeChanges(aOldVisibleBegin, aOldVisibleEnd, + m_xParagraphs->end()); + } + break; + } + case VCLEVENT_WINDOW_GETFOCUS: + { + ::osl::MutexGuard aInternalGuard(GetMutex()); + if (!isAlive()) + break; + + if (m_aFocused >= m_aVisibleBegin && m_aFocused < m_aVisibleEnd) + { + ::rtl::Reference< ParagraphImpl > xParagraph( + getParagraph(m_aFocused)); + if (xParagraph.is()) + xParagraph->notifyEvent( + ::css::accessibility::AccessibleEventId:: + STATE_CHANGED, + ::css::uno::Any(), + ::css::uno::makeAny( + ::css::accessibility::AccessibleStateType:: + FOCUSED)); + } + break; + } + case VCLEVENT_WINDOW_LOSEFOCUS: + { + ::osl::MutexGuard aInternalGuard(GetMutex()); + if (!isAlive()) + break; + + if (m_aFocused >= m_aVisibleBegin && m_aFocused < m_aVisibleEnd) + { + ::rtl::Reference< ParagraphImpl > xParagraph( + getParagraph(m_aFocused)); + if (xParagraph.is()) + xParagraph->notifyEvent( + ::css::accessibility::AccessibleEventId:: + STATE_CHANGED, + ::css::uno::makeAny( + ::css::accessibility::AccessibleStateType:: + FOCUSED), + ::css::uno::Any()); + } + break; + } + } + return 0; +} + +void Document::init() +{ + if (m_xParagraphs.get() == 0) + { + ::ULONG nCount = m_rEngine.GetParagraphCount(); + ::std::auto_ptr< Paragraphs > p(new Paragraphs); + p->reserve(static_cast< Paragraphs::size_type >(nCount)); + // numeric overflow is harmless here + for (::ULONG i = 0; i < nCount; ++i) + p->push_back(ParagraphInfo(static_cast< ::sal_Int32 >( + m_rEngine.GetTextHeight(i)))); + // XXX numeric overflow + m_nViewOffset = static_cast< ::sal_Int32 >( + m_rView.GetStartDocPos().Y()); // XXX numeric overflow + m_nViewHeight = static_cast< ::sal_Int32 >( + m_rView.GetWindow()->GetOutputSizePixel().Height()); + // XXX numeric overflow + m_xParagraphs = p; + determineVisibleRange(); + m_nSelectionFirstPara = -1; + m_nSelectionFirstPos = -1; + m_nSelectionLastPara = -1; + m_nSelectionLastPos = -1; + m_aFocused = m_xParagraphs->end(); + m_bSelectionChangedNotification = false; + m_aEngineListener.startListening(m_rEngine); + m_aViewListener.startListening(*m_rView.GetWindow()); + } +} + +::rtl::Reference< ParagraphImpl > +Document::getParagraph(Paragraphs::iterator const & rIt) +{ + return static_cast< ParagraphImpl * >( + ::css::uno::Reference< ::css::accessibility::XAccessible >( + rIt->getParagraph()).get()); +} + +::css::uno::Reference< ::css::accessibility::XAccessible > +Document::getAccessibleChild(Paragraphs::iterator const & rIt) +{ + ::css::uno::Reference< ::css::accessibility::XAccessible > xParagraph( + rIt->getParagraph()); + if (!xParagraph.is()) + { + xParagraph = new Paragraph(this, rIt - m_xParagraphs->begin()); + rIt->setParagraph(xParagraph); + } + return xParagraph; +} + +void Document::determineVisibleRange() +{ + m_aVisibleBegin = m_xParagraphs->end(); + m_aVisibleEnd = m_aVisibleBegin; + ::sal_Int32 nPos = 0; + for (Paragraphs::iterator aIt = m_xParagraphs->begin();;) + { + if (aIt == m_xParagraphs->end()) + { + m_nVisibleBeginOffset = 0; + break; + } + ::sal_Int32 nOldPos = nPos; + nPos += aIt->getHeight(); // XXX numeric overflow + if (m_aVisibleBegin == m_xParagraphs->end() && nPos >= m_nViewOffset) + { + m_aVisibleBegin = aIt; + m_nVisibleBeginOffset = m_nViewOffset - nOldPos; + } + ++aIt; + if (m_aVisibleBegin != m_xParagraphs->end() + && (aIt == m_xParagraphs->end() + || nPos >= m_nViewOffset + m_nViewHeight)) + // XXX numeric overflow + { + m_aVisibleEnd = aIt; + break; + } + } +} + +void Document::notifyVisibleRangeChanges( + Paragraphs::iterator const & rOldVisibleBegin, + Paragraphs::iterator const & rOldVisibleEnd, + Paragraphs::iterator const & rInserted) +{ + // XXX Replace this code that determines which paragraphs have changed from + // invisible to visible or vice versa with a better algorithm. + {for (Paragraphs::iterator aIt(rOldVisibleBegin); aIt != rOldVisibleEnd; + ++aIt) + if (aIt != rInserted + && (aIt < m_aVisibleBegin || aIt >= m_aVisibleEnd)) + NotifyAccessibleEvent( + ::css::accessibility::AccessibleEventId:: + CHILD, + ::css::uno::makeAny(getAccessibleChild(aIt)), + ::css::uno::Any()); + } + {for (Paragraphs::iterator aIt(m_aVisibleBegin); aIt != m_aVisibleEnd; + ++aIt) + if (aIt == rInserted + || aIt < rOldVisibleBegin || aIt >= rOldVisibleEnd) + NotifyAccessibleEvent( + ::css::accessibility::AccessibleEventId:: + CHILD, + ::css::uno::Any(), + ::css::uno::makeAny(getAccessibleChild(aIt))); + } +} + +void +Document::changeParagraphText(::ULONG nNumber, ::USHORT nBegin, ::USHORT nEnd, + bool bCut, bool bPaste, + ::rtl::OUString const & rText) +{ + m_rView.SetSelection(::TextSelection(::TextPaM(nNumber, nBegin), + ::TextPaM(nNumber, nEnd))); + if (bCut) + m_rView.Cut(); + else if (nBegin != nEnd) + m_rView.DeleteSelected(); + if (bPaste) + m_rView.Paste(); + else if (rText.getLength() != 0) + m_rView.InsertText(rText); +} + +void Document::handleParagraphNotifications() +{ + while (!m_aParagraphNotifications.empty()) + { + ::TextHint aHint(m_aParagraphNotifications.front()); + m_aParagraphNotifications.pop(); + switch (aHint.GetId()) + { + case TEXT_HINT_PARAINSERTED: + { + ::ULONG n = aHint.GetValue(); + OSL_ENSURE(n <= m_xParagraphs->size(), + "bad TEXT_HINT_PARAINSERTED event"); + + // Save the values of old iterators (the iterators themselves + // will get invalidated), and adjust the old values so that they + // reflect the insertion of the new paragraph: + Paragraphs::size_type nOldVisibleBegin + = m_aVisibleBegin - m_xParagraphs->begin(); + Paragraphs::size_type nOldVisibleEnd + = m_aVisibleEnd - m_xParagraphs->begin(); + Paragraphs::size_type nOldFocused + = m_aFocused - m_xParagraphs->begin(); + if (n <= nOldVisibleBegin) + ++nOldVisibleBegin; // XXX numeric overflow + if (n <= nOldVisibleEnd) + ++nOldVisibleEnd; // XXX numeric overflow + if (n <= nOldFocused) + ++nOldFocused; // XXX numeric overflow + if (sal::static_int_cast<sal_Int32>(n) <= m_nSelectionFirstPara) + ++m_nSelectionFirstPara; // XXX numeric overflow + if (sal::static_int_cast<sal_Int32>(n) <= m_nSelectionLastPara) + ++m_nSelectionLastPara; // XXX numeric overflow + + Paragraphs::iterator aIns( + m_xParagraphs->insert( + m_xParagraphs->begin() + n, + ParagraphInfo(static_cast< ::sal_Int32 >( + m_rEngine.GetTextHeight(n))))); + // XXX numeric overflow (2x) + + determineVisibleRange(); + m_aFocused = m_xParagraphs->begin() + nOldFocused; + + for (Paragraphs::iterator aIt(aIns);;) + { + ++aIt; + if (aIt == m_xParagraphs->end()) + break; + ::rtl::Reference< ParagraphImpl > xParagraph( + getParagraph(aIt)); + if (xParagraph.is()) + xParagraph->numberChanged(true); + } + + notifyVisibleRangeChanges( + m_xParagraphs->begin() + nOldVisibleBegin, + m_xParagraphs->begin() + nOldVisibleEnd, aIns); + break; + } + case TEXT_HINT_PARAREMOVED: + { + ::ULONG n = aHint.GetValue(); + if (n == TEXT_PARA_ALL) + { + {for (Paragraphs::iterator aIt(m_aVisibleBegin); + aIt != m_aVisibleEnd; ++aIt) + NotifyAccessibleEvent( + ::css::accessibility::AccessibleEventId:: + CHILD, + ::css::uno::makeAny(getAccessibleChild(aIt)), + ::css::uno::Any()); + } + disposeParagraphs(); + m_xParagraphs->clear(); + determineVisibleRange(); + m_nSelectionFirstPara = -1; + m_nSelectionFirstPos = -1; + m_nSelectionLastPara = -1; + m_nSelectionLastPos = -1; + m_aFocused = m_xParagraphs->end(); + } + else + { + OSL_ENSURE(n < m_xParagraphs->size(), + "Bad TEXT_HINT_PARAREMOVED event"); + + Paragraphs::iterator aIt(m_xParagraphs->begin() + n); + // numeric overflow cannot occur + + // Save the values of old iterators (the iterators + // themselves will get invalidated), and adjust the old + // values so that they reflect the removal of the paragraph: + Paragraphs::size_type nOldVisibleBegin + = m_aVisibleBegin - m_xParagraphs->begin(); + Paragraphs::size_type nOldVisibleEnd + = m_aVisibleEnd - m_xParagraphs->begin(); + bool bWasVisible + = nOldVisibleBegin <= n && n < nOldVisibleEnd; + Paragraphs::size_type nOldFocused + = m_aFocused - m_xParagraphs->begin(); + bool bWasFocused = aIt == m_aFocused; + if (n < nOldVisibleBegin) + --nOldVisibleBegin; + if (n < nOldVisibleEnd) + --nOldVisibleEnd; + if (n < nOldFocused) + --nOldFocused; + if (sal::static_int_cast<sal_Int32>(n) < m_nSelectionFirstPara) + --m_nSelectionFirstPara; + else if (sal::static_int_cast<sal_Int32>(n) == m_nSelectionFirstPara) + if (m_nSelectionFirstPara == m_nSelectionLastPara) + { + m_nSelectionFirstPara = -1; + m_nSelectionFirstPos = -1; + m_nSelectionLastPara = -1; + m_nSelectionLastPos = -1; + } + else + { + ++m_nSelectionFirstPara; + m_nSelectionFirstPos = 0; + } + if (sal::static_int_cast<sal_Int32>(n) < m_nSelectionLastPara) + --m_nSelectionLastPara; + else if (sal::static_int_cast<sal_Int32>(n) == m_nSelectionLastPara) + { + OSL_ENSURE(m_nSelectionFirstPara < m_nSelectionLastPara, + "logic error"); + --m_nSelectionLastPara; + m_nSelectionLastPos = 0x7FFFFFFF; + } + + ::css::uno::Reference< ::css::accessibility::XAccessible > + xStrong; + if (bWasVisible) + xStrong = getAccessibleChild(aIt); + ::css::uno::WeakReference< + ::css::accessibility::XAccessible > xWeak( + aIt->getParagraph()); + aIt = m_xParagraphs->erase(aIt); + + determineVisibleRange(); + m_aFocused = bWasFocused ? m_xParagraphs->end() + : m_xParagraphs->begin() + nOldFocused; + + for (; aIt != m_xParagraphs->end(); ++aIt) + { + ::rtl::Reference< ParagraphImpl > xParagraph( + getParagraph(aIt)); + if (xParagraph.is()) + xParagraph->numberChanged(false); + } + + if (bWasVisible) + NotifyAccessibleEvent( + ::css::accessibility::AccessibleEventId:: + CHILD, + ::css::uno::makeAny(getAccessibleChild(aIt)), + ::css::uno::Any()); + + ::css::uno::Reference< ::css::lang::XComponent > xComponent( + xWeak.get(), ::css::uno::UNO_QUERY); + if (xComponent.is()) + xComponent->dispose(); + + notifyVisibleRangeChanges( + m_xParagraphs->begin() + nOldVisibleBegin, + m_xParagraphs->begin() + nOldVisibleEnd, + m_xParagraphs->end()); + } + break; + } + case TEXT_HINT_FORMATPARA: + { + ::ULONG n = aHint.GetValue(); + OSL_ENSURE(n < m_xParagraphs->size(), + "Bad TEXT_HINT_FORMATPARA event"); + + (*m_xParagraphs)[static_cast< Paragraphs::size_type >(n)]. + changeHeight(static_cast< ::sal_Int32 >( + m_rEngine.GetTextHeight(n))); + // XXX numeric overflow + Paragraphs::iterator aOldVisibleBegin(m_aVisibleBegin); + Paragraphs::iterator aOldVisibleEnd(m_aVisibleEnd); + determineVisibleRange(); + notifyVisibleRangeChanges(aOldVisibleBegin, aOldVisibleEnd, + m_xParagraphs->end()); + Paragraphs::iterator aIt(m_xParagraphs->begin() + n); + ::rtl::Reference< ParagraphImpl > xParagraph(getParagraph(aIt)); + if (xParagraph.is()) + xParagraph->textChanged(); + break; + } + default: + OSL_ENSURE(false, "bad buffered hint"); + break; + } + } + if (m_bSelectionChangedNotification) + { + m_bSelectionChangedNotification = false; + handleSelectionChangeNotification(); + } +} + +void Document::handleSelectionChangeNotification() +{ + ::TextSelection const & rSelection = m_rView.GetSelection(); + OSL_ENSURE(rSelection.GetStart().GetPara() < m_xParagraphs->size() + && rSelection.GetEnd().GetPara() < m_xParagraphs->size(), + "bad TEXT_HINT_VIEWSELECTIONCHANGED event"); + ::sal_Int32 nNewFirstPara + = static_cast< ::sal_Int32 >(rSelection.GetStart().GetPara()); + ::sal_Int32 nNewFirstPos + = static_cast< ::sal_Int32 >(rSelection.GetStart().GetIndex()); + // XXX numeric overflow + ::sal_Int32 nNewLastPara + = static_cast< ::sal_Int32 >(rSelection.GetEnd().GetPara()); + ::sal_Int32 nNewLastPos + = static_cast< ::sal_Int32 >(rSelection.GetEnd().GetIndex()); + // XXX numeric overflow + + // Lose focus: + Paragraphs::iterator aIt(m_xParagraphs->begin() + nNewLastPara); + if (m_aFocused != m_xParagraphs->end() && m_aFocused != aIt + && m_aFocused >= m_aVisibleBegin && m_aFocused < m_aVisibleEnd) + { + ::rtl::Reference< ParagraphImpl > xParagraph(getParagraph(m_aFocused)); + if (xParagraph.is()) + xParagraph->notifyEvent( + ::css::accessibility::AccessibleEventId:: + STATE_CHANGED, + ::css::uno::makeAny( + ::css::accessibility::AccessibleStateType::FOCUSED), + ::css::uno::Any()); + } + + // Gain focus and update cursor position: + if (aIt >= m_aVisibleBegin && aIt < m_aVisibleEnd + && (aIt != m_aFocused + || nNewLastPara != m_nSelectionLastPara + || nNewLastPos != m_nSelectionLastPos)) + { + ::rtl::Reference< ParagraphImpl > xParagraph(getParagraph(aIt)); + if (xParagraph.is()) + { + if (aIt != m_aFocused) + xParagraph->notifyEvent( + ::css::accessibility::AccessibleEventId:: + STATE_CHANGED, + ::css::uno::Any(), + ::css::uno::makeAny( + ::css::accessibility::AccessibleStateType::FOCUSED)); + if (nNewLastPara != m_nSelectionLastPara + || nNewLastPos != m_nSelectionLastPos) + xParagraph->notifyEvent( + ::css::accessibility::AccessibleEventId:: + CARET_CHANGED, + ::css::uno::makeAny< ::sal_Int32 >( + nNewLastPara == m_nSelectionLastPara + ? m_nSelectionLastPos : 0), + ::css::uno::makeAny(nNewLastPos)); + } + } + m_aFocused = aIt; + + // Update both old and new selection. (Regardless of how the two selections + // look like, there will always be two ranges to the left and right of the + // overlap---the overlap and/or the range to the right of it possibly being + // empty. Only for these two ranges notifications have to be sent.) + + TextPaM aOldTextStart( static_cast< ULONG >( m_nSelectionFirstPara ), static_cast< USHORT >( m_nSelectionFirstPos ) ); + TextPaM aOldTextEnd( static_cast< ULONG >( m_nSelectionLastPara ), static_cast< USHORT >( m_nSelectionLastPos ) ); + TextPaM aNewTextStart( static_cast< ULONG >( nNewFirstPara ), static_cast< USHORT >( nNewFirstPos ) ); + TextPaM aNewTextEnd( static_cast< ULONG >( nNewLastPara ), static_cast< USHORT >( nNewLastPos ) ); + + // justify selections + justifySelection( aOldTextStart, aOldTextEnd ); + justifySelection( aNewTextStart, aNewTextEnd ); + + sal_Int32 nFirst1; + sal_Int32 nLast1; + sal_Int32 nFirst2; + sal_Int32 nLast2; + + if ( m_nSelectionFirstPara == -1 ) + { + // old selection not initialized yet => notify events only for new selection (if not empty) + nFirst1 = aNewTextStart.GetPara(); + nLast1 = aNewTextEnd.GetPara() + ( aNewTextStart != aNewTextEnd ? 1 : 0 ); + nFirst2 = 0; + nLast2 = 0; + } + else if ( aOldTextStart == aOldTextEnd && aNewTextStart == aNewTextEnd ) + { + // old an new selection empty => no events + nFirst1 = 0; + nLast1 = 0; + nFirst2 = 0; + nLast2 = 0; + } + else if ( aOldTextStart != aOldTextEnd && aNewTextStart == aNewTextEnd ) + { + // old selection not empty + new selection empty => notify events only for old selection + nFirst1 = aOldTextStart.GetPara(); + nLast1 = aOldTextEnd.GetPara() + 1; + nFirst2 = 0; + nLast2 = 0; + } + else if ( aOldTextStart == aOldTextEnd && aNewTextStart != aNewTextEnd ) + { + // old selection empty + new selection not empty => notify events only for new selection + nFirst1 = aNewTextStart.GetPara(); + nLast1 = aNewTextEnd.GetPara() + 1; + nFirst2 = 0; + nLast2 = 0; + } + else + { + // old and new selection not empty => notify events for the two ranges left and right of the overlap + ::std::vector< TextPaM > aTextPaMs(4); + aTextPaMs[0] = aOldTextStart; + aTextPaMs[1] = aOldTextEnd; + aTextPaMs[2] = aNewTextStart; + aTextPaMs[3] = aNewTextEnd; + ::std::sort( aTextPaMs.begin(), aTextPaMs.end() ); + + nFirst1 = aTextPaMs[0].GetPara(); + nLast1 = aTextPaMs[1].GetPara() + ( aTextPaMs[0] != aTextPaMs[1] ? 1 : 0 ); + + nFirst2 = aTextPaMs[2].GetPara(); + nLast2 = aTextPaMs[3].GetPara() + ( aTextPaMs[2] != aTextPaMs[3] ? 1 : 0 ); + + // adjust overlapping ranges + if ( nLast1 > nFirst2 ) + nLast1 = nFirst2; + } + + // notify selection changes + notifySelectionChange( nFirst1, nLast1 ); + notifySelectionChange( nFirst2, nLast2 ); + + m_nSelectionFirstPara = nNewFirstPara; + m_nSelectionFirstPos = nNewFirstPos; + m_nSelectionLastPara = nNewLastPara; + m_nSelectionLastPos = nNewLastPos; +} + +void Document::notifySelectionChange( sal_Int32 nFirst, sal_Int32 nLast ) +{ + if ( nFirst < nLast ) + { + Paragraphs::iterator aEnd( ::std::min( m_xParagraphs->begin() + nLast, m_aVisibleEnd ) ); + for ( Paragraphs::iterator aIt = ::std::max( m_xParagraphs->begin() + nFirst, m_aVisibleBegin ); aIt < aEnd; ++aIt ) + { + ::rtl::Reference< ParagraphImpl > xParagraph( getParagraph( aIt ) ); + if ( xParagraph.is() ) + { + xParagraph->notifyEvent( + ::css::accessibility::AccessibleEventId::SELECTION_CHANGED, + ::css::uno::Any(), ::css::uno::Any() ); + xParagraph->notifyEvent( + ::css::accessibility::AccessibleEventId::TEXT_SELECTION_CHANGED, + ::css::uno::Any(), ::css::uno::Any() ); + } + } + } +} + +void Document::justifySelection( TextPaM& rTextStart, TextPaM& rTextEnd ) +{ + if ( rTextStart > rTextEnd ) + { + TextPaM aTextPaM( rTextStart ); + rTextStart = rTextEnd; + rTextEnd = aTextPaM; + } +} + +void Document::disposeParagraphs() +{ + for (Paragraphs::iterator aIt(m_xParagraphs->begin()); + aIt != m_xParagraphs->end(); ++aIt) + { + ::css::uno::Reference< ::css::lang::XComponent > xComponent( + aIt->getParagraph().get(), ::css::uno::UNO_QUERY); + if (xComponent.is()) + xComponent->dispose(); + } +} + +// static +::css::uno::Any Document::mapFontColor(::Color const & rColor) +{ + return ::css::uno::makeAny( + static_cast< ::sal_Int32 >(COLORDATA_RGB(rColor.GetColor()))); + // FIXME keep transparency? +} + +// static +::Color Document::mapFontColor(::css::uno::Any const & rColor) +{ + ::sal_Int32 nColor = 0; + rColor >>= nColor; + return ::Color(static_cast< ::ColorData >(nColor)); +} + +// static +::css::uno::Any Document::mapFontWeight(::FontWeight nWeight) +{ + // Map from ::FontWeight to ::css:awt::FontWeight, depends on order of + // elements in ::FontWeight (vcl/vclenum.hxx): + static float const aWeight[] + = { ::css::awt::FontWeight::DONTKNOW, // WEIGHT_DONTKNOW + ::css::awt::FontWeight::THIN, // WEIGHT_THIN + ::css::awt::FontWeight::ULTRALIGHT, // WEIGHT_ULTRALIGHT + ::css::awt::FontWeight::LIGHT, // WEIGHT_LIGHT + ::css::awt::FontWeight::SEMILIGHT, // WEIGHT_SEMILIGHT + ::css::awt::FontWeight::NORMAL, // WEIGHT_NORMAL + ::css::awt::FontWeight::NORMAL, // WEIGHT_MEDIUM + ::css::awt::FontWeight::SEMIBOLD, // WEIGHT_SEMIBOLD + ::css::awt::FontWeight::BOLD, // WEIGHT_BOLD + ::css::awt::FontWeight::ULTRABOLD, // WEIGHT_ULTRABOLD + ::css::awt::FontWeight::BLACK }; // WEIGHT_BLACK + return ::css::uno::makeAny(aWeight[nWeight]); +} + +// static +::FontWeight Document::mapFontWeight(::css::uno::Any const & rWeight) +{ + float nWeight = ::css::awt::FontWeight::NORMAL; + rWeight >>= nWeight; + return nWeight <= ::css::awt::FontWeight::DONTKNOW ? WEIGHT_DONTKNOW + : nWeight <= ::css::awt::FontWeight::THIN ? WEIGHT_THIN + : nWeight <= ::css::awt::FontWeight::ULTRALIGHT ? WEIGHT_ULTRALIGHT + : nWeight <= ::css::awt::FontWeight::LIGHT ? WEIGHT_LIGHT + : nWeight <= ::css::awt::FontWeight::SEMILIGHT ? WEIGHT_SEMILIGHT + : nWeight <= ::css::awt::FontWeight::NORMAL ? WEIGHT_NORMAL + : nWeight <= ::css::awt::FontWeight::SEMIBOLD ? WEIGHT_SEMIBOLD + : nWeight <= ::css::awt::FontWeight::BOLD ? WEIGHT_BOLD + : nWeight <= ::css::awt::FontWeight::ULTRABOLD ? WEIGHT_ULTRABOLD + : WEIGHT_BLACK; +} + +} + |