/* -*- 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.
 *
 ************************************************************************/

// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_sw.hxx"
#include <com/sun/star/util/SearchOptions.hpp>
#include <com/sun/star/text/XTextRange.hpp>
#include <hintids.hxx>
#include <svx/svdmodel.hxx>
#include <editeng/frmdiritem.hxx>

#include <SwSmartTagMgr.hxx>
#include <doc.hxx>
#include <rootfrm.hxx>
#include <pagefrm.hxx>
#include <cntfrm.hxx>
#include <viewimp.hxx>
#include <pam.hxx>
#include <swselectionlist.hxx>
#include <IBlockCursor.hxx>
#include "BlockCursor.hxx"
#include <ndtxt.hxx>
#include <flyfrm.hxx>
#include <dview.hxx>
#include <viewopt.hxx>
#include <frmtool.hxx>
#include <crsrsh.hxx>
#include <tabfrm.hxx>
#include <txtfrm.hxx>
#include <sectfrm.hxx>
#include <swtable.hxx>
#include <callnk.hxx>
#include <viscrs.hxx>
#include <section.hxx>
#include <docsh.hxx>
#include <scriptinfo.hxx>
#include <globdoc.hxx>
#include <pamtyp.hxx>
#include <mdiexp.hxx>			// ...Percent()
#include <fmteiro.hxx>
#include <wrong.hxx> // SMARTTAGS
#include <unotextrange.hxx> // SMARTTAGS
#include <vcl/svapp.hxx>
#include <numrule.hxx>
#include <IGrammarContact.hxx>

#include <globals.hrc>

#include <comcore.hrc>

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

TYPEINIT2(SwCrsrShell,ViewShell,SwModify);


// Funktion loescht, alle ueberlappenden Cursor aus einem Cursor-Ring
void CheckRange( SwCursor* );

//-----------------------------------------------------------------------

/*
 * Ueberpruefe ob der pCurCrsr in einen schon bestehenden Bereich zeigt.
 * Wenn ja, dann hebe den alten Bereich auf.
 */


void CheckRange( SwCursor* pCurCrsr )
{
    const SwPosition *pStt = pCurCrsr->Start(),
        *pEnd = pCurCrsr->GetPoint() == pStt ? pCurCrsr->GetMark() : pCurCrsr->GetPoint();

    SwPaM *pTmpDel = 0,
          *pTmp = (SwPaM*)pCurCrsr->GetNext();

    // durchsuche den gesamten Ring
    while( pTmp != pCurCrsr )
    {
        const SwPosition *pTmpStt = pTmp->Start(),
                        *pTmpEnd = pTmp->GetPoint() == pTmpStt ?
                                        pTmp->GetMark() : pTmp->GetPoint();
        if( *pStt <= *pTmpStt )
        {
            if( *pEnd > *pTmpStt ||
                ( *pEnd == *pTmpStt && *pEnd == *pTmpEnd ))
                pTmpDel = pTmp;
        }
        else
            if( *pStt < *pTmpEnd )
                pTmpDel = pTmp;
        /*
         * liegt ein SPoint oder GetMark innerhalb vom Crsr-Bereich
         * muss der alte Bereich aufgehoben werden.
         * Beim Vergleich ist darauf zu achten, das SPoint nicht mehr zum
         * Bereich gehoert !
         */
        pTmp = (SwPaM*)pTmp->GetNext();
        if( pTmpDel )
        {
            delete pTmpDel;         // hebe alten Bereich auf
            pTmpDel = 0;
        }
    }
}

// -------------- Methoden von der SwCrsrShell -------------

SwPaM * SwCrsrShell::CreateCrsr()
{
    // Innerhalb der Tabellen-SSelection keinen neuen Crsr anlegen
    OSL_ENSURE( !IsTableMode(), "in Tabellen SSelection" );

    // neuen Cursor als Kopie vom akt. und in den Ring aufnehmen
    // Verkettung zeigt immer auf den zuerst erzeugten, also vorwaerts
    SwShellCrsr* pNew = new SwShellCrsr( *pCurCrsr );

    // hier den akt. Pam nur logisch Hiden, weil sonst die Invertierung
    // vom kopierten Pam aufgehoben wird !!

    // #i75172#
    pNew->swapContent(*pCurCrsr);

    pCurCrsr->DeleteMark();

    UpdateCrsr( SwCrsrShell::SCROLLWIN );
//	return pCurCrsr;
    return pNew;
}

// loesche den aktuellen Cursor und der folgende wird zum Aktuellen


sal_Bool SwCrsrShell::DestroyCrsr()
{
    // Innerhalb der Tabellen-SSelection keinen neuen Crsr loeschen
    OSL_ENSURE( !IsTableMode(), "in Tabellen SSelection" );

    // ist ueberhaupt ein naechtser vorhanden ?
    if(pCurCrsr->GetNext() == pCurCrsr)
        return sal_False;

    SwCallLink aLk( *this );        // Crsr-Moves ueberwachen,
    SwCursor* pNextCrsr = (SwCursor*)pCurCrsr->GetNext();
    delete pCurCrsr;
    pCurCrsr = dynamic_cast<SwShellCrsr*>(pNextCrsr);
    UpdateCrsr();
    return sal_True;
}


SwPaM & SwCrsrShell::CreateNewShellCursor()
{
    if (HasSelection())
    {
        (void) CreateCrsr(); // n.b. returns old cursor
    }
    return *GetCrsr();
}

SwPaM & SwCrsrShell::GetCurrentShellCursor()
{
    return *GetCrsr();
}


// gebe den aktuellen zurueck

SwPaM* SwCrsrShell::GetCrsr( sal_Bool bMakeTblCrsr ) const
{
    if( pTblCrsr )
    {
        if( bMakeTblCrsr && pTblCrsr->IsCrsrMovedUpdt() )
        {
            // geparkte Cursor werden nicht wieder erzeugt
            const SwCntntNode* pCNd;
            if( pTblCrsr->GetPoint()->nNode.GetIndex() &&
                pTblCrsr->GetMark()->nNode.GetIndex() &&
                0 != ( pCNd = pTblCrsr->GetCntntNode() ) && pCNd->getLayoutFrm( GetLayout() ) &&
                0 != ( pCNd = pTblCrsr->GetCntntNode(sal_False) ) && pCNd->getLayoutFrm( GetLayout() ) )
            {
                SwShellTableCrsr* pTC = (SwShellTableCrsr*)pTblCrsr;
                GetLayout()->MakeTblCrsrs( *pTC );
            }
        }

        if( pTblCrsr->IsChgd() )
        {
            const_cast<SwCrsrShell*>(this)->pCurCrsr =
                dynamic_cast<SwShellCrsr*>(pTblCrsr->MakeBoxSels( pCurCrsr ));
        }
    }
    return pCurCrsr;
}


void SwCrsrShell::StartAction()
{
    if( !ActionPend() )
    {
        // fuer das Update des Ribbon-Bars merken
        const SwNode& rNd = pCurCrsr->GetPoint()->nNode.GetNode();
        nAktNode = rNd.GetIndex();
        nAktCntnt = pCurCrsr->GetPoint()->nContent.GetIndex();
        nAktNdTyp = rNd.GetNodeType();
        bAktSelection = *pCurCrsr->GetPoint() != *pCurCrsr->GetMark();
        if( ND_TEXTNODE & nAktNdTyp )
            nLeftFrmPos = SwCallLink::getLayoutFrm( GetLayout(), (SwTxtNode&)rNd, nAktCntnt, sal_True );
        else
            nLeftFrmPos = 0;
    }
    ViewShell::StartAction();           // zur ViewShell
}


void SwCrsrShell::EndAction( const sal_Bool bIdleEnd )
{

    sal_Bool bVis = bSVCrsrVis;

    // Idle-Formatierung ?
    if( bIdleEnd && Imp()->GetRegion() )
    {
        pCurCrsr->Hide();
    }

    // vor der letzten Action alle invaliden Numerierungen updaten
    if( 1 == nStartAction )
        GetDoc()->UpdateNumRule();

    // Task: 76923: dont show the cursor in the ViewShell::EndAction() - call.
    //				Only the UpdateCrsr shows the cursor.
    sal_Bool bSavSVCrsrVis = bSVCrsrVis;
    bSVCrsrVis = sal_False;

    ViewShell::EndAction( bIdleEnd );	//der ViewShell den Vortritt lassen

    bSVCrsrVis = bSavSVCrsrVis;

    if( ActionPend() )
    {
        if( bVis )    // auch SV-Cursor wieder anzeigen
            pVisCrsr->Show();

        // falls noch ein ChgCall vorhanden ist und nur noch die Basic
        // Klammerung vorhanden ist, dann rufe ihn. Dadurch wird die interne
        // mit der Basic-Klammerung entkoppelt; die Shells werden umgeschaltet
        if( !BasicActionPend() )
        {
            // es muss innerhalb einer BasicAction
            //				der Cursor geupdatet werden; um z.B. den
            //				TabellenCursor zu erzeugen. Im UpdateCrsr wird
            //				das jetzt beruecksichtigt!
            UpdateCrsr( SwCrsrShell::CHKRANGE, bIdleEnd );

            {
                // Crsr-Moves ueberwachen, evt. Link callen
                // der DTOR ist das interressante!!
                SwCallLink aLk( *this, nAktNode, nAktCntnt, (sal_uInt8)nAktNdTyp,
                                nLeftFrmPos, bAktSelection );

            }
            if( bCallChgLnk && bChgCallFlag && aChgLnk.IsSet() )
            {
                aChgLnk.Call( this );
                bChgCallFlag = sal_False;		// Flag zuruecksetzen
            }
        }
        return;
    }

    sal_uInt16 nParm = SwCrsrShell::CHKRANGE;
    if ( !bIdleEnd )
        nParm |= SwCrsrShell::SCROLLWIN;
//    if( !IsViewLocked() )
    UpdateCrsr( nParm, bIdleEnd );		// Cursor-Aenderungen anzeigen

    {
        SwCallLink aLk( *this );        // Crsr-Moves ueberwachen,
        aLk.nNode = nAktNode;           // evt. Link callen
        aLk.nNdTyp = (sal_uInt8)nAktNdTyp;
        aLk.nCntnt = nAktCntnt;
        aLk.nLeftFrmPos = nLeftFrmPos;

        if( !nCrsrMove ||
            ( 1 == nCrsrMove && bInCMvVisportChgd ) )
            ShowCrsrs( bSVCrsrVis ? sal_True : sal_False );    // Cursor & Selektionen wieder anzeigen
    }
    // falls noch ein ChgCall vorhanden ist, dann rufe ihn
    if( bCallChgLnk && bChgCallFlag && aChgLnk.IsSet() )
    {
        aChgLnk.Call( this );
        bChgCallFlag = sal_False;		// Flag zuruecksetzen
    }
}


#if OSL_DEBUG_LEVEL > 1

void SwCrsrShell::SttCrsrMove()
{
    OSL_ENSURE( nCrsrMove < USHRT_MAX, "To many nested CrsrMoves." );
    ++nCrsrMove;
    StartAction();
}

void SwCrsrShell::EndCrsrMove( const sal_Bool bIdleEnd )
{
    OSL_ENSURE( nCrsrMove, "EndCrsrMove() ohne SttCrsrMove()." );
    EndAction( bIdleEnd );
    if( !--nCrsrMove )
        bInCMvVisportChgd = sal_False;
}

#endif


sal_Bool SwCrsrShell::LeftRight( sal_Bool bLeft, sal_uInt16 nCnt, sal_uInt16 nMode,
                                 sal_Bool bVisualAllowed )
{
    if( IsTableMode() )
        return bLeft ? GoPrevCell() : GoNextCell();

    SwCallLink aLk( *this );        // Crsr-Moves ueberwachen, evt. Link callen
    sal_Bool bRet = sal_False;

    // #i27615# Handle cursor in front of label.
    const SwTxtNode* pTxtNd = 0;

    if( pBlockCrsr )
        pBlockCrsr->clearPoints();

    //
    // 1. CASE: Cursor is in front of label. A move to the right
    // will simply reset the bInFrontOfLabel flag:
    //
    SwShellCrsr* pShellCrsr = getShellCrsr( true );
    if ( !bLeft && pShellCrsr->IsInFrontOfLabel() )
    {
        SetInFrontOfLabel( sal_False );
        bRet = sal_True;
    }
    //
    // 2. CASE: Cursor is at beginning of numbered paragraph. A move
    // to the left will simply set the bInFrontOfLabel flag:
    //
    else if ( bLeft && 0 == pShellCrsr->GetPoint()->nContent.GetIndex() &&
             !pShellCrsr->IsInFrontOfLabel() && !pShellCrsr->HasMark() &&
             0 != ( pTxtNd = pShellCrsr->GetNode()->GetTxtNode() ) &&
             pTxtNd->HasVisibleNumberingOrBullet() )
    {
        SetInFrontOfLabel( sal_True );
        bRet = sal_True;
    }
    //
    // 3. CASE: Regular cursor move. Reset the bInFrontOfLabel flag:
    //
    else
    {
        const sal_Bool bSkipHidden = !GetViewOptions()->IsShowHiddenChar();
        // #i107447#
        // To avoid loop the reset of <bInFrontOfLabel> flag is no longer
        // reflected in the return value <bRet>.
        const bool bResetOfInFrontOfLabel = SetInFrontOfLabel( sal_False );
        bRet = pShellCrsr->LeftRight( bLeft, nCnt, nMode, bVisualAllowed,
                                      bSkipHidden, !IsOverwriteCrsr() );
        if ( !bRet && bLeft && bResetOfInFrontOfLabel )
        {
            // undo reset of <bInFrontOfLabel> flag
            SetInFrontOfLabel( sal_True );
        }
    }

    if( bRet )
    {
        UpdateCrsr();
    }
    return bRet;
}

void SwCrsrShell::MarkListLevel( const String& sListId,
                                 const int nListLevel )
{
    if ( sListId != sMarkedListId ||
         nListLevel != nMarkedListLevel)
    {
        if ( sMarkedListId.Len() > 0 )
            pDoc->MarkListLevel( sMarkedListId, nMarkedListLevel, sal_False );

        if ( sListId.Len() > 0 )
        {
            pDoc->MarkListLevel( sListId, nListLevel, sal_True );
        }

        sMarkedListId = sListId;
        nMarkedListLevel = nListLevel;
    }
}

void SwCrsrShell::UpdateMarkedListLevel()
{
    SwTxtNode * pTxtNd = _GetCrsr()->GetNode()->GetTxtNode();

    if ( pTxtNd )
    {
        if ( !pTxtNd->IsNumbered() )
        {
            pCurCrsr->_SetInFrontOfLabel( sal_False );
            MarkListLevel( String(), 0 );
        }
        else if ( pCurCrsr->IsInFrontOfLabel() )
        {
            if ( pTxtNd->IsInList() )
            {
                OSL_ENSURE( pTxtNd->GetActualListLevel() >= 0 &&
                        pTxtNd->GetActualListLevel() < MAXLEVEL, "Which level?");
                MarkListLevel( pTxtNd->GetListId(),
                               pTxtNd->GetActualListLevel() );
            }
        }
        else
        {
            MarkListLevel( String(), 0 );
        }
    }
}
// <--

sal_Bool SwCrsrShell::UpDown( sal_Bool bUp, sal_uInt16 nCnt )
{
    SET_CURR_SHELL( this );
    SwCallLink aLk( *this );        // Crsr-Moves ueberwachen, evt. Link callen

    sal_Bool bTableMode = IsTableMode();
    SwShellCrsr* pTmpCrsr = getShellCrsr( true );

    sal_Bool bRet = pTmpCrsr->UpDown( bUp, nCnt );
    // #i40019# UpDown should always reset the bInFrontOfLabel flag:
    bRet = SetInFrontOfLabel(sal_False) || bRet;

    if( pBlockCrsr )
        pBlockCrsr->clearPoints();

    if( bRet )
    {
        eMvState = MV_UPDOWN;		// Status fuers Crsr-Travelling - GetCrsrOfst
        if( !ActionPend() )
        {
            CrsrFlag eUpdtMode = SwCrsrShell::SCROLLWIN;
            if( !bTableMode )
                eUpdtMode = (CrsrFlag) (eUpdtMode
                            | SwCrsrShell::UPDOWN | SwCrsrShell::CHKRANGE);
            UpdateCrsr( static_cast<sal_uInt16>(eUpdtMode) );
        }
    }
    return bRet;
}


