/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*************************************************************************
 *
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * Copyright 2000, 2010 Oracle and/or its affiliates.
 *
 * OpenOffice.org - a multi-platform office productivity suite
 *
 * This file is part of OpenOffice.org.
 *
 * OpenOffice.org is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License version 3
 * only, as published by the Free Software Foundation.
 *
 * OpenOffice.org is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License version 3 for more details
 * (a copy is included in the LICENSE file that accompanied this code).
 *
 * You should have received a copy of the GNU Lesser General Public License
 * version 3 along with OpenOffice.org.  If not, see
 * <http://www.openoffice.org/license.html>
 * for a copy of the LGPLv3 License.
 *
 ************************************************************************/

#include <svtools/textview.hxx>
#include <svtools/texteng.hxx>
#include <textdoc.hxx>
#include <svtools/textdata.hxx>
#include <textdat2.hxx>

#include <svl/undo.hxx>
#include <vcl/cursor.hxx>
#include <vcl/window.hxx>
#include <vcl/svapp.hxx>
#include <vcl/sound.hxx>
#include <tools/stream.hxx>

#include <sot/formats.hxx>
#include <svl/urlbmk.hxx>

#include <com/sun/star/i18n/XBreakIterator.hpp>

#include <com/sun/star/i18n/CharacterIteratorMode.hpp>

#include <com/sun/star/i18n/WordType.hpp>
#include <cppuhelper/weak.hxx>
#include <vcl/unohelp.hxx>
#include <com/sun/star/datatransfer/XTransferable.hpp>
#include <com/sun/star/datatransfer/clipboard/XClipboard.hpp>
#include <com/sun/star/datatransfer/clipboard/XFlushableClipboard.hpp>
#include <com/sun/star/lang/XMultiServiceFactory.hpp>

#include <com/sun/star/datatransfer/dnd/DNDConstants.hpp>
#include <com/sun/star/datatransfer/dnd/XDragGestureRecognizer.hpp>
#include <com/sun/star/datatransfer/dnd/XDropTarget.hpp>

#include <vcl/edit.hxx>


#include <sot/exchange.hxx>

#include <osl/mutex.hxx>


using namespace ::com::sun::star;

class TETextDataObject :    public ::com::sun::star::datatransfer::XTransferable,
                        public ::cppu::OWeakObject

{
private:
    String          maText;
    SvMemoryStream  maHTMLStream;

public:
                    TETextDataObject( const String& rText );
                    ~TETextDataObject();

    String&         GetText() { return maText; }
    SvMemoryStream& GetHTMLStream() { return maHTMLStream; }

    // ::com::sun::star::uno::XInterface
    ::com::sun::star::uno::Any                  SAL_CALL queryInterface( const ::com::sun::star::uno::Type & rType ) throw(::com::sun::star::uno::RuntimeException);
    void                                        SAL_CALL acquire() throw()  { OWeakObject::acquire(); }
    void                                        SAL_CALL release() throw()  { OWeakObject::release(); }

    // ::com::sun::star::datatransfer::XTransferable
    ::com::sun::star::uno::Any SAL_CALL getTransferData( const ::com::sun::star::datatransfer::DataFlavor& aFlavor ) throw(::com::sun::star::datatransfer::UnsupportedFlavorException, ::com::sun::star::io::IOException, ::com::sun::star::uno::RuntimeException);
    ::com::sun::star::uno::Sequence< ::com::sun::star::datatransfer::DataFlavor > SAL_CALL getTransferDataFlavors(  ) throw(::com::sun::star::uno::RuntimeException);
    sal_Bool SAL_CALL isDataFlavorSupported( const ::com::sun::star::datatransfer::DataFlavor& aFlavor ) throw(::com::sun::star::uno::RuntimeException);
};

TETextDataObject::TETextDataObject( const String& rText ) : maText( rText )
{
}

TETextDataObject::~TETextDataObject()
{
}

// uno::XInterface
uno::Any TETextDataObject::queryInterface( const uno::Type & rType ) throw(uno::RuntimeException)
{
    uno::Any aRet = ::cppu::queryInterface( rType, (static_cast< datatransfer::XTransferable* >(this)) );
    return (aRet.hasValue() ? aRet : OWeakObject::queryInterface( rType ));
}

// datatransfer::XTransferable
uno::Any TETextDataObject::getTransferData( const datatransfer::DataFlavor& rFlavor ) throw(datatransfer::UnsupportedFlavorException, io::IOException, uno::RuntimeException)
{
    uno::Any aAny;

    sal_uLong nT = SotExchange::GetFormat( rFlavor );
    if ( nT == SOT_FORMAT_STRING )
    {
        aAny <<= (::rtl::OUString)GetText();
    }
    else if ( nT == SOT_FORMATSTR_ID_HTML )
    {
        GetHTMLStream().Seek( STREAM_SEEK_TO_END );
        sal_uLong nLen = GetHTMLStream().Tell();
        GetHTMLStream().Seek(0);

        uno::Sequence< sal_Int8 > aSeq( nLen );
        memcpy( aSeq.getArray(), GetHTMLStream().GetData(), nLen );
        aAny <<= aSeq;
    }
    else
    {
        throw datatransfer::UnsupportedFlavorException();
    }
    return aAny;
}

uno::Sequence< datatransfer::DataFlavor > TETextDataObject::getTransferDataFlavors(  ) throw(uno::RuntimeException)
{
    GetHTMLStream().Seek( STREAM_SEEK_TO_END );
    sal_Bool bHTML = GetHTMLStream().Tell() > 0;
    uno::Sequence< datatransfer::DataFlavor > aDataFlavors( bHTML ? 2 : 1 );
    SotExchange::GetFormatDataFlavor( SOT_FORMAT_STRING, aDataFlavors.getArray()[0] );
    if ( bHTML )
        SotExchange::GetFormatDataFlavor( SOT_FORMATSTR_ID_HTML, aDataFlavors.getArray()[1] );
    return aDataFlavors;
}

sal_Bool TETextDataObject::isDataFlavorSupported( const datatransfer::DataFlavor& rFlavor ) throw(uno::RuntimeException)
{
    sal_uLong nT = SotExchange::GetFormat( rFlavor );
    return ( nT == SOT_FORMAT_STRING );
}

struct ImpTextView
{
    TextEngine*         mpTextEngine;

    Window*             mpWindow;
    TextSelection       maSelection;
    Point               maStartDocPos;
//    TextPaM             maMBDownPaM;

    Cursor*             mpCursor;

    TextDDInfo*         mpDDInfo;

    VirtualDevice*      mpVirtDev;

    SelectionEngine*    mpSelEngine;
    TextSelFunctionSet* mpSelFuncSet;

    ::com::sun::star::uno::Reference< ::com::sun::star::datatransfer::dnd::XDragSourceListener > mxDnDListener;

    sal_uInt16              mnTravelXPos;

    sal_Bool                mbAutoScroll            : 1;
    sal_Bool                mbInsertMode            : 1;
    sal_Bool                mbReadOnly              : 1;
    sal_Bool                mbPaintSelection        : 1;
    sal_Bool                mbAutoIndent            : 1;
    sal_Bool                mbHighlightSelection    : 1;
    sal_Bool                mbCursorEnabled         : 1;
    sal_Bool                mbClickedInSelection    : 1;
    sal_Bool                mbSupportProtectAttribute : 1;
    bool                mbCursorAtEndOfLine;
};

// -------------------------------------------------------------------------
// (+) class TextView
// -------------------------------------------------------------------------
TextView::TextView( TextEngine* pEng, Window* pWindow ) :
    mpImpl(new ImpTextView)
{
    pWindow->EnableRTL( sal_False );

    mpImpl->mpWindow = pWindow;
    mpImpl->mpTextEngine = pEng;
    mpImpl->mpVirtDev = NULL;

    mpImpl->mbPaintSelection = sal_True;
    mpImpl->mbAutoScroll = sal_True;
    mpImpl->mbInsertMode = sal_True;
    mpImpl->mbReadOnly = sal_False;
    mpImpl->mbHighlightSelection = sal_False;
    mpImpl->mbAutoIndent = sal_False;
    mpImpl->mbCursorEnabled = sal_True;
    mpImpl->mbClickedInSelection = sal_False;
    mpImpl->mbSupportProtectAttribute = sal_False;
    mpImpl->mbCursorAtEndOfLine = false;
//  mbInSelection = sal_False;

    mpImpl->mnTravelXPos = TRAVEL_X_DONTKNOW;

    mpImpl->mpSelFuncSet = new TextSelFunctionSet( this );
    mpImpl->mpSelEngine = new SelectionEngine( mpImpl->mpWindow, mpImpl->mpSelFuncSet );
    mpImpl->mpSelEngine->SetSelectionMode( RANGE_SELECTION );
    mpImpl->mpSelEngine->EnableDrag( sal_True );

    mpImpl->mpCursor = new Cursor;
    mpImpl->mpCursor->Show();
    pWindow->SetCursor( mpImpl->mpCursor );
    pWindow->SetInputContext( InputContext( pEng->GetFont(), INPUTCONTEXT_TEXT|INPUTCONTEXT_EXTTEXTINPUT ) );

    if ( pWindow->GetSettings().GetStyleSettings().GetSelectionOptions() & SELECTION_OPTION_INVERT )
        mpImpl->mbHighlightSelection = sal_True;

    pWindow->SetLineColor();

    mpImpl->mpDDInfo = NULL;

    if ( pWindow->GetDragGestureRecognizer().is() )
    {
        vcl::unohelper::DragAndDropWrapper* pDnDWrapper = new vcl::unohelper::DragAndDropWrapper( this );
        mpImpl->mxDnDListener = pDnDWrapper;

        uno::Reference< datatransfer::dnd::XDragGestureListener> xDGL( mpImpl->mxDnDListener, uno::UNO_QUERY );
        pWindow->GetDragGestureRecognizer()->addDragGestureListener( xDGL );
        uno::Reference< datatransfer::dnd::XDropTargetListener> xDTL( xDGL, uno::UNO_QUERY );
        pWindow->GetDropTarget()->addDropTargetListener( xDTL );
        pWindow->GetDropTarget()->setActive( sal_True );
        pWindow->GetDropTarget()->setDefaultActions( datatransfer::dnd::DNDConstants::ACTION_COPY_OR_MOVE );
    }
}

TextView::~TextView()
{
    delete mpImpl->mpSelEngine;
    delete mpImpl->mpSelFuncSet;
    delete mpImpl->mpVirtDev;

    if ( mpImpl->mpWindow->GetCursor() == mpImpl->mpCursor )
        mpImpl->mpWindow->SetCursor( 0 );
    delete mpImpl->mpCursor;
    delete mpImpl->mpDDInfo;
    delete mpImpl;
}

void TextView::Invalidate()
{
    mpImpl->mpWindow->Invalidate();
}

void TextView::SetSelection( const TextSelection& rTextSel, sal_Bool bGotoCursor )
{
    // Falls jemand gerade ein leeres Attribut hinterlassen hat,
    // und dann der Outliner die Selektion manipulitert:
    if ( !mpImpl->maSelection.HasRange() )
        mpImpl->mpTextEngine->CursorMoved( mpImpl->maSelection.GetStart().GetPara() );

    // Wenn nach einem KeyInput die Selection manipuliert wird:
    mpImpl->mpTextEngine->CheckIdleFormatter();

    HideSelection();
    TextSelection aNewSel( rTextSel );
    mpImpl->mpTextEngine->ValidateSelection( aNewSel );
    ImpSetSelection( aNewSel );
    ShowSelection();
    ShowCursor( bGotoCursor );
}

void TextView::SetSelection( const TextSelection& rTextSel )
{
    SetSelection( rTextSel, mpImpl->mbAutoScroll );
}

const TextSelection& TextView::GetSelection() const
{
    return mpImpl->maSelection;
}
TextSelection&      TextView::GetSelection()
{
    return mpImpl->maSelection;
}

void TextView::DeleteSelected()
{
//  HideSelection();

    mpImpl->mpTextEngine->UndoActionStart();
    TextPaM aPaM = mpImpl->mpTextEngine->ImpDeleteText( mpImpl->maSelection );
    mpImpl->mpTextEngine->UndoActionEnd();

    ImpSetSelection( aPaM );
    mpImpl->mpTextEngine->FormatAndUpdate( this );
    ShowCursor();
}

void TextView::ImpPaint( OutputDevice* pOut, const Point& rStartPos, Rectangle const* pPaintArea, TextSelection const* pPaintRange, TextSelection const* pSelection )
{
    if ( !mpImpl->mbPaintSelection )
        pSelection = NULL;
    else
    {
        // Richtige Hintergrundfarbe einstellen.
        // Ich bekomme leider nicht mit, ob sich diese inzwischen geaendert hat.
        Font aFont = mpImpl->mpTextEngine->GetFont();
        Color aColor = pOut->GetBackground().GetColor();
        aColor.SetTransparency( 0 );
        if ( aColor != aFont.GetFillColor() )
        {
            if( aFont.IsTransparent() )
                aColor = Color( COL_TRANSPARENT );
            aFont.SetFillColor( aColor );
            mpImpl->mpTextEngine->maFont = aFont;
        }
    }

    mpImpl->mpTextEngine->ImpPaint( pOut, rStartPos, pPaintArea, pPaintRange, pSelection );
}

void TextView::Paint( const Rectangle& rRect )
{
    ImpPaint( rRect, sal_False );
}

