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