sal_Bool SwCrsrShell::LRMargin( sal_Bool bLeft, sal_Bool bAPI)
{
    SwCallLink aLk( *this );        // Crsr-Moves ueberwachen, evt. Link callen
    SET_CURR_SHELL( this );
    eMvState = MV_LEFTMARGIN;		// Status fuers Crsr-Travelling - GetCrsrOfst

    const sal_Bool bTableMode = IsTableMode();
    SwShellCrsr* pTmpCrsr = getShellCrsr( true );

    if( pBlockCrsr )
        pBlockCrsr->clearPoints();

    const sal_Bool bWasAtLM =
            ( 0 == _GetCrsr()->GetPoint()->nContent.GetIndex() );

    sal_Bool bRet = pTmpCrsr->LeftRightMargin( bLeft, bAPI );

    if ( bLeft && !bTableMode && bRet && bWasAtLM && !_GetCrsr()->HasMark() )
    {
        const SwTxtNode * pTxtNd = _GetCrsr()->GetNode()->GetTxtNode();
        if ( pTxtNd && pTxtNd->HasVisibleNumberingOrBullet() )
            SetInFrontOfLabel( sal_True );
    }
    else if ( !bLeft )
    {
        bRet = SetInFrontOfLabel( sal_False ) || bRet;
    }

    if( bRet )
    {
        UpdateCrsr();
    }
    return bRet;
}

sal_Bool SwCrsrShell::IsAtLRMargin( sal_Bool bLeft, sal_Bool bAPI ) const
{
    const SwShellCrsr* pTmpCrsr = getShellCrsr( true );
    return pTmpCrsr->IsAtLeftRightMargin( bLeft, bAPI );
}


sal_Bool SwCrsrShell::SttEndDoc( sal_Bool bStt )
{
    SwCallLink aLk( *this );        // Crsr-Moves ueberwachen, evt. Link callen

    SwShellCrsr* pTmpCrsr = pBlockCrsr ? &pBlockCrsr->getShellCrsr() : pCurCrsr;
    sal_Bool bRet = pTmpCrsr->SttEndDoc( bStt );
    if( bRet )
    {
        if( bStt )
            pTmpCrsr->GetPtPos().Y() = 0;		// expl. 0 setzen (TabellenHeader)
        if( pBlockCrsr )
        {
            pBlockCrsr->clearPoints();
            RefreshBlockCursor();
        }

        UpdateCrsr(SwCrsrShell::SCROLLWIN|SwCrsrShell::CHKRANGE|SwCrsrShell::READONLY);
    }
    return bRet;
}

void SwCrsrShell::ExtendedSelectAll()
{
    SwNodes& rNodes = GetDoc()->GetNodes();
    SwPosition* pPos = pCurCrsr->GetPoint();
    pPos->nNode = rNodes.GetEndOfPostIts();
    pPos->nContent.Assign( rNodes.GoNext( &pPos->nNode ), 0 );
    pPos = pCurCrsr->GetMark();
    pPos->nNode = rNodes.GetEndOfContent();
    SwCntntNode* pCNd = rNodes.GoPrevious( &pPos->nNode );
    pPos->nContent.Assign( pCNd, pCNd ? pCNd->Len() : 0 );
}

sal_Bool SwCrsrShell::MovePage( SwWhichPage fnWhichPage, SwPosPage fnPosPage )
{
    sal_Bool bRet = sal_False;

    // Springe beim Selektieren nie ueber Section-Grenzen !!
    if( !pCurCrsr->HasMark() || !pCurCrsr->IsNoCntnt() )
    {
        SwCallLink aLk( *this );        // Crsr-Moves ueberwachen, evt. Link callen
        SET_CURR_SHELL( this );

        SwCrsrSaveState aSaveState( *pCurCrsr );
        Point& rPt = pCurCrsr->GetPtPos();
        SwCntntFrm * pFrm = pCurCrsr->GetCntntNode()->
                            getLayoutFrm( GetLayout(), &rPt, pCurCrsr->GetPoint(), sal_False );
        if( pFrm && sal_True == ( bRet = GetFrmInPage( pFrm, fnWhichPage,
                                                fnPosPage, pCurCrsr )  ) &&
            !pCurCrsr->IsSelOvr( nsSwCursorSelOverFlags::SELOVER_TOGGLE |
                                 nsSwCursorSelOverFlags::SELOVER_CHANGEPOS ))
            UpdateCrsr();
        else
            bRet = sal_False;
    }
    return bRet;
}


sal_Bool SwCrsrShell::MovePara(SwWhichPara fnWhichPara, SwPosPara fnPosPara )
{
    SwCallLink aLk( *this );        // Crsr-Moves ueberwachen, evt. Link callen
    SwCursor* pTmpCrsr = getShellCrsr( true );
    sal_Bool bRet = pTmpCrsr->MovePara( fnWhichPara, fnPosPara );
    if( bRet )
        UpdateCrsr();
    return bRet;
}


sal_Bool SwCrsrShell::MoveSection( SwWhichSection fnWhichSect,
                                SwPosSection fnPosSect)
{
    SwCallLink aLk( *this );        // Crsr-Moves ueberwachen, evt. Link callen
    SwCursor* pTmpCrsr = getShellCrsr( true );
    sal_Bool bRet = pTmpCrsr->MoveSection( fnWhichSect, fnPosSect );
    if( bRet )
        UpdateCrsr();
    return bRet;

}


// Positionieren des Cursors


SwFrm* lcl_IsInHeaderFooter( const SwNodeIndex& rIdx, Point& rPt )
{
    SwFrm* pFrm = 0;
    SwCntntNode* pCNd = rIdx.GetNode().GetCntntNode();
    if( pCNd )
    {
        pFrm = pCNd->getLayoutFrm( pCNd->GetDoc()->GetCurrentLayout(), &rPt, 0, sal_False )->GetUpper();
        while( pFrm && !pFrm->IsHeaderFrm() && !pFrm->IsFooterFrm() )
            pFrm = pFrm->IsFlyFrm() ? ((SwFlyFrm*)pFrm)->AnchorFrm()
                                    : pFrm->GetUpper();
    }
    return pFrm;
}

sal_Bool SwCrsrShell::IsInHeaderFooter( sal_Bool* pbInHeader ) const
{
    Point aPt;
    SwFrm* pFrm = ::lcl_IsInHeaderFooter( pCurCrsr->GetPoint()->nNode, aPt );
    if( pFrm && pbInHeader )
        *pbInHeader = pFrm->IsHeaderFrm();
    return 0 != pFrm;
}

int SwCrsrShell::SetCrsr( const Point &rLPt, sal_Bool bOnlyText, bool bBlock )
{
    SET_CURR_SHELL( this );

    SwShellCrsr* pCrsr = getShellCrsr( bBlock );
    SwPosition aPos( *pCrsr->GetPoint() );
    Point aPt( rLPt );
    Point & rAktCrsrPt = pCrsr->GetPtPos();
    SwCrsrMoveState aTmpState( IsTableMode() ? MV_TBLSEL :
                                    bOnlyText ?  MV_SETONLYTEXT : MV_NONE );
    aTmpState.bSetInReadOnly = IsReadOnlyAvailable();

    SwTxtNode * pTxtNd = pCrsr->GetNode()->GetTxtNode();

    if ( pTxtNd && !IsTableMode() &&
        // --> FME 2004-11-25 #i37515# No bInFrontOfLabel during selection
        !pCrsr->HasMark() &&
        // <--
        pTxtNd->HasVisibleNumberingOrBullet() )
    {
        aTmpState.bInFrontOfLabel = sal_True; // #i27615#
    }
    else
    {
        aTmpState.bInFrontOfLabel = sal_False;
    }

    int bRet = CRSR_POSOLD |
                ( GetLayout()->GetCrsrOfst( &aPos, aPt, &aTmpState )
                    ? 0 : CRSR_POSCHG );

    const bool bOldInFrontOfLabel = IsInFrontOfLabel();
    const bool bNewInFrontOfLabel = aTmpState.bInFrontOfLabel;

    pCrsr->SetCrsrBidiLevel( aTmpState.nCursorBidiLevel );

    if( MV_RIGHTMARGIN == aTmpState.eState )
        eMvState = MV_RIGHTMARGIN;
    // steht neu Pos im Header/Footer ?
    SwFrm* pFrm = lcl_IsInHeaderFooter( aPos.nNode, aPt );
    if( IsTableMode() && !pFrm && aPos.nNode.GetNode().StartOfSectionNode() ==
        pCrsr->GetPoint()->nNode.GetNode().StartOfSectionNode() )
        // gleiche Tabellenzelle und nicht im Header/Footer
        // -> zurueck
        return bRet;

    if( pBlockCrsr && bBlock )
    {
        pBlockCrsr->setEndPoint( rLPt );
        if( !pCrsr->HasMark() )
            pBlockCrsr->setStartPoint( rLPt );
        else if( !pBlockCrsr->getStartPoint() )
            pBlockCrsr->setStartPoint( pCrsr->GetMkPos() );
    }
    if( !pCrsr->HasMark() )
    {
        // steht an der gleichen Position und wenn im Header/Footer,
        // dann im gleichen
        if( aPos == *pCrsr->GetPoint() &&
            bOldInFrontOfLabel == bNewInFrontOfLabel )
        {
            if( pFrm )
            {
                if( pFrm->Frm().IsInside( rAktCrsrPt ))
                    return bRet;
            }
            else if( aPos.nNode.GetNode().IsCntntNode() )
            {
                // im gleichen Frame gelandet?
                SwFrm* pOld = ((SwCntntNode&)aPos.nNode.GetNode()).getLayoutFrm(
                                GetLayout(), &aCharRect.Pos(), 0, sal_False );
                SwFrm* pNew = ((SwCntntNode&)aPos.nNode.GetNode()).getLayoutFrm(
                                GetLayout(), &aPt, 0, sal_False );
                if( pNew == pOld )
                    return bRet;
            }
        }
    }
    else
    {
        // SSelection ueber nicht erlaubte Sections oder wenn im Header/Footer
        // dann in verschiedene
        if( !CheckNodesRange( aPos.nNode, pCrsr->GetMark()->nNode, sal_True )
            || ( pFrm && !pFrm->Frm().IsInside( pCrsr->GetMkPos() ) ))
            return bRet;

        // steht an der gleichen Position und nicht im Header/Footer
        if( aPos == *pCrsr->GetPoint() )
            return bRet;
    }

    SwCallLink aLk( *this );        // Crsr-Moves ueberwachen, evt. Link callen
    SwCrsrSaveState aSaveState( *pCrsr );

    *pCrsr->GetPoint() = aPos;
    rAktCrsrPt = aPt;

    // #i41424# Only update the marked number levels if necessary
    // Force update of marked number levels if necessary.
    if ( bNewInFrontOfLabel || bOldInFrontOfLabel )
        pCurCrsr->_SetInFrontOfLabel( !bNewInFrontOfLabel );
    SetInFrontOfLabel( bNewInFrontOfLabel );

    if( !pCrsr->IsSelOvr( nsSwCursorSelOverFlags::SELOVER_CHANGEPOS ) )
    {
        sal_uInt16 nFlag = SwCrsrShell::SCROLLWIN | SwCrsrShell::CHKRANGE;
        UpdateCrsr( nFlag );
        bRet &= ~CRSR_POSOLD;
    }
    else if( bOnlyText && !pCurCrsr->HasMark() )
    {
        if( FindValidCntntNode( bOnlyText ) )
        {
            // Cursor in einen gueltigen Content stellen
            if( aPos == *pCrsr->GetPoint() )
                bRet = CRSR_POSOLD;
            else
            {
                UpdateCrsr( SwCrsrShell::SCROLLWIN | SwCrsrShell::CHKRANGE );
                bRet &= ~CRSR_POSOLD;
            }
        }
        else
        {
            // es gibt keinen gueltigen Inhalt -> Cursor verstecken
            pVisCrsr->Hide();       // sichtbaren Cursor immer verstecken
            eMvState = MV_NONE;		// Status fuers Crsr-Travelling
            bAllProtect = sal_True;
            if( GetDoc()->GetDocShell() )
            {
                GetDoc()->GetDocShell()->SetReadOnlyUI( sal_True );
                CallChgLnk();			// UI bescheid sagen!
            }
        }
    }

    return bRet;
}


void SwCrsrShell::TblCrsrToCursor()
{
    OSL_ENSURE( pTblCrsr, "TblCrsrToCursor: Why?" );
    delete pTblCrsr, pTblCrsr = 0;
}

void SwCrsrShell::BlockCrsrToCrsr()
{
    OSL_ENSURE( pBlockCrsr, "BlockCrsrToCrsr: Why?" );
    if( pBlockCrsr && !HasSelection() )
    {
        SwPaM& rPam = pBlockCrsr->getShellCrsr();
        pCurCrsr->SetMark();
        *pCurCrsr->GetPoint() = *rPam.GetPoint();
        if( rPam.HasMark() )
            *pCurCrsr->GetMark() = *rPam.GetMark();
        else
            pCurCrsr->DeleteMark();
    }
    delete pBlockCrsr, pBlockCrsr = 0;
}

void SwCrsrShell::CrsrToBlockCrsr()
{
    if( !pBlockCrsr )
    {
        SwPosition aPos( *pCurCrsr->GetPoint() );
        pBlockCrsr = createBlockCursor( *this, aPos );
        SwShellCrsr &rBlock = pBlockCrsr->getShellCrsr();
        rBlock.GetPtPos() = pCurCrsr->GetPtPos();
        if( pCurCrsr->HasMark() )
        {
            rBlock.SetMark();
            *rBlock.GetMark() = *pCurCrsr->GetMark();
            rBlock.GetMkPos() = pCurCrsr->GetMkPos();
        }
    }
    pBlockCrsr->clearPoints();
    RefreshBlockCursor();
}

void SwCrsrShell::ClearMark()
{
    // ist ueberhaupt ein GetMark gesetzt ?
    if( pTblCrsr )
    {
        while( pCurCrsr->GetNext() != pCurCrsr )
            delete pCurCrsr->GetNext();
        pTblCrsr->DeleteMark();

        if( pCurCrsr->HasMark() )
        {
            // falls doch nicht alle Indizies richtig verschoben werden
            //	(z.B.: Kopf-/Fusszeile loeschen) den Content-Anteil vom
            //	Mark aufs Nodes-Array setzen
            SwPosition& rPos = *pCurCrsr->GetMark();
            rPos.nNode.Assign( pDoc->GetNodes(), 0 );
            rPos.nContent.Assign( 0, 0 );
            pCurCrsr->DeleteMark();
        }

        *pCurCrsr->GetPoint() = *pTblCrsr->GetPoint();
        pCurCrsr->GetPtPos() = pTblCrsr->GetPtPos();
        delete pTblCrsr, pTblCrsr = 0;
        pCurCrsr->SwSelPaintRects::Show();
    }
    else
    {
        if( !pCurCrsr->HasMark() )
            return;
        // falls doch nicht alle Indizies richtig verschoben werden
        //	(z.B.: Kopf-/Fusszeile loeschen) den Content-Anteil vom
        //	Mark aufs Nodes-Array setzen
        SwPosition& rPos = *pCurCrsr->GetMark();
        rPos.nNode.Assign( pDoc->GetNodes(), 0 );
        rPos.nContent.Assign( 0, 0 );
        pCurCrsr->DeleteMark();
        if( !nCrsrMove )
            pCurCrsr->SwSelPaintRects::Show();
    }
}


void SwCrsrShell::NormalizePam(sal_Bool bPointFirst)
{
    SwCallLink aLk( *this );        // Crsr-Moves ueberwachen, evt. Link callen
    pCurCrsr->Normalize(bPointFirst);
}

void SwCrsrShell::SwapPam()
{
    SwCallLink aLk( *this );        // Crsr-Moves ueberwachen, evt. Link callen
    pCurCrsr->Exchange();
}


// suche innerhalb der Selektierten-Bereiche nach einer Selektion, die
// den angebenen SPoint umschliesst
// Ist das Flag bTstOnly gesetzt, dann wird nur getestet, ob dort eine
// SSelection besteht; des akt. Cursr wird nicht umgesetzt!
// Ansonsten wird er auf die gewaehlte SSelection gesetzt.