void TextView::ImpPaint( const Rectangle& rRect, sal_Bool bUseVirtDev )
{
    if ( !mpImpl->mpTextEngine->GetUpdateMode() || mpImpl->mpTextEngine->IsInUndo() )
        return;

    TextSelection *pDrawSelection = NULL;
    if ( !mpImpl->mbHighlightSelection && mpImpl->maSelection.HasRange() )
        pDrawSelection = &mpImpl->maSelection;

    if ( bUseVirtDev )
    {
        VirtualDevice* pVDev = GetVirtualDevice();

        const Color& rBackgroundColor = mpImpl->mpWindow->GetBackground().GetColor();
        if ( pVDev->GetFillColor() != rBackgroundColor )
            pVDev->SetFillColor( rBackgroundColor );
        if ( pVDev->GetBackground().GetColor() != rBackgroundColor )
            pVDev->SetBackground( rBackgroundColor );

        sal_Bool bVDevValid = sal_True;
        Size aOutSz( pVDev->GetOutputSizePixel() );
        if ( (  aOutSz.Width() < rRect.GetWidth() ) ||
             (  aOutSz.Height() < rRect.GetHeight() ) )
        {
            bVDevValid = pVDev->SetOutputSizePixel( rRect.GetSize() );
        }
        else
        {
            // Das VirtDev kann bei einem Resize sehr gross werden =>
            // irgendwann mal kleiner machen!
            if ( ( aOutSz.Height() > ( rRect.GetHeight() + 20 ) ) ||
                 ( aOutSz.Width() > ( rRect.GetWidth() + 20 ) ) )
            {
                bVDevValid = pVDev->SetOutputSizePixel( rRect.GetSize() );
            }
            else
            {
                pVDev->Erase();
            }
        }
        if ( !bVDevValid )
        {
            ImpPaint( rRect, sal_False /* ohne VDev */ );
            return;
        }

        Rectangle aTmpRec( Point( 0, 0 ), rRect.GetSize() );

        Point aDocPos( mpImpl->maStartDocPos.X(), mpImpl->maStartDocPos.Y() + rRect.Top() );
        Point aStartPos = ImpGetOutputStartPos( aDocPos );
        ImpPaint( pVDev, aStartPos, &aTmpRec, NULL, pDrawSelection );
        mpImpl->mpWindow->DrawOutDev( rRect.TopLeft(), rRect.GetSize(),
                                Point(0,0), rRect.GetSize(), *pVDev );
//      ShowSelection();
        if ( mpImpl->mbHighlightSelection )
            ImpHighlight( mpImpl->maSelection );
    }
    else
    {
        Point aStartPos = ImpGetOutputStartPos( mpImpl->maStartDocPos );
        ImpPaint( mpImpl->mpWindow, aStartPos, &rRect, NULL, pDrawSelection );

//      ShowSelection();
        if ( mpImpl->mbHighlightSelection )
            ImpHighlight( mpImpl->maSelection );
    }
}

void TextView::ImpHighlight( const TextSelection& rSel )
{
    TextSelection aSel( rSel );
    aSel.Justify();
    if ( aSel.HasRange() && !mpImpl->mpTextEngine->IsInUndo() && mpImpl->mpTextEngine->GetUpdateMode() )
    {
        mpImpl->mpCursor->Hide();

        DBG_ASSERT( !mpImpl->mpTextEngine->mpIdleFormatter->IsActive(), "ImpHighlight: Not formatted!" );

        Rectangle aVisArea( mpImpl->maStartDocPos, mpImpl->mpWindow->GetOutputSizePixel() );
        long nY = 0;
        sal_uLong nStartPara = aSel.GetStart().GetPara();
        sal_uLong nEndPara = aSel.GetEnd().GetPara();
        for ( sal_uLong nPara = 0; nPara <= nEndPara; nPara++ )
        {
            long nParaHeight = (long)mpImpl->mpTextEngine->CalcParaHeight( nPara );
            if ( ( nPara >= nStartPara ) && ( ( nY + nParaHeight ) > aVisArea.Top() ) )
            {
                TEParaPortion* pTEParaPortion = mpImpl->mpTextEngine->mpTEParaPortions->GetObject( nPara );
                sal_uInt16 nStartLine = 0;
                sal_uInt16 nEndLine = pTEParaPortion->GetLines().Count() -1;
                if ( nPara == nStartPara )
                    nStartLine = pTEParaPortion->GetLineNumber( aSel.GetStart().GetIndex(), sal_False );
                if ( nPara == nEndPara )
                    nEndLine = pTEParaPortion->GetLineNumber( aSel.GetEnd().GetIndex(), sal_True );

                // ueber die Zeilen iterieren....
                for ( sal_uInt16 nLine = nStartLine; nLine <= nEndLine; nLine++ )
                {
                    TextLine* pLine = pTEParaPortion->GetLines().GetObject( nLine );
                    sal_uInt16 nStartIndex = pLine->GetStart();
                    sal_uInt16 nEndIndex = pLine->GetEnd();
                    if ( ( nPara == nStartPara ) && ( nLine == nStartLine ) )
                        nStartIndex = aSel.GetStart().GetIndex();
                    if ( ( nPara == nEndPara ) && ( nLine == nEndLine ) )
                        nEndIndex = aSel.GetEnd().GetIndex();

                    // Kann passieren, wenn am Anfang einer umgebrochenen Zeile.
                    if ( nEndIndex < nStartIndex )
                        nEndIndex = nStartIndex;

                    Rectangle aTmpRec( mpImpl->mpTextEngine->GetEditCursor( TextPaM( nPara, nStartIndex ), sal_False ) );
                    aTmpRec.Top() += nY;
                    aTmpRec.Bottom() += nY;
                    Point aTopLeft( aTmpRec.TopLeft() );

                    aTmpRec = mpImpl->mpTextEngine->GetEditCursor( TextPaM( nPara, nEndIndex ), sal_True );
                    aTmpRec.Top() += nY;
                    aTmpRec.Bottom() += nY;
                    Point aBottomRight( aTmpRec.BottomRight() );
                    aBottomRight.X()--;

                    // Nur Painten, wenn im sichtbaren Bereich...
                    if ( ( aTopLeft.X() < aBottomRight.X() ) && ( aBottomRight.Y() >= aVisArea.Top() ) )
                    {
                        Point aPnt1( GetWindowPos( aTopLeft ) );
                        Point aPnt2( GetWindowPos( aBottomRight ) );

                        Rectangle aRect( aPnt1, aPnt2 );
                        mpImpl->mpWindow->Invert( aRect );
                    }
                }
            }
            nY += nParaHeight;

            if ( nY >= aVisArea.Bottom() )
                break;
        }
    }
}

void TextView::ImpSetSelection( const TextSelection& rSelection )
{
    if ( rSelection != mpImpl->maSelection )
    {
        mpImpl->maSelection = rSelection;
        mpImpl->mpTextEngine->Broadcast( TextHint( TEXT_HINT_VIEWSELECTIONCHANGED ) );
    }
}

void TextView::ShowSelection()
{
    ImpShowHideSelection( sal_True );
}

void TextView::HideSelection()
{
    ImpShowHideSelection( sal_False );
}

void TextView::ShowSelection( const TextSelection& rRange )
{
    ImpShowHideSelection( sal_True, &rRange );
}

void TextView::ImpShowHideSelection( sal_Bool bShow, const TextSelection* pRange )
{
    const TextSelection* pRangeOrSelection = pRange ? pRange : &mpImpl->maSelection;

    if ( pRangeOrSelection->HasRange() )
    {
        if ( mpImpl->mbHighlightSelection )
        {
            ImpHighlight( *pRangeOrSelection );
        }
        else
        {
            if( mpImpl->mpWindow->IsPaintTransparent() )
                mpImpl->mpWindow->Invalidate();
            else
            {
                Rectangle aOutArea( Point( 0, 0 ), mpImpl->mpWindow->GetOutputSizePixel() );
                Point aStartPos( ImpGetOutputStartPos( mpImpl->maStartDocPos ) );
                TextSelection aRange( *pRangeOrSelection );
                aRange.Justify();
                sal_Bool bVisCursor = mpImpl->mpCursor->IsVisible();
                mpImpl->mpCursor->Hide();
                ImpPaint( mpImpl->mpWindow, aStartPos, &aOutArea, &aRange, bShow ? &mpImpl->maSelection : NULL );
                if ( bVisCursor )
                    mpImpl->mpCursor->Show();
            }
        }
    }
}

VirtualDevice* TextView::GetVirtualDevice()
{
    if ( !mpImpl->mpVirtDev )
    {
        mpImpl->mpVirtDev = new VirtualDevice;
        mpImpl->mpVirtDev->SetLineColor();
    }
    return mpImpl->mpVirtDev;
}

void TextView::EraseVirtualDevice()
{
    delete mpImpl->mpVirtDev;
    mpImpl->mpVirtDev = 0;
}

