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