xref: /aoo41x/main/sw/source/core/undo/undobj.cxx (revision cdf0e10c)
1 /*************************************************************************
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * Copyright 2000, 2010 Oracle and/or its affiliates.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * This file is part of OpenOffice.org.
10  *
11  * OpenOffice.org is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License version 3
13  * only, as published by the Free Software Foundation.
14  *
15  * OpenOffice.org is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Lesser General Public License version 3 for more details
19  * (a copy is included in the LICENSE file that accompanied this code).
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * version 3 along with OpenOffice.org.  If not, see
23  * <http://www.openoffice.org/license.html>
24  * for a copy of the LGPLv3 License.
25  *
26  ************************************************************************/
27 
28 // MARKER(update_precomp.py): autogen include statement, do not remove
29 #include "precompiled_sw.hxx"
30 
31 
32 #include <IShellCursorSupplier.hxx>
33 #include <txtftn.hxx>
34 #include <fmtanchr.hxx>
35 #include <ftnidx.hxx>
36 #include <frmfmt.hxx>
37 #include <doc.hxx>
38 #include <UndoManager.hxx>
39 #include <docary.hxx>
40 #include <swundo.hxx>			// fuer die UndoIds
41 #include <pam.hxx>
42 #include <ndtxt.hxx>
43 #include <UndoCore.hxx>
44 #include <rolbck.hxx>
45 #include <ndnotxt.hxx>
46 #include <IMark.hxx>
47 #include <mvsave.hxx>
48 #include <redline.hxx>
49 #include <crossrefbookmark.hxx>
50 #include <undo.hrc>
51 #include <comcore.hrc>
52 #include <docsh.hxx>
53 
54 class SwRedlineSaveData : public SwUndRng, public SwRedlineData,
55 						  private SwUndoSaveSection
56 {
57 public:
58 	SwRedlineSaveData( SwComparePosition eCmpPos,
59 						const SwPosition& rSttPos, const SwPosition& rEndPos,
60 						SwRedline& rRedl, sal_Bool bCopyNext );
61 	~SwRedlineSaveData();
62 	void RedlineToDoc( SwPaM& rPam );
63 	SwNodeIndex* GetMvSttIdx() const
64 		{ return SwUndoSaveSection::GetMvSttIdx(); }
65 
66 #ifdef DBG_UTIL
67     sal_uInt16 nRedlineCount;
68 #endif
69 };
70 
71 SV_IMPL_PTRARR( SwRedlineSaveDatas, SwRedlineSaveDataPtr )
72 
73 
74 //------------------------------------------------------------
75 
76 // Diese Klasse speichert den Pam als sal_uInt16's und kann diese wieder zu
77 
78 // einem PaM zusammensetzen
79 SwUndRng::SwUndRng()
80 	: nSttNode( 0 ), nEndNode( 0 ), nSttCntnt( 0 ), nEndCntnt( 0 )
81 {
82 }
83 
84 SwUndRng::SwUndRng( const SwPaM& rPam )
85 {
86 	SetValues( rPam );
87 }
88 
89 void SwUndRng::SetValues( const SwPaM& rPam )
90 {
91 	const SwPosition *pStt = rPam.Start();
92 	if( rPam.HasMark() )
93 	{
94 		const SwPosition *pEnd = rPam.GetPoint() == pStt
95 						? rPam.GetMark()
96 						: rPam.GetPoint();
97 		nEndNode = pEnd->nNode.GetIndex();
98 		nEndCntnt = pEnd->nContent.GetIndex();
99 	}
100 	else
101 		// keine Selektion !!
102 		nEndNode = 0, nEndCntnt = STRING_MAXLEN;
103 
104 	nSttNode = pStt->nNode.GetIndex();
105 	nSttCntnt = pStt->nContent.GetIndex();
106 }
107 
108 void SwUndRng::SetPaM( SwPaM & rPam, sal_Bool bCorrToCntnt ) const
109 {
110 	rPam.DeleteMark();
111 	rPam.GetPoint()->nNode = nSttNode;
112 	SwNode* pNd = rPam.GetNode();
113 	if( pNd->IsCntntNode() )
114 		rPam.GetPoint()->nContent.Assign( pNd->GetCntntNode(), nSttCntnt );
115 	else if( bCorrToCntnt )
116 		rPam.Move( fnMoveForward, fnGoCntnt );
117 	else
118 		rPam.GetPoint()->nContent.Assign( 0, 0 );
119 
120 	if( !nEndNode && STRING_MAXLEN == nEndCntnt )		// keine Selection
121 		return ;
122 
123 	rPam.SetMark();
124 	if( nSttNode == nEndNode && nSttCntnt == nEndCntnt )
125 		return;								// nichts mehr zu tun
126 
127 	rPam.GetPoint()->nNode = nEndNode;
128 	if( (pNd = rPam.GetNode())->IsCntntNode() )
129 		rPam.GetPoint()->nContent.Assign( pNd->GetCntntNode(), nEndCntnt );
130 	else if( bCorrToCntnt )
131 		rPam.Move( fnMoveBackward, fnGoCntnt );
132 	else
133 		rPam.GetPoint()->nContent.Assign( 0, 0 );
134 }
135 
136 SwPaM & SwUndRng::AddUndoRedoPaM(
137         ::sw::UndoRedoContext & rContext, bool const bCorrToCntnt) const
138 {
139     SwPaM & rPaM( rContext.GetCursorSupplier().CreateNewShellCursor() );
140     SetPaM( rPaM, bCorrToCntnt );
141     return rPaM;
142 }
143 
144 
145 //------------------------------------------------------------
146 
147 
148 void SwUndo::RemoveIdxFromSection( SwDoc& rDoc, sal_uLong nSttIdx,
149 									sal_uLong* pEndIdx )
150 {
151 	SwNodeIndex aIdx( rDoc.GetNodes(), nSttIdx );
152 	SwNodeIndex aEndIdx( rDoc.GetNodes(), pEndIdx ? *pEndIdx
153 									: aIdx.GetNode().EndOfSectionIndex() );
154 	SwPosition aPos( rDoc.GetNodes().GetEndOfPostIts() );
155 	rDoc.CorrAbs( aIdx, aEndIdx, aPos, sal_True );
156 }
157 
158 void SwUndo::RemoveIdxFromRange( SwPaM& rPam, sal_Bool bMoveNext )
159 {
160 	const SwPosition* pEnd = rPam.End();
161 	if( bMoveNext )
162 	{
163 		if( pEnd != rPam.GetPoint() )
164 			rPam.Exchange();
165 
166 		SwNodeIndex aStt( rPam.GetMark()->nNode );
167 		SwNodeIndex aEnd( rPam.GetPoint()->nNode );
168 
169 		if( !rPam.Move( fnMoveForward ) )
170 		{
171 			rPam.Exchange();
172 			if( !rPam.Move( fnMoveBackward ) )
173 			{
174 				rPam.GetPoint()->nNode = rPam.GetDoc()->GetNodes().GetEndOfPostIts();
175 				rPam.GetPoint()->nContent.Assign( 0, 0 );
176 			}
177 		}
178 
179 		rPam.GetDoc()->CorrAbs( aStt, aEnd, *rPam.GetPoint(), sal_True );
180 	}
181 	else
182 		rPam.GetDoc()->CorrAbs( rPam, *pEnd, sal_True );
183 }
184 
185 void SwUndo::RemoveIdxRel( sal_uLong nIdx, const SwPosition& rPos )
186 {
187 	// nur die Crsr verschieben; die Bookmarks/TOXMarks/.. werden vom
188 	// entsp. JoinNext/JoinPrev erledigt!
189 	SwNodeIndex aIdx( rPos.nNode.GetNode().GetNodes(), nIdx );
190 	::PaMCorrRel( aIdx, rPos );
191 }
192 
193 SwUndo::SwUndo(SwUndoId const nId)
194     : m_nId(nId), nOrigRedlineMode(nsRedlineMode_t::REDLINE_NONE),
195       bCacheComment(true), pComment(NULL)
196 {
197 }
198 
199 bool SwUndo::IsDelBox() const
200 {
201     return GetId() == UNDO_COL_DELETE || GetId() == UNDO_ROW_DELETE ||
202         GetId() == UNDO_TABLE_DELBOX;
203 }
204 
205 SwUndo::~SwUndo()
206 {
207     delete pComment;
208 }
209 
210 
211 class UndoRedoRedlineGuard
212 {
213 public:
214     UndoRedoRedlineGuard(::sw::UndoRedoContext & rContext, SwUndo & rUndo)
215         : m_rRedlineAccess(rContext.GetDoc())
216         , m_eMode(m_rRedlineAccess.GetRedlineMode())
217     {
218         RedlineMode_t const eTmpMode =
219             static_cast<RedlineMode_t>(rUndo.GetRedlineMode());
220         if ((nsRedlineMode_t::REDLINE_SHOW_MASK & eTmpMode) !=
221             (nsRedlineMode_t::REDLINE_SHOW_MASK & m_eMode))
222         {
223             m_rRedlineAccess.SetRedlineMode( eTmpMode );
224         }
225         m_rRedlineAccess.SetRedlineMode_intern( static_cast<RedlineMode_t>(
226                 eTmpMode | nsRedlineMode_t::REDLINE_IGNORE) );
227     }
228     ~UndoRedoRedlineGuard()
229     {
230         m_rRedlineAccess.SetRedlineMode(m_eMode);
231     }
232 private:
233     IDocumentRedlineAccess & m_rRedlineAccess;
234     RedlineMode_t const m_eMode;
235 };
236 
237 void SwUndo::Undo()
238 {
239     OSL_ENSURE(false, "SwUndo::Undo(): ERROR: must call Undo(context) instead");
240 }
241 
242 void SwUndo::Redo()
243 {
244     OSL_ENSURE(false, "SwUndo::Redo(): ERROR: must call Redo(context) instead");
245 }
246 
247 void SwUndo::UndoWithContext(SfxUndoContext & rContext)
248 {
249     ::sw::UndoRedoContext *const pContext(
250             dynamic_cast< ::sw::UndoRedoContext * >(& rContext));
251     OSL_ASSERT(pContext);
252     if (!pContext) { return; }
253     UndoRedoRedlineGuard(*pContext, *this);
254     UndoImpl(*pContext);
255 }
256 
257 void SwUndo::RedoWithContext(SfxUndoContext & rContext)
258 {
259     ::sw::UndoRedoContext *const pContext(
260             dynamic_cast< ::sw::UndoRedoContext * >(& rContext));
261     OSL_ASSERT(pContext);
262     if (!pContext) { return; }
263     UndoRedoRedlineGuard(*pContext, *this);
264     RedoImpl(*pContext);
265 }
266 
267 void SwUndo::Repeat(SfxRepeatTarget & rContext)
268 {
269     ::sw::RepeatContext *const pRepeatContext(
270             dynamic_cast< ::sw::RepeatContext * >(& rContext));
271     OSL_ASSERT(pRepeatContext);
272     if (!pRepeatContext) { return; }
273     RepeatImpl(*pRepeatContext);
274 }
275 
276 sal_Bool SwUndo::CanRepeat(SfxRepeatTarget & rContext) const
277 {
278     ::sw::RepeatContext *const pRepeatContext(
279             dynamic_cast< ::sw::RepeatContext * >(& rContext));
280     OSL_ASSERT(pRepeatContext);
281     if (!pRepeatContext) { return false; }
282     return CanRepeatImpl(*pRepeatContext);
283 }
284 
285 void SwUndo::RepeatImpl( ::sw::RepeatContext & )
286 {
287 }
288 
289 bool SwUndo::CanRepeatImpl( ::sw::RepeatContext & ) const
290 {
291 //    return false;
292     return ((REPEAT_START <= GetId()) && (GetId() < REPEAT_END));
293 }
294 
295 String SwUndo::GetComment() const
296 {
297     String aResult;
298 
299     if (bCacheComment)
300     {
301         if (! pComment)
302         {
303             pComment = new String(SW_RES(UNDO_BASE + GetId()));
304 
305             SwRewriter aRewriter = GetRewriter();
306 
307             *pComment = aRewriter.Apply(*pComment);
308         }
309 
310         aResult = *pComment;
311     }
312     else
313     {
314         aResult = String(SW_RES(UNDO_BASE + GetId()));
315 
316         SwRewriter aRewriter = GetRewriter();
317 
318         aResult = aRewriter.Apply(aResult);
319     }
320 
321     return aResult;
322 }
323 
324 SwRewriter SwUndo::GetRewriter() const
325 {
326     SwRewriter aResult;
327 
328     return aResult;
329 }
330 
331 
332 //------------------------------------------------------------
333 
334 SwUndoSaveCntnt::SwUndoSaveCntnt()
335 	: pHistory( 0 )
336 {}
337 
338 SwUndoSaveCntnt::~SwUndoSaveCntnt()
339 {
340 	delete pHistory;
341 }
342 
343 	// wird fuer das Loeschen von Inhalt benoetigt. Fuer das ReDo werden
344 	// Inhalte in das UndoNodesArray verschoben. Diese Methoden fuegen
345 	// am Ende eines TextNodes fuer die Attribute einen Trenner ein.
346 	// Dadurch werden die Attribute nicht expandiert.
347 	// MoveTo.. 	verschiebt aus dem NodesArray in das UndoNodesArray
348 	// MoveFrom..	verschiebt aus dem UndoNodesArray in das NodesArray
349 
350 	// 2.8.93:	ist pEndNdIdx angebenen, wird vom Undo/Redo -Ins/DelFly
351 	//			aufgerufen. Dann soll die gesamte Section verschoben werden.
352 
353 void SwUndoSaveCntnt::MoveToUndoNds( SwPaM& rPaM, SwNodeIndex* pNodeIdx,
354 					SwIndex* pCntIdx, sal_uLong* pEndNdIdx, xub_StrLen* pEndCntIdx )
355 {
356 	SwDoc& rDoc = *rPaM.GetDoc();
357     ::sw::UndoGuard const undoGuard(rDoc.GetIDocumentUndoRedo());
358 
359 	SwNoTxtNode* pCpyNd = rPaM.GetNode()->GetNoTxtNode();
360 
361 	// jetzt kommt das eigentliche Loeschen(Verschieben)
362     SwNodes & rNds = rDoc.GetUndoManager().GetUndoNodes();
363 	SwPosition aPos( pEndNdIdx ? rNds.GetEndOfPostIts()
364 							   : rNds.GetEndOfExtras() );
365 	aPos.nNode--;
366 
367 	const SwPosition* pStt = rPaM.Start(), *pEnd = rPaM.End();
368 
369 	if( pCpyNd || pEndNdIdx || !aPos.nNode.GetNode().GetCntntNode() ||
370 		(!pStt->nContent.GetIndex() && (pStt->nNode != pEnd->nNode ||
371 				(!pStt->nNode.GetNode().GetCntntNode() ||
372 					pStt->nNode.GetNode().GetCntntNode()->Len() ==
373 						pEnd->nContent.GetIndex() ) ) ) )
374 	{
375 		aPos.nNode++;
376 		aPos.nContent = 0;
377 	}
378 	else
379 		aPos.nNode.GetNode().GetCntntNode()->MakeEndIndex( &aPos.nContent );
380 
381 	// als sal_uInt16 merken; die Indizies verschieben sich !!
382 	sal_uLong nTmpMvNode = aPos.nNode.GetIndex();
383 	xub_StrLen nTmpMvCntnt = aPos.nContent.GetIndex();
384 
385 	if( pCpyNd || pEndNdIdx )
386 	{
387 		SwNodeRange aRg( pStt->nNode, 0, pEnd->nNode, 1 );
388 		rDoc.GetNodes()._MoveNodes( aRg, rNds, aPos.nNode, sal_False );
389 		aPos.nContent = 0;
390 		aPos.nNode--;
391 	}
392 	else
393     {
394         rDoc.GetNodes().MoveRange( rPaM, aPos, rNds );
395 
396 		SwTxtNode* pTxtNd = aPos.nNode.GetNode().GetTxtNode();
397 		if( pTxtNd )		// fuege einen Trenner fuer die Attribute ein !
398 		{
399 			// weil aber beim Insert die Attribute angefasst/sprich
400 			// aus dem Array geloescht und wieder eingefuegt werden, koennen
401 			// dadurch Attribute verschwinden (z.B "Fett aus" von 10-20,
402 			// "Fett an" von 12-15, dann wird durchs Insert/Delete das
403 			// "Fett an" geloescht !! Ist hier aber nicht erwuenscht !!)
404 			// DARUM: nicht die Hints anfassen, direct den String manipulieren
405 
406 			String& rStr = (String&)pTxtNd->GetTxt();
407 			// Zur Sicherheit lieber nur wenn wirklich am Ende steht
408 			if( rStr.Len() == aPos.nContent.GetIndex() )
409 			{
410 				rStr.Insert( ' ' );
411 				++aPos.nContent;
412 			}
413 			else
414             {
415                 pTxtNd->InsertText( sal_Unicode(' '), aPos.nContent,
416                         IDocumentContentOperations::INS_NOHINTEXPAND );
417             }
418 		}
419 	}
420 	if( pEndNdIdx )
421 		*pEndNdIdx = aPos.nNode.GetIndex();
422 	if( pEndCntIdx )
423 		*pEndCntIdx = aPos.nContent.GetIndex();
424 
425 	// alte Position
426 	aPos.nNode = nTmpMvNode;
427 	if( pNodeIdx )
428 		*pNodeIdx = aPos.nNode;
429 
430 	if( pCntIdx )
431 	{
432 		SwCntntNode* pCNd = aPos.nNode.GetNode().GetCntntNode();
433 		if( pCNd )
434 			pCntIdx->Assign( pCNd, nTmpMvCntnt );
435 		else
436 			pCntIdx->Assign( 0, 0 );
437 	}
438 }
439 
440 void SwUndoSaveCntnt::MoveFromUndoNds( SwDoc& rDoc, sal_uLong nNodeIdx,
441 							xub_StrLen nCntIdx, SwPosition& rInsPos,
442 							sal_uLong* pEndNdIdx, xub_StrLen* pEndCntIdx )
443 {
444 	// jetzt kommt das wiederherstellen
445     SwNodes & rNds = rDoc.GetUndoManager().GetUndoNodes();
446 	if( nNodeIdx == rNds.GetEndOfPostIts().GetIndex() )
447 		return;		// nichts gespeichert
448 
449     ::sw::UndoGuard const undoGuard(rDoc.GetIDocumentUndoRedo());
450 
451 	SwPaM aPaM( rInsPos );
452 	if( pEndNdIdx )			// dann hole aus diesem den Bereich
453 		aPaM.GetPoint()->nNode.Assign( rNds, *pEndNdIdx );
454 	else
455 	{
456 		aPaM.GetPoint()->nNode = rNds.GetEndOfExtras();
457 		GoInCntnt( aPaM, fnMoveBackward );
458 	}
459 
460 	SwTxtNode* pTxtNd = aPaM.GetNode()->GetTxtNode();
461 	if( !pEndNdIdx && pTxtNd )	// loesche den Trenner wieder
462 	{
463 		if( pEndCntIdx )
464 			aPaM.GetPoint()->nContent.Assign( pTxtNd, *pEndCntIdx );
465 		if( pTxtNd->GetTxt().Len() )
466 		{
467 			GoInCntnt( aPaM, fnMoveBackward );
468             pTxtNd->EraseText( aPaM.GetPoint()->nContent, 1 );
469         }
470 
471 		aPaM.SetMark();
472 		aPaM.GetPoint()->nNode = nNodeIdx;
473 		aPaM.GetPoint()->nContent.Assign( aPaM.GetCntntNode(), nCntIdx );
474 
475 		_SaveRedlEndPosForRestore aRedlRest( rInsPos.nNode, rInsPos.nContent.GetIndex() );
476 
477         rNds.MoveRange( aPaM, rInsPos, rDoc.GetNodes() );
478 
479 		// noch den letzen Node loeschen.
480 		if( !aPaM.GetPoint()->nContent.GetIndex() ||
481 			( aPaM.GetPoint()->nNode++ && 		// noch leere Nodes am Ende ??
482 			&rNds.GetEndOfExtras() != &aPaM.GetPoint()->nNode.GetNode() ))
483 		{
484 			aPaM.GetPoint()->nContent.Assign( 0, 0 );
485 			aPaM.SetMark();
486 			rNds.Delete( aPaM.GetPoint()->nNode,
487 						rNds.GetEndOfExtras().GetIndex() -
488 						aPaM.GetPoint()->nNode.GetIndex() );
489 		}
490 
491 		aRedlRest.Restore();
492 	}
493 	else if( pEndNdIdx || !pTxtNd )
494 	{
495 		SwNodeRange aRg( rNds, nNodeIdx, rNds, (pEndNdIdx
496 						? ((*pEndNdIdx) + 1)
497 						: rNds.GetEndOfExtras().GetIndex() ) );
498 		rNds._MoveNodes( aRg, rDoc.GetNodes(), rInsPos.nNode, 0 == pEndNdIdx );
499 
500 	}
501 	else {
502 		ASSERT( sal_False, "was ist es denn nun?" );
503     }
504 }
505 
506 // diese beiden Methoden bewegen den Point vom Pam zurueck/vor. Damit
507 // kann fuer ein Undo/Redo ein Bereich aufgespannt werden. (Der
508 // Point liegt dann vor dem manipuliertem Bereich !!)
509 // Das Flag gibt an, ob noch vorm Point Inhalt steht.
510 
511 sal_Bool SwUndoSaveCntnt::MovePtBackward( SwPaM& rPam )
512 {
513 	rPam.SetMark();
514 	if( rPam.Move( fnMoveBackward ))
515 		return sal_True;
516 
517 	// gibt es nach vorne keinen Inhalt mehr, so setze den Point einfach
518 	// auf die vorherige Position (Node und Content, damit der Content
519 	// abgemeldet wird !!)
520 	rPam.GetPoint()->nNode--;
521 	rPam.GetPoint()->nContent.Assign( 0, 0 );
522 	return sal_False;
523 }
524 
525 void SwUndoSaveCntnt::MovePtForward( SwPaM& rPam, sal_Bool bMvBkwrd )
526 {
527 	// gab es noch Inhalt vor der Position ?
528 	if( bMvBkwrd )
529 		rPam.Move( fnMoveForward );
530 	else
531 	{                       // setzen Point auf die naechste Position
532 		rPam.GetPoint()->nNode++;
533 		SwCntntNode* pCNd = rPam.GetCntntNode();
534 		if( pCNd )
535 			pCNd->MakeStartIndex( &rPam.GetPoint()->nContent );
536 		else
537 			rPam.Move( fnMoveForward );
538 	}
539 }
540 
541 
542 /*
543    JP 21.03.94: loesche alle Objecte, die ContentIndizies auf den ang.
544 				Bereich besitzen.
545 				Zur Zeit gibts folgende Objecte
546 					- Fussnoten
547 					- Flys
548 					- Bookmarks
549 					- Verzeichnisse
550 */
551 // --> OD 2007-10-17 #i81002# - extending method:
552 // delete certain (not all) cross-reference bookmarks at text node of <rMark>
553 // and at text node of <rPoint>, if these text nodes aren't the same.
554 void SwUndoSaveCntnt::DelCntntIndex( const SwPosition& rMark,
555 								     const SwPosition& rPoint,
556 									 DelCntntType nDelCntntType )
557 {
558 	const SwPosition *pStt = rMark < rPoint ? &rMark : &rPoint,
559 					*pEnd = &rMark == pStt ? &rPoint : &rMark;
560 
561 	SwDoc* pDoc = rMark.nNode.GetNode().GetDoc();
562 
563     ::sw::UndoGuard const undoGuard(pDoc->GetIDocumentUndoRedo());
564 
565 	// 1. Fussnoten
566     if( nsDelCntntType::DELCNT_FTN & nDelCntntType )
567 	{
568 		SwFtnIdxs& rFtnArr = pDoc->GetFtnIdxs();
569 		if( rFtnArr.Count() )
570 		{
571 			const SwNode* pFtnNd;
572 			sal_uInt16 nPos;
573 			rFtnArr.SeekEntry( pStt->nNode, &nPos );
574 			SwTxtFtn* pSrch;
575 
576 			// loesche erstmal alle, die dahinter stehen
577 			while( nPos < rFtnArr.Count() && ( pFtnNd =
578 				&( pSrch = rFtnArr[ nPos ] )->GetTxtNode())->GetIndex()
579 						<= pEnd->nNode.GetIndex() )
580 			{
581 				xub_StrLen nFtnSttIdx = *pSrch->GetStart();
582                 if( (nsDelCntntType::DELCNT_CHKNOCNTNT & nDelCntntType )
583 					? (&pEnd->nNode.GetNode() == pFtnNd )
584 					: (( &pStt->nNode.GetNode() == pFtnNd &&
585 					pStt->nContent.GetIndex() > nFtnSttIdx) ||
586 					( &pEnd->nNode.GetNode() == pFtnNd &&
587 					nFtnSttIdx >= pEnd->nContent.GetIndex() )) )
588 				{
589 					++nPos;		// weiter suchen
590 					continue;
591 				}
592 
593 				// es muss leider ein Index angelegt werden. Sonst knallts im
594 				// TextNode, weil im DTOR der SwFtn dieser geloescht wird !!
595 				SwTxtNode* pTxtNd = (SwTxtNode*)pFtnNd;
596 				if( !pHistory )
597 					pHistory = new SwHistory;
598                 SwTxtAttr* const pFtnHnt =
599                     pTxtNd->GetTxtAttrForCharAt( nFtnSttIdx );
600 				ASSERT( pFtnHnt, "kein FtnAttribut" );
601 				SwIndex aIdx( pTxtNd, nFtnSttIdx );
602                 pHistory->Add( pFtnHnt, pTxtNd->GetIndex(), false );
603                 pTxtNd->EraseText( aIdx, 1 );
604             }
605 
606 			while( nPos-- && ( pFtnNd = &( pSrch = rFtnArr[ nPos ] )->
607 					GetTxtNode())->GetIndex() >= pStt->nNode.GetIndex() )
608 			{
609 				xub_StrLen nFtnSttIdx = *pSrch->GetStart();
610                 if( !(nsDelCntntType::DELCNT_CHKNOCNTNT & nDelCntntType) && (
611 					( &pStt->nNode.GetNode() == pFtnNd &&
612 					pStt->nContent.GetIndex() > nFtnSttIdx ) ||
613 					( &pEnd->nNode.GetNode() == pFtnNd &&
614 					nFtnSttIdx >= pEnd->nContent.GetIndex() )))
615 					continue;				// weiter suchen
616 
617 				// es muss leider ein Index angelegt werden. Sonst knallts im
618 				// TextNode, weil im DTOR der SwFtn dieser geloescht wird !!
619 				SwTxtNode* pTxtNd = (SwTxtNode*)pFtnNd;
620 				if( !pHistory )
621 					pHistory = new SwHistory;
622                 SwTxtAttr* const pFtnHnt =
623                     pTxtNd->GetTxtAttrForCharAt( nFtnSttIdx );
624 				ASSERT( pFtnHnt, "kein FtnAttribut" );
625 				SwIndex aIdx( pTxtNd, nFtnSttIdx );
626                 pHistory->Add( pFtnHnt, pTxtNd->GetIndex(), false );
627                 pTxtNd->EraseText( aIdx, 1 );
628             }
629         }
630     }
631 
632 	// 2. Flys
633     if( nsDelCntntType::DELCNT_FLY & nDelCntntType )
634 	{
635 		sal_uInt16 nChainInsPos = pHistory ? pHistory->Count() : 0;
636 		const SwSpzFrmFmts& rSpzArr = *pDoc->GetSpzFrmFmts();
637 		if( rSpzArr.Count() )
638 		{
639 			const sal_Bool bDelFwrd = rMark.nNode.GetIndex() <= rPoint.nNode.GetIndex();
640 			SwFlyFrmFmt* pFmt;
641 			const SwFmtAnchor* pAnchor;
642 			sal_uInt16 n = rSpzArr.Count();
643 			const SwPosition* pAPos;
644 
645 			while( n && rSpzArr.Count() )
646 			{
647 				pFmt = (SwFlyFrmFmt*)rSpzArr[--n];
648 				pAnchor = &pFmt->GetAnchor();
649 				switch( pAnchor->GetAnchorId() )
650 				{
651                 case FLY_AS_CHAR:
652 					if( 0 != (pAPos = pAnchor->GetCntntAnchor() ) &&
653                         (( nsDelCntntType::DELCNT_CHKNOCNTNT & nDelCntntType )
654 						? ( pStt->nNode <= pAPos->nNode &&
655 							pAPos->nNode < pEnd->nNode )
656 						: ( *pStt <= *pAPos && *pAPos < *pEnd )) )
657 					{
658 						if( !pHistory )
659 							pHistory = new SwHistory;
660                         SwTxtNode *const pTxtNd =
661                             pAPos->nNode.GetNode().GetTxtNode();
662                         SwTxtAttr* const pFlyHnt = pTxtNd->GetTxtAttrForCharAt(
663                             pAPos->nContent.GetIndex());
664 						ASSERT( pFlyHnt, "kein FlyAttribut" );
665                         pHistory->Add( pFlyHnt, 0, false );
666 						// n wieder zurueck, damit nicht ein Format uebesprungen wird !
667 						n = n >= rSpzArr.Count() ? rSpzArr.Count() : n+1;
668 					}
669 					break;
670                 case FLY_AT_PARA:
671                     {
672                         pAPos =  pAnchor->GetCntntAnchor();
673                         if( pAPos )
674                         {
675                             bool bTmp;
676                             if( nsDelCntntType::DELCNT_CHKNOCNTNT & nDelCntntType )
677                                 bTmp = pStt->nNode <= pAPos->nNode && pAPos->nNode < pEnd->nNode;
678                             else
679                             {
680                                 if (bDelFwrd)
681                                     bTmp = rMark.nNode < pAPos->nNode &&
682                                         pAPos->nNode <= rPoint.nNode;
683                                 else
684                                     bTmp = rPoint.nNode <= pAPos->nNode &&
685                                         pAPos->nNode < rMark.nNode;
686                             }
687 
688                             if (bTmp)
689                             {
690                                 if( !pHistory )
691                                     pHistory = new SwHistory;
692 
693                                 // Moving the anchor?
694                                 if( !( nsDelCntntType::DELCNT_CHKNOCNTNT & nDelCntntType ) &&
695                                     ( rPoint.nNode.GetIndex() == pAPos->nNode.GetIndex() ) )
696                                 {
697                                     // Do not try to move the anchor to a table!
698                                     if( rMark.nNode.GetNode().GetTxtNode() )
699                                     {
700                                         pHistory->Add( *pFmt );
701                                         SwFmtAnchor aAnch( *pAnchor );
702                                         SwPosition aPos( rMark.nNode );
703                                         aAnch.SetAnchor( &aPos );
704                                         pFmt->SetFmtAttr( aAnch );
705                                     }
706                                 }
707                                 else
708                                 {
709                                     pHistory->Add( *pFmt, nChainInsPos );
710                                     // n wieder zurueck, damit nicht ein
711                                     // Format uebesprungen wird !
712                                     n = n >= rSpzArr.Count() ?
713                                         rSpzArr.Count() : n+1;
714                                 }
715                             }
716                         }
717                     }
718 					break;
719                 case FLY_AT_CHAR:
720 					if( 0 != (pAPos = pAnchor->GetCntntAnchor() ) &&
721 						( pStt->nNode <= pAPos->nNode && pAPos->nNode <= pEnd->nNode ) )
722 					{
723 						if( !pHistory )
724 							pHistory = new SwHistory;
725                         if (IsDestroyFrameAnchoredAtChar(
726                                 *pAPos, *pStt, *pEnd, nDelCntntType))
727                         {
728 							pHistory->Add( *pFmt, nChainInsPos );
729 							n = n >= rSpzArr.Count() ? rSpzArr.Count() : n+1;
730 						}
731                         else if( !( nsDelCntntType::DELCNT_CHKNOCNTNT & nDelCntntType ) )
732                         {
733                             if( *pStt <= *pAPos && *pAPos < *pEnd )
734                             {
735                                 // These are the objects anchored
736                                 // between section start and end position
737                                 // Do not try to move the anchor to a table!
738                                 if( rMark.nNode.GetNode().GetTxtNode() )
739                                 {
740                                     pHistory->Add( *pFmt );
741                                     SwFmtAnchor aAnch( *pAnchor );
742                                     aAnch.SetAnchor( &rMark );
743                                     pFmt->SetFmtAttr( aAnch );
744                                 }
745                             }
746                         }
747 					}
748 					break;
749 				case FLY_AT_FLY:
750 
751 					if( 0 != (pAPos = pAnchor->GetCntntAnchor() ) &&
752 						pStt->nNode == pAPos->nNode )
753 					{
754 						if( !pHistory )
755 							pHistory = new SwHistory;
756 
757 						pHistory->Add( *pFmt, nChainInsPos );
758 
759 						// n wieder zurueck, damit nicht ein Format uebesprungen wird !
760 						n = n >= rSpzArr.Count() ? rSpzArr.Count() : n+1;
761 					}
762 					break;
763 				default: break;
764 				}
765 			}
766 		}
767 	}
768 
769 	// 3. Bookmarks
770     if( nsDelCntntType::DELCNT_BKM & nDelCntntType )
771 	{
772         IDocumentMarkAccess* const pMarkAccess = pDoc->getIDocumentMarkAccess();
773 		if( pMarkAccess->getMarksCount() )
774 		{
775 
776 			for( sal_uInt16 n = 0; n < pMarkAccess->getMarksCount(); ++n )
777 			{
778                 // --> OD 2007-10-17 #i81002#
779                 bool bSavePos = false;
780                 bool bSaveOtherPos = false;
781                 const ::sw::mark::IMark* pBkmk = (pMarkAccess->getMarksBegin() + n)->get();
782                 if( nsDelCntntType::DELCNT_CHKNOCNTNT & nDelCntntType )
783                 {
784                     if( pStt->nNode <= pBkmk->GetMarkPos().nNode &&
785                         pBkmk->GetMarkPos().nNode < pEnd->nNode )
786                         bSavePos = true;
787                     if( pBkmk->IsExpanded() &&
788                         pStt->nNode <= pBkmk->GetOtherMarkPos().nNode &&
789                         pBkmk->GetOtherMarkPos().nNode < pEnd->nNode )
790                         bSaveOtherPos = true;
791                 }
792                 else
793                 {
794                     // --> OD 2009-08-06 #i92125#
795                     bool bKeepCrossRefBkmk( false );
796                     {
797                         if ( rMark.nNode == rPoint.nNode &&
798                              ( IDocumentMarkAccess::GetType(*pBkmk) ==
799                                 IDocumentMarkAccess::CROSSREF_HEADING_BOOKMARK ||
800                                IDocumentMarkAccess::GetType(*pBkmk) ==
801                                 IDocumentMarkAccess::CROSSREF_NUMITEM_BOOKMARK ) )
802                         {
803                             bKeepCrossRefBkmk = true;
804                         }
805                     }
806                     if ( !bKeepCrossRefBkmk )
807                     {
808                         bool bMaybe = false;
809                         if ( *pStt <= pBkmk->GetMarkPos() && pBkmk->GetMarkPos() <= *pEnd )
810                         {
811                             if( pBkmk->GetMarkPos() == *pEnd ||
812                                 ( *pStt == pBkmk->GetMarkPos() && pBkmk->IsExpanded() ) )
813                                 bMaybe = true;
814                             else
815                                 bSavePos = true;
816                         }
817                         if( pBkmk->IsExpanded() &&
818                             *pStt <= pBkmk->GetOtherMarkPos() && pBkmk->GetOtherMarkPos() <= *pEnd )
819                         {
820                             if( bSavePos || bSaveOtherPos ||
821                                 ( pBkmk->GetOtherMarkPos() < *pEnd && pBkmk->GetOtherMarkPos() > *pStt ) )
822                             {
823                                 if( bMaybe )
824                                     bSavePos = true;
825                                 bSaveOtherPos = true;
826                             }
827                         }
828                     }
829                     // <--
830 
831                     // --> OD 2007-10-17 #i81002#
832                     const bool bDifferentTxtNodesAtMarkAndPoint(
833                                         rMark.nNode != rPoint.nNode &&
834                                         rMark.nNode.GetNode().GetTxtNode() &&
835                                         rPoint.nNode.GetNode().GetTxtNode() );
836                     // <--
837                     if( !bSavePos && !bSaveOtherPos && bDifferentTxtNodesAtMarkAndPoint &&
838                         dynamic_cast< const ::sw::mark::CrossRefBookmark* >(pBkmk))
839                     {
840                         // delete cross-reference bookmark at <pStt>, if only part of
841                         // <pEnd> text node content is deleted.
842                         if( pStt->nNode == pBkmk->GetMarkPos().nNode &&
843                             pEnd->nContent.GetIndex() !=
844                                 pEnd->nNode.GetNode().GetTxtNode()->Len() )
845                         {
846                             bSavePos = true;
847                             bSaveOtherPos = false;
848                         }
849                         // delete cross-reference bookmark at <pEnd>, if only part of
850                         // <pStt> text node content is deleted.
851                         else if( pEnd->nNode == pBkmk->GetMarkPos().nNode &&
852                             pStt->nContent.GetIndex() != 0 )
853                         {
854                             bSavePos = true;
855                             bSaveOtherPos = false;
856                         }
857                     }
858                 }
859 				if( bSavePos || bSaveOtherPos )
860 				{
861 					if( !pHistory )
862 						pHistory = new SwHistory;
863 
864 					pHistory->Add( *pBkmk, bSavePos, bSaveOtherPos );
865                     if(bSavePos &&
866                         (bSaveOtherPos || !pBkmk->IsExpanded()))
867                     {
868                         pMarkAccess->deleteMark(pMarkAccess->getMarksBegin()+n);
869                         n--;
870                     }
871 				}
872 			}
873 		}
874 	}
875 }
876 
877 
878 // sicher eine vollstaendige Section im Undo-Nodes-Array
879 
880 SwUndoSaveSection::SwUndoSaveSection()
881 	: pMvStt( 0 ), pRedlSaveData( 0 ), nMvLen( 0 ), nStartPos( ULONG_MAX )
882 {
883 }
884 
885 SwUndoSaveSection::~SwUndoSaveSection()
886 {
887 	if( pMvStt )		// loesche noch den Bereich aus dem UndoNodes Array
888 	{
889 		// SaveSection speichert den Inhalt in der PostIt-Section
890 		SwNodes& rUNds = pMvStt->GetNode().GetNodes();
891 		rUNds.Delete( *pMvStt, nMvLen );
892 
893 		delete pMvStt;
894 	}
895 	delete pRedlSaveData;
896 }
897 
898 void SwUndoSaveSection::SaveSection( SwDoc* pDoc, const SwNodeIndex& rSttIdx )
899 {
900 	SwNodeRange aRg( rSttIdx.GetNode(), *rSttIdx.GetNode().EndOfSectionNode() );
901 	SaveSection( pDoc, aRg );
902 }
903 
904 
905 void SwUndoSaveSection::SaveSection( SwDoc* , const SwNodeRange& rRange )
906 {
907 	SwPaM aPam( rRange.aStart, rRange.aEnd );
908 
909 	// loesche alle Fussnoten / FlyFrames / Bookmarks / Verzeichnisse
910 	DelCntntIndex( *aPam.GetMark(), *aPam.GetPoint() );
911 
912 	pRedlSaveData = new SwRedlineSaveDatas;
913 	if( !SwUndo::FillSaveData( aPam, *pRedlSaveData, sal_True, sal_True ))
914 		delete pRedlSaveData, pRedlSaveData = 0;
915 
916 	nStartPos = rRange.aStart.GetIndex();
917 
918 	aPam.GetPoint()->nNode--;
919 	aPam.GetMark()->nNode++;
920 
921 	SwCntntNode* pCNd = aPam.GetCntntNode( sal_False );
922 	if( pCNd )
923 		aPam.GetMark()->nContent.Assign( pCNd, 0 );
924 	if( 0 != ( pCNd = aPam.GetCntntNode( sal_True )) )
925 		aPam.GetPoint()->nContent.Assign( pCNd, pCNd->Len() );
926 
927 	// Positionen als SwIndex merken, damit im DTOR dieser Bereich
928 	// entfernt werden kann !!
929 	sal_uLong nEnd;
930 	pMvStt = new SwNodeIndex( rRange.aStart );
931 	MoveToUndoNds( aPam, pMvStt, 0, &nEnd, 0 );
932 	nMvLen = nEnd - pMvStt->GetIndex() + 1;
933 }
934 
935 void SwUndoSaveSection::RestoreSection( SwDoc* pDoc, SwNodeIndex* pIdx,
936 										sal_uInt16 nSectType )
937 {
938 	if( ULONG_MAX != nStartPos )		// gab es ueberhaupt Inhalt ?
939 	{
940 		// ueberpruefe, ob der Inhalt an der alten Position steht
941 		SwNodeIndex aSttIdx( pDoc->GetNodes(), nStartPos );
942         OSL_ENSURE(!aSttIdx.GetNode().GetCntntNode(),
943                 "RestoreSection(): Position on content node");
944 
945 		// move den Inhalt aus dem UndoNodes-Array in den Fly
946 		SwStartNode* pSttNd = pDoc->GetNodes().MakeEmptySection( aSttIdx,
947 												(SwStartNodeType)nSectType );
948 
949 		RestoreSection( pDoc, SwNodeIndex( *pSttNd->EndOfSectionNode() ));
950 
951 		if( pIdx )
952 			*pIdx = *pSttNd;
953 	}
954 }
955 
956 void SwUndoSaveSection::RestoreSection( SwDoc* pDoc, const SwNodeIndex& rInsPos )
957 {
958 	if( ULONG_MAX != nStartPos )		// gab es ueberhaupt Inhalt ?
959 	{
960 		SwPosition aInsPos( rInsPos );
961 		sal_uLong nEnd = pMvStt->GetIndex() + nMvLen - 1;
962 		MoveFromUndoNds( *pDoc, pMvStt->GetIndex(), 0, aInsPos, &nEnd, 0 );
963 
964 		// Indizies wieder zerstoren, Inhalt ist aus dem UndoNodes-Array
965 		// entfernt worden.
966 		DELETEZ( pMvStt );
967 		nMvLen = 0;
968 
969 		if( pRedlSaveData )
970 		{
971 			SwUndo::SetSaveData( *pDoc, *pRedlSaveData );
972 			delete pRedlSaveData, pRedlSaveData = 0;
973 		}
974 	}
975 }
976 
977 		// sicher und setze die RedlineDaten
978 
979 SwRedlineSaveData::SwRedlineSaveData( SwComparePosition eCmpPos,
980 										const SwPosition& rSttPos,
981 										const SwPosition& rEndPos,
982 										SwRedline& rRedl,
983 										sal_Bool bCopyNext )
984 	: SwUndRng( rRedl ),
985 	SwRedlineData( rRedl.GetRedlineData(), bCopyNext )
986 {
987 	ASSERT( POS_OUTSIDE == eCmpPos ||
988 			!rRedl.GetContentIdx(), "Redline mit Content" );
989 
990 	switch( eCmpPos )
991 	{
992 	case POS_OVERLAP_BEFORE:		// Pos1 ueberlappt Pos2 am Anfang
993 		nEndNode = rEndPos.nNode.GetIndex();
994 		nEndCntnt = rEndPos.nContent.GetIndex();
995 		break;
996 	case POS_OVERLAP_BEHIND: 		// Pos1 ueberlappt Pos2 am Ende
997 		nSttNode = rSttPos.nNode.GetIndex();
998 		nSttCntnt = rSttPos.nContent.GetIndex();
999 		break;
1000 
1001 	case POS_INSIDE:				// Pos1 liegt vollstaendig in Pos2
1002 		nSttNode = rSttPos.nNode.GetIndex();
1003 		nSttCntnt = rSttPos.nContent.GetIndex();
1004 		nEndNode = rEndPos.nNode.GetIndex();
1005 		nEndCntnt = rEndPos.nContent.GetIndex();
1006 		break;
1007 
1008 	case POS_OUTSIDE:				// Pos2 liegt vollstaendig in Pos1
1009 		if( rRedl.GetContentIdx() )
1010 		{
1011 			// dann den Bereich ins UndoArray verschieben und merken
1012 			SaveSection( rRedl.GetDoc(), *rRedl.GetContentIdx() );
1013 			rRedl.SetContentIdx( 0 );
1014 		}
1015 		break;
1016 
1017 	case POS_EQUAL:					// Pos1 ist genauso gross wie Pos2
1018 		break;
1019 
1020 	default:
1021 		ASSERT( !this, "keine gueltigen Daten!" )
1022 	}
1023 
1024 #ifdef DBG_UTIL
1025     nRedlineCount = rSttPos.nNode.GetNode().GetDoc()->GetRedlineTbl().Count();
1026 #endif
1027 }
1028 
1029 SwRedlineSaveData::~SwRedlineSaveData()
1030 {
1031 }
1032 
1033 void SwRedlineSaveData::RedlineToDoc( SwPaM& rPam )
1034 {
1035 	SwDoc& rDoc = *rPam.GetDoc();
1036 	SwRedline* pRedl = new SwRedline( *this, rPam );
1037 
1038 	if( GetMvSttIdx() )
1039 	{
1040 		SwNodeIndex aIdx( rDoc.GetNodes() );
1041 		RestoreSection( &rDoc, &aIdx, SwNormalStartNode );
1042 		if( GetHistory() )
1043 			GetHistory()->Rollback( &rDoc );
1044 		pRedl->SetContentIdx( &aIdx );
1045 	}
1046 	SetPaM( *pRedl );
1047 	// erstmal die "alten" entfernen, damit im Append keine unerwarteten
1048 	// Dinge passieren, wie z.B. eine Delete in eigenen Insert. Dann wird
1049 	// naehmlich das gerade restaurierte wieder geloescht - nicht das gewollte
1050     rDoc.DeleteRedline( *pRedl, false, USHRT_MAX );
1051 
1052     RedlineMode_t eOld = rDoc.GetRedlineMode();
1053     rDoc.SetRedlineMode_intern((RedlineMode_t)(eOld | nsRedlineMode_t::REDLINE_DONTCOMBINE_REDLINES));
1054 	//#i92154# let UI know about a new redline with comment
1055 	if (rDoc.GetDocShell() && (pRedl->GetComment() != String(::rtl::OUString::createFromAscii(""))) )
1056 		rDoc.GetDocShell()->Broadcast(SwRedlineHint(pRedl,SWREDLINE_INSERTED));
1057 	//
1058 #if OSL_DEBUG_LEVEL > 0
1059     bool const bSuccess =
1060 #endif
1061         rDoc.AppendRedline( pRedl, true );
1062     OSL_ENSURE(bSuccess,
1063         "SwRedlineSaveData::RedlineToDoc: insert redline failed");
1064 	rDoc.SetRedlineMode_intern( eOld );
1065 }
1066 
1067 sal_Bool SwUndo::FillSaveData( const SwPaM& rRange, SwRedlineSaveDatas& rSData,
1068 							sal_Bool bDelRange, sal_Bool bCopyNext )
1069 {
1070 	if( rSData.Count() )
1071 		rSData.DeleteAndDestroy( 0, rSData.Count() );
1072 
1073 	SwRedlineSaveData* pNewData;
1074 	const SwPosition *pStt = rRange.Start(), *pEnd = rRange.End();
1075 	const SwRedlineTbl& rTbl = rRange.GetDoc()->GetRedlineTbl();
1076 	sal_uInt16 n = 0;
1077 	rRange.GetDoc()->GetRedline( *pStt, &n );
1078 	for( ; n < rTbl.Count(); ++n )
1079 	{
1080 		SwRedline* pRedl = rTbl[ n ];
1081 		const SwPosition *pRStt = pRedl->Start(), *pREnd = pRedl->End();
1082 
1083 		SwComparePosition eCmpPos = ComparePosition( *pStt, *pEnd, *pRStt, *pREnd );
1084 		if( POS_BEFORE != eCmpPos && POS_BEHIND != eCmpPos &&
1085 			POS_COLLIDE_END != eCmpPos && POS_COLLIDE_START != eCmpPos )
1086 		{
1087 			pNewData = new SwRedlineSaveData( eCmpPos, *pStt, *pEnd,
1088 												*pRedl, bCopyNext );
1089 			rSData.Insert( pNewData, rSData.Count() );
1090 		}
1091 	}
1092 	if( rSData.Count() && bDelRange )
1093         rRange.GetDoc()->DeleteRedline( rRange, false, USHRT_MAX );
1094 	return 0 != rSData.Count();
1095 }
1096 
1097 sal_Bool SwUndo::FillSaveDataForFmt( const SwPaM& rRange, SwRedlineSaveDatas& rSData )
1098 {
1099 	if( rSData.Count() )
1100 		rSData.DeleteAndDestroy( 0, rSData.Count() );
1101 
1102 	SwRedlineSaveData* pNewData;
1103 	const SwPosition *pStt = rRange.Start(), *pEnd = rRange.End();
1104 	const SwRedlineTbl& rTbl = rRange.GetDoc()->GetRedlineTbl();
1105 	sal_uInt16 n = 0;
1106 	rRange.GetDoc()->GetRedline( *pStt, &n );
1107 	for( ; n < rTbl.Count(); ++n )
1108 	{
1109 		SwRedline* pRedl = rTbl[ n ];
1110         if( nsRedlineType_t::REDLINE_FORMAT == pRedl->GetType() )
1111 		{
1112 			const SwPosition *pRStt = pRedl->Start(), *pREnd = pRedl->End();
1113 
1114 			SwComparePosition eCmpPos = ComparePosition( *pStt, *pEnd, *pRStt, *pREnd );
1115 			if( POS_BEFORE != eCmpPos && POS_BEHIND != eCmpPos &&
1116 				POS_COLLIDE_END != eCmpPos && POS_COLLIDE_START != eCmpPos )
1117 			{
1118 				pNewData = new SwRedlineSaveData( eCmpPos, *pStt, *pEnd,
1119 													*pRedl, sal_True );
1120 				rSData.Insert( pNewData, rSData.Count() );
1121 			}
1122 
1123 
1124 		}
1125 	}
1126 	return 0 != rSData.Count();
1127 }
1128 
1129 void SwUndo::SetSaveData( SwDoc& rDoc, const SwRedlineSaveDatas& rSData )
1130 {
1131     RedlineMode_t eOld = rDoc.GetRedlineMode();
1132     rDoc.SetRedlineMode_intern( (RedlineMode_t)(( eOld & ~nsRedlineMode_t::REDLINE_IGNORE) | nsRedlineMode_t::REDLINE_ON ));
1133 	SwPaM aPam( rDoc.GetNodes().GetEndOfContent() );
1134 
1135 	for( sal_uInt16 n = rSData.Count(); n; )
1136 		rSData[ --n ]->RedlineToDoc( aPam );
1137 
1138     // check redline count against count saved in RedlineSaveData object
1139     DBG_ASSERT( (rSData.Count() == 0) ||
1140                 (rSData[0]->nRedlineCount == rDoc.GetRedlineTbl().Count()),
1141                 "redline count not restored properly" );
1142 
1143 	rDoc.SetRedlineMode_intern( eOld );
1144 }
1145 
1146 sal_Bool SwUndo::HasHiddenRedlines( const SwRedlineSaveDatas& rSData )
1147 {
1148 	for( sal_uInt16 n = rSData.Count(); n; )
1149 		if( rSData[ --n ]->GetMvSttIdx() )
1150 			return sal_True;
1151 	return sal_False;
1152 }
1153 
1154 sal_Bool SwUndo::CanRedlineGroup( SwRedlineSaveDatas& rCurr,
1155 						const SwRedlineSaveDatas& rCheck, sal_Bool bCurrIsEnd )
1156 {
1157 	sal_Bool bRet = sal_False;
1158 	sal_uInt16 n;
1159 
1160 	if( rCurr.Count() == rCheck.Count() )
1161 	{
1162 		bRet = sal_True;
1163 		for( n = 0; n < rCurr.Count(); ++n )
1164 		{
1165 			const SwRedlineSaveData& rSet = *rCurr[ n ];
1166 			const SwRedlineSaveData& rGet = *rCheck[ n ];
1167 			if( rSet.nSttNode != rGet.nSttNode ||
1168 				rSet.GetMvSttIdx() || rGet.GetMvSttIdx() ||
1169 				( bCurrIsEnd ? rSet.nSttCntnt != rGet.nEndCntnt
1170 							 : rSet.nEndCntnt != rGet.nSttCntnt ) ||
1171 				!rGet.CanCombine( rSet ) )
1172 			{
1173 				bRet = sal_False;
1174 				break;
1175 			}
1176 		}
1177 
1178 		if( bRet )
1179 			for( n = 0; n < rCurr.Count(); ++n )
1180 			{
1181 				SwRedlineSaveData& rSet = *rCurr[ n ];
1182 				const SwRedlineSaveData& rGet = *rCheck[ n ];
1183 				if( bCurrIsEnd )
1184 					rSet.nSttCntnt = rGet.nSttCntnt;
1185 				else
1186 					rSet.nEndCntnt = rGet.nEndCntnt;
1187 			}
1188 	}
1189 	return bRet;
1190 }
1191 
1192 // #111827#
1193 String ShortenString(const String & rStr, xub_StrLen nLength, const String & rFillStr)
1194 {
1195     ASSERT( nLength - rFillStr.Len() >= 2, "improper arguments")
1196 
1197     String aResult;
1198 
1199     if (rStr.Len() <= nLength)
1200         aResult = rStr;
1201     else
1202     {
1203         long nTmpLength = nLength - rFillStr.Len();
1204         if ( nTmpLength < 2 )
1205             nTmpLength = 2;
1206 
1207         nLength = static_cast<xub_StrLen>(nTmpLength);
1208 
1209         const xub_StrLen nFrontLen = nLength - nLength / 2;
1210         const xub_StrLen nBackLen = nLength - nFrontLen;
1211 
1212         aResult += rStr.Copy(0, nFrontLen);
1213         aResult += rFillStr;
1214         aResult += rStr.Copy(rStr.Len() - nBackLen, nBackLen);
1215     }
1216 
1217     return aResult;
1218 }
1219 
1220 static bool lcl_IsSpecialCharacter(sal_Unicode nChar)
1221 {
1222     switch (nChar)
1223     {
1224     case CH_TXTATR_BREAKWORD:
1225     case CH_TXTATR_INWORD:
1226     case CH_TXTATR_TAB:
1227     case CH_TXTATR_NEWLINE:
1228         return true;
1229 
1230     default:
1231         break;
1232     }
1233 
1234     return false;
1235 }
1236 
1237 static String lcl_DenotedPortion(String rStr, xub_StrLen nStart,
1238                                  xub_StrLen nEnd)
1239 {
1240     String aResult;
1241 
1242     if (nEnd - nStart > 0)
1243     {
1244         sal_Unicode cLast = rStr.GetChar(nEnd - 1);
1245         if (lcl_IsSpecialCharacter(cLast))
1246         {
1247             switch(cLast)
1248             {
1249             case CH_TXTATR_TAB:
1250                 aResult += String(SW_RES(STR_UNDO_TABS));
1251 
1252                 break;
1253             case CH_TXTATR_NEWLINE:
1254                 aResult += String(SW_RES(STR_UNDO_NLS));
1255 
1256                 break;
1257 
1258             case CH_TXTATR_INWORD:
1259             case CH_TXTATR_BREAKWORD:
1260                 aResult += UNDO_ARG2;
1261 
1262                 break;
1263 
1264             }
1265             SwRewriter aRewriter;
1266             aRewriter.AddRule(UNDO_ARG1,
1267                               String::CreateFromInt32(nEnd - nStart));
1268             aResult = aRewriter.Apply(aResult);
1269         }
1270         else
1271         {
1272             aResult = String(SW_RES(STR_START_QUOTE));
1273             aResult += rStr.Copy(nStart, nEnd - nStart);
1274             aResult += String(SW_RES(STR_END_QUOTE));
1275         }
1276     }
1277 
1278     return aResult;
1279 }
1280 
1281 String DenoteSpecialCharacters(const String & rStr)
1282 {
1283     String aResult;
1284 
1285     if (rStr.Len() > 0)
1286     {
1287         bool bStart = false;
1288         xub_StrLen nStart = 0;
1289         sal_Unicode cLast = 0;
1290 
1291         for (xub_StrLen i = 0; i < rStr.Len(); i++)
1292         {
1293             if (lcl_IsSpecialCharacter(rStr.GetChar(i)))
1294             {
1295                 if (cLast != rStr.GetChar(i))
1296                     bStart = true;
1297 
1298             }
1299             else
1300             {
1301                 if (lcl_IsSpecialCharacter(cLast))
1302                     bStart = true;
1303             }
1304 
1305             if (bStart)
1306             {
1307                 aResult += lcl_DenotedPortion(rStr, nStart, i);
1308 
1309                 nStart = i;
1310                 bStart = false;
1311             }
1312 
1313             cLast = rStr.GetChar(i);
1314         }
1315 
1316         aResult += lcl_DenotedPortion(rStr, nStart, rStr.Len());
1317     }
1318     else
1319         aResult = UNDO_ARG2;
1320 
1321     return aResult;
1322 }
1323 
1324 bool IsDestroyFrameAnchoredAtChar(SwPosition const & rAnchorPos,
1325         SwPosition const & rStart, SwPosition const & rEnd,
1326         DelCntntType const nDelCntntType)
1327 {
1328 
1329     // Here we identified the objects to destroy:
1330     // - anchored between start and end of the selection
1331     // - anchored in start of the selection with "CheckNoContent"
1332     // - anchored in start of sel. and the selection start at pos 0
1333     return  (rAnchorPos.nNode < rEnd.nNode)
1334          && (   (nsDelCntntType::DELCNT_CHKNOCNTNT & nDelCntntType)
1335             ||  (rStart.nNode < rAnchorPos.nNode)
1336             ||  !rStart.nContent.GetIndex()
1337             );
1338 }
1339 
1340