sal_Bool TextView::KeyInput( const KeyEvent& rKeyEvent )
{
    sal_Bool bDone      = sal_True;
    sal_Bool bModified  = sal_False;
    sal_Bool bMoved     = sal_False;
    sal_Bool bEndKey    = sal_False;    // spezielle CursorPosition
    sal_Bool bAllowIdle = sal_True;

    // Um zu pruefen ob durch irgendeine Aktion mModified, das lokale
    // bModified wird z.B. bei Cut/Paste nicht gesetzt, weil dort an anderen
    // Stellen das updaten erfolgt.
    sal_Bool bWasModified = mpImpl->mpTextEngine->IsModified();
    mpImpl->mpTextEngine->SetModified( sal_False );

    TextSelection aCurSel( mpImpl->maSelection );
    TextSelection aOldSel( aCurSel );

    sal_uInt16 nCode = rKeyEvent.GetKeyCode().GetCode();
    KeyFuncType eFunc = rKeyEvent.GetKeyCode().GetFunction();
    if ( eFunc != KEYFUNC_DONTKNOW )
    {
        switch ( eFunc )
        {
            case KEYFUNC_CUT:
            {
                if ( !mpImpl->mbReadOnly )
                    Cut();
            }
            break;
            case KEYFUNC_COPY:
            {
                Copy();
            }
            break;
            case KEYFUNC_PASTE:
            {
                if ( !mpImpl->mbReadOnly )
                    Paste();
            }
            break;
            case KEYFUNC_UNDO:
            {
                if ( !mpImpl->mbReadOnly )
                    Undo();
            }
            break;
            case KEYFUNC_REDO:
            {
                if ( !mpImpl->mbReadOnly )
                    Redo();
            }
            break;

            default:    // wird dann evtl. unten bearbeitet.
                        eFunc = KEYFUNC_DONTKNOW;
        }
    }
    if ( eFunc == KEYFUNC_DONTKNOW )
    {
        switch ( nCode )
        {
            case KEY_UP:
            case KEY_DOWN:
            case KEY_LEFT:
            case KEY_RIGHT:
            case KEY_HOME:
            case KEY_END:
            case KEY_PAGEUP:
            case KEY_PAGEDOWN:
            case com::sun::star::awt::Key::MOVE_WORD_FORWARD:
            case com::sun::star::awt::Key::SELECT_WORD_FORWARD:
            case com::sun::star::awt::Key::MOVE_WORD_BACKWARD:
            case com::sun::star::awt::Key::SELECT_WORD_BACKWARD:
            case com::sun::star::awt::Key::MOVE_TO_BEGIN_OF_LINE:
            case com::sun::star::awt::Key::MOVE_TO_END_OF_LINE:
            case com::sun::star::awt::Key::SELECT_TO_BEGIN_OF_LINE:
            case com::sun::star::awt::Key::SELECT_TO_END_OF_LINE:
            case com::sun::star::awt::Key::MOVE_TO_BEGIN_OF_PARAGRAPH:
            case com::sun::star::awt::Key::MOVE_TO_END_OF_PARAGRAPH:
            case com::sun::star::awt::Key::SELECT_TO_BEGIN_OF_PARAGRAPH:
            case com::sun::star::awt::Key::SELECT_TO_END_OF_PARAGRAPH:
            case com::sun::star::awt::Key::MOVE_TO_BEGIN_OF_DOCUMENT:
            case com::sun::star::awt::Key::MOVE_TO_END_OF_DOCUMENT:
            case com::sun::star::awt::Key::SELECT_TO_BEGIN_OF_DOCUMENT:
            case com::sun::star::awt::Key::SELECT_TO_END_OF_DOCUMENT:
            {
                if ( ( !rKeyEvent.GetKeyCode().IsMod2() || ( nCode == KEY_LEFT ) || ( nCode == KEY_RIGHT ) )
                      && !( rKeyEvent.GetKeyCode().IsMod1() && ( nCode == KEY_PAGEUP || nCode == KEY_PAGEDOWN ) ) )
                {
                    aCurSel = ImpMoveCursor( rKeyEvent );
                    if ( aCurSel.HasRange() ) {
                        uno::Reference<datatransfer::clipboard::XClipboard> aSelection(GetWindow()->GetPrimarySelection());
                        Copy( aSelection );
                    }
                    bMoved = sal_True;
                    if ( nCode == KEY_END )
                        bEndKey = sal_True;
                }
                else
                    bDone = sal_False;
            }
            break;
            case KEY_BACKSPACE:
            case KEY_DELETE:
            case com::sun::star::awt::Key::DELETE_WORD_BACKWARD:
            case com::sun::star::awt::Key::DELETE_WORD_FORWARD:
            case com::sun::star::awt::Key::DELETE_TO_BEGIN_OF_LINE:
            case com::sun::star::awt::Key::DELETE_TO_END_OF_LINE:
            {
                if ( !mpImpl->mbReadOnly && !rKeyEvent.GetKeyCode().IsMod2() )
                {
                    sal_uInt8 nDel = ( nCode == KEY_DELETE ) ? DEL_RIGHT : DEL_LEFT;
                    sal_uInt8 nMode = rKeyEvent.GetKeyCode().IsMod1() ? DELMODE_RESTOFWORD : DELMODE_SIMPLE;
                    if ( ( nMode == DELMODE_RESTOFWORD ) && rKeyEvent.GetKeyCode().IsShift() )
                        nMode = DELMODE_RESTOFCONTENT;

                    switch( nCode )
                    {
                    case com::sun::star::awt::Key::DELETE_WORD_BACKWARD:
                        nDel = DEL_LEFT;
                        nMode = DELMODE_RESTOFWORD;
                        break;
                    case com::sun::star::awt::Key::DELETE_WORD_FORWARD:
                        nDel = DEL_RIGHT;
                        nMode = DELMODE_RESTOFWORD;
                        break;
                    case com::sun::star::awt::Key::DELETE_TO_BEGIN_OF_LINE:
                        nDel = DEL_LEFT;
                        nMode = DELMODE_RESTOFCONTENT;
                        break;
                    case com::sun::star::awt::Key::DELETE_TO_END_OF_LINE:
                        nDel = DEL_RIGHT;
                        nMode = DELMODE_RESTOFCONTENT;
                        break;
                    default: break;
                    }

                    mpImpl->mpTextEngine->UndoActionStart();
                    if(mpImpl->mbSupportProtectAttribute)
                    {
                        //expand selection to include all protected content - if there is any
                        const TextCharAttrib* pStartAttr = mpImpl->mpTextEngine->FindCharAttrib(
                                    TextPaM(mpImpl->maSelection.GetStart().GetPara(),
                                    mpImpl->maSelection.GetStart().GetIndex()),
                                    TEXTATTR_PROTECTED );
                        const TextCharAttrib* pEndAttr = mpImpl->mpTextEngine->FindCharAttrib(
                                    TextPaM(mpImpl->maSelection.GetEnd().GetPara(),
                                    mpImpl->maSelection.GetEnd().GetIndex()),
                                    TEXTATTR_PROTECTED );
                        if(pStartAttr && pStartAttr->GetStart() < mpImpl->maSelection.GetStart().GetIndex())
                        {
                            mpImpl->maSelection.GetStart().GetIndex() = pStartAttr->GetStart();
                        }
                        if(pEndAttr && pEndAttr->GetEnd() > mpImpl->maSelection.GetEnd().GetIndex())
                        {
                            mpImpl->maSelection.GetEnd().GetIndex() = pEndAttr->GetEnd();
                        }
                    }
                    aCurSel = ImpDelete( nDel, nMode );
                    mpImpl->mpTextEngine->UndoActionEnd();
                    bModified = sal_True;
                    bAllowIdle = sal_False;
                }
                else
                    bDone = sal_False;
            }
            break;
            case KEY_TAB:
            {
                if ( !mpImpl->mbReadOnly && !rKeyEvent.GetKeyCode().IsShift() &&
                        !rKeyEvent.GetKeyCode().IsMod1() && !rKeyEvent.GetKeyCode().IsMod2() &&
                        ImplCheckTextLen( 'x' ) )
                {
                    aCurSel = mpImpl->mpTextEngine->ImpInsertText( aCurSel, '\t', !IsInsertMode() );
                    bModified = sal_True;
                }
                else
                    bDone = sal_False;
            }
            break;
            case KEY_RETURN:
            {
                // Shift-RETURN darf nicht geschluckt werden, weil dann keine
                // mehrzeilige Eingabe in Dialogen/Property-Editor moeglich.
                if ( !mpImpl->mbReadOnly && !rKeyEvent.GetKeyCode().IsMod1() &&
                        !rKeyEvent.GetKeyCode().IsMod2() && ImplCheckTextLen( 'x' ) )
                {
                    mpImpl->mpTextEngine->UndoActionStart();
                    aCurSel = mpImpl->mpTextEngine->ImpInsertParaBreak( aCurSel );
                    if ( mpImpl->mbAutoIndent )
                    {
                        TextNode* pPrev = mpImpl->mpTextEngine->mpDoc->GetNodes().GetObject( aCurSel.GetEnd().GetPara() - 1 );
                        sal_uInt16 n = 0;
                        while ( ( n < pPrev->GetText().Len() ) && (
                                    ( pPrev->GetText().GetChar( n ) == ' ' ) ||
                                    ( pPrev->GetText().GetChar( n ) == '\t' ) ) )
                        {
                            n++;
                        }
                        if ( n )
                            aCurSel = mpImpl->mpTextEngine->ImpInsertText( aCurSel, pPrev->GetText().Copy( 0, n ) );
                    }
                    mpImpl->mpTextEngine->UndoActionEnd();
                    bModified = sal_True;
                }
                else
                    bDone = sal_False;
            }
            break;
            case KEY_INSERT:
            {
                if ( !mpImpl->mbReadOnly )
                    SetInsertMode( !IsInsertMode() );
            }
            break;
            default:
            {
                if ( TextEngine::IsSimpleCharInput( rKeyEvent ) )
                {
                    xub_Unicode nCharCode = rKeyEvent.GetCharCode();
                    if ( !mpImpl->mbReadOnly && ImplCheckTextLen( nCharCode ) )    // sonst trotzdem das Zeichen schlucken...
                    {
                        aCurSel = mpImpl->mpTextEngine->ImpInsertText( nCharCode, aCurSel, !IsInsertMode(), sal_True );
                        bModified = sal_True;
                    }
                }
                else
                    bDone = sal_False;
            }
        }
    }

    if ( aCurSel != aOldSel )   // Check if changed, maybe other method already changed mpImpl->maSelection, don't overwrite that!
        ImpSetSelection( aCurSel );

    mpImpl->mpTextEngine->UpdateSelections();

    if ( ( nCode != KEY_UP ) && ( nCode != KEY_DOWN ) )
        mpImpl->mnTravelXPos = TRAVEL_X_DONTKNOW;

    if ( bModified )
    {
        // Idle-Formatter nur, wenn AnyInput.
        if ( bAllowIdle && Application::AnyInput( VCL_INPUT_KEYBOARD) )
            mpImpl->mpTextEngine->IdleFormatAndUpdate( this );
        else
            mpImpl->mpTextEngine->FormatAndUpdate( this);
    }
    else if ( bMoved )
    {
        // Selection wird jetzt gezielt in ImpMoveCursor gemalt.
        ImpShowCursor( mpImpl->mbAutoScroll, sal_True, bEndKey );
    }

    if ( mpImpl->mpTextEngine->IsModified() )
        mpImpl->mpTextEngine->Broadcast( TextHint( TEXT_HINT_MODIFIED ) );
    else if ( bWasModified )
        mpImpl->mpTextEngine->SetModified( sal_True );

    return bDone;
}

void TextView::MouseButtonUp( const MouseEvent& rMouseEvent )
{
    mpImpl->mbClickedInSelection = sal_False;
    mpImpl->mnTravelXPos = TRAVEL_X_DONTKNOW;
    mpImpl->mpSelEngine->SelMouseButtonUp( rMouseEvent );
    if ( rMouseEvent.IsMiddle() && !IsReadOnly() &&
         ( GetWindow()->GetSettings().GetMouseSettings().GetMiddleButtonAction() == MOUSE_MIDDLE_PASTESELECTION ) )
    {
        uno::Reference<datatransfer::clipboard::XClipboard> aSelection(GetWindow()->GetPrimarySelection());
        Paste( aSelection );
        if ( mpImpl->mpTextEngine->IsModified() )
            mpImpl->mpTextEngine->Broadcast( TextHint( TEXT_HINT_MODIFIED ) );
    }
    else if ( rMouseEvent.IsLeft() && GetSelection().HasRange() )
    {
        uno::Reference<datatransfer::clipboard::XClipboard> aSelection(GetWindow()->GetPrimarySelection());
        Copy( aSelection );
    }
}

void TextView::MouseButtonDown( const MouseEvent& rMouseEvent )
{
    mpImpl->mpTextEngine->CheckIdleFormatter();    // Falls schnelles Tippen und MouseButtonDown
    mpImpl->mnTravelXPos = TRAVEL_X_DONTKNOW;
    mpImpl->mbClickedInSelection = IsSelectionAtPoint( rMouseEvent.GetPosPixel() );

    mpImpl->mpTextEngine->SetActiveView( this );

    mpImpl->mpSelEngine->SelMouseButtonDown( rMouseEvent );

    // mbu 20.01.2005 - SelMouseButtonDown() possibly triggers a 'selection changed'
    // notification. The appropriate handler could change the current selection,
    // which is the case in the MailMerge address block control. To enable select'n'drag
    // we need to reevaluate the selection after the notification has been fired.
    mpImpl->mbClickedInSelection = IsSelectionAtPoint( rMouseEvent.GetPosPixel() );

    // Sonderbehandlungen
    if ( !rMouseEvent.IsShift() && ( rMouseEvent.GetClicks() >= 2 ) )
    {
        if ( rMouseEvent.IsMod2() )
        {
            HideSelection();
            ImpSetSelection( mpImpl->maSelection.GetEnd() );
            SetCursorAtPoint( rMouseEvent.GetPosPixel() );  // Wird von SelectionEngine bei MOD2 nicht gesetzt
        }

        if ( rMouseEvent.GetClicks() == 2 )
        {
            // Wort selektieren
            if ( mpImpl->maSelection.GetEnd().GetIndex() < mpImpl->mpTextEngine->GetTextLen( mpImpl->maSelection.GetEnd().GetPara() ) )
            {
                HideSelection();
                TextNode* pNode = mpImpl->mpTextEngine->mpDoc->GetNodes().GetObject(  mpImpl->maSelection.GetEnd().GetPara() );
                uno::Reference < i18n::XBreakIterator > xBI = mpImpl->mpTextEngine->GetBreakIterator();
                i18n::Boundary aBoundary = xBI->getWordBoundary( pNode->GetText(), mpImpl->maSelection.GetEnd().GetIndex(), mpImpl->mpTextEngine->GetLocale(), i18n::WordType::ANYWORD_IGNOREWHITESPACES, sal_True );
                TextSelection aNewSel( mpImpl->maSelection );
                aNewSel.GetStart().GetIndex() = (sal_uInt16)aBoundary.startPos;
                aNewSel.GetEnd().GetIndex() = (sal_uInt16)aBoundary.endPos;
                if(mpImpl->mbSupportProtectAttribute)
                {
                    //expand selection to include all protected content - if there is any
                    const TextCharAttrib* pStartAttr = mpImpl->mpTextEngine->FindCharAttrib(
                                TextPaM(aNewSel.GetStart().GetPara(),
                                (sal_uInt16)aBoundary.startPos),
                                TEXTATTR_PROTECTED );
                    const TextCharAttrib* pEndAttr = mpImpl->mpTextEngine->FindCharAttrib(
                                TextPaM(aNewSel.GetEnd().GetPara(),
                                (sal_uInt16)aBoundary.endPos),
                                TEXTATTR_PROTECTED );
                    if(pStartAttr && pStartAttr->GetStart() < aNewSel.GetStart().GetIndex())
                    {
                        aNewSel.GetStart().GetIndex() = pStartAttr->GetStart();
                    }
                    if(pEndAttr && pEndAttr->GetEnd() > aNewSel.GetEnd().GetIndex())
                    {
                        aNewSel.GetEnd().GetIndex() = pEndAttr->GetEnd();
                    }
                }
                ImpSetSelection( aNewSel );
                ShowSelection();
                ShowCursor( sal_True, sal_True );
            }
        }
        else if ( rMouseEvent.GetClicks() == 3 )
        {
            // Absatz selektieren
            if ( mpImpl->maSelection.GetStart().GetIndex() || ( mpImpl->maSelection.GetEnd().GetIndex() < mpImpl->mpTextEngine->GetTextLen( mpImpl->maSelection.GetEnd().GetPara() ) ) )
            {
                HideSelection();
                TextSelection aNewSel( mpImpl->maSelection );
                aNewSel.GetStart().GetIndex() = 0;
                aNewSel.GetEnd().GetIndex() = mpImpl->mpTextEngine->mpDoc->GetNodes().GetObject( mpImpl->maSelection.GetEnd().GetPara() )->GetText().Len();
                ImpSetSelection( aNewSel );
                ShowSelection();
                ShowCursor( sal_True, sal_True );
            }
        }
    }
}


void TextView::MouseMove( const MouseEvent& rMouseEvent )
{
    mpImpl->mnTravelXPos = TRAVEL_X_DONTKNOW;
    mpImpl->mpSelEngine->SelMouseMove( rMouseEvent );
}

