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