xref: /aoo42x/main/sw/source/core/undo/undobj.cxx (revision efeef26f)
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->getMarksCount() )
770 		{
771 
772 			for( sal_uInt16 n = 0; n < pMarkAccess->getMarksCount(); ++n )
773 			{
774                 // --> OD 2007-10-17 #i81002#
775                 bool bSavePos = false;
776                 bool bSaveOtherPos = false;
777                 const ::sw::mark::IMark* pBkmk = (pMarkAccess->getMarksBegin() + n)->get();
778                 if( nsDelCntntType::DELCNT_CHKNOCNTNT & nDelCntntType )
779                 {
780                     if( pStt->nNode <= pBkmk->GetMarkPos().nNode &&
781                         pBkmk->GetMarkPos().nNode < pEnd->nNode )
782                         bSavePos = true;
783                     if( pBkmk->IsExpanded() &&
784                         pStt->nNode <= pBkmk->GetOtherMarkPos().nNode &&
785                         pBkmk->GetOtherMarkPos().nNode < pEnd->nNode )
786                         bSaveOtherPos = true;
787                 }
788                 else
789                 {
790                     // --> OD 2009-08-06 #i92125#
791                     bool bKeepCrossRefBkmk( false );
792                     {
793                         if ( rMark.nNode == rPoint.nNode &&
794                              ( IDocumentMarkAccess::GetType(*pBkmk) ==
795                                 IDocumentMarkAccess::CROSSREF_HEADING_BOOKMARK ||
796                                IDocumentMarkAccess::GetType(*pBkmk) ==
797                                 IDocumentMarkAccess::CROSSREF_NUMITEM_BOOKMARK ) )
798                         {
799                             bKeepCrossRefBkmk = true;
800                         }
801                     }
802                     if ( !bKeepCrossRefBkmk )
803                     {
804                         bool bMaybe = false;
805                         if ( *pStt <= pBkmk->GetMarkPos() && pBkmk->GetMarkPos() <= *pEnd )
806                         {
807                             if( pBkmk->GetMarkPos() == *pEnd ||
808                                 ( *pStt == pBkmk->GetMarkPos() && pBkmk->IsExpanded() ) )
809                                 bMaybe = true;
810                             else
811                                 bSavePos = true;
812                         }
813                         if( pBkmk->IsExpanded() &&
814                             *pStt <= pBkmk->GetOtherMarkPos() && pBkmk->GetOtherMarkPos() <= *pEnd )
815                         {
816                             if( bSavePos || bSaveOtherPos ||
817                                 ( pBkmk->GetOtherMarkPos() < *pEnd && pBkmk->GetOtherMarkPos() > *pStt ) )
818                             {
819                                 if( bMaybe )
820                                     bSavePos = true;
821                                 bSaveOtherPos = true;
822                             }
823                         }
824                     }
825                     // <--
826 
827                     // --> OD 2007-10-17 #i81002#
828                     const bool bDifferentTxtNodesAtMarkAndPoint(
829                                         rMark.nNode != rPoint.nNode &&
830                                         rMark.nNode.GetNode().GetTxtNode() &&
831                                         rPoint.nNode.GetNode().GetTxtNode() );
832                     // <--
833                     if( !bSavePos && !bSaveOtherPos && bDifferentTxtNodesAtMarkAndPoint &&
834                         dynamic_cast< const ::sw::mark::CrossRefBookmark* >(pBkmk))
835                     {
836                         // delete cross-reference bookmark at <pStt>, if only part of
837                         // <pEnd> text node content is deleted.
838                         if( pStt->nNode == pBkmk->GetMarkPos().nNode &&
839                             pEnd->nContent.GetIndex() !=
840                                 pEnd->nNode.GetNode().GetTxtNode()->Len() )
841                         {
842                             bSavePos = true;
843                             bSaveOtherPos = false;
844                         }
845                         // delete cross-reference bookmark at <pEnd>, if only part of
846                         // <pStt> text node content is deleted.
847                         else if( pEnd->nNode == pBkmk->GetMarkPos().nNode &&
848                             pStt->nContent.GetIndex() != 0 )
849                         {
850                             bSavePos = true;
851                             bSaveOtherPos = false;
852                         }
853                     }
854                 }
855 				if( bSavePos || bSaveOtherPos )
856 				{
857 					if( !pHistory )
858 						pHistory = new SwHistory;
859 
860 					pHistory->Add( *pBkmk, bSavePos, bSaveOtherPos );
861                     if(bSavePos &&
862                         (bSaveOtherPos || !pBkmk->IsExpanded()))
863                     {
864                         pMarkAccess->deleteMark(pMarkAccess->getMarksBegin()+n);
865                         n--;
866                     }
867 				}
868 			}
869 		}
870 	}
871 }
872 
873 
874 // sicher eine vollstaendige Section im Undo-Nodes-Array
875 
876 SwUndoSaveSection::SwUndoSaveSection()
877 	: pMvStt( 0 ), pRedlSaveData( 0 ), nMvLen( 0 ), nStartPos( ULONG_MAX )
878 {
879 }
880 
881 SwUndoSaveSection::~SwUndoSaveSection()
882 {
883 	if( pMvStt )		// loesche noch den Bereich aus dem UndoNodes Array
884 	{
885 		// SaveSection speichert den Inhalt in der PostIt-Section
886 		SwNodes& rUNds = pMvStt->GetNode().GetNodes();
887 		rUNds.Delete( *pMvStt, nMvLen );
888 
889 		delete pMvStt;
890 	}
891 	delete pRedlSaveData;
892 }
893 
894 void SwUndoSaveSection::SaveSection( SwDoc* pDoc, const SwNodeIndex& rSttIdx )
895 {
896 	SwNodeRange aRg( rSttIdx.GetNode(), *rSttIdx.GetNode().EndOfSectionNode() );
897 	SaveSection( pDoc, aRg );
898 }
899 
900 
901 void SwUndoSaveSection::SaveSection( SwDoc* , const SwNodeRange& rRange )
902 {
903 	SwPaM aPam( rRange.aStart, rRange.aEnd );
904 
905 	// loesche alle Fussnoten / FlyFrames / Bookmarks / Verzeichnisse
906 	DelCntntIndex( *aPam.GetMark(), *aPam.GetPoint() );
907 
908 	pRedlSaveData = new SwRedlineSaveDatas;
909 	if( !SwUndo::FillSaveData( aPam, *pRedlSaveData, sal_True, sal_True ))
910 		delete pRedlSaveData, pRedlSaveData = 0;
911 
912 	nStartPos = rRange.aStart.GetIndex();
913 
914 	aPam.GetPoint()->nNode--;
915 	aPam.GetMark()->nNode++;
916 
917 	SwCntntNode* pCNd = aPam.GetCntntNode( sal_False );
918 	if( pCNd )
919 		aPam.GetMark()->nContent.Assign( pCNd, 0 );
920 	if( 0 != ( pCNd = aPam.GetCntntNode( sal_True )) )
921 		aPam.GetPoint()->nContent.Assign( pCNd, pCNd->Len() );
922 
923 	// Positionen als SwIndex merken, damit im DTOR dieser Bereich
924 	// entfernt werden kann !!
925 	sal_uLong nEnd;
926 	pMvStt = new SwNodeIndex( rRange.aStart );
927 	MoveToUndoNds( aPam, pMvStt, 0, &nEnd, 0 );
928 	nMvLen = nEnd - pMvStt->GetIndex() + 1;
929 }
930 
931 void SwUndoSaveSection::RestoreSection( SwDoc* pDoc, SwNodeIndex* pIdx,
932 										sal_uInt16 nSectType )
933 {
934 	if( ULONG_MAX != nStartPos )		// gab es ueberhaupt Inhalt ?
935 	{
936 		// ueberpruefe, ob der Inhalt an der alten Position steht
937 		SwNodeIndex aSttIdx( pDoc->GetNodes(), nStartPos );
938         OSL_ENSURE(!aSttIdx.GetNode().GetCntntNode(),
939                 "RestoreSection(): Position on content node");
940 
941 		// move den Inhalt aus dem UndoNodes-Array in den Fly
942 		SwStartNode* pSttNd = pDoc->GetNodes().MakeEmptySection( aSttIdx,
943 												(SwStartNodeType)nSectType );
944 
945 		RestoreSection( pDoc, SwNodeIndex( *pSttNd->EndOfSectionNode() ));
946 
947 		if( pIdx )
948 			*pIdx = *pSttNd;
949 	}
950 }
951 
952 void SwUndoSaveSection::RestoreSection( SwDoc* pDoc, const SwNodeIndex& rInsPos )
953 {
954 	if( ULONG_MAX != nStartPos )		// gab es ueberhaupt Inhalt ?
955 	{
956 		SwPosition aInsPos( rInsPos );
957 		sal_uLong nEnd = pMvStt->GetIndex() + nMvLen - 1;
958 		MoveFromUndoNds( *pDoc, pMvStt->GetIndex(), 0, aInsPos, &nEnd, 0 );
959 
960 		// Indizies wieder zerstoren, Inhalt ist aus dem UndoNodes-Array
961 		// entfernt worden.
962 		DELETEZ( pMvStt );
963 		nMvLen = 0;
964 
965 		if( pRedlSaveData )
966 		{
967 			SwUndo::SetSaveData( *pDoc, *pRedlSaveData );
968 			delete pRedlSaveData, pRedlSaveData = 0;
969 		}
970 	}
971 }
972 
973 		// sicher und setze die RedlineDaten
974 
975 SwRedlineSaveData::SwRedlineSaveData( SwComparePosition eCmpPos,
976 										const SwPosition& rSttPos,
977 										const SwPosition& rEndPos,
978 										SwRedline& rRedl,
979 										sal_Bool bCopyNext )
980 	: SwUndRng( rRedl ),
981 	SwRedlineData( rRedl.GetRedlineData(), bCopyNext )
982 {
983 	ASSERT( POS_OUTSIDE == eCmpPos ||
984 			!rRedl.GetContentIdx(), "Redline mit Content" );
985 
986 	switch( eCmpPos )
987 	{
988 	case POS_OVERLAP_BEFORE:		// Pos1 ueberlappt Pos2 am Anfang
989 		nEndNode = rEndPos.nNode.GetIndex();
990 		nEndCntnt = rEndPos.nContent.GetIndex();
991 		break;
992 	case POS_OVERLAP_BEHIND: 		// Pos1 ueberlappt Pos2 am Ende
993 		nSttNode = rSttPos.nNode.GetIndex();
994 		nSttCntnt = rSttPos.nContent.GetIndex();
995 		break;
996 
997 	case POS_INSIDE:				// Pos1 liegt vollstaendig in Pos2
998 		nSttNode = rSttPos.nNode.GetIndex();
999 		nSttCntnt = rSttPos.nContent.GetIndex();
1000 		nEndNode = rEndPos.nNode.GetIndex();
1001 		nEndCntnt = rEndPos.nContent.GetIndex();
1002 		break;
1003 
1004 	case POS_OUTSIDE:				// Pos2 liegt vollstaendig in Pos1
1005 		if( rRedl.GetContentIdx() )
1006 		{
1007 			// dann den Bereich ins UndoArray verschieben und merken
1008 			SaveSection( rRedl.GetDoc(), *rRedl.GetContentIdx() );
1009 			rRedl.SetContentIdx( 0 );
1010 		}
1011 		break;
1012 
1013 	case POS_EQUAL:					// Pos1 ist genauso gross wie Pos2
1014 		break;
1015 
1016 	default:
1017 		ASSERT( !this, "keine gueltigen Daten!" )
1018 	}
1019 
1020 #ifdef DBG_UTIL
1021     nRedlineCount = rSttPos.nNode.GetNode().GetDoc()->GetRedlineTbl().Count();
1022 #endif
1023 }
1024 
1025 SwRedlineSaveData::~SwRedlineSaveData()
1026 {
1027 }
1028 
1029 void SwRedlineSaveData::RedlineToDoc( SwPaM& rPam )
1030 {
1031 	SwDoc& rDoc = *rPam.GetDoc();
1032 	SwRedline* pRedl = new SwRedline( *this, rPam );
1033 
1034 	if( GetMvSttIdx() )
1035 	{
1036 		SwNodeIndex aIdx( rDoc.GetNodes() );
1037 		RestoreSection( &rDoc, &aIdx, SwNormalStartNode );
1038 		if( GetHistory() )
1039 			GetHistory()->Rollback( &rDoc );
1040 		pRedl->SetContentIdx( &aIdx );
1041 	}
1042 	SetPaM( *pRedl );
1043 	// erstmal die "alten" entfernen, damit im Append keine unerwarteten
1044 	// Dinge passieren, wie z.B. eine Delete in eigenen Insert. Dann wird
1045 	// naehmlich das gerade restaurierte wieder geloescht - nicht das gewollte
1046     rDoc.DeleteRedline( *pRedl, false, USHRT_MAX );
1047 
1048     RedlineMode_t eOld = rDoc.GetRedlineMode();
1049     rDoc.SetRedlineMode_intern((RedlineMode_t)(eOld | nsRedlineMode_t::REDLINE_DONTCOMBINE_REDLINES));
1050 	//#i92154# let UI know about a new redline with comment
1051 	if (rDoc.GetDocShell() && (pRedl->GetComment() != String(::rtl::OUString::createFromAscii(""))) )
1052 		rDoc.GetDocShell()->Broadcast(SwRedlineHint(pRedl,SWREDLINE_INSERTED));
1053 	//
1054 #if OSL_DEBUG_LEVEL > 0
1055     bool const bSuccess =
1056 #endif
1057         rDoc.AppendRedline( pRedl, true );
1058     OSL_ENSURE(bSuccess,
1059         "SwRedlineSaveData::RedlineToDoc: insert redline failed");
1060 	rDoc.SetRedlineMode_intern( eOld );
1061 }
1062 
1063 sal_Bool SwUndo::FillSaveData( const SwPaM& rRange, SwRedlineSaveDatas& rSData,
1064 							sal_Bool bDelRange, sal_Bool bCopyNext )
1065 {
1066 	if( rSData.Count() )
1067 		rSData.DeleteAndDestroy( 0, rSData.Count() );
1068 
1069 	SwRedlineSaveData* pNewData;
1070 	const SwPosition *pStt = rRange.Start(), *pEnd = rRange.End();
1071 	const SwRedlineTbl& rTbl = rRange.GetDoc()->GetRedlineTbl();
1072 	sal_uInt16 n = 0;
1073 	rRange.GetDoc()->GetRedline( *pStt, &n );
1074 	for( ; n < rTbl.Count(); ++n )
1075 	{
1076 		SwRedline* pRedl = rTbl[ n ];
1077 		const SwPosition *pRStt = pRedl->Start(), *pREnd = pRedl->End();
1078 
1079 		SwComparePosition eCmpPos = ComparePosition( *pStt, *pEnd, *pRStt, *pREnd );
1080 		if( POS_BEFORE != eCmpPos && POS_BEHIND != eCmpPos &&
1081 			POS_COLLIDE_END != eCmpPos && POS_COLLIDE_START != eCmpPos )
1082 		{
1083 			pNewData = new SwRedlineSaveData( eCmpPos, *pStt, *pEnd,
1084 												*pRedl, bCopyNext );
1085 			rSData.Insert( pNewData, rSData.Count() );
1086 		}
1087 	}
1088 	if( rSData.Count() && bDelRange )
1089         rRange.GetDoc()->DeleteRedline( rRange, false, USHRT_MAX );
1090 	return 0 != rSData.Count();
1091 }
1092 
1093 sal_Bool SwUndo::FillSaveDataForFmt( const SwPaM& rRange, SwRedlineSaveDatas& rSData )
1094 {
1095 	if( rSData.Count() )
1096 		rSData.DeleteAndDestroy( 0, rSData.Count() );
1097 
1098 	SwRedlineSaveData* pNewData;
1099 	const SwPosition *pStt = rRange.Start(), *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         if( nsRedlineType_t::REDLINE_FORMAT == pRedl->GetType() )
1107 		{
1108 			const SwPosition *pRStt = pRedl->Start(), *pREnd = pRedl->End();
1109 
1110 			SwComparePosition eCmpPos = ComparePosition( *pStt, *pEnd, *pRStt, *pREnd );
1111 			if( POS_BEFORE != eCmpPos && POS_BEHIND != eCmpPos &&
1112 				POS_COLLIDE_END != eCmpPos && POS_COLLIDE_START != eCmpPos )
1113 			{
1114 				pNewData = new SwRedlineSaveData( eCmpPos, *pStt, *pEnd,
1115 													*pRedl, sal_True );
1116 				rSData.Insert( pNewData, rSData.Count() );
1117 			}
1118 
1119 
1120 		}
1121 	}
1122 	return 0 != rSData.Count();
1123 }
1124 
1125 void SwUndo::SetSaveData( SwDoc& rDoc, const SwRedlineSaveDatas& rSData )
1126 {
1127     RedlineMode_t eOld = rDoc.GetRedlineMode();
1128     rDoc.SetRedlineMode_intern( (RedlineMode_t)(( eOld & ~nsRedlineMode_t::REDLINE_IGNORE) | nsRedlineMode_t::REDLINE_ON ));
1129 	SwPaM aPam( rDoc.GetNodes().GetEndOfContent() );
1130 
1131 	for( sal_uInt16 n = rSData.Count(); n; )
1132 		rSData[ --n ]->RedlineToDoc( aPam );
1133 
1134     // check redline count against count saved in RedlineSaveData object
1135     DBG_ASSERT( (rSData.Count() == 0) ||
1136                 (rSData[0]->nRedlineCount == rDoc.GetRedlineTbl().Count()),
1137                 "redline count not restored properly" );
1138 
1139 	rDoc.SetRedlineMode_intern( eOld );
1140 }
1141 
1142 sal_Bool SwUndo::HasHiddenRedlines( const SwRedlineSaveDatas& rSData )
1143 {
1144 	for( sal_uInt16 n = rSData.Count(); n; )
1145 		if( rSData[ --n ]->GetMvSttIdx() )
1146 			return sal_True;
1147 	return sal_False;
1148 }
1149 
1150 sal_Bool SwUndo::CanRedlineGroup( SwRedlineSaveDatas& rCurr,
1151 						const SwRedlineSaveDatas& rCheck, sal_Bool bCurrIsEnd )
1152 {
1153 	sal_Bool bRet = sal_False;
1154 	sal_uInt16 n;
1155 
1156 	if( rCurr.Count() == rCheck.Count() )
1157 	{
1158 		bRet = sal_True;
1159 		for( n = 0; n < rCurr.Count(); ++n )
1160 		{
1161 			const SwRedlineSaveData& rSet = *rCurr[ n ];
1162 			const SwRedlineSaveData& rGet = *rCheck[ n ];
1163 			if( rSet.nSttNode != rGet.nSttNode ||
1164 				rSet.GetMvSttIdx() || rGet.GetMvSttIdx() ||
1165 				( bCurrIsEnd ? rSet.nSttCntnt != rGet.nEndCntnt
1166 							 : rSet.nEndCntnt != rGet.nSttCntnt ) ||
1167 				!rGet.CanCombine( rSet ) )
1168 			{
1169 				bRet = sal_False;
1170 				break;
1171 			}
1172 		}
1173 
1174 		if( bRet )
1175 			for( n = 0; n < rCurr.Count(); ++n )
1176 			{
1177 				SwRedlineSaveData& rSet = *rCurr[ n ];
1178 				const SwRedlineSaveData& rGet = *rCheck[ n ];
1179 				if( bCurrIsEnd )
1180 					rSet.nSttCntnt = rGet.nSttCntnt;
1181 				else
1182 					rSet.nEndCntnt = rGet.nEndCntnt;
1183 			}
1184 	}
1185 	return bRet;
1186 }
1187 
1188 // #111827#
1189 String ShortenString(const String & rStr, xub_StrLen nLength, const String & rFillStr)
1190 {
1191     ASSERT( nLength - rFillStr.Len() >= 2, "improper arguments")
1192 
1193     String aResult;
1194 
1195     if (rStr.Len() <= nLength)
1196         aResult = rStr;
1197     else
1198     {
1199         long nTmpLength = nLength - rFillStr.Len();
1200         if ( nTmpLength < 2 )
1201             nTmpLength = 2;
1202 
1203         nLength = static_cast<xub_StrLen>(nTmpLength);
1204 
1205         const xub_StrLen nFrontLen = nLength - nLength / 2;
1206         const xub_StrLen nBackLen = nLength - nFrontLen;
1207 
1208         aResult += rStr.Copy(0, nFrontLen);
1209         aResult += rFillStr;
1210         aResult += rStr.Copy(rStr.Len() - nBackLen, nBackLen);
1211     }
1212 
1213     return aResult;
1214 }
1215 
1216 static bool lcl_IsSpecialCharacter(sal_Unicode nChar)
1217 {
1218     switch (nChar)
1219     {
1220     case CH_TXTATR_BREAKWORD:
1221     case CH_TXTATR_INWORD:
1222     case CH_TXTATR_TAB:
1223     case CH_TXTATR_NEWLINE:
1224         return true;
1225 
1226     default:
1227         break;
1228     }
1229 
1230     return false;
1231 }
1232 
1233 static String lcl_DenotedPortion(String rStr, xub_StrLen nStart,
1234                                  xub_StrLen nEnd)
1235 {
1236     String aResult;
1237 
1238     if (nEnd - nStart > 0)
1239     {
1240         sal_Unicode cLast = rStr.GetChar(nEnd - 1);
1241         if (lcl_IsSpecialCharacter(cLast))
1242         {
1243             switch(cLast)
1244             {
1245             case CH_TXTATR_TAB:
1246                 aResult += String(SW_RES(STR_UNDO_TABS));
1247 
1248                 break;
1249             case CH_TXTATR_NEWLINE:
1250                 aResult += String(SW_RES(STR_UNDO_NLS));
1251 
1252                 break;
1253 
1254             case CH_TXTATR_INWORD:
1255             case CH_TXTATR_BREAKWORD:
1256                 aResult += UNDO_ARG2;
1257 
1258                 break;
1259 
1260             }
1261             SwRewriter aRewriter;
1262             aRewriter.AddRule(UNDO_ARG1,
1263                               String::CreateFromInt32(nEnd - nStart));
1264             aResult = aRewriter.Apply(aResult);
1265         }
1266         else
1267         {
1268             aResult = String(SW_RES(STR_START_QUOTE));
1269             aResult += rStr.Copy(nStart, nEnd - nStart);
1270             aResult += String(SW_RES(STR_END_QUOTE));
1271         }
1272     }
1273 
1274     return aResult;
1275 }
1276 
1277 String DenoteSpecialCharacters(const String & rStr)
1278 {
1279     String aResult;
1280 
1281     if (rStr.Len() > 0)
1282     {
1283         bool bStart = false;
1284         xub_StrLen nStart = 0;
1285         sal_Unicode cLast = 0;
1286 
1287         for (xub_StrLen i = 0; i < rStr.Len(); i++)
1288         {
1289             if (lcl_IsSpecialCharacter(rStr.GetChar(i)))
1290             {
1291                 if (cLast != rStr.GetChar(i))
1292                     bStart = true;
1293 
1294             }
1295             else
1296             {
1297                 if (lcl_IsSpecialCharacter(cLast))
1298                     bStart = true;
1299             }
1300 
1301             if (bStart)
1302             {
1303                 aResult += lcl_DenotedPortion(rStr, nStart, i);
1304 
1305                 nStart = i;
1306                 bStart = false;
1307             }
1308 
1309             cLast = rStr.GetChar(i);
1310         }
1311 
1312         aResult += lcl_DenotedPortion(rStr, nStart, rStr.Len());
1313     }
1314     else
1315         aResult = UNDO_ARG2;
1316 
1317     return aResult;
1318 }
1319 
1320 bool IsDestroyFrameAnchoredAtChar(SwPosition const & rAnchorPos,
1321         SwPosition const & rStart, SwPosition const & rEnd,
1322         DelCntntType const nDelCntntType)
1323 {
1324 
1325     // Here we identified the objects to destroy:
1326     // - anchored between start and end of the selection
1327     // - anchored in start of the selection with "CheckNoContent"
1328     // - anchored in start of sel. and the selection start at pos 0
1329     return  (rAnchorPos.nNode < rEnd.nNode)
1330          && (   (nsDelCntntType::DELCNT_CHKNOCNTNT & nDelCntntType)
1331             ||  (rStart.nNode < rAnchorPos.nNode)
1332             ||  !rStart.nContent.GetIndex()
1333             );
1334 }
1335 
1336