sal_Bool SwCrsrShell::ChgCurrPam( const Point & rPt,
                              sal_Bool bTstOnly, sal_Bool bTstHit )
{
    SET_CURR_SHELL( this );

    // Pruefe ob der SPoint in einer Tabellen-Selektion liegt
    if( bTstOnly && pTblCrsr )
        return pTblCrsr->IsInside( rPt );

    SwCallLink aLk( *this );        // Crsr-Moves ueberwachen, evt. Link callen
    // Suche die Position rPt im Dokument
    SwPosition aPtPos( *pCurCrsr->GetPoint() );
    Point aPt( rPt );

    SwCrsrMoveState aTmpState( MV_NONE );
    aTmpState.bSetInReadOnly = IsReadOnlyAvailable();
    if ( !GetLayout()->GetCrsrOfst( &aPtPos, aPt, &aTmpState ) && bTstHit )
        return sal_False;

    // suche in allen Selektionen nach dieser Position
    SwShellCrsr* pCmp = (SwShellCrsr*)pCurCrsr;        // sicher den Pointer auf Cursor
    do {
        if( pCmp->HasMark() &&
            *pCmp->Start() <= aPtPos && *pCmp->End() > aPtPos )
        {
            if( bTstOnly || pCurCrsr == pCmp )	   // ist der aktuelle.
                return sal_True;         			   // return ohne Update

            pCurCrsr = pCmp;
            UpdateCrsr();     // Cursor steht schon richtig
            return sal_True;
        }
    } while( pCurCrsr !=
        ( pCmp = dynamic_cast<SwShellCrsr*>(pCmp->GetNext()) ) );
    return sal_False;
}


void SwCrsrShell::KillPams()
{
    // keiner zum loeschen vorhanden?
    if( !pTblCrsr && !pBlockCrsr && pCurCrsr->GetNext() == pCurCrsr )
        return;

    while( pCurCrsr->GetNext() != pCurCrsr )
        delete pCurCrsr->GetNext();
    pCurCrsr->SetColumnSelection( false );

    if( pTblCrsr )
    {
        // Cursor Ring loeschen
        pCurCrsr->DeleteMark();
        *pCurCrsr->GetPoint() = *pTblCrsr->GetPoint();
        pCurCrsr->GetPtPos() = pTblCrsr->GetPtPos();
        delete pTblCrsr;
        pTblCrsr = 0;
    }
    else if( pBlockCrsr )
    {
        // delete the ring of cursors
        pCurCrsr->DeleteMark();
        SwShellCrsr &rBlock = pBlockCrsr->getShellCrsr();
        *pCurCrsr->GetPoint() = *rBlock.GetPoint();
        pCurCrsr->GetPtPos() = rBlock.GetPtPos();
        rBlock.DeleteMark();
        pBlockCrsr->clearPoints();
    }
    UpdateCrsr( SwCrsrShell::SCROLLWIN );
}


int SwCrsrShell::CompareCursor( CrsrCompareType eType ) const
{
    int nRet = 0;
    const SwPosition *pFirst = 0, *pSecond = 0;
    const SwPaM *pCur = GetCrsr(), *pStk = pCrsrStk;
    if( CurrPtCurrMk != eType && pStk )
    {
        switch ( eType)
        {
        case StackPtStackMk:
            pFirst = pStk->GetPoint();
            pSecond = pStk->GetMark();
            break;
        case StackPtCurrPt:
            pFirst = pStk->GetPoint();
            pSecond = pCur->GetPoint();
            break;
        case StackPtCurrMk:
            pFirst = pStk->GetPoint();
            pSecond = pCur->GetMark();
            break;
        case StackMkCurrPt:
            pFirst = pStk->GetMark();
            pSecond = pCur->GetPoint();
            break;
        case StackMkCurrMk:
            pFirst = pStk->GetMark();
            pSecond = pStk->GetMark();
            break;
        case CurrPtCurrMk:
            pFirst = pCur->GetPoint();
            pSecond = pCur->GetMark();
            break;
        }
    }
    if( !pFirst || !pSecond )
        nRet = INT_MAX;
    else if( *pFirst < *pSecond )
        nRet = -1;
    else if( *pFirst == *pSecond )
        nRet = 0;
    else
        nRet = 1;
    return nRet;
}


sal_Bool SwCrsrShell::IsSttPara() const
{   return( pCurCrsr->GetPoint()->nContent == 0 ? sal_True : sal_False ); }


sal_Bool SwCrsrShell::IsEndPara() const
{   return( pCurCrsr->GetPoint()->nContent == pCurCrsr->GetCntntNode()->Len() ? sal_True : sal_False ); }


sal_Bool SwCrsrShell::IsInFrontOfLabel() const
{
    return pCurCrsr->IsInFrontOfLabel();
}

bool SwCrsrShell::SetInFrontOfLabel( sal_Bool bNew )
{
    if ( bNew != IsInFrontOfLabel() )
    {
        pCurCrsr->_SetInFrontOfLabel( bNew );
        UpdateMarkedListLevel();
        return true;
    }
    return false;
}

sal_Bool SwCrsrShell::GotoPage( sal_uInt16 nPage )
{
    SET_CURR_SHELL( this );
    SwCallLink aLk( *this );        // Crsr-Moves ueberwachen, evt. Link callen
    SwCrsrSaveState aSaveState( *pCurCrsr );
    sal_Bool bRet = GetLayout()->SetCurrPage( pCurCrsr, nPage ) &&
                    !pCurCrsr->IsSelOvr( nsSwCursorSelOverFlags::SELOVER_TOGGLE |
                                         nsSwCursorSelOverFlags::SELOVER_CHANGEPOS );
    if( bRet )
        UpdateCrsr(SwCrsrShell::SCROLLWIN|SwCrsrShell::CHKRANGE|SwCrsrShell::READONLY);
    return bRet;
}


void SwCrsrShell::GetPageNum( sal_uInt16 &rnPhyNum, sal_uInt16 &rnVirtNum,
                              sal_Bool bAtCrsrPos, const sal_Bool bCalcFrm )
{
    SET_CURR_SHELL( this );
    // Seitennummer: die erste sichtbare Seite oder die am Cursor
    const SwCntntFrm* pCFrm;
    const SwPageFrm *pPg = 0;

    if( !bAtCrsrPos || 0 == (pCFrm = GetCurrFrm( bCalcFrm )) ||
                       0 == (pPg   = pCFrm->FindPageFrm()) )
    {
        pPg = Imp()->GetFirstVisPage();
        while( pPg && pPg->IsEmptyPage() )
            pPg = (const SwPageFrm *)pPg->GetNext();
    }
    // Abfrage auf pPg muss fuer den Sonderfall Writerstart mit
    // standard.vor sein.
    rnPhyNum  = pPg? pPg->GetPhyPageNum() : 1;
    rnVirtNum = pPg? pPg->GetVirtPageNum() : 1;
}


sal_uInt16 SwCrsrShell::GetNextPrevPageNum( sal_Bool bNext )
{
    SET_CURR_SHELL( this );

    // Seitennummer: die erste sichtbare Seite oder die am Cursor
    const SwPageFrm *pPg = Imp()->GetFirstVisPage();
    if( pPg )
    {
        const SwTwips nPageTop = pPg->Frm().Top();

        if( bNext )
        {
            // go to next view layout row:
            do
            {
                pPg = (const SwPageFrm *)pPg->GetNext();
            }
            while( pPg && pPg->Frm().Top() == nPageTop );

            while( pPg && pPg->IsEmptyPage() )
                pPg = (const SwPageFrm *)pPg->GetNext();
        }
        else
        {
            // go to previous view layout row:
            do
            {
                pPg = (const SwPageFrm *)pPg->GetPrev();
            }
            while( pPg && pPg->Frm().Top() == nPageTop );

            while( pPg && pPg->IsEmptyPage() )
                pPg = (const SwPageFrm *)pPg->GetPrev();
        }
    }
    // Abfrage auf pPg muss fuer den Sonderfall Writerstart mit
    // standard.vor sein.
    return pPg ? pPg->GetPhyPageNum() : USHRT_MAX;
}


sal_uInt16 SwCrsrShell::GetPageCnt()
{
    SET_CURR_SHELL( this );
    // gebe die Anzahl der Seiten zurueck
    return GetLayout()->GetPageNum();
}

// Gehe zur naechsten SSelection


sal_Bool SwCrsrShell::GoNextCrsr()
{
    // besteht ueberhaupt ein Ring ?
    if( pCurCrsr->GetNext() == pCurCrsr )
        return sal_False;

    SET_CURR_SHELL( this );
    SwCallLink aLk( *this );        // Crsr-Moves ueberwachen, evt. Link callen
    pCurCrsr = dynamic_cast<SwShellCrsr*>(pCurCrsr->GetNext());

    // Bug 24086: auch alle anderen anzeigen
    if( !ActionPend() )
    {
        UpdateCrsr();
        pCurCrsr->Show();
    }
    return sal_True;
}

// gehe zur vorherigen SSelection


sal_Bool SwCrsrShell::GoPrevCrsr()
{
    // besteht ueberhaupt ein Ring ?
    if( pCurCrsr->GetNext() == pCurCrsr )
        return sal_False;

    SET_CURR_SHELL( this );
    SwCallLink aLk( *this );        // Crsr-Moves ueberwachen, evt. Link callen
    pCurCrsr = dynamic_cast<SwShellCrsr*>(pCurCrsr->GetPrev());

    // Bug 24086: auch alle anderen anzeigen
    if( !ActionPend() )
    {
        UpdateCrsr();
        pCurCrsr->Show();
    }

    return sal_True;
}


void SwCrsrShell::Paint( const Rectangle &rRect)
{
    SET_CURR_SHELL( this );

    // beim Painten immer alle Cursor ausschalten
    SwRect aRect( rRect );

    sal_Bool bVis = sal_False;
    // ist Cursor sichtbar, dann verstecke den SV-Cursor
    if( pVisCrsr->IsVisible() && !aRect.IsOver( aCharRect ) )	//JP 18.06.97: ???
    {
        bVis = sal_True;
        pVisCrsr->Hide();
    }

    // Bereich neu painten
    ViewShell::Paint( rRect );

    if( bHasFocus && !bBasicHideCrsr )
    {
        SwShellCrsr* pAktCrsr = pTblCrsr ? pTblCrsr : pCurCrsr;
//		pAktCrsr->Invalidate( aRect );
        if( !ActionPend() )
        {
            // damit nicht rechts/unten die Raender abgeschnitten werden
            pAktCrsr->Invalidate( VisArea() );
            pAktCrsr->Show();
        }
        else
            pAktCrsr->Invalidate( aRect );

    }
    if( bSVCrsrVis && bVis )        // auch SV-Cursor wieder anzeigen
        pVisCrsr->Show();
}



void SwCrsrShell::VisPortChgd( const SwRect & rRect )
{
    SET_CURR_SHELL( this );
    sal_Bool bVis;      // beim Scrollen immer alle Cursor ausschalten

    // ist Cursor sichtbar, dann verstecke den SV-Cursor
    if( sal_True == ( bVis = pVisCrsr->IsVisible() ))
        pVisCrsr->Hide();

    bVisPortChgd = sal_True;
    aOldRBPos.X() = VisArea().Right();
    aOldRBPos.Y() = VisArea().Bottom();

    //Damit es es keine Probleme mit dem SV-Cursor gibt, wird in
    //ViewShell::VisPo.. ein Update() auf das Window gerufen.
    //Waehrend des Paintens duerfen aber nun wieder keine Selectionen
    //angezeigt werden, deshalb wird der Aufruf hier geklammert.
    ViewShell::VisPortChgd( rRect );        // Bereich verschieben

    if( bSVCrsrVis && bVis )    // auch SV-Cursor wieder anzeigen
        pVisCrsr->Show();

    if( nCrsrMove )
        bInCMvVisportChgd = sal_True;

    bVisPortChgd = sal_False;
}

// aktualisiere den Crsrs, d.H. setze ihn wieder in den Content.
// Das sollte nur aufgerufen werden, wenn der Cursor z.B. beim
// Loeschen von Rahmen irgendwohin gesetzt wurde. Die Position
// ergibt sich aus seiner aktuellen Position im Layout !!


void SwCrsrShell::UpdateCrsrPos()
{
    SET_CURR_SHELL( this );
    ++nStartAction;
    SwShellCrsr* pShellCrsr = getShellCrsr( true );
    Size aOldSz( GetDocSize() );
    SwCntntNode *pCNode = pShellCrsr->GetCntntNode();
    SwCntntFrm  *pFrm = pCNode ?
        pCNode->getLayoutFrm( GetLayout(), &pShellCrsr->GetPtPos(), pShellCrsr->GetPoint(), sal_False ) :0;
    if( !pFrm || (pFrm->IsTxtFrm() && ((SwTxtFrm*)pFrm)->IsHiddenNow()) )
    {
        SwCrsrMoveState aTmpState( MV_NONE );
        aTmpState.bSetInReadOnly = IsReadOnlyAvailable();
        GetLayout()->GetCrsrOfst( pShellCrsr->GetPoint(), pShellCrsr->GetPtPos(),
                                     &aTmpState );
        if( pShellCrsr->HasMark())
            pShellCrsr->DeleteMark();
    }
    IGrammarContact *pGrammarContact = GetDoc() ? GetDoc()->getGrammarContact() : 0;
    if( pGrammarContact )
        pGrammarContact->updateCursorPosition( *pCurCrsr->GetPoint() );
    --nStartAction;
    if( aOldSz != GetDocSize() )
        SizeChgNotify();
}

// JP 30.04.99: Bug 65475 - falls Point/Mark in versteckten Bereichen
//				stehen, so mussen diese daraus verschoben werden
static void lcl_CheckHiddenSection( SwNodeIndex& rIdx )
{
    const SwSectionNode* pSectNd = rIdx.GetNode().FindSectionNode();
    if( pSectNd && pSectNd->GetSection().IsHiddenFlag() )
    {
        SwNodeIndex aTmp( *pSectNd );
#if OSL_DEBUG_LEVEL > 1
        const SwNode* pFrmNd =
#endif
        rIdx.GetNodes().FindPrvNxtFrmNode( aTmp, pSectNd->EndOfSectionNode() );

#if OSL_DEBUG_LEVEL > 1
        (void) pFrmNd;
        OSL_ENSURE( pFrmNd, "keinen Node mit Frames gefunden" );
#endif
        rIdx = aTmp;
    }
}

// Try to set the cursor to the next visible content node.
static void lcl_CheckHiddenPara( SwPosition& rPos )
{
    SwNodeIndex aTmp( rPos.nNode );
    SwTxtNode* pTxtNd = aTmp.GetNode().GetTxtNode();
    while( pTxtNd && pTxtNd->HasHiddenCharAttribute( true ) )
    {
        SwCntntNode* pCntnt = aTmp.GetNodes().GoNext( &aTmp );
        if ( pCntnt && pCntnt->IsTxtNode() )
            pTxtNd = (SwTxtNode*)pCntnt;
        else
            pTxtNd = 0;
    }

    if ( pTxtNd )
        rPos = SwPosition( aTmp, SwIndex( pTxtNd, 0 ) );
}

// #i27301# - helper class, which notifies the accessibility about invalid text
// selections in its destructor
class SwNotifyAccAboutInvalidTextSelections
{
    private:
        SwCrsrShell& mrCrsrSh;

    public:
        SwNotifyAccAboutInvalidTextSelections( SwCrsrShell& _rCrsrSh )
            : mrCrsrSh( _rCrsrSh )
        {}

        ~SwNotifyAccAboutInvalidTextSelections()
        {
            mrCrsrSh.InvalidateAccessibleParaTextSelection();
        }
};
// <--
void SwCrsrShell::UpdateCrsr( sal_uInt16 eFlags, sal_Bool bIdleEnd )
{
    SET_CURR_SHELL( this );

    ClearUpCrsrs();

    // es muss innerhalb einer BasicAction der
    //				Cursor geupdatet werden; um z.B. den TabellenCursor zu
    //				erzeugen. Im EndAction wird jetzt das UpdateCrsr gerufen!
    if( ActionPend() && BasicActionPend() )
    {
        if ( eFlags & SwCrsrShell::READONLY )
            bIgnoreReadonly = sal_True;
        return;             // wenn nicht, dann kein Update !!
    }

    // #i27301#
    SwNotifyAccAboutInvalidTextSelections aInvalidateTextSelections( *this );

    if ( bIgnoreReadonly )
    {
        bIgnoreReadonly = sal_False;
        eFlags |= SwCrsrShell::READONLY;
    }

    if( eFlags & SwCrsrShell::CHKRANGE )	// alle Cursor-Bewegungen auf
        CheckRange( pCurCrsr );     	// ueberlappende Bereiche testen

    if( !bIdleEnd )
        CheckTblBoxCntnt();

    // steht der akt. Crsr in einer Tabelle und in unterschiedlichen Boxen
    // (oder ist noch TabellenMode), dann gilt der Tabellen Mode
    SwPaM* pTstCrsr = getShellCrsr( true );
    if( pTstCrsr->HasMark() && !pBlockCrsr &&
        pDoc->IsIdxInTbl( pTstCrsr->GetPoint()->nNode ) &&
          ( pTblCrsr ||
            pTstCrsr->GetNode( sal_True )->StartOfSectionNode() !=
            pTstCrsr->GetNode( sal_False )->StartOfSectionNode() ) )
    {
        SwShellCrsr* pITmpCrsr = getShellCrsr( true );
        Point aTmpPt( pITmpCrsr->GetPtPos() );
        Point aTmpMk( pITmpCrsr->GetMkPos() );
        SwPosition* pPos = pITmpCrsr->GetPoint();

        // JP 30.04.99: Bug 65475 - falls Point/Mark in versteckten Bereichen
        //				stehen, so mussen diese daraus verschoben werden
        lcl_CheckHiddenSection( pPos->nNode );
        lcl_CheckHiddenSection( pITmpCrsr->GetMark()->nNode );

        // Move cursor out of hidden paragraphs
        if ( !GetViewOptions()->IsShowHiddenChar() )
        {
            lcl_CheckHiddenPara( *pPos );
            lcl_CheckHiddenPara( *pITmpCrsr->GetMark() );
        }

        SwCntntFrm *pTblFrm = pPos->nNode.GetNode().GetCntntNode()->
                              getLayoutFrm( GetLayout(), &aTmpPt, pPos, sal_False );

        OSL_ENSURE( pTblFrm, "Tabelle Crsr nicht im Content ??" );

        // --> Make code robust. The table cursor may point
        // to a table in a currently inactive header.
        SwTabFrm *pTab = pTblFrm ? pTblFrm->FindTabFrm() : 0;
        // <--

        if ( pTab && pTab->GetTable()->GetRowsToRepeat() > 0 )
        {
            // First check if point is in repeated headline:
            bool bInRepeatedHeadline = pTab->IsFollow() && pTab->IsInHeadline( *pTblFrm );

            // Second check if mark is in repeated headline:
            if ( !bInRepeatedHeadline )
            {
                SwCntntFrm* pMarkTblFrm = pITmpCrsr->GetCntntNode( sal_False )->
                    getLayoutFrm( GetLayout(), &aTmpMk, pITmpCrsr->GetMark(), sal_False );
                OSL_ENSURE( pMarkTblFrm, "Tabelle Crsr nicht im Content ??" );

                if ( pMarkTblFrm )
                {
                    SwTabFrm* pMarkTab = pMarkTblFrm->FindTabFrm();
                    OSL_ENSURE( pMarkTab, "Tabelle Crsr nicht im Content ??" );

                    // Make code robust:
                    if ( pMarkTab )
                    {
                        bInRepeatedHeadline = pMarkTab->IsFollow() && pMarkTab->IsInHeadline( *pMarkTblFrm );
                    }
                }
            }

            // No table cursor in repeaded headlines:
            if ( bInRepeatedHeadline )
            {
                pTblFrm = 0;

                SwPosSection fnPosSect = *pPos <  *pITmpCrsr->GetMark()
                                            ? fnSectionStart
                                            : fnSectionEnd;

                // dann nur innerhalb der Box selektieren
                if( pTblCrsr )
                {
                    pCurCrsr->SetMark();
                    *pCurCrsr->GetMark() = *pTblCrsr->GetMark();
                    pCurCrsr->GetMkPos() = pTblCrsr->GetMkPos();
                    pTblCrsr->DeleteMark();
                    pTblCrsr->SwSelPaintRects::Hide();
                }

                *pCurCrsr->GetPoint() = *pCurCrsr->GetMark();
                (*fnSectionCurr)( *pCurCrsr, fnPosSect );
            }
        }

        // wir wollen wirklich eine Tabellen-Selektion
        if( pTab && pTblFrm )
        {
            if( !pTblCrsr )
            {
                pTblCrsr = new SwShellTableCrsr( *this,
                                *pCurCrsr->GetMark(), pCurCrsr->GetMkPos(),
                                *pPos, aTmpPt );
                pCurCrsr->DeleteMark();
                pCurCrsr->SwSelPaintRects::Hide();

                CheckTblBoxCntnt();
            }

            SwCrsrMoveState aTmpState( MV_NONE );
            aTmpState.bRealHeight = sal_True;
            if( !pTblFrm->GetCharRect( aCharRect, *pTblCrsr->GetPoint(), &aTmpState ) )
            {
                Point aCentrPt( aCharRect.Center() );
                aTmpState.bSetInReadOnly = IsReadOnlyAvailable();
                pTblFrm->GetCrsrOfst( pTblCrsr->GetPoint(), aCentrPt, &aTmpState );
#if OSL_DEBUG_LEVEL > 1
                if ( !pTblFrm->GetCharRect( aCharRect, *pTblCrsr->GetPoint() ) )
                    OSL_ENSURE( !this, "GetCharRect failed." );
#else
                pTblFrm->GetCharRect( aCharRect, *pTblCrsr->GetPoint() );
#endif
            }

            pVisCrsr->Hide();       // sichtbaren Cursor immer verstecken
            // Curosr in den sichtbaren Bereich scrollen
            if( (eFlags & SwCrsrShell::SCROLLWIN) &&
                (HasSelection() || eFlags & SwCrsrShell::READONLY ||
                 !IsCrsrReadonly()) )
            {
                SwFrm* pBoxFrm = pTblFrm;
                while( pBoxFrm && !pBoxFrm->IsCellFrm() )
                    pBoxFrm = pBoxFrm->GetUpper();
                if( pBoxFrm && pBoxFrm->Frm().HasArea() )
                    MakeVisible( pBoxFrm->Frm() );
                else
                    MakeVisible( aCharRect );
            }

            // lasse vom Layout die Crsr in den Boxen erzeugen
            if( pTblCrsr->IsCrsrMovedUpdt() )
                GetLayout()->MakeTblCrsrs( *pTblCrsr );
            if( bHasFocus && !bBasicHideCrsr )
                pTblCrsr->Show();

            // Cursor-Points auf die neuen Positionen setzen
            pTblCrsr->GetPtPos().X() = aCharRect.Left();
            pTblCrsr->GetPtPos().Y() = aCharRect.Top();

            if( bSVCrsrVis )
            {
                aCrsrHeight.X() = 0;
                aCrsrHeight.Y() = aTmpState.aRealHeight.Y() < 0 ?
                                  -aCharRect.Width() : aCharRect.Height();
                pVisCrsr->Show();           // wieder anzeigen
            }
            eMvState = MV_NONE;		// Status fuers Crsr-Travelling - GetCrsrOfst
            if( pTblFrm && Imp()->IsAccessible() )
                Imp()->InvalidateAccessibleCursorPosition( pTblFrm );
            return;
        }
    }

    if( pTblCrsr )
    {
        // Cursor Ring loeschen
        while( pCurCrsr->GetNext() != pCurCrsr )
            delete pCurCrsr->GetNext();
        pCurCrsr->DeleteMark();
        *pCurCrsr->GetPoint() = *pTblCrsr->GetPoint();
        pCurCrsr->GetPtPos() = pTblCrsr->GetPtPos();
        delete pTblCrsr, pTblCrsr = 0;
    }

    pVisCrsr->Hide();       // sichtbaren Cursor immer verstecken

    // sind wir vielleicht in einer geschuetzten/versteckten Section ?
    {
        SwShellCrsr* pShellCrsr = getShellCrsr( true );
        sal_Bool bChgState = sal_True;
        const SwSectionNode* pSectNd = pShellCrsr->GetNode()->FindSectionNode();
        if( pSectNd && ( pSectNd->GetSection().IsHiddenFlag() ||
            ( !IsReadOnlyAvailable() &&
              pSectNd->GetSection().IsProtectFlag() &&
             ( !pDoc->GetDocShell() ||
               !pDoc->GetDocShell()->IsReadOnly() || bAllProtect )) ) )
        {
            if( !FindValidCntntNode( !HasDrawView() ||
                    0 == Imp()->GetDrawView()->GetMarkedObjectList().GetMarkCount()))
            {
                // alles ist geschuetzt / versteckt -> besonderer Mode
                if( bAllProtect && !IsReadOnlyAvailable() &&
                    pSectNd->GetSection().IsProtectFlag() )
                    bChgState = sal_False;
                else
                {
                    eMvState = MV_NONE;		// Status fuers Crsr-Travelling
                    bAllProtect = sal_True;
                    if( GetDoc()->GetDocShell() )
                    {
                        GetDoc()->GetDocShell()->SetReadOnlyUI( sal_True );
                        CallChgLnk();		// UI bescheid sagen!
                    }
                    return;
                }
            }
        }
        if( bChgState )
        {
            sal_Bool bWasAllProtect = bAllProtect;
            bAllProtect = sal_False;
            if( bWasAllProtect && GetDoc()->GetDocShell() &&
                GetDoc()->GetDocShell()->IsReadOnlyUI() )
            {
                GetDoc()->GetDocShell()->SetReadOnlyUI( sal_False );
                CallChgLnk();		// UI bescheid sagen!
            }
        }
    }

    UpdateCrsrPos();

    // The cursor must always point into content; there's some code
    // that relies on this. (E.g. in SwEditShell::GetScriptType, which always
    // loops _behind_ the last node in the selection, which always works if you
    // are in content.) To achieve this, we'll force cursor(s) to point into
    // content, if UpdateCrsrPos() hasn't already done so.
    SwPaM* pCmp = pCurCrsr;
    do
    {
        // start will move forwards, end will move backwards
        bool bPointIsStart = ( pCmp->Start() == pCmp->GetPoint() );

        // move point; forward if it's the start, backwards if it's the end
        if( ! pCmp->GetPoint()->nNode.GetNode().IsCntntNode() )
            pCmp->Move( bPointIsStart ? fnMoveForward : fnMoveBackward,
                        fnGoCntnt );

        // move mark (if exists); forward if it's the start, else backwards
        if( pCmp->HasMark() )
        {
            if( ! pCmp->GetMark()->nNode.GetNode().IsCntntNode() )
            {
                pCmp->Exchange();
                pCmp->Move( !bPointIsStart ? fnMoveForward : fnMoveBackward,
                            fnGoCntnt );
                pCmp->Exchange();
            }
        }

        // iterate to next PaM in ring
        pCmp = static_cast<SwPaM*>( pCmp->GetNext() );
    }
    while( pCmp != pCurCrsr );


    SwRect aOld( aCharRect );
    sal_Bool bFirst = sal_True;
    SwCntntFrm *pFrm;
    int nLoopCnt = 100;
    SwShellCrsr* pShellCrsr = getShellCrsr( true );

    do {
        sal_Bool bAgainst;
        do {
            bAgainst = sal_False;
            pFrm = pShellCrsr->GetCntntNode()->getLayoutFrm( GetLayout(),
                        &pShellCrsr->GetPtPos(), pShellCrsr->GetPoint(), sal_False );
            // ist der Frm nicht mehr vorhanden, dann muss das gesamte Layout
            // erzeugt werden, weil ja mal hier einer vorhanden war !!
            if ( !pFrm )
            {
                do
                {
                    CalcLayout();
                    pFrm = pShellCrsr->GetCntntNode()->getLayoutFrm( GetLayout(),
                                &pShellCrsr->GetPtPos(), pShellCrsr->GetPoint(), sal_False );
                }  while( !pFrm );
            }
            else if ( Imp()->IsIdleAction() )
                //Wir stellen sicher, dass anstaendig Formatiert wurde
                pFrm->PrepareCrsr();

            // im geschuetzten Fly? aber bei Rahmenselektion ignorieren
            if( !IsReadOnlyAvailable() && pFrm->IsProtected() &&
                ( !Imp()->GetDrawView() ||
                  !Imp()->GetDrawView()->GetMarkedObjectList().GetMarkCount() ) &&
                (!pDoc->GetDocShell() ||
                 !pDoc->GetDocShell()->IsReadOnly() || bAllProtect ) )
            {
                // dann suche eine gueltige Position
                sal_Bool bChgState = sal_True;
                if( !FindValidCntntNode(!HasDrawView() ||
                    0 == Imp()->GetDrawView()->GetMarkedObjectList().GetMarkCount()))
                {
                    // alles ist geschuetzt / versteckt -> besonderer Mode
                    if( bAllProtect )
                        bChgState = sal_False;
                    else
                    {
                        eMvState = MV_NONE;     // Status fuers Crsr-Travelling
                        bAllProtect = sal_True;
                        if( GetDoc()->GetDocShell() )
                        {
                            GetDoc()->GetDocShell()->SetReadOnlyUI( sal_True );
                            CallChgLnk();		// UI bescheid sagen!
                        }
                        return;
                    }
                }

                if( bChgState )
                {
                    sal_Bool bWasAllProtect = bAllProtect;
                    bAllProtect = sal_False;
                    if( bWasAllProtect && GetDoc()->GetDocShell() &&
                        GetDoc()->GetDocShell()->IsReadOnlyUI() )
                    {
                        GetDoc()->GetDocShell()->SetReadOnlyUI( sal_False );
                        CallChgLnk();		// UI bescheid sagen!
                    }
                    bAllProtect = sal_False;
                    bAgainst = sal_True;        // nochmal den richigen Frm suchen
                }
            }
        } while( bAgainst );

        if( !( eFlags & SwCrsrShell::NOCALRECT ))
        {
            SwCrsrMoveState aTmpState( eMvState );
            aTmpState.bSetInReadOnly = IsReadOnlyAvailable();
            aTmpState.bRealHeight = sal_True;
            aTmpState.bRealWidth = IsOverwriteCrsr();
            aTmpState.nCursorBidiLevel = pShellCrsr->GetCrsrBidiLevel();

            // #i27615#,#i30453#
            SwSpecialPos aSpecialPos;
            aSpecialPos.nExtendRange = SP_EXTEND_RANGE_BEFORE;
            if (pShellCrsr->IsInFrontOfLabel())
            {
                aTmpState.pSpecialPos = &aSpecialPos;
            }

            if( !pFrm->GetCharRect( aCharRect, *pShellCrsr->GetPoint(), &aTmpState ) )
            {
                Point& rPt = pShellCrsr->GetPtPos();
                rPt = aCharRect.Center();
                pFrm->GetCrsrOfst( pShellCrsr->GetPoint(), rPt, &aTmpState );
            }

            if( !pShellCrsr->HasMark() )
                aCrsrHeight = aTmpState.aRealHeight;
            else
            {
                aCrsrHeight.X() = 0;
                aCrsrHeight.Y() = aTmpState.aRealHeight.Y() < 0 ?
                                  -aCharRect.Width() : aCharRect.Height();
            }
        }
        else
        {
            aCrsrHeight.X() = 0;
            aCrsrHeight.Y() = aCharRect.Height();
        }

        if( !bFirst && aOld == aCharRect )
            break;

        // falls das Layout meint, nach dem 100 durchlauf ist man immer noch
        // im Fluss, sollte man die akt. Pos. als gegeben hinnehmen!
        // siehe Bug: 29658
        if( !--nLoopCnt )
        {
            OSL_ENSURE( !this, "Endlosschleife? CharRect != OldCharRect ");
            break;
        }
        aOld = aCharRect;
        bFirst = sal_False;

        // Cursor-Points auf die neuen Positionen setzen
        pShellCrsr->GetPtPos().X() = aCharRect.Left();
        pShellCrsr->GetPtPos().Y() = aCharRect.Top();

        if( !(eFlags & SwCrsrShell::UPDOWN ))	// alte Pos. von Up/Down loeschen
        {
            pFrm->Calc();
            nUpDownX = pFrm->IsVertical() ?
                       aCharRect.Top() - pFrm->Frm().Top() :
                       aCharRect.Left() - pFrm->Frm().Left();
        }

        // Curosr in den sichtbaren Bereich scrollen
        if( bHasFocus && eFlags & SwCrsrShell::SCROLLWIN &&
            (HasSelection() || eFlags & SwCrsrShell::READONLY ||
             !IsCrsrReadonly() || GetViewOptions()->IsSelectionInReadonly()) )
        {
            //JP 30.04.99:  damit das EndAction, beim evtuellen Scrollen, den
            //		SV-Crsr nicht wieder sichtbar macht, wird hier das Flag
            //		gesichert und zurueckgesetzt.
            sal_Bool bSav = bSVCrsrVis; bSVCrsrVis = sal_False;
            MakeSelVisible();
            bSVCrsrVis = bSav;
        }

    } while( eFlags & SwCrsrShell::SCROLLWIN );

    if( pBlockCrsr )
        RefreshBlockCursor();

    if( !bIdleEnd && bHasFocus && !bBasicHideCrsr )
    {
        if( pTblCrsr )
            pTblCrsr->SwSelPaintRects::Show();
        else
        {
            pCurCrsr->SwSelPaintRects::Show();
            if( pBlockCrsr )
            {
                SwShellCrsr* pNxt = dynamic_cast<SwShellCrsr*>(pCurCrsr->GetNext());
                while( pNxt && pNxt != pCurCrsr )
                {
                    pNxt->SwSelPaintRects::Show();
                    pNxt = dynamic_cast<SwShellCrsr*>(pNxt->GetNext());
                }
            }
        }
    }

    eMvState = MV_NONE;		// Status fuers Crsr-Travelling - GetCrsrOfst

    if( pFrm && Imp()->IsAccessible() )
        Imp()->InvalidateAccessibleCursorPosition( pFrm );

    // switch from blinking cursor to read-only-text-selection cursor
    static const long nNoBlinkTime = STYLE_CURSOR_NOBLINKTIME;
    const long nBlinkTime = GetOut()->GetSettings().GetStyleSettings().
                            GetCursorBlinkTime();

    if ( (IsCrsrReadonly() && GetViewOptions()->IsSelectionInReadonly()) ==
        ( nBlinkTime != nNoBlinkTime ) )
    {
        // non blinking cursor in read only - text selection mode
        AllSettings aSettings = GetOut()->GetSettings();
        StyleSettings aStyleSettings = aSettings.GetStyleSettings();
        const long nNewBlinkTime = nBlinkTime == nNoBlinkTime ?
                                   Application::GetSettings().GetStyleSettings().GetCursorBlinkTime() :
                                   nNoBlinkTime;
        aStyleSettings.SetCursorBlinkTime( nNewBlinkTime );
        aSettings.SetStyleSettings( aStyleSettings );
        GetOut()->SetSettings( aSettings );
    }

    if( bSVCrsrVis )
        pVisCrsr->Show();           // wieder anzeigen
}

void SwCrsrShell::RefreshBlockCursor()
{
    OSL_ENSURE( pBlockCrsr, "Don't call me without a block cursor" );
    SwShellCrsr &rBlock = pBlockCrsr->getShellCrsr();
    Point aPt = rBlock.GetPtPos();
    SwCntntFrm* pFrm = rBlock.GetCntntNode()->getLayoutFrm( GetLayout(), &aPt, rBlock.GetPoint(), sal_False );
    Point aMk;
    if( pBlockCrsr->getEndPoint() && pBlockCrsr->getStartPoint() )
    {
        aPt = *pBlockCrsr->getStartPoint();
        aMk = *pBlockCrsr->getEndPoint();
    }
    else
    {
        aPt = rBlock.GetPtPos();
        if( pFrm )
        {
            if( pFrm->IsVertical() )
                aPt.Y() = pFrm->Frm().Top() + GetUpDownX();
            else
                aPt.X() = pFrm->Frm().Left() + GetUpDownX();
        }
        aMk = rBlock.GetMkPos();
    }
    SwRect aRect( aMk, aPt );
    aRect.Justify();
    SwSelectionList aSelList( pFrm );

    if( GetLayout()->FillSelection( aSelList, aRect ) )
    {
        SwCursor* pNxt = (SwCursor*)pCurCrsr->GetNext();
        while( pNxt != pCurCrsr )
        {
            delete pNxt;
            pNxt = (SwCursor*)pCurCrsr->GetNext();
        }

        std::list<SwPaM*>::iterator pStart = aSelList.getStart();
        std::list<SwPaM*>::iterator pPam = aSelList.getEnd();
        OSL_ENSURE( pPam != pStart, "FillSelection should deliver at least one PaM" );
        pCurCrsr->SetMark();
        --pPam;
        // If there is only one text portion inside the rectangle, a simple
        // selection is created
        if( pPam == pStart )
        {
            *pCurCrsr->GetPoint() = *(*pPam)->GetPoint();
            if( (*pPam)->HasMark() )
                *pCurCrsr->GetMark() = *(*pPam)->GetMark();
            else
                pCurCrsr->DeleteMark();
            delete *pPam;
            pCurCrsr->SetColumnSelection( false );
        }
        else
        {
            // The order of the SwSelectionList has to be preserved but
            // the order inside the ring created by CreateCrsr() is not like
            // exspected => First create the selections before the last one
            // downto the first selection.
            // At least create the cursor for the last selection
            --pPam;
            *pCurCrsr->GetPoint() = *(*pPam)->GetPoint(); // n-1 (if n == number of selections)
            if( (*pPam)->HasMark() )
                *pCurCrsr->GetMark() = *(*pPam)->GetMark();
            else
                pCurCrsr->DeleteMark();
            delete *pPam;
            pCurCrsr->SetColumnSelection( true );
            while( pPam != pStart )
            {
                --pPam;

                SwShellCrsr* pNew = new SwShellCrsr( *pCurCrsr );
                pNew->Insert( pCurCrsr, 0 );
                pCurCrsr->Remove( 0, pCurCrsr->Count() );
                pCurCrsr->DeleteMark();

                *pCurCrsr->GetPoint() = *(*pPam)->GetPoint(); // n-2, n-3, .., 2, 1
                if( (*pPam)->HasMark() )
                {
                    pCurCrsr->SetMark();
                    *pCurCrsr->GetMark() = *(*pPam)->GetMark();
                }
                else
                    pCurCrsr->DeleteMark();
                pCurCrsr->SetColumnSelection( true );
                delete *pPam;
            }
            {
                SwShellCrsr* pNew = new SwShellCrsr( *pCurCrsr );
                pNew->Insert( pCurCrsr, 0 );
                pCurCrsr->Remove( 0, pCurCrsr->Count() );
                pCurCrsr->DeleteMark();
            }
            pPam = aSelList.getEnd();
            --pPam;
            *pCurCrsr->GetPoint() = *(*pPam)->GetPoint(); // n, the last selection
            if( (*pPam)->HasMark() )
            {
                pCurCrsr->SetMark();
                *pCurCrsr->GetMark() = *(*pPam)->GetMark();
            }
            else
                pCurCrsr->DeleteMark();
            pCurCrsr->SetColumnSelection( true );
            delete *pPam;
        }
    }
}

// erzeuge eine Kopie vom Cursor und speicher diese im Stack


void SwCrsrShell::Push()
{
    pCrsrStk = new SwShellCrsr( *this, *pCurCrsr->GetPoint(),
                                    pCurCrsr->GetPtPos(), pCrsrStk );

    if( pCurCrsr->HasMark() )
    {
        pCrsrStk->SetMark();
        *pCrsrStk->GetMark() = *pCurCrsr->GetMark();
    }
}

/*
 *  Loescht einen Cursor (gesteuert durch bOldCrsr)
 *      - vom Stack oder    ( bOldCrsr = sal_True )
 *      - den aktuellen und der auf dem Stack stehende wird zum aktuellen
 *
 *  Return:  es war auf dem Stack noch einer vorhanden
 */


sal_Bool SwCrsrShell::Pop( sal_Bool bOldCrsr )
{
    SwCallLink aLk( *this );        // Crsr-Moves ueberwachen, evt. Link callen

    // noch weitere vorhanden ?
    if( 0 == pCrsrStk )
        return sal_False;

    SwShellCrsr *pTmp = 0, *pOldStk = pCrsrStk;

    // der Nachfolger wird der Aktuelle
    if( pCrsrStk->GetNext() != pCrsrStk )
    {
        pTmp = dynamic_cast<SwShellCrsr*>(pCrsrStk->GetNext());
    }

    if( bOldCrsr )              // loesche vom Stack
        delete pCrsrStk;        //

    pCrsrStk = pTmp;            // neu zuweisen

    if( !bOldCrsr )
    {
        SwCrsrSaveState aSaveState( *pCurCrsr );

        // wurde die sichtbare SSelection nicht veraendert
        if( pOldStk->GetPtPos() == pCurCrsr->GetPtPos() ||
            pOldStk->GetPtPos() == pCurCrsr->GetMkPos() )
        {
            // "Selektions-Rechtecke" verschieben
            pCurCrsr->Insert( pOldStk, 0 );
            pOldStk->Remove( 0, pOldStk->Count() );
        }

        if( pOldStk->HasMark() )
        {
            pCurCrsr->SetMark();
            *pCurCrsr->GetMark() = *pOldStk->GetMark();
            pCurCrsr->GetMkPos() = pOldStk->GetMkPos();
        }
        else
            // keine Selection also alte aufheben und auf die alte Pos setzen
            pCurCrsr->DeleteMark();
        *pCurCrsr->GetPoint() = *pOldStk->GetPoint();
        pCurCrsr->GetPtPos() = pOldStk->GetPtPos();
        delete pOldStk;

        if( !pCurCrsr->IsInProtectTable( sal_True ) &&
            !pCurCrsr->IsSelOvr( nsSwCursorSelOverFlags::SELOVER_TOGGLE |
                                 nsSwCursorSelOverFlags::SELOVER_CHANGEPOS ) )
            UpdateCrsr();             // akt. Cursor Updaten
    }
    return sal_True;
}

/*
 * Verbinde zwei Cursor miteinander.
 * Loesche vom Stack den obersten und setzen dessen GetMark im Aktuellen.
 */


void SwCrsrShell::Combine()
{
    // noch weitere vorhanden ?
    if( 0 == pCrsrStk )
        return;

    SwCallLink aLk( *this );        // Crsr-Moves ueberwachen, evt. Link callen
    SwCrsrSaveState aSaveState( *pCurCrsr );
    if( pCrsrStk->HasMark() )           // nur wenn GetMark gesetzt wurde
    {
#if OSL_DEBUG_LEVEL > 1
        if( !CheckNodesRange( pCrsrStk->GetMark()->nNode, pCurCrsr->GetPoint()->nNode, sal_True ))
            OSL_ENSURE( !this, "StackCrsr & akt. Crsr nicht in gleicher Section." );
#else
        CheckNodesRange( pCrsrStk->GetMark()->nNode, pCurCrsr->GetPoint()->nNode, sal_True );
#endif
        // kopiere das GetMark
        if( !pCurCrsr->HasMark() )
            pCurCrsr->SetMark();
        *pCurCrsr->GetMark() = *pCrsrStk->GetMark();
        pCurCrsr->GetMkPos() = pCrsrStk->GetMkPos();
    }

    SwShellCrsr * pTmp = 0;
    if( pCrsrStk->GetNext() != pCrsrStk )
    {
        pTmp = dynamic_cast<SwShellCrsr*>(pCrsrStk->GetNext());
    }
    delete pCrsrStk;
    pCrsrStk = pTmp;
    if( !pCurCrsr->IsInProtectTable( sal_True ) &&
        !pCurCrsr->IsSelOvr( nsSwCursorSelOverFlags::SELOVER_TOGGLE |
                             nsSwCursorSelOverFlags::SELOVER_CHANGEPOS ) )
        UpdateCrsr();             // akt. Cursor Updaten
}


void SwCrsrShell::HideCrsrs()
{
    if( !bHasFocus || bBasicHideCrsr )
        return;

    // ist Cursor sichtbar, dann verstecke den SV-Cursor
    if( pVisCrsr->IsVisible() )
    {
        SET_CURR_SHELL( this );
        pVisCrsr->Hide();
    }
    // hebe die Invertierung der SSelection auf
    SwShellCrsr* pAktCrsr = pTblCrsr ? pTblCrsr : pCurCrsr;
    pAktCrsr->Hide();
}



void SwCrsrShell::ShowCrsrs( sal_Bool bCrsrVis )
{
    if( !bHasFocus || bAllProtect || bBasicHideCrsr )
        return;

    SET_CURR_SHELL( this );
    SwShellCrsr* pAktCrsr = pTblCrsr ? pTblCrsr : pCurCrsr;
    pAktCrsr->Show();

    if( bSVCrsrVis && bCrsrVis )    // auch SV-Cursor wieder anzeigen
        pVisCrsr->Show();
}

// Methoden zum Anzeigen bzw. Verstecken des sichtbaren Text-Cursors


void SwCrsrShell::ShowCrsr()
{
    if( !bBasicHideCrsr )
    {
        bSVCrsrVis = sal_True;
        UpdateCrsr();
    }
}


void SwCrsrShell::HideCrsr()
{
    if( !bBasicHideCrsr )
    {
        bSVCrsrVis = sal_False;
        // evt. die sel. Bereiche aufheben !!
        SET_CURR_SHELL( this );
        pVisCrsr->Hide();
    }
}


void SwCrsrShell::ShLooseFcs()
{
    if( !bBasicHideCrsr )
        HideCrsrs();
    bHasFocus = sal_False;
}


void SwCrsrShell::ShGetFcs( sal_Bool bUpdate )
{
    bHasFocus = sal_True;
    if( !bBasicHideCrsr && VisArea().Width() )
    {
        UpdateCrsr( static_cast<sal_uInt16>( bUpdate ?
                    SwCrsrShell::CHKRANGE|SwCrsrShell::SCROLLWIN
                    : SwCrsrShell::CHKRANGE ) );
        ShowCrsrs( bSVCrsrVis ? sal_True : sal_False );
    }
}

// gebe den aktuellen Frame, in dem der Cursor steht, zurueck

SwCntntFrm *SwCrsrShell::GetCurrFrm( const sal_Bool bCalcFrm ) const
{
    SET_CURR_SHELL( (ViewShell*)this );
    SwCntntFrm *pRet = 0;
    SwCntntNode *pNd = pCurCrsr->GetCntntNode();
    if ( pNd )
    {
        if ( bCalcFrm )
        {
            const sal_uInt16* pST = &nStartAction;
            ++(*((sal_uInt16*)pST));
            const Size aOldSz( GetDocSize() );
            pRet = pNd->getLayoutFrm( GetLayout(), &pCurCrsr->GetPtPos(), pCurCrsr->GetPoint() );
            --(*((sal_uInt16*)pST));
            if( aOldSz != GetDocSize() )
                ((SwCrsrShell*)this)->SizeChgNotify();
        }
        else
            pRet = pNd->getLayoutFrm( GetLayout(), &pCurCrsr->GetPtPos(), pCurCrsr->GetPoint(), sal_False);
    }
    return pRet;
}


// alle Attribut/Format-Aenderungen am akt. Node werden an den
// Link weitergeleitet.


void SwCrsrShell::Modify( const SfxPoolItem* pOld, const SfxPoolItem* pNew )
{
    const sal_uInt16 nWhich = pOld ?
                          pOld->Which() :
                          pNew ?
                          pNew->Which() :
                          sal::static_int_cast<sal_uInt16>(RES_MSG_BEGIN);

    if( bCallChgLnk &&
        ( nWhich < RES_MSG_BEGIN || nWhich >= RES_MSG_END ||
            nWhich == RES_FMT_CHG || nWhich == RES_UPDATE_ATTR ||
            nWhich == RES_ATTRSET_CHG ))
        // die Messages werden nicht weitergemeldet
        //MA 07. Apr. 94 fix(6681): RES_UPDATE_ATTR wird implizit vom
        //SwTxtNode::Insert(SwTxtHint*, sal_uInt16) abgesetzt; hier wird reagiert und
        //vom Insert brauch nicht mehr die Keule RES_FMT_CHG versandt werden.
        CallChgLnk();

    if( aGrfArrivedLnk.IsSet() &&
        ( RES_GRAPHIC_ARRIVED == nWhich || RES_GRAPHIC_SWAPIN == nWhich ))
        aGrfArrivedLnk.Call( this );
}


// Abfrage, ob der aktuelle Cursor eine Selektion aufspannt,
// also, ob GetMark gesetzt und SPoint und GetMark unterschiedlich sind.


sal_Bool SwCrsrShell::HasSelection() const
{
    const SwPaM* pCrsr = getShellCrsr( true );
    return( IsTableMode() || ( pCrsr->HasMark() &&
            *pCrsr->GetPoint() != *pCrsr->GetMark())
        ? sal_True : sal_False );
}


void SwCrsrShell::CallChgLnk()
{
    // innerhalb von Start-/End-Action kein Call, sondern nur merken,
    // das sich etwas geaendert hat. Wird bei EndAction beachtet.
    if( BasicActionPend() )
        bChgCallFlag = sal_True;		// das Change merken
    else if( aChgLnk.IsSet() )
    {
        if( bCallChgLnk )
            aChgLnk.Call( this );
        bChgCallFlag = sal_False;		// Flag zuruecksetzen
    }
}

// returne den am akt.Cursor selektierten Text eines Nodes.


String SwCrsrShell::GetSelTxt() const
{
    String aTxt;
    if( pCurCrsr->GetPoint()->nNode.GetIndex() ==
        pCurCrsr->GetMark()->nNode.GetIndex() )
    {
        SwTxtNode* pTxtNd = pCurCrsr->GetNode()->GetTxtNode();
        if( pTxtNd )
        {
            xub_StrLen nStt = pCurCrsr->Start()->nContent.GetIndex();
            aTxt = pTxtNd->GetExpandTxt( nStt,
                    pCurCrsr->End()->nContent.GetIndex() - nStt );
        }
    }
    return aTxt;
}

// gebe nur den Text ab der akt. Cursor Position zurueck (bis zum NodeEnde)


String SwCrsrShell::GetText() const
{
    String aTxt;
    if( pCurCrsr->GetPoint()->nNode.GetIndex() ==
        pCurCrsr->GetMark()->nNode.GetIndex() )
    {
        SwTxtNode* pTxtNd = pCurCrsr->GetNode()->GetTxtNode();
        if( pTxtNd )
            aTxt = pTxtNd->GetTxt().Copy(
                    pCurCrsr->GetPoint()->nContent.GetIndex() );
    }
    return aTxt;
}

// hole vom Start/Ende der akt. SSelection das nte Zeichen
sal_Unicode SwCrsrShell::GetChar( sal_Bool bEnd, long nOffset )
{
    if( IsTableMode() )			// im TabelleMode nicht moeglich
        return 0;

    const SwPosition* pPos = !pCurCrsr->HasMark() ? pCurCrsr->GetPoint()
                                : bEnd ? pCurCrsr->End() : pCurCrsr->Start();
    SwTxtNode* pTxtNd = pPos->nNode.GetNode().GetTxtNode();
    if( !pTxtNd )
        return 0;

    xub_StrLen nPos = pPos->nContent.GetIndex();
    const String& rStr = pTxtNd->GetTxt();
    sal_Unicode cCh = 0;

    if( ((nPos+nOffset) >= 0 ) && (nPos+nOffset) < rStr.Len() )
        cCh = rStr.GetChar( static_cast<xub_StrLen>(nPos+nOffset) );

    return cCh;
}

// erweiter die akt. SSelection am Anfang/Ende um n Zeichen


sal_Bool SwCrsrShell::ExtendSelection( sal_Bool bEnd, xub_StrLen nCount )
{
    if( !pCurCrsr->HasMark() || IsTableMode() )
        return sal_False;			// keine Selektion

    SwPosition* pPos = bEnd ? pCurCrsr->End() : pCurCrsr->Start();
    SwTxtNode* pTxtNd = pPos->nNode.GetNode().GetTxtNode();
    OSL_ENSURE( pTxtNd, "kein TextNode, wie soll erweitert werden?" );

    xub_StrLen nPos = pPos->nContent.GetIndex();
    if( bEnd )
    {
        if( ( nPos + nCount ) <= pTxtNd->GetTxt().Len() )
            nPos = nPos + nCount;
        else
            return sal_False;		// nicht mehr moeglich
    }
    else if( nPos >= nCount )
        nPos = nPos - nCount;
    else
        return sal_False;			// nicht mehr moeglich

    SwCallLink aLk( *this );	// Crsr-Moves ueberwachen,

    pPos->nContent = nPos;
    UpdateCrsr();

    return sal_True;
}

// setze nur den sichtbaren Cursor an die angegebene Dokument-Pos.
// returnt sal_False: wenn der SPoint vom Layout korrigiert wurde.

sal_Bool SwCrsrShell::SetVisCrsr( const Point &rPt )
{
    SET_CURR_SHELL( this );
    Point aPt( rPt );
    SwPosition aPos( *pCurCrsr->GetPoint() );
    SwCrsrMoveState aTmpState( MV_SETONLYTEXT );
    aTmpState.bSetInReadOnly = IsReadOnlyAvailable();
    aTmpState.bRealHeight = sal_True;

    sal_Bool bRet = GetLayout()->GetCrsrOfst( &aPos, aPt /*, &aTmpState*/ );

    SetInFrontOfLabel( sal_False ); // #i27615#

    // nur in TextNodes anzeigen !!
    SwTxtNode* pTxtNd = aPos.nNode.GetNode().GetTxtNode();
    if( !pTxtNd )
        return sal_False;

    const SwSectionNode* pSectNd = pTxtNd->FindSectionNode();
    if( pSectNd && (pSectNd->GetSection().IsHiddenFlag() ||
                    ( !IsReadOnlyAvailable() &&
                      pSectNd->GetSection().IsProtectFlag())) )
        return sal_False;

    SwCntntFrm *pFrm = pTxtNd->getLayoutFrm( GetLayout(), &aPt, &aPos );
    if ( Imp()->IsIdleAction() )
        pFrm->PrepareCrsr();
    SwRect aTmp( aCharRect );

    pFrm->GetCharRect( aCharRect, aPos, &aTmpState );

    if( aTmp == aCharRect && 		// BUG 10137: bleibt der Cursor auf der
        pVisCrsr->IsVisible() )     // Position nicht hidden & showen
        return sal_True;

    pVisCrsr->Hide();       // sichtbaren Cursor immer verstecken
    if( IsScrollMDI( this, aCharRect ))
    {
        MakeVisible( aCharRect );
        pCurCrsr->Show();
    }

    {
        if( aTmpState.bRealHeight )
            aCrsrHeight = aTmpState.aRealHeight;
        else
        {
            aCrsrHeight.X() = 0;
            aCrsrHeight.Y() = aCharRect.Height();
        }

        pVisCrsr->SetDragCrsr( sal_True );
        pVisCrsr->Show();           // wieder anzeigen
    }
    return bRet;
}

sal_Bool SwCrsrShell::IsOverReadOnlyPos( const Point& rPt ) const
{
    Point aPt( rPt );
    SwPaM aPam( *pCurCrsr->GetPoint() );
    GetLayout()->GetCrsrOfst( aPam.GetPoint(), aPt );
    // Formular view
    return aPam.HasReadonlySel( GetViewOptions()->IsFormView() );
}


    // returne die Anzahl der Cursor im Ring (Flag besagt ob man nur
    // aufgepspannte haben will - sprich etwas selektiert ist (Basic))
sal_uInt16 SwCrsrShell::GetCrsrCnt( sal_Bool bAll ) const
{
    Ring* pTmp = GetCrsr()->GetNext();
    sal_uInt16 n = (bAll || ( pCurCrsr->HasMark() &&
                    *pCurCrsr->GetPoint() != *pCurCrsr->GetMark())) ? 1 : 0;
    while( pTmp != pCurCrsr )
    {
        if( bAll || ( ((SwPaM*)pTmp)->HasMark() &&
                *((SwPaM*)pTmp)->GetPoint() != *((SwPaM*)pTmp)->GetMark()))
            ++n;
        pTmp = pTmp->GetNext();
    }
    return n;
}


sal_Bool SwCrsrShell::IsStartOfDoc() const
{
    if( pCurCrsr->GetPoint()->nContent.GetIndex() )
        return sal_False;

    // Hinter EndOfIcons kommt die Content-Section (EndNd+StNd+CntntNd)
    SwNodeIndex aIdx( GetDoc()->GetNodes().GetEndOfExtras(), 2 );
    if( !aIdx.GetNode().IsCntntNode() )
        GetDoc()->GetNodes().GoNext( &aIdx );
    return aIdx == pCurCrsr->GetPoint()->nNode;
}


sal_Bool SwCrsrShell::IsEndOfDoc() const
{
    SwNodeIndex aIdx( GetDoc()->GetNodes().GetEndOfContent(), -1 );
    SwCntntNode* pCNd = aIdx.GetNode().GetCntntNode();
    if( !pCNd )
        pCNd = GetDoc()->GetNodes().GoPrevious( &aIdx );

    return aIdx == pCurCrsr->GetPoint()->nNode &&
            pCNd->Len() == pCurCrsr->GetPoint()->nContent.GetIndex();
}


// loesche alle erzeugten Crsr, setze den Tabellen-Crsr und den letzten
// Cursor auf seinen TextNode (oder StartNode?).
// Beim naechsten ::GetCrsr werden sie wieder alle erzeugt
// Wird fuers Drag&Drop / ClipBorad-Paste in Tabellen benoetigt.
sal_Bool SwCrsrShell::ParkTblCrsr()
{
    if( !pTblCrsr )
        return sal_False;

    pTblCrsr->ParkCrsr();

    while( pCurCrsr->GetNext() != pCurCrsr )
        delete pCurCrsr->GetNext();

    // vom Cursor !immer! SPoint und Mark umsetzen
    pCurCrsr->SetMark();
    *pCurCrsr->GetMark() = *pCurCrsr->GetPoint() = *pTblCrsr->GetPoint();
    pCurCrsr->DeleteMark();

    return sal_True;
}

/***********************************************************************
#*	Class		:  SwCrsrShell
#*	Methode 	:  ParkCrsr
#*	Beschreibung:  Vernichtet Selektionen und zus. Crsr aller Shell der
#*				   verbleibende Crsr der Shell wird geparkt.
#***********************************************************************/

void SwCrsrShell::_ParkPams( SwPaM* pDelRg, SwShellCrsr** ppDelRing )
{
    const SwPosition *pStt = pDelRg->Start(),
        *pEnd = pDelRg->GetPoint() == pStt ? pDelRg->GetMark() : pDelRg->GetPoint();

    SwPaM *pTmpDel = 0, *pTmp = *ppDelRing;

    // durchsuche den gesamten Ring
    sal_Bool bGoNext;
    do {
        const SwPosition *pTmpStt = pTmp->Start(),
                        *pTmpEnd = pTmp->GetPoint() == pTmpStt ?
                                        pTmp->GetMark() : pTmp->GetPoint();
        /*
         * liegt ein SPoint oder GetMark innerhalb vom Crsr-Bereich
         * muss der alte Bereich aufgehoben werden.
         * Beim Vergleich ist darauf zu achten, das End() nicht mehr zum
         * Bereich gehoert !
         */
        if( *pStt <= *pTmpStt )
        {
            if( *pEnd > *pTmpStt ||
                ( *pEnd == *pTmpStt && *pEnd == *pTmpEnd ))
                pTmpDel = pTmp;
        }
        else
            if( *pStt < *pTmpEnd )
                pTmpDel = pTmp;

        bGoNext = sal_True;
        if( pTmpDel )			// ist der Pam im Bereich ?? loesche ihn
        {
            sal_Bool bDelete = sal_True;
            if( *ppDelRing == pTmpDel )
            {
                if( *ppDelRing == pCurCrsr )
                {
                    if( sal_True == ( bDelete = GoNextCrsr() ))
                    {
                        bGoNext = sal_False;
                        pTmp = (SwPaM*)pTmp->GetNext();
                    }
                }
                else
                    bDelete = sal_False;		// StackCrsr nie loeschen !!
            }

            if( bDelete )
                delete pTmpDel; 		// hebe alten Bereich auf
            else
            {
                pTmpDel->GetPoint()->nContent.Assign( 0, 0 );
                pTmpDel->GetPoint()->nNode = 0;
                pTmpDel->SetMark();
                pTmpDel->DeleteMark();
            }
            pTmpDel = 0;
        }
        else if( !pTmp->HasMark() ) 	// sorge auf jedenfall dafuer, das
        {						// nicht benutzte Indizies beachtet werden!
            pTmp->SetMark();			// SPoint liegt nicht im Bereich,
            pTmp->DeleteMark(); 		// aber vielleicht GetMark, also setzen
        }
        if( bGoNext )
            pTmp = (SwPaM*)pTmp->GetNext();
    } while( !bGoNext || *ppDelRing != pTmp );
}

void SwCrsrShell::ParkCrsr( const SwNodeIndex &rIdx )
{
    SwNode *pNode = &rIdx.GetNode();

    // erzeuge einen neuen Pam
    SwPaM * pNew = new SwPaM( *GetCrsr()->GetPoint() );
    if( pNode->GetStartNode() )
    {
        if( ( pNode = pNode->StartOfSectionNode())->IsTableNode() )
        {
            // der angegebene Node steht in einer Tabelle, also Parke
            // den Crsr auf dem Tabellen-Node (ausserhalb der Tabelle)
            pNew->GetPoint()->nNode = *pNode->StartOfSectionNode();
        }
        else	// also auf dem StartNode selbst.
                // Dann immer ueber seinen EndNode den StartNode erfragen !!!
                // (StartOfSection vom StartNode ist der Parent !)
            pNew->GetPoint()->nNode = *pNode->EndOfSectionNode()->StartOfSectionNode();
    }
    else
        pNew->GetPoint()->nNode = *pNode->StartOfSectionNode();
    pNew->SetMark();
    pNew->GetPoint()->nNode = *pNode->EndOfSectionNode();

    //Alle Shells wollen etwas davon haben.
    ViewShell *pTmp = this;
    do {
        if( pTmp->IsA( TYPE( SwCrsrShell )))
        {
            SwCrsrShell* pSh = (SwCrsrShell*)pTmp;
            if( pSh->pCrsrStk )
                pSh->_ParkPams( pNew, &pSh->pCrsrStk );

            pSh->_ParkPams( pNew, &pSh->pCurCrsr );
            if( pSh->pTblCrsr )
            {
                // setze den Tabellen Cursor immer auf 0, den aktuellen
                // immer auf den Anfang der Tabelle
                SwPaM* pTCrsr = pSh->GetTblCrs();
                SwNode* pTblNd = pTCrsr->GetPoint()->nNode.GetNode().FindTableNode();
                if ( pTblNd )
                {
                    pTCrsr->GetPoint()->nContent.Assign( 0, 0 );
                    pTCrsr->GetPoint()->nNode = 0;
                    pTCrsr->SetMark();
                    pTCrsr->DeleteMark();
                    pSh->pCurCrsr->GetPoint()->nNode = *pTblNd;
                }
            }
        }
    } while ( this != (pTmp = (ViewShell*)pTmp->GetNext() ));
    delete pNew;
}

//=========================================================================

/*
 * der Copy-Constructor
 * Cursor-Position kopieren, in den Ring eingetragen.
 * Alle Ansichten eines Dokumentes stehen im Ring der Shells.
 */

SwCrsrShell::SwCrsrShell( SwCrsrShell& rShell, Window *pInitWin )
    : ViewShell( rShell, pInitWin ),
    SwModify( 0 ), pCrsrStk( 0 ), pBlockCrsr( 0 ), pTblCrsr( 0 ),
    pBoxIdx( 0 ), pBoxPtr( 0 ), nCrsrMove( 0 ), nBasicActionCnt( 0 ),
    eMvState( MV_NONE ),
    sMarkedListId(),
    nMarkedListLevel( 0 )
{
    SET_CURR_SHELL( this );
    // Nur die Position vom aktuellen Cursor aus der Copy-Shell uebernehmen
    pCurCrsr = new SwShellCrsr( *this, *(rShell.pCurCrsr->GetPoint()) );
    pCurCrsr->GetCntntNode()->Add( this );

    bAllProtect = bVisPortChgd = bChgCallFlag = bInCMvVisportChgd =
    bGCAttr = bIgnoreReadonly = bSelTblCells = bBasicHideCrsr =
    bOverwriteCrsr = sal_False;
    bCallChgLnk = bHasFocus = bSVCrsrVis = bAutoUpdateCells = sal_True;
    bSetCrsrInReadOnly = sal_True;
    pVisCrsr = new SwVisCrsr( this );
    mbMacroExecAllowed = rShell.IsMacroExecAllowed();
}


/*
 * der normale Constructor
 */

SwCrsrShell::SwCrsrShell( SwDoc& rDoc, Window *pInitWin,
                            const SwViewOption *pInitOpt )
    : ViewShell( rDoc, pInitWin, pInitOpt ),
    SwModify( 0 ), pCrsrStk( 0 ), pBlockCrsr( 0 ), pTblCrsr( 0 ),
    pBoxIdx( 0 ), pBoxPtr( 0 ), nCrsrMove( 0 ), nBasicActionCnt( 0 ),
    eMvState( MV_NONE ), // state for crsr-travelling - GetCrsrOfst
    sMarkedListId(),
    nMarkedListLevel( 0 )
{
    SET_CURR_SHELL( this );
    /*
     * Erzeugen des initialen Cursors, wird auf die erste
     * Inhaltsposition gesetzt
     */
    SwNodes& rNds = rDoc.GetNodes();

    SwNodeIndex aNodeIdx( *rNds.GetEndOfContent().StartOfSectionNode() );
    SwCntntNode* pCNd = rNds.GoNext( &aNodeIdx ); // gehe zum 1. ContentNode

    pCurCrsr = new SwShellCrsr( *this, SwPosition( aNodeIdx, SwIndex( pCNd, 0 )));

    // melde die Shell beim akt. Node als abhaengig an, dadurch koennen alle
    // Attribut-Aenderungen ueber den Link weiter gemeldet werden.
    pCNd->Add( this );

    bAllProtect = bVisPortChgd = bChgCallFlag = bInCMvVisportChgd =
    bGCAttr = bIgnoreReadonly = bSelTblCells = bBasicHideCrsr =
    bOverwriteCrsr = sal_False;
    bCallChgLnk = bHasFocus = bSVCrsrVis = bAutoUpdateCells = sal_True;
    bSetCrsrInReadOnly = sal_True;

    pVisCrsr = new SwVisCrsr( this );
    mbMacroExecAllowed = true;
}



SwCrsrShell::~SwCrsrShell()
{
    // wenn es nicht die letzte View so sollte zu mindest das
    // Feld noch geupdatet werden.
    if( GetNext() != this )
        CheckTblBoxCntnt( pCurCrsr->GetPoint() );
    else
        ClearTblBoxCntnt();

    delete pVisCrsr;
    delete pBlockCrsr;
    delete pTblCrsr;

    /*
     * Freigabe der Cursor
     */
    while(pCurCrsr->GetNext() != pCurCrsr)
        delete pCurCrsr->GetNext();
    delete pCurCrsr;

    // Stack freigeben
    if( pCrsrStk )
    {
        while( pCrsrStk->GetNext() != pCrsrStk )
            delete pCrsrStk->GetNext();
        delete pCrsrStk;
    }

    // JP 27.07.98: Bug 54025 - ggfs. den HTML-Parser, der als Client in
    // 				der CursorShell haengt keine Chance geben, sich an den
    //				TextNode zu haengen.
    if( GetRegisteredIn() )
        GetRegisteredInNonConst()->Remove( this );
}

SwShellCrsr* SwCrsrShell::getShellCrsr( bool bBlock )
{
    if( pTblCrsr )
        return pTblCrsr;
    if( pBlockCrsr && bBlock )
        return &pBlockCrsr->getShellCrsr();
    return pCurCrsr;
}

//Sollte fuer das Clipboard der WaitPtr geschaltet werden?
//Warten bei TableMode, Mehrfachselektion und mehr als x Selektieren Absaetzen.

sal_Bool SwCrsrShell::ShouldWait() const
{
    if ( IsTableMode() || GetCrsrCnt() > 1 )
        return sal_True;

    if( HasDrawView() && GetDrawView()->GetMarkedObjectList().GetMarkCount() )
        return sal_True;

    SwPaM* pPam = GetCrsr();
    return pPam->Start()->nNode.GetIndex() + 10 <
            pPam->End()->nNode.GetIndex();
}


sal_uInt16 SwCrsrShell::UpdateTblSelBoxes()
{
    if( pTblCrsr && ( pTblCrsr->IsChgd() || !pTblCrsr->GetBoxesCount() ))
         GetLayout()->MakeTblCrsrs( *pTblCrsr );
    return pTblCrsr ? pTblCrsr->GetBoxesCount() : 0;
}

// zeige das akt. selektierte "Object" an
void SwCrsrShell::MakeSelVisible()
{
    OSL_ENSURE( bHasFocus, "kein Focus aber Cursor sichtbar machen?" );
    if( aCrsrHeight.Y() < aCharRect.Height() && aCharRect.Height() > VisArea().Height() )
    {
        SwRect aTmp( aCharRect );
        long nDiff = aCharRect.Height() - VisArea().Height();
        if( nDiff < aCrsrHeight.X() )
            aTmp.Top( nDiff + aCharRect.Top() );
        else
        {
            aTmp.Top( aCrsrHeight.X() + aCharRect.Top() );
            aTmp.Height( aCrsrHeight.Y() );
        }
        if( !aTmp.HasArea() )
        {
            aTmp.SSize().Height() += 1;
            aTmp.SSize().Width() += 1;
        }
        MakeVisible( aTmp );
    }
    else
    {
        if( aCharRect.HasArea() )
            MakeVisible( aCharRect );
        else
        {
            SwRect aTmp( aCharRect );
            aTmp.SSize().Height() += 1; aTmp.SSize().Width() += 1;
            MakeVisible( aTmp );
        }
    }
}


// suche eine gueltige ContentPosition (nicht geschuetzt/nicht versteckt)
sal_Bool SwCrsrShell::FindValidCntntNode( sal_Bool bOnlyText )
{
    if( pTblCrsr )		// was soll ich jetzt machen ??
    {
        OSL_ENSURE( !this, "TabellenSelection nicht aufgehoben!" );
        return sal_False;
    }

    //JP 28.10.97: Bug 45129 - im UI-ReadOnly ist alles erlaubt
    if( !bAllProtect && GetDoc()->GetDocShell() &&
        GetDoc()->GetDocShell()->IsReadOnlyUI() )
        return sal_True;

    // dann raus da!
    if( pCurCrsr->HasMark()	)
        ClearMark();

    // als erstes mal auf Rahmen abpruefen
    SwNodeIndex& rNdIdx = pCurCrsr->GetPoint()->nNode;
    sal_uLong nNdIdx = rNdIdx.GetIndex();		// sichern
    SwNodes& rNds = pDoc->GetNodes();
    SwCntntNode* pCNd = rNdIdx.GetNode().GetCntntNode();
    const SwCntntFrm * pFrm;

    if( pCNd && 0 != (pFrm = pCNd->getLayoutFrm( GetLayout(),0,pCurCrsr->GetPoint(),sal_False)) &&
        !IsReadOnlyAvailable() && pFrm->IsProtected() &&
        nNdIdx < rNds.GetEndOfExtras().GetIndex() )
    {
        // geschuetzter Rahmen ueberspringen
        SwPaM aPam( *pCurCrsr->GetPoint() );
        aPam.SetMark();
        aPam.GetMark()->nNode = rNds.GetEndOfContent();
        aPam.GetPoint()->nNode = *pCNd->EndOfSectionNode();

        sal_Bool bFirst = sal_False;
        if( 0 == (pCNd = ::GetNode( aPam, bFirst, fnMoveForward, sal_False )))
        {
            aPam.GetMark()->nNode = *rNds.GetEndOfPostIts().StartOfSectionNode();
            pCNd = ::GetNode( aPam, bFirst, fnMoveBackward, sal_False );
        }

        if( !pCNd )		// sollte nie passieren !!!
        {
            rNdIdx = nNdIdx;		// alten Node zurueck
            return sal_False;
        }
        *pCurCrsr->GetPoint() = *aPam.GetPoint();
    }
    else if( bOnlyText && pCNd && pCNd->IsNoTxtNode() )
    {
        // dann auf den Anfang vom Doc stellen
        rNdIdx = pDoc->GetNodes().GetEndOfExtras();
        pCurCrsr->GetPoint()->nContent.Assign( pDoc->GetNodes().GoNext(
                                                            &rNdIdx ), 0 );
        nNdIdx = rNdIdx.GetIndex();
    }

    sal_Bool bOk = sal_True;

    // #i9059# cursor may not stand in protected cells
    //         (unless cursor in protected areas is OK.)
    const SwTableNode* pTableNode = rNdIdx.GetNode().FindTableNode();
    if( !IsReadOnlyAvailable()  &&
        pTableNode != NULL  &&  rNdIdx.GetNode().IsProtect() )
    {
        // we're in a table, and we're in a protected area, so we're
        // probably in a protected cell.

        // move forward into non-protected area.
        SwPaM aPam( rNdIdx.GetNode(), 0 );
        while( aPam.GetNode()->IsProtect() &&
               aPam.Move( fnMoveForward, fnGoCntnt ) )
            ; // nothing to do in the loop; the aPam.Move does the moving!

        // didn't work? then go backwards!
        if( aPam.GetNode()->IsProtect() )
        {
            SwPaM aTmpPaM( rNdIdx.GetNode(), 0 );
            aPam = aTmpPaM;
            while( aPam.GetNode()->IsProtect() &&
                   aPam.Move( fnMoveBackward, fnGoCntnt ) )
                ; // nothing to do in the loop; the aPam.Move does the moving!
        }

        // if we're successful, set the new position
        if( ! aPam.GetNode()->IsProtect() )
        {
            *pCurCrsr->GetPoint() = *aPam.GetPoint();
        }
    }

    // in einem geschuetzten Bereich
    const SwSectionNode* pSectNd = rNdIdx.GetNode().FindSectionNode();
    if( pSectNd && ( pSectNd->GetSection().IsHiddenFlag() ||
        ( !IsReadOnlyAvailable() &&
           pSectNd->GetSection().IsProtectFlag() )) )
    {
        typedef SwCntntNode* (SwNodes:: *FNGoSection)( SwNodeIndex *, int, int ) const;
        FNGoSection funcGoSection = &SwNodes::GoNextSection;

        bOk = sal_False;

        for( int nLoopCnt = 0; !bOk && nLoopCnt < 2; ++nLoopCnt )
        {
            sal_Bool bWeiter;
            do {
                bWeiter = sal_False;
                while( 0 != ( pCNd = (rNds.*funcGoSection)( &rNdIdx,
                                            sal_True, !IsReadOnlyAvailable() )) )
                {
                    // in eine Tabelle verschoben -> pruefe ob die
                    // vielleicht geschuetzt ist
                    if( pCNd->FindTableNode() )
                    {
                        SwCallLink aTmp( *this );
                        SwCrsrSaveState aSaveState( *pCurCrsr );
                        aTmp.nNdTyp = 0;		// im DTOR nichts machen!
                        if( !pCurCrsr->IsInProtectTable( sal_True, sal_True ) )
                        {
                            const SwSectionNode* pSNd = pCNd->FindSectionNode();
                            if( !pSNd || !pSNd->GetSection().IsHiddenFlag()
                                || (!IsReadOnlyAvailable()  &&
                                    pSNd->GetSection().IsProtectFlag() ))
                            {
                                bOk = sal_True;
                                break;		// eine nicht geschuetzte Zelle gef.
                            }
                            continue;		// dann weiter suchen
                        }
                    }
                    else
                    {
                        bOk = sal_True;
                        break;		// eine nicht geschuetzte Zelle gef.
                    }
                }

                if( bOk && rNdIdx.GetIndex() < rNds.GetEndOfExtras().GetIndex() )
                {
                    // Teste mal auf Fly - kann auch noch geschuetzt sein!!
                    if( 0 == (pFrm = pCNd->getLayoutFrm( GetLayout(),0,0,sal_False)) ||
                        ( !IsReadOnlyAvailable() && pFrm->IsProtected() ) ||
                        ( bOnlyText && pCNd->IsNoTxtNode() ) )
                    {
                        // dann weiter suchen!
                        bOk = sal_False;
                        bWeiter = sal_True;
                    }
                }
            } while( bWeiter );

            if( !bOk )
            {
                if( !nLoopCnt )
                    funcGoSection = &SwNodes::GoPrevSection;
                rNdIdx = nNdIdx;
            }
        }
    }
    if( bOk )
    {
        pCNd = rNdIdx.GetNode().GetCntntNode();
        xub_StrLen nCntnt = rNdIdx.GetIndex() < nNdIdx ? pCNd->Len() : 0;
        pCurCrsr->GetPoint()->nContent.Assign( pCNd, nCntnt );
    }
    else
    {
        pCNd = rNdIdx.GetNode().GetCntntNode();

        // falls Cursor im versteckten Bereich ist, auf jedenfall schon mal
        // verschieben!!
        if( !pCNd || !pCNd->getLayoutFrm( GetLayout(),0,0,sal_False) )
        {
            SwCrsrMoveState aTmpState( MV_NONE );
            aTmpState.bSetInReadOnly = IsReadOnlyAvailable();
            GetLayout()->GetCrsrOfst( pCurCrsr->GetPoint(), pCurCrsr->GetPtPos(),
                                        &aTmpState );
        }
    }
    return bOk;
}


void SwCrsrShell::NewCoreSelection()
{
}


sal_Bool SwCrsrShell::IsCrsrReadonly() const
{
    if ( GetViewOptions()->IsReadonly() ||
         // Formular view
         GetViewOptions()->IsFormView() )
    {
        SwFrm *pFrm = GetCurrFrm( sal_False );
        const SwFlyFrm* pFly;
        const SwSection* pSection;

        if( pFrm && pFrm->IsInFly() &&
             (pFly = pFrm->FindFlyFrm())->GetFmt()->GetEditInReadonly().GetValue() &&
             pFly->Lower() &&
             !pFly->Lower()->IsNoTxtFrm() &&
             !GetDrawView()->GetMarkedObjectList().GetMarkCount() )
        {
            return sal_False;
        }
        // edit in readonly sections
        else if ( pFrm && pFrm->IsInSct() &&
                  0 != ( pSection = pFrm->FindSctFrm()->GetSection() ) &&
                  pSection->IsEditInReadonlyFlag() )
        {
            return sal_False;
        }

        return sal_True;
    }
    return sal_False;
}


// darf der Cursor in ReadOnlyBereiche?
void SwCrsrShell::SetReadOnlyAvailable( sal_Bool bFlag )
{
    // im GlobalDoc darf NIE umgeschaltet werden
    if( (!GetDoc()->GetDocShell() ||
         !GetDoc()->GetDocShell()->IsA( SwGlobalDocShell::StaticType() )) &&
        bFlag != bSetCrsrInReadOnly )
    {
        // wenn das Flag ausgeschaltet wird, dann muessen erstmal alle
        // Selektionen aufgehoben werden. Denn sonst wird sich darauf
        // verlassen, das nichts geschuetztes selektiert ist!
        if( !bFlag )
        {
            ClearMark();
        }
        bSetCrsrInReadOnly = bFlag;
        UpdateCrsr();
    }
}

sal_Bool SwCrsrShell::HasReadonlySel() const
{
    sal_Bool bRet = sal_False;
    if( IsReadOnlyAvailable() || GetViewOptions()->IsFormView() )
    {
        if( pTblCrsr )
            bRet = pTblCrsr->HasReadOnlyBoxSel() ||
                   pTblCrsr->HasReadonlySel( GetViewOptions()->IsFormView() );
        else
        {
            const SwPaM* pCrsr = pCurCrsr;

            do {
                if( pCrsr->HasReadonlySel( GetViewOptions()->IsFormView() ) )
                    bRet = sal_True;
            } while( !bRet && pCurCrsr != ( pCrsr = (SwPaM*)pCrsr->GetNext() ));
        }
    }
    return bRet;
}

sal_Bool SwCrsrShell::IsSelFullPara() const
{
    sal_Bool bRet = sal_False;

    if( pCurCrsr->GetPoint()->nNode.GetIndex() ==
        pCurCrsr->GetMark()->nNode.GetIndex() && pCurCrsr == pCurCrsr->GetNext() )
    {
        xub_StrLen nStt = pCurCrsr->GetPoint()->nContent.GetIndex(),
                   nEnd = pCurCrsr->GetMark()->nContent.GetIndex();
        if( nStt > nEnd )
        {
            xub_StrLen nTmp = nStt;
            nStt = nEnd;
            nEnd = nTmp;
        }
        const SwCntntNode* pCNd = pCurCrsr->GetCntntNode();
        bRet = pCNd && !nStt && nEnd == pCNd->Len();
    }
    return bRet;
}

short SwCrsrShell::GetTextDirection( const Point* pPt ) const
{
    SwPosition aPos( *pCurCrsr->GetPoint() );
    Point aPt( pPt ? *pPt : pCurCrsr->GetPtPos() );
    if( pPt )
    {
        SwCrsrMoveState aTmpState( MV_NONE );
        aTmpState.bSetInReadOnly = IsReadOnlyAvailable();

        GetLayout()->GetCrsrOfst( &aPos, aPt, &aTmpState );
    }

    return pDoc->GetTextDirection( aPos, &aPt );
}

sal_Bool SwCrsrShell::IsInVerticalText( const Point* pPt ) const
{
    const short nDir = GetTextDirection( pPt );
    return FRMDIR_VERT_TOP_RIGHT == nDir || FRMDIR_VERT_TOP_LEFT == nDir;
}

sal_Bool SwCrsrShell::IsInRightToLeftText( const Point* pPt ) const
{
    const short nDir = GetTextDirection( pPt );
    // GetTextDirection uses FRMDIR_VERT_TOP_LEFT to indicate RTL in
    // vertical environment
    return FRMDIR_VERT_TOP_LEFT == nDir || FRMDIR_HORI_RIGHT_TOP == nDir;
}

