1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <UndoDelete.hxx>
21 #include <hintids.hxx>
22 #include <unotools/charclass.hxx>
23 #include <editeng/brkitem.hxx>
24 #include <fmtpdsc.hxx>
26 #include <fmtanchr.hxx>
28 #include <UndoManager.hxx>
29 #include <swtable.hxx>
33 #include <UndoCore.hxx>
35 #include <poolfmt.hxx>
37 #include <redline.hxx>
39 #include <sfx2/app.hxx>
42 #include <comcore.hrc> // #111827#
47 /* lcl_MakeAutoFrms has to call MakeFrms for objects bounded "AtChar"
48 ( == AUTO ), if the anchor frame has be moved via _MoveNodes(..) and
51 static void lcl_MakeAutoFrms( const SwFrmFmts
& rSpzArr
, sal_uLong nMovedIndex
)
53 if( !rSpzArr
.empty() )
56 const SwFmtAnchor
* pAnchor
;
57 for( sal_uInt16 n
= 0; n
< rSpzArr
.size(); ++n
)
59 pFmt
= (SwFlyFrmFmt
*)rSpzArr
[n
];
60 pAnchor
= &pFmt
->GetAnchor();
61 if (pAnchor
->GetAnchorId() == FLY_AT_CHAR
)
63 const SwPosition
* pAPos
= pAnchor
->GetCntntAnchor();
64 if( pAPos
&& nMovedIndex
== pAPos
->nNode
.GetIndex() )
71 // SwUndoDelete has to perform a deletion and to record anything that is needed
72 // to restore the situation before the deletion. Unfortunately a part of the
73 // deletion will be done after calling this Ctor, this has to be kept in mind!
74 // In this Ctor only the complete paragraphs will be deleted, the joining of
75 // the first and last paragraph of the selection will be handled outside this
77 // Here are the main steps of the function:
78 // 1. Deletion/recording of content indizes of the selection: footnotes, fly
79 // frames and bookmarks
80 // Step 1 could shift all nodes by deletion of footnotes => nNdDiff will be set.
81 // 2. If the paragraph where the selection ends, is the last content of a
82 // section so that this section becomes empty when the paragraphs will be
83 // joined we have to do some smart actions ;-) The paragraph will be moved
84 // outside the section and replaced by a dummy text node, the complete
85 // section will be deleted in step 3. The difference between replacement
86 // dummy and original is nReplacementDummy.
87 // 3. Moving complete selected nodes into the UndoArray. Before this happens the
88 // selection has to be extended if there are sections which would become
89 // empty otherwise. BTW: sections will be moved into the UndoArray if they
90 // are complete part of the selection. Sections starting or ending outside
91 // of the selection will not be removed from the DocNodeArray even they got
92 // a "dummy"-copy in the UndoArray.
93 // 4. We have to anticipate the joining of the two paragraphs if the start
94 // paragraph is inside a section and the end paragraph not. Then we have to
95 // move the paragraph into this section and to record this in nSectDiff.
96 SwUndoDelete::SwUndoDelete( SwPaM
& rPam
, sal_Bool bFullPara
, sal_Bool bCalledByTblCpy
)
97 : SwUndo(UNDO_DELETE
), SwUndRng( rPam
),
98 pMvStt( 0 ), pSttStr(0), pEndStr(0), pRedlData(0), pRedlSaveData(0),
99 nNode(0), nNdDiff(0), nSectDiff(0), nReplaceDummy(0), nSetPos(0),
100 bGroup( sal_False
), bBackSp( sal_False
), bJoinNext( sal_False
), bTblDelLastNd( sal_False
),
101 bDelFullPara( bFullPara
), bResetPgDesc( sal_False
), bResetPgBrk( sal_False
),
102 bFromTableCopy( bCalledByTblCpy
)
104 // bFullPara is set e.g. if an empty paragraph before a table is deleted
105 bDelFullPara
= bFullPara
;
107 bCacheComment
= false;
109 SwDoc
* pDoc
= rPam
.GetDoc();
111 if( !pDoc
->IsIgnoreRedline() && !pDoc
->GetRedlineTbl().empty() )
113 pRedlSaveData
= new SwRedlineSaveDatas
;
114 if( !FillSaveData( rPam
, *pRedlSaveData
))
115 delete pRedlSaveData
, pRedlSaveData
= 0;
119 pHistory
= new SwHistory
;
121 // delete all footnotes for now
122 const SwPosition
*pStt
= rPam
.Start(),
123 *pEnd
= rPam
.GetPoint() == pStt
127 // Step 1. deletion/record of content indizes
130 OSL_ENSURE( rPam
.HasMark(), "PaM ohne Mark" );
131 DelCntntIndex( *rPam
.GetMark(), *rPam
.GetPoint(),
132 DelCntntType(nsDelCntntType::DELCNT_ALL
| nsDelCntntType::DELCNT_CHKNOCNTNT
) );
134 ::sw::UndoGuard
const undoGuard(pDoc
->GetIDocumentUndoRedo());
135 _DelBookmarks(pStt
->nNode
, pEnd
->nNode
);
138 DelCntntIndex( *rPam
.GetMark(), *rPam
.GetPoint() );
140 nSetPos
= pHistory
? pHistory
->Count() : 0;
142 // Is already anything deleted?
143 nNdDiff
= nSttNode
- pStt
->nNode
.GetIndex();
145 bJoinNext
= !bFullPara
&& pEnd
== rPam
.GetPoint();
146 bBackSp
= !bFullPara
&& !bJoinNext
;
148 SwTxtNode
*pSttTxtNd
= 0, *pEndTxtNd
= 0;
151 pSttTxtNd
= pStt
->nNode
.GetNode().GetTxtNode();
152 pEndTxtNd
= nSttNode
== nEndNode
154 : pEnd
->nNode
.GetNode().GetTxtNode();
157 sal_Bool bMoveNds
= *pStt
== *pEnd
// any area still existent?
159 : ( SaveCntnt( pStt
, pEnd
, pSttTxtNd
, pEndTxtNd
) || bFromTableCopy
);
161 if( pSttTxtNd
&& pEndTxtNd
&& pSttTxtNd
!= pEndTxtNd
)
163 // two different TextNodes, thus save also the TextFormatCollection
164 pHistory
->Add( pSttTxtNd
->GetTxtColl(),pStt
->nNode
.GetIndex(), ND_TEXTNODE
);
165 pHistory
->Add( pEndTxtNd
->GetTxtColl(),pEnd
->nNode
.GetIndex(), ND_TEXTNODE
);
167 if( !bJoinNext
) // Selection from bottom to top
169 // When using JoinPrev() all AUTO-PageBreak's will be copied
170 // correctly. To restore them with UNDO, Auto-PageBreak of the
171 // EndNode needs to be reset. Same for PageDesc and ColBreak.
172 if( pEndTxtNd
->HasSwAttrSet() )
174 SwRegHistory
aRegHist( *pEndTxtNd
, pHistory
);
175 if( SFX_ITEM_SET
== pEndTxtNd
->GetpSwAttrSet()->GetItemState(
176 RES_BREAK
, sal_False
) )
177 pEndTxtNd
->ResetAttr( RES_BREAK
);
178 if( pEndTxtNd
->HasSwAttrSet() &&
179 SFX_ITEM_SET
== pEndTxtNd
->GetpSwAttrSet()->GetItemState(
180 RES_PAGEDESC
, sal_False
) )
181 pEndTxtNd
->ResetAttr( RES_PAGEDESC
);
186 // Move now also the PaM. The SPoint is at the beginning of a SSelection.
187 if( pEnd
== rPam
.GetPoint() && ( !bFullPara
|| pSttTxtNd
|| pEndTxtNd
) )
190 if( !pSttTxtNd
&& !pEndTxtNd
)
191 rPam
.GetPoint()->nNode
--;
192 rPam
.DeleteMark(); // the SPoint is in the selection
199 if( bMoveNds
) // Do Nodes exist that need to be moved?
201 SwNodes
& rNds
= pDoc
->GetUndoManager().GetUndoNodes();
202 SwNodes
& rDocNds
= pDoc
->GetNodes();
203 SwNodeRange
aRg( rDocNds
, nSttNode
- nNdDiff
,
204 rDocNds
, nEndNode
- nNdDiff
);
205 if( !bFullPara
&& !pEndTxtNd
&&
206 &aRg
.aEnd
.GetNode() != &pDoc
->GetNodes().GetEndOfContent() )
208 SwNode
* pNode
= aRg
.aEnd
.GetNode().StartOfSectionNode();
209 if( pNode
->GetIndex() >= nSttNode
- nNdDiff
)
210 aRg
.aEnd
++; // Deletion of a complete table
213 // Step 2: Expand selection if necessary
214 if( bJoinNext
|| bFullPara
)
216 // If all content of a section will be moved into Undo, the section
217 // itself should be moved completely.
218 while( aRg
.aEnd
.GetIndex() + 2 < rDocNds
.Count() &&
219 ( (pTmpNd
= rDocNds
[ aRg
.aEnd
.GetIndex()+1 ])->IsEndNode() &&
220 pTmpNd
->StartOfSectionNode()->IsSectionNode() &&
221 pTmpNd
->StartOfSectionNode()->GetIndex() >= aRg
.aStart
.GetIndex() ) )
223 nReplaceDummy
= aRg
.aEnd
.GetIndex() + nNdDiff
- nEndNode
;
225 { // The selection has been expanded, because
229 // The end text node has to leave the (expanded) selection
230 // The dummy is needed because _MoveNodes deletes empty
233 SwNodeRange
aMvRg( *pEndTxtNd
, 0, *pEndTxtNd
, 1 );
234 SwPosition
aSplitPos( *pEndTxtNd
);
235 ::sw::UndoGuard
const ug(pDoc
->GetIDocumentUndoRedo());
236 pDoc
->SplitNode( aSplitPos
, false );
237 rDocNds
._MoveNodes( aMvRg
, rDocNds
, aRg
.aEnd
, sal_True
);
244 if( bBackSp
|| bFullPara
)
246 // See above, the selection has to be expanded if there are "nearly
247 // empty" sections and a replacement dummy has to be set if needed.
248 while( 1 < aRg
.aStart
.GetIndex() &&
249 ( (pTmpNd
= rDocNds
[ aRg
.aStart
.GetIndex()-1 ])->IsSectionNode() &&
250 pTmpNd
->EndOfSectionIndex() < aRg
.aEnd
.GetIndex() ) )
254 nReplaceDummy
= nSttNode
- nNdDiff
- aRg
.aStart
.GetIndex();
257 SwNodeRange
aMvRg( *pSttTxtNd
, 0, *pSttTxtNd
, 1 );
258 SwPosition
aSplitPos( *pSttTxtNd
);
259 ::sw::UndoGuard
const ug(pDoc
->GetIDocumentUndoRedo());
260 pDoc
->SplitNode( aSplitPos
, false );
261 rDocNds
._MoveNodes( aMvRg
, rDocNds
, aRg
.aStart
, sal_True
);
273 else if( !bFullPara
&& !aRg
.aEnd
.GetNode().IsCntntNode() )
277 else if( pSttTxtNd
&& ( pEndTxtNd
|| pSttTxtNd
->GetTxt().Len() ) )
280 // Step 3: Moving into UndoArray...
281 nNode
= rNds
.GetEndOfContent().GetIndex();
282 rDocNds
._MoveNodes( aRg
, rNds
, SwNodeIndex( rNds
.GetEndOfContent() ));
283 pMvStt
= new SwNodeIndex( rNds
, nNode
);
284 // remember difference!
285 nNode
= rNds
.GetEndOfContent().GetIndex() - nNode
;
287 if( pSttTxtNd
&& pEndTxtNd
)
289 //Step 4: Moving around sections
290 nSectDiff
= aRg
.aEnd
.GetIndex() - aRg
.aStart
.GetIndex();
291 // nSect is the number of sections which starts(ends) between start
292 // and end node of the selection. The "loser" paragraph has to be
293 // moved into the section(s) of the "winner" paragraph
298 SwNodeRange
aMvRg( *pEndTxtNd
, 0, *pEndTxtNd
, 1 );
299 rDocNds
._MoveNodes( aMvRg
, rDocNds
, aRg
.aStart
, sal_True
);
303 SwNodeRange
aMvRg( *pSttTxtNd
, 0, *pSttTxtNd
, 1 );
304 rDocNds
._MoveNodes( aMvRg
, rDocNds
, aRg
.aEnd
, sal_True
);
308 if( nSectDiff
|| nReplaceDummy
)
309 lcl_MakeAutoFrms( *pDoc
->GetSpzFrmFmts(),
310 bJoinNext
? pEndTxtNd
->GetIndex() : pSttTxtNd
->GetIndex() );
313 nNode
= 0; // moved no node -> no difference at the end
315 // Are there any Nodes that got deleted before that (FootNotes
316 // have ContentNodes)?
317 if( !pSttTxtNd
&& !pEndTxtNd
)
319 nNdDiff
= nSttNode
- rPam
.GetPoint()->nNode
.GetIndex() - (bFullPara
? 0 : 1);
320 rPam
.Move( fnMoveForward
, fnGoNode
);
325 if( nSectDiff
&& bBackSp
)
326 nNdDiff
+= nSectDiff
;
327 nNdDiff
-= rPam
.GetPoint()->nNode
.GetIndex();
330 if( !rPam
.GetNode()->IsCntntNode() )
331 rPam
.GetPoint()->nContent
.Assign( 0, 0 );
333 // is a history necessary here at all?
334 if( pHistory
&& !pHistory
->Count() )
338 sal_Bool
SwUndoDelete::SaveCntnt( const SwPosition
* pStt
, const SwPosition
* pEnd
,
339 SwTxtNode
* pSttTxtNd
, SwTxtNode
* pEndTxtNd
)
341 sal_uLong nNdIdx
= pStt
->nNode
.GetIndex();
342 // 1 - copy start in Start-String
345 sal_Bool bOneNode
= nSttNode
== nEndNode
;
346 xub_StrLen nLen
= bOneNode
? nEndCntnt
- nSttCntnt
347 : pSttTxtNd
->GetTxt().Len() - nSttCntnt
;
348 SwRegHistory
aRHst( *pSttTxtNd
, pHistory
);
349 // always save all text atttibutes because of possibly overlapping
351 pHistory
->CopyAttr( pSttTxtNd
->GetpSwpHints(), nNdIdx
,
352 0, pSttTxtNd
->GetTxt().Len(), true );
353 if( !bOneNode
&& pSttTxtNd
->HasSwAttrSet() )
354 pHistory
->CopyFmtAttr( *pSttTxtNd
->GetpSwAttrSet(), nNdIdx
);
356 // the length might have changed (!!Fields!!)
357 nLen
= ( bOneNode
? pEnd
->nContent
.GetIndex() : pSttTxtNd
->GetTxt().Len() )
358 - pStt
->nContent
.GetIndex();
360 // delete now also the text (all attribute changes are added to
362 pSttStr
= (String
*)new String( pSttTxtNd
->GetTxt().Copy( nSttCntnt
, nLen
));
363 pSttTxtNd
->EraseText( pStt
->nContent
, nLen
);
364 if( pSttTxtNd
->GetpSwpHints() )
365 pSttTxtNd
->GetpSwpHints()->DeRegister();
368 bool emptied( pSttStr
->Len() && !pSttTxtNd
->Len() );
369 if (!bOneNode
|| emptied
) // merging may overwrite xmlids...
371 m_pMetadataUndoStart
= (emptied
)
372 ? pSttTxtNd
->CreateUndoForDelete()
373 : pSttTxtNd
->CreateUndo();
377 return sal_False
; // stop moving more nodes
380 // 2 - copy end into End-String
383 SwIndex
aEndIdx( pEndTxtNd
);
384 nNdIdx
= pEnd
->nNode
.GetIndex();
385 SwRegHistory
aRHst( *pEndTxtNd
, pHistory
);
387 // always save all text atttibutes because of possibly overlapping
389 pHistory
->CopyAttr( pEndTxtNd
->GetpSwpHints(), nNdIdx
, 0,
390 pEndTxtNd
->GetTxt().Len(), true );
392 if( pEndTxtNd
->HasSwAttrSet() )
393 pHistory
->CopyFmtAttr( *pEndTxtNd
->GetpSwAttrSet(), nNdIdx
);
395 // delete now also the text (all attribute changes are added to
397 pEndStr
= (String
*)new String( pEndTxtNd
->GetTxt().Copy( 0,
398 pEnd
->nContent
.GetIndex() ));
399 pEndTxtNd
->EraseText( aEndIdx
, pEnd
->nContent
.GetIndex() );
400 if( pEndTxtNd
->GetpSwpHints() )
401 pEndTxtNd
->GetpSwpHints()->DeRegister();
404 bool emptied( pEndStr
->Len() && !pEndTxtNd
->Len() );
406 m_pMetadataUndoEnd
= (emptied
)
407 ? pEndTxtNd
->CreateUndoForDelete()
408 : pEndTxtNd
->CreateUndo();
411 // if there are only two Nodes than we're done
412 if( ( pSttTxtNd
|| pEndTxtNd
) && nSttNode
+ 1 == nEndNode
)
413 return sal_False
; // do not move any Node
415 return sal_True
; // move Nodes lying in between
418 sal_Bool
SwUndoDelete::CanGrouping( SwDoc
* pDoc
, const SwPaM
& rDelPam
)
420 // Is Undo greater than one Node (that is Start and EndString)?
421 if( pSttStr
? !pSttStr
->Len() || pEndStr
: sal_True
)
424 // only the deletion of single char's can be condensed
425 if( nSttNode
!= nEndNode
|| ( !bGroup
&& nSttCntnt
+1 != nEndCntnt
))
428 const SwPosition
*pStt
= rDelPam
.Start(),
429 *pEnd
= rDelPam
.GetPoint() == pStt
431 : rDelPam
.GetPoint();
433 if( pStt
->nNode
!= pEnd
->nNode
||
434 pStt
->nContent
.GetIndex()+1 != pEnd
->nContent
.GetIndex() ||
435 pEnd
->nNode
!= nSttNode
)
438 // Distinguish between BackSpace and Delete because the Undo array needs to
439 // be constructed differently!
440 if( pEnd
->nContent
== nSttCntnt
)
442 if( bGroup
&& !bBackSp
) return sal_False
;
445 else if( pStt
->nContent
== nSttCntnt
)
447 if( bGroup
&& bBackSp
) return sal_False
;
453 // are both Nodes (Node/Undo array) TextNodes at all?
454 SwTxtNode
* pDelTxtNd
= pStt
->nNode
.GetNode().GetTxtNode();
455 if( !pDelTxtNd
) return sal_False
;
457 xub_StrLen nUChrPos
= bBackSp
? 0 : pSttStr
->Len()-1;
458 sal_Unicode cDelChar
= pDelTxtNd
->GetTxt().GetChar( pStt
->nContent
.GetIndex() );
459 CharClass
& rCC
= GetAppCharClass();
460 if( ( CH_TXTATR_BREAKWORD
== cDelChar
|| CH_TXTATR_INWORD
== cDelChar
) ||
461 rCC
.isLetterNumeric( rtl::OUString( cDelChar
), 0 ) !=
462 rCC
.isLetterNumeric( *pSttStr
, nUChrPos
) )
466 SwRedlineSaveDatas
* pTmpSav
= new SwRedlineSaveDatas
;
467 if( !FillSaveData( rDelPam
, *pTmpSav
, sal_False
))
468 delete pTmpSav
, pTmpSav
= 0;
470 sal_Bool bOk
= ( !pRedlSaveData
&& !pTmpSav
) ||
471 ( pRedlSaveData
&& pTmpSav
&&
472 SwUndo::CanRedlineGroup( *pRedlSaveData
, *pTmpSav
, bBackSp
));
477 pDoc
->DeleteRedline( rDelPam
, false, USHRT_MAX
);
480 // Both 'deletes' can be consolidated, so 'move' the related character
482 nSttCntnt
--; // BackSpace: add char to array!
485 nEndCntnt
++; // Delete: attach char at the end
488 pSttStr
->Insert( cDelChar
, nUChrPos
);
489 pDelTxtNd
->EraseText( pStt
->nContent
, 1 );
495 SwUndoDelete::~SwUndoDelete()
499 if( pMvStt
) // Delete also the selection from UndoNodes array
501 // Insert saves content in IconSection
502 pMvStt
->GetNode().GetNodes().Delete( *pMvStt
, nNode
);
506 delete pRedlSaveData
;
509 static SwRewriter
lcl_RewriterFromHistory(SwHistory
& rHistory
)
511 SwRewriter aRewriter
;
515 for ( sal_uInt16 n
= 0; n
< rHistory
.Count(); n
++)
517 String aDescr
= rHistory
[n
]->GetDescription();
519 if (aDescr
.Len() > 0)
521 aRewriter
.AddRule(UndoArg2
, aDescr
);
530 aRewriter
.AddRule(UndoArg2
, SW_RESSTR(STR_FIELD
));
536 static bool lcl_IsSpecialCharacter(sal_Unicode nChar
)
540 case CH_TXTATR_BREAKWORD
:
541 case CH_TXTATR_INWORD
:
543 case CH_TXTATR_NEWLINE
:
553 const char UNDO_ARG1
[] = "$1";
554 const char UNDO_ARG2
[] = "$2";
555 const char UNDO_ARG3
[] = "$3";
557 static String
lcl_DenotedPortion(String rStr
, xub_StrLen nStart
,
562 if (nEnd
- nStart
> 0)
564 sal_Unicode cLast
= rStr
.GetChar(nEnd
- 1);
565 if (lcl_IsSpecialCharacter(cLast
))
570 aResult
= SW_RESSTR(STR_UNDO_TABS
);
573 case CH_TXTATR_NEWLINE
:
574 aResult
= SW_RESSTR(STR_UNDO_NLS
);
578 case CH_TXTATR_INWORD
:
579 case CH_TXTATR_BREAKWORD
:
580 aResult
= rtl::OUString(UNDO_ARG2
);
585 SwRewriter aRewriter
;
586 aRewriter
.AddRule(UndoArg1
,
587 String::CreateFromInt32(nEnd
- nStart
));
588 aResult
= aRewriter
.Apply(aResult
);
592 aResult
= SW_RESSTR(STR_START_QUOTE
);
593 aResult
+= rStr
.Copy(nStart
, nEnd
- nStart
);
594 aResult
+= SW_RESSTR(STR_END_QUOTE
);
601 String
DenoteSpecialCharacters(const String
& rStr
)
608 xub_StrLen nStart
= 0;
609 sal_Unicode cLast
= 0;
611 for (xub_StrLen i
= 0; i
< rStr
.Len(); i
++)
613 if (lcl_IsSpecialCharacter(rStr
.GetChar(i
)))
615 if (cLast
!= rStr
.GetChar(i
))
621 if (lcl_IsSpecialCharacter(cLast
))
627 aResult
+= lcl_DenotedPortion(rStr
, nStart
, i
);
633 cLast
= rStr
.GetChar(i
);
636 aResult
+= lcl_DenotedPortion(rStr
, nStart
, rStr
.Len());
639 aResult
= rtl::OUString(UNDO_ARG2
);
644 SwRewriter
SwUndoDelete::GetRewriter() const
647 String
* pStr
= NULL
;
651 if (sTableName
.Len() > 0)
654 SwRewriter aRewriter
;
655 aRewriter
.AddRule(UndoArg1
, SW_RESSTR(STR_START_QUOTE
));
656 aRewriter
.AddRule(UndoArg2
, sTableName
);
657 aRewriter
.AddRule(UndoArg3
, SW_RESSTR(STR_END_QUOTE
));
659 String sTmp
= aRewriter
.Apply(SW_RES(STR_TABLE_NAME
));
660 aResult
.AddRule(UndoArg1
, sTmp
);
663 aResult
.AddRule(UndoArg1
, SW_RESSTR(STR_PARAGRAPHS
));
669 if (pSttStr
!= NULL
&& pEndStr
!= NULL
&& pSttStr
->Len() == 0 &&
672 aStr
= SW_RESSTR(STR_PARAGRAPH_UNDO
);
678 else if (pEndStr
!= NULL
)
683 aStr
= DenoteSpecialCharacters(*pStr
);
687 aStr
= rtl::OUString(UNDO_ARG2
);
691 aStr
= ShortenString(aStr
, nUndoStringLength
, String(SW_RES(STR_LDOTS
)));
694 SwRewriter aRewriter
= lcl_RewriterFromHistory(*pHistory
);
695 aStr
= aRewriter
.Apply(aStr
);
698 aResult
.AddRule(UndoArg1
, aStr
);
704 // Every object, anchored "AtCntnt" will be reanchored at rPos
705 static void lcl_ReAnchorAtCntntFlyFrames( const SwFrmFmts
& rSpzArr
, SwPosition
&rPos
, sal_uLong nOldIdx
)
707 if( !rSpzArr
.empty() )
710 const SwFmtAnchor
* pAnchor
;
711 const SwPosition
* pAPos
;
712 for( sal_uInt16 n
= 0; n
< rSpzArr
.size(); ++n
)
714 pFmt
= (SwFlyFrmFmt
*)rSpzArr
[n
];
715 pAnchor
= &pFmt
->GetAnchor();
716 if (pAnchor
->GetAnchorId() == FLY_AT_PARA
)
718 pAPos
= pAnchor
->GetCntntAnchor();
719 if( pAPos
&& nOldIdx
== pAPos
->nNode
.GetIndex() )
721 SwFmtAnchor
aAnch( *pAnchor
);
722 aAnch
.SetAnchor( &rPos
);
723 pFmt
->SetFmtAttr( aAnch
);
730 void SwUndoDelete::UndoImpl(::sw::UndoRedoContext
& rContext
)
732 SwDoc
*const pDoc
= & rContext
.GetDoc();
734 sal_uLong nCalcStt
= nSttNode
- nNdDiff
;
736 if( nSectDiff
&& bBackSp
)
737 nCalcStt
+= nSectDiff
;
739 SwNodeIndex
aIdx( pDoc
->GetNodes(), nCalcStt
);
740 SwNode
* pInsNd
= &aIdx
.GetNode();
742 { // code block so that SwPosition is detached when deleting a Node
743 SwPosition
aPos( aIdx
);
746 if( pInsNd
->IsTableNode() )
748 pInsNd
= pDoc
->GetNodes().MakeTxtNode( aIdx
,
749 (SwTxtFmtColl
*)pDoc
->GetDfltTxtFmtColl() );
752 aPos
.nContent
.Assign( pInsNd
->GetCntntNode(), nSttCntnt
);
756 if( pInsNd
->IsCntntNode() )
757 aPos
.nContent
.Assign( (SwCntntNode
*)pInsNd
, nSttCntnt
);
759 pInsNd
= 0; // do not delete Node!
763 pInsNd
= 0; // do not delete Node!
765 sal_Bool bNodeMove
= 0 != nNode
;
769 // discard attributes since they all saved!
770 SwTxtNode
* pTxtNd
= aPos
.nNode
.GetNode().GetTxtNode();
772 if( pTxtNd
&& pTxtNd
->HasSwAttrSet() )
773 pTxtNd
->ResetAllAttr();
775 if( pTxtNd
&& pTxtNd
->GetpSwpHints() )
776 pTxtNd
->ClearSwpHintsArr( true );
778 if( pSttStr
&& !bFromTableCopy
)
780 sal_uLong nOldIdx
= aPos
.nNode
.GetIndex();
781 pDoc
->SplitNode( aPos
, false );
782 // After the split all objects are anchored at the first
783 // paragraph, but the pHistory of the fly frame formats relies
784 // on anchoring at the start of the selection
785 // => selection backwards needs a correction.
787 lcl_ReAnchorAtCntntFlyFrames( *pDoc
->GetSpzFrmFmts(), aPos
, nOldIdx
);
788 pTxtNd
= aPos
.nNode
.GetNode().GetTxtNode();
792 OUString
const ins( pTxtNd
->InsertText(*pEndStr
, aPos
.nContent
,
793 IDocumentContentOperations::INS_NOHINTEXPAND
) );
794 assert(ins
.getLength() == pEndStr
->Len()); // must succeed
796 pTxtNd
->RestoreMetadata(m_pMetadataUndoEnd
);
799 else if( pSttStr
&& bNodeMove
)
801 SwTxtNode
* pNd
= aPos
.nNode
.GetNode().GetTxtNode();
804 if( nSttCntnt
< pNd
->GetTxt().Len() )
806 sal_uLong nOldIdx
= aPos
.nNode
.GetIndex();
807 pDoc
->SplitNode( aPos
, false );
809 lcl_ReAnchorAtCntntFlyFrames( *pDoc
->GetSpzFrmFmts(), aPos
, nOldIdx
);
815 SwNode
* pMovedNode
= NULL
;
818 sal_uLong nMoveIndex
= aPos
.nNode
.GetIndex();
822 nMoveIndex
+= nSectDiff
+ 1;
823 pMovedNode
= &aPos
.nNode
.GetNode();
827 nMoveIndex
-= nSectDiff
+ 1;
830 SwNodeIndex
aMvIdx( pDoc
->GetNodes(), nMoveIndex
);
831 SwNodeRange
aRg( aPos
.nNode
, 0 - nDiff
, aPos
.nNode
, 1 - nDiff
);
834 pMovedNode
= &aPos
.nNode
.GetNode();
835 pDoc
->GetNodes()._MoveNodes( aRg
, pDoc
->GetNodes(), aMvIdx
, sal_True
);
841 SwNodeRange
aRange( *pMvStt
, 0, *pMvStt
, nNode
);
842 SwNodeIndex
aCopyIndex( aPos
.nNode
, -1 );
843 pDoc
->GetUndoManager().GetUndoNodes()._Copy( aRange
, aPos
.nNode
);
847 sal_uLong nMoveIndex
;
850 nMoveIndex
= nEndNode
- nNdDiff
;
851 aPos
.nNode
= nMoveIndex
+ nReplaceDummy
;
855 aPos
= SwPosition( aCopyIndex
);
856 nMoveIndex
= aPos
.nNode
.GetIndex() + nReplaceDummy
+ 1;
858 SwNodeIndex
aMvIdx( pDoc
->GetNodes(), nMoveIndex
);
859 SwNodeRange
aRg( aPos
.nNode
, 0, aPos
.nNode
, 1 );
860 pMovedNode
= &aPos
.nNode
.GetNode();
861 pDoc
->GetNodes()._MoveNodes( aRg
, pDoc
->GetNodes(), aMvIdx
, sal_True
);
862 pDoc
->GetNodes().Delete( aMvIdx
, 1 );
867 lcl_MakeAutoFrms( *pDoc
->GetSpzFrmFmts(), pMovedNode
->GetIndex() );
871 aPos
.nNode
= nSttNode
- nNdDiff
+ ( bJoinNext
? 0 : nReplaceDummy
);
872 SwTxtNode
* pTxtNd
= aPos
.nNode
.GetNode().GetTxtNode();
873 // If more than a single Node got deleted, also all "Node"
874 // attributes were saved
877 if( pTxtNd
->HasSwAttrSet() && bNodeMove
&& !pEndStr
)
878 pTxtNd
->ResetAllAttr();
880 if( pTxtNd
->GetpSwpHints() )
881 pTxtNd
->ClearSwpHintsArr( true );
883 // SectionNode mode and selection from top to bottom:
884 // -> in StartNode is still the rest of the Join => delete
885 aPos
.nContent
.Assign( pTxtNd
, nSttCntnt
);
886 OUString
const ins( pTxtNd
->InsertText(*pSttStr
, aPos
.nContent
,
887 IDocumentContentOperations::INS_NOHINTEXPAND
) );
888 assert(ins
.getLength() == pSttStr
->Len()); // must succeed
890 pTxtNd
->RestoreMetadata(m_pMetadataUndoStart
);
896 pHistory
->TmpRollback( pDoc
, nSetPos
, false );
897 if( nSetPos
) // there were Footnodes/FlyFrames
899 // are there others than these ones?
900 if( nSetPos
< pHistory
->Count() )
902 // if so save the attributes of the others
904 aHstr
.Move( 0, pHistory
, nSetPos
);
905 pHistory
->Rollback( pDoc
);
906 pHistory
->Move( 0, &aHstr
);
910 pHistory
->Rollback( pDoc
);
916 if( bResetPgDesc
|| bResetPgBrk
)
918 sal_uInt16 nStt
= static_cast<sal_uInt16
>( bResetPgDesc
? RES_PAGEDESC
: RES_BREAK
);
919 sal_uInt16 nEnd
= static_cast<sal_uInt16
>( bResetPgBrk
? RES_BREAK
: RES_PAGEDESC
);
921 SwNode
* pNode
= pDoc
->GetNodes()[ nEndNode
+ 1 ];
922 if( pNode
->IsCntntNode() )
923 ((SwCntntNode
*)pNode
)->ResetAttr( nStt
, nEnd
);
924 else if( pNode
->IsTableNode() )
925 ((SwTableNode
*)pNode
)->GetTable().GetFrmFmt()->ResetFmtAttr( nStt
, nEnd
);
928 // delete the temporarily added Node
930 pDoc
->GetNodes().Delete( aIdx
, 1 );
932 SetSaveData( *pDoc
, *pRedlSaveData
);
934 AddUndoRedoPaM(rContext
, true);
937 void SwUndoDelete::RedoImpl(::sw::UndoRedoContext
& rContext
)
939 SwPaM
& rPam
= AddUndoRedoPaM(rContext
);
940 SwDoc
& rDoc
= *rPam
.GetDoc();
944 bool bSuccess
= FillSaveData(rPam
, *pRedlSaveData
, sal_True
);
946 "SwUndoDelete::Redo: used to have redline data, but now none?");
949 delete pRedlSaveData
, pRedlSaveData
= 0;
955 SwUndRng
aTmpRng( rPam
);
956 RemoveIdxFromRange( rPam
, sal_False
);
957 aTmpRng
.SetPaM( rPam
);
959 if( !bJoinNext
) // then restore selection from bottom to top
963 if( pHistory
) // are the attributes saved?
965 pHistory
->SetTmpEnd( pHistory
->Count() );
967 aHstr
.Move( 0, pHistory
);
971 OSL_ENSURE( rPam
.HasMark(), "PaM without Mark" );
972 DelCntntIndex( *rPam
.GetMark(), *rPam
.GetPoint(),
973 DelCntntType(nsDelCntntType::DELCNT_ALL
| nsDelCntntType::DELCNT_CHKNOCNTNT
) );
975 _DelBookmarks(rPam
.GetMark()->nNode
, rPam
.GetPoint()->nNode
);
978 DelCntntIndex( *rPam
.GetMark(), *rPam
.GetPoint() );
979 nSetPos
= pHistory
? pHistory
->Count() : 0;
981 pHistory
->Move( nSetPos
, &aHstr
);
987 OSL_ENSURE( rPam
.HasMark(), "PaM without Mark" );
988 DelCntntIndex( *rPam
.GetMark(), *rPam
.GetPoint(),
989 DelCntntType(nsDelCntntType::DELCNT_ALL
| nsDelCntntType::DELCNT_CHKNOCNTNT
) );
991 _DelBookmarks( rPam
.GetMark()->nNode
, rPam
.GetPoint()->nNode
);
994 DelCntntIndex( *rPam
.GetMark(), *rPam
.GetPoint() );
995 nSetPos
= pHistory
? pHistory
->Count() : 0;
998 if( !pSttStr
&& !pEndStr
)
1000 SwNodeIndex aSttIdx
= ( bDelFullPara
|| bJoinNext
)
1001 ? rPam
.GetMark()->nNode
1002 : rPam
.GetPoint()->nNode
;
1003 SwTableNode
* pTblNd
= aSttIdx
.GetNode().GetTableNode();
1008 // than add again a Node at the end
1009 const SwNodeIndex
aTmpIdx( *pTblNd
->EndOfSectionNode(), 1 );
1010 rDoc
.GetNodes().MakeTxtNode( aTmpIdx
,
1011 rDoc
.GetTxtCollFromPool( RES_POOLCOLL_STANDARD
) );
1014 SwCntntNode
* pNextNd
= rDoc
.GetNodes()[
1015 pTblNd
->EndOfSectionIndex()+1 ]->GetCntntNode();
1018 SwFrmFmt
* pTableFmt
= pTblNd
->GetTable().GetFrmFmt();
1020 const SfxPoolItem
*pItem
;
1021 if( SFX_ITEM_SET
== pTableFmt
->GetItemState( RES_PAGEDESC
,
1022 sal_False
, &pItem
) )
1023 pNextNd
->SetAttr( *pItem
);
1025 if( SFX_ITEM_SET
== pTableFmt
->GetItemState( RES_BREAK
,
1026 sal_False
, &pItem
) )
1027 pNextNd
->SetAttr( *pItem
);
1035 rDoc
.GetNodes().Delete( aSttIdx
, nEndNode
- nSttNode
);
1037 // always set the cursor into a ContentNode!
1038 if( !rPam
.Move( fnMoveBackward
, fnGoCntnt
) &&
1039 !rPam
.Move( fnMoveForward
, fnGoCntnt
) )
1040 rPam
.GetPoint()->nContent
.Assign( rPam
.GetCntntNode(), 0 );
1042 else if( bDelFullPara
)
1044 // The Pam was incremented by one at Point (== end) to provide space
1045 // for UNDO. This now needs to be reverted!
1046 rPam
.End()->nNode
--;
1047 if( rPam
.GetPoint()->nNode
== rPam
.GetMark()->nNode
)
1048 *rPam
.GetMark() = *rPam
.GetPoint();
1049 rDoc
.DelFullPara( rPam
);
1052 rDoc
.DeleteAndJoin( rPam
);
1055 void SwUndoDelete::RepeatImpl(::sw::RepeatContext
& rContext
)
1057 // this action does not seem idempotent,
1058 // so make sure it is only executed once on repeat
1059 if (rContext
.m_bDeleteRepeated
)
1062 SwPaM
& rPam
= rContext
.GetRepeatPaM();
1063 SwDoc
& rDoc
= *rPam
.GetDoc();
1064 ::sw::GroupUndoGuard
const undoGuard(rDoc
.GetIDocumentUndoRedo());
1065 if( !rPam
.HasMark() )
1068 rPam
.Move( fnMoveForward
, fnGoCntnt
);
1071 rDoc
.DelFullPara( rPam
);
1073 rDoc
.DeleteAndJoin( rPam
);
1074 rContext
.m_bDeleteRepeated
= true;
1078 void SwUndoDelete::SetTableName(const String
& rName
)
1083 String
SwRewriter::Apply(const String
& rStr
) const
1085 rtl::OUString aResult
= rStr
;
1086 std::vector
<SwRewriteRule
>::const_iterator aIt
;
1088 for (aIt
= mRules
.begin(); aIt
!= mRules
.end(); ++aIt
)
1094 aResult
= aResult
.replaceAll(UNDO_ARG1
, aIt
->second
);
1097 aResult
= aResult
.replaceAll(UNDO_ARG2
, aIt
->second
);
1100 aResult
= aResult
.replaceAll(UNDO_ARG3
, aIt
->second
);
1108 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */