/**************************************************************
 * 
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 * 
 *   http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 * 
 *************************************************************/



// 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
#include "comphelper/accessibleeventnotifier.hxx"
#include "unotools/accessiblerelationsethelper.hxx"
#include <unotools/accessiblestatesethelper.hxx>
#include <vcl/window.hxx>
#include <toolkit/helper/convert.hxx>

#include <algorithm>
#include <vector>
#include <hash_map>

namespace css = ::com::sun::star;

namespace accessibility
{
    ::sal_Int32 getSelectionType(::sal_Int32 nNewFirstPara, ::sal_Int32 nNewFirstPos, ::sal_Int32 nNewLastPara, ::sal_Int32 nNewLastPos);
    void sendEvent(::sal_Int32 start, ::sal_Int32 end, ::sal_Int16 nEventId);

// 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)
{
    m_aParagraphText = m_xDocument->retrieveParagraphText(this);
}

void
ParagraphImpl::numberChanged(bool bIncremented)
{
    if (bIncremented)
        ++m_nNumber;
    else
        --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);
    }
}

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(
                "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 ::rtl::OUString();
}

// virtual
::rtl::OUString SAL_CALL ParagraphImpl::getAccessibleName()
    throw (::css::uno::RuntimeException)
{
    checkDisposed();
    return ::rtl::OUString();
}

// 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();
    Window* pWindow = m_xDocument->GetWindow();
    if ( pWindow )
    {
        pWindow->GrabFocus();
    }
    try
    {
        m_xDocument->changeParagraphSelection(this, 0, 0);
    }
    catch (::css::lang::IndexOutOfBoundsException & rEx)
    {
        OSL_TRACE(
            "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 
::sal_Int32 SAL_CALL ParagraphImpl::getLineNumberAtIndex( ::sal_Int32 nIndex ) 
    throw (::css::lang::IndexOutOfBoundsException, 
           ::css::uno::RuntimeException)
{
    checkDisposed();
    
    ::sal_Int32 nLineNo = -1;
    ::css::i18n::Boundary aBoundary = 
        m_xDocument->retrieveParagraphLineBoundary( this, nIndex, &nLineNo );
        
    return nLineNo;
}
    
// virtual 
::css::accessibility::TextSegment SAL_CALL ParagraphImpl::getTextAtLineNumber( ::sal_Int32 nLineNo ) 
    throw (::css::lang::IndexOutOfBoundsException,
           ::css::uno::RuntimeException)
{
    checkDisposed();
    
    ::css::i18n::Boundary aBoundary = 
        m_xDocument->retrieveParagraphBoundaryOfLine( this, nLineNo );
    
    return ::css::accessibility::TextSegment( getTextRange(aBoundary.startPos, aBoundary.endPos),
        aBoundary.startPos, aBoundary.endPos);
}

// virtual
::css::accessibility::TextSegment SAL_CALL ParagraphImpl::getTextAtLineWithCaret(  ) 
    throw (::css::uno::RuntimeException)
{
    checkDisposed();
    
    sal_Int32 nLineNo = getNumberOfLineWithCaret();
    
    try {
        return ( nLineNo >= 0 ) ? 
            getTextAtLineNumber( nLineNo ) : 
            ::css::accessibility::TextSegment();
    } catch (const ::css::lang::IndexOutOfBoundsException&) {
        throw ::css::uno::RuntimeException(
            ::rtl::OUString(
                RTL_CONSTASCII_USTRINGPARAM(
                    "textwindowaccessibility.cxx:"
                    " ParagraphImpl::getTextAtLineWithCaret") ),
            static_cast< ::css::uno::XWeak * >( this ) );
    }
}
    
// virtual
::sal_Int32 SAL_CALL ParagraphImpl::getNumberOfLineWithCaret(  ) 
    throw (::css::uno::RuntimeException)
{
    checkDisposed();
    return m_xDocument->retrieveParagraphLineWithCursor(this);
}


// 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));
}

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();
}