void TextView::Command( const CommandEvent& rCEvt )
{
    mpImpl->mpTextEngine->CheckIdleFormatter();    // Falls schnelles Tippen und MouseButtonDown
    mpImpl->mpTextEngine->SetActiveView( this );

    if ( rCEvt.GetCommand() == COMMAND_STARTEXTTEXTINPUT )
    {
        DeleteSelected();
        delete mpImpl->mpTextEngine->mpIMEInfos;
        TextNode* pNode = mpImpl->mpTextEngine->mpDoc->GetNodes().GetObject( GetSelection().GetEnd().GetPara() );
        mpImpl->mpTextEngine->mpIMEInfos = new TEIMEInfos( GetSelection().GetEnd(), pNode->GetText().Copy( GetSelection().GetEnd().GetIndex() ) );
        mpImpl->mpTextEngine->mpIMEInfos->bWasCursorOverwrite = !IsInsertMode();
    }
    else if ( rCEvt.GetCommand() == COMMAND_ENDEXTTEXTINPUT )
    {
        DBG_ASSERT( mpImpl->mpTextEngine->mpIMEInfos, "COMMAND_ENDEXTTEXTINPUT => Kein Start ?" );
        if( mpImpl->mpTextEngine->mpIMEInfos )
        {
            TEParaPortion* pPortion = mpImpl->mpTextEngine->mpTEParaPortions->GetObject( mpImpl->mpTextEngine->mpIMEInfos->aPos.GetPara() );
            pPortion->MarkSelectionInvalid( mpImpl->mpTextEngine->mpIMEInfos->aPos.GetIndex(), 0 );

            sal_Bool bInsertMode = !mpImpl->mpTextEngine->mpIMEInfos->bWasCursorOverwrite;

            delete mpImpl->mpTextEngine->mpIMEInfos;
            mpImpl->mpTextEngine->mpIMEInfos = NULL;

            mpImpl->mpTextEngine->FormatAndUpdate( this );

            SetInsertMode( bInsertMode );

            if ( mpImpl->mpTextEngine->IsModified() )
                mpImpl->mpTextEngine->Broadcast( TextHint( TEXT_HINT_MODIFIED ) );
        }
    }
    else if ( rCEvt.GetCommand() == COMMAND_EXTTEXTINPUT )
    {
        DBG_ASSERT( mpImpl->mpTextEngine->mpIMEInfos, "COMMAND_EXTTEXTINPUT => Kein Start ?" );
        if( mpImpl->mpTextEngine->mpIMEInfos )
        {
            const CommandExtTextInputData* pData = rCEvt.GetExtTextInputData();

            if ( !pData->IsOnlyCursorChanged() )
            {
                TextSelection aSelect( mpImpl->mpTextEngine->mpIMEInfos->aPos );
                aSelect.GetEnd().GetIndex() = aSelect.GetEnd().GetIndex() + mpImpl->mpTextEngine->mpIMEInfos->nLen;
                aSelect = mpImpl->mpTextEngine->ImpDeleteText( aSelect );
                aSelect = mpImpl->mpTextEngine->ImpInsertText( aSelect, pData->GetText() );

                if ( mpImpl->mpTextEngine->mpIMEInfos->bWasCursorOverwrite )
                {
                    sal_uInt16 nOldIMETextLen = mpImpl->mpTextEngine->mpIMEInfos->nLen;
                    sal_uInt16 nNewIMETextLen = pData->GetText().Len();

                    if ( ( nOldIMETextLen > nNewIMETextLen ) &&
                         ( nNewIMETextLen < mpImpl->mpTextEngine->mpIMEInfos->aOldTextAfterStartPos.Len() ) )
                    {
                        // restore old characters
                        sal_uInt16 nRestore = nOldIMETextLen - nNewIMETextLen;
                        TextPaM aPaM( mpImpl->mpTextEngine->mpIMEInfos->aPos );
                        aPaM.GetIndex() = aPaM.GetIndex() + nNewIMETextLen;
                        mpImpl->mpTextEngine->ImpInsertText( aPaM, mpImpl->mpTextEngine->mpIMEInfos->aOldTextAfterStartPos.Copy( nNewIMETextLen, nRestore ) );
                    }
                    else if ( ( nOldIMETextLen < nNewIMETextLen ) &&
                              ( nOldIMETextLen < mpImpl->mpTextEngine->mpIMEInfos->aOldTextAfterStartPos.Len() ) )
                    {
                        // overwrite
                        sal_uInt16 nOverwrite = nNewIMETextLen - nOldIMETextLen;
                        if ( ( nOldIMETextLen + nOverwrite ) > mpImpl->mpTextEngine->mpIMEInfos->aOldTextAfterStartPos.Len() )
                            nOverwrite = mpImpl->mpTextEngine->mpIMEInfos->aOldTextAfterStartPos.Len() - nOldIMETextLen;
                        DBG_ASSERT( nOverwrite && (nOverwrite < 0xFF00), "IME Overwrite?!" );
                        TextPaM aPaM( mpImpl->mpTextEngine->mpIMEInfos->aPos );
                        aPaM.GetIndex() = aPaM.GetIndex() + nNewIMETextLen;
                        TextSelection aSel( aPaM );
                        aSel.GetEnd().GetIndex() =
                            aSel.GetEnd().GetIndex() + nOverwrite;
                        mpImpl->mpTextEngine->ImpDeleteText( aSel );
                    }
                }

                if ( pData->GetTextAttr() )
                {
                    mpImpl->mpTextEngine->mpIMEInfos->CopyAttribs( pData->GetTextAttr(), pData->GetText().Len() );
                    mpImpl->mpTextEngine->mpIMEInfos->bCursor = pData->IsCursorVisible();
                }
                else
                {
                    mpImpl->mpTextEngine->mpIMEInfos->DestroyAttribs();
                }

                TEParaPortion* pPPortion = mpImpl->mpTextEngine->mpTEParaPortions->GetObject( mpImpl->mpTextEngine->mpIMEInfos->aPos.GetPara() );
                pPPortion->MarkSelectionInvalid( mpImpl->mpTextEngine->mpIMEInfos->aPos.GetIndex(), 0 );
                mpImpl->mpTextEngine->FormatAndUpdate( this );
            }

            TextSelection aNewSel = TextPaM( mpImpl->mpTextEngine->mpIMEInfos->aPos.GetPara(), mpImpl->mpTextEngine->mpIMEInfos->aPos.GetIndex()+pData->GetCursorPos() );
            SetSelection( aNewSel );
            SetInsertMode( !pData->IsCursorOverwrite() );

            if ( pData->IsCursorVisible() )
                ShowCursor();
            else
                HideCursor();
        }
    }
    else if ( rCEvt.GetCommand() == COMMAND_CURSORPOS )
    {
        if ( mpImpl->mpTextEngine->mpIMEInfos && mpImpl->mpTextEngine->mpIMEInfos->nLen )
        {
            TextPaM aPaM( GetSelection().GetEnd() );
            Rectangle aR1 = mpImpl->mpTextEngine->PaMtoEditCursor( aPaM );

            sal_uInt16 nInputEnd = mpImpl->mpTextEngine->mpIMEInfos->aPos.GetIndex() + mpImpl->mpTextEngine->mpIMEInfos->nLen;

            if ( !mpImpl->mpTextEngine->IsFormatted() )
                mpImpl->mpTextEngine->FormatDoc();

            TEParaPortion* pParaPortion = mpImpl->mpTextEngine->mpTEParaPortions->GetObject( aPaM.GetPara() );
            sal_uInt16 nLine = pParaPortion->GetLineNumber( aPaM.GetIndex(), sal_True );
            TextLine* pLine = pParaPortion->GetLines().GetObject( nLine );
            if ( pLine && ( nInputEnd > pLine->GetEnd() ) )
                nInputEnd = pLine->GetEnd();
            Rectangle aR2 = mpImpl->mpTextEngine->PaMtoEditCursor( TextPaM( aPaM.GetPara(), nInputEnd ) );

            long nWidth = aR2.Left()-aR1.Right();
            aR1.Move( -GetStartDocPos().X(), -GetStartDocPos().Y() );
            GetWindow()->SetCursorRect( &aR1, nWidth );
        }
        else
        {
            GetWindow()->SetCursorRect();
        }
    }
    else
    {
        mpImpl->mpSelEngine->Command( rCEvt );
    }
}

void TextView::ShowCursor( sal_Bool bGotoCursor, sal_Bool bForceVisCursor )
{
    // Die Einstellung hat mehr Gewicht:
    if ( !mpImpl->mbAutoScroll )
        bGotoCursor = sal_False;
    ImpShowCursor( bGotoCursor, bForceVisCursor, sal_False );
}

void TextView::HideCursor()
{
    mpImpl->mpCursor->Hide();
}

void TextView::Scroll( long ndX, long ndY )
{
    DBG_ASSERT( mpImpl->mpTextEngine->IsFormatted(), "Scroll: Nicht formatiert!" );

    if ( !ndX && !ndY )
        return;

    Point aNewStartPos( mpImpl->maStartDocPos );

    // Vertical:
    aNewStartPos.Y() -= ndY;
    if ( aNewStartPos.Y() < 0 )
        aNewStartPos.Y() = 0;

    // Horizontal:
    aNewStartPos.X() -= ndX;
    if ( aNewStartPos.X() < 0 )
        aNewStartPos.X() = 0;

    long nDiffX = mpImpl->maStartDocPos.X() - aNewStartPos.X();
    long nDiffY = mpImpl->maStartDocPos.Y() - aNewStartPos.Y();

    if ( nDiffX || nDiffY )
    {
        sal_Bool bVisCursor = mpImpl->mpCursor->IsVisible();
        mpImpl->mpCursor->Hide();
        mpImpl->mpWindow->Update();
        mpImpl->maStartDocPos = aNewStartPos;

        if ( mpImpl->mpTextEngine->IsRightToLeft() )
            nDiffX = -nDiffX;
        mpImpl->mpWindow->Scroll( nDiffX, nDiffY );
        mpImpl->mpWindow->Update();
        mpImpl->mpCursor->SetPos( mpImpl->mpCursor->GetPos() + Point( nDiffX, nDiffY ) );
        if ( bVisCursor && !mpImpl->mbReadOnly )
            mpImpl->mpCursor->Show();
    }

    mpImpl->mpTextEngine->Broadcast( TextHint( TEXT_HINT_VIEWSCROLLED ) );
}

void TextView::Undo()
{
    mpImpl->mpTextEngine->SetActiveView( this );
    mpImpl->mpTextEngine->GetUndoManager().Undo();
}

void TextView::Redo()
{
    mpImpl->mpTextEngine->SetActiveView( this );
    mpImpl->mpTextEngine->GetUndoManager().Redo();
}

void TextView::Cut()
{
    mpImpl->mpTextEngine->UndoActionStart();
    Copy();
    DeleteSelected();
    mpImpl->mpTextEngine->UndoActionEnd();
}

void TextView::Copy( uno::Reference< datatransfer::clipboard::XClipboard >& rxClipboard )
{
    if ( rxClipboard.is() )
    {
        TETextDataObject* pDataObj = new TETextDataObject( GetSelected() );

        if ( mpImpl->mpTextEngine->HasAttrib( TEXTATTR_HYPERLINK ) )  // Dann auch als HTML
            mpImpl->mpTextEngine->Write( pDataObj->GetHTMLStream(), &mpImpl->maSelection, sal_True );

        const sal_uInt32 nRef = Application::ReleaseSolarMutex();

        try
        {
            rxClipboard->setContents( pDataObj, NULL );

            uno::Reference< datatransfer::clipboard::XFlushableClipboard > xFlushableClipboard( rxClipboard, uno::UNO_QUERY );
            if( xFlushableClipboard.is() )
                xFlushableClipboard->flushClipboard();
        }
        catch( const ::com::sun::star::uno::Exception& )
        {
        }

        Application::AcquireSolarMutex( nRef );
    }
}

void TextView::Copy()
{
    uno::Reference<datatransfer::clipboard::XClipboard> aClipboard(GetWindow()->GetClipboard());
    Copy( aClipboard );
}

void TextView::Paste( uno::Reference< datatransfer::clipboard::XClipboard >& rxClipboard )
{
    if ( rxClipboard.is() )
    {
        uno::Reference< datatransfer::XTransferable > xDataObj;

        const sal_uInt32 nRef = Application::ReleaseSolarMutex();

        try
        {
            xDataObj = rxClipboard->getContents();
        }
        catch( const ::com::sun::star::uno::Exception& )
        {
        }

        Application::AcquireSolarMutex( nRef );

        if ( xDataObj.is() )
        {
            datatransfer::DataFlavor aFlavor;
            SotExchange::GetFormatDataFlavor( SOT_FORMAT_STRING, aFlavor );
            if ( xDataObj->isDataFlavorSupported( aFlavor ) )
            {
                try
                {
                    uno::Any aData = xDataObj->getTransferData( aFlavor );
                    ::rtl::OUString aText;
                    aData >>= aText;
                    bool bWasTruncated = false;
                    if( mpImpl->mpTextEngine->GetMaxTextLen() != 0 )
                        bWasTruncated = ImplTruncateNewText( aText );
                    InsertNewText( aText, sal_False );
                    mpImpl->mpTextEngine->Broadcast( TextHint( TEXT_HINT_MODIFIED ) );

                    if( bWasTruncated )
                        Edit::ShowTruncationWarning( mpImpl->mpWindow );
                }
                catch( const ::com::sun::star::datatransfer::UnsupportedFlavorException& )
                {
                }
            }
        }
    }
}

void TextView::Paste()
{
    uno::Reference<datatransfer::clipboard::XClipboard> aClipboard(GetWindow()->GetClipboard());
    Paste( aClipboard );
}

String TextView::GetSelected()
{
    return GetSelected( GetSystemLineEnd() );
}

String TextView::GetSelected( LineEnd aSeparator )
{
    return mpImpl->mpTextEngine->GetText( mpImpl->maSelection, aSeparator );
}

void TextView::SetInsertMode( sal_Bool bInsert )
{
    if ( mpImpl->mbInsertMode != bInsert )
    {
        mpImpl->mbInsertMode = bInsert;
        ShowCursor( mpImpl->mbAutoScroll, sal_False );
    }
}

