xref: /aoo41x/main/sw/source/core/doc/docedt.cxx (revision 7879fa4d)
1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 
23 
24 // MARKER(update_precomp.py): autogen include statement, do not remove
25 #include "precompiled_sw.hxx"
26 
27 #include <string.h>			// fuer strchr()
28 #include <hintids.hxx>
29 
30 #include <vcl/sound.hxx>
31 #include <editeng/cscoitem.hxx>
32 #include <editeng/brkitem.hxx>
33 #include <linguistic/lngprops.hxx>
34 #include <com/sun/star/beans/XPropertySet.hpp>
35 #include <com/sun/star/i18n/WordType.hdl>
36 #include <unotools/charclass.hxx>
37 #include <unotools/transliterationwrapper.hxx>
38 #include <fmtanchr.hxx>
39 #include <fmtcntnt.hxx>
40 #include <fmtpdsc.hxx>
41 #include <txtftn.hxx>
42 #include <acorrect.hxx>		// Autokorrektur
43 #include <IMark.hxx>		// fuer SwBookmark
44 #include <cntfrm.hxx>			// fuers Spell
45 #include <crsrsh.hxx>
46 #include <doc.hxx>
47 #include <UndoManager.hxx>
48 #include <docsh.hxx>
49 #include <docary.hxx>
50 #include <doctxm.hxx>		// beim Move: Verzeichnisse korrigieren
51 #include <ftnidx.hxx>
52 #include <ftninfo.hxx>
53 #include <mdiexp.hxx>		// Statusanzeige
54 #include <mvsave.hxx>		// Strukturen zum Sichern beim Move/Delete
55 #include <ndtxt.hxx>
56 #include <pam.hxx>
57 #include <redline.hxx>
58 #include <rootfrm.hxx>			// fuers UpdateFtn
59 #include <splargs.hxx>		// fuer Spell
60 #include <swtable.hxx>
61 #include <swundo.hxx>		// fuer die UndoIds
62 #include <txtfrm.hxx>
63 #include <hints.hxx>
64 #include <UndoSplitMove.hxx>
65 #include <UndoRedline.hxx>
66 #include <UndoOverwrite.hxx>
67 #include <UndoInsert.hxx>
68 #include <UndoDelete.hxx>
69 #include <breakit.hxx>
70 #include <hhcwrp.hxx>
71 #include <breakit.hxx>
72 #include <vcl/msgbox.hxx>
73 #include "comcore.hrc"
74 #include "editsh.hxx"
75 #include <unoflatpara.hxx>
76 #include <SwGrammarMarkUp.hxx>
77 
78 #include <vector>
79 
80 using ::rtl::OUString;
81 using namespace ::com::sun::star;
82 using namespace ::com::sun::star::linguistic2;
83 using namespace ::com::sun::star::i18n;
84 
85 //using namespace ::utl;
86 #ifndef S2U
87 #define S2U(rString) OUString::createFromAscii(rString)
88 #endif
89 
90 struct _SaveRedline
91 {
92 	SwRedline* pRedl;
93 	sal_uInt32 nStt, nEnd;
94 	xub_StrLen nSttCnt, nEndCnt;
95 
96 	_SaveRedline( SwRedline* pR, const SwNodeIndex& rSttIdx )
97 		: pRedl( pR )
98 	{
99 		const SwPosition* pStt = pR->Start(),
100 			* pEnd = pR->GetMark() == pStt ? pR->GetPoint() : pR->GetMark();
101 		sal_uInt32 nSttIdx = rSttIdx.GetIndex();
102 		nStt = pStt->nNode.GetIndex() - nSttIdx;
103 		nSttCnt = pStt->nContent.GetIndex();
104 		if( pR->HasMark() )
105 		{
106 			nEnd = pEnd->nNode.GetIndex() - nSttIdx;
107 			nEndCnt = pEnd->nContent.GetIndex();
108 		}
109 
110 		pRedl->GetPoint()->nNode = 0;
111 		pRedl->GetPoint()->nContent.Assign( 0, 0 );
112 		pRedl->GetMark()->nNode = 0;
113 		pRedl->GetMark()->nContent.Assign( 0, 0 );
114 	}
115 
116 	_SaveRedline( SwRedline* pR, const SwPosition& rPos )
117 		: pRedl( pR )
118 	{
119 		const SwPosition* pStt = pR->Start(),
120 			* pEnd = pR->GetMark() == pStt ? pR->GetPoint() : pR->GetMark();
121 		sal_uInt32 nSttIdx = rPos.nNode.GetIndex();
122 		nStt = pStt->nNode.GetIndex() - nSttIdx;
123 		nSttCnt = pStt->nContent.GetIndex();
124         if( nStt == 0 )
125             nSttCnt = nSttCnt - rPos.nContent.GetIndex();
126 		if( pR->HasMark() )
127 		{
128 			nEnd = pEnd->nNode.GetIndex() - nSttIdx;
129 			nEndCnt = pEnd->nContent.GetIndex();
130             if( nEnd == 0 )
131                 nEndCnt = nEndCnt - rPos.nContent.GetIndex();
132 		}
133 
134 		pRedl->GetPoint()->nNode = 0;
135 		pRedl->GetPoint()->nContent.Assign( 0, 0 );
136 		pRedl->GetMark()->nNode = 0;
137 		pRedl->GetMark()->nContent.Assign( 0, 0 );
138 	}
139 
140 	void SetPos( sal_uInt32 nInsPos )
141 	{
142 		pRedl->GetPoint()->nNode = nInsPos + nStt;
143 		pRedl->GetPoint()->nContent.Assign( pRedl->GetCntntNode(), nSttCnt );
144 		if( pRedl->HasMark() )
145 		{
146 			pRedl->GetMark()->nNode = nInsPos + nEnd;
147 			pRedl->GetMark()->nContent.Assign( pRedl->GetCntntNode(sal_False), nEndCnt );
148 		}
149 	}
150 
151 	void SetPos( const SwPosition& aPos )
152 	{
153 		pRedl->GetPoint()->nNode = aPos.nNode.GetIndex() + nStt;
154         pRedl->GetPoint()->nContent.Assign( pRedl->GetCntntNode(), nSttCnt + ( nStt == 0 ? aPos.nContent.GetIndex() : 0 ) );
155 		if( pRedl->HasMark() )
156 		{
157 			pRedl->GetMark()->nNode = aPos.nNode.GetIndex() + nEnd;
158 			pRedl->GetMark()->nContent.Assign( pRedl->GetCntntNode(sal_False), nEndCnt  + ( nEnd == 0 ? aPos.nContent.GetIndex() : 0 ) );
159 		}
160 	}
161 };
162 
163 SV_DECL_PTRARR_DEL( _SaveRedlines, _SaveRedline*, 0, 4 )
164 
165 SV_IMPL_VARARR( _SaveFlyArr, _SaveFly )
166 SV_IMPL_PTRARR( _SaveRedlines, _SaveRedline* )
167 
168 bool lcl_MayOverwrite( const SwTxtNode *pNode, const xub_StrLen nPos )
169 {
170     sal_Unicode cChr = pNode->GetTxt().GetChar( nPos );
171     return !( ( CH_TXTATR_BREAKWORD == cChr || CH_TXTATR_INWORD == cChr ) &&
172               (0 != pNode->GetTxtAttrForCharAt( nPos ) ) );
173 }
174 
175 void lcl_SkipAttr( const SwTxtNode *pNode, SwIndex &rIdx, xub_StrLen &rStart )
176 {
177 	if( !lcl_MayOverwrite( pNode, rStart ) )
178 	{
179 		// ueberspringe alle SonderAttribute
180 		do {
181 			// "Beep" bei jedem ausgelassenen
182 			Sound::Beep(SOUND_ERROR);
183 			rIdx++;
184 		} while( (rStart = rIdx.GetIndex()) < pNode->GetTxt().Len()
185 			   && !lcl_MayOverwrite(pNode, rStart) );
186 	}
187 }
188 
189 // -----------------------------------------------------------------
190 
191 void _RestFlyInRange( _SaveFlyArr & rArr, const SwNodeIndex& rSttIdx,
192                       const SwNodeIndex* pInsertPos )
193 {
194 	SwPosition aPos( rSttIdx );
195 	for( sal_uInt16 n = 0; n < rArr.Count(); ++n )
196 	{
197 		// neuen Anker anlegen
198 		_SaveFly& rSave = rArr[n];
199 		SwFrmFmt* pFmt = rSave.pFrmFmt;
200 
201         if( rSave.bInsertPosition )
202         {
203             if( pInsertPos != NULL )
204                 aPos.nNode = *pInsertPos;
205             else
206                 aPos.nNode = rSttIdx.GetIndex();
207         }
208         else
209             aPos.nNode = rSttIdx.GetIndex() + rSave.nNdDiff;
210 
211 		aPos.nContent.Assign( 0, 0 );
212 		SwFmtAnchor aAnchor( pFmt->GetAnchor() );
213 		aAnchor.SetAnchor( &aPos );
214 		pFmt->GetDoc()->GetSpzFrmFmts()->Insert(
215 				pFmt, pFmt->GetDoc()->GetSpzFrmFmts()->Count() );
216         pFmt->SetFmtAttr( aAnchor );
217 		SwCntntNode* pCNd = aPos.nNode.GetNode().GetCntntNode();
218 		if( pCNd && pCNd->getLayoutFrm( pFmt->GetDoc()->GetCurrentLayout(), 0, 0, sal_False ) )
219 			pFmt->MakeFrms();
220 	}
221 }
222 
223 void _SaveFlyInRange( const SwNodeRange& rRg, _SaveFlyArr& rArr )
224 {
225 	SwSpzFrmFmts& rFmts = *rRg.aStart.GetNode().GetDoc()->GetSpzFrmFmts();
226 	for( sal_uInt16 n = 0; n < rFmts.Count(); ++n )
227 	{
228         SwFrmFmt *const pFmt = static_cast<SwFrmFmt*>(rFmts[n]);
229         SwFmtAnchor const*const pAnchor = &pFmt->GetAnchor();
230         SwPosition const*const pAPos = pAnchor->GetCntntAnchor();
231         if (pAPos &&
232             ((FLY_AT_PARA == pAnchor->GetAnchorId()) ||
233              (FLY_AT_CHAR == pAnchor->GetAnchorId())) &&
234 			rRg.aStart <= pAPos->nNode && pAPos->nNode < rRg.aEnd )
235 		{
236 			_SaveFly aSave( pAPos->nNode.GetIndex() - rRg.aStart.GetIndex(),
237                             pFmt, sal_False );
238 			rArr.Insert( aSave, rArr.Count());
239 			pFmt->DelFrms();
240 			rFmts.Remove( n--, 1 );
241 		}
242 	}
243 }
244 
245 void _SaveFlyInRange( const SwPaM& rPam, const SwNodeIndex& rInsPos,
246 					   _SaveFlyArr& rArr, bool bMoveAllFlys )
247 {
248 	SwSpzFrmFmts& rFmts = *rPam.GetPoint()->nNode.GetNode().GetDoc()->GetSpzFrmFmts();
249 	SwFrmFmt* pFmt;
250 	const SwFmtAnchor* pAnchor;
251 
252 	const SwPosition* pPos = rPam.Start();
253 	const SwNodeIndex& rSttNdIdx = pPos->nNode;
254 	short nSttOff = (!bMoveAllFlys && rSttNdIdx.GetNode().IsCntntNode() &&
255 					pPos->nContent.GetIndex()) ? 1 : 0;
256 
257 	pPos = rPam.GetPoint() == pPos ? rPam.GetMark() : rPam.GetPoint();
258 	const SwNodeIndex& rEndNdIdx = pPos->nNode;
259 	short nOff = ( bMoveAllFlys || ( rEndNdIdx.GetNode().IsCntntNode() &&
260 				pPos->nContent == rEndNdIdx.GetNode().GetCntntNode()->Len() ))
261 					? 0 : 1;
262 
263 	const SwNodeIndex* pCntntIdx;
264 
265 	for( sal_uInt16 n = 0; n < rFmts.Count(); ++n )
266 	{
267 		sal_Bool bInsPos = sal_False;
268 		pFmt = (SwFrmFmt*)rFmts[n];
269 		pAnchor = &pFmt->GetAnchor();
270         const SwPosition* pAPos = pAnchor->GetCntntAnchor();
271         if (pAPos &&
272             ((FLY_AT_PARA == pAnchor->GetAnchorId()) ||
273              (FLY_AT_CHAR == pAnchor->GetAnchorId())) &&
274 			// nicht verschieben, wenn die InsPos im CntntBereich vom Fly ist
275 			( 0 == ( pCntntIdx = pFmt->GetCntnt().GetCntntIdx() ) ||
276 			  !( *pCntntIdx < rInsPos &&
277 				rInsPos < pCntntIdx->GetNode().EndOfSectionIndex() )) )
278 		{
279 			if( !bMoveAllFlys && rEndNdIdx == pAPos->nNode )
280 			{
281 				// wenn nur teil vom EndNode oder der EndNode und SttNode
282 				// identisch sind, chaos::Anchor nicht anfassen
283 				if( rSttNdIdx != pAPos->nNode )
284 				{
285 					// Anker nur an Anfang/Ende haengen
286 					SwPosition aPos( rSttNdIdx );
287 					SwFmtAnchor aAnchor( *pAnchor );
288 					aAnchor.SetAnchor( &aPos );
289                     pFmt->SetFmtAttr( aAnchor );
290 //        	        ((SwFmtAnchor*)pAnchor)->SetAnchor( &aPos );
291 				}
292 			}
293 			else if( ( rSttNdIdx.GetIndex() + nSttOff <= pAPos->nNode.GetIndex()
294 					&& pAPos->nNode.GetIndex() <= rEndNdIdx.GetIndex() - nOff ) ||
295 						0 != ( bInsPos = rInsPos == pAPos->nNode ))
296 
297 			{
298 				_SaveFly aSave( pAPos->nNode.GetIndex() - rSttNdIdx.GetIndex(),
299                                 pFmt, bInsPos );
300 				rArr.Insert( aSave, rArr.Count());
301 				pFmt->DelFrms();
302 				rFmts.Remove( n--, 1 );
303 			}
304 		}
305 	}
306 }
307 
308 // -----------------------------------------------------------------
309 
310 // loesche und verschiebe alle "Fly's am Absatz", die in der SSelection
311 // liegen. Steht am SPoint ein Fly, wird dieser auf den Mark verschoben.
312 
313 void DelFlyInRange( const SwNodeIndex& rMkNdIdx,
314 					const SwNodeIndex& rPtNdIdx )
315 {
316 	const sal_Bool bDelFwrd = rMkNdIdx.GetIndex() <= rPtNdIdx.GetIndex();
317 
318 	SwDoc* pDoc = rMkNdIdx.GetNode().GetDoc();
319 	SwSpzFrmFmts& rTbl = *pDoc->GetSpzFrmFmts();
320 	for ( sal_uInt16 i = rTbl.Count(); i; )
321 	{
322 		SwFrmFmt *pFmt = rTbl[--i];
323 		const SwFmtAnchor &rAnch = pFmt->GetAnchor();
324         SwPosition const*const pAPos = rAnch.GetCntntAnchor();
325         if (pAPos &&
326             ((rAnch.GetAnchorId() == FLY_AT_PARA) ||
327              (rAnch.GetAnchorId() == FLY_AT_CHAR)) &&
328 			( bDelFwrd
329 				? rMkNdIdx < pAPos->nNode && pAPos->nNode <= rPtNdIdx
330 				: rPtNdIdx <= pAPos->nNode && pAPos->nNode < rMkNdIdx ))
331 		{
332 			// nur den Anker verschieben ??
333 			if( rPtNdIdx == pAPos->nNode )
334 			{
335 				SwFmtAnchor aAnch( pFmt->GetAnchor() );
336 				SwPosition aPos( rMkNdIdx );
337 				aAnch.SetAnchor( &aPos );
338                 pFmt->SetFmtAttr( aAnch );
339 			}
340 			else
341 			{
342 				// wird der Fly geloescht muss auch im seinem Inhalt alle
343 				// Flys geloescht werden !!
344 				const SwFmtCntnt &rCntnt = pFmt->GetCntnt();
345 				if( rCntnt.GetCntntIdx() )
346 				{
347 					DelFlyInRange( *rCntnt.GetCntntIdx(),
348 									SwNodeIndex( *rCntnt.GetCntntIdx()->
349 											GetNode().EndOfSectionNode() ));
350 					// Position kann sich verschoben haben !
351 					if( i > rTbl.Count() )
352 						i = rTbl.Count();
353 					else if( pFmt != rTbl[i] )
354 						i = rTbl.GetPos( pFmt );
355 				}
356 
357 				pDoc->DelLayoutFmt( pFmt );
358 
359                 // --> FME 2004-10-06 #117913# DelLayoutFmt can also
360                 // trigger the deletion of objects.
361     			if( i > rTbl.Count() )
362 					i = rTbl.Count();
363                 // <--
364             }
365 		}
366 	}
367 }
368 
369 
370 bool lcl_SaveFtn( const SwNodeIndex& rSttNd, const SwNodeIndex& rEndNd,
371 				 const SwNodeIndex& rInsPos,
372 				 SwFtnIdxs& rFtnArr, SwFtnIdxs& rSaveArr,
373 				 const SwIndex* pSttCnt = 0, const SwIndex* pEndCnt = 0 )
374 {
375 	bool bUpdateFtn = sal_False;
376     const SwNodes& rNds = rInsPos.GetNodes();
377     const bool bDelFtn = rInsPos.GetIndex() < rNds.GetEndOfAutotext().GetIndex() &&
378                 rSttNd.GetIndex() >= rNds.GetEndOfAutotext().GetIndex();
379     const bool bSaveFtn = !bDelFtn &&
380                     rInsPos.GetIndex() >= rNds.GetEndOfExtras().GetIndex();
381 	if( rFtnArr.Count() )
382 	{
383 
384 		sal_uInt16 nPos;
385 		rFtnArr.SeekEntry( rSttNd, &nPos );
386 		SwTxtFtn* pSrch;
387 		const SwNode* pFtnNd;
388 
389 		// loesche/sicher erstmal alle, die dahinter stehen
390 		while( nPos < rFtnArr.Count() && ( pFtnNd =
391 			&( pSrch = rFtnArr[ nPos ] )->GetTxtNode())->GetIndex()
392 					<= rEndNd.GetIndex() )
393 		{
394 			xub_StrLen nFtnSttIdx = *pSrch->GetStart();
395 			if( ( pEndCnt && pSttCnt )
396 				? (( &rSttNd.GetNode() == pFtnNd &&
397 					 pSttCnt->GetIndex() > nFtnSttIdx) ||
398 				   ( &rEndNd.GetNode() == pFtnNd &&
399 					nFtnSttIdx >= pEndCnt->GetIndex() ))
400 				: ( &rEndNd.GetNode() == pFtnNd ))
401 			{
402 				++nPos;		// weiter suchen
403 			}
404 			else
405 			{
406 				// dann weg damit
407 				if( bDelFtn )
408 				{
409 					SwTxtNode& rTxtNd = (SwTxtNode&)pSrch->GetTxtNode();
410 					SwIndex aIdx( &rTxtNd, nFtnSttIdx );
411                     rTxtNd.EraseText( aIdx, 1 );
412                 }
413 				else
414 				{
415 					pSrch->DelFrms(0);
416 					rFtnArr.Remove( nPos );
417 					if( bSaveFtn )
418 						rSaveArr.Insert( pSrch );
419 				}
420 				bUpdateFtn = sal_True;
421 			}
422 		}
423 
424 		while( nPos-- && ( pFtnNd = &( pSrch = rFtnArr[ nPos ] )->
425 				GetTxtNode())->GetIndex() >= rSttNd.GetIndex() )
426 		{
427 			xub_StrLen nFtnSttIdx = *pSrch->GetStart();
428 			if( !pEndCnt || !pSttCnt ||
429 				!( (( &rSttNd.GetNode() == pFtnNd &&
430 					pSttCnt->GetIndex() > nFtnSttIdx ) ||
431 				   ( &rEndNd.GetNode() == pFtnNd &&
432 					nFtnSttIdx >= pEndCnt->GetIndex() )) ))
433 			{
434 				if( bDelFtn )
435 				{
436 					// dann weg damit
437 					SwTxtNode& rTxtNd = (SwTxtNode&)pSrch->GetTxtNode();
438 					SwIndex aIdx( &rTxtNd, nFtnSttIdx );
439                     rTxtNd.EraseText( aIdx, 1 );
440                 }
441 				else
442 				{
443 					pSrch->DelFrms(0);
444 					rFtnArr.Remove( nPos );
445 					if( bSaveFtn )
446 						rSaveArr.Insert( pSrch );
447 				}
448 				bUpdateFtn = sal_True;
449 			}
450 		}
451 	}
452     // When moving from redline section into document content section, e.g.
453     // after loading a document with (delete-)redlines, the footnote array
454     // has to be adjusted... (#i70572)
455     if( bSaveFtn )
456     {
457         SwNodeIndex aIdx( rSttNd );
458         while( aIdx < rEndNd ) // Check the moved section
459         {
460             SwNode* pNode = &aIdx.GetNode();
461             if( pNode->IsTxtNode() ) // Looking for text nodes...
462             {
463                 SwpHints *pHints =
464                     static_cast<SwTxtNode*>(pNode)->GetpSwpHints();
465                 if( pHints && pHints->HasFtn() ) //...with footnotes
466                 {
467                     bUpdateFtn = sal_True; // Heureka
468                     sal_uInt16 nCount = pHints->Count();
469                     for( sal_uInt16 i = 0; i < nCount; ++i )
470                     {
471                         SwTxtAttr *pAttr = pHints->GetTextHint( i );
472                         if ( pAttr->Which() == RES_TXTATR_FTN )
473                         {
474                             rSaveArr.Insert( static_cast<SwTxtFtn*>(pAttr) );
475                         }
476                     }
477                 }
478             }
479             ++aIdx;
480         }
481     }
482 	return bUpdateFtn;
483 }
484 
485 void lcl_SaveRedlines( const SwPaM& aPam, _SaveRedlines& rArr )
486 {
487 	SwDoc* pDoc = aPam.GetNode()->GetDoc();
488 
489     const SwPosition* pStart = aPam.Start();
490     const SwPosition* pEnd = aPam.End();
491 
492     // get first relevant redline
493 	sal_uInt16 nCurrentRedline;
494     pDoc->GetRedline( *pStart, &nCurrentRedline );
495     if( nCurrentRedline > 0)
496         nCurrentRedline--;
497 
498     // redline mode REDLINE_IGNORE|REDLINE_ON; save old mode
499 	RedlineMode_t eOld = pDoc->GetRedlineMode();
500 	pDoc->SetRedlineMode_intern( (RedlineMode_t)(( eOld & ~nsRedlineMode_t::REDLINE_IGNORE) | nsRedlineMode_t::REDLINE_ON ));
501 
502     // iterate over relevant redlines and decide for each whether it should
503     // be saved, or split + saved
504 	SwRedlineTbl& rRedlineTable = const_cast<SwRedlineTbl&>( pDoc->GetRedlineTbl() );
505     for( ; nCurrentRedline < rRedlineTable.Count(); nCurrentRedline++ )
506     {
507         SwRedline* pCurrent = rRedlineTable[ nCurrentRedline ];
508         SwComparePosition eCompare =
509             ComparePosition( *pCurrent->Start(), *pCurrent->End(),
510                              *pStart, *pEnd);
511 
512         // we must save this redline if it overlaps aPam
513         // (we may have to split it, too)
514         if( eCompare == POS_OVERLAP_BEHIND  ||
515             eCompare == POS_OVERLAP_BEFORE  ||
516             eCompare == POS_OUTSIDE ||
517             eCompare == POS_INSIDE ||
518             eCompare == POS_EQUAL )
519         {
520 			rRedlineTable.Remove( nCurrentRedline-- );
521 
522             // split beginning, if necessary
523             if( eCompare == POS_OVERLAP_BEFORE  ||
524                 eCompare == POS_OUTSIDE )
525             {
526 
527                 SwRedline* pNewRedline = new SwRedline( *pCurrent );
528 			    *pNewRedline->End() = *pStart;
529 			    *pCurrent->Start() = *pStart;
530                 pDoc->AppendRedline( pNewRedline, true );
531             }
532 
533             // split end, if necessary
534             if( eCompare == POS_OVERLAP_BEHIND  ||
535                 eCompare == POS_OUTSIDE )
536             {
537                 SwRedline* pNewRedline = new SwRedline( *pCurrent );
538 	    		*pNewRedline->Start() = *pEnd;
539 		    	*pCurrent->End() = *pEnd;
540                 pDoc->AppendRedline( pNewRedline, true );
541             }
542 
543             // save the current redline
544             _SaveRedline* pSave = new _SaveRedline( pCurrent, *pStart );
545             rArr.C40_INSERT( _SaveRedline, pSave, rArr.Count() );
546         }
547     }
548 
549     // restore old redline mode
550     pDoc->SetRedlineMode_intern( eOld );
551 }
552 
553 void lcl_RestoreRedlines( SwDoc* pDoc, const SwPosition& rPos, _SaveRedlines& rArr )
554 {
555 	RedlineMode_t eOld = pDoc->GetRedlineMode();
556 	pDoc->SetRedlineMode_intern( (RedlineMode_t)(( eOld & ~nsRedlineMode_t::REDLINE_IGNORE) | nsRedlineMode_t::REDLINE_ON ));
557 
558 	for( sal_uInt16 n = 0; n < rArr.Count(); ++n )
559 	{
560 		_SaveRedline* pSave = rArr[ n ];
561 		pSave->SetPos( rPos );
562 		pDoc->AppendRedline( pSave->pRedl, true );
563 	}
564 
565 	pDoc->SetRedlineMode_intern( eOld );
566 }
567 
568 
569 void lcl_SaveRedlines( const SwNodeRange& rRg, _SaveRedlines& rArr )
570 {
571 	SwDoc* pDoc = rRg.aStart.GetNode().GetDoc();
572 	sal_uInt16 nRedlPos;
573 	SwPosition aSrchPos( rRg.aStart ); aSrchPos.nNode--;
574 	aSrchPos.nContent.Assign( aSrchPos.nNode.GetNode().GetCntntNode(), 0 );
575 	if( pDoc->GetRedline( aSrchPos, &nRedlPos ) && nRedlPos )
576 		--nRedlPos;
577 	else if( nRedlPos >= pDoc->GetRedlineTbl().Count() )
578 		return ;
579 
580 	RedlineMode_t eOld = pDoc->GetRedlineMode();
581 	pDoc->SetRedlineMode_intern( (RedlineMode_t)(( eOld & ~nsRedlineMode_t::REDLINE_IGNORE) | nsRedlineMode_t::REDLINE_ON ));
582 	SwRedlineTbl& rRedlTbl = (SwRedlineTbl&)pDoc->GetRedlineTbl();
583 
584 	do {
585 		SwRedline* pTmp = rRedlTbl[ nRedlPos ];
586 
587 		const SwPosition* pRStt = pTmp->Start(),
588 						* pREnd = pTmp->GetMark() == pRStt
589 							? pTmp->GetPoint() : pTmp->GetMark();
590 
591 		if( pRStt->nNode < rRg.aStart )
592 		{
593 			if( pREnd->nNode > rRg.aStart && pREnd->nNode < rRg.aEnd )
594 			{
595 				// Kopie erzeugen und Ende vom Original ans Ende des
596 				// MoveBereiches setzen. Die Kopie wird mit verschoben
597 				SwRedline* pNewRedl = new SwRedline( *pTmp );
598 				SwPosition* pTmpPos = pNewRedl->Start();
599 				pTmpPos->nNode = rRg.aStart;
600 				pTmpPos->nContent.Assign(
601 							pTmpPos->nNode.GetNode().GetCntntNode(), 0 );
602 
603 				_SaveRedline* pSave = new _SaveRedline( pNewRedl, rRg.aStart );
604 //				rArr.Insert( pSave, rArr.Count() );
605 				rArr.C40_INSERT( _SaveRedline, pSave, rArr.Count() );
606 
607 				pTmpPos = pTmp->End();
608 				pTmpPos->nNode = rRg.aEnd;
609 				pTmpPos->nContent.Assign(
610 							pTmpPos->nNode.GetNode().GetCntntNode(), 0 );
611 			}
612 			else if( pREnd->nNode == rRg.aStart )
613 			{
614 				SwPosition* pTmpPos = pTmp->End();
615 				pTmpPos->nNode = rRg.aEnd;
616 				pTmpPos->nContent.Assign(
617 							pTmpPos->nNode.GetNode().GetCntntNode(), 0 );
618 			}
619 		}
620 		else if( pRStt->nNode < rRg.aEnd )
621 		{
622 			rRedlTbl.Remove( nRedlPos-- );
623 			if( pREnd->nNode < rRg.aEnd ||
624 				( pREnd->nNode == rRg.aEnd && !pREnd->nContent.GetIndex()) )
625 			{
626 				// gesamt verschieben
627 				_SaveRedline* pSave = new _SaveRedline( pTmp, rRg.aStart );
628 //				rArr.Insert( pSave, rArr.Count() );
629 				rArr.C40_INSERT( _SaveRedline, pSave, rArr.Count() );
630 			}
631 			else
632 			{
633 				// aufsplitten
634 				SwRedline* pNewRedl = new SwRedline( *pTmp );
635 				SwPosition* pTmpPos = pNewRedl->End();
636 				pTmpPos->nNode = rRg.aEnd;
637 				pTmpPos->nContent.Assign(
638 							pTmpPos->nNode.GetNode().GetCntntNode(), 0 );
639 
640 				_SaveRedline* pSave = new _SaveRedline( pNewRedl, rRg.aStart );
641 //				rArr.Insert( pSave, rArr.Count() );
642 				rArr.C40_INSERT( _SaveRedline, pSave, rArr.Count() );
643 
644 				pTmpPos = pTmp->Start();
645 				pTmpPos->nNode = rRg.aEnd;
646 				pTmpPos->nContent.Assign(
647 							pTmpPos->nNode.GetNode().GetCntntNode(), 0 );
648 				pDoc->AppendRedline( pTmp, true );
649 			}
650 		}
651 		else
652 			break;
653 
654 	} while( ++nRedlPos < pDoc->GetRedlineTbl().Count() );
655 	pDoc->SetRedlineMode_intern( eOld );
656 }
657 
658 void lcl_RestoreRedlines( SwDoc* pDoc, sal_uInt32 nInsPos, _SaveRedlines& rArr )
659 {
660 	RedlineMode_t eOld = pDoc->GetRedlineMode();
661 	pDoc->SetRedlineMode_intern( (RedlineMode_t)(( eOld & ~nsRedlineMode_t::REDLINE_IGNORE) | nsRedlineMode_t::REDLINE_ON ));
662 
663 	for( sal_uInt16 n = 0; n < rArr.Count(); ++n )
664 	{
665 		_SaveRedline* pSave = rArr[ n ];
666 		pSave->SetPos( nInsPos );
667 		pDoc->AppendRedline( pSave->pRedl, true );
668 	}
669 
670 	pDoc->SetRedlineMode_intern( eOld );
671 }
672 
673 // ------------------------------------------------------------------------
674 // #i59534: Redo of insertion of multiple text nodes runs into trouble
675 // because of unnecessary expanded redlines
676 // From now on this class saves the redline positions of all redlines which ends exact at the
677 // insert position (node _and_ content index)
678 
679 _SaveRedlEndPosForRestore::_SaveRedlEndPosForRestore( const SwNodeIndex& rInsIdx, xub_StrLen nCnt )
680 	: pSavArr( 0 ), pSavIdx( 0 ), nSavCntnt( nCnt )
681 {
682 	SwNode& rNd = rInsIdx.GetNode();
683 	SwDoc* pDest = rNd.GetDoc();
684 	if( pDest->GetRedlineTbl().Count() )
685 	{
686 		sal_uInt16 nFndPos;
687 		const SwPosition* pEnd;
688 		SwPosition aSrcPos( rInsIdx, SwIndex( rNd.GetCntntNode(), nCnt ));
689 		const SwRedline* pRedl = pDest->GetRedline( aSrcPos, &nFndPos );
690 		while( nFndPos-- && *( pEnd = ( pRedl =
691 			pDest->GetRedlineTbl()[ nFndPos ] )->End() ) == aSrcPos && *pRedl->Start() < aSrcPos )
692 		{
693 			if( !pSavArr )
694 			{
695 				pSavArr = new SvPtrarr( 2, 2 );
696 				pSavIdx = new SwNodeIndex( rInsIdx, -1 );
697 			}
698 			void* p = (void*)pEnd;
699 			pSavArr->Insert( p, pSavArr->Count() );
700 		}
701 	}
702 }
703 
704 _SaveRedlEndPosForRestore::~_SaveRedlEndPosForRestore()
705 {
706 	if( pSavArr )
707 	{
708 		delete pSavArr;
709 		delete pSavIdx;
710 	}
711 }
712 
713 void _SaveRedlEndPosForRestore::_Restore()
714 {
715 	(*pSavIdx)++;
716     SwCntntNode* pNode = pSavIdx->GetNode().GetCntntNode();
717     // If there's no content node at the remembered position, we will not restore the old position
718     // This may happen if a table (or section?) will be inserted.
719     if( pNode )
720     {
721         SwPosition aPos( *pSavIdx, SwIndex( pNode, nSavCntnt ));
722         for( sal_uInt16 n = pSavArr->Count(); n; )
723             *((SwPosition*)pSavArr->GetObject( --n )) = aPos;
724     }
725 }
726 
727 
728 // ------------------------------------------------------------------------
729 
730 // Loeschen einer vollstaendigen Section des NodesArray.
731 // Der uebergebene Node steht irgendwo in der gewuenschten Section
732 void SwDoc::DeleteSection( SwNode *pNode )
733 {
734 	ASSERT( pNode, "Kein Node uebergeben." );
735 	SwStartNode* pSttNd = pNode->IsStartNode() ? (SwStartNode*)pNode
736 											   : pNode->StartOfSectionNode();
737 	SwNodeIndex aSttIdx( *pSttNd ), aEndIdx( *pNode->EndOfSectionNode() );
738 
739 	// dann loesche mal alle Fly's, text::Bookmarks, ...
740 	DelFlyInRange( aSttIdx, aEndIdx );
741 	DeleteRedline( *pSttNd, true, USHRT_MAX );
742 	_DelBookmarks(aSttIdx, aEndIdx);
743 
744 	{
745 		// alle Crsr/StkCrsr/UnoCrsr aus dem Loeschbereich verschieben
746 		SwNodeIndex aMvStt( aSttIdx, 1 );
747 		CorrAbs( aMvStt, aEndIdx, SwPosition( aSttIdx ), sal_True );
748 	}
749 
750 	GetNodes().DelNodes( aSttIdx, aEndIdx.GetIndex() - aSttIdx.GetIndex() + 1 );
751 }
752 
753 
754 void SwDoc::SetModified(SwPaM &rPaM)
755 {
756     SwDataChanged aTmp( rPaM, 0 );
757     SetModified();
758 }
759 
760 /*************************************************************************
761  *				  SwDoc::Overwrite()
762  ************************************************************************/
763 
764 bool SwDoc::Overwrite( const SwPaM &rRg, const String &rStr )
765 {
766 	SwPosition& rPt = *(SwPosition*)rRg.GetPoint();
767 	if( pACEWord )					// Aufnahme in die Autokorrektur
768 	{
769 		if( 1 == rStr.Len() )
770 			pACEWord->CheckChar( rPt, rStr.GetChar( 0 ) );
771 		delete pACEWord, pACEWord = 0;
772 	}
773 
774 	SwTxtNode *pNode = rPt.nNode.GetNode().GetTxtNode();
775 	if(!pNode)
776 		return sal_False;
777 
778     if (GetIDocumentUndoRedo().DoesUndo())
779     {
780         GetIDocumentUndoRedo().ClearRedo(); // AppendUndo not always called
781     }
782 
783 	sal_uInt16 nOldAttrCnt = pNode->GetpSwpHints()
784 								? pNode->GetpSwpHints()->Count() : 0;
785 	SwDataChanged aTmp( rRg, 0 );
786 	SwIndex& rIdx = rPt.nContent;
787 	xub_StrLen nStart = 0;
788 
789 	sal_Unicode c;
790 	String aStr;
791 
792 	sal_Bool bOldExpFlg = pNode->IsIgnoreDontExpand();
793 	pNode->SetIgnoreDontExpand( sal_True );
794 
795 	for( xub_StrLen nCnt = 0; nCnt < rStr.Len(); ++nCnt )
796 	{
797 		// hinter das Zeichen (zum aufspannen der Attribute !!)
798         nStart = rIdx.GetIndex();
799         if ( nStart < pNode->GetTxt().Len() )
800         {
801             lcl_SkipAttr( pNode, rIdx, nStart );
802         }
803 		c = rStr.GetChar( nCnt );
804         if (GetIDocumentUndoRedo().DoesUndo())
805         {
806             bool bMerged(false);
807             if (GetIDocumentUndoRedo().DoesGroupUndo())
808             {
809                 SwUndo *const pUndo = GetUndoManager().GetLastUndo();
810                 SwUndoOverwrite *const pUndoOW(
811                     dynamic_cast<SwUndoOverwrite *>(pUndo) );
812                 if (pUndoOW)
813                 {
814                     // if CanGrouping() returns true it's already merged
815                     bMerged = pUndoOW->CanGrouping( this, rPt, c );
816                 }
817             }
818             if (!bMerged)
819             {
820                 SwUndo *const pUndoOW( new SwUndoOverwrite(this, rPt, c) );
821                 GetIDocumentUndoRedo().AppendUndo(pUndoOW);
822             }
823         }
824         else
825         {
826 			// hinter das Zeichen (zum Aufspannen der Attribute !!)
827 			if( nStart < pNode->GetTxt().Len() )
828 				rIdx++;
829             pNode->InsertText( c, rIdx, INS_EMPTYEXPAND );
830 			if( nStart+1 < rIdx.GetIndex() )
831 			{
832 				rIdx = nStart;
833                 pNode->EraseText( rIdx, 1 );
834 				rIdx++;
835 			}
836 		}
837 	}
838 	pNode->SetIgnoreDontExpand( bOldExpFlg );
839 
840 	sal_uInt16 nNewAttrCnt = pNode->GetpSwpHints()
841 								? pNode->GetpSwpHints()->Count() : 0;
842 	if( nOldAttrCnt != nNewAttrCnt )
843 	{
844 		SwUpdateAttr aHint( 0, 0, 0 );
845         pNode->ModifyBroadcast( 0, &aHint, TYPE( SwCrsrShell ) );
846 	}
847 
848     if (!GetIDocumentUndoRedo().DoesUndo() &&
849         !IsIgnoreRedline() && GetRedlineTbl().Count())
850     {
851 		SwPaM aPam( rPt.nNode, nStart, rPt.nNode, rPt.nContent.GetIndex() );
852 		DeleteRedline( aPam, true, USHRT_MAX );
853 	}
854 	else if( IsRedlineOn() )
855 	{
856         // FIXME: this redline is WRONG: there is no DELETE, and the skipped
857         // characters are also included in aPam
858 		SwPaM aPam( rPt.nNode, nStart, rPt.nNode, rPt.nContent.GetIndex() );
859 		AppendRedline( new SwRedline( nsRedlineType_t::REDLINE_INSERT, aPam ), true);
860 	}
861 
862 	SetModified();
863 	return sal_True;
864 }
865 
866 
867 bool SwDoc::MoveAndJoin( SwPaM& rPaM, SwPosition& rPos, SwMoveFlags eMvFlags )
868 {
869 	SwNodeIndex aIdx( rPaM.Start()->nNode );
870 	sal_Bool bJoinTxt = aIdx.GetNode().IsTxtNode();
871 	sal_Bool bOneNode = rPaM.GetPoint()->nNode == rPaM.GetMark()->nNode;
872 	aIdx--;				// vor den Move Bereich !!
873 
874     bool bRet = MoveRange( rPaM, rPos, eMvFlags );
875 	if( bRet && !bOneNode )
876 	{
877 		if( bJoinTxt )
878 			aIdx++;
879 		SwTxtNode * pTxtNd = aIdx.GetNode().GetTxtNode();
880 		SwNodeIndex aNxtIdx( aIdx );
881 		if( pTxtNd && pTxtNd->CanJoinNext( &aNxtIdx ) )
882 		{
883 			{   // Block wegen SwIndex in den Node !!
884 				CorrRel( aNxtIdx, SwPosition( aIdx, SwIndex( pTxtNd,
885 							pTxtNd->GetTxt().Len() ) ), 0, sal_True );
886 			}
887 			pTxtNd->JoinNext();
888 		}
889 	}
890 	return bRet;
891 }
892 
893 // mst: it seems that this is mostly used by SwDoc internals; the only
894 // way to call this from the outside seems to be the special case in
895 // SwDoc::CopyRange (but i have not managed to actually hit that case)
896 bool SwDoc::MoveRange( SwPaM& rPaM, SwPosition& rPos, SwMoveFlags eMvFlags )
897 {
898 	// keine Moves-Abfangen
899 	const SwPosition *pStt = rPaM.Start(), *pEnd = rPaM.End();
900 	if( !rPaM.HasMark() || *pStt >= *pEnd || (*pStt <= rPos && rPos < *pEnd))
901         return false;
902 
903 	// sicher die absatzgebundenen Flys, damit sie verschoben werden koennen.
904 	_SaveFlyArr aSaveFlyArr;
905 	_SaveFlyInRange( rPaM, rPos.nNode, aSaveFlyArr, 0 != ( DOC_MOVEALLFLYS & eMvFlags ) );
906 
907     // save redlines (if DOC_MOVEREDLINES is used)
908     _SaveRedlines aSaveRedl( 0, 4 );
909 	if( DOC_MOVEREDLINES & eMvFlags && GetRedlineTbl().Count() )
910 	{
911 		lcl_SaveRedlines( rPaM, aSaveRedl );
912 
913         // #i17764# unfortunately, code below relies on undos being
914         //          in a particular order, and presence of bookmarks
915         //          will change this order. Hence, we delete bookmarks
916         //          here without undo.
917         ::sw::UndoGuard const undoGuard(GetIDocumentUndoRedo());
918         _DelBookmarks(
919             pStt->nNode,
920             pEnd->nNode,
921             NULL,
922             &pStt->nContent,
923             &pEnd->nContent);
924     }
925 
926 
927 	int bUpdateFtn = sal_False;
928 	SwFtnIdxs aTmpFntIdx;
929 
930 	// falls Undo eingeschaltet, erzeuge das UndoMove-Objekt
931 	SwUndoMove * pUndoMove = 0;
932     if (GetIDocumentUndoRedo().DoesUndo())
933     {
934         GetIDocumentUndoRedo().ClearRedo();
935 		pUndoMove = new SwUndoMove( rPaM, rPos );
936         pUndoMove->SetMoveRedlines( eMvFlags == DOC_MOVEREDLINES );
937 	}
938 	else
939 	{
940 		bUpdateFtn = lcl_SaveFtn( pStt->nNode, pEnd->nNode, rPos.nNode,
941 									GetFtnIdxs(), aTmpFntIdx,
942 									&pStt->nContent, &pEnd->nContent );
943 	}
944 
945 	sal_Bool bSplit = sal_False;
946     SwPaM aSavePam( rPos, rPos );
947 
948 	// stelle den SPoint an den Anfang vom Bereich (Definition)
949 	if( rPaM.GetPoint() == pEnd )
950 		rPaM.Exchange();
951 
952 	// in der EditShell wird nach dem Move ein JoinNext erzeugt, wenn
953 	// vor und nach dem Move ein Text-Node steht.
954 	SwTxtNode* pSrcNd = rPaM.GetPoint()->nNode.GetNode().GetTxtNode();
955 	sal_Bool bCorrSavePam = pSrcNd && pStt->nNode != pEnd->nNode;
956 
957 	// werden ein oder mehr TextNodes bewegt, so wird
958 	// im SwNodes::Move ein SplitNode erzeugt. Dieser Updated aber nicht
959 	// den Cursor. Um das zu verhindern, wird hier ein TextNode angelegt,
960 	// um die Updaterei der Indizies zu erhalten. Nach dem Move wird
961 	// evt. der Node geloescht.
962 
963 	SwTxtNode * pTNd = rPos.nNode.GetNode().GetTxtNode();
964 	if( pTNd && rPaM.GetPoint()->nNode != rPaM.GetMark()->nNode &&
965 		( rPos.nContent.GetIndex() || ( pTNd->Len() && bCorrSavePam  )) )
966 	{
967 		bSplit = sal_True;
968 		xub_StrLen nMkCntnt = rPaM.GetMark()->nContent.GetIndex();
969 
970 		SvULongs aBkmkArr( 15, 15 );
971 		_SaveCntntIdx( this, rPos.nNode.GetIndex(), rPos.nContent.GetIndex(),
972 						aBkmkArr, SAVEFLY_SPLIT );
973 
974         pTNd = static_cast<SwTxtNode*>(pTNd->SplitCntntNode( rPos ));
975 
976 		if( aBkmkArr.Count() )
977 			_RestoreCntntIdx( this, aBkmkArr, rPos.nNode.GetIndex()-1, 0, sal_True );
978 
979 		// jetzt noch den Pam berichtigen !!
980 		if( rPos.nNode == rPaM.GetMark()->nNode )
981 		{
982 			rPaM.GetMark()->nNode = rPos.nNode.GetIndex()-1;
983 			rPaM.GetMark()->nContent.Assign( pTNd, nMkCntnt );
984 		}
985 	}
986 
987 	// setze den Pam um einen "Inhalt" zurueck; dadurch steht er immer
988 	// ausserhalb des manipulierten Bereiches. Falls kein Inhalt mehr vor-
989 	// handen, dann auf den StartNode (es ist immer einer vorhanden !!!)
990     sal_Bool bNullCntnt = !aSavePam.Move( fnMoveBackward, fnGoCntnt );
991 	if( bNullCntnt )
992     {
993         aSavePam.GetPoint()->nNode--;
994     }
995 
996     // kopiere alle Bookmarks, die im Move Bereich stehen in ein
997     // Array, das alle Angaben auf die Position als Offset speichert.
998     // Die neue Zuordung erfolgt nach dem Moven.
999     ::std::vector< ::sw::mark::SaveBookmark> aSaveBkmks;
1000     _DelBookmarks(
1001         pStt->nNode,
1002         pEnd->nNode,
1003         &aSaveBkmks,
1004         &pStt->nContent,
1005         &pEnd->nContent);
1006 
1007 	// falls durch die vorherigen Loeschungen (z.B. der Fussnoten) kein
1008 	// Bereich mehr existiert, ist das immernoch ein gueltiger Move!
1009 	if( *rPaM.GetPoint() != *rPaM.GetMark() )
1010 	{
1011         // now do the actual move
1012         GetNodes().MoveRange( rPaM, rPos, GetNodes() );
1013 
1014         // after a MoveRange() the Mark is deleted
1015         if ( rPaM.HasMark() ) // => no Move occurred!
1016         {
1017 			delete pUndoMove;
1018             return false;
1019         }
1020     }
1021 	else
1022 		rPaM.DeleteMark();
1023 
1024     ASSERT( *aSavePam.GetMark() == rPos ||
1025             ( aSavePam.GetMark()->nNode.GetNode().GetCntntNode() == NULL ),
1026 			"PaM wurde nicht verschoben, am Anfang/Ende keine ContentNodes?" );
1027     *aSavePam.GetMark() = rPos;
1028 
1029 	rPaM.SetMark();			// um den neuen Bereich eine Sel. aufspannen
1030     pTNd = aSavePam.GetNode()->GetTxtNode();
1031     if (GetIDocumentUndoRedo().DoesUndo())
1032     {
1033 		// korrigiere erstmal den Content vom SavePam
1034 		if( bNullCntnt )
1035         {
1036             aSavePam.GetPoint()->nContent = 0;
1037         }
1038 
1039 		// die Methode SwEditShell::Move() fuegt nach dem Move den Text-Node
1040 		// zusammen, in dem der rPaM steht. Wurde der Inhalt nach hinten
1041 		// geschoben und liegt der SPoint vom SavePam im naechsten Node, so
1042 		// muss beim Speichern vom Undo-Object das beachtet werden !!
1043         SwTxtNode * pPamTxtNd = 0;
1044 
1045 		// wird ans SwUndoMove weitergegeben, das dann beim Undo JoinNext
1046 		// aufruft. (falls es hier nicht moeglich ist).
1047 		sal_Bool bJoin = bSplit && pTNd;
1048 		bCorrSavePam = bCorrSavePam &&
1049 						0 != ( pPamTxtNd = rPaM.GetNode()->GetTxtNode() )
1050 						&& pPamTxtNd->CanJoinNext()
1051                         && (*rPaM.GetPoint() <= *aSavePam.GetPoint());
1052 
1053 		// muessen am SavePam 2 Nodes zusammengefasst werden ??
1054 		if( bJoin && pTNd->CanJoinNext() )
1055 		{
1056 			pTNd->JoinNext();
1057 			// kein temp. sdbcx::Index bei &&
1058 			// es sollten wohl nur die Indexwerte verglichen werden.
1059 			if( bCorrSavePam && rPaM.GetPoint()->nNode.GetIndex()+1 ==
1060                                 aSavePam.GetPoint()->nNode.GetIndex() )
1061             {
1062                 aSavePam.GetPoint()->nContent += pPamTxtNd->Len();
1063             }
1064 			bJoin = sal_False;
1065 		}
1066 //		else if( !bCorrSavePam && !pSavePam->Move( fnMoveForward, fnGoCntnt ))
1067         else if ( !aSavePam.Move( fnMoveForward, fnGoCntnt ) )
1068         {
1069             aSavePam.GetPoint()->nNode++;
1070         }
1071 
1072 		// zwischen SPoint und GetMark steht jetzt der neu eingefuegte Bereich
1073         pUndoMove->SetDestRange( aSavePam, *rPaM.GetPoint(),
1074 									bJoin, bCorrSavePam );
1075         GetIDocumentUndoRedo().AppendUndo( pUndoMove );
1076     }
1077     else
1078     {
1079         bool bRemove = true;
1080 		// muessen am SavePam 2 Nodes zusammengefasst werden ??
1081 		if( bSplit && pTNd )
1082 		{
1083 			if( pTNd->CanJoinNext())
1084             {
1085                 // --> OD 2009-08-20 #i100466#
1086                 // Always join next, because <pTNd> has to stay as it is.
1087                 // A join previous from its next would more or less delete <pTNd>
1088                 pTNd->JoinNext();
1089                 // <--
1090                 bRemove = false;
1091             }
1092 		}
1093 		if( bNullCntnt )
1094         {
1095             aSavePam.GetPoint()->nNode++;
1096             aSavePam.GetPoint()->nContent.Assign( aSavePam.GetCntntNode(), 0 );
1097         }
1098 		else if( bRemove ) // No move forward after joining with next paragraph
1099         {
1100             aSavePam.Move( fnMoveForward, fnGoCntnt );
1101         }
1102 	}
1103 
1104     // setze jetzt wieder die text::Bookmarks in das Dokument
1105     *rPaM.GetMark() = *aSavePam.Start();
1106     for(
1107         ::std::vector< ::sw::mark::SaveBookmark>::iterator pBkmk = aSaveBkmks.begin();
1108         pBkmk != aSaveBkmks.end();
1109         ++pBkmk)
1110         pBkmk->SetInDoc(
1111             this,
1112             rPaM.GetMark()->nNode,
1113             &rPaM.GetMark()->nContent);
1114     *rPaM.GetPoint() = *aSavePam.End();
1115 
1116 	// verschiebe die Flys an die neue Position
1117 	_RestFlyInRange( aSaveFlyArr, rPaM.Start()->nNode, &(rPos.nNode) );
1118 
1119     // restore redlines (if DOC_MOVEREDLINES is used)
1120 	if( aSaveRedl.Count() )
1121     {
1122         lcl_RestoreRedlines( this, *aSavePam.Start(), aSaveRedl );
1123     }
1124 
1125     if( bUpdateFtn )
1126 	{
1127 		if( aTmpFntIdx.Count() )
1128 		{
1129 			GetFtnIdxs().Insert( &aTmpFntIdx );
1130 			aTmpFntIdx.Remove( sal_uInt16( 0 ), aTmpFntIdx.Count() );
1131 		}
1132 
1133 		GetFtnIdxs().UpdateAllFtn();
1134 	}
1135 
1136 	SetModified();
1137     return true;
1138 }
1139 
1140 bool SwDoc::MoveNodeRange( SwNodeRange& rRange, SwNodeIndex& rPos,
1141         SwMoveFlags eMvFlags )
1142 {
1143 	// bewegt alle Nodes an die neue Position. Dabei werden die
1144 	// text::Bookmarks mit verschoben !! (zur Zeit ohne Undo)
1145 
1146 	// falls durchs Move Fussnoten in den Sonderbereich kommen sollten,
1147 	// dann entferne sie jetzt.
1148 	//JP 13.07.95:
1149 	// ansonsten bei allen Fussnoten, die verschoben werden, die Frames
1150 	// loeschen und nach dem Move wieder aufbauen lassen (Fussnoten koennen
1151 	// die Seite wechseln). Zusaetzlich muss natuerlich die Sortierung
1152 	// der FtnIdx-Array wieder korrigiert werden.
1153 
1154 	int bUpdateFtn = sal_False;
1155 	SwFtnIdxs aTmpFntIdx;
1156 
1157 	SwUndoMove* pUndo = 0;
1158     if ((DOC_CREATEUNDOOBJ & eMvFlags ) && GetIDocumentUndoRedo().DoesUndo())
1159     {
1160 		pUndo = new SwUndoMove( this, rRange, rPos );
1161     }
1162     else
1163     {
1164 		bUpdateFtn = lcl_SaveFtn( rRange.aStart, rRange.aEnd, rPos,
1165 									GetFtnIdxs(), aTmpFntIdx );
1166     }
1167 
1168 	_SaveRedlines aSaveRedl( 0, 4 );
1169 	SvPtrarr aSavRedlInsPosArr( 0, 4 );
1170 	if( DOC_MOVEREDLINES & eMvFlags && GetRedlineTbl().Count() )
1171 	{
1172 		lcl_SaveRedlines( rRange, aSaveRedl );
1173 
1174 		// suche alle Redlines, die an der InsPos aufhoeren. Diese muessen
1175 		// nach dem Move wieder an die "alte" Position verschoben werden
1176 		sal_uInt16 nRedlPos = GetRedlinePos( rPos.GetNode(), USHRT_MAX );
1177 		if( USHRT_MAX != nRedlPos )
1178 		{
1179 			const SwPosition *pRStt, *pREnd;
1180 			do {
1181 				SwRedline* pTmp = GetRedlineTbl()[ nRedlPos ];
1182 				pRStt = pTmp->Start();
1183 				pREnd = pTmp->End();
1184 				if( pREnd->nNode == rPos && pRStt->nNode < rPos )
1185 				{
1186 					void* p = pTmp;
1187 					aSavRedlInsPosArr.Insert( p, aSavRedlInsPosArr.Count() );
1188 				}
1189 			} while( pRStt->nNode < rPos && ++nRedlPos < GetRedlineTbl().Count());
1190 		}
1191 	}
1192 
1193     // kopiere alle Bookmarks, die im Move Bereich stehen in ein
1194     // Array, das alle Angaben auf die Position als Offset speichert.
1195     // Die neue Zuordung erfolgt nach dem Moven.
1196     ::std::vector< ::sw::mark::SaveBookmark> aSaveBkmks;
1197     _DelBookmarks(rRange.aStart, rRange.aEnd, &aSaveBkmks);
1198 
1199 	// sicher die absatzgebundenen Flys, damit verschoben werden koennen.
1200 	_SaveFlyArr aSaveFlyArr;
1201 	if( GetSpzFrmFmts()->Count() )
1202 		_SaveFlyInRange( rRange, aSaveFlyArr );
1203 
1204 	// vor die Position setzen, damit er nicht weitergeschoben wird
1205 	SwNodeIndex aIdx( rPos, -1 );
1206 
1207 	SwNodeIndex* pSaveInsPos = 0;
1208 	if( pUndo )
1209 		pSaveInsPos = new SwNodeIndex( rRange.aStart, -1 );
1210 
1211 	// verschiebe die Nodes
1212     sal_Bool bNoDelFrms = 0 != (DOC_NO_DELFRMS & eMvFlags);
1213 	if( GetNodes()._MoveNodes( rRange, GetNodes(), rPos, !bNoDelFrms ) )
1214 	{
1215 		aIdx++;		// wieder auf alte Position
1216 		if( pSaveInsPos )
1217 			(*pSaveInsPos)++;
1218 	}
1219 	else
1220 	{
1221 		aIdx = rRange.aStart;
1222 		delete pUndo, pUndo = 0;
1223 	}
1224 
1225 	// verschiebe die Flys an die neue Position
1226 	if( aSaveFlyArr.Count() )
1227 		_RestFlyInRange( aSaveFlyArr, aIdx, NULL );
1228 
1229 	// setze jetzt wieder die text::Bookmarks in das Dokument
1230     for(
1231         ::std::vector< ::sw::mark::SaveBookmark>::iterator pBkmk = aSaveBkmks.begin();
1232         pBkmk != aSaveBkmks.end();
1233         ++pBkmk)
1234 		pBkmk->SetInDoc(this, aIdx);
1235 
1236 	if( aSavRedlInsPosArr.Count() )
1237 	{
1238 		SwNode* pNewNd = &aIdx.GetNode();
1239 		for( sal_uInt16 n = 0; n < aSavRedlInsPosArr.Count(); ++n )
1240 		{
1241 			SwRedline* pTmp = (SwRedline*)aSavRedlInsPosArr[ n ];
1242 			if( USHRT_MAX != GetRedlineTbl().GetPos( pTmp ) )
1243 			{
1244 				SwPosition* pEnd = pTmp->End();
1245 				pEnd->nNode = aIdx;
1246 				pEnd->nContent.Assign( pNewNd->GetCntntNode(), 0 );
1247 			}
1248 		}
1249 	}
1250 
1251 	if( aSaveRedl.Count() )
1252 		lcl_RestoreRedlines( this, aIdx.GetIndex(), aSaveRedl );
1253 
1254 	if( pUndo )
1255     {
1256 		pUndo->SetDestRange( aIdx, rPos, *pSaveInsPos );
1257         GetIDocumentUndoRedo().AppendUndo(pUndo);
1258     }
1259 
1260 	if( pSaveInsPos )
1261 		delete pSaveInsPos;
1262 
1263 	if( bUpdateFtn )
1264 	{
1265 		if( aTmpFntIdx.Count() )
1266 		{
1267 			GetFtnIdxs().Insert( &aTmpFntIdx );
1268 			aTmpFntIdx.Remove( sal_uInt16( 0 ), aTmpFntIdx.Count() );
1269 		}
1270 
1271 		GetFtnIdxs().UpdateAllFtn();
1272 	}
1273 
1274 	SetModified();
1275 	return sal_True;
1276 }
1277 
1278 /* #107318# Convert list of ranges of whichIds to a corresponding list
1279     of whichIds*/
1280 SvUShorts * lcl_RangesToUShorts(sal_uInt16 * pRanges)
1281 {
1282     SvUShorts * pResult = new SvUShorts();
1283 
1284     int i = 0;
1285     while (pRanges[i] != 0)
1286     {
1287         ASSERT(pRanges[i+1] != 0, "malformed ranges");
1288 
1289         for (sal_uInt16 j = pRanges[i]; j < pRanges[i+1]; j++)
1290             pResult->Insert(j, pResult->Count());
1291 
1292         i += 2;
1293     }
1294 
1295     return pResult;
1296 }
1297 
1298 bool lcl_StrLenOverFlow( const SwPaM& rPam )
1299 {
1300     // If we try to merge two paragraph we have to test if afterwards
1301     // the string doesn't exceed the allowed string length
1302     bool bRet = false;
1303 	if( rPam.GetPoint()->nNode != rPam.GetMark()->nNode )
1304 	{
1305 		const SwPosition* pStt = rPam.Start(), *pEnd = rPam.End();
1306 		const SwTxtNode* pEndNd = pEnd->nNode.GetNode().GetTxtNode();
1307 		if( (0 != pEndNd) && pStt->nNode.GetNode().IsTxtNode() )
1308         {
1309             sal_uInt64 nSum = pStt->nContent.GetIndex() +
1310                 pEndNd->GetTxt().Len() - pEnd->nContent.GetIndex();
1311             if( nSum > STRING_LEN )
1312                 bRet = true;
1313         }
1314 	}
1315     return bRet;
1316 }
1317 
1318 void lcl_GetJoinFlags( SwPaM& rPam, sal_Bool& rJoinTxt, sal_Bool& rJoinPrev )
1319 {
1320     rJoinTxt = sal_False;
1321     rJoinPrev = sal_False;
1322 	if( rPam.GetPoint()->nNode != rPam.GetMark()->nNode )
1323 	{
1324 		const SwPosition* pStt = rPam.Start(), *pEnd = rPam.End();
1325 		SwTxtNode *pSttNd = pStt->nNode.GetNode().GetTxtNode();
1326         if( pSttNd )
1327         {
1328             SwTxtNode *pEndNd = pEnd->nNode.GetNode().GetTxtNode();
1329             rJoinTxt = 0 != pEndNd;
1330             if( rJoinTxt )
1331             {
1332                 bool bExchange = pStt == rPam.GetPoint();
1333                 if( !pStt->nContent.GetIndex() &&
1334                     pEndNd->GetTxt().Len() != pEnd->nContent.GetIndex() )
1335                     bExchange = !bExchange;
1336                 if( bExchange )
1337                     rPam.Exchange();
1338                 rJoinPrev = rPam.GetPoint() == pStt;
1339                 ASSERT( !pStt->nContent.GetIndex() &&
1340                     pEndNd->GetTxt().Len() != pEnd->nContent.GetIndex()
1341                     ? rPam.GetPoint()->nNode < rPam.GetMark()->nNode
1342                     : rPam.GetPoint()->nNode > rPam.GetMark()->nNode,
1343                     "lcl_GetJoinFlags");
1344             }
1345         }
1346 	}
1347 }
1348 
1349 void lcl_JoinText( SwPaM& rPam, sal_Bool bJoinPrev )
1350 {
1351 	SwNodeIndex aIdx( rPam.GetPoint()->nNode );
1352 	SwTxtNode *pTxtNd = aIdx.GetNode().GetTxtNode();
1353 	SwNodeIndex aOldIdx( aIdx );
1354 	SwTxtNode *pOldTxtNd = pTxtNd;
1355 
1356 	if( pTxtNd && pTxtNd->CanJoinNext( &aIdx ) )
1357 	{
1358 		SwDoc* pDoc = rPam.GetDoc();
1359 		if( bJoinPrev )
1360 		{
1361             // N.B.: we do not need to handle xmlids in this case, because
1362             // it is only invoked if one paragraph is completely empty
1363             // (see lcl_GetJoinFlags)
1364 			{
1365 				// falls PageBreaks geloescht / gesetzt werden, darf das
1366 				// nicht in die Undo-History aufgenommen werden !!
1367 				// (das loeschen vom Node geht auch am Undo vorbei !!!)
1368                 ::sw::UndoGuard const undoGuard(pDoc->GetIDocumentUndoRedo());
1369 
1370 				/* PageBreaks, PageDesc, ColumnBreaks */
1371 				// Sollte an der Logik zum Kopieren der PageBreak's ...
1372 				// etwas geaendert werden, muss es auch im SwUndoDelete
1373 				// geandert werden. Dort wird sich das AUTO-PageBreak
1374 				// aus dem GetMarkNode kopiert.!!!
1375 
1376 				/* Der GetMarkNode */
1377                 if( ( pTxtNd = aIdx.GetNode().GetTxtNode())->HasSwAttrSet() )
1378 				{
1379 					const SfxPoolItem* pItem;
1380 					if( SFX_ITEM_SET == pTxtNd->GetpSwAttrSet()->GetItemState(
1381 						RES_BREAK, sal_False, &pItem ) )
1382 						pTxtNd->ResetAttr( RES_BREAK );
1383                     if( pTxtNd->HasSwAttrSet() &&
1384 						SFX_ITEM_SET == pTxtNd->GetpSwAttrSet()->GetItemState(
1385 						RES_PAGEDESC, sal_False, &pItem ) )
1386 						pTxtNd->ResetAttr( RES_PAGEDESC );
1387 				}
1388 
1389 				/* Der PointNode */
1390                 if( pOldTxtNd->HasSwAttrSet() )
1391 				{
1392 					const SfxPoolItem* pItem;
1393 					SfxItemSet aSet( pDoc->GetAttrPool(), aBreakSetRange );
1394                     const SfxItemSet* pSet = pOldTxtNd->GetpSwAttrSet();
1395 					if( SFX_ITEM_SET == pSet->GetItemState( RES_BREAK,
1396 						sal_False, &pItem ) )
1397 						aSet.Put( *pItem );
1398 					if( SFX_ITEM_SET == pSet->GetItemState( RES_PAGEDESC,
1399 						sal_False, &pItem ) )
1400 						aSet.Put( *pItem );
1401 					if( aSet.Count() )
1402                         pTxtNd->SetAttr( aSet );
1403 				}
1404 				pOldTxtNd->FmtToTxtAttr( pTxtNd );
1405 
1406 				SvULongs aBkmkArr( 15, 15 );
1407 				::_SaveCntntIdx( pDoc, aOldIdx.GetIndex(),
1408 									pOldTxtNd->Len(), aBkmkArr );
1409 
1410 				SwIndex aAlphaIdx(pTxtNd);
1411                 pOldTxtNd->CutText( pTxtNd, aAlphaIdx, SwIndex(pOldTxtNd),
1412 									pOldTxtNd->Len() );
1413 				SwPosition aAlphaPos( aIdx, aAlphaIdx );
1414 				pDoc->CorrRel( rPam.GetPoint()->nNode, aAlphaPos, 0, sal_True );
1415 
1416 				// verschiebe noch alle Bookmarks/TOXMarks
1417 				if( aBkmkArr.Count() )
1418 					::_RestoreCntntIdx( pDoc, aBkmkArr, aIdx.GetIndex() );
1419 
1420 				// falls der uebergebene PaM nicht im Crsr-Ring steht,
1421 				// gesondert behandeln (z.B. Aufruf aus dem Auto-Format)
1422 				if( pOldTxtNd == rPam.GetBound( sal_True ).nContent.GetIdxReg() )
1423 					rPam.GetBound( sal_True ) = aAlphaPos;
1424 				if( pOldTxtNd == rPam.GetBound( sal_False ).nContent.GetIdxReg() )
1425 					rPam.GetBound( sal_False ) = aAlphaPos;
1426 			}
1427 			// jetzt nur noch den Node loeschen
1428 			pDoc->GetNodes().Delete( aOldIdx, 1 );
1429 		}
1430 		else
1431 		{
1432 			SwTxtNode* pDelNd = aIdx.GetNode().GetTxtNode();
1433 			if( pTxtNd->Len() )
1434 				pDelNd->FmtToTxtAttr( pTxtNd );
1435 			else
1436             {
1437                 /* #107318# This case was missed:
1438 
1439                    <something></something>   <-- pTxtNd
1440                    <other>ccc</other>        <-- pDelNd
1441 
1442                    <something> and <other> are paragraph
1443                    attributes. The attribute <something> stayed if not
1444                    overwritten by an attribute in "ccc". Fixed by
1445                    first resetting all character attributes in first
1446                    paragraph (pTxtNd).
1447                 */
1448                 SvUShorts * pShorts =
1449                     lcl_RangesToUShorts(aCharFmtSetRange);
1450                 pTxtNd->ResetAttr(*pShorts);
1451                 delete pShorts;
1452 
1453                 if( pDelNd->HasSwAttrSet() )
1454                 {
1455                     // nur die Zeichenattribute kopieren
1456                     SfxItemSet aTmpSet( pDoc->GetAttrPool(), aCharFmtSetRange );
1457                     aTmpSet.Put( *pDelNd->GetpSwAttrSet() );
1458                     pTxtNd->SetAttr( aTmpSet );
1459                 }
1460             }
1461 
1462 			pDoc->CorrRel( aIdx, *rPam.GetPoint(), 0, sal_True );
1463             // --> OD 2009-08-20 #i100466#
1464             // adjust given <rPam>, if it does not belong to the cursors
1465             if ( pDelNd == rPam.GetBound( sal_True ).nContent.GetIdxReg() )
1466             {
1467                 rPam.GetBound( sal_True ) = SwPosition( SwNodeIndex( *pTxtNd ), SwIndex( pTxtNd ) );
1468             }
1469             if( pDelNd == rPam.GetBound( sal_False ).nContent.GetIdxReg() )
1470             {
1471                 rPam.GetBound( sal_False ) = SwPosition( SwNodeIndex( *pTxtNd ), SwIndex( pTxtNd ) );
1472             }
1473             // <--
1474 			pTxtNd->JoinNext();
1475 		}
1476 	}
1477 }
1478 
1479 static void
1480 lcl_CalcBreaks( ::std::vector<xub_StrLen> & rBreaks, SwPaM const & rPam )
1481 {
1482     SwTxtNode const * const pTxtNode(
1483             rPam.End()->nNode.GetNode().GetTxtNode() );
1484     if (!pTxtNode)
1485         return; // left-overlap only possible at end of selection...
1486 
1487     const xub_StrLen nStart(rPam.Start()->nContent.GetIndex());
1488     const xub_StrLen nEnd  (rPam.End  ()->nContent.GetIndex());
1489     if (nEnd == pTxtNode->Len())
1490         return; // paragraph selected until the end
1491 
1492     for (xub_StrLen i = nStart; i < nEnd; ++i)
1493     {
1494         const sal_Unicode c(pTxtNode->GetTxt().GetChar(i));
1495         if ((CH_TXTATR_INWORD == c) || (CH_TXTATR_BREAKWORD == c))
1496         {
1497             SwTxtAttr const * const pAttr( pTxtNode->GetTxtAttrForCharAt(i) );
1498             if (pAttr && pAttr->GetEnd() && (*pAttr->GetEnd() > nEnd))
1499             {
1500                 ASSERT(pAttr->HasDummyChar(), "GetTxtAttrForCharAt broken?");
1501                 rBreaks.push_back(i);
1502             }
1503         }
1504     }
1505 }
1506 
1507 bool lcl_DoWithBreaks(SwDoc & rDoc, SwPaM & rPam,
1508         bool (SwDoc::*pFunc)(SwPaM&, bool), const bool bForceJoinNext = false)
1509 {
1510     ::std::vector<xub_StrLen> Breaks;
1511 
1512     lcl_CalcBreaks(Breaks, rPam);
1513 
1514     if (!Breaks.size())
1515     {
1516         return (rDoc.*pFunc)(rPam, bForceJoinNext);
1517     }
1518 
1519     // N.B.: deletion must be split into several parts if the text node
1520     // contains a text attribute with end and with dummy character
1521     // and the selection does not contain the text attribute completely,
1522     // but overlaps its start (left), where the dummy character is.
1523 
1524     SwPosition const & rSelectionEnd( *rPam.End() );
1525 
1526     bool bRet( true );
1527     // iterate from end to start, to avoid invalidating the offsets!
1528     ::std::vector<xub_StrLen>::reverse_iterator iter( Breaks.rbegin() );
1529     SwPaM aPam( rSelectionEnd, rSelectionEnd ); // end node!
1530     SwPosition & rEnd( *aPam.End() );
1531     SwPosition & rStart( *aPam.Start() );
1532 
1533     while (iter != Breaks.rend())
1534     {
1535         rStart.nContent = *iter + 1;
1536         if (rEnd.nContent > rStart.nContent) // check if part is empty
1537         {
1538             bRet &= (rDoc.*pFunc)(aPam, bForceJoinNext);
1539         }
1540         rEnd.nContent = *iter;
1541         ++iter;
1542     }
1543 
1544     rStart = *rPam.Start(); // set to original start
1545     if (rEnd.nContent > rStart.nContent) // check if part is empty
1546     {
1547         bRet &= (rDoc.*pFunc)(aPam, bForceJoinNext);
1548     }
1549 
1550     return bRet;
1551 }
1552 
1553 
1554 bool SwDoc::DeleteAndJoinWithRedlineImpl( SwPaM & rPam, const bool )
1555 {
1556     ASSERT( IsRedlineOn(), "DeleteAndJoinWithRedline: redline off" );
1557 
1558     {
1559 		SwUndoRedlineDelete* pUndo = 0;
1560 		RedlineMode_t eOld = GetRedlineMode();
1561 		checkRedlining(eOld);
1562         if (GetIDocumentUndoRedo().DoesUndo())
1563         {
1564 
1565     //JP 06.01.98: MUSS noch optimiert werden!!!
1566     SetRedlineMode(
1567 		   (RedlineMode_t)(nsRedlineMode_t::REDLINE_ON | nsRedlineMode_t::REDLINE_SHOW_INSERT | nsRedlineMode_t::REDLINE_SHOW_DELETE ));
1568 
1569             GetIDocumentUndoRedo().StartUndo(UNDO_EMPTY, NULL);
1570             pUndo = new SwUndoRedlineDelete( rPam, UNDO_DELETE );
1571             GetIDocumentUndoRedo().AppendUndo(pUndo);
1572         }
1573         if( *rPam.GetPoint() != *rPam.GetMark() )
1574             AppendRedline( new SwRedline( nsRedlineType_t::REDLINE_DELETE, rPam ), true);
1575         SetModified();
1576 
1577 		if( pUndo )
1578 		{
1579             GetIDocumentUndoRedo().EndUndo(UNDO_EMPTY, NULL);
1580             // ??? why the hell is the AppendUndo not below the
1581             // CanGrouping, so this hideous cleanup wouldn't be necessary?
1582             // bah, this is redlining, probably changing this would break it...
1583             if (GetIDocumentUndoRedo().DoesGroupUndo())
1584             {
1585                 SwUndo *const pLastUndo( GetUndoManager().GetLastUndo() );
1586                 SwUndoRedlineDelete *const pUndoRedlineDel(
1587                         dynamic_cast<SwUndoRedlineDelete*>(pLastUndo) );
1588                 if (pUndoRedlineDel)
1589                 {
1590                     bool const bMerged = pUndoRedlineDel->CanGrouping(*pUndo);
1591                     if (bMerged)
1592                     {
1593                         ::sw::UndoGuard const undoGuard(GetIDocumentUndoRedo());
1594                         SwUndo const*const pDeleted =
1595                             GetUndoManager().RemoveLastUndo();
1596                         OSL_ENSURE(pDeleted == pUndo,
1597                             "DeleteAndJoinWithRedlineImpl: "
1598                             "undo removed is not undo inserted?");
1599                         delete pDeleted;
1600                     }
1601                 }
1602             }
1603 //JP 06.01.98: MUSS noch optimiert werden!!!
1604 SetRedlineMode( eOld );
1605         }
1606         return true;
1607     }
1608 }
1609 
1610 bool SwDoc::DeleteAndJoinImpl( SwPaM & rPam,
1611                                const bool bForceJoinNext )
1612 {
1613 	sal_Bool bJoinTxt, bJoinPrev;
1614 	lcl_GetJoinFlags( rPam, bJoinTxt, bJoinPrev );
1615     // --> OD 2009-08-20 #i100466#
1616     if ( bForceJoinNext )
1617     {
1618         bJoinPrev = sal_False;
1619     }
1620     // <--
1621 	{
1622         bool const bSuccess( DeleteRangeImpl( rPam ) );
1623         if (!bSuccess)
1624             return false;
1625 	}
1626 
1627 	if( bJoinTxt )
1628     {
1629 		lcl_JoinText( rPam, bJoinPrev );
1630     }
1631 
1632     return true;
1633 }
1634 
1635 bool SwDoc::DeleteRangeImpl(SwPaM & rPam, const bool)
1636 {
1637     // move all cursors out of the deleted range.
1638     // but first copy the given PaM, because it could be a cursor that
1639     // would be moved!
1640     SwPaM aDelPam( *rPam.GetMark(), *rPam.GetPoint() );
1641     ::PaMCorrAbs( aDelPam, *aDelPam.GetPoint() );
1642 
1643     bool const bSuccess( DeleteRangeImplImpl( aDelPam ) );
1644     if (bSuccess)
1645     {   // now copy position from temp copy to given PaM
1646         *rPam.GetPoint() = *aDelPam.GetPoint();
1647     }
1648 
1649     return bSuccess;
1650 }
1651 
1652 bool SwDoc::DeleteRangeImplImpl(SwPaM & rPam)
1653 {
1654 	SwPosition *pStt = (SwPosition*)rPam.Start(), *pEnd = (SwPosition*)rPam.End();
1655 
1656 	if( !rPam.HasMark() || *pStt >= *pEnd )
1657         return false;
1658 
1659 	if( pACEWord )
1660 	{
1661 		// ggfs. das gesicherte Word fuer die Ausnahme
1662 		if( pACEWord->IsDeleted() ||  pStt->nNode != pEnd->nNode ||
1663 			pStt->nContent.GetIndex() + 1 != pEnd->nContent.GetIndex() ||
1664 			!pACEWord->CheckDelChar( *pStt ))
1665 			delete pACEWord, pACEWord = 0;
1666 	}
1667 
1668 	{
1669 		// loesche alle leeren TextHints an der Mark-Position
1670 		SwTxtNode* pTxtNd = rPam.GetMark()->nNode.GetNode().GetTxtNode();
1671 		SwpHints* pHts;
1672 		if( pTxtNd &&  0 != ( pHts = pTxtNd->GetpSwpHints()) && pHts->Count() )
1673 		{
1674 			const xub_StrLen *pEndIdx;
1675 			xub_StrLen nMkCntPos = rPam.GetMark()->nContent.GetIndex();
1676 			for( sal_uInt16 n = pHts->Count(); n; )
1677 			{
1678 				const SwTxtAttr* pAttr = (*pHts)[ --n ];
1679 				if( nMkCntPos > *pAttr->GetStart() )
1680 					break;
1681 
1682 				if( nMkCntPos == *pAttr->GetStart() &&
1683 					0 != (pEndIdx = pAttr->GetEnd()) &&
1684 					*pEndIdx == *pAttr->GetStart() )
1685 					pTxtNd->DestroyAttr( pHts->Cut( n ) );
1686 			}
1687 		}
1688 	}
1689 
1690 	{
1691 		// Bug 26675:	DataChanged vorm loeschen verschicken, dann bekommt
1692 		//			man noch mit, welche Objecte sich im Bereich befinden.
1693 		//			Danach koennen sie vor/hinter der Position befinden.
1694 		SwDataChanged aTmp( rPam, 0 );
1695 	}
1696 
1697 
1698     if (GetIDocumentUndoRedo().DoesUndo())
1699     {
1700         GetIDocumentUndoRedo().ClearRedo();
1701         bool bMerged(false);
1702         if (GetIDocumentUndoRedo().DoesGroupUndo())
1703         {
1704             SwUndo *const pLastUndo( GetUndoManager().GetLastUndo() );
1705             SwUndoDelete *const pUndoDelete(
1706                     dynamic_cast<SwUndoDelete *>(pLastUndo) );
1707             if (pUndoDelete)
1708             {
1709                 bMerged = pUndoDelete->CanGrouping( this, rPam );
1710                 // if CanGrouping() returns true it's already merged
1711             }
1712         }
1713         if (!bMerged)
1714         {
1715             GetIDocumentUndoRedo().AppendUndo( new SwUndoDelete( rPam ) );
1716         }
1717 
1718 		SetModified();
1719 
1720         return true;
1721     }
1722 
1723 	if( !IsIgnoreRedline() && GetRedlineTbl().Count() )
1724 		DeleteRedline( rPam, true, USHRT_MAX );
1725 
1726     // loesche und verschiebe erstmal alle "Fly's am Absatz", die in der
1727     // Selection liegen
1728     DelFlyInRange(rPam.GetMark()->nNode, rPam.GetPoint()->nNode);
1729     _DelBookmarks(
1730         pStt->nNode,
1731         pEnd->nNode,
1732         NULL,
1733         &pStt->nContent,
1734         &pEnd->nContent);
1735 
1736 	SwNodeIndex aSttIdx( pStt->nNode );
1737 	SwCntntNode * pCNd = aSttIdx.GetNode().GetCntntNode();
1738 
1739 	do {		// middle checked loop!
1740 		if( pCNd )
1741 		{
1742             SwTxtNode * pStartTxtNode( pCNd->GetTxtNode() );
1743             if ( pStartTxtNode )
1744 			{
1745 				// verschiebe jetzt noch den Inhalt in den neuen Node
1746 				sal_Bool bOneNd = pStt->nNode == pEnd->nNode;
1747 				xub_StrLen nLen = ( bOneNd ? pEnd->nContent.GetIndex()
1748 										   : pCNd->Len() )
1749 										- pStt->nContent.GetIndex();
1750 
1751 				// falls schon leer, dann nicht noch aufrufen
1752 				if( nLen )
1753                 {
1754                     pStartTxtNode->EraseText( pStt->nContent, nLen );
1755 
1756                     if( !pStartTxtNode->Len() )
1757                     {
1758                 // METADATA: remove reference if empty (consider node deleted)
1759                         pStartTxtNode->RemoveMetadataReference();
1760                     }
1761                 }
1762 
1763 				if( bOneNd )		// das wars schon
1764 					break;
1765 
1766 				aSttIdx++;
1767 			}
1768 			else
1769 			{
1770 				// damit beim loeschen keine Indizies mehr angemeldet sind,
1771 				// wird hier der SwPaM aus dem Content entfernt !!
1772 				pStt->nContent.Assign( 0, 0 );
1773 			}
1774 		}
1775 
1776 		pCNd = pEnd->nNode.GetNode().GetCntntNode();
1777 		if( pCNd )
1778 		{
1779             SwTxtNode * pEndTxtNode( pCNd->GetTxtNode() );
1780             if( pEndTxtNode )
1781 			{
1782 				// falls schon leer, dann nicht noch aufrufen
1783 				if( pEnd->nContent.GetIndex() )
1784 				{
1785 					SwIndex aIdx( pCNd, 0 );
1786                     pEndTxtNode->EraseText( aIdx, pEnd->nContent.GetIndex() );
1787 
1788                     if( !pEndTxtNode->Len() )
1789                     {
1790                 // METADATA: remove reference if empty (consider node deleted)
1791                         pEndTxtNode->RemoveMetadataReference();
1792                     }
1793 				}
1794 			}
1795 			else
1796 			{
1797 				// damit beim Loeschen keine Indizies mehr angemeldet sind,
1798 				// wird hier der SwPaM aus dem Content entfernt !!
1799 				pEnd->nContent.Assign( 0, 0 );
1800 			}
1801 		}
1802 
1803         // if the end is not a content node, delete it as well
1804         sal_uInt32 nEnde = pEnd->nNode.GetIndex();
1805         if( pCNd == NULL )
1806             nEnde++;
1807 
1808 		if( aSttIdx != nEnde )
1809 		{
1810 			// loesche jetzt die Nodes in das NodesArary
1811 			GetNodes().Delete( aSttIdx, nEnde - aSttIdx.GetIndex() );
1812 		}
1813 
1814 		// falls der Node geloescht wurde, in dem der Cursor stand, so
1815 		// muss der Content im akt. Content angemeldet werden !!!
1816 		pStt->nContent.Assign( pStt->nNode.GetNode().GetCntntNode(),
1817 								pStt->nContent.GetIndex() );
1818 
1819 		// der PaM wird korrigiert, denn falls ueber Nodegrenzen geloescht
1820 		// wurde, so stehen sie in unterschieden Nodes. Auch die Selektion
1821 		// wird aufgehoben !
1822 		*pEnd = *pStt;
1823 		rPam.DeleteMark();
1824 
1825 	} while( sal_False );
1826 
1827 	if( !IsIgnoreRedline() && GetRedlineTbl().Count() )
1828 		CompressRedlines();
1829 	SetModified();
1830 
1831     return true;
1832 }
1833 
1834 // OD 2009-08-20 #i100466#
1835 // Add handling of new optional parameter <bForceJoinNext>
1836 bool SwDoc::DeleteAndJoin( SwPaM & rPam,
1837                            const bool bForceJoinNext )
1838 {
1839     if ( lcl_StrLenOverFlow( rPam ) )
1840         return false;
1841 
1842     return lcl_DoWithBreaks( *this, rPam, (IsRedlineOn())
1843                 ? &SwDoc::DeleteAndJoinWithRedlineImpl
1844                 : &SwDoc::DeleteAndJoinImpl,
1845                 bForceJoinNext );
1846 }
1847 
1848 bool SwDoc::DeleteRange( SwPaM & rPam )
1849 {
1850     return lcl_DoWithBreaks( *this, rPam, &SwDoc::DeleteRangeImpl );
1851 }
1852 
1853 
1854 void lcl_syncGrammarError( SwTxtNode &rTxtNode, linguistic2::ProofreadingResult& rResult,
1855     xub_StrLen /*nBeginGrammarCheck*/, const ModelToViewHelper::ConversionMap* pConversionMap )
1856 {
1857     if( rTxtNode.IsGrammarCheckDirty() )
1858         return;
1859     SwGrammarMarkUp* pWrong = rTxtNode.GetGrammarCheck();
1860 	linguistic2::SingleProofreadingError* pArray = rResult.aErrors.getArray();
1861     sal_uInt16 i, j = 0;
1862 	if( pWrong )
1863 	{
1864 		for( i = 0; i < rResult.aErrors.getLength(); ++i )
1865 		{
1866 			const linguistic2::SingleProofreadingError &rError = rResult.aErrors[i];
1867 			xub_StrLen nStart = (xub_StrLen)ModelToViewHelper::ConvertToModelPosition( pConversionMap, rError.nErrorStart ).mnPos;
1868 			xub_StrLen nEnd = (xub_StrLen)ModelToViewHelper::ConvertToModelPosition( pConversionMap, rError.nErrorStart + rError.nErrorLength ).mnPos;
1869 			if( i != j )
1870 				pArray[j] = pArray[i];
1871 			if( pWrong->LookForEntry( nStart, nEnd ) )
1872 				++j;
1873 		}
1874 	}
1875     if( rResult.aErrors.getLength() > j )
1876         rResult.aErrors.realloc( j );
1877 }
1878 
1879 
1880 uno::Any SwDoc::Spell( SwPaM& rPaM,
1881 					uno::Reference< XSpellChecker1 >  &xSpeller,
1882                     sal_uInt16* pPageCnt, sal_uInt16* pPageSt,
1883                     bool bGrammarCheck,
1884                     SwConversionArgs *pConvArgs  ) const
1885 {
1886 	SwPosition* pSttPos = rPaM.Start(), *pEndPos = rPaM.End();
1887 	uno::Reference< beans::XPropertySet >  xProp( ::GetLinguPropertySet() );
1888 
1889     SwSpellArgs      *pSpellArgs = 0;
1890     //SwConversionArgs *pConvArgs  = 0;
1891     if (pConvArgs)
1892 	{
1893         pConvArgs->SetStart(pSttPos->nNode.GetNode().GetTxtNode(), pSttPos->nContent);
1894 		pConvArgs->SetEnd(  pEndPos->nNode.GetNode().GetTxtNode(), pEndPos->nContent );
1895 	}
1896     else
1897         pSpellArgs = new SwSpellArgs( xSpeller,
1898                             pSttPos->nNode.GetNode().GetTxtNode(), pSttPos->nContent,
1899                             pEndPos->nNode.GetNode().GetTxtNode(), pEndPos->nContent,
1900                             bGrammarCheck );
1901 
1902 	sal_uLong nCurrNd = pSttPos->nNode.GetIndex();
1903 	sal_uLong nEndNd = pEndPos->nNode.GetIndex();
1904 
1905     uno::Any aRet;
1906 	if( nCurrNd <= nEndNd )
1907 	{
1908 		SwCntntFrm* pCntFrm;
1909 		sal_Bool bGoOn = sal_True;
1910 		while( bGoOn )
1911 		{
1912 			SwNode* pNd = GetNodes()[ nCurrNd ];
1913 			switch( pNd->GetNodeType() )
1914 			{
1915 			case ND_TEXTNODE:
1916 				if( 0 != ( pCntFrm = ((SwTxtNode*)pNd)->getLayoutFrm( GetCurrentLayout() )) )
1917 				{
1918 					// geschutze Cellen/Flys ueberspringen, ausgeblendete
1919 					//ebenfalls
1920 					if( pCntFrm->IsProtected() )
1921 					{
1922                         nCurrNd = pNd->EndOfSectionIndex();
1923 					}
1924 					else if( !((SwTxtFrm*)pCntFrm)->IsHiddenNow() )
1925 					{
1926 						if( pPageCnt && *pPageCnt && pPageSt )
1927 						{
1928 							sal_uInt16 nPageNr = pCntFrm->GetPhyPageNum();
1929 							if( !*pPageSt )
1930 							{
1931 								*pPageSt = nPageNr;
1932 								if( *pPageCnt < *pPageSt )
1933 									*pPageCnt = *pPageSt;
1934 							}
1935 							long nStat;
1936 							if( nPageNr >= *pPageSt )
1937                                 nStat = nPageNr - *pPageSt + 1;
1938 							else
1939                                 nStat = nPageNr + *pPageCnt - *pPageSt + 1;
1940 							::SetProgressState( nStat, (SwDocShell*)GetDocShell() );
1941 						}
1942                         //Spell() changes the pSpellArgs in case an error is found
1943                         xub_StrLen nBeginGrammarCheck = 0;
1944                         xub_StrLen nEndGrammarCheck = 0;
1945                         if( pSpellArgs && pSpellArgs->bIsGrammarCheck)
1946                         {
1947                             nBeginGrammarCheck = pSpellArgs->pStartNode == pNd ?  pSpellArgs->pStartIdx->GetIndex() : 0;
1948                             // if grammar checking starts inside of a sentence the start position has to be adjusted
1949                             if( nBeginGrammarCheck )
1950                             {
1951                                 SwIndex aStartIndex( dynamic_cast< SwTxtNode* >( pNd ), nBeginGrammarCheck );
1952                                 SwPosition aStart( *pNd, aStartIndex );
1953                                 SwCursor aCrsr(aStart, 0, false);
1954                                 SwPosition aOrigPos = *aCrsr.GetPoint();
1955                                 aCrsr.GoSentence( SwCursor::START_SENT );
1956                                 if( aOrigPos != *aCrsr.GetPoint() )
1957                                 {
1958                                     nBeginGrammarCheck = aCrsr.GetPoint()->nContent.GetIndex();
1959                                 }
1960                             }
1961                             nEndGrammarCheck = pSpellArgs->pEndNode == pNd ? pSpellArgs->pEndIdx->GetIndex() : ((SwTxtNode*)pNd)->GetTxt().Len();
1962                         }
1963 
1964                         xub_StrLen nSpellErrorPosition = ((SwTxtNode*)pNd)->GetTxt().Len();
1965                         if( (!pConvArgs &&
1966                                 ((SwTxtNode*)pNd)->Spell( pSpellArgs )) ||
1967                             ( pConvArgs &&
1968                                 ((SwTxtNode*)pNd)->Convert( *pConvArgs )))
1969 						{
1970 							// Abbrechen und Position merken
1971 							pSttPos->nNode = nCurrNd;
1972 							pEndPos->nNode = nCurrNd;
1973 							nCurrNd = nEndNd;
1974                             if( pSpellArgs )
1975                                 nSpellErrorPosition = pSpellArgs->pStartIdx->GetIndex() > pSpellArgs->pEndIdx->GetIndex() ?
1976                                             pSpellArgs->pEndIdx->GetIndex() :
1977                                             pSpellArgs->pStartIdx->GetIndex();
1978 						}
1979 
1980 
1981                         if( pSpellArgs && pSpellArgs->bIsGrammarCheck )
1982                         {
1983                             uno::Reference< linguistic2::XProofreadingIterator >  xGCIterator( GetGCIterator() );
1984                             if (xGCIterator.is())
1985                             {
1986                                 String aText( ((SwTxtNode*)pNd)->GetTxt().Copy( nBeginGrammarCheck, nEndGrammarCheck - nBeginGrammarCheck ) );
1987                                 uno::Reference< lang::XComponent > xDoc( ((SwDocShell*)GetDocShell())->GetBaseModel(), uno::UNO_QUERY );
1988                                 // Expand the string:
1989                                 rtl::OUString aExpandText;
1990                                 const ModelToViewHelper::ConversionMap* pConversionMap =
1991                                         ((SwTxtNode*)pNd)->BuildConversionMap( aExpandText );
1992                                 // get XFlatParagraph to use...
1993                                 uno::Reference< text::XFlatParagraph > xFlatPara = new SwXFlatParagraph( *((SwTxtNode*)pNd), aExpandText, pConversionMap );
1994 
1995                                 // get error position of cursor in XFlatParagraph
1996                                 sal_Int32 nGrammarErrorPosInText;
1997                                 linguistic2::ProofreadingResult aResult;
1998                                 sal_Int32 nGrammarErrors;
1999                                 do
2000                                 {
2001                                     nGrammarErrorPosInText = ModelToViewHelper::ConvertToViewPosition( pConversionMap, nBeginGrammarCheck );
2002                                     aResult = xGCIterator->checkSentenceAtPosition(
2003                                             xDoc, xFlatPara, aExpandText, lang::Locale(), nBeginGrammarCheck, -1, -1 );
2004 
2005                                     lcl_syncGrammarError( *((SwTxtNode*)pNd), aResult, nBeginGrammarCheck, pConversionMap );
2006 
2007                                     // get suggestions to use for the specific error position
2008                                     nGrammarErrors = aResult.aErrors.getLength();
2009                                     // if grammar checking doesn't have any progress then quit
2010                                     if( aResult.nStartOfNextSentencePosition <= nBeginGrammarCheck )
2011                                         break;
2012                                     // prepare next iteration
2013                                     nBeginGrammarCheck = (xub_StrLen)aResult.nStartOfNextSentencePosition;
2014                                 }
2015                                 while( nSpellErrorPosition > aResult.nBehindEndOfSentencePosition && !nGrammarErrors && aResult.nBehindEndOfSentencePosition < nEndGrammarCheck );
2016 
2017                                 if( nGrammarErrors > 0 && nSpellErrorPosition >= aResult.nBehindEndOfSentencePosition )
2018                                 {
2019                                     aRet <<= aResult;
2020                                     //put the cursor to the current error
2021                                     const linguistic2::SingleProofreadingError &rError = aResult.aErrors[0];
2022 						            nCurrNd = pNd->GetIndex();
2023                                     pSttPos->nNode = nCurrNd;
2024 							        pEndPos->nNode = nCurrNd;
2025                                     pSpellArgs->pStartNode = ((SwTxtNode*)pNd);
2026 						            pSpellArgs->pEndNode = ((SwTxtNode*)pNd);
2027                                     pSpellArgs->pStartIdx->Assign(((SwTxtNode*)pNd), (xub_StrLen)ModelToViewHelper::ConvertToModelPosition( pConversionMap, rError.nErrorStart ).mnPos );
2028                                     pSpellArgs->pEndIdx->Assign(((SwTxtNode*)pNd), (xub_StrLen)ModelToViewHelper::ConvertToModelPosition( pConversionMap, rError.nErrorStart + rError.nErrorLength ).mnPos );
2029                                     nCurrNd = nEndNd;
2030                                 }
2031                             }
2032                         }
2033                     }
2034                 }
2035 				break;
2036 			case ND_SECTIONNODE:
2037                 if( ( ((SwSectionNode*)pNd)->GetSection().IsProtect() ||
2038                     ((SwSectionNode*)pNd)->GetSection().IsHidden() ) )
2039 					nCurrNd = pNd->EndOfSectionIndex();
2040 				break;
2041 			case ND_ENDNODE:
2042 				{
2043 					break;
2044 				}
2045 			}
2046 
2047             bGoOn = nCurrNd < nEndNd;
2048             ++nCurrNd;
2049 		}
2050 	}
2051 
2052     if( !aRet.hasValue() )
2053     {
2054         if (pConvArgs)
2055             aRet <<= pConvArgs->aConvText;
2056         else
2057             aRet <<= pSpellArgs->xSpellAlt;
2058     }
2059     delete pSpellArgs;
2060 
2061     return aRet;
2062 }
2063 
2064 class SwHyphArgs : public SwInterHyphInfo
2065 {
2066 	const SwNode *pStart;
2067 	const SwNode *pEnd;
2068 		  SwNode *pNode;
2069 	sal_uInt16 *pPageCnt;
2070 	sal_uInt16 *pPageSt;
2071 
2072 	sal_uInt32 nNode;
2073 	xub_StrLen nPamStart;
2074 	xub_StrLen nPamLen;
2075 
2076 public:
2077 		 SwHyphArgs( const SwPaM *pPam, const Point &rPoint,
2078 						 sal_uInt16* pPageCount, sal_uInt16* pPageStart );
2079 	void SetPam( SwPaM *pPam ) const;
2080 	inline void SetNode( SwNode *pNew ) { pNode = pNew; }
2081 	inline const SwNode *GetNode() const { return pNode; }
2082 	inline void SetRange( const SwNode *pNew );
2083 	inline void NextNode() { ++nNode; }
2084 	inline sal_uInt16 *GetPageCnt() { return pPageCnt; }
2085 	inline sal_uInt16 *GetPageSt() { return pPageSt; }
2086 };
2087 
2088 SwHyphArgs::SwHyphArgs( const SwPaM *pPam, const Point &rCrsrPos,
2089 						 sal_uInt16* pPageCount, sal_uInt16* pPageStart )
2090 	 : SwInterHyphInfo( rCrsrPos ), pNode(0),
2091 	 pPageCnt( pPageCount ), pPageSt( pPageStart )
2092 {
2093 	// Folgende Bedingungen muessen eingehalten werden:
2094 	// 1) es gibt mindestens eine Selektion
2095 	// 2) SPoint() == Start()
2096 	ASSERT( pPam->HasMark(), "SwDoc::Hyphenate: blowing in the wind");
2097 	ASSERT( *pPam->GetPoint() <= *pPam->GetMark(),
2098 			"SwDoc::Hyphenate: New York, New York");
2099 
2100 	const SwPosition *pPoint = pPam->GetPoint();
2101 	nNode = pPoint->nNode.GetIndex();
2102 
2103 	// Start einstellen
2104 	pStart = pPoint->nNode.GetNode().GetTxtNode();
2105 	nPamStart = pPoint->nContent.GetIndex();
2106 
2107 	// Ende und Laenge einstellen.
2108 	const SwPosition *pMark = pPam->GetMark();
2109 	pEnd = pMark->nNode.GetNode().GetTxtNode();
2110 	nPamLen = pMark->nContent.GetIndex();
2111 	if( pPoint->nNode == pMark->nNode )
2112 		nPamLen = nPamLen - pPoint->nContent.GetIndex();
2113 }
2114 
2115 inline void SwHyphArgs::SetRange( const SwNode *pNew )
2116 {
2117 	nStart = pStart == pNew ? nPamStart : 0;
2118 	nLen   = pEnd	== pNew ? nPamLen : STRING_NOTFOUND;
2119 }
2120 
2121 void SwHyphArgs::SetPam( SwPaM *pPam ) const
2122 {
2123 	if( !pNode )
2124 		*pPam->GetPoint() = *pPam->GetMark();
2125 	else
2126 	{
2127 		pPam->GetPoint()->nNode = nNode;
2128 		pPam->GetPoint()->nContent.Assign( pNode->GetCntntNode(), nWordStart );
2129 		pPam->GetMark()->nNode = nNode;
2130 		pPam->GetMark()->nContent.Assign( pNode->GetCntntNode(),
2131 										  nWordStart + nWordLen );
2132 		ASSERT( nNode == pNode->GetIndex(),
2133 				"SwHyphArgs::SetPam: Pam desaster" );
2134 	}
2135 }
2136 
2137 // liefert sal_True zurueck, wenn es weitergehen soll.
2138 sal_Bool lcl_HyphenateNode( const SwNodePtr& rpNd, void* pArgs )
2139 {
2140 	// Hyphenate liefert sal_True zurueck, wenn eine Trennstelle anliegt
2141 	// und stellt pPam ein.
2142 	SwTxtNode *pNode = rpNd->GetTxtNode();
2143 	SwHyphArgs *pHyphArgs = (SwHyphArgs*)pArgs;
2144 	if( pNode )
2145 	{
2146 		SwCntntFrm* pCntFrm = pNode->getLayoutFrm( pNode->GetDoc()->GetCurrentLayout() );
2147 		if( pCntFrm && !((SwTxtFrm*)pCntFrm)->IsHiddenNow() )
2148 		{
2149 			sal_uInt16 *pPageSt = pHyphArgs->GetPageSt();
2150 			sal_uInt16 *pPageCnt = pHyphArgs->GetPageCnt();
2151 			if( pPageCnt && *pPageCnt && pPageSt )
2152 			{
2153 				sal_uInt16 nPageNr = pCntFrm->GetPhyPageNum();
2154 				if( !*pPageSt )
2155 				{
2156 					*pPageSt = nPageNr;
2157 					if( *pPageCnt < *pPageSt )
2158 						*pPageCnt = *pPageSt;
2159 				}
2160 				long nStat = nPageNr >= *pPageSt ? nPageNr - *pPageSt + 1
2161 										 : nPageNr + *pPageCnt - *pPageSt + 1;
2162 				::SetProgressState( nStat, (SwDocShell*)pNode->GetDoc()->GetDocShell() );
2163 			}
2164 			pHyphArgs->SetRange( rpNd );
2165 			if( pNode->Hyphenate( *pHyphArgs ) )
2166 			{
2167 				pHyphArgs->SetNode( rpNd );
2168 				return sal_False;
2169 			}
2170 		}
2171 	}
2172 	pHyphArgs->NextNode();
2173 	return sal_True;
2174 }
2175 
2176 uno::Reference< XHyphenatedWord >  SwDoc::Hyphenate(
2177 							SwPaM *pPam, const Point &rCrsrPos,
2178 						 	sal_uInt16* pPageCnt, sal_uInt16* pPageSt )
2179 {
2180 	ASSERT(this == pPam->GetDoc(), "SwDoc::Hyphenate: strangers in the night");
2181 
2182 	if( *pPam->GetPoint() > *pPam->GetMark() )
2183 		pPam->Exchange();
2184 
2185 	SwHyphArgs aHyphArg( pPam, rCrsrPos, pPageCnt, pPageSt );
2186 	SwNodeIndex aTmpIdx( pPam->GetMark()->nNode, 1 );
2187 	GetNodes().ForEach( pPam->GetPoint()->nNode, aTmpIdx,
2188 					lcl_HyphenateNode, &aHyphArg );
2189 	aHyphArg.SetPam( pPam );
2190 	return aHyphArg.GetHyphWord();	// will be set by lcl_HyphenateNode
2191 }
2192 
2193 
2194 sal_Bool lcl_GetTokenToParaBreak( String& rStr, String& rRet, sal_Bool bRegExpRplc )
2195 {
2196 	sal_Bool bRet = sal_False;
2197 	if( bRegExpRplc )
2198 	{
2199 		xub_StrLen nPos = 0;
2200 		String sPara( String::CreateFromAscii(
2201 									RTL_CONSTASCII_STRINGPARAM( "\\n" )));
2202 		while( STRING_NOTFOUND != ( nPos = rStr.Search( sPara, nPos )) )
2203 		{
2204 			// wurde das escaped?
2205 			if( nPos && '\\' == rStr.GetChar( nPos-1 ))
2206 			{
2207 				if( ++nPos >= rStr.Len() )
2208 					break;
2209 			}
2210 			else
2211 			{
2212 				rRet = rStr.Copy( 0, nPos );
2213 				rStr.Erase( 0, nPos + sPara.Len() );
2214 				bRet = sal_True;
2215 				break;
2216 			}
2217 		}
2218 	}
2219 	if( !bRet )
2220 	{
2221 		rRet = rStr;
2222 		rStr.Erase();
2223 	}
2224 	return bRet;
2225 }
2226 
2227 bool SwDoc::ReplaceRange( SwPaM& rPam, const String& rStr,
2228         const bool bRegExReplace )
2229 {
2230     // unfortunately replace works slightly differently from delete,
2231     // so we cannot use lcl_DoWithBreaks here...
2232 
2233     ::std::vector<xub_StrLen> Breaks;
2234 
2235     SwPaM aPam( *rPam.GetMark(), *rPam.GetPoint() );
2236     aPam.Normalize(sal_False);
2237     if (aPam.GetPoint()->nNode != aPam.GetMark()->nNode)
2238     {
2239         aPam.Move(fnMoveBackward);
2240     }
2241     ASSERT((aPam.GetPoint()->nNode == aPam.GetMark()->nNode), "invalid pam?");
2242 
2243     lcl_CalcBreaks(Breaks, aPam);
2244 
2245     while (!Breaks.empty() // skip over prefix of dummy chars
2246             && (aPam.GetMark()->nContent.GetIndex() == *Breaks.begin()) )
2247     {
2248         // skip!
2249         ++aPam.GetMark()->nContent; // always in bounds if Breaks valid
2250         Breaks.erase(Breaks.begin());
2251     }
2252     *rPam.Start() = *aPam.GetMark(); // update start of original pam w/ prefix
2253 
2254     if (!Breaks.size())
2255     {
2256         return ReplaceRangeImpl(rPam, rStr, bRegExReplace); // original pam!
2257     }
2258 
2259     // N.B.: deletion must be split into several parts if the text node
2260     // contains a text attribute with end and with dummy character
2261     // and the selection does not contain the text attribute completely,
2262     // but overlaps its start (left), where the dummy character is.
2263 
2264     bool bRet( true );
2265     // iterate from end to start, to avoid invalidating the offsets!
2266     ::std::vector<xub_StrLen>::reverse_iterator iter( Breaks.rbegin() );
2267     ASSERT(aPam.GetPoint() == aPam.End(), "wrong!");
2268     SwPosition & rEnd( *aPam.End() );
2269     SwPosition & rStart( *aPam.Start() );
2270 
2271     // set end of temp pam to original end (undo Move backward above)
2272     rEnd = *rPam.End();
2273     // after first deletion, rEnd will point into the original text node again!
2274 
2275     while (iter != Breaks.rend())
2276     {
2277         rStart.nContent = *iter + 1;
2278         if (rEnd.nContent != rStart.nContent) // check if part is empty
2279         {
2280             bRet &= (IsRedlineOn())
2281                 ? DeleteAndJoinWithRedlineImpl(aPam)
2282                 : DeleteAndJoinImpl(aPam, false);
2283         }
2284         rEnd.nContent = *iter;
2285         ++iter;
2286     }
2287 
2288     rStart = *rPam.Start(); // set to original start
2289     ASSERT(rEnd.nContent > rStart.nContent, "replace part empty!");
2290     if (rEnd.nContent > rStart.nContent) // check if part is empty
2291     {
2292         bRet &= ReplaceRangeImpl(aPam, rStr, bRegExReplace);
2293     }
2294 
2295     rPam = aPam; // update original pam (is this required?)
2296 
2297     return bRet;
2298 }
2299 
2300 // N.B.: it is possible to call Replace with a PaM that spans 2 paragraphs:
2301 // search with regex for "$", then replace _all_
2302 bool SwDoc::ReplaceRangeImpl( SwPaM& rPam, const String& rStr,
2303         const bool bRegExReplace )
2304 {
2305 	if( !rPam.HasMark() || *rPam.GetPoint() == *rPam.GetMark() )
2306         return false;
2307 
2308 	sal_Bool bJoinTxt, bJoinPrev;
2309 	lcl_GetJoinFlags( rPam, bJoinTxt, bJoinPrev );
2310 
2311 	{
2312 		// dann eine Kopie vom Cursor erzeugen um alle Pams aus den
2313 		// anderen Sichten aus dem Loeschbereich zu verschieben
2314 		// ABER NICHT SICH SELBST !!
2315 		SwPaM aDelPam( *rPam.GetMark(), *rPam.GetPoint() );
2316 		::PaMCorrAbs( aDelPam, *aDelPam.GetPoint() );
2317 
2318 		SwPosition *pStt = (SwPosition*)aDelPam.Start(),
2319 				   *pEnd = (SwPosition*)aDelPam.End();
2320 		ASSERT( pStt->nNode == pEnd->nNode ||
2321 				( pStt->nNode.GetIndex() + 1 == pEnd->nNode.GetIndex() &&
2322 					!pEnd->nContent.GetIndex() ),
2323                 "invalid range: Point and Mark on different nodes" );
2324 		sal_Bool bOneNode = pStt->nNode == pEnd->nNode;
2325 
2326 		// eigenes Undo ????
2327 		String sRepl( rStr );
2328 		SwTxtNode* pTxtNd = pStt->nNode.GetNode().GetTxtNode();
2329 		xub_StrLen nStt = pStt->nContent.GetIndex(),
2330 				nEnd = bOneNode ? pEnd->nContent.GetIndex()
2331 								: pTxtNd->GetTxt().Len();
2332 
2333 		SwDataChanged aTmp( aDelPam, 0 );
2334 
2335 		if( IsRedlineOn() )
2336         {
2337 			RedlineMode_t eOld = GetRedlineMode();
2338 			checkRedlining(eOld);
2339             if (GetIDocumentUndoRedo().DoesUndo())
2340             {
2341                 GetIDocumentUndoRedo().StartUndo(UNDO_EMPTY, NULL);
2342 
2343                 // Bug 68584 - if any Redline will change (split!) the node
2344                 const ::sw::mark::IMark* pBkmk = getIDocumentMarkAccess()->makeMark( aDelPam, ::rtl::OUString(), IDocumentMarkAccess::UNO_BOOKMARK );
2345 
2346                 //JP 06.01.98: MUSS noch optimiert werden!!!
2347                 SetRedlineMode(
2348                     (RedlineMode_t)(nsRedlineMode_t::REDLINE_ON | nsRedlineMode_t::REDLINE_SHOW_INSERT | nsRedlineMode_t::REDLINE_SHOW_DELETE ));
2349 
2350                 *aDelPam.GetPoint() = pBkmk->GetMarkPos();
2351                 if(pBkmk->IsExpanded())
2352                     *aDelPam.GetMark() = pBkmk->GetOtherMarkPos();
2353                 getIDocumentMarkAccess()->deleteMark(pBkmk);
2354                 pStt = aDelPam.Start();
2355                 pTxtNd = pStt->nNode.GetNode().GetTxtNode();
2356                 nStt = pStt->nContent.GetIndex();
2357 			}
2358 
2359 			if( sRepl.Len() )
2360 			{
2361 				// Attribute des 1. Zeichens ueber den ReplaceText setzen
2362 				SfxItemSet aSet( GetAttrPool(),
2363 							RES_CHRATR_BEGIN,	  RES_TXTATR_WITHEND_END - 1,
2364 							RES_UNKNOWNATR_BEGIN, RES_UNKNOWNATR_END-1,
2365 							0 );
2366 				pTxtNd->GetAttr( aSet, nStt+1, nStt+1 );
2367 
2368 				aSet.ClearItem( RES_TXTATR_REFMARK );
2369 				aSet.ClearItem( RES_TXTATR_TOXMARK );
2370                 aSet.ClearItem( RES_TXTATR_CJK_RUBY );
2371                 aSet.ClearItem( RES_TXTATR_INETFMT );
2372                 aSet.ClearItem( RES_TXTATR_META );
2373                 aSet.ClearItem( RES_TXTATR_METAFIELD );
2374 
2375 				if( aDelPam.GetPoint() != aDelPam.End() )
2376 					aDelPam.Exchange();
2377 
2378 				// das Ende merken
2379 				SwNodeIndex aPtNd( aDelPam.GetPoint()->nNode, -1 );
2380 				xub_StrLen nPtCnt = aDelPam.GetPoint()->nContent.GetIndex();
2381 
2382 				sal_Bool bFirst = sal_True;
2383 				String sIns;
2384                 while ( lcl_GetTokenToParaBreak( sRepl, sIns, bRegExReplace ) )
2385 				{
2386                     InsertString( aDelPam, sIns );
2387 					if( bFirst )
2388 					{
2389 						SwNodeIndex aMkNd( aDelPam.GetMark()->nNode, -1 );
2390 						xub_StrLen nMkCnt = aDelPam.GetMark()->nContent.GetIndex();
2391 
2392 						SplitNode( *aDelPam.GetPoint(), false );
2393 
2394 						aMkNd++;
2395 						aDelPam.GetMark()->nNode = aMkNd;
2396 						aDelPam.GetMark()->nContent.Assign(
2397 									aMkNd.GetNode().GetCntntNode(), nMkCnt );
2398 						bFirst = sal_False;
2399 					}
2400 					else
2401 						SplitNode( *aDelPam.GetPoint(), false );
2402 				}
2403 				if( sIns.Len() )
2404                 {
2405                     InsertString( aDelPam, sIns );
2406                 }
2407 
2408 				SwPaM aTmpRange( *aDelPam.GetPoint() );
2409 				aTmpRange.SetMark();
2410 
2411 				aPtNd++;
2412 				aDelPam.GetPoint()->nNode = aPtNd;
2413 				aDelPam.GetPoint()->nContent.Assign( aPtNd.GetNode().GetCntntNode(),
2414 													nPtCnt);
2415 				*aTmpRange.GetMark() = *aDelPam.GetPoint();
2416 
2417                 RstTxtAttrs( aTmpRange );
2418                 InsertItemSet( aTmpRange, aSet, 0 );
2419             }
2420 
2421             if (GetIDocumentUndoRedo().DoesUndo())
2422             {
2423                 SwUndo *const pUndoRD =
2424                     new SwUndoRedlineDelete( aDelPam, UNDO_REPLACE );
2425                 GetIDocumentUndoRedo().AppendUndo(pUndoRD);
2426             }
2427 			AppendRedline( new SwRedline( nsRedlineType_t::REDLINE_DELETE, aDelPam ), true);
2428 
2429 			*rPam.GetMark() = *aDelPam.GetMark();
2430             if (GetIDocumentUndoRedo().DoesUndo())
2431             {
2432 				*aDelPam.GetPoint() = *rPam.GetPoint();
2433                 GetIDocumentUndoRedo().EndUndo(UNDO_EMPTY, NULL);
2434 
2435 				// Bug 68584 - if any Redline will change (split!) the node
2436                 const ::sw::mark::IMark* pBkmk = getIDocumentMarkAccess()->makeMark( aDelPam, ::rtl::OUString(), IDocumentMarkAccess::UNO_BOOKMARK );
2437 
2438 				SwIndex& rIdx = aDelPam.GetPoint()->nContent;
2439 				rIdx.Assign( 0, 0 );
2440 				aDelPam.GetMark()->nContent = rIdx;
2441 				rPam.GetPoint()->nNode = 0;
2442 				rPam.GetPoint()->nContent = rIdx;
2443 				*rPam.GetMark() = *rPam.GetPoint();
2444 //JP 06.01.98: MUSS noch optimiert werden!!!
2445 SetRedlineMode( eOld );
2446 
2447                 *rPam.GetPoint() = pBkmk->GetMarkPos();
2448                 if(pBkmk->IsExpanded())
2449                     *rPam.GetMark() = pBkmk->GetOtherMarkPos();
2450                 getIDocumentMarkAccess()->deleteMark(pBkmk);
2451 			}
2452 			bJoinTxt = sal_False;
2453 		}
2454 		else
2455 		{
2456 			if( !IsIgnoreRedline() && GetRedlineTbl().Count() )
2457 				DeleteRedline( aDelPam, true, USHRT_MAX );
2458 
2459 			SwUndoReplace* pUndoRpl = 0;
2460             bool const bDoesUndo = GetIDocumentUndoRedo().DoesUndo();
2461             if (bDoesUndo)
2462             {
2463                 pUndoRpl = new SwUndoReplace(aDelPam, sRepl, bRegExReplace);
2464                 GetIDocumentUndoRedo().AppendUndo(pUndoRpl);
2465             }
2466             ::sw::UndoGuard const undoGuard(GetIDocumentUndoRedo());
2467 
2468 			if( aDelPam.GetPoint() != pStt )
2469 				aDelPam.Exchange();
2470 
2471 			SwNodeIndex aPtNd( pStt->nNode, -1 );
2472 			xub_StrLen nPtCnt = pStt->nContent.GetIndex();
2473 
2474 			// die Werte nochmal setzen, falls schohn Rahmen oder Fussnoten
2475 			// auf dem Text entfernt wurden!
2476 			nStt = nPtCnt;
2477 			nEnd = bOneNode ? pEnd->nContent.GetIndex()
2478 							: pTxtNd->GetTxt().Len();
2479 
2480 			sal_Bool bFirst = sal_True;
2481 			String sIns;
2482             while ( lcl_GetTokenToParaBreak( sRepl, sIns, bRegExReplace ) )
2483 			{
2484 				if( !bFirst || nStt == pTxtNd->GetTxt().Len() )
2485                 {
2486                     InsertString( aDelPam, sIns );
2487                 }
2488 				else if( nStt < nEnd || sIns.Len() )
2489                 {
2490                     pTxtNd->ReplaceText( pStt->nContent, nEnd - nStt, sIns );
2491                 }
2492 				SplitNode( *pStt, false);
2493 				bFirst = sal_False;
2494 			}
2495 
2496 			if( bFirst || sIns.Len() )
2497 			{
2498 				if( !bFirst || nStt == pTxtNd->GetTxt().Len() )
2499                 {
2500                     InsertString( aDelPam, sIns );
2501                 }
2502 				else if( nStt < nEnd || sIns.Len() )
2503                 {
2504                     pTxtNd->ReplaceText( pStt->nContent, nEnd - nStt, sIns );
2505                 }
2506             }
2507 
2508 			*rPam.GetMark() = *aDelPam.GetMark();
2509 
2510 			aPtNd++;
2511 			rPam.GetMark()->nNode = aPtNd;
2512 			rPam.GetMark()->nContent.Assign( aPtNd.GetNode().GetCntntNode(),
2513 												nPtCnt );
2514 
2515             if ( bJoinTxt && !bJoinPrev )
2516             {
2517                 rPam.Move( fnMoveBackward );
2518             }
2519 
2520 			if( pUndoRpl )
2521             {
2522                 pUndoRpl->SetEnd(rPam);
2523             }
2524         }
2525     }
2526 
2527 	if( bJoinTxt )
2528 		lcl_JoinText( rPam, bJoinPrev );
2529 
2530 	SetModified();
2531     return true;
2532 }
2533 
2534 	// speicher die akt. Werte fuer die automatische Aufnahme von Ausnahmen
2535 	// in die Autokorrektur
2536 void SwDoc::SetAutoCorrExceptWord( SwAutoCorrExceptWord* pNew )
2537 {
2538 	if( pACEWord && pNew != pACEWord )
2539 		delete pACEWord;
2540 	pACEWord = pNew;
2541 }
2542 
2543 bool SwDoc::DelFullPara( SwPaM& rPam )
2544 {
2545 	const SwPosition &rStt = *rPam.Start(), &rEnd = *rPam.End();
2546 	const SwNode* pNd = &rStt.nNode.GetNode();
2547     sal_uInt32 nSectDiff = pNd->StartOfSectionNode()->EndOfSectionIndex() -
2548 						pNd->StartOfSectionIndex();
2549 	sal_uInt32 nNodeDiff = rEnd.nNode.GetIndex() - rStt.nNode.GetIndex();
2550 
2551 	if ( nSectDiff-2 <= nNodeDiff || IsRedlineOn() ||
2552 		 /* #i9185# Prevent getting the node after the end node (see below) */
2553         rEnd.nNode.GetIndex() + 1 == GetNodes().Count() )
2554     {
2555 		return sal_False;
2556     }
2557 
2558 	// harte SeitenUmbrueche am nachfolgenden Node verschieben
2559 	sal_Bool bSavePageBreak = sal_False, bSavePageDesc = sal_False;
2560 
2561 	/* #i9185# This whould lead to a segmentation fault if not catched
2562        above. */
2563 	sal_uLong nNextNd = rEnd.nNode.GetIndex() + 1;
2564     SwTableNode *const pTblNd = GetNodes()[ nNextNd ]->GetTableNode();
2565 
2566 	if( pTblNd && pNd->IsCntntNode() )
2567 	{
2568 		SwFrmFmt* pTableFmt = pTblNd->GetTable().GetFrmFmt();
2569 //JP 24.08.98: will man wirklich den PageDesc/Break vom
2570 //				nachfolgen Absatz ueberbuegeln?
2571 //		const SwAttrSet& rAttrSet = pTableFmt->GetAttrSet();
2572 //		if( SFX_ITEM_SET != rAttrSet.GetItemState( RES_PAGEDESC ) &&
2573 //			SFX_ITEM_SET != rAttrSet.GetItemState( RES_BREAK ))
2574 		{
2575 			const SfxPoolItem *pItem;
2576 			const SfxItemSet* pSet = ((SwCntntNode*)pNd)->GetpSwAttrSet();
2577 			if( pSet && SFX_ITEM_SET == pSet->GetItemState( RES_PAGEDESC,
2578 				sal_False, &pItem ) )
2579 			{
2580                 pTableFmt->SetFmtAttr( *pItem );
2581 				bSavePageDesc = sal_True;
2582 			}
2583 
2584 			if( pSet && SFX_ITEM_SET == pSet->GetItemState( RES_BREAK,
2585 				sal_False, &pItem ) )
2586 			{
2587                 pTableFmt->SetFmtAttr( *pItem );
2588 				bSavePageBreak = sal_True;
2589 			}
2590 		}
2591 	}
2592 
2593     bool const bDoesUndo = GetIDocumentUndoRedo().DoesUndo();
2594 	if( bDoesUndo )
2595 	{
2596 		if( !rPam.HasMark() )
2597 			rPam.SetMark();
2598 		else if( rPam.GetPoint() == &rStt )
2599 			rPam.Exchange();
2600 		rPam.GetPoint()->nNode++;
2601 
2602         SwCntntNode *pTmpNode = rPam.GetPoint()->nNode.GetNode().GetCntntNode();
2603         rPam.GetPoint()->nContent.Assign( pTmpNode, 0 );
2604         bool bGoNext = (0 == pTmpNode);
2605         pTmpNode = rPam.GetMark()->nNode.GetNode().GetCntntNode();
2606 		rPam.GetMark()->nContent.Assign( pTmpNode, 0 );
2607 
2608         GetIDocumentUndoRedo().ClearRedo();
2609 
2610 		SwPaM aDelPam( *rPam.GetMark(), *rPam.GetPoint() );
2611         {
2612             SwPosition aTmpPos( *aDelPam.GetPoint() );
2613             if( bGoNext )
2614             {
2615                 pTmpNode = GetNodes().GoNext( &aTmpPos.nNode );
2616                 aTmpPos.nContent.Assign( pTmpNode, 0 );
2617             }
2618             ::PaMCorrAbs( aDelPam, aTmpPos );
2619         }
2620 
2621 		SwUndoDelete* pUndo = new SwUndoDelete( aDelPam, sal_True );
2622 
2623 		*rPam.GetPoint() = *aDelPam.GetPoint();
2624 		pUndo->SetPgBrkFlags( bSavePageBreak, bSavePageDesc );
2625         GetIDocumentUndoRedo().AppendUndo(pUndo);
2626     }
2627     else
2628     {
2629 		SwNodeRange aRg( rStt.nNode, rEnd.nNode );
2630 		if( rPam.GetPoint() != &rEnd )
2631 			rPam.Exchange();
2632 
2633 		// versuche hinters Ende zu verschieben
2634 		if( !rPam.Move( fnMoveForward, fnGoNode ) )
2635 		{
2636 			// na gut, dann an den Anfang
2637 			rPam.Exchange();
2638 			if( !rPam.Move( fnMoveBackward, fnGoNode ))
2639 			{
2640 				ASSERT( sal_False, "kein Node mehr vorhanden" );
2641 				return sal_False;
2642 			}
2643 		}
2644         // move bookmarks, redlines etc.
2645         if (aRg.aStart == aRg.aEnd) // only first CorrAbs variant handles this
2646         {
2647             CorrAbs( aRg.aStart, *rPam.GetPoint(), 0, sal_True );
2648         }
2649         else
2650         {
2651             CorrAbs( aRg.aStart, aRg.aEnd, *rPam.GetPoint(), sal_True );
2652         }
2653 
2654 			// was ist mit Fly's ??
2655 		{
2656 			// stehen noch FlyFrames rum, loesche auch diese
2657 			for( sal_uInt16 n = 0; n < GetSpzFrmFmts()->Count(); ++n )
2658 			{
2659 				SwFrmFmt* pFly = (*GetSpzFrmFmts())[n];
2660 				const SwFmtAnchor* pAnchor = &pFly->GetAnchor();
2661                 SwPosition const*const pAPos = pAnchor->GetCntntAnchor();
2662                 if (pAPos &&
2663                     ((FLY_AT_PARA == pAnchor->GetAnchorId()) ||
2664                      (FLY_AT_CHAR == pAnchor->GetAnchorId())) &&
2665 					aRg.aStart <= pAPos->nNode && pAPos->nNode <= aRg.aEnd )
2666 				{
2667 					DelLayoutFmt( pFly );
2668 					--n;
2669 				}
2670 			}
2671 		}
2672 
2673         SwCntntNode *pTmpNode = rPam.GetBound( sal_True ).nNode.GetNode().GetCntntNode();
2674 		rPam.GetBound( sal_True ).nContent.Assign( pTmpNode, 0 );
2675         pTmpNode = rPam.GetBound( sal_False ).nNode.GetNode().GetCntntNode();
2676 		rPam.GetBound( sal_False ).nContent.Assign( pTmpNode, 0 );
2677 		GetNodes().Delete( aRg.aStart, nNodeDiff+1 );
2678 	}
2679 	rPam.DeleteMark();
2680 	SetModified();
2681 
2682 	return sal_True;
2683 }
2684 
2685 
2686 void SwDoc::TransliterateText(
2687     const SwPaM& rPaM,
2688     utl::TransliterationWrapper& rTrans )
2689 {
2690     SwUndoTransliterate *const pUndo = (GetIDocumentUndoRedo().DoesUndo())
2691         ?   new SwUndoTransliterate( rPaM, rTrans )
2692         :   0;
2693 
2694     const SwPosition* pStt = rPaM.Start(),
2695        			    * pEnd = rPaM.End();
2696     sal_uLong nSttNd = pStt->nNode.GetIndex(),
2697           nEndNd = pEnd->nNode.GetIndex();
2698 	xub_StrLen nSttCnt = pStt->nContent.GetIndex(),
2699 			   nEndCnt = pEnd->nContent.GetIndex();
2700 
2701 	SwTxtNode* pTNd = pStt->nNode.GetNode().GetTxtNode();
2702 	if( pStt == pEnd && pTNd )  // no selection?
2703 	{
2704         // set current word as 'area of effect'
2705 
2706 		Boundary aBndry;
2707 		if( pBreakIt->GetBreakIter().is() )
2708 			aBndry = pBreakIt->GetBreakIter()->getWordBoundary(
2709 						pTNd->GetTxt(), nSttCnt,
2710 						pBreakIt->GetLocale( pTNd->GetLang( nSttCnt ) ),
2711 						WordType::ANY_WORD /*ANYWORD_IGNOREWHITESPACES*/,
2712 						sal_True );
2713 
2714 		if( aBndry.startPos < nSttCnt && nSttCnt < aBndry.endPos )
2715 		{
2716 			nSttCnt = (xub_StrLen)aBndry.startPos;
2717 			nEndCnt = (xub_StrLen)aBndry.endPos;
2718 		}
2719 	}
2720 
2721 	if( nSttNd != nEndNd )  // is more than one text node involved?
2722 	{
2723         // iterate over all effected text nodes, the first and the last one
2724         // may be incomplete because the selection starts and/or ends there
2725 
2726 		SwNodeIndex aIdx( pStt->nNode );
2727 		if( nSttCnt )
2728 		{
2729 			aIdx++;
2730 			if( pTNd )
2731 				pTNd->TransliterateText( rTrans, nSttCnt, pTNd->GetTxt().Len(), pUndo );
2732 		}
2733 
2734 		for( ; aIdx.GetIndex() < nEndNd; aIdx++ )
2735         {
2736 			if( 0 != ( pTNd = aIdx.GetNode().GetTxtNode() ))
2737 				pTNd->TransliterateText( rTrans, 0, pTNd->GetTxt().Len(), pUndo );
2738         }
2739 
2740 		if( nEndCnt && 0 != ( pTNd = pEnd->nNode.GetNode().GetTxtNode() ))
2741 			pTNd->TransliterateText( rTrans, 0, nEndCnt, pUndo );
2742 	}
2743 	else if( pTNd && nSttCnt < nEndCnt )
2744 		pTNd->TransliterateText( rTrans, nSttCnt, nEndCnt, pUndo );
2745 
2746 	if( pUndo )
2747 	{
2748 		if( pUndo->HasData() )
2749         {
2750             GetIDocumentUndoRedo().AppendUndo(pUndo);
2751         }
2752         else
2753 			delete pUndo;
2754 	}
2755     SetModified();
2756 }
2757 
2758 
2759 #define MAX_REDLINE_COUNT	250
2760 // -----------------------------------------------------------------------------
2761 void SwDoc::checkRedlining(RedlineMode_t& _rReadlineMode)
2762 {
2763 	const SwRedlineTbl& rRedlineTbl = GetRedlineTbl();
2764 	SwEditShell* pEditShell = GetEditShell();
2765 	Window* pParent = pEditShell ? pEditShell->GetWin() : NULL;
2766 	if ( pParent && !mbReadlineChecked && rRedlineTbl.Count() > MAX_REDLINE_COUNT
2767 		&& !((_rReadlineMode & nsRedlineMode_t::REDLINE_SHOW_DELETE) == nsRedlineMode_t::REDLINE_SHOW_DELETE) )
2768 	{
2769 		WarningBox aWarning( pParent,SW_RES(MSG_DISABLE_READLINE_QUESTION));
2770 		sal_uInt16 nResult = aWarning.Execute();
2771 		mbReadlineChecked = sal_True;
2772 		if ( nResult == RET_YES )
2773 		{
2774 			sal_Int32 nMode = (sal_Int32)_rReadlineMode;
2775 			nMode |= nsRedlineMode_t::REDLINE_SHOW_INSERT | nsRedlineMode_t::REDLINE_SHOW_DELETE;
2776 			_rReadlineMode = (RedlineMode_t)nMode;
2777 		}
2778 	}
2779 }
2780 // -----------------------------------------------------------------------------
2781 
2782 void SwDoc::CountWords( const SwPaM& rPaM, SwDocStat& rStat ) const
2783 {
2784     // This is a modified version of SwDoc::TransliterateText
2785     const SwPosition* pStt = rPaM.Start();
2786     const SwPosition* pEnd = pStt == rPaM.GetPoint() ? rPaM.GetMark()
2787                                                      : rPaM.GetPoint();
2788 
2789     const sal_uLong nSttNd = pStt->nNode.GetIndex();
2790     const sal_uLong nEndNd = pEnd->nNode.GetIndex();
2791 
2792     const xub_StrLen nSttCnt = pStt->nContent.GetIndex();
2793     const xub_StrLen nEndCnt = pEnd->nContent.GetIndex();
2794 
2795     const SwTxtNode* pTNd = pStt->nNode.GetNode().GetTxtNode();
2796     if( pStt == pEnd && pTNd )                  // no region ?
2797     {
2798         // do nothing
2799         return;
2800     }
2801 
2802     if( nSttNd != nEndNd )
2803     {
2804         SwNodeIndex aIdx( pStt->nNode );
2805         if( nSttCnt )
2806         {
2807             aIdx++;
2808             if( pTNd )
2809                 pTNd->CountWords( rStat, nSttCnt, pTNd->GetTxt().Len() );
2810         }
2811 
2812         for( ; aIdx.GetIndex() < nEndNd; aIdx++ )
2813             if( 0 != ( pTNd = aIdx.GetNode().GetTxtNode() ))
2814                 pTNd->CountWords( rStat, 0, pTNd->GetTxt().Len() );
2815 
2816         if( nEndCnt && 0 != ( pTNd = pEnd->nNode.GetNode().GetTxtNode() ))
2817             pTNd->CountWords( rStat, 0, nEndCnt );
2818     }
2819     else if( pTNd && nSttCnt < nEndCnt )
2820         pTNd->CountWords( rStat, nSttCnt, nEndCnt );
2821 }
2822 
2823 void SwDoc::RemoveLeadingWhiteSpace(const SwPosition & rPos )
2824 {
2825     const SwTxtNode* pTNd = rPos.nNode.GetNode().GetTxtNode();
2826     if ( pTNd )
2827     {
2828         const String& rTxt = pTNd->GetTxt();
2829         xub_StrLen nIdx = 0;
2830         sal_Unicode cCh;
2831         while( nIdx < rTxt.Len() &&
2832                 ( '\t' == ( cCh = rTxt.GetChar( nIdx ) ) ||
2833                 (  ' ' == cCh ) ) )
2834             ++nIdx;
2835 
2836         if ( nIdx > 0 )
2837         {
2838             SwPaM aPam(rPos);
2839             aPam.GetPoint()->nContent = 0;
2840             aPam.SetMark();
2841             aPam.GetMark()->nContent = nIdx;
2842             DeleteRange( aPam );
2843         }
2844     }
2845 }
2846