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