::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_nViewOffset,
        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< ::sal_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< ::sal_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());
    ::sal_uLong nNumber = static_cast< ::sal_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(
                    "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< ::sal_uInt16 >(nIndex))));
    }
    else
    {
        ::Rectangle aLeft(
            m_rEngine.PaMtoEditCursor(::TextPaM(nNumber,
                                                static_cast< ::sal_uInt16 >(nIndex))));
            // XXX  numeric overflow
        ::Rectangle aRight(
            m_rEngine.PaMtoEditCursor(::TextPaM(nNumber,
                                                static_cast< ::sal_uInt16 >(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() - m_nViewOffset),
                                        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());
    ::sal_uLong nNumber = static_cast< ::sal_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
}

struct IndexCompare
{
	const ::css::beans::PropertyValue* pValues;
	IndexCompare( const ::css::beans::PropertyValue* pVals ) : pValues(pVals) {}
	bool operator() ( const sal_Int32& a, const sal_Int32& b ) const
	{
		return (pValues[a].Name < pValues[b].Name) ? true : false;
	}
};

::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());

	Font aFont = m_rEngine.GetFont();
	const sal_Int32 AttributeCount = 9;
	sal_Int32 i = 0;
	::css::uno::Sequence< ::css::beans::PropertyValue > aAttribs( AttributeCount );
	//character background color
	{
		aAttribs[i].Name = ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("CharBackColor"));
		aAttribs[i].Handle = -1;
		aAttribs[i].Value = mapFontColor( aFont.GetFillColor() );
		aAttribs[i].State = ::css::beans::PropertyState_DIRECT_VALUE;
		i++;
	}
	//character color
	{
		aAttribs[i].Name = ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("CharColor"));
		aAttribs[i].Handle = -1;
		//aAttribs[i].Value = mapFontColor( aFont.GetColor() );
		aAttribs[i].Value = mapFontColor( m_rEngine.GetTextColor() );
		aAttribs[i].State = ::css::beans::PropertyState_DIRECT_VALUE;
		i++;
	}
	//character font name
	{
		aAttribs[i].Name = ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("CharFontName"));
		aAttribs[i].Handle = -1;
		aAttribs[i].Value = ::css::uno::makeAny( (::rtl::OUString)aFont.GetName() );
		aAttribs[i].State = ::css::beans::PropertyState_DIRECT_VALUE;
		i++;
	}
	//character height
	{
		aAttribs[i].Name = ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("CharHeight"));
		aAttribs[i].Handle = -1;
		aAttribs[i].Value = ::css::uno::makeAny( (sal_Int16)aFont.GetHeight() );
		aAttribs[i].State = ::css::beans::PropertyState_DIRECT_VALUE;
		i++;
	}
	//character posture
	{
		aAttribs[i].Name = ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("CharPosture"));
		aAttribs[i].Handle = -1;
		aAttribs[i].Value = ::css::uno::makeAny( (sal_Int16)aFont.GetItalic() );
		aAttribs[i].State = ::css::beans::PropertyState_DIRECT_VALUE;
		i++;
	}
	//character relief
	/*{
		aAttribs[i].Name = ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("CharRelief"));
		aAttribs[i].Handle = -1;
		aAttribs[i].Value = ::css::uno::makeAny( (sal_Int16)aFont.GetRelief() );
		aAttribs[i].State = ::css::beans::PropertyState_DIRECT_VALUE;
		i++;
	}*/
	//character strikeout
	{
		aAttribs[i].Name = ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("CharStrikeout"));
		aAttribs[i].Handle = -1;
		aAttribs[i].Value = ::css::uno::makeAny( (sal_Int16)aFont.GetStrikeout() );
		aAttribs[i].State = ::css::beans::PropertyState_DIRECT_VALUE;
		i++;
	}
	//character underline
	{
		aAttribs[i].Name = ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("CharUnderline"));
		aAttribs[i].Handle = -1;
		aAttribs[i].Value = ::css::uno::makeAny( (sal_Int16)aFont.GetUnderline() );
		aAttribs[i].State = ::css::beans::PropertyState_DIRECT_VALUE;
		i++;
	}
	//character weight
	{
		aAttribs[i].Name = ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("CharWeight"));
		aAttribs[i].Handle = -1;
		aAttribs[i].Value = ::css::uno::makeAny( (float)aFont.GetWeight() );
		aAttribs[i].State = ::css::beans::PropertyState_DIRECT_VALUE;
		i++;
	}
	//character alignment
	{
		aAttribs[i].Name = ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("ParaAdjust"));
		aAttribs[i].Handle = -1;
		aAttribs[i].Value = ::css::uno::makeAny( (sal_Int16)m_rEngine.GetTextAlign() );
		aAttribs[i].State = ::css::beans::PropertyState_DIRECT_VALUE;
		i++;
	}
    ::osl::MutexGuard aInternalGuard(GetMutex());
    ::sal_uLong nNumber = static_cast< ::sal_uLong >(pParagraph->getNumber());
        // XXX  numeric overflow
	// nIndex can be equal to Len();
    //if (nIndex < 0 || nIndex >= m_rEngine.GetText(nNumber).Len())
	if (nIndex < 0 || nIndex > m_rEngine.GetText(nNumber).Len())
        throw ::css::lang::IndexOutOfBoundsException(
            ::rtl::OUString(
                RTL_CONSTASCII_USTRINGPARAM(
                    "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;
    }

	::css::beans::PropertyValue* pValues = aAttribs.getArray();
	for (i = 0; i < AttributeCount; i++,pValues++)
	{
		aCharAttrSeq[ pValues->Name ] = *pValues;
	}

    ::css::uno::Sequence< ::css::beans::PropertyValue > aRes = convertHashMapToSequence( aCharAttrSeq );

	// sort the attributes
	sal_Int32 nLength = aRes.getLength();
	const ::css::beans::PropertyValue* pPairs = aRes.getConstArray();
	sal_Int32* pIndices = new sal_Int32[nLength];
	for( i = 0; i < nLength; i++ )
		pIndices[i] = i;
	std::sort( &pIndices[0], &pIndices[nLength], IndexCompare(pPairs) );
	// create sorted sequences accoring to index array
	::css::uno::Sequence< ::css::beans::PropertyValue > aNewValues( nLength );
	::css::beans::PropertyValue* pNewValues = aNewValues.getArray();
	for( i = 0; i < nLength; i++ )
	{
		pNewValues[i] = pPairs[pIndices[i]];
	}
	delete[] pIndices;

	return aNewValues;
}

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)
{
    ::sal_uLong nNumber = static_cast< ::sal_uLong >( pParagraph->getNumber() );
    ::TextPaM aPaM( nNumber, static_cast< ::sal_uInt16 >( 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() );
    ::sal_uLong nNumber = static_cast< ::sal_uLong >( pParagraph->getNumber() );
        // XXX  numeric overflow
    if ( Index < 0 || Index >= m_rEngine.GetText(nNumber).Len() )
        throw ::css::lang::IndexOutOfBoundsException(
            ::rtl::OUString(
                RTL_CONSTASCII_USTRINGPARAM(
                    "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());
        ::sal_uLong nNumber = static_cast< ::sal_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());
        ::sal_uLong nNumber = static_cast< ::sal_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(
                        "textwindowaccessibility.cxx:"
                        " Document::changeParagraphText")),
                static_cast< ::css::uno::XWeak * >(this));
        changeParagraphText(nNumber, static_cast< ::sal_uInt16 >(nBegin),
                            static_cast< ::sal_uInt16 >(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());
        ::sal_uLong nNumber = static_cast< ::sal_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(
                                    "textwindowaccessibility.cxx:"
                                    " Document::copyParagraphText")),
                static_cast< ::css::uno::XWeak * >(this));
        m_rView.SetSelection(
            ::TextSelection(::TextPaM(nNumber, static_cast< ::sal_uInt16 >(nBegin)),
                            ::TextPaM(nNumber, static_cast< ::sal_uInt16 >(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());
        ::sal_uLong nNumber = static_cast< ::sal_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(
                        "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< ::sal_uInt16 >(nBegin),
                                    static_cast< ::sal_uInt16 >(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< ::sal_uInt16 >(nBegin),
                                    static_cast< ::sal_uInt16 >(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());
        ::sal_uLong nNumber = static_cast< ::sal_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(
                                    "textwindowaccessibility.cxx:"
                                    " Document::changeParagraphSelection")),
                static_cast< ::css::uno::XWeak * >(this));
        m_rView.SetSelection(
            ::TextSelection(::TextPaM(nNumber, static_cast< ::sal_uInt16 >(nBegin)),
                            ::TextPaM(nNumber, static_cast< ::sal_uInt16 >(nEnd))));
            // XXX  numeric overflow (2x)
    }
}

::css::i18n::Boundary
Document::retrieveParagraphLineBoundary( ParagraphImpl const * pParagraph,
                                         ::sal_Int32 nIndex, ::sal_Int32 *pLineNo )
{
    ::css::i18n::Boundary aBoundary;
    aBoundary.startPos = nIndex;
    aBoundary.endPos = nIndex;

    ::osl::Guard< ::comphelper::IMutex > aExternalGuard( getExternalLock() );
    {
        ::osl::MutexGuard aInternalGuard( GetMutex() );
        ::sal_uLong nNumber = static_cast< ::sal_uLong >( pParagraph->getNumber() );
        if ( nIndex < 0 || nIndex > m_rEngine.GetText( nNumber ).Len() )
            throw ::css::lang::IndexOutOfBoundsException(
                ::rtl::OUString(
                    RTL_CONSTASCII_USTRINGPARAM(
                        "textwindowaccessibility.cxx:"
                        " Document::retrieveParagraphLineBoundary" ) ),
                static_cast< ::css::uno::XWeak * >( this ) );
        ::sal_Int32 nLineStart = 0;
        ::sal_Int32 nLineEnd = 0;
        ::sal_uInt16 nLineCount = m_rEngine.GetLineCount( nNumber );
        for ( ::sal_uInt16 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;
                if( pLineNo )
                    pLineNo[0] = nLine;
                break;
            }
        }
    }

    return aBoundary;
}

::css::i18n::Boundary
Document::retrieveParagraphBoundaryOfLine( ParagraphImpl const * pParagraph,
                                           ::sal_Int32 nLineNo )
{
    ::css::i18n::Boundary aBoundary;
    aBoundary.startPos = 0;
    aBoundary.endPos = 0;

    ::osl::Guard< ::comphelper::IMutex > aExternalGuard( getExternalLock() );
    {
        ::osl::MutexGuard aInternalGuard( GetMutex() );
        ::sal_uLong nNumber = static_cast< ::sal_uLong >( pParagraph->getNumber() );
        if ( nLineNo >= m_rEngine.GetLineCount( nNumber ) )
            throw ::css::lang::IndexOutOfBoundsException(
                ::rtl::OUString(
                    RTL_CONSTASCII_USTRINGPARAM(
                        "textwindowaccessibility.cxx:"
                        " Document::retrieveParagraphBoundaryOfLine" ) ),
                static_cast< ::css::uno::XWeak * >( this ) );
        ::sal_Int32 nLineStart = 0;
        ::sal_Int32 nLineEnd = 0;
        for ( ::sal_uInt16 nLine = 0; nLine <= nLineNo; ++nLine )
        {
            ::sal_Int32 nLineLength = static_cast< ::sal_Int32 >( 
                m_rEngine.GetLineLen( nNumber, nLine ) );
            nLineStart = nLineEnd;
            nLineEnd += nLineLength;
        }
        
        aBoundary.startPos = nLineStart;
        aBoundary.endPos = nLineEnd;
    }

    return aBoundary;
}

sal_Int32 Document::retrieveParagraphLineWithCursor( 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 
        ? m_rView.GetLineNumberOfCursorInSelection() : -1;
}


::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 )
{
	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(
                    "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;
}
void Document::FillAccessibleStateSet( utl::AccessibleStateSetHelper& rStateSet )        
{
	VCLXAccessibleComponent::FillAccessibleStateSet( rStateSet );
	if (!m_rView.IsReadOnly())
		rStateSet.AddState( ::css::accessibility::AccessibleStateType::EDITABLE );
}

void	Document::FillAccessibleRelationSet( utl::AccessibleRelationSetHelper& rRelationSet )
{
	if( getAccessibleParent()->getAccessibleContext()->getAccessibleRole() == ::css::accessibility::AccessibleRole::SCROLL_PANE )
	{
		::css::uno::Sequence< ::css::uno::Reference< ::css::uno::XInterface > > aSequence(1);
		aSequence[0] = getAccessibleParent();
		rRelationSet.AddRelation( ::css::accessibility::AccessibleRelation( ::css::accessibility::AccessibleRelationType::MEMBER_OF, aSequence ) );		
	}
	else
	{
		 VCLXAccessibleComponent::FillAccessibleRelationSet(rRelationSet);
	}
}
// 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;
			//to enable the PARAGRAPH to get focus for multiline edit
			::sal_Int32 count = getAccessibleChildCount();
			::sal_Bool bEmpty = m_aFocused == m_aVisibleEnd && count == 1;
            if ((m_aFocused >= m_aVisibleBegin && m_aFocused < m_aVisibleEnd) || bEmpty)
            {
				Paragraphs::iterator m_aTemp = bEmpty ? m_aVisibleBegin : m_aFocused;
                ::rtl::Reference< ParagraphImpl > xParagraph(getParagraph(m_aTemp));
				if (xParagraph.is())
				{
					xParagraph->notifyEvent(
						::css::accessibility::AccessibleEventId::
						STATE_CHANGED,
						::css::uno::Any(),
						::css::uno::makeAny(
							::css::accessibility::AccessibleStateType::
							FOCUSED));
				}
            }
			/*
                ::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;
			//to enable the PARAGRAPH to get focus for multiline edit
			::sal_Int32 count = getAccessibleChildCount();
			::sal_Bool bEmpty = m_aFocused == m_aVisibleEnd && count == 1;
            if ((m_aFocused >= m_aVisibleBegin && m_aFocused < m_aVisibleEnd) || bEmpty)
            {
				Paragraphs::iterator m_aTemp = bEmpty ? m_aVisibleBegin : m_aFocused;
                ::rtl::Reference< ParagraphImpl > xParagraph(getParagraph(m_aTemp));
                if (xParagraph.is())
                    xParagraph->notifyEvent(
                        ::css::accessibility::AccessibleEventId::
                        STATE_CHANGED,
                        ::css::uno::makeAny(
                            ::css::accessibility::AccessibleStateType::
                            FOCUSED),
                        ::css::uno::Any());
            }

			/*
            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)
    {
        ::sal_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 (::sal_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(::sal_uLong nNumber, ::sal_uInt16 nBegin, ::sal_uInt16 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:
            {
                ::sal_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:
            {
                ::sal_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:
            {
                ::sal_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());

                if (n < m_xParagraphs->size())
                {
                    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();
    }
}

::sal_Int32 Document::getSelectionType(::sal_Int32 nNewFirstPara, ::sal_Int32 nNewFirstPos, ::sal_Int32 nNewLastPara, ::sal_Int32 nNewLastPos)
{
	if (m_nSelectionFirstPara == -1)
		return -1;
	::sal_Int32 Osp = m_nSelectionFirstPara, Osl = m_nSelectionFirstPos, Oep = m_nSelectionLastPara, Oel = m_nSelectionLastPos;
	::sal_Int32 Nsp = nNewFirstPara, Nsl = nNewFirstPos, Nep = nNewLastPara, Nel = nNewLastPos;
	TextPaM Ns(Nsp, sal_uInt16(Nsl));
	TextPaM Ne(Nep, sal_uInt16(Nel));
	TextPaM Os(Osp, sal_uInt16(Osl));
	TextPaM Oe(Oep, sal_uInt16(Oel));

	if (Os == Oe && Ns == Ne)
	{
		//only caret moves.
		return 1;
	}
	else if (Os == Oe && Ns != Ne)
	{
		//old has no selection but new has selection
		return 2;
	}
	else if (Os != Oe && Ns == Ne)
	{
		//old has selection but new has no selection.
		return 3;
	}
	else if (Os != Oe && Ns != Ne && Osp == Nsp && Osl == Nsl)
	{
		//both old and new have selections. 
		if (Oep == Nep )
		{
			//Send text_selection_change event on Nep

			return 4;
		}
		else if (Oep < Nep)
		{
			//all the following examples like 1,2->1,3 means that old start select para is 1, old end select para is 2, 
			// then press shift up, the new start select para is 1, new end select para is 3;
			//for example, 1, 2 -> 1, 3; 4,1 -> 4, 7; 4,1 -> 4, 2; 4,4->4,5
			if (Nep >= Nsp)
			{
				// 1, 2 -> 1, 3; 4, 1 -> 4, 7; 4,4->4,5;
				if (Oep < Osp)
				{
					// 4,1 -> 4,7; 
					return 5;
				}
				else if (Oep >= Osp)
				{
					// 1, 2 -> 1, 3; 4,4->4,5;
					return 6;
				}
			}
			else 
			{
				// 4,1 -> 4,2, 
				if (Oep < Osp)
				{
					// 4,1 -> 4,2, 
					return 7;
				}
				else if (Oep >= Osp)
				{
					// no such condition. Oep > Osp = Nsp > Nep
				}
			}
		}
		else if (Oep > Nep)
		{
			// 3,2 -> 3,1; 4,7 -> 4,1; 4, 7 -> 4,6; 4,4 -> 4,3
			if (Nep >= Nsp)
			{
				// 4,7 -> 4,6
				if (Oep <= Osp)
				{
					//no such condition, Oep<Osp=Nsp <= Nep
				}
				else if (Oep > Osp)
				{
					// 4,7 ->4,6
					return 8;
				}
			}
			else 
			{
				// 3,2 -> 3,1, 4,7 -> 4,1; 4,4->4,3
				if (Oep <= Osp)
				{
					// 3,2 -> 3,1; 4,4->4,3
					return 9; 
				}
				else if (Oep > Osp)
				{
					// 4,7 -> 4,1
					return 10;
				}
			}			
		}		
	}
	return -1;
}


void Document::sendEvent(::sal_Int32 start, ::sal_Int32 end, ::sal_Int16 nEventId)
{
	 Paragraphs::iterator aEnd = ::std::min(m_xParagraphs->begin() + end + 1, m_aVisibleEnd);
	for (Paragraphs::iterator aIt = ::std::max(m_xParagraphs->begin() + start, m_aVisibleBegin);
	     aIt < aEnd; ++aIt)
	{
	    ::rtl::Reference< ParagraphImpl > xParagraph(getParagraph(aIt));
	    if (xParagraph.is())
	        xParagraph->notifyEvent(
			nEventId,
	            ::css::uno::Any(), ::css::uno::Any());
	}
}

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())
        {
		//disable the first event when user types in empty field.
		::sal_Int32 count = getAccessibleChildCount();
		::sal_Bool bEmpty = count > 1;
            //if (aIt != m_aFocused)
			if (aIt != m_aFocused && bEmpty)
                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;

    ::sal_Int32 nMin;
    ::sal_Int32 nMax;
    ::sal_Int32 ret = getSelectionType(nNewFirstPara, nNewFirstPos, nNewLastPara, nNewLastPos);
	switch (ret)
	{
		case -1:
			{
				//no event
			}
			break;
		case 1:
			{
				//only caret moved, already handled in above
			}
			break;
		case 2:
			{
				//old has no selection but new has selection
				nMin = ::std::min(nNewFirstPara, nNewLastPara);
				nMax = ::std::max(nNewFirstPara, nNewLastPara); 
				sendEvent(nMin, nMax,  ::css::accessibility::AccessibleEventId::SELECTION_CHANGED);
				sendEvent(nMin, nMax,  ::css::accessibility::AccessibleEventId::TEXT_SELECTION_CHANGED);
			}
			break;
		case 3:
			{
				//old has selection but new has no selection.
				nMin = ::std::min(m_nSelectionFirstPara, m_nSelectionLastPara);
				nMax = ::std::max(m_nSelectionFirstPara, m_nSelectionLastPara); 
				sendEvent(nMin, nMax,  ::css::accessibility::AccessibleEventId::SELECTION_CHANGED);
				sendEvent(nMin, nMax,  ::css::accessibility::AccessibleEventId::TEXT_SELECTION_CHANGED);
			}
			break;
		case 4:
			{
				//Send text_selection_change event on Nep
				sendEvent(nNewLastPara, nNewLastPara, ::css::accessibility::AccessibleEventId::TEXT_SELECTION_CHANGED);
			}
			break;
		case 5:
			{
				// 4, 1 -> 4, 7
				sendEvent(m_nSelectionLastPara, m_nSelectionFirstPara-1, ::css::accessibility::AccessibleEventId::SELECTION_CHANGED);
				sendEvent(nNewFirstPara+1, nNewLastPara, ::css::accessibility::AccessibleEventId::SELECTION_CHANGED);
				
				sendEvent(m_nSelectionLastPara, nNewLastPara, ::css::accessibility::AccessibleEventId::TEXT_SELECTION_CHANGED);
			}
			break;
		case 6:
			{
				// 1, 2 -> 1, 4; 4,4->4,5;
				sendEvent(m_nSelectionLastPara+1, nNewLastPara, ::css::accessibility::AccessibleEventId::SELECTION_CHANGED);

				sendEvent(m_nSelectionLastPara, nNewLastPara, ::css::accessibility::AccessibleEventId::TEXT_SELECTION_CHANGED);
			}
			break;
		case 7:
			{
				// 4,1 -> 4,3, 
				sendEvent(m_nSelectionLastPara +1, nNewLastPara , ::css::accessibility::AccessibleEventId::SELECTION_CHANGED);

				sendEvent(m_nSelectionLastPara, nNewLastPara, ::css::accessibility::AccessibleEventId::TEXT_SELECTION_CHANGED);
			}
			break;
		case 8:
			{
				// 4,7 ->4,5; 
				sendEvent(nNewLastPara + 1, m_nSelectionLastPara, ::css::accessibility::AccessibleEventId::SELECTION_CHANGED);

				sendEvent(nNewLastPara, m_nSelectionLastPara, ::css::accessibility::AccessibleEventId::TEXT_SELECTION_CHANGED);
			}
			break;
		case 9:
			{
				// 3,2 -> 3,1; 4,4->4,3
				sendEvent(nNewLastPara, m_nSelectionLastPara - 1, ::css::accessibility::AccessibleEventId::SELECTION_CHANGED);

				sendEvent(nNewLastPara, m_nSelectionLastPara, ::css::accessibility::AccessibleEventId::TEXT_SELECTION_CHANGED);
			}
			break;
		case 10:
			{
				// 4,7 -> 4,1
				sendEvent(m_nSelectionFirstPara + 1, m_nSelectionLastPara, ::css::accessibility::AccessibleEventId::SELECTION_CHANGED);
				sendEvent(nNewLastPara, nNewFirstPara - 1, ::css::accessibility::AccessibleEventId::SELECTION_CHANGED);
				
				sendEvent(nNewLastPara, m_nSelectionLastPara, ::css::accessibility::AccessibleEventId::TEXT_SELECTION_CHANGED);
			}
			break;
		default:
			break;
	}
	
    /*
    // 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< sal_uLong >( m_nSelectionFirstPara ), static_cast< sal_uInt16 >( m_nSelectionFirstPos ) );
    TextPaM aOldTextEnd( static_cast< sal_uLong >( m_nSelectionLastPara ), static_cast< sal_uInt16 >( m_nSelectionLastPos ) );
    TextPaM aNewTextStart( static_cast< sal_uLong >( nNewFirstPara ), static_cast< sal_uInt16 >( nNewFirstPos ) );
    TextPaM aNewTextEnd( static_cast< sal_uLong >( nNewLastPara ), static_cast< sal_uInt16 >( 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;
}

}

