Impress Remote 1.0.5, tag sdremote-1.0.5
[LibreOffice.git] / sw / source / core / undo / undel.cxx
blob06d4529c5fd04e7b0a2b30124a033ab9f0f26f51
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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>
25 #include <frmfmt.hxx>
26 #include <fmtanchr.hxx>
27 #include <doc.hxx>
28 #include <UndoManager.hxx>
29 #include <swtable.hxx>
30 #include <swundo.hxx>
31 #include <pam.hxx>
32 #include <ndtxt.hxx>
33 #include <UndoCore.hxx>
34 #include <rolbck.hxx>
35 #include <poolfmt.hxx>
36 #include <mvsave.hxx>
37 #include <redline.hxx>
38 #include <docary.hxx>
39 #include <sfx2/app.hxx>
40 #include <fldbas.hxx>
41 #include <fmtfld.hxx>
42 #include <comcore.hrc> // #111827#
43 #include <undo.hrc>
44 #include <vector>
46 // DELETE
47 /* lcl_MakeAutoFrms has to call MakeFrms for objects bounded "AtChar"
48 ( == AUTO ), if the anchor frame has be moved via _MoveNodes(..) and
49 DelFrms(..)
51 static void lcl_MakeAutoFrms( const SwFrmFmts& rSpzArr, sal_uLong nMovedIndex )
53 if( !rSpzArr.empty() )
55 SwFlyFrmFmt* pFmt;
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() )
65 pFmt->MakeFrms();
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
76 // function.
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;
118 if( !pHistory )
119 pHistory = new SwHistory;
121 // delete all footnotes for now
122 const SwPosition *pStt = rPam.Start(),
123 *pEnd = rPam.GetPoint() == pStt
124 ? rPam.GetMark()
125 : rPam.GetPoint();
127 // Step 1. deletion/record of content indizes
128 if( bDelFullPara )
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);
137 else
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;
149 if( !bFullPara )
151 pSttTxtNd = pStt->nNode.GetNode().GetTxtNode();
152 pEndTxtNd = nSttNode == nEndNode
153 ? pSttTxtNd
154 : pEnd->nNode.GetNode().GetTxtNode();
157 sal_Bool bMoveNds = *pStt == *pEnd // any area still existent?
158 ? sal_False
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 ) )
188 rPam.Exchange();
190 if( !pSttTxtNd && !pEndTxtNd )
191 rPam.GetPoint()->nNode--;
192 rPam.DeleteMark(); // the SPoint is in the selection
194 if( !pEndTxtNd )
195 nEndCntnt = 0;
196 if( !pSttTxtNd )
197 nSttCntnt = 0;
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
212 SwNode* pTmpNd;
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() ) )
222 aRg.aEnd++;
223 nReplaceDummy = aRg.aEnd.GetIndex() + nNdDiff - nEndNode;
224 if( nReplaceDummy )
225 { // The selection has been expanded, because
226 aRg.aEnd++;
227 if( pEndTxtNd )
229 // The end text node has to leave the (expanded) selection
230 // The dummy is needed because _MoveNodes deletes empty
231 // sections
232 ++nReplaceDummy;
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 );
238 aRg.aEnd--;
240 else
241 nReplaceDummy = 0;
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() ) )
251 aRg.aStart--;
252 if( pSttTxtNd )
254 nReplaceDummy = nSttNode - nNdDiff - aRg.aStart.GetIndex();
255 if( nReplaceDummy )
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 );
262 aRg.aStart--;
267 if( bFromTableCopy )
269 if( !pEndTxtNd )
271 if( pSttTxtNd )
272 aRg.aStart++;
273 else if( !bFullPara && !aRg.aEnd.GetNode().IsCntntNode() )
274 aRg.aEnd--;
277 else if( pSttTxtNd && ( pEndTxtNd || pSttTxtNd->GetTxt().Len() ) )
278 aRg.aStart++;
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
294 if( nSectDiff )
296 if( bJoinNext )
298 SwNodeRange aMvRg( *pEndTxtNd, 0, *pEndTxtNd, 1 );
299 rDocNds._MoveNodes( aMvRg, rDocNds, aRg.aStart, sal_True );
301 else
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() );
312 else
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 );
322 else
324 nNdDiff = nSttNode;
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() )
335 DELETEZ( pHistory );
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
343 if( pSttTxtNd )
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
350 // areas of on/off
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
361 // UNDO history)
362 pSttStr = (String*)new String( pSttTxtNd->GetTxt().Copy( nSttCntnt, nLen ));
363 pSttTxtNd->EraseText( pStt->nContent, nLen );
364 if( pSttTxtNd->GetpSwpHints() )
365 pSttTxtNd->GetpSwpHints()->DeRegister();
367 // METADATA: store
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();
376 if( bOneNode )
377 return sal_False; // stop moving more nodes
380 // 2 - copy end into End-String
381 if( pEndTxtNd )
383 SwIndex aEndIdx( pEndTxtNd );
384 nNdIdx = pEnd->nNode.GetIndex();
385 SwRegHistory aRHst( *pEndTxtNd, pHistory );
387 // always save all text atttibutes because of possibly overlapping
388 // areas of on/off
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
396 // UNDO history)
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();
403 // METADATA: store
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 )
422 return sal_False;
424 // only the deletion of single char's can be condensed
425 if( nSttNode != nEndNode || ( !bGroup && nSttCntnt+1 != nEndCntnt ))
426 return sal_False;
428 const SwPosition *pStt = rDelPam.Start(),
429 *pEnd = rDelPam.GetPoint() == pStt
430 ? rDelPam.GetMark()
431 : rDelPam.GetPoint();
433 if( pStt->nNode != pEnd->nNode ||
434 pStt->nContent.GetIndex()+1 != pEnd->nContent.GetIndex() ||
435 pEnd->nNode != nSttNode )
436 return sal_False;
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;
443 bBackSp = sal_True;
445 else if( pStt->nContent == nSttCntnt )
447 if( bGroup && bBackSp ) return sal_False;
448 bBackSp = sal_False;
450 else
451 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 ) )
463 return sal_False;
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 ));
473 delete pTmpSav;
474 if( !bOk )
475 return sal_False;
477 pDoc->DeleteRedline( rDelPam, false, USHRT_MAX );
480 // Both 'deletes' can be consolidated, so 'move' the related character
481 if( bBackSp )
482 nSttCntnt--; // BackSpace: add char to array!
483 else
485 nEndCntnt++; // Delete: attach char at the end
486 nUChrPos++;
488 pSttStr->Insert( cDelChar, nUChrPos );
489 pDelTxtNd->EraseText( pStt->nContent, 1 );
491 bGroup = sal_True;
492 return sal_True;
495 SwUndoDelete::~SwUndoDelete()
497 delete pSttStr;
498 delete pEndStr;
499 if( pMvStt ) // Delete also the selection from UndoNodes array
501 // Insert saves content in IconSection
502 pMvStt->GetNode().GetNodes().Delete( *pMvStt, nNode );
503 delete pMvStt;
505 delete pRedlData;
506 delete pRedlSaveData;
509 static SwRewriter lcl_RewriterFromHistory(SwHistory & rHistory)
511 SwRewriter aRewriter;
513 bool bDone = false;
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);
523 bDone = true;
524 break;
528 if (! bDone)
530 aRewriter.AddRule(UndoArg2, SW_RESSTR(STR_FIELD));
533 return aRewriter;
536 static bool lcl_IsSpecialCharacter(sal_Unicode nChar)
538 switch (nChar)
540 case CH_TXTATR_BREAKWORD:
541 case CH_TXTATR_INWORD:
542 case CH_TXTATR_TAB:
543 case CH_TXTATR_NEWLINE:
544 return true;
546 default:
547 break;
550 return false;
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,
558 xub_StrLen nEnd)
560 String aResult;
562 if (nEnd - nStart > 0)
564 sal_Unicode cLast = rStr.GetChar(nEnd - 1);
565 if (lcl_IsSpecialCharacter(cLast))
567 switch(cLast)
569 case CH_TXTATR_TAB:
570 aResult = SW_RESSTR(STR_UNDO_TABS);
572 break;
573 case CH_TXTATR_NEWLINE:
574 aResult = SW_RESSTR(STR_UNDO_NLS);
576 break;
578 case CH_TXTATR_INWORD:
579 case CH_TXTATR_BREAKWORD:
580 aResult = rtl::OUString(UNDO_ARG2);
582 break;
585 SwRewriter aRewriter;
586 aRewriter.AddRule(UndoArg1,
587 String::CreateFromInt32(nEnd - nStart));
588 aResult = aRewriter.Apply(aResult);
590 else
592 aResult = SW_RESSTR(STR_START_QUOTE);
593 aResult += rStr.Copy(nStart, nEnd - nStart);
594 aResult += SW_RESSTR(STR_END_QUOTE);
598 return aResult;
601 String DenoteSpecialCharacters(const String & rStr)
603 String aResult;
605 if (rStr.Len() > 0)
607 bool bStart = false;
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))
616 bStart = true;
619 else
621 if (lcl_IsSpecialCharacter(cLast))
622 bStart = true;
625 if (bStart)
627 aResult += lcl_DenotedPortion(rStr, nStart, i);
629 nStart = i;
630 bStart = false;
633 cLast = rStr.GetChar(i);
636 aResult += lcl_DenotedPortion(rStr, nStart, rStr.Len());
638 else
639 aResult = rtl::OUString(UNDO_ARG2);
641 return aResult;
644 SwRewriter SwUndoDelete::GetRewriter() const
646 SwRewriter aResult;
647 String * pStr = NULL;
649 if (nNode != 0)
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);
662 else
663 aResult.AddRule(UndoArg1, SW_RESSTR(STR_PARAGRAPHS));
665 else
667 String aStr;
669 if (pSttStr != NULL && pEndStr != NULL && pSttStr->Len() == 0 &&
670 pEndStr->Len() == 0)
672 aStr = SW_RESSTR(STR_PARAGRAPH_UNDO);
674 else
676 if (pSttStr != NULL)
677 pStr = pSttStr;
678 else if (pEndStr != NULL)
679 pStr = pEndStr;
681 if (pStr != NULL)
683 aStr = DenoteSpecialCharacters(*pStr);
685 else
687 aStr = rtl::OUString(UNDO_ARG2);
691 aStr = ShortenString(aStr, nUndoStringLength, String(SW_RES(STR_LDOTS)));
692 if (pHistory)
694 SwRewriter aRewriter = lcl_RewriterFromHistory(*pHistory);
695 aStr = aRewriter.Apply(aStr);
698 aResult.AddRule(UndoArg1, aStr);
701 return aResult;
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() )
709 SwFlyFrmFmt* pFmt;
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 );
744 if( !bDelFullPara )
746 if( pInsNd->IsTableNode() )
748 pInsNd = pDoc->GetNodes().MakeTxtNode( aIdx,
749 (SwTxtFmtColl*)pDoc->GetDfltTxtFmtColl() );
750 aIdx--;
751 aPos.nNode = aIdx;
752 aPos.nContent.Assign( pInsNd->GetCntntNode(), nSttCntnt );
754 else
756 if( pInsNd->IsCntntNode() )
757 aPos.nContent.Assign( (SwCntntNode*)pInsNd, nSttCntnt );
758 if( !bTblDelLastNd )
759 pInsNd = 0; // do not delete Node!
762 else
763 pInsNd = 0; // do not delete Node!
765 sal_Bool bNodeMove = 0 != nNode;
767 if( pEndStr )
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.
786 if( bBackSp )
787 lcl_ReAnchorAtCntntFlyFrames( *pDoc->GetSpzFrmFmts(), aPos, nOldIdx );
788 pTxtNd = aPos.nNode.GetNode().GetTxtNode();
790 if( pTxtNd )
792 OUString const ins( pTxtNd->InsertText(*pEndStr, aPos.nContent,
793 IDocumentContentOperations::INS_NOHINTEXPAND) );
794 assert(ins.getLength() == pEndStr->Len()); // must succeed
795 // METADATA: restore
796 pTxtNd->RestoreMetadata(m_pMetadataUndoEnd);
799 else if( pSttStr && bNodeMove )
801 SwTxtNode * pNd = aPos.nNode.GetNode().GetTxtNode();
802 if( pNd )
804 if( nSttCntnt < pNd->GetTxt().Len() )
806 sal_uLong nOldIdx = aPos.nNode.GetIndex();
807 pDoc->SplitNode( aPos, false );
808 if( bBackSp )
809 lcl_ReAnchorAtCntntFlyFrames( *pDoc->GetSpzFrmFmts(), aPos, nOldIdx );
811 else
812 aPos.nNode++;
815 SwNode* pMovedNode = NULL;
816 if( nSectDiff )
818 sal_uLong nMoveIndex = aPos.nNode.GetIndex();
819 int nDiff = 0;
820 if( bJoinNext )
822 nMoveIndex += nSectDiff + 1;
823 pMovedNode = &aPos.nNode.GetNode();
825 else
827 nMoveIndex -= nSectDiff + 1;
828 ++nDiff;
830 SwNodeIndex aMvIdx( pDoc->GetNodes(), nMoveIndex );
831 SwNodeRange aRg( aPos.nNode, 0 - nDiff, aPos.nNode, 1 - nDiff );
832 aPos.nNode--;
833 if( !bJoinNext )
834 pMovedNode = &aPos.nNode.GetNode();
835 pDoc->GetNodes()._MoveNodes( aRg, pDoc->GetNodes(), aMvIdx, sal_True );
836 aPos.nNode++;
839 if( bNodeMove )
841 SwNodeRange aRange( *pMvStt, 0, *pMvStt, nNode );
842 SwNodeIndex aCopyIndex( aPos.nNode, -1 );
843 pDoc->GetUndoManager().GetUndoNodes()._Copy( aRange, aPos.nNode );
845 if( nReplaceDummy )
847 sal_uLong nMoveIndex;
848 if( bJoinNext )
850 nMoveIndex = nEndNode - nNdDiff;
851 aPos.nNode = nMoveIndex + nReplaceDummy;
853 else
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 );
866 if( pMovedNode )
867 lcl_MakeAutoFrms( *pDoc->GetSpzFrmFmts(), pMovedNode->GetIndex() );
869 if( pSttStr )
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
875 if (pTxtNd != NULL)
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
889 // METADATA: restore
890 pTxtNd->RestoreMetadata(m_pMetadataUndoStart);
894 if( pHistory )
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
903 SwHistory aHstr;
904 aHstr.Move( 0, pHistory, nSetPos );
905 pHistory->Rollback( pDoc );
906 pHistory->Move( 0, &aHstr );
908 else
910 pHistory->Rollback( pDoc );
911 DELETEZ( pHistory );
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
929 if( pInsNd )
930 pDoc->GetNodes().Delete( aIdx, 1 );
931 if( pRedlSaveData )
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();
942 if( pRedlSaveData )
944 bool bSuccess = FillSaveData(rPam, *pRedlSaveData, sal_True);
945 OSL_ENSURE(bSuccess,
946 "SwUndoDelete::Redo: used to have redline data, but now none?");
947 if (!bSuccess)
949 delete pRedlSaveData, pRedlSaveData = 0;
953 if( !bDelFullPara )
955 SwUndRng aTmpRng( rPam );
956 RemoveIdxFromRange( rPam, sal_False );
957 aTmpRng.SetPaM( rPam );
959 if( !bJoinNext ) // then restore selection from bottom to top
960 rPam.Exchange();
963 if( pHistory ) // are the attributes saved?
965 pHistory->SetTmpEnd( pHistory->Count() );
966 SwHistory aHstr;
967 aHstr.Move( 0, pHistory );
969 if( bDelFullPara )
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);
977 else
978 DelCntntIndex( *rPam.GetMark(), *rPam.GetPoint() );
979 nSetPos = pHistory ? pHistory->Count() : 0;
981 pHistory->Move( nSetPos, &aHstr );
983 else
985 if( bDelFullPara )
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 );
993 else
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();
1004 if( pTblNd )
1006 if( bTblDelLastNd )
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();
1016 if( pNextNd )
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 );
1029 pTblNd->DelFrms();
1032 rPam.SetMark();
1033 rPam.DeleteMark();
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 );
1051 else
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)
1060 return;
1062 SwPaM & rPam = rContext.GetRepeatPaM();
1063 SwDoc& rDoc = *rPam.GetDoc();
1064 ::sw::GroupUndoGuard const undoGuard(rDoc.GetIDocumentUndoRedo());
1065 if( !rPam.HasMark() )
1067 rPam.SetMark();
1068 rPam.Move( fnMoveForward, fnGoCntnt );
1070 if( bDelFullPara )
1071 rDoc.DelFullPara( rPam );
1072 else
1073 rDoc.DeleteAndJoin( rPam );
1074 rContext.m_bDeleteRepeated = true;
1078 void SwUndoDelete::SetTableName(const String & rName)
1080 sTableName = 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)
1090 switch (aIt->first)
1092 case UndoArg1:
1093 default:
1094 aResult = aResult.replaceAll(UNDO_ARG1, aIt->second);
1095 break;
1096 case UndoArg2:
1097 aResult = aResult.replaceAll(UNDO_ARG2, aIt->second);
1098 break;
1099 case UndoArg3:
1100 aResult = aResult.replaceAll(UNDO_ARG3, aIt->second);
1101 break;
1105 return aResult;
1108 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */