SAL_UNUSED_PARAMETER (expanding to __attribute__ ((unused)) for GCC) is used to annotate legitimately unused parameters, so that static analysis tools can tell legitimately unused parameters from truly unnecessary ones. To that end, some patches for external modules are also added, that are only applied when compiling with GCC and add necessary __attribute__ ((unused)) in headers.
2792 lines
100 KiB
C++
2792 lines
100 KiB
C++
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
|
/*************************************************************************
|
|
*
|
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
|
*
|
|
* Copyright 2000, 2010 Oracle and/or its affiliates.
|
|
*
|
|
* OpenOffice.org - a multi-platform office productivity suite
|
|
*
|
|
* This file is part of OpenOffice.org.
|
|
*
|
|
* OpenOffice.org is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU Lesser General Public License version 3
|
|
* only, as published by the Free Software Foundation.
|
|
*
|
|
* OpenOffice.org is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU Lesser General Public License version 3 for more details
|
|
* (a copy is included in the LICENSE file that accompanied this code).
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public License
|
|
* version 3 along with OpenOffice.org. If not, see
|
|
* <http://www.openoffice.org/license.html>
|
|
* for a copy of the LGPLv3 License.
|
|
*
|
|
************************************************************************/
|
|
|
|
|
|
#include <string.h> // for strchr()
|
|
#include <hintids.hxx>
|
|
|
|
#include <vcl/sound.hxx>
|
|
#include <editeng/cscoitem.hxx>
|
|
#include <editeng/brkitem.hxx>
|
|
#include <linguistic/lngprops.hxx>
|
|
#include <com/sun/star/beans/XPropertySet.hpp>
|
|
#include <com/sun/star/i18n/WordType.hdl>
|
|
#include <unotools/charclass.hxx>
|
|
#include <unotools/transliterationwrapper.hxx>
|
|
#include <fmtanchr.hxx>
|
|
#include <fmtcntnt.hxx>
|
|
#include <fmtpdsc.hxx>
|
|
#include <txtftn.hxx>
|
|
#include <acorrect.hxx> // AutoCorrect
|
|
#include <IMark.hxx> // for SwBookmark
|
|
#include <cntfrm.hxx> // for Spell
|
|
#include <crsrsh.hxx>
|
|
#include <doc.hxx>
|
|
#include <UndoManager.hxx>
|
|
#include <docsh.hxx>
|
|
#include <docary.hxx>
|
|
#include <doctxm.hxx> // when moving: correct indexes
|
|
#include <ftnidx.hxx>
|
|
#include <ftninfo.hxx>
|
|
#include <mdiexp.hxx> // status bar
|
|
#include <mvsave.hxx> // structures to save when moving/deleting
|
|
#include <ndtxt.hxx>
|
|
#include <pam.hxx>
|
|
#include <redline.hxx>
|
|
#include <rootfrm.hxx> // for UpdateFtn
|
|
#include <splargs.hxx> // for Spell
|
|
#include <swtable.hxx>
|
|
#include <swundo.hxx> // for the UndoIds
|
|
#include <txtfrm.hxx>
|
|
#include <hints.hxx>
|
|
#include <UndoSplitMove.hxx>
|
|
#include <UndoRedline.hxx>
|
|
#include <UndoOverwrite.hxx>
|
|
#include <UndoInsert.hxx>
|
|
#include <UndoDelete.hxx>
|
|
#include <breakit.hxx>
|
|
#include <hhcwrp.hxx>
|
|
#include <vcl/msgbox.hxx>
|
|
#include "comcore.hrc"
|
|
#include "editsh.hxx"
|
|
#include <unoflatpara.hxx>
|
|
#include <SwGrammarMarkUp.hxx>
|
|
|
|
#include <vector>
|
|
|
|
using ::rtl::OUString;
|
|
using namespace ::com::sun::star;
|
|
using namespace ::com::sun::star::linguistic2;
|
|
using namespace ::com::sun::star::i18n;
|
|
|
|
#ifndef S2U
|
|
#define S2U(rString) OUString::createFromAscii(rString)
|
|
#endif
|
|
|
|
struct _SaveRedline
|
|
{
|
|
SwRedline* pRedl;
|
|
sal_uInt32 nStt, nEnd;
|
|
xub_StrLen nSttCnt, nEndCnt;
|
|
|
|
_SaveRedline( SwRedline* pR, const SwNodeIndex& rSttIdx )
|
|
: pRedl( pR )
|
|
{
|
|
const SwPosition* pStt = pR->Start(),
|
|
* pEnd = pR->GetMark() == pStt ? pR->GetPoint() : pR->GetMark();
|
|
sal_uInt32 nSttIdx = rSttIdx.GetIndex();
|
|
nStt = pStt->nNode.GetIndex() - nSttIdx;
|
|
nSttCnt = pStt->nContent.GetIndex();
|
|
if( pR->HasMark() )
|
|
{
|
|
nEnd = pEnd->nNode.GetIndex() - nSttIdx;
|
|
nEndCnt = pEnd->nContent.GetIndex();
|
|
}
|
|
|
|
pRedl->GetPoint()->nNode = 0;
|
|
pRedl->GetPoint()->nContent.Assign( 0, 0 );
|
|
pRedl->GetMark()->nNode = 0;
|
|
pRedl->GetMark()->nContent.Assign( 0, 0 );
|
|
}
|
|
|
|
_SaveRedline( SwRedline* pR, const SwPosition& rPos )
|
|
: pRedl( pR )
|
|
{
|
|
const SwPosition* pStt = pR->Start(),
|
|
* pEnd = pR->GetMark() == pStt ? pR->GetPoint() : pR->GetMark();
|
|
sal_uInt32 nSttIdx = rPos.nNode.GetIndex();
|
|
nStt = pStt->nNode.GetIndex() - nSttIdx;
|
|
nSttCnt = pStt->nContent.GetIndex();
|
|
if( nStt == 0 )
|
|
nSttCnt = nSttCnt - rPos.nContent.GetIndex();
|
|
if( pR->HasMark() )
|
|
{
|
|
nEnd = pEnd->nNode.GetIndex() - nSttIdx;
|
|
nEndCnt = pEnd->nContent.GetIndex();
|
|
if( nEnd == 0 )
|
|
nEndCnt = nEndCnt - rPos.nContent.GetIndex();
|
|
}
|
|
|
|
pRedl->GetPoint()->nNode = 0;
|
|
pRedl->GetPoint()->nContent.Assign( 0, 0 );
|
|
pRedl->GetMark()->nNode = 0;
|
|
pRedl->GetMark()->nContent.Assign( 0, 0 );
|
|
}
|
|
|
|
void SetPos( sal_uInt32 nInsPos )
|
|
{
|
|
pRedl->GetPoint()->nNode = nInsPos + nStt;
|
|
pRedl->GetPoint()->nContent.Assign( pRedl->GetCntntNode(), nSttCnt );
|
|
if( pRedl->HasMark() )
|
|
{
|
|
pRedl->GetMark()->nNode = nInsPos + nEnd;
|
|
pRedl->GetMark()->nContent.Assign( pRedl->GetCntntNode(sal_False), nEndCnt );
|
|
}
|
|
}
|
|
|
|
void SetPos( const SwPosition& aPos )
|
|
{
|
|
pRedl->GetPoint()->nNode = aPos.nNode.GetIndex() + nStt;
|
|
pRedl->GetPoint()->nContent.Assign( pRedl->GetCntntNode(), nSttCnt + ( nStt == 0 ? aPos.nContent.GetIndex() : 0 ) );
|
|
if( pRedl->HasMark() )
|
|
{
|
|
pRedl->GetMark()->nNode = aPos.nNode.GetIndex() + nEnd;
|
|
pRedl->GetMark()->nContent.Assign( pRedl->GetCntntNode(sal_False), nEndCnt + ( nEnd == 0 ? aPos.nContent.GetIndex() : 0 ) );
|
|
}
|
|
}
|
|
};
|
|
|
|
SV_DECL_PTRARR_DEL( _SaveRedlines, _SaveRedline*, 0 )
|
|
|
|
SV_IMPL_VARARR( _SaveFlyArr, _SaveFly )
|
|
SV_IMPL_PTRARR( _SaveRedlines, _SaveRedline* )
|
|
|
|
bool lcl_MayOverwrite( const SwTxtNode *pNode, const xub_StrLen nPos )
|
|
{
|
|
sal_Unicode cChr = pNode->GetTxt().GetChar( nPos );
|
|
return !( ( CH_TXTATR_BREAKWORD == cChr || CH_TXTATR_INWORD == cChr ) &&
|
|
(0 != pNode->GetTxtAttrForCharAt( nPos ) ) );
|
|
}
|
|
|
|
void lcl_SkipAttr( const SwTxtNode *pNode, SwIndex &rIdx, xub_StrLen &rStart )
|
|
{
|
|
if( !lcl_MayOverwrite( pNode, rStart ) )
|
|
{
|
|
// skip all special attributes
|
|
do {
|
|
// "Beep" for every single one we skip
|
|
Sound::Beep(SOUND_ERROR);
|
|
rIdx++;
|
|
} while( (rStart = rIdx.GetIndex()) < pNode->GetTxt().Len()
|
|
&& !lcl_MayOverwrite(pNode, rStart) );
|
|
}
|
|
}
|
|
|
|
void _RestFlyInRange( _SaveFlyArr & rArr, const SwNodeIndex& rSttIdx,
|
|
const SwNodeIndex* pInsertPos )
|
|
{
|
|
SwPosition aPos( rSttIdx );
|
|
for( sal_uInt16 n = 0; n < rArr.Count(); ++n )
|
|
{
|
|
// create new anchor
|
|
_SaveFly& rSave = rArr[n];
|
|
SwFrmFmt* pFmt = rSave.pFrmFmt;
|
|
|
|
if( rSave.bInsertPosition )
|
|
{
|
|
if( pInsertPos != NULL )
|
|
aPos.nNode = *pInsertPos;
|
|
else
|
|
aPos.nNode = rSttIdx.GetIndex();
|
|
}
|
|
else
|
|
aPos.nNode = rSttIdx.GetIndex() + rSave.nNdDiff;
|
|
|
|
aPos.nContent.Assign( 0, 0 );
|
|
SwFmtAnchor aAnchor( pFmt->GetAnchor() );
|
|
aAnchor.SetAnchor( &aPos );
|
|
pFmt->GetDoc()->GetSpzFrmFmts()->Insert(
|
|
pFmt, pFmt->GetDoc()->GetSpzFrmFmts()->Count() );
|
|
pFmt->SetFmtAttr( aAnchor );
|
|
SwCntntNode* pCNd = aPos.nNode.GetNode().GetCntntNode();
|
|
if( pCNd && pCNd->getLayoutFrm( pFmt->GetDoc()->GetCurrentLayout(), 0, 0, sal_False ) )
|
|
pFmt->MakeFrms();
|
|
}
|
|
}
|
|
|
|
void _SaveFlyInRange( const SwNodeRange& rRg, _SaveFlyArr& rArr )
|
|
{
|
|
SwSpzFrmFmts& rFmts = *rRg.aStart.GetNode().GetDoc()->GetSpzFrmFmts();
|
|
for( sal_uInt16 n = 0; n < rFmts.Count(); ++n )
|
|
{
|
|
SwFrmFmt *const pFmt = static_cast<SwFrmFmt*>(rFmts[n]);
|
|
SwFmtAnchor const*const pAnchor = &pFmt->GetAnchor();
|
|
SwPosition const*const pAPos = pAnchor->GetCntntAnchor();
|
|
if (pAPos &&
|
|
((FLY_AT_PARA == pAnchor->GetAnchorId()) ||
|
|
(FLY_AT_CHAR == pAnchor->GetAnchorId())) &&
|
|
rRg.aStart <= pAPos->nNode && pAPos->nNode < rRg.aEnd )
|
|
{
|
|
_SaveFly aSave( pAPos->nNode.GetIndex() - rRg.aStart.GetIndex(),
|
|
pFmt, sal_False );
|
|
rArr.Insert( aSave, rArr.Count());
|
|
pFmt->DelFrms();
|
|
rFmts.Remove( n--, 1 );
|
|
}
|
|
}
|
|
}
|
|
|
|
void _SaveFlyInRange( const SwPaM& rPam, const SwNodeIndex& rInsPos,
|
|
_SaveFlyArr& rArr, bool bMoveAllFlys )
|
|
{
|
|
SwSpzFrmFmts& rFmts = *rPam.GetPoint()->nNode.GetNode().GetDoc()->GetSpzFrmFmts();
|
|
SwFrmFmt* pFmt;
|
|
const SwFmtAnchor* pAnchor;
|
|
|
|
const SwPosition* pPos = rPam.Start();
|
|
const SwNodeIndex& rSttNdIdx = pPos->nNode;
|
|
short nSttOff = (!bMoveAllFlys && rSttNdIdx.GetNode().IsCntntNode() &&
|
|
pPos->nContent.GetIndex()) ? 1 : 0;
|
|
|
|
pPos = rPam.GetPoint() == pPos ? rPam.GetMark() : rPam.GetPoint();
|
|
const SwNodeIndex& rEndNdIdx = pPos->nNode;
|
|
short nOff = ( bMoveAllFlys || ( rEndNdIdx.GetNode().IsCntntNode() &&
|
|
pPos->nContent == rEndNdIdx.GetNode().GetCntntNode()->Len() ))
|
|
? 0 : 1;
|
|
|
|
const SwNodeIndex* pCntntIdx;
|
|
|
|
for( sal_uInt16 n = 0; n < rFmts.Count(); ++n )
|
|
{
|
|
sal_Bool bInsPos = sal_False;
|
|
pFmt = (SwFrmFmt*)rFmts[n];
|
|
pAnchor = &pFmt->GetAnchor();
|
|
const SwPosition* pAPos = pAnchor->GetCntntAnchor();
|
|
if (pAPos &&
|
|
((FLY_AT_PARA == pAnchor->GetAnchorId()) ||
|
|
(FLY_AT_CHAR == pAnchor->GetAnchorId())) &&
|
|
// do not move if the InsPos is in the CntntArea of the Fly
|
|
( 0 == ( pCntntIdx = pFmt->GetCntnt().GetCntntIdx() ) ||
|
|
!( *pCntntIdx < rInsPos &&
|
|
rInsPos < pCntntIdx->GetNode().EndOfSectionIndex() )) )
|
|
{
|
|
if( !bMoveAllFlys && rEndNdIdx == pAPos->nNode )
|
|
{
|
|
// Do not touch Anchor, if only a part of the EndNode
|
|
// or the whole EndNode is identical with the SttNode
|
|
if( rSttNdIdx != pAPos->nNode )
|
|
{
|
|
// Only attach an anchor to the beginning or end
|
|
SwPosition aPos( rSttNdIdx );
|
|
SwFmtAnchor aAnchor( *pAnchor );
|
|
aAnchor.SetAnchor( &aPos );
|
|
pFmt->SetFmtAttr( aAnchor );
|
|
}
|
|
}
|
|
else if( ( rSttNdIdx.GetIndex() + nSttOff <= pAPos->nNode.GetIndex()
|
|
&& pAPos->nNode.GetIndex() <= rEndNdIdx.GetIndex() - nOff ) ||
|
|
0 != ( bInsPos = rInsPos == pAPos->nNode ))
|
|
|
|
{
|
|
_SaveFly aSave( pAPos->nNode.GetIndex() - rSttNdIdx.GetIndex(),
|
|
pFmt, bInsPos );
|
|
rArr.Insert( aSave, rArr.Count());
|
|
pFmt->DelFrms();
|
|
rFmts.Remove( n--, 1 );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Delete and move all Flys at the paragraph, that are within the selection.
|
|
// If there is a Fly at the SPoint, it is moved onto the Mark.
|
|
void DelFlyInRange( const SwNodeIndex& rMkNdIdx,
|
|
const SwNodeIndex& rPtNdIdx )
|
|
{
|
|
const sal_Bool bDelFwrd = rMkNdIdx.GetIndex() <= rPtNdIdx.GetIndex();
|
|
|
|
SwDoc* pDoc = rMkNdIdx.GetNode().GetDoc();
|
|
SwSpzFrmFmts& rTbl = *pDoc->GetSpzFrmFmts();
|
|
for ( sal_uInt16 i = rTbl.Count(); i; )
|
|
{
|
|
SwFrmFmt *pFmt = rTbl[--i];
|
|
const SwFmtAnchor &rAnch = pFmt->GetAnchor();
|
|
SwPosition const*const pAPos = rAnch.GetCntntAnchor();
|
|
if (pAPos &&
|
|
((rAnch.GetAnchorId() == FLY_AT_PARA) ||
|
|
(rAnch.GetAnchorId() == FLY_AT_CHAR)) &&
|
|
( bDelFwrd
|
|
? rMkNdIdx < pAPos->nNode && pAPos->nNode <= rPtNdIdx
|
|
: rPtNdIdx <= pAPos->nNode && pAPos->nNode < rMkNdIdx ))
|
|
{
|
|
// Only move the Anchor??
|
|
if( rPtNdIdx == pAPos->nNode )
|
|
{
|
|
SwFmtAnchor aAnch( pFmt->GetAnchor() );
|
|
SwPosition aPos( rMkNdIdx );
|
|
aAnch.SetAnchor( &aPos );
|
|
pFmt->SetFmtAttr( aAnch );
|
|
}
|
|
else
|
|
{
|
|
// If the Fly is deleted, all Flys in it's content have to be deleted too.
|
|
const SwFmtCntnt &rCntnt = pFmt->GetCntnt();
|
|
if( rCntnt.GetCntntIdx() )
|
|
{
|
|
DelFlyInRange( *rCntnt.GetCntntIdx(),
|
|
SwNodeIndex( *rCntnt.GetCntntIdx()->
|
|
GetNode().EndOfSectionNode() ));
|
|
// Position could have been moved!
|
|
if( i > rTbl.Count() )
|
|
i = rTbl.Count();
|
|
else if( pFmt != rTbl[i] )
|
|
i = rTbl.GetPos( pFmt );
|
|
}
|
|
|
|
pDoc->DelLayoutFmt( pFmt );
|
|
|
|
// DelLayoutFmt can also trigger the deletion of objects.
|
|
if( i > rTbl.Count() )
|
|
i = rTbl.Count();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
bool lcl_SaveFtn( const SwNodeIndex& rSttNd, const SwNodeIndex& rEndNd,
|
|
const SwNodeIndex& rInsPos,
|
|
SwFtnIdxs& rFtnArr, SwFtnIdxs& rSaveArr,
|
|
const SwIndex* pSttCnt = 0, const SwIndex* pEndCnt = 0 )
|
|
{
|
|
bool bUpdateFtn = sal_False;
|
|
const SwNodes& rNds = rInsPos.GetNodes();
|
|
const bool bDelFtn = rInsPos.GetIndex() < rNds.GetEndOfAutotext().GetIndex() &&
|
|
rSttNd.GetIndex() >= rNds.GetEndOfAutotext().GetIndex();
|
|
const bool bSaveFtn = !bDelFtn &&
|
|
rInsPos.GetIndex() >= rNds.GetEndOfExtras().GetIndex();
|
|
if( rFtnArr.Count() )
|
|
{
|
|
|
|
sal_uInt16 nPos;
|
|
rFtnArr.SeekEntry( rSttNd, &nPos );
|
|
SwTxtFtn* pSrch;
|
|
const SwNode* pFtnNd;
|
|
|
|
// Delete/save all that come after it
|
|
while( nPos < rFtnArr.Count() && ( pFtnNd =
|
|
&( pSrch = rFtnArr[ nPos ] )->GetTxtNode())->GetIndex()
|
|
<= rEndNd.GetIndex() )
|
|
{
|
|
xub_StrLen nFtnSttIdx = *pSrch->GetStart();
|
|
if( ( pEndCnt && pSttCnt )
|
|
? (( &rSttNd.GetNode() == pFtnNd &&
|
|
pSttCnt->GetIndex() > nFtnSttIdx) ||
|
|
( &rEndNd.GetNode() == pFtnNd &&
|
|
nFtnSttIdx >= pEndCnt->GetIndex() ))
|
|
: ( &rEndNd.GetNode() == pFtnNd ))
|
|
{
|
|
++nPos; // continue searching
|
|
}
|
|
else
|
|
{
|
|
// delete it
|
|
if( bDelFtn )
|
|
{
|
|
SwTxtNode& rTxtNd = (SwTxtNode&)pSrch->GetTxtNode();
|
|
SwIndex aIdx( &rTxtNd, nFtnSttIdx );
|
|
rTxtNd.EraseText( aIdx, 1 );
|
|
}
|
|
else
|
|
{
|
|
pSrch->DelFrms(0);
|
|
rFtnArr.Remove( nPos );
|
|
if( bSaveFtn )
|
|
rSaveArr.Insert( pSrch );
|
|
}
|
|
bUpdateFtn = sal_True;
|
|
}
|
|
}
|
|
|
|
while( nPos-- && ( pFtnNd = &( pSrch = rFtnArr[ nPos ] )->
|
|
GetTxtNode())->GetIndex() >= rSttNd.GetIndex() )
|
|
{
|
|
xub_StrLen nFtnSttIdx = *pSrch->GetStart();
|
|
if( !pEndCnt || !pSttCnt ||
|
|
!( (( &rSttNd.GetNode() == pFtnNd &&
|
|
pSttCnt->GetIndex() > nFtnSttIdx ) ||
|
|
( &rEndNd.GetNode() == pFtnNd &&
|
|
nFtnSttIdx >= pEndCnt->GetIndex() )) ))
|
|
{
|
|
if( bDelFtn )
|
|
{
|
|
// delete it
|
|
SwTxtNode& rTxtNd = (SwTxtNode&)pSrch->GetTxtNode();
|
|
SwIndex aIdx( &rTxtNd, nFtnSttIdx );
|
|
rTxtNd.EraseText( aIdx, 1 );
|
|
}
|
|
else
|
|
{
|
|
pSrch->DelFrms(0);
|
|
rFtnArr.Remove( nPos );
|
|
if( bSaveFtn )
|
|
rSaveArr.Insert( pSrch );
|
|
}
|
|
bUpdateFtn = sal_True;
|
|
}
|
|
}
|
|
}
|
|
// When moving from redline section into document content section, e.g.
|
|
// after loading a document with (delete-)redlines, the footnote array
|
|
// has to be adjusted... (#i70572)
|
|
if( bSaveFtn )
|
|
{
|
|
SwNodeIndex aIdx( rSttNd );
|
|
while( aIdx < rEndNd ) // Check the moved section
|
|
{
|
|
SwNode* pNode = &aIdx.GetNode();
|
|
if( pNode->IsTxtNode() ) // Looking for text nodes...
|
|
{
|
|
SwpHints *pHints =
|
|
static_cast<SwTxtNode*>(pNode)->GetpSwpHints();
|
|
if( pHints && pHints->HasFtn() ) //...with footnotes
|
|
{
|
|
bUpdateFtn = sal_True; // Heureka
|
|
sal_uInt16 nCount = pHints->Count();
|
|
for( sal_uInt16 i = 0; i < nCount; ++i )
|
|
{
|
|
SwTxtAttr *pAttr = pHints->GetTextHint( i );
|
|
if ( pAttr->Which() == RES_TXTATR_FTN )
|
|
{
|
|
rSaveArr.Insert( static_cast<SwTxtFtn*>(pAttr) );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
++aIdx;
|
|
}
|
|
}
|
|
return bUpdateFtn;
|
|
}
|
|
|
|
void lcl_SaveRedlines( const SwPaM& aPam, _SaveRedlines& rArr )
|
|
{
|
|
SwDoc* pDoc = aPam.GetNode()->GetDoc();
|
|
|
|
const SwPosition* pStart = aPam.Start();
|
|
const SwPosition* pEnd = aPam.End();
|
|
|
|
// get first relevant redline
|
|
sal_uInt16 nCurrentRedline;
|
|
pDoc->GetRedline( *pStart, &nCurrentRedline );
|
|
if( nCurrentRedline > 0)
|
|
nCurrentRedline--;
|
|
|
|
// redline mode REDLINE_IGNORE|REDLINE_ON; save old mode
|
|
RedlineMode_t eOld = pDoc->GetRedlineMode();
|
|
pDoc->SetRedlineMode_intern( (RedlineMode_t)(( eOld & ~nsRedlineMode_t::REDLINE_IGNORE) | nsRedlineMode_t::REDLINE_ON ));
|
|
|
|
// iterate over relevant redlines and decide for each whether it should
|
|
// be saved, or split + saved
|
|
SwRedlineTbl& rRedlineTable = const_cast<SwRedlineTbl&>( pDoc->GetRedlineTbl() );
|
|
for( ; nCurrentRedline < rRedlineTable.Count(); nCurrentRedline++ )
|
|
{
|
|
SwRedline* pCurrent = rRedlineTable[ nCurrentRedline ];
|
|
SwComparePosition eCompare =
|
|
ComparePosition( *pCurrent->Start(), *pCurrent->End(),
|
|
*pStart, *pEnd);
|
|
|
|
// we must save this redline if it overlaps aPam
|
|
// (we may have to split it, too)
|
|
if( eCompare == POS_OVERLAP_BEHIND ||
|
|
eCompare == POS_OVERLAP_BEFORE ||
|
|
eCompare == POS_OUTSIDE ||
|
|
eCompare == POS_INSIDE ||
|
|
eCompare == POS_EQUAL )
|
|
{
|
|
rRedlineTable.Remove( nCurrentRedline-- );
|
|
|
|
// split beginning, if necessary
|
|
if( eCompare == POS_OVERLAP_BEFORE ||
|
|
eCompare == POS_OUTSIDE )
|
|
{
|
|
|
|
SwRedline* pNewRedline = new SwRedline( *pCurrent );
|
|
*pNewRedline->End() = *pStart;
|
|
*pCurrent->Start() = *pStart;
|
|
pDoc->AppendRedline( pNewRedline, true );
|
|
}
|
|
|
|
// split end, if necessary
|
|
if( eCompare == POS_OVERLAP_BEHIND ||
|
|
eCompare == POS_OUTSIDE )
|
|
{
|
|
SwRedline* pNewRedline = new SwRedline( *pCurrent );
|
|
*pNewRedline->Start() = *pEnd;
|
|
*pCurrent->End() = *pEnd;
|
|
pDoc->AppendRedline( pNewRedline, true );
|
|
}
|
|
|
|
// save the current redline
|
|
_SaveRedline* pSave = new _SaveRedline( pCurrent, *pStart );
|
|
rArr.C40_INSERT( _SaveRedline, pSave, rArr.Count() );
|
|
}
|
|
}
|
|
|
|
// restore old redline mode
|
|
pDoc->SetRedlineMode_intern( eOld );
|
|
}
|
|
|
|
void lcl_RestoreRedlines( SwDoc* pDoc, const SwPosition& rPos, _SaveRedlines& rArr )
|
|
{
|
|
RedlineMode_t eOld = pDoc->GetRedlineMode();
|
|
pDoc->SetRedlineMode_intern( (RedlineMode_t)(( eOld & ~nsRedlineMode_t::REDLINE_IGNORE) | nsRedlineMode_t::REDLINE_ON ));
|
|
|
|
for( sal_uInt16 n = 0; n < rArr.Count(); ++n )
|
|
{
|
|
_SaveRedline* pSave = rArr[ n ];
|
|
pSave->SetPos( rPos );
|
|
pDoc->AppendRedline( pSave->pRedl, true );
|
|
}
|
|
|
|
pDoc->SetRedlineMode_intern( eOld );
|
|
}
|
|
|
|
void lcl_SaveRedlines( const SwNodeRange& rRg, _SaveRedlines& rArr )
|
|
{
|
|
SwDoc* pDoc = rRg.aStart.GetNode().GetDoc();
|
|
sal_uInt16 nRedlPos;
|
|
SwPosition aSrchPos( rRg.aStart ); aSrchPos.nNode--;
|
|
aSrchPos.nContent.Assign( aSrchPos.nNode.GetNode().GetCntntNode(), 0 );
|
|
if( pDoc->GetRedline( aSrchPos, &nRedlPos ) && nRedlPos )
|
|
--nRedlPos;
|
|
else if( nRedlPos >= pDoc->GetRedlineTbl().Count() )
|
|
return ;
|
|
|
|
RedlineMode_t eOld = pDoc->GetRedlineMode();
|
|
pDoc->SetRedlineMode_intern( (RedlineMode_t)(( eOld & ~nsRedlineMode_t::REDLINE_IGNORE) | nsRedlineMode_t::REDLINE_ON ));
|
|
SwRedlineTbl& rRedlTbl = (SwRedlineTbl&)pDoc->GetRedlineTbl();
|
|
|
|
do {
|
|
SwRedline* pTmp = rRedlTbl[ nRedlPos ];
|
|
|
|
const SwPosition* pRStt = pTmp->Start(),
|
|
* pREnd = pTmp->GetMark() == pRStt
|
|
? pTmp->GetPoint() : pTmp->GetMark();
|
|
|
|
if( pRStt->nNode < rRg.aStart )
|
|
{
|
|
if( pREnd->nNode > rRg.aStart && pREnd->nNode < rRg.aEnd )
|
|
{
|
|
// Create a copy and set the end of the original to the end of the MoveArea.
|
|
// The copy is moved too.
|
|
SwRedline* pNewRedl = new SwRedline( *pTmp );
|
|
SwPosition* pTmpPos = pNewRedl->Start();
|
|
pTmpPos->nNode = rRg.aStart;
|
|
pTmpPos->nContent.Assign(
|
|
pTmpPos->nNode.GetNode().GetCntntNode(), 0 );
|
|
|
|
_SaveRedline* pSave = new _SaveRedline( pNewRedl, rRg.aStart );
|
|
rArr.C40_INSERT( _SaveRedline, pSave, rArr.Count() );
|
|
|
|
pTmpPos = pTmp->End();
|
|
pTmpPos->nNode = rRg.aEnd;
|
|
pTmpPos->nContent.Assign(
|
|
pTmpPos->nNode.GetNode().GetCntntNode(), 0 );
|
|
}
|
|
else if( pREnd->nNode == rRg.aStart )
|
|
{
|
|
SwPosition* pTmpPos = pTmp->End();
|
|
pTmpPos->nNode = rRg.aEnd;
|
|
pTmpPos->nContent.Assign(
|
|
pTmpPos->nNode.GetNode().GetCntntNode(), 0 );
|
|
}
|
|
}
|
|
else if( pRStt->nNode < rRg.aEnd )
|
|
{
|
|
rRedlTbl.Remove( nRedlPos-- );
|
|
if( pREnd->nNode < rRg.aEnd ||
|
|
( pREnd->nNode == rRg.aEnd && !pREnd->nContent.GetIndex()) )
|
|
{
|
|
// move everything
|
|
_SaveRedline* pSave = new _SaveRedline( pTmp, rRg.aStart );
|
|
rArr.C40_INSERT( _SaveRedline, pSave, rArr.Count() );
|
|
}
|
|
else
|
|
{
|
|
// split
|
|
SwRedline* pNewRedl = new SwRedline( *pTmp );
|
|
SwPosition* pTmpPos = pNewRedl->End();
|
|
pTmpPos->nNode = rRg.aEnd;
|
|
pTmpPos->nContent.Assign(
|
|
pTmpPos->nNode.GetNode().GetCntntNode(), 0 );
|
|
|
|
_SaveRedline* pSave = new _SaveRedline( pNewRedl, rRg.aStart );
|
|
rArr.C40_INSERT( _SaveRedline, pSave, rArr.Count() );
|
|
|
|
pTmpPos = pTmp->Start();
|
|
pTmpPos->nNode = rRg.aEnd;
|
|
pTmpPos->nContent.Assign(
|
|
pTmpPos->nNode.GetNode().GetCntntNode(), 0 );
|
|
pDoc->AppendRedline( pTmp, true );
|
|
}
|
|
}
|
|
else
|
|
break;
|
|
|
|
} while( ++nRedlPos < pDoc->GetRedlineTbl().Count() );
|
|
pDoc->SetRedlineMode_intern( eOld );
|
|
}
|
|
|
|
void lcl_RestoreRedlines( SwDoc* pDoc, sal_uInt32 nInsPos, _SaveRedlines& rArr )
|
|
{
|
|
RedlineMode_t eOld = pDoc->GetRedlineMode();
|
|
pDoc->SetRedlineMode_intern( (RedlineMode_t)(( eOld & ~nsRedlineMode_t::REDLINE_IGNORE) | nsRedlineMode_t::REDLINE_ON ));
|
|
|
|
for( sal_uInt16 n = 0; n < rArr.Count(); ++n )
|
|
{
|
|
_SaveRedline* pSave = rArr[ n ];
|
|
pSave->SetPos( nInsPos );
|
|
pDoc->AppendRedline( pSave->pRedl, true );
|
|
}
|
|
|
|
pDoc->SetRedlineMode_intern( eOld );
|
|
}
|
|
|
|
// #i59534: Redo of insertion of multiple text nodes runs into trouble
|
|
// because of unnecessary expanded redlines
|
|
// From now on this class saves the redline positions of all redlines which ends exact at the
|
|
// insert position (node _and_ content index)
|
|
_SaveRedlEndPosForRestore::_SaveRedlEndPosForRestore( const SwNodeIndex& rInsIdx, xub_StrLen nCnt )
|
|
: pSavArr( 0 ), pSavIdx( 0 ), nSavCntnt( nCnt )
|
|
{
|
|
SwNode& rNd = rInsIdx.GetNode();
|
|
SwDoc* pDest = rNd.GetDoc();
|
|
if( pDest->GetRedlineTbl().Count() )
|
|
{
|
|
sal_uInt16 nFndPos;
|
|
const SwPosition* pEnd;
|
|
SwPosition aSrcPos( rInsIdx, SwIndex( rNd.GetCntntNode(), nCnt ));
|
|
const SwRedline* pRedl = pDest->GetRedline( aSrcPos, &nFndPos );
|
|
while( nFndPos-- && *( pEnd = ( pRedl =
|
|
pDest->GetRedlineTbl()[ nFndPos ] )->End() ) == aSrcPos && *pRedl->Start() < aSrcPos )
|
|
{
|
|
if( !pSavArr )
|
|
{
|
|
pSavArr = new SvPtrarr( 2 );
|
|
pSavIdx = new SwNodeIndex( rInsIdx, -1 );
|
|
}
|
|
void* p = (void*)pEnd;
|
|
pSavArr->Insert( p, pSavArr->Count() );
|
|
}
|
|
}
|
|
}
|
|
|
|
_SaveRedlEndPosForRestore::~_SaveRedlEndPosForRestore()
|
|
{
|
|
delete pSavArr;
|
|
delete pSavIdx;
|
|
}
|
|
|
|
void _SaveRedlEndPosForRestore::_Restore()
|
|
{
|
|
(*pSavIdx)++;
|
|
SwCntntNode* pNode = pSavIdx->GetNode().GetCntntNode();
|
|
// If there's no content node at the remembered position, we will not restore the old position
|
|
// This may happen if a table (or section?) will be inserted.
|
|
if( pNode )
|
|
{
|
|
SwPosition aPos( *pSavIdx, SwIndex( pNode, nSavCntnt ));
|
|
for( sal_uInt16 n = pSavArr->Count(); n; )
|
|
*((SwPosition*)pSavArr->GetObject( --n )) = aPos;
|
|
}
|
|
}
|
|
|
|
// Delete a full Section of the NodeArray.
|
|
// The passed Node is located somewhere in the designated Section.
|
|
void SwDoc::DeleteSection( SwNode *pNode )
|
|
{
|
|
OSL_ENSURE( pNode, "Didn't pass a Node." );
|
|
SwStartNode* pSttNd = pNode->IsStartNode() ? (SwStartNode*)pNode
|
|
: pNode->StartOfSectionNode();
|
|
SwNodeIndex aSttIdx( *pSttNd ), aEndIdx( *pNode->EndOfSectionNode() );
|
|
|
|
// delete all Flys, Bookmarks, ...
|
|
DelFlyInRange( aSttIdx, aEndIdx );
|
|
DeleteRedline( *pSttNd, true, USHRT_MAX );
|
|
_DelBookmarks(aSttIdx, aEndIdx);
|
|
|
|
{
|
|
// move all Crsr/StkCrsr/UnoCrsr out of the to-be-deleted area
|
|
SwNodeIndex aMvStt( aSttIdx, 1 );
|
|
CorrAbs( aMvStt, aEndIdx, SwPosition( aSttIdx ), sal_True );
|
|
}
|
|
|
|
GetNodes().DelNodes( aSttIdx, aEndIdx.GetIndex() - aSttIdx.GetIndex() + 1 );
|
|
}
|
|
|
|
void SwDoc::SetModified(SwPaM &rPaM)
|
|
{
|
|
SwDataChanged aTmp( rPaM, 0 );
|
|
SetModified();
|
|
}
|
|
|
|
/*************************************************************************
|
|
* SwDoc::Overwrite()
|
|
************************************************************************/
|
|
bool SwDoc::Overwrite( const SwPaM &rRg, const String &rStr )
|
|
{
|
|
SwPosition& rPt = *(SwPosition*)rRg.GetPoint();
|
|
if( pACEWord ) // Add to AutoCorrect
|
|
{
|
|
if( 1 == rStr.Len() )
|
|
pACEWord->CheckChar( rPt, rStr.GetChar( 0 ) );
|
|
delete pACEWord, pACEWord = 0;
|
|
}
|
|
|
|
SwTxtNode *pNode = rPt.nNode.GetNode().GetTxtNode();
|
|
if(!pNode)
|
|
return sal_False;
|
|
|
|
if (GetIDocumentUndoRedo().DoesUndo())
|
|
{
|
|
GetIDocumentUndoRedo().ClearRedo(); // AppendUndo not always called
|
|
}
|
|
|
|
sal_uInt16 nOldAttrCnt = pNode->GetpSwpHints()
|
|
? pNode->GetpSwpHints()->Count() : 0;
|
|
SwDataChanged aTmp( rRg, 0 );
|
|
SwIndex& rIdx = rPt.nContent;
|
|
xub_StrLen nStart = 0;
|
|
|
|
sal_Unicode c;
|
|
String aStr;
|
|
|
|
sal_Bool bOldExpFlg = pNode->IsIgnoreDontExpand();
|
|
pNode->SetIgnoreDontExpand( sal_True );
|
|
|
|
for( xub_StrLen nCnt = 0; nCnt < rStr.Len(); ++nCnt )
|
|
{
|
|
// start behind the characters (to fix the attributes!)
|
|
nStart = rIdx.GetIndex();
|
|
if ( nStart < pNode->GetTxt().Len() )
|
|
{
|
|
lcl_SkipAttr( pNode, rIdx, nStart );
|
|
}
|
|
c = rStr.GetChar( nCnt );
|
|
if (GetIDocumentUndoRedo().DoesUndo())
|
|
{
|
|
bool bMerged(false);
|
|
if (GetIDocumentUndoRedo().DoesGroupUndo())
|
|
{
|
|
SwUndo *const pUndo = GetUndoManager().GetLastUndo();
|
|
SwUndoOverwrite *const pUndoOW(
|
|
dynamic_cast<SwUndoOverwrite *>(pUndo) );
|
|
if (pUndoOW)
|
|
{
|
|
// if CanGrouping() returns true it's already merged
|
|
bMerged = pUndoOW->CanGrouping( this, rPt, c );
|
|
}
|
|
}
|
|
if (!bMerged)
|
|
{
|
|
SwUndo *const pUndoOW( new SwUndoOverwrite(this, rPt, c) );
|
|
GetIDocumentUndoRedo().AppendUndo(pUndoOW);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// start behind the characters (to fix the attributes!)
|
|
if( nStart < pNode->GetTxt().Len() )
|
|
rIdx++;
|
|
pNode->InsertText( c, rIdx, INS_EMPTYEXPAND );
|
|
if( nStart+1 < rIdx.GetIndex() )
|
|
{
|
|
rIdx = nStart;
|
|
pNode->EraseText( rIdx, 1 );
|
|
rIdx++;
|
|
}
|
|
}
|
|
}
|
|
pNode->SetIgnoreDontExpand( bOldExpFlg );
|
|
|
|
sal_uInt16 nNewAttrCnt = pNode->GetpSwpHints()
|
|
? pNode->GetpSwpHints()->Count() : 0;
|
|
if( nOldAttrCnt != nNewAttrCnt )
|
|
{
|
|
SwUpdateAttr aHint( 0, 0, 0 );
|
|
pNode->ModifyBroadcast( 0, &aHint, TYPE( SwCrsrShell ) );
|
|
}
|
|
|
|
if (!GetIDocumentUndoRedo().DoesUndo() &&
|
|
!IsIgnoreRedline() && GetRedlineTbl().Count())
|
|
{
|
|
SwPaM aPam( rPt.nNode, nStart, rPt.nNode, rPt.nContent.GetIndex() );
|
|
DeleteRedline( aPam, true, USHRT_MAX );
|
|
}
|
|
else if( IsRedlineOn() )
|
|
{
|
|
// FIXME: this redline is WRONG: there is no DELETE, and the skipped
|
|
// characters are also included in aPam
|
|
SwPaM aPam( rPt.nNode, nStart, rPt.nNode, rPt.nContent.GetIndex() );
|
|
AppendRedline( new SwRedline( nsRedlineType_t::REDLINE_INSERT, aPam ), true);
|
|
}
|
|
|
|
SetModified();
|
|
return sal_True;
|
|
}
|
|
|
|
bool SwDoc::MoveAndJoin( SwPaM& rPaM, SwPosition& rPos, SwMoveFlags eMvFlags )
|
|
{
|
|
SwNodeIndex aIdx( rPaM.Start()->nNode );
|
|
sal_Bool bJoinTxt = aIdx.GetNode().IsTxtNode();
|
|
sal_Bool bOneNode = rPaM.GetPoint()->nNode == rPaM.GetMark()->nNode;
|
|
aIdx--; // in front of the move area!
|
|
|
|
bool bRet = MoveRange( rPaM, rPos, eMvFlags );
|
|
if( bRet && !bOneNode )
|
|
{
|
|
if( bJoinTxt )
|
|
aIdx++;
|
|
SwTxtNode * pTxtNd = aIdx.GetNode().GetTxtNode();
|
|
SwNodeIndex aNxtIdx( aIdx );
|
|
if( pTxtNd && pTxtNd->CanJoinNext( &aNxtIdx ) )
|
|
{
|
|
{ // Block so SwIndex into node is deleted before Join
|
|
CorrRel( aNxtIdx, SwPosition( aIdx, SwIndex( pTxtNd,
|
|
pTxtNd->GetTxt().Len() ) ), 0, sal_True );
|
|
}
|
|
pTxtNd->JoinNext();
|
|
}
|
|
}
|
|
return bRet;
|
|
}
|
|
|
|
// It seems that this is mostly used by SwDoc internals; the only
|
|
// way to call this from the outside seems to be the special case in
|
|
// SwDoc::CopyRange (but I have not managed to actually hit that case).
|
|
bool SwDoc::MoveRange( SwPaM& rPaM, SwPosition& rPos, SwMoveFlags eMvFlags )
|
|
{
|
|
// nothing moved: return
|
|
const SwPosition *pStt = rPaM.Start(), *pEnd = rPaM.End();
|
|
if( !rPaM.HasMark() || *pStt >= *pEnd || (*pStt <= rPos && rPos < *pEnd))
|
|
return false;
|
|
|
|
// Save the paragraph anchored Flys, so that they can be moved.
|
|
_SaveFlyArr aSaveFlyArr;
|
|
_SaveFlyInRange( rPaM, rPos.nNode, aSaveFlyArr, 0 != ( DOC_MOVEALLFLYS & eMvFlags ) );
|
|
|
|
// save redlines (if DOC_MOVEREDLINES is used)
|
|
_SaveRedlines aSaveRedl( 0 );
|
|
if( DOC_MOVEREDLINES & eMvFlags && GetRedlineTbl().Count() )
|
|
{
|
|
lcl_SaveRedlines( rPaM, aSaveRedl );
|
|
|
|
// #i17764# unfortunately, code below relies on undos being
|
|
// in a particular order, and presence of bookmarks
|
|
// will change this order. Hence, we delete bookmarks
|
|
// here without undo.
|
|
::sw::UndoGuard const undoGuard(GetIDocumentUndoRedo());
|
|
_DelBookmarks(
|
|
pStt->nNode,
|
|
pEnd->nNode,
|
|
NULL,
|
|
&pStt->nContent,
|
|
&pEnd->nContent);
|
|
}
|
|
|
|
|
|
int bUpdateFtn = sal_False;
|
|
SwFtnIdxs aTmpFntIdx;
|
|
|
|
SwUndoMove * pUndoMove = 0;
|
|
if (GetIDocumentUndoRedo().DoesUndo())
|
|
{
|
|
GetIDocumentUndoRedo().ClearRedo();
|
|
pUndoMove = new SwUndoMove( rPaM, rPos );
|
|
pUndoMove->SetMoveRedlines( eMvFlags == DOC_MOVEREDLINES );
|
|
}
|
|
else
|
|
{
|
|
bUpdateFtn = lcl_SaveFtn( pStt->nNode, pEnd->nNode, rPos.nNode,
|
|
GetFtnIdxs(), aTmpFntIdx,
|
|
&pStt->nContent, &pEnd->nContent );
|
|
}
|
|
|
|
sal_Bool bSplit = sal_False;
|
|
SwPaM aSavePam( rPos, rPos );
|
|
|
|
// Move the SPoint to the beginning of the range
|
|
if( rPaM.GetPoint() == pEnd )
|
|
rPaM.Exchange();
|
|
|
|
// If there is a TextNode before and after the Move, create a JoinNext in the EditShell.
|
|
SwTxtNode* pSrcNd = rPaM.GetPoint()->nNode.GetNode().GetTxtNode();
|
|
sal_Bool bCorrSavePam = pSrcNd && pStt->nNode != pEnd->nNode;
|
|
|
|
// If one ore more TextNodes are moved, SwNodes::Move will do a SplitNode.
|
|
// However, this does not update the cursor. So we create a TextNode to keep
|
|
// updating the indices. After the Move the Node is optionally deleted.
|
|
SwTxtNode * pTNd = rPos.nNode.GetNode().GetTxtNode();
|
|
if( pTNd && rPaM.GetPoint()->nNode != rPaM.GetMark()->nNode &&
|
|
( rPos.nContent.GetIndex() || ( pTNd->Len() && bCorrSavePam )) )
|
|
{
|
|
bSplit = sal_True;
|
|
xub_StrLen nMkCntnt = rPaM.GetMark()->nContent.GetIndex();
|
|
|
|
std::vector<sal_uLong> aBkmkArr;
|
|
_SaveCntntIdx( this, rPos.nNode.GetIndex(), rPos.nContent.GetIndex(),
|
|
aBkmkArr, SAVEFLY_SPLIT );
|
|
|
|
pTNd = static_cast<SwTxtNode*>(pTNd->SplitCntntNode( rPos ));
|
|
|
|
if( !aBkmkArr.empty() )
|
|
_RestoreCntntIdx( this, aBkmkArr, rPos.nNode.GetIndex()-1, 0, sal_True );
|
|
|
|
// correct the PaM!
|
|
if( rPos.nNode == rPaM.GetMark()->nNode )
|
|
{
|
|
rPaM.GetMark()->nNode = rPos.nNode.GetIndex()-1;
|
|
rPaM.GetMark()->nContent.Assign( pTNd, nMkCntnt );
|
|
}
|
|
}
|
|
|
|
// Put back the Pam by one "content"; so that it's always outside of
|
|
// the manipulated range.
|
|
// If there's no content anymore, set it to the StartNode (that's
|
|
// always there).
|
|
sal_Bool bNullCntnt = !aSavePam.Move( fnMoveBackward, fnGoCntnt );
|
|
if( bNullCntnt )
|
|
{
|
|
aSavePam.GetPoint()->nNode--;
|
|
}
|
|
|
|
// Copy all Bookmarks that are within the Move range into an array,
|
|
// that saves the positon as an offset.
|
|
::std::vector< ::sw::mark::SaveBookmark> aSaveBkmks;
|
|
_DelBookmarks(
|
|
pStt->nNode,
|
|
pEnd->nNode,
|
|
&aSaveBkmks,
|
|
&pStt->nContent,
|
|
&pEnd->nContent);
|
|
|
|
// If there is no range anymore due to the above deletions (e.g. the
|
|
// footnotes got deleted), it's still a valid Move!
|
|
if( *rPaM.GetPoint() != *rPaM.GetMark() )
|
|
{
|
|
// now do the actual move
|
|
GetNodes().MoveRange( rPaM, rPos, GetNodes() );
|
|
|
|
// after a MoveRange() the Mark is deleted
|
|
if ( rPaM.HasMark() ) // => no Move occurred!
|
|
{
|
|
delete pUndoMove;
|
|
return false;
|
|
}
|
|
}
|
|
else
|
|
rPaM.DeleteMark();
|
|
|
|
OSL_ENSURE( *aSavePam.GetMark() == rPos ||
|
|
( aSavePam.GetMark()->nNode.GetNode().GetCntntNode() == NULL ),
|
|
"PaM was not moved. Aren't there ContentNodes at the beginning/end?" );
|
|
*aSavePam.GetMark() = rPos;
|
|
|
|
rPaM.SetMark(); // create a Sel. around the new range
|
|
pTNd = aSavePam.GetNode()->GetTxtNode();
|
|
if (GetIDocumentUndoRedo().DoesUndo())
|
|
{
|
|
// correct the SavePam's Content first
|
|
if( bNullCntnt )
|
|
{
|
|
aSavePam.GetPoint()->nContent = 0;
|
|
}
|
|
|
|
// The method SwEditShell::Move() merges the TextNode after the Move,
|
|
// where the rPaM is located.
|
|
// If the Content was moved to the back and the SavePam's SPoint is
|
|
// in the next Node, we have to deal with this when saving the Undo object!
|
|
SwTxtNode * pPamTxtNd = 0;
|
|
|
|
// Is passed to SwUndoMove, which happens when subsequently calling Undo JoinNext.
|
|
// If it's not possible to call Undo JoinNext here.
|
|
sal_Bool bJoin = bSplit && pTNd;
|
|
bCorrSavePam = bCorrSavePam &&
|
|
0 != ( pPamTxtNd = rPaM.GetNode()->GetTxtNode() )
|
|
&& pPamTxtNd->CanJoinNext()
|
|
&& (*rPaM.GetPoint() <= *aSavePam.GetPoint());
|
|
|
|
// Do two Nodes have to be joined at the SavePam?
|
|
if( bJoin && pTNd->CanJoinNext() )
|
|
{
|
|
pTNd->JoinNext();
|
|
// No temporary Index when using &&.
|
|
// We probably only want to compare the indices.
|
|
if( bCorrSavePam && rPaM.GetPoint()->nNode.GetIndex()+1 ==
|
|
aSavePam.GetPoint()->nNode.GetIndex() )
|
|
{
|
|
aSavePam.GetPoint()->nContent += pPamTxtNd->Len();
|
|
}
|
|
bJoin = sal_False;
|
|
}
|
|
else if ( !aSavePam.Move( fnMoveForward, fnGoCntnt ) )
|
|
{
|
|
aSavePam.GetPoint()->nNode++;
|
|
}
|
|
|
|
// The newly inserted range is now inbetween SPoint and GetMark.
|
|
pUndoMove->SetDestRange( aSavePam, *rPaM.GetPoint(),
|
|
bJoin, bCorrSavePam );
|
|
GetIDocumentUndoRedo().AppendUndo( pUndoMove );
|
|
}
|
|
else
|
|
{
|
|
bool bRemove = true;
|
|
// Do two Nodes have to be joined at the SavePam?
|
|
if( bSplit && pTNd )
|
|
{
|
|
if( pTNd->CanJoinNext())
|
|
{
|
|
// Always join next, because <pTNd> has to stay as it is.
|
|
// A join previous from its next would more or less delete <pTNd>
|
|
pTNd->JoinNext();
|
|
bRemove = false;
|
|
}
|
|
}
|
|
if( bNullCntnt )
|
|
{
|
|
aSavePam.GetPoint()->nNode++;
|
|
aSavePam.GetPoint()->nContent.Assign( aSavePam.GetCntntNode(), 0 );
|
|
}
|
|
else if( bRemove ) // No move forward after joining with next paragraph
|
|
{
|
|
aSavePam.Move( fnMoveForward, fnGoCntnt );
|
|
}
|
|
}
|
|
|
|
// Insert the Bookmarks back into the Document.
|
|
*rPaM.GetMark() = *aSavePam.Start();
|
|
for(
|
|
::std::vector< ::sw::mark::SaveBookmark>::iterator pBkmk = aSaveBkmks.begin();
|
|
pBkmk != aSaveBkmks.end();
|
|
++pBkmk)
|
|
pBkmk->SetInDoc(
|
|
this,
|
|
rPaM.GetMark()->nNode,
|
|
&rPaM.GetMark()->nContent);
|
|
*rPaM.GetPoint() = *aSavePam.End();
|
|
|
|
// Move the Flys to the new position.
|
|
_RestFlyInRange( aSaveFlyArr, rPaM.Start()->nNode, &(rPos.nNode) );
|
|
|
|
// restore redlines (if DOC_MOVEREDLINES is used)
|
|
if( aSaveRedl.Count() )
|
|
{
|
|
lcl_RestoreRedlines( this, *aSavePam.Start(), aSaveRedl );
|
|
}
|
|
|
|
if( bUpdateFtn )
|
|
{
|
|
if( aTmpFntIdx.Count() )
|
|
{
|
|
GetFtnIdxs().Insert( &aTmpFntIdx );
|
|
aTmpFntIdx.Remove( sal_uInt16( 0 ), aTmpFntIdx.Count() );
|
|
}
|
|
|
|
GetFtnIdxs().UpdateAllFtn();
|
|
}
|
|
|
|
SetModified();
|
|
return true;
|
|
}
|
|
|
|
bool SwDoc::MoveNodeRange( SwNodeRange& rRange, SwNodeIndex& rPos,
|
|
SwMoveFlags eMvFlags )
|
|
{
|
|
// Moves all Nodes to the new position.
|
|
// Bookmarks are moved too (currently without Undo support).
|
|
|
|
// If footnotes are being moved to the special section, remove them now.
|
|
//
|
|
// Or else delete the Frames for all footnotes that are being moved
|
|
// and have it rebuild after the Move (footnotes can change pages).
|
|
// Additionally we have to correct the FtnIdx array's sorting.
|
|
int bUpdateFtn = sal_False;
|
|
SwFtnIdxs aTmpFntIdx;
|
|
|
|
SwUndoMove* pUndo = 0;
|
|
if ((DOC_CREATEUNDOOBJ & eMvFlags ) && GetIDocumentUndoRedo().DoesUndo())
|
|
{
|
|
pUndo = new SwUndoMove( this, rRange, rPos );
|
|
}
|
|
else
|
|
{
|
|
bUpdateFtn = lcl_SaveFtn( rRange.aStart, rRange.aEnd, rPos,
|
|
GetFtnIdxs(), aTmpFntIdx );
|
|
}
|
|
|
|
_SaveRedlines aSaveRedl( 0 );
|
|
SvPtrarr aSavRedlInsPosArr( 0 );
|
|
if( DOC_MOVEREDLINES & eMvFlags && GetRedlineTbl().Count() )
|
|
{
|
|
lcl_SaveRedlines( rRange, aSaveRedl );
|
|
|
|
// Find all RedLines that end at the InsPos.
|
|
// These have to be moved back to the "old" position after the Move.
|
|
sal_uInt16 nRedlPos = GetRedlinePos( rPos.GetNode(), USHRT_MAX );
|
|
if( USHRT_MAX != nRedlPos )
|
|
{
|
|
const SwPosition *pRStt, *pREnd;
|
|
do {
|
|
SwRedline* pTmp = GetRedlineTbl()[ nRedlPos ];
|
|
pRStt = pTmp->Start();
|
|
pREnd = pTmp->End();
|
|
if( pREnd->nNode == rPos && pRStt->nNode < rPos )
|
|
{
|
|
void* p = pTmp;
|
|
aSavRedlInsPosArr.Insert( p, aSavRedlInsPosArr.Count() );
|
|
}
|
|
} while( pRStt->nNode < rPos && ++nRedlPos < GetRedlineTbl().Count());
|
|
}
|
|
}
|
|
|
|
// Copy all Bookmarks that are within the Move range into an array
|
|
// that stores all references to positions as an offset.
|
|
// The final mapping happens after the Move.
|
|
::std::vector< ::sw::mark::SaveBookmark> aSaveBkmks;
|
|
_DelBookmarks(rRange.aStart, rRange.aEnd, &aSaveBkmks);
|
|
|
|
// Save the paragraph-bound Flys, so that they can be moved.
|
|
_SaveFlyArr aSaveFlyArr;
|
|
if( GetSpzFrmFmts()->Count() )
|
|
_SaveFlyInRange( rRange, aSaveFlyArr );
|
|
|
|
// Set it to before the Position, so that it cannot be moved further.
|
|
SwNodeIndex aIdx( rPos, -1 );
|
|
|
|
SwNodeIndex* pSaveInsPos = 0;
|
|
if( pUndo )
|
|
pSaveInsPos = new SwNodeIndex( rRange.aStart, -1 );
|
|
|
|
// move the Nodes
|
|
sal_Bool bNoDelFrms = 0 != (DOC_NO_DELFRMS & eMvFlags);
|
|
if( GetNodes()._MoveNodes( rRange, GetNodes(), rPos, !bNoDelFrms ) )
|
|
{
|
|
aIdx++; // again back to old position
|
|
if( pSaveInsPos )
|
|
(*pSaveInsPos)++;
|
|
}
|
|
else
|
|
{
|
|
aIdx = rRange.aStart;
|
|
delete pUndo, pUndo = 0;
|
|
}
|
|
|
|
// move the Flys to the new position
|
|
if( aSaveFlyArr.Count() )
|
|
_RestFlyInRange( aSaveFlyArr, aIdx, NULL );
|
|
|
|
// Add the Bookmarks back to the Document
|
|
for(
|
|
::std::vector< ::sw::mark::SaveBookmark>::iterator pBkmk = aSaveBkmks.begin();
|
|
pBkmk != aSaveBkmks.end();
|
|
++pBkmk)
|
|
pBkmk->SetInDoc(this, aIdx);
|
|
|
|
if( aSavRedlInsPosArr.Count() )
|
|
{
|
|
SwNode* pNewNd = &aIdx.GetNode();
|
|
for( sal_uInt16 n = 0; n < aSavRedlInsPosArr.Count(); ++n )
|
|
{
|
|
SwRedline* pTmp = (SwRedline*)aSavRedlInsPosArr[ n ];
|
|
if( USHRT_MAX != GetRedlineTbl().GetPos( pTmp ) )
|
|
{
|
|
SwPosition* pEnd = pTmp->End();
|
|
pEnd->nNode = aIdx;
|
|
pEnd->nContent.Assign( pNewNd->GetCntntNode(), 0 );
|
|
}
|
|
}
|
|
}
|
|
|
|
if( aSaveRedl.Count() )
|
|
lcl_RestoreRedlines( this, aIdx.GetIndex(), aSaveRedl );
|
|
|
|
if( pUndo )
|
|
{
|
|
pUndo->SetDestRange( aIdx, rPos, *pSaveInsPos );
|
|
GetIDocumentUndoRedo().AppendUndo(pUndo);
|
|
}
|
|
|
|
delete pSaveInsPos;
|
|
|
|
if( bUpdateFtn )
|
|
{
|
|
if( aTmpFntIdx.Count() )
|
|
{
|
|
GetFtnIdxs().Insert( &aTmpFntIdx );
|
|
aTmpFntIdx.Remove( sal_uInt16( 0 ), aTmpFntIdx.Count() );
|
|
}
|
|
|
|
GetFtnIdxs().UpdateAllFtn();
|
|
}
|
|
|
|
SetModified();
|
|
return sal_True;
|
|
}
|
|
|
|
// Convert list of ranges of whichIds to a corresponding list of whichIds
|
|
std::vector<sal_uInt16> * lcl_RangesToVector(sal_uInt16 * pRanges)
|
|
{
|
|
std::vector<sal_uInt16> * pResult = new std::vector<sal_uInt16>();
|
|
|
|
int i = 0;
|
|
while (pRanges[i] != 0)
|
|
{
|
|
OSL_ENSURE(pRanges[i+1] != 0, "malformed ranges");
|
|
|
|
for (sal_uInt16 j = pRanges[i]; j < pRanges[i+1]; j++)
|
|
pResult->push_back(j);
|
|
|
|
i += 2;
|
|
}
|
|
|
|
return pResult;
|
|
}
|
|
|
|
bool lcl_StrLenOverFlow( const SwPaM& rPam )
|
|
{
|
|
// If we try to merge two paragraph we have to test if afterwards
|
|
// the string doesn't exceed the allowed string length
|
|
bool bRet = false;
|
|
if( rPam.GetPoint()->nNode != rPam.GetMark()->nNode )
|
|
{
|
|
const SwPosition* pStt = rPam.Start(), *pEnd = rPam.End();
|
|
const SwTxtNode* pEndNd = pEnd->nNode.GetNode().GetTxtNode();
|
|
if( (0 != pEndNd) && pStt->nNode.GetNode().IsTxtNode() )
|
|
{
|
|
sal_uInt64 nSum = pStt->nContent.GetIndex() +
|
|
pEndNd->GetTxt().Len() - pEnd->nContent.GetIndex();
|
|
if( nSum > STRING_LEN )
|
|
bRet = true;
|
|
}
|
|
}
|
|
return bRet;
|
|
}
|
|
|
|
void lcl_GetJoinFlags( SwPaM& rPam, sal_Bool& rJoinTxt, sal_Bool& rJoinPrev )
|
|
{
|
|
rJoinTxt = sal_False;
|
|
rJoinPrev = sal_False;
|
|
if( rPam.GetPoint()->nNode != rPam.GetMark()->nNode )
|
|
{
|
|
const SwPosition* pStt = rPam.Start(), *pEnd = rPam.End();
|
|
SwTxtNode *pSttNd = pStt->nNode.GetNode().GetTxtNode();
|
|
if( pSttNd )
|
|
{
|
|
SwTxtNode *pEndNd = pEnd->nNode.GetNode().GetTxtNode();
|
|
rJoinTxt = 0 != pEndNd;
|
|
if( rJoinTxt )
|
|
{
|
|
bool bExchange = pStt == rPam.GetPoint();
|
|
if( !pStt->nContent.GetIndex() &&
|
|
pEndNd->GetTxt().Len() != pEnd->nContent.GetIndex() )
|
|
bExchange = !bExchange;
|
|
if( bExchange )
|
|
rPam.Exchange();
|
|
rJoinPrev = rPam.GetPoint() == pStt;
|
|
OSL_ENSURE( !pStt->nContent.GetIndex() &&
|
|
pEndNd->GetTxt().Len() != pEnd->nContent.GetIndex()
|
|
? rPam.GetPoint()->nNode < rPam.GetMark()->nNode
|
|
: rPam.GetPoint()->nNode > rPam.GetMark()->nNode,
|
|
"lcl_GetJoinFlags");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void lcl_JoinText( SwPaM& rPam, sal_Bool bJoinPrev )
|
|
{
|
|
SwNodeIndex aIdx( rPam.GetPoint()->nNode );
|
|
SwTxtNode *pTxtNd = aIdx.GetNode().GetTxtNode();
|
|
SwNodeIndex aOldIdx( aIdx );
|
|
SwTxtNode *pOldTxtNd = pTxtNd;
|
|
|
|
if( pTxtNd && pTxtNd->CanJoinNext( &aIdx ) )
|
|
{
|
|
SwDoc* pDoc = rPam.GetDoc();
|
|
if( bJoinPrev )
|
|
{
|
|
// We do not need to handle xmlids in this case, because
|
|
// it is only invoked if one paragraph is completely empty
|
|
// (see lcl_GetJoinFlags)
|
|
{
|
|
// If PageBreaks are deleted/set, it must not be added to the Undo history!
|
|
// Also, deleteing the Node is not added to the Undo histroy!
|
|
::sw::UndoGuard const undoGuard(pDoc->GetIDocumentUndoRedo());
|
|
|
|
/* PageBreaks, PageDesc, ColumnBreaks */
|
|
// If we need to change something about the logic to copy the PageBreaks,
|
|
// PageDesc, etc. we also have to change SwUndoDelete.
|
|
// There, we copy the AUTO PageBreak from the GetMarkNode!
|
|
|
|
/* The GetMarkNode */
|
|
if( ( pTxtNd = aIdx.GetNode().GetTxtNode())->HasSwAttrSet() )
|
|
{
|
|
const SfxPoolItem* pItem;
|
|
if( SFX_ITEM_SET == pTxtNd->GetpSwAttrSet()->GetItemState(
|
|
RES_BREAK, sal_False, &pItem ) )
|
|
pTxtNd->ResetAttr( RES_BREAK );
|
|
if( pTxtNd->HasSwAttrSet() &&
|
|
SFX_ITEM_SET == pTxtNd->GetpSwAttrSet()->GetItemState(
|
|
RES_PAGEDESC, sal_False, &pItem ) )
|
|
pTxtNd->ResetAttr( RES_PAGEDESC );
|
|
}
|
|
|
|
/* The PointNode */
|
|
if( pOldTxtNd->HasSwAttrSet() )
|
|
{
|
|
const SfxPoolItem* pItem;
|
|
SfxItemSet aSet( pDoc->GetAttrPool(), aBreakSetRange );
|
|
const SfxItemSet* pSet = pOldTxtNd->GetpSwAttrSet();
|
|
if( SFX_ITEM_SET == pSet->GetItemState( RES_BREAK,
|
|
sal_False, &pItem ) )
|
|
aSet.Put( *pItem );
|
|
if( SFX_ITEM_SET == pSet->GetItemState( RES_PAGEDESC,
|
|
sal_False, &pItem ) )
|
|
aSet.Put( *pItem );
|
|
if( aSet.Count() )
|
|
pTxtNd->SetAttr( aSet );
|
|
}
|
|
pOldTxtNd->FmtToTxtAttr( pTxtNd );
|
|
|
|
std::vector<sal_uLong> aBkmkArr;
|
|
::_SaveCntntIdx( pDoc, aOldIdx.GetIndex(),
|
|
pOldTxtNd->Len(), aBkmkArr );
|
|
|
|
SwIndex aAlphaIdx(pTxtNd);
|
|
pOldTxtNd->CutText( pTxtNd, aAlphaIdx, SwIndex(pOldTxtNd),
|
|
pOldTxtNd->Len() );
|
|
SwPosition aAlphaPos( aIdx, aAlphaIdx );
|
|
pDoc->CorrRel( rPam.GetPoint()->nNode, aAlphaPos, 0, sal_True );
|
|
|
|
// move all Bookmarks/TOXMarks
|
|
if( !aBkmkArr.empty() )
|
|
::_RestoreCntntIdx( pDoc, aBkmkArr, aIdx.GetIndex() );
|
|
|
|
// If the passed PaM is not in the Crsr ring,
|
|
// treat it seperately (e.g. when it's being called from AutoFormat)
|
|
if( pOldTxtNd == rPam.GetBound( sal_True ).nContent.GetIdxReg() )
|
|
rPam.GetBound( sal_True ) = aAlphaPos;
|
|
if( pOldTxtNd == rPam.GetBound( sal_False ).nContent.GetIdxReg() )
|
|
rPam.GetBound( sal_False ) = aAlphaPos;
|
|
}
|
|
// delete the Node, at last!
|
|
pDoc->GetNodes().Delete( aOldIdx, 1 );
|
|
}
|
|
else
|
|
{
|
|
SwTxtNode* pDelNd = aIdx.GetNode().GetTxtNode();
|
|
if( pTxtNd->Len() )
|
|
pDelNd->FmtToTxtAttr( pTxtNd );
|
|
else
|
|
{
|
|
/* This case was missed:
|
|
|
|
<something></something> <-- pTxtNd
|
|
<other>ccc</other> <-- pDelNd
|
|
|
|
<something> and <other> are paragraph
|
|
attributes. The attribute <something> stayed if not
|
|
overwritten by an attribute in "ccc". Fixed by
|
|
first resetting all character attributes in first
|
|
paragraph (pTxtNd).
|
|
*/
|
|
std::vector<sal_uInt16> * pShorts =
|
|
lcl_RangesToVector(aCharFmtSetRange);
|
|
pTxtNd->ResetAttr(*pShorts);
|
|
delete pShorts;
|
|
|
|
if( pDelNd->HasSwAttrSet() )
|
|
{
|
|
// only copy the character attributes
|
|
SfxItemSet aTmpSet( pDoc->GetAttrPool(), aCharFmtSetRange );
|
|
aTmpSet.Put( *pDelNd->GetpSwAttrSet() );
|
|
pTxtNd->SetAttr( aTmpSet );
|
|
}
|
|
}
|
|
|
|
pDoc->CorrRel( aIdx, *rPam.GetPoint(), 0, sal_True );
|
|
// #i100466# adjust given <rPam>, if it does not belong to the cursors
|
|
if ( pDelNd == rPam.GetBound( sal_True ).nContent.GetIdxReg() )
|
|
{
|
|
rPam.GetBound( sal_True ) = SwPosition( SwNodeIndex( *pTxtNd ), SwIndex( pTxtNd ) );
|
|
}
|
|
if( pDelNd == rPam.GetBound( sal_False ).nContent.GetIdxReg() )
|
|
{
|
|
rPam.GetBound( sal_False ) = SwPosition( SwNodeIndex( *pTxtNd ), SwIndex( pTxtNd ) );
|
|
}
|
|
pTxtNd->JoinNext();
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
lcl_CalcBreaks( ::std::vector<xub_StrLen> & rBreaks, SwPaM const & rPam )
|
|
{
|
|
SwTxtNode const * const pTxtNode(
|
|
rPam.End()->nNode.GetNode().GetTxtNode() );
|
|
if (!pTxtNode)
|
|
return; // left-overlap only possible at end of selection...
|
|
|
|
const xub_StrLen nStart(rPam.Start()->nContent.GetIndex());
|
|
const xub_StrLen nEnd (rPam.End ()->nContent.GetIndex());
|
|
if (nEnd == pTxtNode->Len())
|
|
return; // paragraph selected until the end
|
|
|
|
for (xub_StrLen i = nStart; i < nEnd; ++i)
|
|
{
|
|
const sal_Unicode c(pTxtNode->GetTxt().GetChar(i));
|
|
if ((CH_TXTATR_INWORD == c) || (CH_TXTATR_BREAKWORD == c))
|
|
{
|
|
SwTxtAttr const * const pAttr( pTxtNode->GetTxtAttrForCharAt(i) );
|
|
if (pAttr && pAttr->GetEnd() && (*pAttr->GetEnd() > nEnd))
|
|
{
|
|
OSL_ENSURE(pAttr->HasDummyChar(), "GetTxtAttrForCharAt broken?");
|
|
rBreaks.push_back(i);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
bool lcl_DoWithBreaks(SwDoc & rDoc, SwPaM & rPam,
|
|
bool (SwDoc::*pFunc)(SwPaM&, bool), const bool bForceJoinNext = false)
|
|
{
|
|
::std::vector<xub_StrLen> Breaks;
|
|
|
|
lcl_CalcBreaks(Breaks, rPam);
|
|
|
|
if (!Breaks.size())
|
|
{
|
|
return (rDoc.*pFunc)(rPam, bForceJoinNext);
|
|
}
|
|
|
|
// Deletion must be split into several parts if the text node
|
|
// contains a text attribute with end and with dummy character
|
|
// and the selection does not contain the text attribute completely,
|
|
// but overlaps its start (left), where the dummy character is.
|
|
|
|
SwPosition const & rSelectionEnd( *rPam.End() );
|
|
|
|
bool bRet( true );
|
|
// iterate from end to start, to avoid invalidating the offsets!
|
|
::std::vector<xub_StrLen>::reverse_iterator iter( Breaks.rbegin() );
|
|
SwPaM aPam( rSelectionEnd, rSelectionEnd ); // end node!
|
|
SwPosition & rEnd( *aPam.End() );
|
|
SwPosition & rStart( *aPam.Start() );
|
|
|
|
while (iter != Breaks.rend())
|
|
{
|
|
rStart.nContent = *iter + 1;
|
|
if (rEnd.nContent > rStart.nContent) // check if part is empty
|
|
{
|
|
bRet &= (rDoc.*pFunc)(aPam, bForceJoinNext);
|
|
}
|
|
rEnd.nContent = *iter;
|
|
++iter;
|
|
}
|
|
|
|
rStart = *rPam.Start(); // set to original start
|
|
if (rEnd.nContent > rStart.nContent) // check if part is empty
|
|
{
|
|
bRet &= (rDoc.*pFunc)(aPam, bForceJoinNext);
|
|
}
|
|
|
|
return bRet;
|
|
}
|
|
|
|
bool SwDoc::DeleteAndJoinWithRedlineImpl( SwPaM & rPam, const bool )
|
|
{
|
|
OSL_ENSURE( IsRedlineOn(), "DeleteAndJoinWithRedline: redline off" );
|
|
|
|
{
|
|
SwUndoRedlineDelete* pUndo = 0;
|
|
RedlineMode_t eOld = GetRedlineMode();
|
|
checkRedlining(eOld);
|
|
if (GetIDocumentUndoRedo().DoesUndo())
|
|
{
|
|
|
|
/* please don't translate -- for cultural reasons this comment is protected
|
|
until the redline implementation is finally fixed some day */
|
|
//JP 06.01.98: MUSS noch optimiert werden!!!
|
|
SetRedlineMode(
|
|
(RedlineMode_t)(nsRedlineMode_t::REDLINE_ON | nsRedlineMode_t::REDLINE_SHOW_INSERT | nsRedlineMode_t::REDLINE_SHOW_DELETE ));
|
|
|
|
GetIDocumentUndoRedo().StartUndo(UNDO_EMPTY, NULL);
|
|
pUndo = new SwUndoRedlineDelete( rPam, UNDO_DELETE );
|
|
GetIDocumentUndoRedo().AppendUndo(pUndo);
|
|
}
|
|
if( *rPam.GetPoint() != *rPam.GetMark() )
|
|
AppendRedline( new SwRedline( nsRedlineType_t::REDLINE_DELETE, rPam ), true);
|
|
SetModified();
|
|
|
|
if( pUndo )
|
|
{
|
|
GetIDocumentUndoRedo().EndUndo(UNDO_EMPTY, NULL);
|
|
// ??? why the hell is the AppendUndo not below the
|
|
// CanGrouping, so this hideous cleanup wouldn't be necessary?
|
|
// bah, this is redlining, probably changing this would break it...
|
|
if (GetIDocumentUndoRedo().DoesGroupUndo())
|
|
{
|
|
SwUndo *const pLastUndo( GetUndoManager().GetLastUndo() );
|
|
SwUndoRedlineDelete *const pUndoRedlineDel(
|
|
dynamic_cast<SwUndoRedlineDelete*>(pLastUndo) );
|
|
if (pUndoRedlineDel)
|
|
{
|
|
bool const bMerged = pUndoRedlineDel->CanGrouping(*pUndo);
|
|
if (bMerged)
|
|
{
|
|
::sw::UndoGuard const undoGuard(GetIDocumentUndoRedo());
|
|
SwUndo const*const pDeleted =
|
|
GetUndoManager().RemoveLastUndo();
|
|
OSL_ENSURE(pDeleted == pUndo,
|
|
"DeleteAndJoinWithRedlineImpl: "
|
|
"undo removed is not undo inserted?");
|
|
delete pDeleted;
|
|
}
|
|
}
|
|
}
|
|
//JP 06.01.98: MUSS noch optimiert werden!!!
|
|
SetRedlineMode( eOld );
|
|
}
|
|
return true;
|
|
}
|
|
}
|
|
|
|
bool SwDoc::DeleteAndJoinImpl( SwPaM & rPam,
|
|
const bool bForceJoinNext )
|
|
{
|
|
sal_Bool bJoinTxt, bJoinPrev;
|
|
lcl_GetJoinFlags( rPam, bJoinTxt, bJoinPrev );
|
|
// #i100466#
|
|
if ( bForceJoinNext )
|
|
{
|
|
bJoinPrev = sal_False;
|
|
}
|
|
|
|
{
|
|
bool const bSuccess( DeleteRangeImpl( rPam ) );
|
|
if (!bSuccess)
|
|
return false;
|
|
}
|
|
|
|
if( bJoinTxt )
|
|
{
|
|
lcl_JoinText( rPam, bJoinPrev );
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool SwDoc::DeleteRangeImpl(SwPaM & rPam, const bool)
|
|
{
|
|
// Move all cursors out of the deleted range, but first copy the
|
|
// passed PaM, because it could be a cursor that would be moved!
|
|
SwPaM aDelPam( *rPam.GetMark(), *rPam.GetPoint() );
|
|
::PaMCorrAbs( aDelPam, *aDelPam.GetPoint() );
|
|
|
|
bool const bSuccess( DeleteRangeImplImpl( aDelPam ) );
|
|
if (bSuccess)
|
|
{ // now copy position from temp copy to given PaM
|
|
*rPam.GetPoint() = *aDelPam.GetPoint();
|
|
}
|
|
|
|
return bSuccess;
|
|
}
|
|
|
|
bool SwDoc::DeleteRangeImplImpl(SwPaM & rPam)
|
|
{
|
|
SwPosition *pStt = (SwPosition*)rPam.Start(), *pEnd = (SwPosition*)rPam.End();
|
|
|
|
if( !rPam.HasMark() || *pStt >= *pEnd )
|
|
return false;
|
|
|
|
if( pACEWord )
|
|
{
|
|
// if necessary the saved Word for the exception
|
|
if( pACEWord->IsDeleted() || pStt->nNode != pEnd->nNode ||
|
|
pStt->nContent.GetIndex() + 1 != pEnd->nContent.GetIndex() ||
|
|
!pACEWord->CheckDelChar( *pStt ))
|
|
delete pACEWord, pACEWord = 0;
|
|
}
|
|
|
|
{
|
|
// Delete all empty TextHints at the Mark's position
|
|
SwTxtNode* pTxtNd = rPam.GetMark()->nNode.GetNode().GetTxtNode();
|
|
SwpHints* pHts;
|
|
if( pTxtNd && 0 != ( pHts = pTxtNd->GetpSwpHints()) && pHts->Count() )
|
|
{
|
|
const xub_StrLen *pEndIdx;
|
|
xub_StrLen nMkCntPos = rPam.GetMark()->nContent.GetIndex();
|
|
for( sal_uInt16 n = pHts->Count(); n; )
|
|
{
|
|
const SwTxtAttr* pAttr = (*pHts)[ --n ];
|
|
if( nMkCntPos > *pAttr->GetStart() )
|
|
break;
|
|
|
|
if( nMkCntPos == *pAttr->GetStart() &&
|
|
0 != (pEndIdx = pAttr->GetEnd()) &&
|
|
*pEndIdx == *pAttr->GetStart() )
|
|
pTxtNd->DestroyAttr( pHts->Cut( n ) );
|
|
}
|
|
}
|
|
}
|
|
|
|
{
|
|
// Send DataChanged before deletion, so that we still know
|
|
// which objects are in the range.
|
|
// Afterwards they could be before/after the Position.
|
|
SwDataChanged aTmp( rPam, 0 );
|
|
}
|
|
|
|
|
|
if (GetIDocumentUndoRedo().DoesUndo())
|
|
{
|
|
GetIDocumentUndoRedo().ClearRedo();
|
|
bool bMerged(false);
|
|
if (GetIDocumentUndoRedo().DoesGroupUndo())
|
|
{
|
|
SwUndo *const pLastUndo( GetUndoManager().GetLastUndo() );
|
|
SwUndoDelete *const pUndoDelete(
|
|
dynamic_cast<SwUndoDelete *>(pLastUndo) );
|
|
if (pUndoDelete)
|
|
{
|
|
bMerged = pUndoDelete->CanGrouping( this, rPam );
|
|
// if CanGrouping() returns true it's already merged
|
|
}
|
|
}
|
|
if (!bMerged)
|
|
{
|
|
GetIDocumentUndoRedo().AppendUndo( new SwUndoDelete( rPam ) );
|
|
}
|
|
|
|
SetModified();
|
|
|
|
return true;
|
|
}
|
|
|
|
if( !IsIgnoreRedline() && GetRedlineTbl().Count() )
|
|
DeleteRedline( rPam, true, USHRT_MAX );
|
|
|
|
// Delete and move all "Flys at the paragraph", which are within the Selection
|
|
DelFlyInRange(rPam.GetMark()->nNode, rPam.GetPoint()->nNode);
|
|
_DelBookmarks(
|
|
pStt->nNode,
|
|
pEnd->nNode,
|
|
NULL,
|
|
&pStt->nContent,
|
|
&pEnd->nContent);
|
|
|
|
SwNodeIndex aSttIdx( pStt->nNode );
|
|
SwCntntNode * pCNd = aSttIdx.GetNode().GetCntntNode();
|
|
|
|
do { // middle checked loop!
|
|
if( pCNd )
|
|
{
|
|
SwTxtNode * pStartTxtNode( pCNd->GetTxtNode() );
|
|
if ( pStartTxtNode )
|
|
{
|
|
// now move the Content to the new Node
|
|
sal_Bool bOneNd = pStt->nNode == pEnd->nNode;
|
|
xub_StrLen nLen = ( bOneNd ? pEnd->nContent.GetIndex()
|
|
: pCNd->Len() )
|
|
- pStt->nContent.GetIndex();
|
|
|
|
// Don't call again, if already empty
|
|
if( nLen )
|
|
{
|
|
pStartTxtNode->EraseText( pStt->nContent, nLen );
|
|
|
|
if( !pStartTxtNode->Len() )
|
|
{
|
|
// METADATA: remove reference if empty (consider node deleted)
|
|
pStartTxtNode->RemoveMetadataReference();
|
|
}
|
|
}
|
|
|
|
if( bOneNd ) // that's it
|
|
break;
|
|
|
|
aSttIdx++;
|
|
}
|
|
else
|
|
{
|
|
// So that there are no indices left registered when deleted,
|
|
// we remove a SwPaM from the Content here.
|
|
pStt->nContent.Assign( 0, 0 );
|
|
}
|
|
}
|
|
|
|
pCNd = pEnd->nNode.GetNode().GetCntntNode();
|
|
if( pCNd )
|
|
{
|
|
SwTxtNode * pEndTxtNode( pCNd->GetTxtNode() );
|
|
if( pEndTxtNode )
|
|
{
|
|
// if already empty, don't call again
|
|
if( pEnd->nContent.GetIndex() )
|
|
{
|
|
SwIndex aIdx( pCNd, 0 );
|
|
pEndTxtNode->EraseText( aIdx, pEnd->nContent.GetIndex() );
|
|
|
|
if( !pEndTxtNode->Len() )
|
|
{
|
|
// METADATA: remove reference if empty (consider node deleted)
|
|
pEndTxtNode->RemoveMetadataReference();
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// So that there are no indices left registered when deleted,
|
|
// we remove a SwPaM from the Content here.
|
|
pEnd->nContent.Assign( 0, 0 );
|
|
}
|
|
}
|
|
|
|
// if the end is not a content node, delete it as well
|
|
sal_uInt32 nEnde = pEnd->nNode.GetIndex();
|
|
if( pCNd == NULL )
|
|
nEnde++;
|
|
|
|
if( aSttIdx != nEnde )
|
|
{
|
|
// delete the Nodes into the NodesArary
|
|
GetNodes().Delete( aSttIdx, nEnde - aSttIdx.GetIndex() );
|
|
}
|
|
|
|
// If the Node that contained the Cursor has been deleted,
|
|
// the Content has to be assigned to the current Content.
|
|
pStt->nContent.Assign( pStt->nNode.GetNode().GetCntntNode(),
|
|
pStt->nContent.GetIndex() );
|
|
|
|
// If we deleted across Node boundaries we have to correct the PaM,
|
|
// because they are in different Nodes now.
|
|
// Also, the Selection is revoked.
|
|
*pEnd = *pStt;
|
|
rPam.DeleteMark();
|
|
|
|
} while( sal_False );
|
|
|
|
if( !IsIgnoreRedline() && GetRedlineTbl().Count() )
|
|
CompressRedlines();
|
|
SetModified();
|
|
|
|
return true;
|
|
}
|
|
|
|
// #i100466# Add handling of new optional parameter <bForceJoinNext>
|
|
bool SwDoc::DeleteAndJoin( SwPaM & rPam,
|
|
const bool bForceJoinNext )
|
|
{
|
|
if ( lcl_StrLenOverFlow( rPam ) )
|
|
return false;
|
|
|
|
return lcl_DoWithBreaks( *this, rPam, (IsRedlineOn())
|
|
? &SwDoc::DeleteAndJoinWithRedlineImpl
|
|
: &SwDoc::DeleteAndJoinImpl,
|
|
bForceJoinNext );
|
|
}
|
|
|
|
bool SwDoc::DeleteRange( SwPaM & rPam )
|
|
{
|
|
return lcl_DoWithBreaks( *this, rPam, &SwDoc::DeleteRangeImpl );
|
|
}
|
|
|
|
|
|
void lcl_syncGrammarError( SwTxtNode &rTxtNode, linguistic2::ProofreadingResult& rResult,
|
|
xub_StrLen /*nBeginGrammarCheck*/, const ModelToViewHelper::ConversionMap* pConversionMap )
|
|
{
|
|
if( rTxtNode.IsGrammarCheckDirty() )
|
|
return;
|
|
SwGrammarMarkUp* pWrong = rTxtNode.GetGrammarCheck();
|
|
linguistic2::SingleProofreadingError* pArray = rResult.aErrors.getArray();
|
|
sal_uInt16 i, j = 0;
|
|
if( pWrong )
|
|
{
|
|
for( i = 0; i < rResult.aErrors.getLength(); ++i )
|
|
{
|
|
const linguistic2::SingleProofreadingError &rError = rResult.aErrors[i];
|
|
xub_StrLen nStart = (xub_StrLen)ModelToViewHelper::ConvertToModelPosition( pConversionMap, rError.nErrorStart ).mnPos;
|
|
xub_StrLen nEnd = (xub_StrLen)ModelToViewHelper::ConvertToModelPosition( pConversionMap, rError.nErrorStart + rError.nErrorLength ).mnPos;
|
|
if( i != j )
|
|
pArray[j] = pArray[i];
|
|
if( pWrong->LookForEntry( nStart, nEnd ) )
|
|
++j;
|
|
}
|
|
}
|
|
if( rResult.aErrors.getLength() > j )
|
|
rResult.aErrors.realloc( j );
|
|
}
|
|
|
|
uno::Any SwDoc::Spell( SwPaM& rPaM,
|
|
uno::Reference< XSpellChecker1 > &xSpeller,
|
|
sal_uInt16* pPageCnt, sal_uInt16* pPageSt,
|
|
bool bGrammarCheck,
|
|
SwConversionArgs *pConvArgs ) const
|
|
{
|
|
SwPosition* pSttPos = rPaM.Start(), *pEndPos = rPaM.End();
|
|
uno::Reference< beans::XPropertySet > xProp( ::GetLinguPropertySet() );
|
|
|
|
SwSpellArgs *pSpellArgs = 0;
|
|
if (pConvArgs)
|
|
{
|
|
pConvArgs->SetStart(pSttPos->nNode.GetNode().GetTxtNode(), pSttPos->nContent);
|
|
pConvArgs->SetEnd( pEndPos->nNode.GetNode().GetTxtNode(), pEndPos->nContent );
|
|
}
|
|
else
|
|
pSpellArgs = new SwSpellArgs( xSpeller,
|
|
pSttPos->nNode.GetNode().GetTxtNode(), pSttPos->nContent,
|
|
pEndPos->nNode.GetNode().GetTxtNode(), pEndPos->nContent,
|
|
bGrammarCheck );
|
|
|
|
sal_uLong nCurrNd = pSttPos->nNode.GetIndex();
|
|
sal_uLong nEndNd = pEndPos->nNode.GetIndex();
|
|
|
|
uno::Any aRet;
|
|
if( nCurrNd <= nEndNd )
|
|
{
|
|
SwCntntFrm* pCntFrm;
|
|
sal_Bool bGoOn = sal_True;
|
|
while( bGoOn )
|
|
{
|
|
SwNode* pNd = GetNodes()[ nCurrNd ];
|
|
switch( pNd->GetNodeType() )
|
|
{
|
|
case ND_TEXTNODE:
|
|
if( 0 != ( pCntFrm = ((SwTxtNode*)pNd)->getLayoutFrm( GetCurrentLayout() )) )
|
|
{
|
|
// skip protected and hidden Cells and Flys
|
|
if( pCntFrm->IsProtected() )
|
|
{
|
|
nCurrNd = pNd->EndOfSectionIndex();
|
|
}
|
|
else if( !((SwTxtFrm*)pCntFrm)->IsHiddenNow() )
|
|
{
|
|
if( pPageCnt && *pPageCnt && pPageSt )
|
|
{
|
|
sal_uInt16 nPageNr = pCntFrm->GetPhyPageNum();
|
|
if( !*pPageSt )
|
|
{
|
|
*pPageSt = nPageNr;
|
|
if( *pPageCnt < *pPageSt )
|
|
*pPageCnt = *pPageSt;
|
|
}
|
|
long nStat;
|
|
if( nPageNr >= *pPageSt )
|
|
nStat = nPageNr - *pPageSt + 1;
|
|
else
|
|
nStat = nPageNr + *pPageCnt - *pPageSt + 1;
|
|
::SetProgressState( nStat, (SwDocShell*)GetDocShell() );
|
|
}
|
|
//Spell() changes the pSpellArgs in case an error is found
|
|
xub_StrLen nBeginGrammarCheck = 0;
|
|
xub_StrLen nEndGrammarCheck = 0;
|
|
if( pSpellArgs && pSpellArgs->bIsGrammarCheck)
|
|
{
|
|
nBeginGrammarCheck = pSpellArgs->pStartNode == pNd ? pSpellArgs->pStartIdx->GetIndex() : 0;
|
|
// if grammar checking starts inside of a sentence the start position has to be adjusted
|
|
if( nBeginGrammarCheck )
|
|
{
|
|
SwIndex aStartIndex( dynamic_cast< SwTxtNode* >( pNd ), nBeginGrammarCheck );
|
|
SwPosition aStart( *pNd, aStartIndex );
|
|
SwCursor aCrsr(aStart, 0, false);
|
|
SwPosition aOrigPos = *aCrsr.GetPoint();
|
|
aCrsr.GoSentence( SwCursor::START_SENT );
|
|
if( aOrigPos != *aCrsr.GetPoint() )
|
|
{
|
|
nBeginGrammarCheck = aCrsr.GetPoint()->nContent.GetIndex();
|
|
}
|
|
}
|
|
nEndGrammarCheck = pSpellArgs->pEndNode == pNd ? pSpellArgs->pEndIdx->GetIndex() : ((SwTxtNode*)pNd)->GetTxt().Len();
|
|
}
|
|
|
|
xub_StrLen nSpellErrorPosition = ((SwTxtNode*)pNd)->GetTxt().Len();
|
|
if( (!pConvArgs &&
|
|
((SwTxtNode*)pNd)->Spell( pSpellArgs )) ||
|
|
( pConvArgs &&
|
|
((SwTxtNode*)pNd)->Convert( *pConvArgs )))
|
|
{
|
|
// Cancel and remember position
|
|
pSttPos->nNode = nCurrNd;
|
|
pEndPos->nNode = nCurrNd;
|
|
nCurrNd = nEndNd;
|
|
if( pSpellArgs )
|
|
nSpellErrorPosition = pSpellArgs->pStartIdx->GetIndex() > pSpellArgs->pEndIdx->GetIndex() ?
|
|
pSpellArgs->pEndIdx->GetIndex() :
|
|
pSpellArgs->pStartIdx->GetIndex();
|
|
}
|
|
|
|
|
|
if( pSpellArgs && pSpellArgs->bIsGrammarCheck )
|
|
{
|
|
uno::Reference< linguistic2::XProofreadingIterator > xGCIterator( GetGCIterator() );
|
|
if (xGCIterator.is())
|
|
{
|
|
String aText( ((SwTxtNode*)pNd)->GetTxt().Copy( nBeginGrammarCheck, nEndGrammarCheck - nBeginGrammarCheck ) );
|
|
uno::Reference< lang::XComponent > xDoc( ((SwDocShell*)GetDocShell())->GetBaseModel(), uno::UNO_QUERY );
|
|
// Expand the string:
|
|
rtl::OUString aExpandText;
|
|
const ModelToViewHelper::ConversionMap* pConversionMap =
|
|
((SwTxtNode*)pNd)->BuildConversionMap( aExpandText );
|
|
// get XFlatParagraph to use...
|
|
uno::Reference< text::XFlatParagraph > xFlatPara = new SwXFlatParagraph( *((SwTxtNode*)pNd), aExpandText, pConversionMap );
|
|
|
|
// get error position of cursor in XFlatParagraph
|
|
linguistic2::ProofreadingResult aResult;
|
|
sal_Int32 nGrammarErrors;
|
|
do
|
|
{
|
|
ModelToViewHelper::ConvertToViewPosition( pConversionMap, nBeginGrammarCheck );
|
|
aResult = xGCIterator->checkSentenceAtPosition(
|
|
xDoc, xFlatPara, aExpandText, lang::Locale(), nBeginGrammarCheck, -1, -1 );
|
|
|
|
lcl_syncGrammarError( *((SwTxtNode*)pNd), aResult, nBeginGrammarCheck, pConversionMap );
|
|
|
|
// get suggestions to use for the specific error position
|
|
nGrammarErrors = aResult.aErrors.getLength();
|
|
// if grammar checking doesn't have any progress then quit
|
|
if( aResult.nStartOfNextSentencePosition <= nBeginGrammarCheck )
|
|
break;
|
|
// prepare next iteration
|
|
nBeginGrammarCheck = (xub_StrLen)aResult.nStartOfNextSentencePosition;
|
|
}
|
|
while( nSpellErrorPosition > aResult.nBehindEndOfSentencePosition && !nGrammarErrors && aResult.nBehindEndOfSentencePosition < nEndGrammarCheck );
|
|
|
|
if( nGrammarErrors > 0 && nSpellErrorPosition >= aResult.nBehindEndOfSentencePosition )
|
|
{
|
|
aRet <<= aResult;
|
|
//put the cursor to the current error
|
|
const linguistic2::SingleProofreadingError &rError = aResult.aErrors[0];
|
|
nCurrNd = pNd->GetIndex();
|
|
pSttPos->nNode = nCurrNd;
|
|
pEndPos->nNode = nCurrNd;
|
|
pSpellArgs->pStartNode = ((SwTxtNode*)pNd);
|
|
pSpellArgs->pEndNode = ((SwTxtNode*)pNd);
|
|
pSpellArgs->pStartIdx->Assign(((SwTxtNode*)pNd), (xub_StrLen)ModelToViewHelper::ConvertToModelPosition( pConversionMap, rError.nErrorStart ).mnPos );
|
|
pSpellArgs->pEndIdx->Assign(((SwTxtNode*)pNd), (xub_StrLen)ModelToViewHelper::ConvertToModelPosition( pConversionMap, rError.nErrorStart + rError.nErrorLength ).mnPos );
|
|
nCurrNd = nEndNd;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case ND_SECTIONNODE:
|
|
if( ( ((SwSectionNode*)pNd)->GetSection().IsProtect() ||
|
|
((SwSectionNode*)pNd)->GetSection().IsHidden() ) )
|
|
nCurrNd = pNd->EndOfSectionIndex();
|
|
break;
|
|
case ND_ENDNODE:
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
bGoOn = nCurrNd < nEndNd;
|
|
++nCurrNd;
|
|
}
|
|
}
|
|
|
|
if( !aRet.hasValue() )
|
|
{
|
|
if (pConvArgs)
|
|
aRet <<= pConvArgs->aConvText;
|
|
else
|
|
aRet <<= pSpellArgs->xSpellAlt;
|
|
}
|
|
delete pSpellArgs;
|
|
|
|
return aRet;
|
|
}
|
|
|
|
class SwHyphArgs : public SwInterHyphInfo
|
|
{
|
|
const SwNode *pStart;
|
|
const SwNode *pEnd;
|
|
SwNode *pNode;
|
|
sal_uInt16 *pPageCnt;
|
|
sal_uInt16 *pPageSt;
|
|
|
|
sal_uInt32 nNode;
|
|
xub_StrLen nPamStart;
|
|
xub_StrLen nPamLen;
|
|
|
|
public:
|
|
SwHyphArgs( const SwPaM *pPam, const Point &rPoint,
|
|
sal_uInt16* pPageCount, sal_uInt16* pPageStart );
|
|
void SetPam( SwPaM *pPam ) const;
|
|
inline void SetNode( SwNode *pNew ) { pNode = pNew; }
|
|
inline const SwNode *GetNode() const { return pNode; }
|
|
inline void SetRange( const SwNode *pNew );
|
|
inline void NextNode() { ++nNode; }
|
|
inline sal_uInt16 *GetPageCnt() { return pPageCnt; }
|
|
inline sal_uInt16 *GetPageSt() { return pPageSt; }
|
|
};
|
|
|
|
SwHyphArgs::SwHyphArgs( const SwPaM *pPam, const Point &rCrsrPos,
|
|
sal_uInt16* pPageCount, sal_uInt16* pPageStart )
|
|
: SwInterHyphInfo( rCrsrPos ), pNode(0),
|
|
pPageCnt( pPageCount ), pPageSt( pPageStart )
|
|
{
|
|
// The following constraints have to be met:
|
|
// 1) there is at least one Selection
|
|
// 2) SPoint() == Start()
|
|
OSL_ENSURE( pPam->HasMark(), "SwDoc::Hyphenate: blowing in the wind");
|
|
OSL_ENSURE( *pPam->GetPoint() <= *pPam->GetMark(),
|
|
"SwDoc::Hyphenate: New York, New York");
|
|
|
|
const SwPosition *pPoint = pPam->GetPoint();
|
|
nNode = pPoint->nNode.GetIndex();
|
|
|
|
// Set start
|
|
pStart = pPoint->nNode.GetNode().GetTxtNode();
|
|
nPamStart = pPoint->nContent.GetIndex();
|
|
|
|
// Set End and Length
|
|
const SwPosition *pMark = pPam->GetMark();
|
|
pEnd = pMark->nNode.GetNode().GetTxtNode();
|
|
nPamLen = pMark->nContent.GetIndex();
|
|
if( pPoint->nNode == pMark->nNode )
|
|
nPamLen = nPamLen - pPoint->nContent.GetIndex();
|
|
}
|
|
|
|
inline void SwHyphArgs::SetRange( const SwNode *pNew )
|
|
{
|
|
nStart = pStart == pNew ? nPamStart : 0;
|
|
nLen = pEnd == pNew ? nPamLen : STRING_NOTFOUND;
|
|
}
|
|
|
|
void SwHyphArgs::SetPam( SwPaM *pPam ) const
|
|
{
|
|
if( !pNode )
|
|
*pPam->GetPoint() = *pPam->GetMark();
|
|
else
|
|
{
|
|
pPam->GetPoint()->nNode = nNode;
|
|
pPam->GetPoint()->nContent.Assign( pNode->GetCntntNode(), nWordStart );
|
|
pPam->GetMark()->nNode = nNode;
|
|
pPam->GetMark()->nContent.Assign( pNode->GetCntntNode(),
|
|
nWordStart + nWordLen );
|
|
OSL_ENSURE( nNode == pNode->GetIndex(),
|
|
"SwHyphArgs::SetPam: Pam disaster" );
|
|
}
|
|
}
|
|
|
|
// Returns sal_True if we can proceed.
|
|
sal_Bool lcl_HyphenateNode( const SwNodePtr& rpNd, void* pArgs )
|
|
{
|
|
// Hyphenate returns true if there is a hyphenation point and sets pPam
|
|
SwTxtNode *pNode = rpNd->GetTxtNode();
|
|
SwHyphArgs *pHyphArgs = (SwHyphArgs*)pArgs;
|
|
if( pNode )
|
|
{
|
|
SwCntntFrm* pCntFrm = pNode->getLayoutFrm( pNode->GetDoc()->GetCurrentLayout() );
|
|
if( pCntFrm && !((SwTxtFrm*)pCntFrm)->IsHiddenNow() )
|
|
{
|
|
sal_uInt16 *pPageSt = pHyphArgs->GetPageSt();
|
|
sal_uInt16 *pPageCnt = pHyphArgs->GetPageCnt();
|
|
if( pPageCnt && *pPageCnt && pPageSt )
|
|
{
|
|
sal_uInt16 nPageNr = pCntFrm->GetPhyPageNum();
|
|
if( !*pPageSt )
|
|
{
|
|
*pPageSt = nPageNr;
|
|
if( *pPageCnt < *pPageSt )
|
|
*pPageCnt = *pPageSt;
|
|
}
|
|
long nStat = nPageNr >= *pPageSt ? nPageNr - *pPageSt + 1
|
|
: nPageNr + *pPageCnt - *pPageSt + 1;
|
|
::SetProgressState( nStat, (SwDocShell*)pNode->GetDoc()->GetDocShell() );
|
|
}
|
|
pHyphArgs->SetRange( rpNd );
|
|
if( pNode->Hyphenate( *pHyphArgs ) )
|
|
{
|
|
pHyphArgs->SetNode( rpNd );
|
|
return sal_False;
|
|
}
|
|
}
|
|
}
|
|
pHyphArgs->NextNode();
|
|
return sal_True;
|
|
}
|
|
|
|
uno::Reference< XHyphenatedWord > SwDoc::Hyphenate(
|
|
SwPaM *pPam, const Point &rCrsrPos,
|
|
sal_uInt16* pPageCnt, sal_uInt16* pPageSt )
|
|
{
|
|
OSL_ENSURE(this == pPam->GetDoc(), "SwDoc::Hyphenate: strangers in the night");
|
|
|
|
if( *pPam->GetPoint() > *pPam->GetMark() )
|
|
pPam->Exchange();
|
|
|
|
SwHyphArgs aHyphArg( pPam, rCrsrPos, pPageCnt, pPageSt );
|
|
SwNodeIndex aTmpIdx( pPam->GetMark()->nNode, 1 );
|
|
GetNodes().ForEach( pPam->GetPoint()->nNode, aTmpIdx,
|
|
lcl_HyphenateNode, &aHyphArg );
|
|
aHyphArg.SetPam( pPam );
|
|
return aHyphArg.GetHyphWord(); // will be set by lcl_HyphenateNode
|
|
}
|
|
|
|
sal_Bool lcl_GetTokenToParaBreak( String& rStr, String& rRet, sal_Bool bRegExpRplc )
|
|
{
|
|
sal_Bool bRet = sal_False;
|
|
if( bRegExpRplc )
|
|
{
|
|
xub_StrLen nPos = 0;
|
|
String sPara( String::CreateFromAscii(
|
|
RTL_CONSTASCII_STRINGPARAM( "\\n" )));
|
|
while( STRING_NOTFOUND != ( nPos = rStr.Search( sPara, nPos )) )
|
|
{
|
|
// Has this been escaped?
|
|
if( nPos && '\\' == rStr.GetChar( nPos-1 ))
|
|
{
|
|
if( ++nPos >= rStr.Len() )
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
rRet = rStr.Copy( 0, nPos );
|
|
rStr.Erase( 0, nPos + sPara.Len() );
|
|
bRet = sal_True;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if( !bRet )
|
|
{
|
|
rRet = rStr;
|
|
rStr.Erase();
|
|
}
|
|
return bRet;
|
|
}
|
|
|
|
bool SwDoc::ReplaceRange( SwPaM& rPam, const String& rStr,
|
|
const bool bRegExReplace )
|
|
{
|
|
// unfortunately replace works slightly differently from delete,
|
|
// so we cannot use lcl_DoWithBreaks here...
|
|
|
|
::std::vector<xub_StrLen> Breaks;
|
|
|
|
SwPaM aPam( *rPam.GetMark(), *rPam.GetPoint() );
|
|
aPam.Normalize(sal_False);
|
|
if (aPam.GetPoint()->nNode != aPam.GetMark()->nNode)
|
|
{
|
|
aPam.Move(fnMoveBackward);
|
|
}
|
|
OSL_ENSURE((aPam.GetPoint()->nNode == aPam.GetMark()->nNode), "invalid pam?");
|
|
|
|
lcl_CalcBreaks(Breaks, aPam);
|
|
|
|
while (!Breaks.empty() // skip over prefix of dummy chars
|
|
&& (aPam.GetMark()->nContent.GetIndex() == *Breaks.begin()) )
|
|
{
|
|
// skip!
|
|
++aPam.GetMark()->nContent; // always in bounds if Breaks valid
|
|
Breaks.erase(Breaks.begin());
|
|
}
|
|
*rPam.Start() = *aPam.GetMark(); // update start of original pam w/ prefix
|
|
|
|
if (!Breaks.size())
|
|
{
|
|
// park aPam somewhere so it does not point to node that is deleted
|
|
aPam.DeleteMark();
|
|
*aPam.GetPoint() = SwPosition(GetNodes().GetEndOfContent());
|
|
return ReplaceRangeImpl(rPam, rStr, bRegExReplace); // original pam!
|
|
}
|
|
|
|
// Deletion must be split into several parts if the text node
|
|
// contains a text attribute with end and with dummy character
|
|
// and the selection does not contain the text attribute completely,
|
|
// but overlaps its start (left), where the dummy character is.
|
|
|
|
bool bRet( true );
|
|
// iterate from end to start, to avoid invalidating the offsets!
|
|
::std::vector<xub_StrLen>::reverse_iterator iter( Breaks.rbegin() );
|
|
OSL_ENSURE(aPam.GetPoint() == aPam.End(), "wrong!");
|
|
SwPosition & rEnd( *aPam.End() );
|
|
SwPosition & rStart( *aPam.Start() );
|
|
|
|
// set end of temp pam to original end (undo Move backward above)
|
|
rEnd = *rPam.End();
|
|
// after first deletion, rEnd will point into the original text node again!
|
|
|
|
while (iter != Breaks.rend())
|
|
{
|
|
rStart.nContent = *iter + 1;
|
|
if (rEnd.nContent != rStart.nContent) // check if part is empty
|
|
{
|
|
bRet &= (IsRedlineOn())
|
|
? DeleteAndJoinWithRedlineImpl(aPam)
|
|
: DeleteAndJoinImpl(aPam, false);
|
|
}
|
|
rEnd.nContent = *iter;
|
|
++iter;
|
|
}
|
|
|
|
rStart = *rPam.Start(); // set to original start
|
|
OSL_ENSURE(rEnd.nContent > rStart.nContent, "replace part empty!");
|
|
if (rEnd.nContent > rStart.nContent) // check if part is empty
|
|
{
|
|
bRet &= ReplaceRangeImpl(aPam, rStr, bRegExReplace);
|
|
}
|
|
|
|
rPam = aPam; // update original pam (is this required?)
|
|
|
|
return bRet;
|
|
}
|
|
|
|
// It's possible to call Replace with a PaM that spans 2 paragraphs:
|
|
// search with regex for "$", then replace _all_
|
|
bool SwDoc::ReplaceRangeImpl( SwPaM& rPam, const String& rStr,
|
|
const bool bRegExReplace )
|
|
{
|
|
if( !rPam.HasMark() || *rPam.GetPoint() == *rPam.GetMark() )
|
|
return false;
|
|
|
|
sal_Bool bJoinTxt, bJoinPrev;
|
|
lcl_GetJoinFlags( rPam, bJoinTxt, bJoinPrev );
|
|
|
|
{
|
|
// Create a copy of the Cursor in order to move all Pams from
|
|
// the other views out of the deletion range.
|
|
// Except for itself!
|
|
SwPaM aDelPam( *rPam.GetMark(), *rPam.GetPoint() );
|
|
::PaMCorrAbs( aDelPam, *aDelPam.GetPoint() );
|
|
|
|
SwPosition *pStt = (SwPosition*)aDelPam.Start(),
|
|
*pEnd = (SwPosition*)aDelPam.End();
|
|
OSL_ENSURE( pStt->nNode == pEnd->nNode ||
|
|
( pStt->nNode.GetIndex() + 1 == pEnd->nNode.GetIndex() &&
|
|
!pEnd->nContent.GetIndex() ),
|
|
"invalid range: Point and Mark on different nodes" );
|
|
sal_Bool bOneNode = pStt->nNode == pEnd->nNode;
|
|
|
|
// Own Undo?
|
|
String sRepl( rStr );
|
|
SwTxtNode* pTxtNd = pStt->nNode.GetNode().GetTxtNode();
|
|
xub_StrLen nStt = pStt->nContent.GetIndex(),
|
|
nEnd = bOneNode ? pEnd->nContent.GetIndex()
|
|
: pTxtNd->GetTxt().Len();
|
|
|
|
SwDataChanged aTmp( aDelPam, 0 );
|
|
|
|
if( IsRedlineOn() )
|
|
{
|
|
RedlineMode_t eOld = GetRedlineMode();
|
|
checkRedlining(eOld);
|
|
if (GetIDocumentUndoRedo().DoesUndo())
|
|
{
|
|
GetIDocumentUndoRedo().StartUndo(UNDO_EMPTY, NULL);
|
|
|
|
// If any Redline will change (split!) the node
|
|
const ::sw::mark::IMark* pBkmk = getIDocumentMarkAccess()->makeMark( aDelPam, ::rtl::OUString(), IDocumentMarkAccess::UNO_BOOKMARK );
|
|
|
|
//JP 06.01.98: MUSS noch optimiert werden!!!
|
|
SetRedlineMode(
|
|
(RedlineMode_t)(nsRedlineMode_t::REDLINE_ON | nsRedlineMode_t::REDLINE_SHOW_INSERT | nsRedlineMode_t::REDLINE_SHOW_DELETE ));
|
|
|
|
*aDelPam.GetPoint() = pBkmk->GetMarkPos();
|
|
if(pBkmk->IsExpanded())
|
|
*aDelPam.GetMark() = pBkmk->GetOtherMarkPos();
|
|
getIDocumentMarkAccess()->deleteMark(pBkmk);
|
|
pStt = aDelPam.Start();
|
|
pTxtNd = pStt->nNode.GetNode().GetTxtNode();
|
|
nStt = pStt->nContent.GetIndex();
|
|
}
|
|
|
|
if( sRepl.Len() )
|
|
{
|
|
// Apply the first character's attributes to the ReplaceText
|
|
SfxItemSet aSet( GetAttrPool(),
|
|
RES_CHRATR_BEGIN, RES_TXTATR_WITHEND_END - 1,
|
|
RES_UNKNOWNATR_BEGIN, RES_UNKNOWNATR_END-1,
|
|
0 );
|
|
pTxtNd->GetAttr( aSet, nStt+1, nStt+1 );
|
|
|
|
aSet.ClearItem( RES_TXTATR_REFMARK );
|
|
aSet.ClearItem( RES_TXTATR_TOXMARK );
|
|
aSet.ClearItem( RES_TXTATR_CJK_RUBY );
|
|
aSet.ClearItem( RES_TXTATR_INETFMT );
|
|
aSet.ClearItem( RES_TXTATR_META );
|
|
aSet.ClearItem( RES_TXTATR_METAFIELD );
|
|
|
|
if( aDelPam.GetPoint() != aDelPam.End() )
|
|
aDelPam.Exchange();
|
|
|
|
// Remember the End
|
|
SwNodeIndex aPtNd( aDelPam.GetPoint()->nNode, -1 );
|
|
xub_StrLen nPtCnt = aDelPam.GetPoint()->nContent.GetIndex();
|
|
|
|
sal_Bool bFirst = sal_True;
|
|
String sIns;
|
|
while ( lcl_GetTokenToParaBreak( sRepl, sIns, bRegExReplace ) )
|
|
{
|
|
InsertString( aDelPam, sIns );
|
|
if( bFirst )
|
|
{
|
|
SwNodeIndex aMkNd( aDelPam.GetMark()->nNode, -1 );
|
|
xub_StrLen nMkCnt = aDelPam.GetMark()->nContent.GetIndex();
|
|
|
|
SplitNode( *aDelPam.GetPoint(), false );
|
|
|
|
aMkNd++;
|
|
aDelPam.GetMark()->nNode = aMkNd;
|
|
aDelPam.GetMark()->nContent.Assign(
|
|
aMkNd.GetNode().GetCntntNode(), nMkCnt );
|
|
bFirst = sal_False;
|
|
}
|
|
else
|
|
SplitNode( *aDelPam.GetPoint(), false );
|
|
}
|
|
if( sIns.Len() )
|
|
{
|
|
InsertString( aDelPam, sIns );
|
|
}
|
|
|
|
SwPaM aTmpRange( *aDelPam.GetPoint() );
|
|
aTmpRange.SetMark();
|
|
|
|
aPtNd++;
|
|
aDelPam.GetPoint()->nNode = aPtNd;
|
|
aDelPam.GetPoint()->nContent.Assign( aPtNd.GetNode().GetCntntNode(),
|
|
nPtCnt);
|
|
*aTmpRange.GetMark() = *aDelPam.GetPoint();
|
|
|
|
RstTxtAttrs( aTmpRange );
|
|
InsertItemSet( aTmpRange, aSet, 0 );
|
|
}
|
|
|
|
if (GetIDocumentUndoRedo().DoesUndo())
|
|
{
|
|
SwUndo *const pUndoRD =
|
|
new SwUndoRedlineDelete( aDelPam, UNDO_REPLACE );
|
|
GetIDocumentUndoRedo().AppendUndo(pUndoRD);
|
|
}
|
|
AppendRedline( new SwRedline( nsRedlineType_t::REDLINE_DELETE, aDelPam ), true);
|
|
|
|
*rPam.GetMark() = *aDelPam.GetMark();
|
|
if (GetIDocumentUndoRedo().DoesUndo())
|
|
{
|
|
*aDelPam.GetPoint() = *rPam.GetPoint();
|
|
GetIDocumentUndoRedo().EndUndo(UNDO_EMPTY, NULL);
|
|
|
|
// If any Redline will change (split!) the node
|
|
const ::sw::mark::IMark* pBkmk = getIDocumentMarkAccess()->makeMark( aDelPam, ::rtl::OUString(), IDocumentMarkAccess::UNO_BOOKMARK );
|
|
|
|
SwIndex& rIdx = aDelPam.GetPoint()->nContent;
|
|
rIdx.Assign( 0, 0 );
|
|
aDelPam.GetMark()->nContent = rIdx;
|
|
rPam.GetPoint()->nNode = 0;
|
|
rPam.GetPoint()->nContent = rIdx;
|
|
*rPam.GetMark() = *rPam.GetPoint();
|
|
//JP 06.01.98: MUSS noch optimiert werden!!!
|
|
SetRedlineMode( eOld );
|
|
|
|
*rPam.GetPoint() = pBkmk->GetMarkPos();
|
|
if(pBkmk->IsExpanded())
|
|
*rPam.GetMark() = pBkmk->GetOtherMarkPos();
|
|
getIDocumentMarkAccess()->deleteMark(pBkmk);
|
|
}
|
|
bJoinTxt = sal_False;
|
|
}
|
|
else
|
|
{
|
|
if( !IsIgnoreRedline() && GetRedlineTbl().Count() )
|
|
DeleteRedline( aDelPam, true, USHRT_MAX );
|
|
|
|
SwUndoReplace* pUndoRpl = 0;
|
|
bool const bDoesUndo = GetIDocumentUndoRedo().DoesUndo();
|
|
if (bDoesUndo)
|
|
{
|
|
pUndoRpl = new SwUndoReplace(aDelPam, sRepl, bRegExReplace);
|
|
GetIDocumentUndoRedo().AppendUndo(pUndoRpl);
|
|
}
|
|
::sw::UndoGuard const undoGuard(GetIDocumentUndoRedo());
|
|
|
|
if( aDelPam.GetPoint() != pStt )
|
|
aDelPam.Exchange();
|
|
|
|
SwNodeIndex aPtNd( pStt->nNode, -1 );
|
|
xub_StrLen nPtCnt = pStt->nContent.GetIndex();
|
|
|
|
// Set the values again, if Frames or footnotes on the Text have been removed.
|
|
nStt = nPtCnt;
|
|
nEnd = bOneNode ? pEnd->nContent.GetIndex()
|
|
: pTxtNd->GetTxt().Len();
|
|
|
|
sal_Bool bFirst = sal_True;
|
|
String sIns;
|
|
while ( lcl_GetTokenToParaBreak( sRepl, sIns, bRegExReplace ) )
|
|
{
|
|
if( !bFirst || nStt == pTxtNd->GetTxt().Len() )
|
|
{
|
|
InsertString( aDelPam, sIns );
|
|
}
|
|
else if( nStt < nEnd || sIns.Len() )
|
|
{
|
|
pTxtNd->ReplaceText( pStt->nContent, nEnd - nStt, sIns );
|
|
}
|
|
SplitNode( *pStt, false);
|
|
bFirst = sal_False;
|
|
}
|
|
|
|
if( bFirst || sIns.Len() )
|
|
{
|
|
if( !bFirst || nStt == pTxtNd->GetTxt().Len() )
|
|
{
|
|
InsertString( aDelPam, sIns );
|
|
}
|
|
else if( nStt < nEnd || sIns.Len() )
|
|
{
|
|
pTxtNd->ReplaceText( pStt->nContent, nEnd - nStt, sIns );
|
|
}
|
|
}
|
|
|
|
*rPam.GetMark() = *aDelPam.GetMark();
|
|
|
|
aPtNd++;
|
|
rPam.GetMark()->nNode = aPtNd;
|
|
rPam.GetMark()->nContent.Assign( aPtNd.GetNode().GetCntntNode(),
|
|
nPtCnt );
|
|
|
|
if (bJoinTxt && !bJoinPrev)
|
|
{
|
|
rPam.Move(fnMoveBackward);
|
|
}
|
|
|
|
if( pUndoRpl )
|
|
{
|
|
pUndoRpl->SetEnd(rPam);
|
|
}
|
|
}
|
|
}
|
|
|
|
if( bJoinTxt )
|
|
lcl_JoinText( rPam, bJoinPrev );
|
|
|
|
SetModified();
|
|
return true;
|
|
}
|
|
|
|
// Save the current values to add them as automatic entries to to AutoCorrect.
|
|
void SwDoc::SetAutoCorrExceptWord( SwAutoCorrExceptWord* pNew )
|
|
{
|
|
if( pNew != pACEWord )
|
|
delete pACEWord;
|
|
pACEWord = pNew;
|
|
}
|
|
|
|
bool SwDoc::DelFullPara( SwPaM& rPam )
|
|
{
|
|
const SwPosition &rStt = *rPam.Start(), &rEnd = *rPam.End();
|
|
const SwNode* pNd = &rStt.nNode.GetNode();
|
|
sal_uInt32 nSectDiff = pNd->StartOfSectionNode()->EndOfSectionIndex() -
|
|
pNd->StartOfSectionIndex();
|
|
sal_uInt32 nNodeDiff = rEnd.nNode.GetIndex() - rStt.nNode.GetIndex();
|
|
|
|
if ( nSectDiff-2 <= nNodeDiff || IsRedlineOn() ||
|
|
/* #i9185# Prevent getting the node after the end node (see below) */
|
|
rEnd.nNode.GetIndex() + 1 == GetNodes().Count() )
|
|
{
|
|
return sal_False;
|
|
}
|
|
|
|
// Move hard page brakes to the following Node.
|
|
sal_Bool bSavePageBreak = sal_False, bSavePageDesc = sal_False;
|
|
|
|
/* #i9185# This whould lead to a segmentation fault if not caught above. */
|
|
sal_uLong nNextNd = rEnd.nNode.GetIndex() + 1;
|
|
SwTableNode *const pTblNd = GetNodes()[ nNextNd ]->GetTableNode();
|
|
|
|
if( pTblNd && pNd->IsCntntNode() )
|
|
{
|
|
SwFrmFmt* pTableFmt = pTblNd->GetTable().GetFrmFmt();
|
|
|
|
{
|
|
const SfxPoolItem *pItem;
|
|
const SfxItemSet* pSet = ((SwCntntNode*)pNd)->GetpSwAttrSet();
|
|
if( pSet && SFX_ITEM_SET == pSet->GetItemState( RES_PAGEDESC,
|
|
sal_False, &pItem ) )
|
|
{
|
|
pTableFmt->SetFmtAttr( *pItem );
|
|
bSavePageDesc = sal_True;
|
|
}
|
|
|
|
if( pSet && SFX_ITEM_SET == pSet->GetItemState( RES_BREAK,
|
|
sal_False, &pItem ) )
|
|
{
|
|
pTableFmt->SetFmtAttr( *pItem );
|
|
bSavePageBreak = sal_True;
|
|
}
|
|
}
|
|
}
|
|
|
|
bool const bDoesUndo = GetIDocumentUndoRedo().DoesUndo();
|
|
if( bDoesUndo )
|
|
{
|
|
if( !rPam.HasMark() )
|
|
rPam.SetMark();
|
|
else if( rPam.GetPoint() == &rStt )
|
|
rPam.Exchange();
|
|
rPam.GetPoint()->nNode++;
|
|
|
|
SwCntntNode *pTmpNode = rPam.GetPoint()->nNode.GetNode().GetCntntNode();
|
|
rPam.GetPoint()->nContent.Assign( pTmpNode, 0 );
|
|
bool bGoNext = (0 == pTmpNode);
|
|
pTmpNode = rPam.GetMark()->nNode.GetNode().GetCntntNode();
|
|
rPam.GetMark()->nContent.Assign( pTmpNode, 0 );
|
|
|
|
GetIDocumentUndoRedo().ClearRedo();
|
|
|
|
SwPaM aDelPam( *rPam.GetMark(), *rPam.GetPoint() );
|
|
{
|
|
SwPosition aTmpPos( *aDelPam.GetPoint() );
|
|
if( bGoNext )
|
|
{
|
|
pTmpNode = GetNodes().GoNext( &aTmpPos.nNode );
|
|
aTmpPos.nContent.Assign( pTmpNode, 0 );
|
|
}
|
|
::PaMCorrAbs( aDelPam, aTmpPos );
|
|
}
|
|
|
|
SwUndoDelete* pUndo = new SwUndoDelete( aDelPam, sal_True );
|
|
|
|
*rPam.GetPoint() = *aDelPam.GetPoint();
|
|
pUndo->SetPgBrkFlags( bSavePageBreak, bSavePageDesc );
|
|
GetIDocumentUndoRedo().AppendUndo(pUndo);
|
|
}
|
|
else
|
|
{
|
|
SwNodeRange aRg( rStt.nNode, rEnd.nNode );
|
|
if( rPam.GetPoint() != &rEnd )
|
|
rPam.Exchange();
|
|
|
|
// Try to move past the End
|
|
if( !rPam.Move( fnMoveForward, fnGoNode ) )
|
|
{
|
|
// Fair enough, at the Beginning then
|
|
rPam.Exchange();
|
|
if( !rPam.Move( fnMoveBackward, fnGoNode ))
|
|
{
|
|
OSL_FAIL( "no more Nodes" );
|
|
return sal_False;
|
|
}
|
|
}
|
|
// move bookmarks, redlines etc.
|
|
if (aRg.aStart == aRg.aEnd) // only first CorrAbs variant handles this
|
|
{
|
|
CorrAbs( aRg.aStart, *rPam.GetPoint(), 0, sal_True );
|
|
}
|
|
else
|
|
{
|
|
CorrAbs( aRg.aStart, aRg.aEnd, *rPam.GetPoint(), sal_True );
|
|
}
|
|
|
|
// What's with Flys?
|
|
{
|
|
// If there are FlyFrames left, delete these too
|
|
for( sal_uInt16 n = 0; n < GetSpzFrmFmts()->Count(); ++n )
|
|
{
|
|
SwFrmFmt* pFly = (*GetSpzFrmFmts())[n];
|
|
const SwFmtAnchor* pAnchor = &pFly->GetAnchor();
|
|
SwPosition const*const pAPos = pAnchor->GetCntntAnchor();
|
|
if (pAPos &&
|
|
((FLY_AT_PARA == pAnchor->GetAnchorId()) ||
|
|
(FLY_AT_CHAR == pAnchor->GetAnchorId())) &&
|
|
aRg.aStart <= pAPos->nNode && pAPos->nNode <= aRg.aEnd )
|
|
{
|
|
DelLayoutFmt( pFly );
|
|
--n;
|
|
}
|
|
}
|
|
}
|
|
|
|
SwCntntNode *pTmpNode = rPam.GetBound( sal_True ).nNode.GetNode().GetCntntNode();
|
|
rPam.GetBound( sal_True ).nContent.Assign( pTmpNode, 0 );
|
|
pTmpNode = rPam.GetBound( sal_False ).nNode.GetNode().GetCntntNode();
|
|
rPam.GetBound( sal_False ).nContent.Assign( pTmpNode, 0 );
|
|
GetNodes().Delete( aRg.aStart, nNodeDiff+1 );
|
|
}
|
|
rPam.DeleteMark();
|
|
SetModified();
|
|
|
|
return sal_True;
|
|
}
|
|
|
|
void SwDoc::TransliterateText(
|
|
const SwPaM& rPaM,
|
|
utl::TransliterationWrapper& rTrans )
|
|
{
|
|
SwUndoTransliterate *const pUndo = (GetIDocumentUndoRedo().DoesUndo())
|
|
? new SwUndoTransliterate( rPaM, rTrans )
|
|
: 0;
|
|
|
|
const SwPosition* pStt = rPaM.Start(),
|
|
* pEnd = rPaM.End();
|
|
sal_uLong nSttNd = pStt->nNode.GetIndex(),
|
|
nEndNd = pEnd->nNode.GetIndex();
|
|
xub_StrLen nSttCnt = pStt->nContent.GetIndex(),
|
|
nEndCnt = pEnd->nContent.GetIndex();
|
|
|
|
SwTxtNode* pTNd = pStt->nNode.GetNode().GetTxtNode();
|
|
if( pStt == pEnd && pTNd ) // no selection?
|
|
{
|
|
// set current word as 'area of effect'
|
|
|
|
Boundary aBndry;
|
|
if( pBreakIt->GetBreakIter().is() )
|
|
aBndry = pBreakIt->GetBreakIter()->getWordBoundary(
|
|
pTNd->GetTxt(), nSttCnt,
|
|
pBreakIt->GetLocale( pTNd->GetLang( nSttCnt ) ),
|
|
WordType::ANY_WORD /*ANYWORD_IGNOREWHITESPACES*/,
|
|
sal_True );
|
|
|
|
if( aBndry.startPos < nSttCnt && nSttCnt < aBndry.endPos )
|
|
{
|
|
nSttCnt = (xub_StrLen)aBndry.startPos;
|
|
nEndCnt = (xub_StrLen)aBndry.endPos;
|
|
}
|
|
}
|
|
|
|
if( nSttNd != nEndNd ) // is more than one text node involved?
|
|
{
|
|
// iterate over all effected text nodes, the first and the last one
|
|
// may be incomplete because the selection starts and/or ends there
|
|
|
|
SwNodeIndex aIdx( pStt->nNode );
|
|
if( nSttCnt )
|
|
{
|
|
aIdx++;
|
|
if( pTNd )
|
|
pTNd->TransliterateText( rTrans, nSttCnt, pTNd->GetTxt().Len(), pUndo );
|
|
}
|
|
|
|
for( ; aIdx.GetIndex() < nEndNd; aIdx++ )
|
|
{
|
|
if( 0 != ( pTNd = aIdx.GetNode().GetTxtNode() ))
|
|
pTNd->TransliterateText( rTrans, 0, pTNd->GetTxt().Len(), pUndo );
|
|
}
|
|
|
|
if( nEndCnt && 0 != ( pTNd = pEnd->nNode.GetNode().GetTxtNode() ))
|
|
pTNd->TransliterateText( rTrans, 0, nEndCnt, pUndo );
|
|
}
|
|
else if( pTNd && nSttCnt < nEndCnt )
|
|
pTNd->TransliterateText( rTrans, nSttCnt, nEndCnt, pUndo );
|
|
|
|
if( pUndo )
|
|
{
|
|
if( pUndo->HasData() )
|
|
{
|
|
GetIDocumentUndoRedo().AppendUndo(pUndo);
|
|
}
|
|
else
|
|
delete pUndo;
|
|
}
|
|
SetModified();
|
|
}
|
|
|
|
#define MAX_REDLINE_COUNT 250
|
|
|
|
void SwDoc::checkRedlining(RedlineMode_t& _rReadlineMode)
|
|
{
|
|
const SwRedlineTbl& rRedlineTbl = GetRedlineTbl();
|
|
SwEditShell* pEditShell = GetEditShell();
|
|
Window* pParent = pEditShell ? pEditShell->GetWin() : NULL;
|
|
if ( pParent && !mbReadlineChecked && rRedlineTbl.Count() > MAX_REDLINE_COUNT
|
|
&& !((_rReadlineMode & nsRedlineMode_t::REDLINE_SHOW_DELETE) == nsRedlineMode_t::REDLINE_SHOW_DELETE) )
|
|
{
|
|
WarningBox aWarning( pParent,SW_RES(MSG_DISABLE_READLINE_QUESTION));
|
|
sal_uInt16 nResult = aWarning.Execute();
|
|
mbReadlineChecked = sal_True;
|
|
if ( nResult == RET_YES )
|
|
{
|
|
sal_Int32 nMode = (sal_Int32)_rReadlineMode;
|
|
nMode |= nsRedlineMode_t::REDLINE_SHOW_INSERT | nsRedlineMode_t::REDLINE_SHOW_DELETE;
|
|
_rReadlineMode = (RedlineMode_t)nMode;
|
|
}
|
|
}
|
|
}
|
|
|
|
void SwDoc::CountWords( const SwPaM& rPaM, SwDocStat& rStat ) const
|
|
{
|
|
// This is a modified version of SwDoc::TransliterateText
|
|
const SwPosition* pStt = rPaM.Start();
|
|
const SwPosition* pEnd = pStt == rPaM.GetPoint() ? rPaM.GetMark()
|
|
: rPaM.GetPoint();
|
|
|
|
const sal_uLong nSttNd = pStt->nNode.GetIndex();
|
|
const sal_uLong nEndNd = pEnd->nNode.GetIndex();
|
|
|
|
const xub_StrLen nSttCnt = pStt->nContent.GetIndex();
|
|
const xub_StrLen nEndCnt = pEnd->nContent.GetIndex();
|
|
|
|
const SwTxtNode* pTNd = pStt->nNode.GetNode().GetTxtNode();
|
|
if( pStt == pEnd && pTNd ) // no region ?
|
|
{
|
|
// do nothing
|
|
return;
|
|
}
|
|
|
|
if( nSttNd != nEndNd )
|
|
{
|
|
SwNodeIndex aIdx( pStt->nNode );
|
|
if( nSttCnt )
|
|
{
|
|
aIdx++;
|
|
if( pTNd )
|
|
pTNd->CountWords( rStat, nSttCnt, pTNd->GetTxt().Len() );
|
|
}
|
|
|
|
for( ; aIdx.GetIndex() < nEndNd; aIdx++ )
|
|
if( 0 != ( pTNd = aIdx.GetNode().GetTxtNode() ))
|
|
pTNd->CountWords( rStat, 0, pTNd->GetTxt().Len() );
|
|
|
|
if( nEndCnt && 0 != ( pTNd = pEnd->nNode.GetNode().GetTxtNode() ))
|
|
pTNd->CountWords( rStat, 0, nEndCnt );
|
|
}
|
|
else if( pTNd && nSttCnt < nEndCnt )
|
|
pTNd->CountWords( rStat, nSttCnt, nEndCnt );
|
|
}
|
|
|
|
void SwDoc::RemoveLeadingWhiteSpace(const SwPosition & rPos )
|
|
{
|
|
const SwTxtNode* pTNd = rPos.nNode.GetNode().GetTxtNode();
|
|
if ( pTNd )
|
|
{
|
|
const String& rTxt = pTNd->GetTxt();
|
|
xub_StrLen nIdx = 0;
|
|
sal_Unicode cCh;
|
|
while( nIdx < rTxt.Len() &&
|
|
( '\t' == ( cCh = rTxt.GetChar( nIdx ) ) ||
|
|
( ' ' == cCh ) ) )
|
|
++nIdx;
|
|
|
|
if ( nIdx > 0 )
|
|
{
|
|
SwPaM aPam(rPos);
|
|
aPam.GetPoint()->nContent = 0;
|
|
aPam.SetMark();
|
|
aPam.GetMark()->nContent = nIdx;
|
|
DeleteRange( aPam );
|
|
}
|
|
}
|
|
}
|
|
|
|
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|