//
// If the current cursor position is inside a hidden range, the hidden range
// is selected:
//
bool SwCrsrShell::SelectHiddenRange()
{
    bool bRet = false;
    if ( !GetViewOptions()->IsShowHiddenChar() && !pCurCrsr->HasMark() )
    {
        SwPosition& rPt = *(SwPosition*)pCurCrsr->GetPoint();
        const SwTxtNode* pNode = rPt.nNode.GetNode().GetTxtNode();
        if ( pNode )
        {
            const xub_StrLen nPos = rPt.nContent.GetIndex();

            // check if nPos is in hidden range
            xub_StrLen nHiddenStart;
            xub_StrLen nHiddenEnd;
            SwScriptInfo::GetBoundsOfHiddenRange( *pNode, nPos, nHiddenStart, nHiddenEnd );
            if ( STRING_LEN != nHiddenStart )
            {
                // make selection:
                pCurCrsr->SetMark();
                pCurCrsr->GetMark()->nContent = nHiddenEnd;
                bRet = true;
            }
        }
    }

    return bRet;
}

    // die Suchfunktionen
sal_uLong SwCrsrShell::Find( const SearchOptions& rSearchOpt, sal_Bool bSearchInNotes,
                            SwDocPositions eStart, SwDocPositions eEnde,
                            sal_Bool& bCancel,
                            FindRanges eRng, int bReplace )
{
    if( pTblCrsr )
        GetCrsr();
    delete pTblCrsr, pTblCrsr = 0;
    SwCallLink aLk( *this );        // Crsr-Moves ueberwachen, evt. Link callen
    sal_uLong nRet = pCurCrsr->Find( rSearchOpt, bSearchInNotes, eStart, eEnde, bCancel, eRng, bReplace );
    if( nRet || bCancel )
        UpdateCrsr();
    return nRet;
}

sal_uLong SwCrsrShell::Find( const SwTxtFmtColl& rFmtColl,
                            SwDocPositions eStart, SwDocPositions eEnde,
                            sal_Bool& bCancel,
                            FindRanges eRng, const SwTxtFmtColl* pReplFmt )
{
    if( pTblCrsr )
        GetCrsr();
    delete pTblCrsr, pTblCrsr = 0;
    SwCallLink aLk( *this );        // Crsr-Moves ueberwachen, evt. Link callen
    sal_uLong nRet = pCurCrsr->Find( rFmtColl, eStart, eEnde, bCancel, eRng, pReplFmt );
    if( nRet )
        UpdateCrsr();
    return nRet;
}

sal_uLong SwCrsrShell::Find( const SfxItemSet& rSet, sal_Bool bNoCollections,
                            SwDocPositions eStart, SwDocPositions eEnde,
                            sal_Bool& bCancel,
                            FindRanges eRng, const SearchOptions* pSearchOpt,
                            const SfxItemSet* rReplSet )
{
    if( pTblCrsr )
        GetCrsr();
    delete pTblCrsr, pTblCrsr = 0;
    SwCallLink aLk( *this );        // Crsr-Moves ueberwachen, evt. Link callen
    sal_uLong nRet = pCurCrsr->Find( rSet, bNoCollections, eStart, eEnde, bCancel,
                                eRng, pSearchOpt, rReplSet );
    if( nRet )
        UpdateCrsr();
    return nRet;
}

void SwCrsrShell::SetSelection( const SwPaM& rCrsr )
{
    StartAction();
    SwPaM* pCrsr = GetCrsr();
    *pCrsr->GetPoint() = *rCrsr.GetPoint();
    if(rCrsr.HasMark())
    {
        pCrsr->SetMark();
        *pCrsr->GetMark() = *rCrsr.GetMark();
    }
    if((SwPaM*)rCrsr.GetNext() != &rCrsr)
    {
        const SwPaM *_pStartCrsr = (SwPaM*)rCrsr.GetNext();
        do
        {
            SwPaM* pCurrentCrsr = CreateCrsr();
            *pCurrentCrsr->GetPoint() = *_pStartCrsr->GetPoint();
            if(_pStartCrsr->HasMark())
            {
                pCurrentCrsr->SetMark();
                *pCurrentCrsr->GetMark() = *_pStartCrsr->GetMark();
            }
        } while( (_pStartCrsr=(SwPaM *)_pStartCrsr->GetNext()) != &rCrsr );
    }
    EndAction();
}

void lcl_RemoveMark( SwPaM* pPam )
{
    OSL_ENSURE( pPam->HasMark(), "Don't remove pPoint!" );
    pPam->GetMark()->nContent.Assign( 0, 0 );
    pPam->GetMark()->nNode = 0;
    pPam->DeleteMark();
}

const SwStartNode* lcl_NodeContext( const SwNode& rNode )
{
    const SwStartNode *pRet = rNode.StartOfSectionNode();
    while( pRet->IsSectionNode() || pRet->IsTableNode() ||
        pRet->GetStartNodeType() == SwTableBoxStartNode )
    {
        pRet = pRet->StartOfSectionNode();
    }
    return pRet;
}

/**
   Checks if a position is valid. To be valid the position's node must
   be a content node and the content must not be unregistered.

   @param aPos the position to check.
*/
bool lcl_PosOk(const SwPosition & aPos)
{
    return NULL != aPos.nNode.GetNode().GetCntntNode() &&
           SwIndexReg::pEmptyIndexArray != aPos.nContent.GetIdxReg();
}

/**
   Checks if a PaM is valid. For a PaM to be valid its point must be
   valid. Additionaly if the PaM has a mark this has to be valid, too.

   @param aPam the PaM to check
*/
static bool lcl_CrsrOk(SwPaM & aPam)
{
    return lcl_PosOk(*aPam.GetPoint()) && (! aPam.HasMark()
        || lcl_PosOk(*aPam.GetMark()));
}

void SwCrsrShell::ClearUpCrsrs()
{
    // start of the ring
    SwPaM * pStartCrsr = GetCrsr();
    // start loop with second entry of the ring
    SwPaM * pCrsr = (SwPaM *) pStartCrsr->GetNext();
    SwPaM * pTmpCrsr;
    bool bChanged = false;

    /*
       For all entries in the ring except the start entry delete the
       entry if it is invalid.
    */
    while (pCrsr != pStartCrsr)
    {
        pTmpCrsr = (SwPaM *) pCrsr->GetNext();

        if ( ! lcl_CrsrOk(*pCrsr))
        {
            delete pCrsr;

            bChanged = true;
        }

        pCrsr = pTmpCrsr;
    }

    if( pStartCrsr->HasMark() && !lcl_PosOk( *pStartCrsr->GetMark() ) )
    {
        lcl_RemoveMark( pStartCrsr );
        bChanged = true;
    }
    if( !lcl_PosOk( *pStartCrsr->GetPoint() ) )
    {
        SwNodes & aNodes = GetDoc()->GetNodes();
        const SwNode* pStart = lcl_NodeContext( pStartCrsr->GetPoint()->nNode.GetNode() );
        SwNodeIndex aIdx( pStartCrsr->GetPoint()->nNode );
        SwNode * pNode = aNodes.GoPrevious(&aIdx);
        if( pNode == NULL || lcl_NodeContext( *pNode ) != pStart )
            aNodes.GoNext( &aIdx );
        if( pNode == NULL || lcl_NodeContext( *pNode ) != pStart )
        {
            /*
              If the start entry of the ring is invalid replace it with a
              cursor pointing to the beginning of the first content node in
              the document.
            */
            aIdx = (*(aNodes.GetEndOfContent().StartOfSectionNode()));
            pNode = aNodes.GoNext( &aIdx );
        }
        bool bFound = (pNode != NULL);

        OSL_ENSURE(bFound, "no content node found");

        if (bFound)
        {
            SwPaM aTmpPam(*pNode);
            *pStartCrsr = aTmpPam;
        }

        bChanged = true;
    }

    /*
      If at least one of the cursors in the ring have been deleted or
      replaced, remove the table cursor.
    */
    if (pTblCrsr != NULL && bChanged)
        TblCrsrToCursor();
}

String SwCrsrShell::GetCrsrDescr() const
{
    String aResult;

    if (IsMultiSelection())
        aResult += String(SW_RES(STR_MULTISEL));
    else
        aResult = GetDoc()->GetPaMDescr(*GetCrsr());

    return aResult;
}

// SMARTTAGS

void lcl_FillRecognizerData( uno::Sequence< rtl::OUString >& rSmartTagTypes,
                             uno::Sequence< uno::Reference< container::XStringKeyMap > >& rStringKeyMaps,
                             const SwWrongList& rSmartTagList, xub_StrLen nCurrent )
{
    // Insert smart tag information
    std::vector< rtl::OUString > aSmartTagTypes;
    std::vector< uno::Reference< container::XStringKeyMap > > aStringKeyMaps;

    for ( sal_uInt16 i = 0; i < rSmartTagList.Count(); ++i )
    {
        const xub_StrLen nSTPos = rSmartTagList.Pos( i );
        const xub_StrLen nSTLen = rSmartTagList.Len( i );

        if ( nSTPos <= nCurrent && nCurrent < nSTPos + nSTLen )
        {
            const SwWrongArea* pArea = rSmartTagList.GetElement( i );
            if ( pArea )
            {
                aSmartTagTypes.push_back( pArea->maType );
                aStringKeyMaps.push_back( pArea->mxPropertyBag );
            }
        }
    }

    if ( aSmartTagTypes.size() )
    {
        rSmartTagTypes.realloc( aSmartTagTypes.size() );
        rStringKeyMaps.realloc( aSmartTagTypes.size() );

        std::vector< rtl::OUString >::const_iterator aTypesIter = aSmartTagTypes.begin();
        sal_uInt16 i = 0;
        for ( aTypesIter = aSmartTagTypes.begin(); aTypesIter != aSmartTagTypes.end(); ++aTypesIter )
            rSmartTagTypes[i++] = *aTypesIter;

        std::vector< uno::Reference< container::XStringKeyMap > >::const_iterator aMapsIter = aStringKeyMaps.begin();
        i = 0;
        for ( aMapsIter = aStringKeyMaps.begin(); aMapsIter != aStringKeyMaps.end(); ++aMapsIter )
            rStringKeyMaps[i++] = *aMapsIter;
    }
}

void lcl_FillTextRange( uno::Reference<text::XTextRange>& rRange,
                   SwTxtNode& rNode, xub_StrLen nBegin, xub_StrLen nLen )
{
    // create SwPosition for nStartIndex
    SwIndex aIndex( &rNode, nBegin );
    SwPosition aStartPos( rNode, aIndex );

    // create SwPosition for nEndIndex
    SwPosition aEndPos( aStartPos );
    aEndPos.nContent = nBegin + nLen;

    const uno::Reference<text::XTextRange> xRange =
        SwXTextRange::CreateXTextRange(*rNode.GetDoc(), aStartPos, &aEndPos);

    rRange = xRange;
}

void SwCrsrShell::GetSmartTagTerm( uno::Sequence< rtl::OUString >& rSmartTagTypes,
                                   uno::Sequence< uno::Reference< container::XStringKeyMap > >& rStringKeyMaps,
                                   uno::Reference< text::XTextRange>& rRange ) const
{
    if ( !SwSmartTagMgr::Get().IsSmartTagsEnabled() )
        return;

    SwPaM* pCrsr = GetCrsr();
    SwPosition aPos( *pCrsr->GetPoint() );
    SwTxtNode *pNode = aPos.nNode.GetNode().GetTxtNode();
    if ( pNode && !pNode->IsInProtectSect() )
    {
        const SwWrongList *pSmartTagList = pNode->GetSmartTags();
        if ( pSmartTagList )
        {
            xub_StrLen nCurrent = aPos.nContent.GetIndex();
            xub_StrLen nBegin = nCurrent;
            xub_StrLen nLen = 1;

            if( pSmartTagList->InWrongWord( nBegin, nLen ) && !pNode->IsSymbol(nBegin) )
            {
                const sal_uInt16 nIndex = pSmartTagList->GetWrongPos( nBegin );
                const SwWrongList* pSubList = pSmartTagList->SubList( nIndex );
                if ( pSubList )
                {
                    pSmartTagList = pSubList;
                    nCurrent = 0;
                }

                lcl_FillRecognizerData( rSmartTagTypes, rStringKeyMaps, *pSmartTagList, nCurrent );
                lcl_FillTextRange( rRange, *pNode, nBegin, nLen );
            }
        }
    }
}

// see also SwEditShell::GetCorrection( const Point* pPt, SwRect& rSelectRect )
void SwCrsrShell::GetSmartTagTerm( const Point& rPt, SwRect& rSelectRect,
                                   uno::Sequence< rtl::OUString >& rSmartTagTypes,
                                   uno::Sequence< uno::Reference< container::XStringKeyMap > >& rStringKeyMaps,
                                   uno::Reference<text::XTextRange>& rRange )
{
    if ( !SwSmartTagMgr::Get().IsSmartTagsEnabled() )
        return;

    SwPaM* pCrsr = GetCrsr();
    SwPosition aPos( *pCrsr->GetPoint() );
    Point aPt( rPt );
    SwCrsrMoveState eTmpState( MV_SETONLYTEXT );
    SwSpecialPos aSpecialPos;
    eTmpState.pSpecialPos = &aSpecialPos;
    SwTxtNode *pNode;
    const SwWrongList *pSmartTagList;

    if( GetLayout()->GetCrsrOfst( &aPos, aPt, &eTmpState ) &&
        0 != (pNode = aPos.nNode.GetNode().GetTxtNode()) &&
        0 != (pSmartTagList = pNode->GetSmartTags()) &&
        !pNode->IsInProtectSect() )
    {
        xub_StrLen nCurrent = aPos.nContent.GetIndex();
        xub_StrLen nBegin = nCurrent;
        xub_StrLen nLen = 1;

        if( pSmartTagList->InWrongWord( nBegin, nLen ) && !pNode->IsSymbol(nBegin) )
        {
            const sal_uInt16 nIndex = pSmartTagList->GetWrongPos( nBegin );
            const SwWrongList* pSubList = pSmartTagList->SubList( nIndex );
            if ( pSubList )
            {
                pSmartTagList = pSubList;
                nCurrent = eTmpState.pSpecialPos->nCharOfst;
            }

            lcl_FillRecognizerData( rSmartTagTypes, rStringKeyMaps, *pSmartTagList, nCurrent );
            lcl_FillTextRange( rRange, *pNode, nBegin, nLen );

            // get smarttag word
            String aText( pNode->GetTxt().Copy( nBegin, nLen ) );

            //save the start and end positons of the line and the starting point
            Push();
            LeftMargin();
            xub_StrLen nLineStart = GetCrsr()->GetPoint()->nContent.GetIndex();
            RightMargin();
            xub_StrLen nLineEnd = GetCrsr()->GetPoint()->nContent.GetIndex();
            Pop(sal_False);

            // make sure the selection build later from the
            // data below does not include footnotes and other
            // "in word" character to the left and right in order
            // to preserve those. Therefore count those "in words"
            // in order to modify the selection accordingly.
            const sal_Unicode* pChar = aText.GetBuffer();
            xub_StrLen nLeft = 0;
            while (pChar && *pChar++ == CH_TXTATR_INWORD)
                ++nLeft;
            pChar = aText.Len() ? aText.GetBuffer() + aText.Len() - 1 : 0;
            xub_StrLen nRight = 0;
            while (pChar && *pChar-- == CH_TXTATR_INWORD)
                ++nRight;

            aPos.nContent = nBegin + nLeft;
            pCrsr = GetCrsr();
            *pCrsr->GetPoint() = aPos;
            pCrsr->SetMark();
            ExtendSelection( sal_True, nLen - nLeft - nRight );
            //no determine the rectangle in the current line
            xub_StrLen nWordStart = (nBegin + nLeft) < nLineStart ? nLineStart : nBegin + nLeft;
            //take one less than the line end - otherwise the next line would be calculated
            xub_StrLen nWordEnd = (nBegin + nLen - nLeft - nRight) > nLineEnd ? nLineEnd : (nBegin + nLen - nLeft - nRight);
            Push();
            pCrsr->DeleteMark();
            SwIndex& rContent = GetCrsr()->GetPoint()->nContent;
            rContent = nWordStart;
            SwRect aStartRect;
            SwCrsrMoveState aState;
            aState.bRealWidth = sal_True;
            SwCntntNode* pCntntNode = pCrsr->GetCntntNode();
            SwCntntFrm *pCntntFrame = pCntntNode->getLayoutFrm( GetLayout(), &rPt, pCrsr->GetPoint(), sal_False);

            pCntntFrame->GetCharRect( aStartRect, *pCrsr->GetPoint(), &aState );
            rContent = nWordEnd - 1;
            SwRect aEndRect;
            pCntntFrame->GetCharRect( aEndRect, *pCrsr->GetPoint(),&aState );
            rSelectRect = aStartRect.Union( aEndRect );
            Pop(sal_False);
        }
    }
}

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