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