void TextView::SetReadOnly( sal_Bool bReadOnly )
{
    if ( mpImpl->mbReadOnly != bReadOnly )
    {
        mpImpl->mbReadOnly = bReadOnly;
        if ( !mpImpl->mbReadOnly )
            ShowCursor( mpImpl->mbAutoScroll, sal_False );
        else
            HideCursor();

        GetWindow()->SetInputContext( InputContext( mpImpl->mpTextEngine->GetFont(), bReadOnly ? INPUTCONTEXT_TEXT|INPUTCONTEXT_EXTTEXTINPUT : 0 ) );
    }
}

TextSelection TextView::ImpMoveCursor( const KeyEvent& rKeyEvent )
{
    // Eigentlich nur bei Up/Down noetig, aber was solls.
    mpImpl->mpTextEngine->CheckIdleFormatter();

    TextPaM aPaM( mpImpl->maSelection.GetEnd() );
    TextPaM aOldEnd( aPaM );

    TextDirectionality eTextDirection = TextDirectionality_LeftToRight_TopToBottom;
    if ( mpImpl->mpTextEngine->IsRightToLeft() )
        eTextDirection = TextDirectionality_RightToLeft_TopToBottom;

    KeyEvent aTranslatedKeyEvent = rKeyEvent.LogicalTextDirectionality( eTextDirection );

    sal_Bool bCtrl = aTranslatedKeyEvent.GetKeyCode().IsMod1() ? sal_True : sal_False;
    sal_uInt16 nCode = aTranslatedKeyEvent.GetKeyCode().GetCode();

    bool bSelect = aTranslatedKeyEvent.GetKeyCode().IsShift();
    switch ( nCode )
    {
        case KEY_UP:        aPaM = CursorUp( aPaM );
                            break;
        case KEY_DOWN:      aPaM = CursorDown( aPaM );
                            break;
        case KEY_HOME:      aPaM = bCtrl ? CursorStartOfDoc() : CursorStartOfLine( aPaM );
                            break;
        case KEY_END:       aPaM = bCtrl ? CursorEndOfDoc() : CursorEndOfLine( aPaM );
                            break;
        case KEY_PAGEUP:    aPaM = bCtrl ? CursorStartOfDoc() : PageUp( aPaM );
                            break;
        case KEY_PAGEDOWN:  aPaM = bCtrl ? CursorEndOfDoc() : PageDown( aPaM );
                            break;
        case KEY_LEFT:      aPaM = bCtrl ? CursorWordLeft( aPaM ) : CursorLeft( aPaM, aTranslatedKeyEvent.GetKeyCode().IsMod2() ? (sal_uInt16)i18n::CharacterIteratorMode::SKIPCHARACTER : (sal_uInt16)i18n::CharacterIteratorMode::SKIPCELL );
                            break;
        case KEY_RIGHT:     aPaM = bCtrl ? CursorWordRight( aPaM ) : CursorRight( aPaM, aTranslatedKeyEvent.GetKeyCode().IsMod2() ? (sal_uInt16)i18n::CharacterIteratorMode::SKIPCHARACTER : (sal_uInt16)i18n::CharacterIteratorMode::SKIPCELL );
                            break;
        case com::sun::star::awt::Key::SELECT_WORD_FORWARD:
                            bSelect = true; // fallthrough intentional
        case com::sun::star::awt::Key::MOVE_WORD_FORWARD:
                            aPaM = CursorWordRight( aPaM );
                            break;
        case com::sun::star::awt::Key::SELECT_WORD_BACKWARD:
                            bSelect = true; // fallthrough intentional
        case com::sun::star::awt::Key::MOVE_WORD_BACKWARD:
                            aPaM = CursorWordLeft( aPaM );
                            break;
        case com::sun::star::awt::Key::SELECT_TO_BEGIN_OF_LINE:
                            bSelect = true; // fallthrough intentional
        case com::sun::star::awt::Key::MOVE_TO_BEGIN_OF_LINE:
                            aPaM = CursorStartOfLine( aPaM );
                            break;
        case com::sun::star::awt::Key::SELECT_TO_END_OF_LINE:
                            bSelect = true; // fallthrough intentional
        case com::sun::star::awt::Key::MOVE_TO_END_OF_LINE:
                            aPaM = CursorEndOfLine( aPaM );
                            break;
        case com::sun::star::awt::Key::SELECT_TO_BEGIN_OF_PARAGRAPH:
                            bSelect = true; // falltthrough intentional
        case com::sun::star::awt::Key::MOVE_TO_BEGIN_OF_PARAGRAPH:
                            aPaM = CursorStartOfParagraph( aPaM );
                            break;
        case com::sun::star::awt::Key::SELECT_TO_END_OF_PARAGRAPH:
                            bSelect = true; // falltthrough intentional
        case com::sun::star::awt::Key::MOVE_TO_END_OF_PARAGRAPH:
                            aPaM = CursorEndOfParagraph( aPaM );
                            break;
        case com::sun::star::awt::Key::SELECT_TO_BEGIN_OF_DOCUMENT:
                            bSelect = true; // falltthrough intentional
        case com::sun::star::awt::Key::MOVE_TO_BEGIN_OF_DOCUMENT:
                            aPaM = CursorStartOfDoc();
                            break;
        case com::sun::star::awt::Key::SELECT_TO_END_OF_DOCUMENT:
                            bSelect = true; // falltthrough intentional
        case com::sun::star::awt::Key::MOVE_TO_END_OF_DOCUMENT:
                            aPaM = CursorEndOfDoc();
                            break;
    }

    // Bewirkt evtl. ein CreateAnchor oder Deselection all
    mpImpl->mpSelEngine->CursorPosChanging( bSelect, aTranslatedKeyEvent.GetKeyCode().IsMod1() );

    if ( aOldEnd != aPaM )
    {
        mpImpl->mpTextEngine->CursorMoved( aOldEnd.GetPara() );


        TextSelection aOldSelection( mpImpl->maSelection );
        TextSelection aNewSelection( mpImpl->maSelection );
        aNewSelection.GetEnd() = aPaM;
        if ( bSelect )
        {
            // Dann wird die Selektion erweitert...
            ImpSetSelection( aNewSelection );
            ShowSelection( TextSelection( aOldEnd, aPaM ) );
        }
        else
        {
            aNewSelection.GetStart() = aPaM;
            ImpSetSelection( aNewSelection );
        }
    }

    return mpImpl->maSelection;
}

void TextView::InsertText( const XubString& rStr, sal_Bool bSelect )
{
    InsertNewText( rStr, bSelect );
}

void TextView::InsertNewText( const rtl::OUString& rStr, sal_Bool bSelect )
{
//  HideSelection();
    mpImpl->mpTextEngine->UndoActionStart();

    /* #i87633#
    break inserted text into chunks that fit into the underlying String
    based API (which has a maximum length of 65534 elements

    note: this will of course still cause problems for lines longer than those
    65534 elements, but those cases will hopefully be few.
    In the long run someone should switch the TextEngine to OUString instead of String
    */
    sal_Int32 nLen = rStr.getLength();
    sal_Int32 nPos = 0;
    do
    {
        sal_Int32 nChunkLen = nLen > 65534 ? 65534 : nLen;
        String aChunk( rStr.copy( nPos, nChunkLen ) );

        TextSelection aNewSel( mpImpl->maSelection );

        TextPaM aPaM = mpImpl->mpTextEngine->ImpInsertText( mpImpl->maSelection, aChunk );

        if ( bSelect )
        {
            aNewSel.Justify();
            aNewSel.GetEnd() = aPaM;
        }
        else
        {
            aNewSel = aPaM;
        }

        ImpSetSelection( aNewSel );
        nLen -= nChunkLen;
        nPos += nChunkLen;
    }
    while( nLen );

    mpImpl->mpTextEngine->UndoActionEnd();

    mpImpl->mpTextEngine->FormatAndUpdate( this );
}

TextPaM TextView::CursorLeft( const TextPaM& rPaM, sal_uInt16 nCharacterIteratorMode )
{
    TextPaM aPaM( rPaM );

    if ( aPaM.GetIndex() )
    {
        TextNode* pNode = mpImpl->mpTextEngine->mpDoc->GetNodes().GetObject( aPaM.GetPara() );
        uno::Reference < i18n::XBreakIterator > xBI = mpImpl->mpTextEngine->GetBreakIterator();
        sal_Int32 nCount = 1;
        aPaM.GetIndex() = (sal_uInt16)xBI->previousCharacters( pNode->GetText(), aPaM.GetIndex(), mpImpl->mpTextEngine->GetLocale(), nCharacterIteratorMode, nCount, nCount );
    }
    else if ( aPaM.GetPara() )
    {
        aPaM.GetPara()--;
        TextNode* pNode = mpImpl->mpTextEngine->mpDoc->GetNodes().GetObject( aPaM.GetPara() );
        aPaM.GetIndex() = pNode->GetText().Len();
    }
    return aPaM;
}

TextPaM TextView::CursorRight( const TextPaM& rPaM, sal_uInt16 nCharacterIteratorMode )
{
    TextPaM aPaM( rPaM );

    TextNode* pNode = mpImpl->mpTextEngine->mpDoc->GetNodes().GetObject( aPaM.GetPara() );
    if ( aPaM.GetIndex() < pNode->GetText().Len() )
    {
        uno::Reference < i18n::XBreakIterator > xBI = mpImpl->mpTextEngine->GetBreakIterator();
        sal_Int32 nCount = 1;
        aPaM.GetIndex() = (sal_uInt16)xBI->nextCharacters( pNode->GetText(), aPaM.GetIndex(), mpImpl->mpTextEngine->GetLocale(), nCharacterIteratorMode, nCount, nCount );
    }
    else if ( aPaM.GetPara() < ( mpImpl->mpTextEngine->mpDoc->GetNodes().Count()-1) )
    {
        aPaM.GetPara()++;
        aPaM.GetIndex() = 0;
    }

    return aPaM;
}


TextPaM TextView::CursorWordLeft( const TextPaM& rPaM )
{
    TextPaM aPaM( rPaM );

    if ( aPaM.GetIndex() )
    {
        TextNode* pNode = mpImpl->mpTextEngine->mpDoc->GetNodes().GetObject( aPaM.GetPara() );
        uno::Reference < i18n::XBreakIterator > xBI = mpImpl->mpTextEngine->GetBreakIterator();
        i18n::Boundary aBoundary = xBI->getWordBoundary( pNode->GetText(), rPaM.GetIndex(), mpImpl->mpTextEngine->GetLocale(), i18n::WordType::ANYWORD_IGNOREWHITESPACES, sal_True );
        if ( aBoundary.startPos >= rPaM.GetIndex() )
            aBoundary = xBI->previousWord( pNode->GetText(), rPaM.GetIndex(), mpImpl->mpTextEngine->GetLocale(), i18n::WordType::ANYWORD_IGNOREWHITESPACES );
        aPaM.GetIndex() = ( aBoundary.startPos != -1 ) ? (sal_uInt16)aBoundary.startPos : 0;
    }
    else if ( aPaM.GetPara() )
    {
        aPaM.GetPara()--;
        TextNode* pNode = mpImpl->mpTextEngine->mpDoc->GetNodes().GetObject( aPaM.GetPara() );
        aPaM.GetIndex() = pNode->GetText().Len();
    }
    return aPaM;
}


TextPaM TextView::CursorWordRight( const TextPaM& rPaM )
{
    TextPaM aPaM( rPaM );

    TextNode* pNode = mpImpl->mpTextEngine->mpDoc->GetNodes().GetObject( aPaM.GetPara() );
    if ( aPaM.GetIndex() < pNode->GetText().Len() )
    {
        uno::Reference < i18n::XBreakIterator > xBI = mpImpl->mpTextEngine->GetBreakIterator();
        i18n::Boundary aBoundary = xBI->nextWord(  pNode->GetText(), aPaM.GetIndex(), mpImpl->mpTextEngine->GetLocale(), i18n::WordType::ANYWORD_IGNOREWHITESPACES );
        aPaM.GetIndex() = (sal_uInt16)aBoundary.startPos;
    }
    else if ( aPaM.GetPara() < ( mpImpl->mpTextEngine->mpDoc->GetNodes().Count()-1) )
    {
        aPaM.GetPara()++;
        aPaM.GetIndex() = 0;
    }

    return aPaM;
}

