| /************************************************************** |
| * |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| * |
| *************************************************************/ |
| |
| |
| |
| // MARKER(update_precomp.py): autogen include statement, do not remove |
| #include "precompiled_sw.hxx" |
| |
| #include <UndoDelete.hxx> |
| |
| #include <hintids.hxx> |
| #include <unotools/charclass.hxx> |
| #include <editeng/brkitem.hxx> |
| #include <fmtpdsc.hxx> |
| #include <frmfmt.hxx> |
| #include <fmtanchr.hxx> |
| #include <doc.hxx> |
| #include <UndoManager.hxx> |
| #include <swtable.hxx> |
| #include <swundo.hxx> // fuer die UndoIds |
| #include <pam.hxx> |
| #include <ndtxt.hxx> |
| #include <UndoCore.hxx> |
| #include <rolbck.hxx> |
| #include <poolfmt.hxx> |
| #include <mvsave.hxx> |
| #include <redline.hxx> |
| #include <docary.hxx> |
| #include <sfx2/app.hxx> |
| |
| #include <fldbas.hxx> |
| #include <fmtfld.hxx> |
| #include <comcore.hrc> // #111827# |
| #include <undo.hrc> |
| |
| // #include <editeng/svxacorr.hxx> |
| // #include <comphelper/processfactory.hxx> |
| // #include <editeng/unolingu.hxx> |
| // #include <unotools/localedatawrapper.hxx> |
| |
| // using namespace comphelper; |
| |
| |
| // DELETE |
| /* lcl_MakeAutoFrms has to call MakeFrms for objects bounded "AtChar" ( == AUTO ), |
| if the anchor frame has be moved via _MoveNodes(..) and DelFrms(..) |
| */ |
| |
| void lcl_MakeAutoFrms( const SwSpzFrmFmts& rSpzArr, sal_uLong nMovedIndex ) |
| { |
| if( rSpzArr.Count() ) |
| { |
| SwFlyFrmFmt* pFmt; |
| const SwFmtAnchor* pAnchor; |
| for( sal_uInt16 n = 0; n < rSpzArr.Count(); ++n ) |
| { |
| pFmt = (SwFlyFrmFmt*)rSpzArr[n]; |
| pAnchor = &pFmt->GetAnchor(); |
| if (pAnchor->GetAnchorId() == FLY_AT_CHAR) |
| { |
| const SwPosition* pAPos = pAnchor->GetCntntAnchor(); |
| if( pAPos && nMovedIndex == pAPos->nNode.GetIndex() ) |
| pFmt->MakeFrms(); |
| } |
| } |
| } |
| } |
| |
| /* |
| SwUndoDelete has to perform a deletion and to record anything that is needed to restore the |
| situation before the deletion. Unfortunately a part of the deletion will be done after calling |
| this Ctor, this has to be kept in mind! In this Ctor only the complete paragraphs will be deleted, |
| the joining of the first and last paragraph of the selection will be handled outside this function. |
| Here are the main steps of the function: |
| 1. Deletion/recording of content indizes of the selection: footnotes, fly frames and bookmarks |
| Step 1 could shift all nodes by deletion of footnotes => nNdDiff will be set. |
| 2. If the paragraph where the selection ends, is the last content of a section so that this |
| section becomes empty when the paragraphs will be joined we have to do some smart actions ;-) |
| The paragraph will be moved outside the section and replaced by a dummy text node, the complete |
| section will be deleted in step 3. The difference between replacement dummy and original is |
| nReplacementDummy. |
| 3. Moving complete selected nodes into the UndoArray. Before this happens the selection has to be |
| extended if there are sections which would become empty otherwise. BTW: sections will be moved into |
| the UndoArray if they are complete part of the selection. Sections starting or ending outside of the |
| selection will not be removed from the DocNodeArray even they got a "dummy"-copy in the UndoArray. |
| 4. We have to anticipate the joining of the two paragraphs if the start paragraph is inside a |
| section and the end paragraph not. Then we have to move the paragraph into this section and to |
| record this in nSectDiff. |
| */ |
| |
| SwUndoDelete::SwUndoDelete( |
| SwPaM& rPam, |
| sal_Bool bFullPara, |
| sal_Bool bCalledByTblCpy ) |
| : SwUndo(UNDO_DELETE) |
| , SwUndRng( rPam ) |
| , pMvStt( 0 ) |
| , pSttStr(0) |
| , pEndStr(0) |
| , pRedlData(0) |
| , pRedlSaveData(0) |
| , nNode(0) |
| , nNdDiff(0) |
| , nSectDiff(0) |
| , nReplaceDummy(0) |
| , nSetPos(0) |
| , bGroup( sal_False ) |
| , bBackSp( sal_False ) |
| , bJoinNext( sal_False ) |
| , bTblDelLastNd( sal_False ) |
| , bDelFullPara( bFullPara ) |
| , bResetPgDesc( sal_False ) |
| , bResetPgBrk( sal_False ) |
| , bFromTableCopy( bCalledByTblCpy ) |
| { |
| bDelFullPara = bFullPara; // This is set e.g. if an empty paragraph before a table is deleted |
| |
| bCacheComment = false; |
| |
| SwDoc * pDoc = rPam.GetDoc(); |
| |
| if( !pDoc->IsIgnoreRedline() && pDoc->GetRedlineTbl().Count() ) |
| { |
| pRedlSaveData = new SwRedlineSaveDatas; |
| if( !FillSaveData( rPam, *pRedlSaveData )) |
| delete pRedlSaveData, pRedlSaveData = 0; |
| } |
| |
| if( !pHistory ) |
| pHistory = new SwHistory; |
| |
| // loesche erstmal alle Fussnoten |
| const SwPosition *pStt = rPam.Start(), |
| *pEnd = rPam.GetPoint() == pStt |
| ? rPam.GetMark() |
| : rPam.GetPoint(); |
| |
| // Step 1. deletion/record of content indizes |
| if( bDelFullPara ) |
| { |
| ASSERT( rPam.HasMark(), "PaM ohne Mark" ); |
| DelCntntIndex( *rPam.GetMark(), *rPam.GetPoint(), |
| DelCntntType(nsDelCntntType::DELCNT_ALL | nsDelCntntType::DELCNT_CHKNOCNTNT) ); |
| |
| ::sw::UndoGuard const undoGuard(pDoc->GetIDocumentUndoRedo()); |
| _DelBookmarks(pStt->nNode, pEnd->nNode); |
| } |
| else |
| DelCntntIndex( *rPam.GetMark(), *rPam.GetPoint() ); |
| |
| nSetPos = pHistory ? pHistory->Count() : 0; |
| |
| // wurde schon was geloescht ?? |
| nNdDiff = nSttNode - pStt->nNode.GetIndex(); |
| |
| bJoinNext = !bFullPara && pEnd == rPam.GetPoint(); |
| bBackSp = !bFullPara && !bJoinNext; |
| |
| SwTxtNode *pSttTxtNd = 0, *pEndTxtNd = 0; |
| if( !bFullPara ) |
| { |
| pSttTxtNd = pStt->nNode.GetNode().GetTxtNode(); |
| pEndTxtNd = nSttNode == nEndNode |
| ? pSttTxtNd |
| : pEnd->nNode.GetNode().GetTxtNode(); |
| } |
| |
| sal_Bool bMoveNds = *pStt == *pEnd // noch ein Bereich vorhanden ?? |
| ? sal_False |
| : ( SaveCntnt( pStt, pEnd, pSttTxtNd, pEndTxtNd ) || bFromTableCopy ); |
| |
| if( pSttTxtNd && pEndTxtNd && pSttTxtNd != pEndTxtNd ) |
| { |
| // zwei unterschiedliche TextNodes, also speicher noch die |
| // TextFormatCollection fuers |
| pHistory->Add( pSttTxtNd->GetTxtColl(),pStt->nNode.GetIndex(), ND_TEXTNODE ); |
| pHistory->Add( pEndTxtNd->GetTxtColl(),pEnd->nNode.GetIndex(), ND_TEXTNODE ); |
| |
| if( !bJoinNext ) // Selection von Unten nach Oben |
| { |
| // Beim JoinPrev() werden die AUTO-PageBreak's richtig |
| // kopiert. Um diese beim Undo wieder herzustellen, muss das |
| // Auto-PageBreak aus dem EndNode zurueckgesetzt werden. |
| // - fuer die PageDesc, ColBreak dito ! |
| if( pEndTxtNd->HasSwAttrSet() ) |
| { |
| SwRegHistory aRegHist( *pEndTxtNd, pHistory ); |
| if( SFX_ITEM_SET == pEndTxtNd->GetpSwAttrSet()->GetItemState( |
| RES_BREAK, sal_False ) ) |
| pEndTxtNd->ResetAttr( RES_BREAK ); |
| if( pEndTxtNd->HasSwAttrSet() && |
| SFX_ITEM_SET == pEndTxtNd->GetpSwAttrSet()->GetItemState( |
| RES_PAGEDESC, sal_False ) ) |
| pEndTxtNd->ResetAttr( RES_PAGEDESC ); |
| } |
| } |
| } |
| |
| |
| // verschiebe jetzt noch den PaM !!! |
| // der SPoint steht am Anfang der SSelection |
| if( pEnd == rPam.GetPoint() && ( !bFullPara || pSttTxtNd || pEndTxtNd ) ) |
| rPam.Exchange(); |
| |
| if( !pSttTxtNd && !pEndTxtNd ) |
| rPam.GetPoint()->nNode--; |
| rPam.DeleteMark(); // der SPoint ist aus dem Bereich |
| |
| if( !pEndTxtNd ) |
| nEndCntnt = 0; |
| if( !pSttTxtNd ) |
| nSttCntnt = 0; |
| |
| if( bMoveNds ) // sind noch Nodes zu verschieben ? |
| { |
| SwNodes& rNds = pDoc->GetUndoManager().GetUndoNodes(); |
| SwNodes& rDocNds = pDoc->GetNodes(); |
| SwNodeRange aRg( rDocNds, nSttNode - nNdDiff, |
| rDocNds, nEndNode - nNdDiff ); |
| if( !bFullPara && !pEndTxtNd && |
| &aRg.aEnd.GetNode() != &pDoc->GetNodes().GetEndOfContent() ) |
| { |
| SwNode* pNode = aRg.aEnd.GetNode().StartOfSectionNode(); |
| if( pNode->GetIndex() >= nSttNode - nNdDiff ) |
| aRg.aEnd++; // Deletion of a complete table |
| } |
| SwNode* pTmpNd; |
| // Step 2: Expand selection if necessary |
| if( bJoinNext || bFullPara ) |
| { |
| // If all content of a section will be moved into Undo, |
| // the section itself should be moved complete. |
| while( aRg.aEnd.GetIndex() + 2 < rDocNds.Count() && |
| ( (pTmpNd = rDocNds[ aRg.aEnd.GetIndex()+1 ])->IsEndNode() && |
| pTmpNd->StartOfSectionNode()->IsSectionNode() && |
| pTmpNd->StartOfSectionNode()->GetIndex() >= aRg.aStart.GetIndex() ) ) |
| aRg.aEnd++; |
| nReplaceDummy = aRg.aEnd.GetIndex() + nNdDiff - nEndNode; |
| if( nReplaceDummy ) |
| { // The selection has been expanded, because |
| aRg.aEnd++; |
| if( pEndTxtNd ) |
| { |
| // The end text node has to leave the (expanded) selection |
| // The dummy is needed because _MoveNodes deletes empty sections |
| ++nReplaceDummy; |
| SwNodeRange aMvRg( *pEndTxtNd, 0, *pEndTxtNd, 1 ); |
| SwPosition aSplitPos( *pEndTxtNd ); |
| ::sw::UndoGuard const ug(pDoc->GetIDocumentUndoRedo()); |
| pDoc->SplitNode( aSplitPos, false ); |
| rDocNds._MoveNodes( aMvRg, rDocNds, aRg.aEnd, sal_True ); |
| aRg.aEnd--; |
| } |
| else |
| nReplaceDummy = 0; |
| } |
| } |
| if( bBackSp || bFullPara ) |
| { |
| //See above, the selection has to expanded if there are "nearly empty" sections |
| // and a replacement dummy has to be set if needed. |
| while( 1 < aRg.aStart.GetIndex() && |
| ( (pTmpNd = rDocNds[ aRg.aStart.GetIndex()-1 ])->IsSectionNode() && |
| pTmpNd->EndOfSectionIndex() < aRg.aEnd.GetIndex() ) ) |
| aRg.aStart--; |
| if( pSttTxtNd ) |
| { |
| nReplaceDummy = nSttNode - nNdDiff - aRg.aStart.GetIndex(); |
| if( nReplaceDummy ) |
| { |
| SwNodeRange aMvRg( *pSttTxtNd, 0, *pSttTxtNd, 1 ); |
| SwPosition aSplitPos( *pSttTxtNd ); |
| ::sw::UndoGuard const ug(pDoc->GetIDocumentUndoRedo()); |
| pDoc->SplitNode( aSplitPos, false ); |
| rDocNds._MoveNodes( aMvRg, rDocNds, aRg.aStart, sal_True ); |
| aRg.aStart--; |
| } |
| } |
| } |
| |
| if( bFromTableCopy ) |
| { |
| if( !pEndTxtNd ) |
| { |
| if( pSttTxtNd ) |
| aRg.aStart++; |
| else if( !bFullPara && !aRg.aEnd.GetNode().IsCntntNode() ) |
| aRg.aEnd--; |
| } |
| } |
| else if( pSttTxtNd && ( pEndTxtNd || pSttTxtNd->GetTxt().Len() ) ) |
| aRg.aStart++; |
| |
| // Step 3: Moving into UndoArray... |
| nNode = rNds.GetEndOfContent().GetIndex(); |
| rDocNds._MoveNodes( aRg, rNds, SwNodeIndex( rNds.GetEndOfContent() )); |
| pMvStt = new SwNodeIndex( rNds, nNode ); |
| nNode = rNds.GetEndOfContent().GetIndex() - nNode; // Differenz merken ! |
| if( pSttTxtNd && pEndTxtNd ) |
| { |
| //Step 4: Moving around sections |
| nSectDiff = aRg.aEnd.GetIndex() - aRg.aStart.GetIndex(); |
| // nSect is the number of sections which starts(ends) between start and end node of the |
| // selection. The "loser" paragraph has to be moved into the section(s) of the |
| // "winner" paragraph |
| if( nSectDiff ) |
| { |
| if( bJoinNext ) |
| { |
| SwNodeRange aMvRg( *pEndTxtNd, 0, *pEndTxtNd, 1 ); |
| rDocNds._MoveNodes( aMvRg, rDocNds, aRg.aStart, sal_True ); |
| } |
| else |
| { |
| SwNodeRange aMvRg( *pSttTxtNd, 0, *pSttTxtNd, 1 ); |
| rDocNds._MoveNodes( aMvRg, rDocNds, aRg.aEnd, sal_True ); |
| } |
| } |
| } |
| if( nSectDiff || nReplaceDummy ) |
| lcl_MakeAutoFrms( *pDoc->GetSpzFrmFmts(), |
| bJoinNext ? pEndTxtNd->GetIndex() : pSttTxtNd->GetIndex() ); |
| } |
| else |
| nNode = 0; // kein Node verschoben -> keine Differenz zum Ende |
| |
| // wurden davor noch Nodes geloescht ?? (FootNotes haben ContentNodes!) |
| if( !pSttTxtNd && !pEndTxtNd ) |
| { |
| nNdDiff = nSttNode - rPam.GetPoint()->nNode.GetIndex() - (bFullPara ? 0 : 1); |
| rPam.Move( fnMoveForward, fnGoNode ); |
| } |
| else |
| { |
| nNdDiff = nSttNode; |
| if( nSectDiff && bBackSp ) |
| nNdDiff += nSectDiff; |
| nNdDiff -= rPam.GetPoint()->nNode.GetIndex(); |
| } |
| |
| if( !rPam.GetNode()->IsCntntNode() ) |
| rPam.GetPoint()->nContent.Assign( 0, 0 ); |
| |
| // wird die History ueberhaupt benoetigt ?? |
| if( pHistory && !pHistory->Count() ) |
| DELETEZ( pHistory ); |
| } |
| |
| sal_Bool SwUndoDelete::SaveCntnt( const SwPosition* pStt, const SwPosition* pEnd, |
| SwTxtNode* pSttTxtNd, SwTxtNode* pEndTxtNd ) |
| { |
| sal_uLong nNdIdx = pStt->nNode.GetIndex(); |
| // 1 - kopiere den Anfang in den Start-String |
| if( pSttTxtNd ) |
| { |
| sal_Bool bOneNode = nSttNode == nEndNode; |
| xub_StrLen nLen = bOneNode ? nEndCntnt - nSttCntnt |
| : pSttTxtNd->GetTxt().Len() - nSttCntnt; |
| SwRegHistory aRHst( *pSttTxtNd, pHistory ); |
| // always save all text atttibutes because of possibly overlapping |
| // areas of on/off |
| pHistory->CopyAttr( pSttTxtNd->GetpSwpHints(), nNdIdx, |
| 0, pSttTxtNd->GetTxt().Len(), true ); |
| if( !bOneNode && pSttTxtNd->HasSwAttrSet() ) |
| pHistory->CopyFmtAttr( *pSttTxtNd->GetpSwAttrSet(), nNdIdx ); |
| |
| // die Laenge kann sich veraendert haben (!!Felder!!) |
| nLen = ( bOneNode ? pEnd->nContent.GetIndex() : pSttTxtNd->GetTxt().Len() ) |
| - pStt->nContent.GetIndex(); |
| |
| |
| // loesche jetzt noch den Text (alle Attribut-Aenderungen kommen in |
| // die Undo-History |
| pSttStr = (String*)new String( pSttTxtNd->GetTxt().Copy( nSttCntnt, nLen )); |
| pSttTxtNd->EraseText( pStt->nContent, nLen ); |
| if( pSttTxtNd->GetpSwpHints() ) |
| pSttTxtNd->GetpSwpHints()->DeRegister(); |
| |
| // METADATA: store |
| bool emptied( pSttStr->Len() && !pSttTxtNd->Len() ); |
| if (!bOneNode || emptied) // merging may overwrite xmlids... |
| { |
| m_pMetadataUndoStart = (emptied) |
| ? pSttTxtNd->CreateUndoForDelete() |
| : pSttTxtNd->CreateUndo(); |
| } |
| |
| if( bOneNode ) |
| return sal_False; // keine Nodes mehr verschieben |
| } |
| |
| |
| // 2 - kopiere das Ende in den End-String |
| if( pEndTxtNd ) |
| { |
| SwIndex aEndIdx( pEndTxtNd ); |
| nNdIdx = pEnd->nNode.GetIndex(); |
| SwRegHistory aRHst( *pEndTxtNd, pHistory ); |
| |
| // always save all text atttibutes because of possibly overlapping |
| // areas of on/off |
| pHistory->CopyAttr( pEndTxtNd->GetpSwpHints(), nNdIdx, 0, |
| pEndTxtNd->GetTxt().Len(), true ); |
| |
| if( pEndTxtNd->HasSwAttrSet() ) |
| pHistory->CopyFmtAttr( *pEndTxtNd->GetpSwAttrSet(), nNdIdx ); |
| |
| |
| // loesche jetzt noch den Text (alle Attribut-Aenderungen kommen in |
| // die Undo-History |
| pEndStr = (String*)new String( pEndTxtNd->GetTxt().Copy( 0, |
| pEnd->nContent.GetIndex() )); |
| pEndTxtNd->EraseText( aEndIdx, pEnd->nContent.GetIndex() ); |
| if( pEndTxtNd->GetpSwpHints() ) |
| pEndTxtNd->GetpSwpHints()->DeRegister(); |
| |
| // METADATA: store |
| bool emptied( pEndStr->Len() && !pEndTxtNd->Len() ); |
| |
| m_pMetadataUndoEnd = (emptied) |
| ? pEndTxtNd->CreateUndoForDelete() |
| : pEndTxtNd->CreateUndo(); |
| } |
| |
| // sind es nur zwei Nodes, dann ist schon alles erledigt. |
| if( ( pSttTxtNd || pEndTxtNd ) && nSttNode + 1 == nEndNode ) |
| return sal_False; // keine Nodes mehr verschieben |
| |
| return sal_True; // verschiebe die dazwischen liegenden Nodes |
| } |
| |
| |
| sal_Bool SwUndoDelete::CanGrouping( SwDoc* pDoc, const SwPaM& rDelPam ) |
| { |
| // ist das Undo groesser als 1 Node ? (sprich: Start und EndString) |
| if( pSttStr ? !pSttStr->Len() || pEndStr : sal_True ) |
| return sal_False; |
| |
| // es kann nur das Loeschen von einzelnen char's zusammengefasst werden |
| if( nSttNode != nEndNode || ( !bGroup && nSttCntnt+1 != nEndCntnt )) |
| return sal_False; |
| |
| const SwPosition *pStt = rDelPam.Start(), |
| *pEnd = rDelPam.GetPoint() == pStt |
| ? rDelPam.GetMark() |
| : rDelPam.GetPoint(); |
| |
| if( pStt->nNode != pEnd->nNode || |
| pStt->nContent.GetIndex()+1 != pEnd->nContent.GetIndex() || |
| pEnd->nNode != nSttNode ) |
| return sal_False; |
| |
| // untercheide zwischen BackSpace und Delete. Es muss dann das |
| // Undo-Array unterschiedlich aufgebaut werden !! |
| if( pEnd->nContent == nSttCntnt ) |
| { |
| if( bGroup && !bBackSp ) return sal_False; |
| bBackSp = sal_True; |
| } |
| else if( pStt->nContent == nSttCntnt ) |
| { |
| if( bGroup && bBackSp ) return sal_False; |
| bBackSp = sal_False; |
| } |
| else |
| return sal_False; |
| |
| // sind die beiden Nodes (Nodes-/Undo-Array) ueberhaupt TextNodes? |
| SwTxtNode * pDelTxtNd = pStt->nNode.GetNode().GetTxtNode(); |
| if( !pDelTxtNd ) return sal_False; |
| |
| xub_StrLen nUChrPos = bBackSp ? 0 : pSttStr->Len()-1; |
| sal_Unicode cDelChar = pDelTxtNd->GetTxt().GetChar( pStt->nContent.GetIndex() ); |
| CharClass& rCC = GetAppCharClass(); |
| if( ( CH_TXTATR_BREAKWORD == cDelChar || CH_TXTATR_INWORD == cDelChar ) || |
| rCC.isLetterNumeric( String( cDelChar ), 0 ) != |
| rCC.isLetterNumeric( *pSttStr, nUChrPos ) ) |
| return sal_False; |
| |
| { |
| SwRedlineSaveDatas* pTmpSav = new SwRedlineSaveDatas; |
| if( !FillSaveData( rDelPam, *pTmpSav, sal_False )) |
| delete pTmpSav, pTmpSav = 0; |
| |
| sal_Bool bOk = ( !pRedlSaveData && !pTmpSav ) || |
| ( pRedlSaveData && pTmpSav && |
| SwUndo::CanRedlineGroup( *pRedlSaveData, *pTmpSav, bBackSp )); |
| delete pTmpSav; |
| if( !bOk ) |
| return sal_False; |
| |
| pDoc->DeleteRedline( rDelPam, false, USHRT_MAX ); |
| } |
| |
| // Ok, die beiden 'Deletes' koennen zusammen gefasst werden, also |
| // 'verschiebe' das enstprechende Zeichen |
| if( bBackSp ) |
| nSttCntnt--; // BackSpace: Zeichen in Array einfuegen !! |
| else |
| { |
| nEndCntnt++; // Delete: Zeichen am Ende anhaengen |
| nUChrPos++; |
| } |
| pSttStr->Insert( cDelChar, nUChrPos ); |
| pDelTxtNd->EraseText( pStt->nContent, 1 ); |
| |
| bGroup = sal_True; |
| return sal_True; |
| } |
| |
| |
| |
| SwUndoDelete::~SwUndoDelete() |
| { |
| delete pSttStr; |
| delete pEndStr; |
| if( pMvStt ) // loesche noch den Bereich aus dem UndoNodes Array |
| { |
| // Insert speichert den Inhalt in der IconSection |
| pMvStt->GetNode().GetNodes().Delete( *pMvStt, nNode ); |
| delete pMvStt; |
| } |
| delete pRedlData; |
| delete pRedlSaveData; |
| } |
| |
| static SwRewriter lcl_RewriterFromHistory(SwHistory & rHistory) |
| { |
| SwRewriter aRewriter; |
| |
| bool bDone = false; |
| |
| for ( sal_uInt16 n = 0; n < rHistory.Count(); n++) |
| { |
| String aDescr = rHistory[n]->GetDescription(); |
| |
| if (aDescr.Len() > 0) |
| { |
| aRewriter.AddRule(UNDO_ARG2, aDescr); |
| |
| bDone = true; |
| break; |
| } |
| } |
| |
| if (! bDone) |
| { |
| aRewriter.AddRule(UNDO_ARG2, SW_RES(STR_FIELD)); |
| } |
| |
| return aRewriter; |
| } |
| |
| SwRewriter SwUndoDelete::GetRewriter() const |
| { |
| SwRewriter aResult; |
| String * pStr = NULL; |
| |
| if (nNode != 0) |
| { |
| if (sTableName.Len() > 0) |
| { |
| |
| SwRewriter aRewriter; |
| aRewriter.AddRule(UNDO_ARG1, SW_RES(STR_START_QUOTE)); |
| aRewriter.AddRule(UNDO_ARG2, sTableName); |
| aRewriter.AddRule(UNDO_ARG3, SW_RES(STR_END_QUOTE)); |
| |
| String sTmp = aRewriter.Apply(SW_RES(STR_TABLE_NAME)); |
| aResult.AddRule(UNDO_ARG1, sTmp); |
| } |
| else |
| aResult.AddRule(UNDO_ARG1, String(SW_RES(STR_PARAGRAPHS))); |
| } |
| else |
| { |
| String aStr; |
| |
| if (pSttStr != NULL && pEndStr != NULL && pSttStr->Len() == 0 && |
| pEndStr->Len() == 0) |
| { |
| aStr = SW_RES(STR_PARAGRAPH_UNDO); |
| } |
| else |
| { |
| if (pSttStr != NULL) |
| pStr = pSttStr; |
| else if (pEndStr != NULL) |
| pStr = pEndStr; |
| |
| if (pStr != NULL) |
| { |
| aStr = DenoteSpecialCharacters(*pStr); |
| } |
| else |
| { |
| aStr = UNDO_ARG2; |
| } |
| } |
| |
| aStr = ShortenString(aStr, nUndoStringLength, String(SW_RES(STR_LDOTS))); |
| if (pHistory) |
| { |
| SwRewriter aRewriter = lcl_RewriterFromHistory(*pHistory); |
| aStr = aRewriter.Apply(aStr); |
| } |
| |
| aResult.AddRule(UNDO_ARG1, aStr); |
| } |
| |
| return aResult; |
| } |
| |
| // Every object, anchored "AtCntnt" will be reanchored at rPos |
| void lcl_ReAnchorAtCntntFlyFrames( const SwSpzFrmFmts& rSpzArr, SwPosition &rPos, sal_uLong nOldIdx ) |
| { |
| if( rSpzArr.Count() ) |
| { |
| SwFlyFrmFmt* pFmt; |
| const SwFmtAnchor* pAnchor; |
| const SwPosition* pAPos; |
| for( sal_uInt16 n = 0; n < rSpzArr.Count(); ++n ) |
| { |
| pFmt = (SwFlyFrmFmt*)rSpzArr[n]; |
| pAnchor = &pFmt->GetAnchor(); |
| if (pAnchor->GetAnchorId() == FLY_AT_PARA) |
| { |
| pAPos = pAnchor->GetCntntAnchor(); |
| if( pAPos && nOldIdx == pAPos->nNode.GetIndex() ) |
| { |
| SwFmtAnchor aAnch( *pAnchor ); |
| aAnch.SetAnchor( &rPos ); |
| pFmt->SetFmtAttr( aAnch ); |
| } |
| } |
| } |
| } |
| } |
| |
| void SwUndoDelete::UndoImpl(::sw::UndoRedoContext & rContext) |
| { |
| SwDoc *const pDoc = & rContext.GetDoc(); |
| |
| sal_uLong nCalcStt = nSttNode - nNdDiff; |
| |
| if( nSectDiff && bBackSp ) |
| nCalcStt += nSectDiff; |
| |
| SwNodeIndex aIdx( pDoc->GetNodes(), nCalcStt ); |
| SwNode* pInsNd = &aIdx.GetNode(); |
| |
| { // Block, damit der SwPosition beim loeschen vom Node |
| // abgemeldet ist |
| SwPosition aPos( aIdx ); |
| if( !bDelFullPara ) |
| { |
| if( pInsNd->IsTableNode() ) |
| { |
| pInsNd = pDoc->GetNodes().MakeTxtNode( aIdx, |
| (SwTxtFmtColl*)pDoc->GetDfltTxtFmtColl() ); |
| aIdx--; |
| aPos.nNode = aIdx; |
| aPos.nContent.Assign( pInsNd->GetCntntNode(), nSttCntnt ); |
| } |
| else |
| { |
| if( pInsNd->IsCntntNode() ) |
| aPos.nContent.Assign( (SwCntntNode*)pInsNd, nSttCntnt ); |
| if( !bTblDelLastNd ) |
| pInsNd = 0; // Node nicht loeschen !! |
| } |
| } |
| else |
| pInsNd = 0; // Node nicht loeschen !! |
| |
| sal_Bool bNodeMove = 0 != nNode; |
| |
| if( pEndStr ) |
| { |
| // alle Attribute verwerfen, wurden alle gespeichert! |
| SwTxtNode* pTxtNd = aPos.nNode.GetNode().GetTxtNode(); |
| |
| if( pTxtNd && pTxtNd->HasSwAttrSet() ) |
| pTxtNd->ResetAllAttr(); |
| |
| if( pTxtNd && pTxtNd->GetpSwpHints() ) |
| pTxtNd->ClearSwpHintsArr( true ); |
| |
| if( pSttStr && !bFromTableCopy ) |
| { |
| sal_uLong nOldIdx = aPos.nNode.GetIndex(); |
| pDoc->SplitNode( aPos, false ); |
| // After the split all objects are anchored at the first paragraph, |
| // but the pHistory of the fly frame formats relies on anchoring at |
| // the start of the selection => selection backwards needs a correction. |
| if( bBackSp ) |
| lcl_ReAnchorAtCntntFlyFrames( *pDoc->GetSpzFrmFmts(), aPos, nOldIdx ); |
| pTxtNd = aPos.nNode.GetNode().GetTxtNode(); |
| } |
| if( pTxtNd ) |
| { |
| pTxtNd->InsertText( *pEndStr, aPos.nContent, |
| IDocumentContentOperations::INS_NOHINTEXPAND ); |
| // METADATA: restore |
| pTxtNd->RestoreMetadata(m_pMetadataUndoEnd); |
| } |
| } |
| else if( pSttStr && bNodeMove ) |
| { |
| SwTxtNode * pNd = aPos.nNode.GetNode().GetTxtNode(); |
| if( pNd ) |
| { |
| if( nSttCntnt < pNd->GetTxt().Len() ) |
| { |
| sal_uLong nOldIdx = aPos.nNode.GetIndex(); |
| pDoc->SplitNode( aPos, false ); |
| if( bBackSp ) |
| lcl_ReAnchorAtCntntFlyFrames( *pDoc->GetSpzFrmFmts(), aPos, nOldIdx ); |
| } |
| else |
| aPos.nNode++; |
| } |
| } |
| SwNode* pMovedNode = NULL; |
| if( nSectDiff ) |
| { |
| sal_uLong nMoveIndex = aPos.nNode.GetIndex(); |
| int nDiff = 0; |
| if( bJoinNext ) |
| { |
| nMoveIndex += nSectDiff + 1; |
| pMovedNode = &aPos.nNode.GetNode(); |
| } |
| else |
| { |
| nMoveIndex -= nSectDiff + 1; |
| ++nDiff; |
| } |
| SwNodeIndex aMvIdx( pDoc->GetNodes(), nMoveIndex ); |
| SwNodeRange aRg( aPos.nNode, 0 - nDiff, aPos.nNode, 1 - nDiff ); |
| aPos.nNode--; |
| if( !bJoinNext ) |
| pMovedNode = &aPos.nNode.GetNode(); |
| pDoc->GetNodes()._MoveNodes( aRg, pDoc->GetNodes(), aMvIdx, sal_True ); |
| aPos.nNode++; |
| } |
| |
| if( bNodeMove ) |
| { |
| SwNodeRange aRange( *pMvStt, 0, *pMvStt, nNode ); |
| SwNodeIndex aCopyIndex( aPos.nNode, -1 ); |
| pDoc->GetUndoManager().GetUndoNodes()._Copy( aRange, aPos.nNode ); |
| |
| if( nReplaceDummy ) |
| { |
| sal_uLong nMoveIndex; |
| if( bJoinNext ) |
| { |
| nMoveIndex = nEndNode - nNdDiff; |
| aPos.nNode = nMoveIndex + nReplaceDummy; |
| } |
| else |
| { |
| aPos = SwPosition( aCopyIndex ); |
| nMoveIndex = aPos.nNode.GetIndex() + nReplaceDummy + 1; |
| } |
| SwNodeIndex aMvIdx( pDoc->GetNodes(), nMoveIndex ); |
| SwNodeRange aRg( aPos.nNode, 0, aPos.nNode, 1 ); |
| pMovedNode = &aPos.nNode.GetNode(); |
| pDoc->GetNodes()._MoveNodes( aRg, pDoc->GetNodes(), aMvIdx, sal_True ); |
| pDoc->GetNodes().Delete( aMvIdx, 1 ); |
| } |
| } |
| |
| if( pMovedNode ) |
| lcl_MakeAutoFrms( *pDoc->GetSpzFrmFmts(), pMovedNode->GetIndex() ); |
| |
| if( pSttStr ) |
| { |
| aPos.nNode = nSttNode - nNdDiff + ( bJoinNext ? 0 : nReplaceDummy ); |
| SwTxtNode * pTxtNd = aPos.nNode.GetNode().GetTxtNode(); |
| // wenn mehr als ein Node geloescht wurde, dann wurden auch |
| // alle "Node"-Attribute gespeichert |
| |
| if (pTxtNd != NULL) |
| { |
| if( pTxtNd->HasSwAttrSet() && bNodeMove && !pEndStr ) |
| pTxtNd->ResetAllAttr(); |
| |
| if( pTxtNd->GetpSwpHints() ) |
| pTxtNd->ClearSwpHintsArr( true ); |
| |
| // SectionNode-Modus und von oben nach unten selektiert: |
| // -> im StartNode steht noch der Rest vom Join => loeschen |
| aPos.nContent.Assign( pTxtNd, nSttCntnt ); |
| pTxtNd->InsertText( *pSttStr, aPos.nContent, |
| IDocumentContentOperations::INS_NOHINTEXPAND ); |
| // METADATA: restore |
| pTxtNd->RestoreMetadata(m_pMetadataUndoStart); |
| } |
| } |
| |
| if( pHistory ) |
| { |
| pHistory->TmpRollback( pDoc, nSetPos, false ); |
| if( nSetPos ) // es gab Fussnoten/FlyFrames |
| { |
| // gibts ausser diesen noch andere ? |
| if( nSetPos < pHistory->Count() ) |
| { |
| // dann sicher die Attribute anderen Attribute |
| SwHistory aHstr; |
| aHstr.Move( 0, pHistory, nSetPos ); |
| pHistory->Rollback( pDoc ); |
| pHistory->Move( 0, &aHstr ); |
| } |
| else |
| { |
| pHistory->Rollback( pDoc ); |
| DELETEZ( pHistory ); |
| } |
| } |
| } |
| |
| if( bResetPgDesc || bResetPgBrk ) |
| { |
| sal_uInt16 nStt = static_cast<sal_uInt16>( bResetPgDesc ? RES_PAGEDESC : RES_BREAK ); |
| sal_uInt16 nEnd = static_cast<sal_uInt16>( bResetPgBrk ? RES_BREAK : RES_PAGEDESC ); |
| |
| SwNode* pNode = pDoc->GetNodes()[ nEndNode + 1 ]; |
| if( pNode->IsCntntNode() ) |
| ((SwCntntNode*)pNode)->ResetAttr( nStt, nEnd ); |
| else if( pNode->IsTableNode() ) |
| ((SwTableNode*)pNode)->GetTable().GetFrmFmt()->ResetFmtAttr( nStt, nEnd ); |
| } |
| } |
| // den temp. eingefuegten Node noch loeschen !! |
| if( pInsNd ) |
| pDoc->GetNodes().Delete( aIdx, 1 ); |
| if( pRedlSaveData ) |
| SetSaveData( *pDoc, *pRedlSaveData ); |
| |
| AddUndoRedoPaM(rContext, true); |
| } |
| |
| void SwUndoDelete::RedoImpl(::sw::UndoRedoContext & rContext) |
| { |
| SwPaM & rPam = AddUndoRedoPaM(rContext); |
| SwDoc& rDoc = *rPam.GetDoc(); |
| |
| if( pRedlSaveData ) |
| { |
| bool bSuccess = FillSaveData(rPam, *pRedlSaveData, sal_True); |
| OSL_ENSURE(bSuccess, |
| "SwUndoDelete::Redo: used to have redline data, but now none?"); |
| if (!bSuccess) |
| { |
| delete pRedlSaveData, pRedlSaveData = 0; |
| } |
| } |
| |
| if( !bDelFullPara ) |
| { |
| SwUndRng aTmpRng( rPam ); |
| RemoveIdxFromRange( rPam, sal_False ); |
| aTmpRng.SetPaM( rPam ); |
| |
| if( !bJoinNext ) // Dann Selektion von unten nach oben |
| rPam.Exchange(); // wieder herstellen! |
| } |
| |
| if( pHistory ) // wurden Attribute gesichert ? |
| { |
| pHistory->SetTmpEnd( pHistory->Count() ); |
| SwHistory aHstr; |
| aHstr.Move( 0, pHistory ); |
| |
| if( bDelFullPara ) |
| { |
| ASSERT( rPam.HasMark(), "PaM ohne Mark" ); |
| DelCntntIndex( *rPam.GetMark(), *rPam.GetPoint(), |
| DelCntntType(nsDelCntntType::DELCNT_ALL | nsDelCntntType::DELCNT_CHKNOCNTNT) ); |
| |
| _DelBookmarks(rPam.GetMark()->nNode, rPam.GetPoint()->nNode); |
| } |
| else |
| DelCntntIndex( *rPam.GetMark(), *rPam.GetPoint() ); |
| nSetPos = pHistory ? pHistory->Count() : 0; |
| |
| pHistory->Move( nSetPos, &aHstr ); |
| } |
| else |
| { |
| if( bDelFullPara ) |
| { |
| ASSERT( rPam.HasMark(), "PaM ohne Mark" ); |
| DelCntntIndex( *rPam.GetMark(), *rPam.GetPoint(), |
| DelCntntType(nsDelCntntType::DELCNT_ALL | nsDelCntntType::DELCNT_CHKNOCNTNT) ); |
| |
| _DelBookmarks( rPam.GetMark()->nNode, rPam.GetPoint()->nNode ); |
| } |
| else |
| DelCntntIndex( *rPam.GetMark(), *rPam.GetPoint() ); |
| nSetPos = pHistory ? pHistory->Count() : 0; |
| } |
| |
| if( !pSttStr && !pEndStr ) |
| { |
| SwNodeIndex aSttIdx = ( bDelFullPara || bJoinNext ) |
| ? rPam.GetMark()->nNode |
| : rPam.GetPoint()->nNode; |
| SwTableNode* pTblNd = aSttIdx.GetNode().GetTableNode(); |
| if( pTblNd ) |
| { |
| if( bTblDelLastNd ) |
| { |
| // dann am Ende wieder einen Node einfuegen |
| const SwNodeIndex aTmpIdx( *pTblNd->EndOfSectionNode(), 1 ); |
| rDoc.GetNodes().MakeTxtNode( aTmpIdx, |
| rDoc.GetTxtCollFromPool( RES_POOLCOLL_STANDARD ) ); |
| } |
| |
| SwCntntNode* pNextNd = rDoc.GetNodes()[ |
| pTblNd->EndOfSectionIndex()+1 ]->GetCntntNode(); |
| if( pNextNd ) |
| { |
| SwFrmFmt* pTableFmt = pTblNd->GetTable().GetFrmFmt(); |
| |
| const SfxPoolItem *pItem; |
| if( SFX_ITEM_SET == pTableFmt->GetItemState( RES_PAGEDESC, |
| sal_False, &pItem ) ) |
| pNextNd->SetAttr( *pItem ); |
| |
| if( SFX_ITEM_SET == pTableFmt->GetItemState( RES_BREAK, |
| sal_False, &pItem ) ) |
| pNextNd->SetAttr( *pItem ); |
| } |
| pTblNd->DelFrms(); |
| } |
| |
| rPam.SetMark(); |
| rPam.DeleteMark(); |
| |
| rDoc.GetNodes().Delete( aSttIdx, nEndNode - nSttNode ); |
| |
| // setze den Cursor immer in einen ContentNode !! |
| if( !rPam.Move( fnMoveBackward, fnGoCntnt ) && |
| !rPam.Move( fnMoveForward, fnGoCntnt ) ) |
| rPam.GetPoint()->nContent.Assign( rPam.GetCntntNode(), 0 ); |
| } |
| else if( bDelFullPara ) |
| { |
| // der Pam wurde am Point( == Ende) um eins erhoeht, um einen |
| // Bereich fuers Undo zu haben. Der muss jetzt aber wieder entfernt |
| // werden!!! |
| rPam.End()->nNode--; |
| if( rPam.GetPoint()->nNode == rPam.GetMark()->nNode ) |
| *rPam.GetMark() = *rPam.GetPoint(); |
| rDoc.DelFullPara( rPam ); |
| } |
| else |
| rDoc.DeleteAndJoin( rPam ); |
| } |
| |
| void SwUndoDelete::RepeatImpl(::sw::RepeatContext & rContext) |
| { |
| // this action does not seem idempotent, |
| // so make sure it is only executed once on repeat |
| if (rContext.m_bDeleteRepeated) |
| return; |
| |
| SwPaM & rPam = rContext.GetRepeatPaM(); |
| SwDoc& rDoc = *rPam.GetDoc(); |
| ::sw::GroupUndoGuard const undoGuard(rDoc.GetIDocumentUndoRedo()); |
| if( !rPam.HasMark() ) |
| { |
| rPam.SetMark(); |
| rPam.Move( fnMoveForward, fnGoCntnt ); |
| } |
| if( bDelFullPara ) |
| rDoc.DelFullPara( rPam ); |
| else |
| rDoc.DeleteAndJoin( rPam ); |
| rContext.m_bDeleteRepeated = true; |
| } |
| |
| |
| void SwUndoDelete::SetTableName(const String & rName) |
| { |
| sTableName = rName; |
| } |