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