TextPaM TextView::ImpDelete( sal_uInt8 nMode, sal_uInt8 nDelMode )
{
    if ( mpImpl->maSelection.HasRange() )  // dann nur Sel. loeschen
        return mpImpl->mpTextEngine->ImpDeleteText( mpImpl->maSelection );

    TextPaM aStartPaM = mpImpl->maSelection.GetStart();
    TextPaM aEndPaM = aStartPaM;
    if ( nMode == DEL_LEFT )
    {
        if ( nDelMode == DELMODE_SIMPLE )
        {
            aEndPaM = CursorLeft( aEndPaM, (sal_uInt16)i18n::CharacterIteratorMode::SKIPCHARACTER );
        }
        else if ( nDelMode == DELMODE_RESTOFWORD )
        {
            TextNode* pNode = mpImpl->mpTextEngine->mpDoc->GetNodes().GetObject(  aEndPaM.GetPara() );
            uno::Reference < i18n::XBreakIterator > xBI = mpImpl->mpTextEngine->GetBreakIterator();
            i18n::Boundary aBoundary = xBI->getWordBoundary( pNode->GetText(), mpImpl->maSelection.GetEnd().GetIndex(), mpImpl->mpTextEngine->GetLocale(), i18n::WordType::ANYWORD_IGNOREWHITESPACES, sal_True );
            if ( aBoundary.startPos == mpImpl->maSelection.GetEnd().GetIndex() )
                aBoundary = xBI->previousWord( pNode->GetText(), mpImpl->maSelection.GetEnd().GetIndex(), mpImpl->mpTextEngine->GetLocale(), i18n::WordType::ANYWORD_IGNOREWHITESPACES );
            // #i63506# startPos is -1 when the paragraph starts with a tab
            aEndPaM.GetIndex() = (aBoundary.startPos >= 0) ? (sal_uInt16)aBoundary.startPos : 0;
        }
        else    // DELMODE_RESTOFCONTENT
        {
            if ( aEndPaM.GetIndex() != 0 )
                aEndPaM.GetIndex() = 0;
            else if ( aEndPaM.GetPara() )
            {
                // Absatz davor
                aEndPaM.GetPara()--;
                aEndPaM.GetIndex() = 0;
            }
        }
    }
    else
    {
        if ( nDelMode == DELMODE_SIMPLE )
        {
            aEndPaM = CursorRight( aEndPaM, (sal_uInt16)i18n::CharacterIteratorMode::SKIPCELL );
        }
        else if ( nDelMode == DELMODE_RESTOFWORD )
        {
            TextNode* pNode = mpImpl->mpTextEngine->mpDoc->GetNodes().GetObject(  aEndPaM.GetPara() );
            uno::Reference < i18n::XBreakIterator > xBI = mpImpl->mpTextEngine->GetBreakIterator();
            i18n::Boundary aBoundary = xBI->nextWord( pNode->GetText(), mpImpl->maSelection.GetEnd().GetIndex(), mpImpl->mpTextEngine->GetLocale(), i18n::WordType::ANYWORD_IGNOREWHITESPACES );
            aEndPaM.GetIndex() = (sal_uInt16)aBoundary.startPos;
        }
        else    // DELMODE_RESTOFCONTENT
        {
            TextNode* pNode = mpImpl->mpTextEngine->mpDoc->GetNodes().GetObject( aEndPaM.GetPara() );
            if ( aEndPaM.GetIndex() < pNode->GetText().Len() )
                aEndPaM.GetIndex() = pNode->GetText().Len();
            else if ( aEndPaM.GetPara() < ( mpImpl->mpTextEngine->mpDoc->GetNodes().Count() - 1 ) )
            {
                // Absatz danach
                aEndPaM.GetPara()++;
                TextNode* pNextNode = mpImpl->mpTextEngine->mpDoc->GetNodes().GetObject( aEndPaM.GetPara() );
                aEndPaM.GetIndex() = pNextNode->GetText().Len();
            }
        }
    }

    return mpImpl->mpTextEngine->ImpDeleteText( TextSelection( aStartPaM, aEndPaM ) );
}



TextPaM TextView::CursorUp( const TextPaM& rPaM )
{
    TextPaM aPaM( rPaM );

    long nX;
    if ( mpImpl->mnTravelXPos == TRAVEL_X_DONTKNOW )
    {
        nX = mpImpl->mpTextEngine->GetEditCursor( rPaM, sal_False ).Left();
        mpImpl->mnTravelXPos = (sal_uInt16)nX+1;
    }
    else
        nX = mpImpl->mnTravelXPos;

    TEParaPortion* pPPortion = mpImpl->mpTextEngine->mpTEParaPortions->GetObject( rPaM.GetPara() );
    sal_uInt16 nLine = pPPortion->GetLineNumber( rPaM.GetIndex(), sal_False );
    if ( nLine )    // gleicher Absatz
    {
        sal_uInt16 nCharPos = mpImpl->mpTextEngine->GetCharPos( rPaM.GetPara(), nLine-1, nX );
        aPaM.GetIndex() = nCharPos;
        // Wenn davor eine autom.Umgebrochene Zeile, und ich muss genau an das
        // Ende dieser Zeile, landet der Cursor in der aktuellen Zeile am Anfang
        // Siehe Problem: Letztes Zeichen einer autom.umgebr. Zeile = Cursor
        TextLine* pLine = pPPortion->GetLines().GetObject( nLine - 1 );
        if ( aPaM.GetIndex() && ( aPaM.GetIndex() == pLine->GetEnd() ) )
            aPaM.GetIndex()--;
    }
    else if ( rPaM.GetPara() )  // vorheriger Absatz
    {
        aPaM.GetPara()--;
        pPPortion = mpImpl->mpTextEngine->mpTEParaPortions->GetObject( aPaM.GetPara() );
        sal_uInt16 nL = pPPortion->GetLines().Count() - 1;
        sal_uInt16 nCharPos = mpImpl->mpTextEngine->GetCharPos( aPaM.GetPara(), nL, nX+1 );
        aPaM.GetIndex() = nCharPos;
    }

    return aPaM;
}

TextPaM TextView::CursorDown( const TextPaM& rPaM )
{
    TextPaM aPaM( rPaM );

    long nX;
    if ( mpImpl->mnTravelXPos == TRAVEL_X_DONTKNOW )
    {
        nX = mpImpl->mpTextEngine->GetEditCursor( rPaM, sal_False ).Left();
        mpImpl->mnTravelXPos = (sal_uInt16)nX+1;
    }
    else
        nX = mpImpl->mnTravelXPos;

    TEParaPortion* pPPortion = mpImpl->mpTextEngine->mpTEParaPortions->GetObject( rPaM.GetPara() );
    sal_uInt16 nLine = pPPortion->GetLineNumber( rPaM.GetIndex(), sal_False );
    if ( nLine < ( pPPortion->GetLines().Count() - 1 ) )
    {
        sal_uInt16 nCharPos = mpImpl->mpTextEngine->GetCharPos( rPaM.GetPara(), nLine+1, nX );
        aPaM.GetIndex() = nCharPos;

        // Sonderbehandlung siehe CursorUp...
        TextLine* pLine = pPPortion->GetLines().GetObject( nLine + 1 );
        if ( ( aPaM.GetIndex() == pLine->GetEnd() ) && ( aPaM.GetIndex() > pLine->GetStart() ) && aPaM.GetIndex() < pPPortion->GetNode()->GetText().Len() )
            aPaM.GetIndex()--;
    }
    else if ( rPaM.GetPara() < ( mpImpl->mpTextEngine->mpDoc->GetNodes().Count() - 1 ) )   // naechster Absatz
    {
        aPaM.GetPara()++;
        pPPortion = mpImpl->mpTextEngine->mpTEParaPortions->GetObject( aPaM.GetPara() );
        sal_uInt16 nCharPos = mpImpl->mpTextEngine->GetCharPos( aPaM.GetPara(), 0, nX+1 );
        aPaM.GetIndex() = nCharPos;
        TextLine* pLine = pPPortion->GetLines().GetObject( 0 );
        if ( ( aPaM.GetIndex() == pLine->GetEnd() ) && ( aPaM.GetIndex() > pLine->GetStart() ) && ( pPPortion->GetLines().Count() > 1 ) )
            aPaM.GetIndex()--;
    }

    return aPaM;
}

TextPaM TextView::CursorStartOfLine( const TextPaM& rPaM )
{
    TextPaM aPaM( rPaM );

    TEParaPortion* pPPortion = mpImpl->mpTextEngine->mpTEParaPortions->GetObject( rPaM.GetPara() );
    sal_uInt16 nLine = pPPortion->GetLineNumber( aPaM.GetIndex(), sal_False );
    TextLine* pLine = pPPortion->GetLines().GetObject( nLine );
    aPaM.GetIndex() = pLine->GetStart();

    return aPaM;
}

TextPaM TextView::CursorEndOfLine( const TextPaM& rPaM )
{
    TextPaM aPaM( rPaM );

    TEParaPortion* pPPortion = mpImpl->mpTextEngine->mpTEParaPortions->GetObject( rPaM.GetPara() );
    sal_uInt16 nLine = pPPortion->GetLineNumber( aPaM.GetIndex(), sal_False );
    TextLine* pLine = pPPortion->GetLines().GetObject( nLine );
    aPaM.GetIndex() = pLine->GetEnd();

    if ( pLine->GetEnd() > pLine->GetStart() )  // Leerzeile
    {
        xub_Unicode cLastChar = pPPortion->GetNode()->GetText().GetChar((sal_uInt16)(aPaM.GetIndex()-1) );
        if ( ( cLastChar == ' ' ) && ( aPaM.GetIndex() != pPPortion->GetNode()->GetText().Len() ) )
        {
            // Bei einem Blank in einer autom. umgebrochenen Zeile macht es Sinn,
            // davor zu stehen, da der Anwender hinter das Wort will.
            // Wenn diese geaendert wird, Sonderbehandlung fuer Pos1 nach End!
            aPaM.GetIndex()--;
        }
    }
    return aPaM;
}

TextPaM TextView::CursorStartOfParagraph( const TextPaM& rPaM )
{
    TextPaM aPaM( rPaM );
    aPaM.GetIndex() = 0;
    return aPaM;
}

TextPaM TextView::CursorEndOfParagraph( const TextPaM& rPaM )
{
    TextNode* pNode = mpImpl->mpTextEngine->mpDoc->GetNodes().GetObject( rPaM.GetPara() );
    TextPaM aPaM( rPaM );
    aPaM.GetIndex() = pNode->GetText().Len();
    return aPaM;
}

TextPaM TextView::CursorStartOfDoc()
{
    TextPaM aPaM( 0, 0 );
    return aPaM;
}

TextPaM TextView::CursorEndOfDoc()
{
    sal_uLong nNode = mpImpl->mpTextEngine->mpDoc->GetNodes().Count() - 1;
    TextNode* pNode = mpImpl->mpTextEngine->mpDoc->GetNodes().GetObject( nNode );
    TextPaM aPaM( nNode, pNode->GetText().Len() );
    return aPaM;
}

TextPaM TextView::PageUp( const TextPaM& rPaM )
{
    Rectangle aRec = mpImpl->mpTextEngine->PaMtoEditCursor( rPaM );
    Point aTopLeft = aRec.TopLeft();
    aTopLeft.Y() -= mpImpl->mpWindow->GetOutputSizePixel().Height() * 9/10;
    aTopLeft.X() += 1;
    if ( aTopLeft.Y() < 0 )
        aTopLeft.Y() = 0;

    TextPaM aPaM = mpImpl->mpTextEngine->GetPaM( aTopLeft );
    return aPaM;
}

TextPaM TextView::PageDown( const TextPaM& rPaM )
{
    Rectangle aRec = mpImpl->mpTextEngine->PaMtoEditCursor( rPaM );
    Point aBottomRight = aRec.BottomRight();
    aBottomRight.Y() += mpImpl->mpWindow->GetOutputSizePixel().Height() * 9/10;
    aBottomRight.X() += 1;
    long nHeight = mpImpl->mpTextEngine->GetTextHeight();
    if ( aBottomRight.Y() > nHeight )
        aBottomRight.Y() = nHeight-1;

    TextPaM aPaM = mpImpl->mpTextEngine->GetPaM( aBottomRight );
    return aPaM;
}

void TextView::ImpShowCursor( sal_Bool bGotoCursor, sal_Bool bForceVisCursor, sal_Bool bSpecial )
{
    if ( mpImpl->mpTextEngine->IsFormatting() )
        return;
    if ( mpImpl->mpTextEngine->GetUpdateMode() == sal_False )
        return;
    if ( mpImpl->mpTextEngine->IsInUndo() )
        return;

    mpImpl->mpTextEngine->CheckIdleFormatter();
    if ( !mpImpl->mpTextEngine->IsFormatted() )
        mpImpl->mpTextEngine->FormatAndUpdate( this );


    TextPaM aPaM( mpImpl->maSelection.GetEnd() );
    Rectangle aEditCursor = mpImpl->mpTextEngine->PaMtoEditCursor( aPaM, bSpecial );

    // Remember that we placed the cursor behind the last character of a line
    mpImpl->mbCursorAtEndOfLine = false;
    if( bSpecial )
    {
        TEParaPortion* pParaPortion = mpImpl->mpTextEngine->mpTEParaPortions->GetObject( aPaM.GetPara() );
        mpImpl->mbCursorAtEndOfLine =
            pParaPortion->GetLineNumber( aPaM.GetIndex(), sal_True ) != pParaPortion->GetLineNumber( aPaM.GetIndex(), sal_False );
    }

    if ( !IsInsertMode() && !mpImpl->maSelection.HasRange() )
    {
        TextNode* pNode = mpImpl->mpTextEngine->mpDoc->GetNodes().GetObject( aPaM.GetPara() );
        if ( pNode->GetText().Len() && ( aPaM.GetIndex() < pNode->GetText().Len() ) )
        {
            // If we are behind a portion, and the next portion has other direction, we must change position...
            aEditCursor.Left() = aEditCursor.Right() = mpImpl->mpTextEngine->GetEditCursor( aPaM, sal_False, sal_True ).Left();

            TEParaPortion* pParaPortion = mpImpl->mpTextEngine->mpTEParaPortions->GetObject( aPaM.GetPara() );

            sal_uInt16 nTextPortionStart = 0;
            sal_uInt16 nTextPortion = pParaPortion->GetTextPortions().FindPortion( aPaM.GetIndex(), nTextPortionStart, sal_True );
            TETextPortion* pTextPortion = pParaPortion->GetTextPortions().GetObject( nTextPortion );
            if ( pTextPortion->GetKind() == PORTIONKIND_TAB )
            {
                if ( mpImpl->mpTextEngine->IsRightToLeft() )
                {

                }
                aEditCursor.Right() += pTextPortion->GetWidth();
            }
            else
            {
                TextPaM aNext = CursorRight( TextPaM( aPaM.GetPara(), aPaM.GetIndex() ), (sal_uInt16)i18n::CharacterIteratorMode::SKIPCELL );
                aEditCursor.Right() = mpImpl->mpTextEngine->GetEditCursor( aNext, sal_True ).Left();
            }
        }
    }

    Size aOutSz = mpImpl->mpWindow->GetOutputSizePixel();
    if ( aEditCursor.GetHeight() > aOutSz.Height() )
        aEditCursor.Bottom() = aEditCursor.Top() + aOutSz.Height() - 1;

    aEditCursor.Left() -= 1;

    if ( bGotoCursor
        // #i81283# protext maStartDocPos against initialization problems
        && aOutSz.Width() && aOutSz.Height()
    )
    {
        long nVisStartY = mpImpl->maStartDocPos.Y();
        long nVisEndY = mpImpl->maStartDocPos.Y() + aOutSz.Height();
        long nVisStartX = mpImpl->maStartDocPos.X();
        long nVisEndX = mpImpl->maStartDocPos.X() + aOutSz.Width();
        long nMoreX = aOutSz.Width() / 4;

        Point aNewStartPos( mpImpl->maStartDocPos );

        if ( aEditCursor.Bottom() > nVisEndY )
        {
            aNewStartPos.Y() += ( aEditCursor.Bottom() - nVisEndY );
        }
        else if ( aEditCursor.Top() < nVisStartY )
        {
            aNewStartPos.Y() -= ( nVisStartY - aEditCursor.Top() );
        }

        if ( aEditCursor.Right() >= nVisEndX )
        {
            aNewStartPos.X() += ( aEditCursor.Right() - nVisEndX );

            // Darfs ein bischen mehr sein?
            aNewStartPos.X() += nMoreX;
        }
        else if ( aEditCursor.Left() <= nVisStartX )
        {
            aNewStartPos.X() -= ( nVisStartX - aEditCursor.Left() );

            // Darfs ein bischen mehr sein?
            aNewStartPos.X() -= nMoreX;
        }

        // X kann durch das 'bischen mehr' falsch sein:
//      sal_uLong nMaxTextWidth = mpImpl->mpTextEngine->GetMaxTextWidth();
//      if ( !nMaxTextWidth || ( nMaxTextWidth > 0x7FFFFFFF ) )
//          nMaxTextWidth = 0x7FFFFFFF;
//      long nMaxX = (long)nMaxTextWidth - aOutSz.Width();
        long nMaxX = mpImpl->mpTextEngine->CalcTextWidth() - aOutSz.Width();
        if ( nMaxX < 0 )
            nMaxX = 0;

        if ( aNewStartPos.X() < 0 )
            aNewStartPos.X() = 0;
        else if ( aNewStartPos.X() > nMaxX )
            aNewStartPos.X() = nMaxX;

        // Y sollte nicht weiter unten als noetig liegen:
        long nYMax = mpImpl->mpTextEngine->GetTextHeight() - aOutSz.Height();
        if ( nYMax < 0 )
            nYMax = 0;
        if ( aNewStartPos.Y() > nYMax )
            aNewStartPos.Y() = nYMax;

        if ( aNewStartPos != mpImpl->maStartDocPos )
            Scroll( -(aNewStartPos.X() - mpImpl->maStartDocPos.X()), -(aNewStartPos.Y() - mpImpl->maStartDocPos.Y()) );
    }

    if ( aEditCursor.Right() < aEditCursor.Left() )
    {
        long n = aEditCursor.Left();
        aEditCursor.Left() = aEditCursor.Right();
        aEditCursor.Right() = n;
    }

    Point aPoint( GetWindowPos( !mpImpl->mpTextEngine->IsRightToLeft() ? aEditCursor.TopLeft() : aEditCursor.TopRight() ) );
    mpImpl->mpCursor->SetPos( aPoint );
    mpImpl->mpCursor->SetSize( aEditCursor.GetSize() );
    if ( bForceVisCursor && mpImpl->mbCursorEnabled )
        mpImpl->mpCursor->Show();
}

sal_Bool TextView::SetCursorAtPoint( const Point& rPosPixel )
{
    mpImpl->mpTextEngine->CheckIdleFormatter();

    Point aDocPos = GetDocPos( rPosPixel );

    TextPaM aPaM = mpImpl->mpTextEngine->GetPaM( aDocPos );

    // aTmpNewSel: Diff zwischen alt und neu, nicht die neue Selektion
    TextSelection aTmpNewSel( mpImpl->maSelection.GetEnd(), aPaM );
    TextSelection aNewSel( mpImpl->maSelection );
    aNewSel.GetEnd() = aPaM;

    if ( !mpImpl->mpSelEngine->HasAnchor() )
    {
        if ( mpImpl->maSelection.GetStart() != aPaM )
            mpImpl->mpTextEngine->CursorMoved( mpImpl->maSelection.GetStart().GetPara() );
        aNewSel.GetStart() = aPaM;
        ImpSetSelection( aNewSel );
    }
    else
    {
        ImpSetSelection( aNewSel );
        ShowSelection( aTmpNewSel );
    }

    sal_Bool bForceCursor =  mpImpl->mpDDInfo ? sal_False : sal_True; // && !mbInSelection
    ImpShowCursor( mpImpl->mbAutoScroll, bForceCursor, sal_False );
    return sal_True;
}

sal_Bool TextView::IsSelectionAtPoint( const Point& rPosPixel )
{
//  if ( !Rectangle( Point(), mpImpl->mpWindow->GetOutputSizePixel() ).IsInside( rPosPixel ) && !mbInSelection )
//      return sal_False;

    Point aDocPos = GetDocPos( rPosPixel );
    TextPaM aPaM = mpImpl->mpTextEngine->GetPaM( aDocPos, sal_False );
    // Bei Hyperlinks D&D auch ohne Selektion starten.
    // BeginDrag wird aber nur gerufen, wenn IsSelectionAtPoint()
    // Problem: IsSelectionAtPoint wird bei Command() nicht gerufen,
    // wenn vorher im MBDown schon sal_False returnt wurde.
    return ( IsInSelection( aPaM ) ||
            ( /* mpImpl->mpSelEngine->IsInCommand() && */ mpImpl->mpTextEngine->FindAttrib( aPaM, TEXTATTR_HYPERLINK ) ) );
}

sal_Bool TextView::IsInSelection( const TextPaM& rPaM )
{
    TextSelection aSel = mpImpl->maSelection;
    aSel.Justify();

    sal_uLong nStartNode = aSel.GetStart().GetPara();
    sal_uLong nEndNode = aSel.GetEnd().GetPara();
    sal_uLong nCurNode = rPaM.GetPara();

    if ( ( nCurNode > nStartNode ) && ( nCurNode < nEndNode ) )
        return sal_True;

    if ( nStartNode == nEndNode )
    {
        if ( nCurNode == nStartNode )
            if ( ( rPaM.GetIndex() >= aSel.GetStart().GetIndex() ) && ( rPaM.GetIndex() < aSel.GetEnd().GetIndex() ) )
                return sal_True;
    }
    else if ( ( nCurNode == nStartNode ) && ( rPaM.GetIndex() >= aSel.GetStart().GetIndex() ) )
        return sal_True;
    else if ( ( nCurNode == nEndNode ) && ( rPaM.GetIndex() < aSel.GetEnd().GetIndex() ) )
        return sal_True;

    return sal_False;
}

void TextView::ImpHideDDCursor()
{
    if ( mpImpl->mpDDInfo && mpImpl->mpDDInfo->mbVisCursor )
    {
        mpImpl->mpDDInfo->maCursor.Hide();
        mpImpl->mpDDInfo->mbVisCursor = sal_False;
    }
}

void TextView::ImpShowDDCursor()
{
    if ( !mpImpl->mpDDInfo->mbVisCursor )
    {
        Rectangle aCursor = mpImpl->mpTextEngine->PaMtoEditCursor( mpImpl->mpDDInfo->maDropPos, sal_True );
        aCursor.Right()++;
        aCursor.SetPos( GetWindowPos( aCursor.TopLeft() ) );

        mpImpl->mpDDInfo->maCursor.SetWindow( mpImpl->mpWindow );
        mpImpl->mpDDInfo->maCursor.SetPos( aCursor.TopLeft() );
        mpImpl->mpDDInfo->maCursor.SetSize( aCursor.GetSize() );
        mpImpl->mpDDInfo->maCursor.Show();
        mpImpl->mpDDInfo->mbVisCursor = sal_True;
    }
}

void TextView::SetPaintSelection( sal_Bool bPaint )
{
    if ( bPaint != mpImpl->mbPaintSelection )
    {
        mpImpl->mbPaintSelection = bPaint;
        ShowSelection( mpImpl->maSelection );
    }
}

sal_Bool TextView::Read( SvStream& rInput )
{
    sal_Bool bDone = mpImpl->mpTextEngine->Read( rInput, &mpImpl->maSelection );
    ShowCursor();
    return bDone;
}

bool TextView::ImplTruncateNewText( rtl::OUString& rNewText ) const
{
    bool bTruncated = false;

    if( rNewText.getLength() > 65534 ) // limit to String API
    {
        rNewText = rNewText.copy( 0, 65534 );
        bTruncated = true;
    }

    sal_uLong nMaxLen = mpImpl->mpTextEngine->GetMaxTextLen();
    // 0 means unlimited, there is just the String API limit handled above
    if( nMaxLen != 0 )
    {
        sal_uLong nCurLen = mpImpl->mpTextEngine->GetTextLen();

        sal_uInt32 nNewLen = rNewText.getLength();
        if ( nCurLen + nNewLen > nMaxLen )
        {
            // see how much text will be replaced
            sal_uLong nSelLen = mpImpl->mpTextEngine->GetTextLen( mpImpl->maSelection );
            if ( nCurLen + nNewLen - nSelLen > nMaxLen )
            {
                sal_uInt32 nTruncatedLen = static_cast<sal_uInt32>(nMaxLen - (nCurLen - nSelLen));
                rNewText = rNewText.copy( 0, nTruncatedLen );
                bTruncated = true;
            }
        }
    }
    return bTruncated;
}

sal_Bool TextView::ImplCheckTextLen( const String& rNewText )
{
    sal_Bool bOK = sal_True;
    if ( mpImpl->mpTextEngine->GetMaxTextLen() )
    {
        sal_uLong n = mpImpl->mpTextEngine->GetTextLen();
        n += rNewText.Len();
        if ( n > mpImpl->mpTextEngine->GetMaxTextLen() )
        {
            // nur dann noch ermitteln, wie viel Text geloescht wird
            n -= mpImpl->mpTextEngine->GetTextLen( mpImpl->maSelection );
            if ( n > mpImpl->mpTextEngine->GetMaxTextLen() )
            {
                // Beep hat hier eigentlich nichts verloren, sondern lieber ein Hdl,
                // aber so funktioniert es wenigstens in ME, BasicIDE, SourceView
                Sound::Beep();
                bOK = sal_False;
            }
        }
    }
    return bOK;
}

void TextView::dragGestureRecognized( const ::com::sun::star::datatransfer::dnd::DragGestureEvent& rDGE ) throw (::com::sun::star::uno::RuntimeException)
{
    if ( mpImpl->mbClickedInSelection )
    {
        SolarMutexGuard aVclGuard;

        DBG_ASSERT( mpImpl->maSelection.HasRange(), "TextView::dragGestureRecognized: mpImpl->mbClickedInSelection, but no selection?" );

        delete mpImpl->mpDDInfo;
        mpImpl->mpDDInfo = new TextDDInfo;
        mpImpl->mpDDInfo->mbStarterOfDD = sal_True;

        TETextDataObject* pDataObj = new TETextDataObject( GetSelected() );

        if ( mpImpl->mpTextEngine->HasAttrib( TEXTATTR_HYPERLINK ) )  // Dann auch als HTML
            mpImpl->mpTextEngine->Write( pDataObj->GetHTMLStream(), &mpImpl->maSelection, sal_True );


        /*
        // D&D eines Hyperlinks.
        // Besser waere es im MBDown sich den MBDownPaM zu merken,
        // ist dann aber inkompatibel => spaeter mal umstellen.
        TextPaM aPaM( mpImpl->mpTextEngine->GetPaM( GetDocPos( GetWindow()->GetPointerPosPixel() ) ) );
        const TextCharAttrib* pAttr = mpImpl->mpTextEngine->FindCharAttrib( aPaM, TEXTATTR_HYPERLINK );
        if ( pAttr )
        {
            aSel = aPaM;
            aSel.GetStart().GetIndex() = pAttr->GetStart();
            aSel.GetEnd().GetIndex() = pAttr->GetEnd();

            const TextAttribHyperLink& rLink = (const TextAttribHyperLink&)pAttr->GetAttr();
            String aText( rLink.GetDescription() );
            if ( !aText.Len() )
                aText = mpImpl->mpTextEngine->GetText( aSel );
            INetBookmark aBookmark( rLink.GetURL(), aText );
            aBookmark.CopyDragServer();
        }
        */

        mpImpl->mpCursor->Hide();

        sal_Int8 nActions = datatransfer::dnd::DNDConstants::ACTION_COPY;
        if ( !IsReadOnly() )
            nActions |= datatransfer::dnd::DNDConstants::ACTION_MOVE;
        rDGE.DragSource->startDrag( rDGE, nActions, 0 /*cursor*/, 0 /*image*/, pDataObj, mpImpl->mxDnDListener );
    }
}

void TextView::dragDropEnd( const ::com::sun::star::datatransfer::dnd::DragSourceDropEvent& ) throw (::com::sun::star::uno::RuntimeException)
{
    ImpHideDDCursor();
    delete mpImpl->mpDDInfo;
    mpImpl->mpDDInfo = NULL;
}

void TextView::drop( const ::com::sun::star::datatransfer::dnd::DropTargetDropEvent& rDTDE ) throw (::com::sun::star::uno::RuntimeException)
{
    SolarMutexGuard aVclGuard;

    sal_Bool bChanges = sal_False;
    if ( !mpImpl->mbReadOnly && mpImpl->mpDDInfo )
    {
        ImpHideDDCursor();

        // Daten fuer das loeschen nach einem DROP_MOVE:
        TextSelection aPrevSel( mpImpl->maSelection );
        aPrevSel.Justify();
        sal_uLong nPrevParaCount = mpImpl->mpTextEngine->GetParagraphCount();
        sal_uInt16 nPrevStartParaLen = mpImpl->mpTextEngine->GetTextLen( aPrevSel.GetStart().GetPara() );

        sal_Bool bStarterOfDD = sal_False;
        for ( sal_uInt16 nView = mpImpl->mpTextEngine->GetViewCount(); nView && !bStarterOfDD; )
            bStarterOfDD = mpImpl->mpTextEngine->GetView( --nView )->mpImpl->mpDDInfo ? mpImpl->mpTextEngine->GetView( nView )->mpImpl->mpDDInfo->mbStarterOfDD : sal_False;

        HideSelection();
        ImpSetSelection( mpImpl->mpDDInfo->maDropPos );

        mpImpl->mpTextEngine->UndoActionStart();

        String aText;
        uno::Reference< datatransfer::XTransferable > xDataObj = rDTDE.Transferable;
        if ( xDataObj.is() )
        {
            datatransfer::DataFlavor aFlavor;
            SotExchange::GetFormatDataFlavor( SOT_FORMAT_STRING, aFlavor );
            if ( xDataObj->isDataFlavorSupported( aFlavor ) )
            {
                uno::Any aData = xDataObj->getTransferData( aFlavor );
                ::rtl::OUString aOUString;
                aData >>= aOUString;
                aText = convertLineEnd(aOUString, LINEEND_LF);
            }
        }

        if ( aText.Len() && ( aText.GetChar( aText.Len()-1 ) == LINE_SEP ) )
            aText.Erase( aText.Len()-1 );

        TextPaM aTempStart = mpImpl->maSelection.GetStart();
        if ( ImplCheckTextLen( aText ) )
            ImpSetSelection( mpImpl->mpTextEngine->ImpInsertText( mpImpl->mpDDInfo->maDropPos, aText ) );
        if(mpImpl->mbSupportProtectAttribute)
        {
            mpImpl->mpTextEngine->SetAttrib( TextAttribProtect(),
                aTempStart.GetPara(),
                aTempStart.GetIndex(),
                mpImpl->maSelection.GetEnd().GetIndex(), sal_False );
        }

        if ( aPrevSel.HasRange() &&
                !mpImpl->mbSupportProtectAttribute && // don't remove currently selected element
                (( rDTDE.DropAction & datatransfer::dnd::DNDConstants::ACTION_MOVE ) || !bStarterOfDD) )
        {
            // ggf. Selection anpasssen:
            if ( ( mpImpl->mpDDInfo->maDropPos.GetPara() < aPrevSel.GetStart().GetPara() ) ||
                 ( ( mpImpl->mpDDInfo->maDropPos.GetPara() == aPrevSel.GetStart().GetPara() )
                        && ( mpImpl->mpDDInfo->maDropPos.GetIndex() < aPrevSel.GetStart().GetIndex() ) ) )
            {
                sal_uLong nNewParasBeforeSelection =
                    mpImpl->mpTextEngine->GetParagraphCount() -    nPrevParaCount;

                aPrevSel.GetStart().GetPara() += nNewParasBeforeSelection;
                aPrevSel.GetEnd().GetPara() += nNewParasBeforeSelection;

                if ( mpImpl->mpDDInfo->maDropPos.GetPara() == aPrevSel.GetStart().GetPara() )
                {
                    sal_uInt16 nNewChars =
                        mpImpl->mpTextEngine->GetTextLen( aPrevSel.GetStart().GetPara() ) - nPrevStartParaLen;

                    aPrevSel.GetStart().GetIndex() =
                        aPrevSel.GetStart().GetIndex() + nNewChars;
                    if ( aPrevSel.GetStart().GetPara() == aPrevSel.GetEnd().GetPara() )
                        aPrevSel.GetEnd().GetIndex() =
                            aPrevSel.GetEnd().GetIndex() + nNewChars;
                }
            }
            else
            {
                // aktuelle Selektion anpassen
                TextPaM aPaM = mpImpl->maSelection.GetStart();
                aPaM.GetPara() -= ( aPrevSel.GetEnd().GetPara() - aPrevSel.GetStart().GetPara() );
                if ( aPrevSel.GetEnd().GetPara() == mpImpl->mpDDInfo->maDropPos.GetPara() )
                {
                    aPaM.GetIndex() =
                        aPaM.GetIndex() - aPrevSel.GetEnd().GetIndex();
                    if ( aPrevSel.GetStart().GetPara() == mpImpl->mpDDInfo->maDropPos.GetPara() )
                        aPaM.GetIndex() =
                            aPaM.GetIndex() + aPrevSel.GetStart().GetIndex();
                }
                ImpSetSelection( aPaM );

            }
            mpImpl->mpTextEngine->ImpDeleteText( aPrevSel );
        }

        mpImpl->mpTextEngine->UndoActionEnd();

        delete mpImpl->mpDDInfo;
        mpImpl->mpDDInfo = 0;

        mpImpl->mpTextEngine->FormatAndUpdate( this );

        mpImpl->mpTextEngine->Broadcast( TextHint( TEXT_HINT_MODIFIED ) );
    }
    rDTDE.Context->dropComplete( bChanges );
}

void TextView::dragEnter( const ::com::sun::star::datatransfer::dnd::DropTargetDragEnterEvent& ) throw (::com::sun::star::uno::RuntimeException)
{
}

void TextView::dragExit( const ::com::sun::star::datatransfer::dnd::DropTargetEvent& ) throw (::com::sun::star::uno::RuntimeException)
{
    SolarMutexGuard aVclGuard;
    ImpHideDDCursor();
}

void TextView::dragOver( const ::com::sun::star::datatransfer::dnd::DropTargetDragEvent& rDTDE ) throw (::com::sun::star::uno::RuntimeException)
{
    SolarMutexGuard aVclGuard;

    if ( !mpImpl->mpDDInfo )
        mpImpl->mpDDInfo = new TextDDInfo;

    TextPaM aPrevDropPos = mpImpl->mpDDInfo->maDropPos;
    Point aMousePos( rDTDE.LocationX, rDTDE.LocationY );
    Point aDocPos = GetDocPos( aMousePos );
    mpImpl->mpDDInfo->maDropPos = mpImpl->mpTextEngine->GetPaM( aDocPos );

    sal_Bool bProtected = sal_False;
    if(mpImpl->mbSupportProtectAttribute)
    {
        const TextCharAttrib* pStartAttr = mpImpl->mpTextEngine->FindCharAttrib(
                    mpImpl->mpDDInfo->maDropPos,
                    TEXTATTR_PROTECTED );
        bProtected = pStartAttr != 0 &&
                pStartAttr->GetStart() != mpImpl->mpDDInfo->maDropPos.GetIndex() &&
                pStartAttr->GetEnd() != mpImpl->mpDDInfo->maDropPos.GetIndex();
    }
    // Don't drop in selection or in read only engine
    if ( IsReadOnly() || IsInSelection( mpImpl->mpDDInfo->maDropPos ) || bProtected)
    {
        ImpHideDDCursor();
        rDTDE.Context->rejectDrag();
    }
    else
    {
        // Alten Cursor wegzeichnen...
        if ( !mpImpl->mpDDInfo->mbVisCursor || ( aPrevDropPos != mpImpl->mpDDInfo->maDropPos ) )
        {
            ImpHideDDCursor();
            ImpShowDDCursor();
        }
        rDTDE.Context->acceptDrag( rDTDE.DropAction );
    }
}

Point TextView::ImpGetOutputStartPos( const Point& rStartDocPos ) const
{
    Point aStartPos( -rStartDocPos.X(), -rStartDocPos.Y() );
    if ( mpImpl->mpTextEngine->IsRightToLeft() )
    {
        Size aSz = mpImpl->mpWindow->GetOutputSizePixel();
        aStartPos.X() = rStartDocPos.X() + aSz.Width() - 1; // -1: Start is 0
    }
    return aStartPos;
}

Point TextView::GetDocPos( const Point& rWindowPos ) const
{
    // Fensterposition => Dokumentposition

    Point aPoint;

    aPoint.Y() = rWindowPos.Y() + mpImpl->maStartDocPos.Y();

    if ( !mpImpl->mpTextEngine->IsRightToLeft() )
    {
        aPoint.X() = rWindowPos.X() + mpImpl->maStartDocPos.X();
    }
    else
    {
        Size aSz = mpImpl->mpWindow->GetOutputSizePixel();
        aPoint.X() = ( aSz.Width() - 1 ) - rWindowPos.X() + mpImpl->maStartDocPos.X();
    }

    return aPoint;
}

Point TextView::GetWindowPos( const Point& rDocPos ) const
{
    // Dokumentposition => Fensterposition

    Point aPoint;

    aPoint.Y() = rDocPos.Y() - mpImpl->maStartDocPos.Y();

    if ( !mpImpl->mpTextEngine->IsRightToLeft() )
    {
        aPoint.X() = rDocPos.X() - mpImpl->maStartDocPos.X();
    }
    else
    {
        Size aSz = mpImpl->mpWindow->GetOutputSizePixel();
        aPoint.X() = ( aSz.Width() - 1 ) - ( rDocPos.X() - mpImpl->maStartDocPos.X() );
    }

    return aPoint;
}

sal_Int32 TextView::GetLineNumberOfCursorInSelection() const
{
 // PROGRESS
    sal_Int32 nLineNo = -1;
    if( mpImpl->mbCursorEnabled )
    {
        TextPaM aPaM = GetSelection().GetEnd();
        TEParaPortion* pPPortion = mpImpl->mpTextEngine->mpTEParaPortions->GetObject( aPaM.GetPara() );
        nLineNo = pPPortion->GetLineNumber( aPaM.GetIndex(), sal_False );
        if( mpImpl->mbCursorAtEndOfLine )
            --nLineNo;
    }
    return nLineNo;
}


// -------------------------------------------------------------------------
// (+) class TextSelFunctionSet
// -------------------------------------------------------------------------
TextSelFunctionSet::TextSelFunctionSet( TextView* pView )
{
    mpView = pView;
}

void TextSelFunctionSet::BeginDrag()
{
}

void TextSelFunctionSet::CreateAnchor()
{
//  TextSelection aSel( mpView->GetSelection() );
//  aSel.GetStart() = aSel.GetEnd();
//  mpView->SetSelection( aSel );

    // Es darf kein ShowCursor folgen:
    mpView->HideSelection();
    mpView->ImpSetSelection( mpView->mpImpl->maSelection.GetEnd() );
}

sal_Bool TextSelFunctionSet::SetCursorAtPoint( const Point& rPointPixel, sal_Bool )
{
    return mpView->SetCursorAtPoint( rPointPixel );
}

sal_Bool TextSelFunctionSet::IsSelectionAtPoint( const Point& rPointPixel )
{
    return mpView->IsSelectionAtPoint( rPointPixel );
}

void TextSelFunctionSet::DeselectAll()
{
    CreateAnchor();
}

void TextSelFunctionSet::DeselectAtPoint( const Point& )
{
    // Nur bei Mehrfachselektion
}

void TextSelFunctionSet::DestroyAnchor()
{
    // Nur bei Mehrfachselektion
}
TextEngine*         TextView::GetTextEngine() const
{ return mpImpl->mpTextEngine; }
Window*             TextView::GetWindow() const
{ return mpImpl->mpWindow; }
void                TextView::EnableCursor( sal_Bool bEnable )
{ mpImpl->mbCursorEnabled = bEnable; }
sal_Bool                TextView::IsCursorEnabled() const
{ return mpImpl->mbCursorEnabled; }
void                TextView::SetStartDocPos( const Point& rPos )
{ mpImpl->maStartDocPos = rPos; }
const Point&        TextView::GetStartDocPos() const
{ return mpImpl->maStartDocPos; }
void                TextView::SetAutoIndentMode( sal_Bool bAutoIndent )
{ mpImpl->mbAutoIndent = bAutoIndent; }
sal_Bool                TextView::IsReadOnly() const
{ return mpImpl->mbReadOnly; }
void                TextView::SetAutoScroll( sal_Bool bAutoScroll )
{ mpImpl->mbAutoScroll = bAutoScroll; }
sal_Bool                TextView::IsAutoScroll() const
{ return mpImpl->mbAutoScroll; }
sal_Bool                TextView::HasSelection() const
{ return mpImpl->maSelection.HasRange(); }
sal_Bool                TextView::IsInsertMode() const
{ return mpImpl->mbInsertMode; }
void                TextView::SupportProtectAttribute(sal_Bool bSupport)
{ mpImpl->mbSupportProtectAttribute = bSupport;}

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
