merge the formfield patch from ooo-build
[ooovba.git] / sw / source / core / undo / undobj.cxx
blob38a83445c5c46aa2020d0d4a308e995a8f2a16fa
1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: undobj.cxx,v $
10 * $Revision: 1.29.52.1 $
12 * This file is part of OpenOffice.org.
14 * OpenOffice.org is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU Lesser General Public License version 3
16 * only, as published by the Free Software Foundation.
18 * OpenOffice.org is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU Lesser General Public License version 3 for more details
22 * (a copy is included in the LICENSE file that accompanied this code).
24 * You should have received a copy of the GNU Lesser General Public License
25 * version 3 along with OpenOffice.org. If not, see
26 * <http://www.openoffice.org/license.html>
27 * for a copy of the LGPLv3 License.
29 ************************************************************************/
31 // MARKER(update_precomp.py): autogen include statement, do not remove
32 #include "precompiled_sw.hxx"
35 #include <txtftn.hxx>
36 #include <fmtanchr.hxx>
37 #include <ftnidx.hxx>
38 #include <frmfmt.hxx>
39 #include <doc.hxx>
40 #include <docary.hxx>
41 #include <swundo.hxx> // fuer die UndoIds
42 #include <pam.hxx>
43 #include <ndtxt.hxx>
44 #include <undobj.hxx>
45 #include <rolbck.hxx>
46 #include <ndnotxt.hxx>
47 #include <IMark.hxx>
48 #include <mvsave.hxx>
49 #include <redline.hxx>
50 #include <crossrefbookmark.hxx>
51 #ifndef _UNDO_HRC
52 #include <undo.hrc>
53 #endif
54 #ifndef _COMCORE_HRC
55 #include <comcore.hrc>
56 #endif
57 #include <docsh.hxx>
59 class SwRedlineSaveData : public SwUndRng, public SwRedlineData,
60 private SwUndoSaveSection
62 public:
63 SwRedlineSaveData( SwComparePosition eCmpPos,
64 const SwPosition& rSttPos, const SwPosition& rEndPos,
65 SwRedline& rRedl, BOOL bCopyNext );
66 ~SwRedlineSaveData();
67 void RedlineToDoc( SwPaM& rPam );
68 SwNodeIndex* GetMvSttIdx() const
69 { return SwUndoSaveSection::GetMvSttIdx(); }
71 #ifndef PRODUCT
72 USHORT nRedlineCount;
73 #endif
76 SV_IMPL_PTRARR( SwUndos, SwUndo*)
77 SV_IMPL_PTRARR( SwRedlineSaveDatas, SwRedlineSaveDataPtr )
79 SwUndoIter::SwUndoIter( SwPaM* pPam, SwUndoId nId )
81 nUndoId = nId;
82 bWeiter = nId ? TRUE : FALSE;
83 bUpdateAttr = FALSE;
84 pAktPam = pPam;
85 nEndCnt = 0;
86 pSelFmt = 0;
87 pMarkList = 0;
89 inline SwDoc& SwUndoIter::GetDoc() const { return *pAktPam->GetDoc(); }
91 //------------------------------------------------------------
93 // Diese Klasse speichert den Pam als USHORT's und kann diese wieder zu
95 // einem PaM zusammensetzen
96 SwUndRng::SwUndRng()
97 : nSttNode( 0 ), nEndNode( 0 ), nSttCntnt( 0 ), nEndCntnt( 0 )
101 SwUndRng::SwUndRng( const SwPaM& rPam )
103 SetValues( rPam );
106 void SwUndRng::SetValues( const SwPaM& rPam )
108 const SwPosition *pStt = rPam.Start();
109 if( rPam.HasMark() )
111 const SwPosition *pEnd = rPam.GetPoint() == pStt
112 ? rPam.GetMark()
113 : rPam.GetPoint();
114 nEndNode = pEnd->nNode.GetIndex();
115 nEndCntnt = pEnd->nContent.GetIndex();
117 else
118 // keine Selektion !!
119 nEndNode = 0, nEndCntnt = STRING_MAXLEN;
121 nSttNode = pStt->nNode.GetIndex();
122 nSttCntnt = pStt->nContent.GetIndex();
125 void SwUndRng::SetPaM( SwPaM & rPam, BOOL bCorrToCntnt ) const
127 rPam.DeleteMark();
128 rPam.GetPoint()->nNode = nSttNode;
129 SwNode* pNd = rPam.GetNode();
130 if( pNd->IsCntntNode() )
131 rPam.GetPoint()->nContent.Assign( pNd->GetCntntNode(), nSttCntnt );
132 else if( bCorrToCntnt )
133 rPam.Move( fnMoveForward, fnGoCntnt );
134 else
135 rPam.GetPoint()->nContent.Assign( 0, 0 );
137 if( !nEndNode && STRING_MAXLEN == nEndCntnt ) // keine Selection
138 return ;
140 rPam.SetMark();
141 if( nSttNode == nEndNode && nSttCntnt == nEndCntnt )
142 return; // nichts mehr zu tun
144 rPam.GetPoint()->nNode = nEndNode;
145 if( (pNd = rPam.GetNode())->IsCntntNode() )
146 rPam.GetPoint()->nContent.Assign( pNd->GetCntntNode(), nEndCntnt );
147 else if( bCorrToCntnt )
148 rPam.Move( fnMoveBackward, fnGoCntnt );
149 else
150 rPam.GetPoint()->nContent.Assign( 0, 0 );
153 void SwUndRng::SetPaM( SwUndoIter& rIter, BOOL bCorrToCntnt ) const
155 if( rIter.pAktPam )
156 SetPaM( *rIter.pAktPam, bCorrToCntnt );
159 //------------------------------------------------------------
162 void SwUndo::RemoveIdxFromSection( SwDoc& rDoc, ULONG nSttIdx,
163 ULONG* pEndIdx )
165 SwNodeIndex aIdx( rDoc.GetNodes(), nSttIdx );
166 SwNodeIndex aEndIdx( rDoc.GetNodes(), pEndIdx ? *pEndIdx
167 : aIdx.GetNode().EndOfSectionIndex() );
168 SwPosition aPos( rDoc.GetNodes().GetEndOfPostIts() );
169 rDoc.CorrAbs( aIdx, aEndIdx, aPos, TRUE );
172 void SwUndo::RemoveIdxFromRange( SwPaM& rPam, BOOL bMoveNext )
174 const SwPosition* pEnd = rPam.End();
175 if( bMoveNext )
177 if( pEnd != rPam.GetPoint() )
178 rPam.Exchange();
180 SwNodeIndex aStt( rPam.GetMark()->nNode );
181 SwNodeIndex aEnd( rPam.GetPoint()->nNode );
183 if( !rPam.Move( fnMoveForward ) )
185 rPam.Exchange();
186 if( !rPam.Move( fnMoveBackward ) )
188 rPam.GetPoint()->nNode = rPam.GetDoc()->GetNodes().GetEndOfPostIts();
189 rPam.GetPoint()->nContent.Assign( 0, 0 );
193 rPam.GetDoc()->CorrAbs( aStt, aEnd, *rPam.GetPoint(), TRUE );
195 else
196 rPam.GetDoc()->CorrAbs( rPam, *pEnd, TRUE );
199 void SwUndo::RemoveIdxRel( ULONG nIdx, const SwPosition& rPos )
201 // nur die Crsr verschieben; die Bookmarks/TOXMarks/.. werden vom
202 // entsp. JoinNext/JoinPrev erledigt!
203 SwNodeIndex aIdx( rPos.nNode.GetNode().GetNodes(), nIdx );
204 ::PaMCorrRel( aIdx, rPos );
207 SwUndo::SwUndo( SwUndoId nI )
208 : nId(nI), nOrigRedlineMode(nsRedlineMode_t::REDLINE_NONE),
209 bCacheComment(true), pComment(NULL)
213 bool SwUndo::IsDelBox() const
215 return GetId() == UNDO_COL_DELETE || GetId() == UNDO_ROW_DELETE ||
216 GetId() == UNDO_TABLE_DELBOX;
219 SwUndo::~SwUndo()
221 delete pComment;
224 void SwUndo::Repeat( SwUndoIter& rIter )
226 rIter.pLastUndoObj = this;
229 String SwUndo::GetComment() const
231 String aResult;
233 if (bCacheComment)
235 if (! pComment)
237 pComment = new String(SW_RES(UNDO_BASE + nId));
239 SwRewriter aRewriter = GetRewriter();
241 *pComment = aRewriter.Apply(*pComment);
244 aResult = *pComment;
246 else
248 aResult = String(SW_RES(UNDO_BASE + nId));
250 SwRewriter aRewriter = GetRewriter();
252 aResult = aRewriter.Apply(aResult);
255 return aResult;
258 SwUndoId SwUndo::GetEffectiveId() const
260 return GetId();
263 SwRewriter SwUndo::GetRewriter() const
265 SwRewriter aResult;
267 return aResult;
270 //------------------------------------------------------------
272 SwUndoSaveCntnt::SwUndoSaveCntnt()
273 : pHistory( 0 )
276 SwUndoSaveCntnt::~SwUndoSaveCntnt()
278 delete pHistory;
281 // wird fuer das Loeschen von Inhalt benoetigt. Fuer das ReDo werden
282 // Inhalte in das UndoNodesArray verschoben. Diese Methoden fuegen
283 // am Ende eines TextNodes fuer die Attribute einen Trenner ein.
284 // Dadurch werden die Attribute nicht expandiert.
285 // MoveTo.. verschiebt aus dem NodesArray in das UndoNodesArray
286 // MoveFrom.. verschiebt aus dem UndoNodesArray in das NodesArray
288 // 2.8.93: ist pEndNdIdx angebenen, wird vom Undo/Redo -Ins/DelFly
289 // aufgerufen. Dann soll die gesamte Section verschoben werden.
291 void SwUndoSaveCntnt::MoveToUndoNds( SwPaM& rPaM, SwNodeIndex* pNodeIdx,
292 SwIndex* pCntIdx, ULONG* pEndNdIdx, xub_StrLen* pEndCntIdx )
294 SwDoc& rDoc = *rPaM.GetDoc();
295 BOOL bUndo = rDoc.DoesUndo();
296 rDoc.DoUndo( FALSE );
298 SwNoTxtNode* pCpyNd = rPaM.GetNode()->GetNoTxtNode();
300 // jetzt kommt das eigentliche Loeschen(Verschieben)
301 SwNodes& rNds = (SwNodes&)*rDoc.GetUndoNds();
302 SwPosition aPos( pEndNdIdx ? rNds.GetEndOfPostIts()
303 : rNds.GetEndOfExtras() );
304 aPos.nNode--;
306 const SwPosition* pStt = rPaM.Start(), *pEnd = rPaM.End();
308 if( pCpyNd || pEndNdIdx || !aPos.nNode.GetNode().GetCntntNode() ||
309 (!pStt->nContent.GetIndex() && (pStt->nNode != pEnd->nNode ||
310 (!pStt->nNode.GetNode().GetCntntNode() ||
311 pStt->nNode.GetNode().GetCntntNode()->Len() ==
312 pEnd->nContent.GetIndex() ) ) ) )
314 aPos.nNode++;
315 aPos.nContent = 0;
317 else
318 aPos.nNode.GetNode().GetCntntNode()->MakeEndIndex( &aPos.nContent );
320 // als USHORT merken; die Indizies verschieben sich !!
321 ULONG nTmpMvNode = aPos.nNode.GetIndex();
322 xub_StrLen nTmpMvCntnt = aPos.nContent.GetIndex();
324 if( pCpyNd || pEndNdIdx )
326 SwNodeRange aRg( pStt->nNode, 0, pEnd->nNode, 1 );
327 rDoc.GetNodes()._MoveNodes( aRg, rNds, aPos.nNode, FALSE );
328 aPos.nContent = 0;
329 aPos.nNode--;
331 else
333 rDoc.GetNodes().MoveRange( rPaM, aPos, rNds );
335 SwTxtNode* pTxtNd = aPos.nNode.GetNode().GetTxtNode();
336 if( pTxtNd ) // fuege einen Trenner fuer die Attribute ein !
338 // weil aber beim Insert die Attribute angefasst/sprich
339 // aus dem Array geloescht und wieder eingefuegt werden, koennen
340 // dadurch Attribute verschwinden (z.B "Fett aus" von 10-20,
341 // "Fett an" von 12-15, dann wird durchs Insert/Delete das
342 // "Fett an" geloescht !! Ist hier aber nicht erwuenscht !!)
343 // DARUM: nicht die Hints anfassen, direct den String manipulieren
345 String& rStr = (String&)pTxtNd->GetTxt();
346 // Zur Sicherheit lieber nur wenn wirklich am Ende steht
347 if( rStr.Len() == aPos.nContent.GetIndex() )
349 rStr.Insert( ' ' );
350 ++aPos.nContent;
352 else
354 pTxtNd->InsertText( sal_Unicode(' '), aPos.nContent,
355 IDocumentContentOperations::INS_NOHINTEXPAND );
359 if( pEndNdIdx )
360 *pEndNdIdx = aPos.nNode.GetIndex();
361 if( pEndCntIdx )
362 *pEndCntIdx = aPos.nContent.GetIndex();
364 // alte Position
365 aPos.nNode = nTmpMvNode;
366 if( pNodeIdx )
367 *pNodeIdx = aPos.nNode;
369 if( pCntIdx )
371 SwCntntNode* pCNd = aPos.nNode.GetNode().GetCntntNode();
372 if( pCNd )
373 pCntIdx->Assign( pCNd, nTmpMvCntnt );
374 else
375 pCntIdx->Assign( 0, 0 );
378 rDoc.DoUndo( bUndo );
381 void SwUndoSaveCntnt::MoveFromUndoNds( SwDoc& rDoc, ULONG nNodeIdx,
382 xub_StrLen nCntIdx, SwPosition& rInsPos,
383 ULONG* pEndNdIdx, xub_StrLen* pEndCntIdx )
385 // jetzt kommt das wiederherstellen
386 SwNodes& rNds = (SwNodes&)*rDoc.GetUndoNds();
387 if( nNodeIdx == rNds.GetEndOfPostIts().GetIndex() )
388 return; // nichts gespeichert
390 BOOL bUndo = rDoc.DoesUndo();
391 rDoc.DoUndo( FALSE );
393 SwPaM aPaM( rInsPos );
394 if( pEndNdIdx ) // dann hole aus diesem den Bereich
395 aPaM.GetPoint()->nNode.Assign( rNds, *pEndNdIdx );
396 else
398 aPaM.GetPoint()->nNode = rNds.GetEndOfExtras();
399 GoInCntnt( aPaM, fnMoveBackward );
402 SwTxtNode* pTxtNd = aPaM.GetNode()->GetTxtNode();
403 if( !pEndNdIdx && pTxtNd ) // loesche den Trenner wieder
405 if( pEndCntIdx )
406 aPaM.GetPoint()->nContent.Assign( pTxtNd, *pEndCntIdx );
407 if( pTxtNd->GetTxt().Len() )
409 GoInCntnt( aPaM, fnMoveBackward );
410 pTxtNd->EraseText( aPaM.GetPoint()->nContent, 1 );
413 aPaM.SetMark();
414 aPaM.GetPoint()->nNode = nNodeIdx;
415 aPaM.GetPoint()->nContent.Assign( aPaM.GetCntntNode(), nCntIdx );
417 _SaveRedlEndPosForRestore aRedlRest( rInsPos.nNode, rInsPos.nContent.GetIndex() );
419 rNds.MoveRange( aPaM, rInsPos, rDoc.GetNodes() );
421 // noch den letzen Node loeschen.
422 if( !aPaM.GetPoint()->nContent.GetIndex() ||
423 ( aPaM.GetPoint()->nNode++ && // noch leere Nodes am Ende ??
424 &rNds.GetEndOfExtras() != &aPaM.GetPoint()->nNode.GetNode() ))
426 aPaM.GetPoint()->nContent.Assign( 0, 0 );
427 aPaM.SetMark();
428 rNds.Delete( aPaM.GetPoint()->nNode,
429 rNds.GetEndOfExtras().GetIndex() -
430 aPaM.GetPoint()->nNode.GetIndex() );
433 aRedlRest.Restore();
435 else if( pEndNdIdx || !pTxtNd )
437 SwNodeRange aRg( rNds, nNodeIdx, rNds, (pEndNdIdx
438 ? ((*pEndNdIdx) + 1)
439 : rNds.GetEndOfExtras().GetIndex() ) );
440 rNds._MoveNodes( aRg, rDoc.GetNodes(), rInsPos.nNode, 0 == pEndNdIdx );
443 else {
444 ASSERT( FALSE, "was ist es denn nun?" );
447 rDoc.DoUndo( bUndo );
450 // diese beiden Methoden bewegen den Point vom Pam zurueck/vor. Damit
451 // kann fuer ein Undo/Redo ein Bereich aufgespannt werden. (Der
452 // Point liegt dann vor dem manipuliertem Bereich !!)
453 // Das Flag gibt an, ob noch vorm Point Inhalt steht.
455 BOOL SwUndoSaveCntnt::MovePtBackward( SwPaM& rPam )
457 rPam.SetMark();
458 if( rPam.Move( fnMoveBackward ))
459 return TRUE;
461 // gibt es nach vorne keinen Inhalt mehr, so setze den Point einfach
462 // auf die vorherige Position (Node und Content, damit der Content
463 // abgemeldet wird !!)
464 rPam.GetPoint()->nNode--;
465 rPam.GetPoint()->nContent.Assign( 0, 0 );
466 return FALSE;
469 void SwUndoSaveCntnt::MovePtForward( SwPaM& rPam, BOOL bMvBkwrd )
471 // gab es noch Inhalt vor der Position ?
472 if( bMvBkwrd )
473 rPam.Move( fnMoveForward );
474 else
475 { // setzen Point auf die naechste Position
476 rPam.GetPoint()->nNode++;
477 SwCntntNode* pCNd = rPam.GetCntntNode();
478 if( pCNd )
479 pCNd->MakeStartIndex( &rPam.GetPoint()->nContent );
480 else
481 rPam.Move( fnMoveForward );
487 JP 21.03.94: loesche alle Objecte, die ContentIndizies auf den ang.
488 Bereich besitzen.
489 Zur Zeit gibts folgende Objecte
490 - Fussnoten
491 - Flys
492 - Bookmarks
493 - Verzeichnisse
495 // --> OD 2007-10-17 #i81002# - extending method:
496 // delete certain (not all) cross-reference bookmarks at text node of <rMark>
497 // and at text node of <rPoint>, if these text nodes aren't the same.
498 void SwUndoSaveCntnt::DelCntntIndex( const SwPosition& rMark,
499 const SwPosition& rPoint,
500 DelCntntType nDelCntntType )
502 const SwPosition *pStt = rMark < rPoint ? &rMark : &rPoint,
503 *pEnd = &rMark == pStt ? &rPoint : &rMark;
505 SwDoc* pDoc = rMark.nNode.GetNode().GetDoc();
507 BOOL bDoesUndo = pDoc->DoesUndo();
508 pDoc->DoUndo( FALSE );
510 // 1. Fussnoten
511 if( nsDelCntntType::DELCNT_FTN & nDelCntntType )
513 SwFtnIdxs& rFtnArr = pDoc->GetFtnIdxs();
514 if( rFtnArr.Count() )
516 const SwNode* pFtnNd;
517 USHORT nPos;
518 rFtnArr.SeekEntry( pStt->nNode, &nPos );
519 SwTxtFtn* pSrch;
521 // loesche erstmal alle, die dahinter stehen
522 while( nPos < rFtnArr.Count() && ( pFtnNd =
523 &( pSrch = rFtnArr[ nPos ] )->GetTxtNode())->GetIndex()
524 <= pEnd->nNode.GetIndex() )
526 xub_StrLen nFtnSttIdx = *pSrch->GetStart();
527 if( (nsDelCntntType::DELCNT_CHKNOCNTNT & nDelCntntType )
528 ? (&pEnd->nNode.GetNode() == pFtnNd )
529 : (( &pStt->nNode.GetNode() == pFtnNd &&
530 pStt->nContent.GetIndex() > nFtnSttIdx) ||
531 ( &pEnd->nNode.GetNode() == pFtnNd &&
532 nFtnSttIdx >= pEnd->nContent.GetIndex() )) )
534 ++nPos; // weiter suchen
535 continue;
538 // es muss leider ein Index angelegt werden. Sonst knallts im
539 // TextNode, weil im DTOR der SwFtn dieser geloescht wird !!
540 SwTxtNode* pTxtNd = (SwTxtNode*)pFtnNd;
541 if( !pHistory )
542 pHistory = new SwHistory;
543 SwTxtAttr* const pFtnHnt =
544 pTxtNd->GetTxtAttrForCharAt( nFtnSttIdx );
545 ASSERT( pFtnHnt, "kein FtnAttribut" );
546 SwIndex aIdx( pTxtNd, nFtnSttIdx );
547 pHistory->Add( pFtnHnt, pTxtNd->GetIndex(), false );
548 pTxtNd->EraseText( aIdx, 1 );
551 while( nPos-- && ( pFtnNd = &( pSrch = rFtnArr[ nPos ] )->
552 GetTxtNode())->GetIndex() >= pStt->nNode.GetIndex() )
554 xub_StrLen nFtnSttIdx = *pSrch->GetStart();
555 if( !(nsDelCntntType::DELCNT_CHKNOCNTNT & nDelCntntType) && (
556 ( &pStt->nNode.GetNode() == pFtnNd &&
557 pStt->nContent.GetIndex() > nFtnSttIdx ) ||
558 ( &pEnd->nNode.GetNode() == pFtnNd &&
559 nFtnSttIdx >= pEnd->nContent.GetIndex() )))
560 continue; // weiter suchen
562 // es muss leider ein Index angelegt werden. Sonst knallts im
563 // TextNode, weil im DTOR der SwFtn dieser geloescht wird !!
564 SwTxtNode* pTxtNd = (SwTxtNode*)pFtnNd;
565 if( !pHistory )
566 pHistory = new SwHistory;
567 SwTxtAttr* const pFtnHnt =
568 pTxtNd->GetTxtAttrForCharAt( nFtnSttIdx );
569 ASSERT( pFtnHnt, "kein FtnAttribut" );
570 SwIndex aIdx( pTxtNd, nFtnSttIdx );
571 pHistory->Add( pFtnHnt, pTxtNd->GetIndex(), false );
572 pTxtNd->EraseText( aIdx, 1 );
577 // 2. Flys
578 if( nsDelCntntType::DELCNT_FLY & nDelCntntType )
580 USHORT nChainInsPos = pHistory ? pHistory->Count() : 0;
581 const SwSpzFrmFmts& rSpzArr = *pDoc->GetSpzFrmFmts();
582 if( rSpzArr.Count() )
584 const BOOL bDelFwrd = rMark.nNode.GetIndex() <= rPoint.nNode.GetIndex();
585 SwFlyFrmFmt* pFmt;
586 const SwFmtAnchor* pAnchor;
587 USHORT n = rSpzArr.Count();
588 const SwPosition* pAPos;
590 while( n && rSpzArr.Count() )
592 pFmt = (SwFlyFrmFmt*)rSpzArr[--n];
593 pAnchor = &pFmt->GetAnchor();
594 switch( pAnchor->GetAnchorId() )
596 case FLY_IN_CNTNT:
597 if( 0 != (pAPos = pAnchor->GetCntntAnchor() ) &&
598 (( nsDelCntntType::DELCNT_CHKNOCNTNT & nDelCntntType )
599 ? ( pStt->nNode <= pAPos->nNode &&
600 pAPos->nNode < pEnd->nNode )
601 : ( *pStt <= *pAPos && *pAPos < *pEnd )) )
603 if( !pHistory )
604 pHistory = new SwHistory;
605 SwTxtNode* pTxtNd = pDoc->GetNodes()[ pAPos->nNode]->GetTxtNode();
606 SwTxtAttr* const pFlyHnt = pTxtNd->GetTxtAttrForCharAt(
607 pAPos->nContent.GetIndex());
608 ASSERT( pFlyHnt, "kein FlyAttribut" );
609 pHistory->Add( pFlyHnt, 0, false );
610 // n wieder zurueck, damit nicht ein Format uebesprungen wird !
611 n = n >= rSpzArr.Count() ? rSpzArr.Count() : n+1;
613 break;
614 case FLY_AT_CNTNT:
616 pAPos = pAnchor->GetCntntAnchor();
617 if( pAPos )
619 bool bTmp;
620 if( nsDelCntntType::DELCNT_CHKNOCNTNT & nDelCntntType )
621 bTmp = pStt->nNode <= pAPos->nNode && pAPos->nNode < pEnd->nNode;
622 else
624 if (bDelFwrd)
625 bTmp = rMark.nNode < pAPos->nNode &&
626 pAPos->nNode <= rPoint.nNode;
627 else
628 bTmp = rPoint.nNode <= pAPos->nNode &&
629 pAPos->nNode < rMark.nNode;
632 if (bTmp)
634 if( !pHistory )
635 pHistory = new SwHistory;
637 // Moving the anchor?
638 if( !( nsDelCntntType::DELCNT_CHKNOCNTNT & nDelCntntType ) &&
639 ( rPoint.nNode.GetIndex() == pAPos->nNode.GetIndex() ) )
641 // Do not try to move the anchor to a table!
642 if( rMark.nNode.GetNode().GetTxtNode() )
644 pHistory->Add( *pFmt );
645 SwFmtAnchor aAnch( *pAnchor );
646 SwPosition aPos( rMark.nNode );
647 aAnch.SetAnchor( &aPos );
648 pFmt->SetFmtAttr( aAnch );
651 else
653 pHistory->Add( *pFmt, nChainInsPos );
654 // n wieder zurueck, damit nicht ein
655 // Format uebesprungen wird !
656 n = n >= rSpzArr.Count() ?
657 rSpzArr.Count() : n+1;
662 break;
663 case FLY_AUTO_CNTNT:
664 if( 0 != (pAPos = pAnchor->GetCntntAnchor() ) &&
665 ( pStt->nNode <= pAPos->nNode && pAPos->nNode <= pEnd->nNode ) )
667 if( !pHistory )
668 pHistory = new SwHistory;
669 if( pAPos->nNode < pEnd->nNode &&
670 ( ( nsDelCntntType::DELCNT_CHKNOCNTNT & nDelCntntType ) ||
671 ( pStt->nNode < pAPos->nNode || !pStt->nContent.GetIndex() ) ) )
673 // Here we identified the objects to destroy:
674 // - anchored between start and end of the selection
675 // - anchored in start of the selection with "CheckNoContent"
676 // - anchored in start of sel. and the selection start at pos 0
677 pHistory->Add( *pFmt, nChainInsPos );
678 n = n >= rSpzArr.Count() ? rSpzArr.Count() : n+1;
680 else if( !( nsDelCntntType::DELCNT_CHKNOCNTNT & nDelCntntType ) )
682 if( *pStt <= *pAPos && *pAPos < *pEnd )
684 // These are the objects anchored
685 // between section start and end position
686 // Do not try to move the anchor to a table!
687 if( rMark.nNode.GetNode().GetTxtNode() )
689 pHistory->Add( *pFmt );
690 SwFmtAnchor aAnch( *pAnchor );
691 aAnch.SetAnchor( &rMark );
692 pFmt->SetFmtAttr( aAnch );
697 break;
698 case FLY_AT_FLY:
700 if( 0 != (pAPos = pAnchor->GetCntntAnchor() ) &&
701 pStt->nNode == pAPos->nNode )
703 if( !pHistory )
704 pHistory = new SwHistory;
706 pHistory->Add( *pFmt, nChainInsPos );
708 // n wieder zurueck, damit nicht ein Format uebesprungen wird !
709 n = n >= rSpzArr.Count() ? rSpzArr.Count() : n+1;
711 break;
712 default: break;
718 // 3. Bookmarks
719 if( nsDelCntntType::DELCNT_BKM & nDelCntntType )
721 IDocumentMarkAccess* const pMarkAccess = pDoc->getIDocumentMarkAccess();
722 if( pMarkAccess->getMarksCount() )
725 for( USHORT n = 0; n < pMarkAccess->getMarksCount(); ++n )
727 // --> OD 2007-10-17 #i81002#
728 bool bSavePos = false;
729 bool bSaveOtherPos = false;
730 const ::sw::mark::IMark* pBkmk = (pMarkAccess->getMarksBegin() + n)->get();
731 if( nsDelCntntType::DELCNT_CHKNOCNTNT & nDelCntntType )
733 if( pStt->nNode <= pBkmk->GetMarkPos().nNode &&
734 pBkmk->GetMarkPos().nNode < pEnd->nNode )
735 bSavePos = true;
736 if( pBkmk->IsExpanded() &&
737 pStt->nNode <= pBkmk->GetOtherMarkPos().nNode &&
738 pBkmk->GetOtherMarkPos().nNode < pEnd->nNode )
739 bSaveOtherPos = true;
741 else
743 // --> OD 2009-08-06 #i92125#
744 bool bKeepCrossRefBkmk( false );
746 if ( rMark.nNode == rPoint.nNode &&
747 ( IDocumentMarkAccess::GetType(*pBkmk) ==
748 IDocumentMarkAccess::CROSSREF_HEADING_BOOKMARK ||
749 IDocumentMarkAccess::GetType(*pBkmk) ==
750 IDocumentMarkAccess::CROSSREF_NUMITEM_BOOKMARK ) )
752 bKeepCrossRefBkmk = true;
755 if ( !bKeepCrossRefBkmk )
757 bool bMaybe = false;
758 if ( *pStt <= pBkmk->GetMarkPos() && pBkmk->GetMarkPos() <= *pEnd )
760 if( pBkmk->GetMarkPos() == *pEnd ||
761 ( *pStt == pBkmk->GetMarkPos() && pBkmk->IsExpanded() ) )
762 bMaybe = true;
763 else
764 bSavePos = true;
766 if( pBkmk->IsExpanded() &&
767 *pStt <= pBkmk->GetOtherMarkPos() && pBkmk->GetOtherMarkPos() <= *pEnd )
769 if( bSavePos || bSaveOtherPos ||
770 ( pBkmk->GetOtherMarkPos() < *pEnd && pBkmk->GetOtherMarkPos() > *pStt ) )
772 if( bMaybe )
773 bSavePos = true;
774 bSaveOtherPos = true;
778 // <--
780 // --> OD 2007-10-17 #i81002#
781 const bool bDifferentTxtNodesAtMarkAndPoint(
782 rMark.nNode != rPoint.nNode &&
783 rMark.nNode.GetNode().GetTxtNode() &&
784 rPoint.nNode.GetNode().GetTxtNode() );
785 // <--
786 if( !bSavePos && !bSaveOtherPos && bDifferentTxtNodesAtMarkAndPoint &&
787 dynamic_cast< const ::sw::mark::CrossRefBookmark* >(pBkmk))
789 // delete cross-reference bookmark at <pStt>, if only part of
790 // <pEnd> text node content is deleted.
791 if( pStt->nNode == pBkmk->GetMarkPos().nNode &&
792 pEnd->nContent.GetIndex() !=
793 pEnd->nNode.GetNode().GetTxtNode()->Len() )
795 bSavePos = true;
796 bSaveOtherPos = false;
798 // delete cross-reference bookmark at <pEnd>, if only part of
799 // <pStt> text node content is deleted.
800 else if( pEnd->nNode == pBkmk->GetMarkPos().nNode &&
801 pStt->nContent.GetIndex() != 0 )
803 bSavePos = true;
804 bSaveOtherPos = false;
808 if( bSavePos || bSaveOtherPos )
810 if( !pHistory )
811 pHistory = new SwHistory;
813 pHistory->Add( *pBkmk, bSavePos, bSaveOtherPos );
814 if(bSavePos &&
815 (bSaveOtherPos || !pBkmk->IsExpanded()))
817 pMarkAccess->deleteMark(pMarkAccess->getMarksBegin()+n);
818 n--;
825 pDoc->DoUndo( bDoesUndo );
829 // sicher eine vollstaendige Section im Undo-Nodes-Array
831 SwUndoSaveSection::SwUndoSaveSection()
832 : pMvStt( 0 ), pRedlSaveData( 0 ), nMvLen( 0 ), nStartPos( ULONG_MAX )
836 SwUndoSaveSection::~SwUndoSaveSection()
838 if( pMvStt ) // loesche noch den Bereich aus dem UndoNodes Array
840 // SaveSection speichert den Inhalt in der PostIt-Section
841 SwNodes& rUNds = pMvStt->GetNode().GetNodes();
842 rUNds.Delete( *pMvStt, nMvLen );
844 delete pMvStt;
846 delete pRedlSaveData;
849 void SwUndoSaveSection::SaveSection( SwDoc* pDoc, const SwNodeIndex& rSttIdx )
851 SwNodeRange aRg( rSttIdx.GetNode(), *rSttIdx.GetNode().EndOfSectionNode() );
852 SaveSection( pDoc, aRg );
856 void SwUndoSaveSection::SaveSection( SwDoc* , const SwNodeRange& rRange )
858 SwPaM aPam( rRange.aStart, rRange.aEnd );
860 // loesche alle Fussnoten / FlyFrames / Bookmarks / Verzeichnisse
861 DelCntntIndex( *aPam.GetMark(), *aPam.GetPoint() );
863 pRedlSaveData = new SwRedlineSaveDatas;
864 if( !SwUndo::FillSaveData( aPam, *pRedlSaveData, TRUE, TRUE ))
865 delete pRedlSaveData, pRedlSaveData = 0;
867 nStartPos = rRange.aStart.GetIndex();
869 aPam.GetPoint()->nNode--;
870 aPam.GetMark()->nNode++;
872 SwCntntNode* pCNd = aPam.GetCntntNode( FALSE );
873 if( pCNd )
874 aPam.GetMark()->nContent.Assign( pCNd, 0 );
875 if( 0 != ( pCNd = aPam.GetCntntNode( TRUE )) )
876 aPam.GetPoint()->nContent.Assign( pCNd, pCNd->Len() );
878 // Positionen als SwIndex merken, damit im DTOR dieser Bereich
879 // entfernt werden kann !!
880 ULONG nEnd;
881 pMvStt = new SwNodeIndex( rRange.aStart );
882 MoveToUndoNds( aPam, pMvStt, 0, &nEnd, 0 );
883 nMvLen = nEnd - pMvStt->GetIndex() + 1;
886 void SwUndoSaveSection::RestoreSection( SwDoc* pDoc, SwNodeIndex* pIdx,
887 USHORT nSectType )
889 if( ULONG_MAX != nStartPos ) // gab es ueberhaupt Inhalt ?
891 // ueberpruefe, ob der Inhalt an der alten Position steht
892 SwNodeIndex aSttIdx( pDoc->GetNodes(), nStartPos );
893 ASSERT( !pDoc->GetNodes()[ aSttIdx ]->GetCntntNode(),
894 "Position in irgendeiner Section" );
896 // move den Inhalt aus dem UndoNodes-Array in den Fly
897 SwStartNode* pSttNd = pDoc->GetNodes().MakeEmptySection( aSttIdx,
898 (SwStartNodeType)nSectType );
900 RestoreSection( pDoc, SwNodeIndex( *pSttNd->EndOfSectionNode() ));
902 if( pIdx )
903 *pIdx = *pSttNd;
907 void SwUndoSaveSection::RestoreSection( SwDoc* pDoc, const SwNodeIndex& rInsPos )
909 if( ULONG_MAX != nStartPos ) // gab es ueberhaupt Inhalt ?
911 SwPosition aInsPos( rInsPos );
912 ULONG nEnd = pMvStt->GetIndex() + nMvLen - 1;
913 MoveFromUndoNds( *pDoc, pMvStt->GetIndex(), 0, aInsPos, &nEnd, 0 );
915 // Indizies wieder zerstoren, Inhalt ist aus dem UndoNodes-Array
916 // entfernt worden.
917 DELETEZ( pMvStt );
918 nMvLen = 0;
920 if( pRedlSaveData )
922 SwUndo::SetSaveData( *pDoc, *pRedlSaveData );
923 delete pRedlSaveData, pRedlSaveData = 0;
928 // START
929 SwUndoStart::SwUndoStart( SwUndoId nInitId )
930 : SwUndo( UNDO_START ), nUserId( nInitId ), nEndOffset( 0 )
934 void SwUndoStart::Undo( SwUndoIter& rUndoIter )
936 if( !( --rUndoIter.nEndCnt ) && rUndoIter.bWeiter &&
937 ( rUndoIter.GetId() ? ( rUndoIter.GetId() == nUserId ||
938 ( UNDO_END == rUndoIter.GetId() && UNDO_START == GetId() )) : TRUE ))
939 rUndoIter.bWeiter = FALSE;
942 void SwUndoStart::Redo( SwUndoIter& rUndoIter )
944 rUndoIter.bWeiter = TRUE;
945 ++rUndoIter.nEndCnt;
948 void SwUndoStart::Repeat( SwUndoIter& rUndoIter )
950 rUndoIter.bWeiter = FALSE;
953 String SwUndoStart::GetComment() const
955 String sResult;
957 switch (nUserId)
959 case UNDO_START:
960 case UNDO_END:
961 sResult = String("??", RTL_TEXTENCODING_ASCII_US);
963 break;
965 default:
966 sResult = String(SW_RES(UNDO_BASE + nUserId));
967 sResult = GetRewriter().Apply(sResult);
970 return sResult;
973 SwRewriter SwUndoStart::GetRewriter() const
975 return mRewriter;
978 SwUndoId SwUndoStart::GetEffectiveId() const
980 return GetUserId();
983 void SwUndoStart::SetRewriter(const SwRewriter & rRewriter)
985 mRewriter = rRewriter;
988 // END
989 SwUndoEnd::SwUndoEnd( SwUndoId nInitId )
990 : SwUndo( UNDO_END ), nUserId( nInitId ), nSttOffset( 0 )
994 void SwUndoEnd::Undo( SwUndoIter& rUndoIter )
996 if( rUndoIter.GetId() == GetId() || !rUndoIter.GetId() )
997 rUndoIter.bWeiter = TRUE;
998 if( rUndoIter.bWeiter )
999 ++rUndoIter.nEndCnt;
1002 void SwUndoEnd::Redo( SwUndoIter& rUndoIter )
1004 if( !( --rUndoIter.nEndCnt ) && rUndoIter.bWeiter &&
1005 ( rUndoIter.GetId() ? ( rUndoIter.GetId() == nUserId ||
1006 ( UNDO_END == rUndoIter.GetId() && UNDO_START == GetId() )) : TRUE ))
1007 rUndoIter.bWeiter = FALSE;
1010 void SwUndoEnd::Repeat( SwUndoIter& rUndoIter )
1012 rUndoIter.bWeiter = FALSE;
1015 String SwUndoEnd::GetComment() const
1017 String sResult;
1019 switch (nUserId)
1021 case UNDO_START:
1022 case UNDO_END:
1023 sResult = String("??", RTL_TEXTENCODING_ASCII_US);
1025 break;
1026 default:
1027 sResult = SW_RES(UNDO_BASE + nUserId);
1028 sResult = GetRewriter().Apply(sResult);
1031 return sResult;
1034 void SwUndoEnd::SetRewriter(const SwRewriter & rRewriter)
1036 mRewriter = rRewriter;
1039 SwUndoId SwUndoEnd::GetEffectiveId() const
1041 return GetUserId();
1044 SwRewriter SwUndoEnd::GetRewriter() const
1046 return mRewriter;
1049 /* \f */
1050 // sicher und setze die RedlineDaten
1052 SwRedlineSaveData::SwRedlineSaveData( SwComparePosition eCmpPos,
1053 const SwPosition& rSttPos,
1054 const SwPosition& rEndPos,
1055 SwRedline& rRedl,
1056 BOOL bCopyNext )
1057 : SwUndRng( rRedl ),
1058 SwRedlineData( rRedl.GetRedlineData(), bCopyNext )
1060 ASSERT( POS_OUTSIDE == eCmpPos ||
1061 !rRedl.GetContentIdx(), "Redline mit Content" );
1063 switch( eCmpPos )
1065 case POS_OVERLAP_BEFORE: // Pos1 ueberlappt Pos2 am Anfang
1066 nEndNode = rEndPos.nNode.GetIndex();
1067 nEndCntnt = rEndPos.nContent.GetIndex();
1068 break;
1069 case POS_OVERLAP_BEHIND: // Pos1 ueberlappt Pos2 am Ende
1070 nSttNode = rSttPos.nNode.GetIndex();
1071 nSttCntnt = rSttPos.nContent.GetIndex();
1072 break;
1074 case POS_INSIDE: // Pos1 liegt vollstaendig in Pos2
1075 nSttNode = rSttPos.nNode.GetIndex();
1076 nSttCntnt = rSttPos.nContent.GetIndex();
1077 nEndNode = rEndPos.nNode.GetIndex();
1078 nEndCntnt = rEndPos.nContent.GetIndex();
1079 break;
1081 case POS_OUTSIDE: // Pos2 liegt vollstaendig in Pos1
1082 if( rRedl.GetContentIdx() )
1084 // dann den Bereich ins UndoArray verschieben und merken
1085 SaveSection( rRedl.GetDoc(), *rRedl.GetContentIdx() );
1086 rRedl.SetContentIdx( 0 );
1088 break;
1090 case POS_EQUAL: // Pos1 ist genauso gross wie Pos2
1091 break;
1093 default:
1094 ASSERT( !this, "keine gueltigen Daten!" )
1097 #ifndef PRODUCT
1098 nRedlineCount = rSttPos.nNode.GetNode().GetDoc()->GetRedlineTbl().Count();
1099 #endif
1102 SwRedlineSaveData::~SwRedlineSaveData()
1106 void SwRedlineSaveData::RedlineToDoc( SwPaM& rPam )
1108 SwDoc& rDoc = *rPam.GetDoc();
1109 SwRedline* pRedl = new SwRedline( *this, rPam );
1111 if( GetMvSttIdx() )
1113 SwNodeIndex aIdx( rDoc.GetNodes() );
1114 RestoreSection( &rDoc, &aIdx, SwNormalStartNode );
1115 if( GetHistory() )
1116 GetHistory()->Rollback( &rDoc );
1117 pRedl->SetContentIdx( &aIdx );
1119 SetPaM( *pRedl );
1120 // erstmal die "alten" entfernen, damit im Append keine unerwarteten
1121 // Dinge passieren, wie z.B. eine Delete in eigenen Insert. Dann wird
1122 // naehmlich das gerade restaurierte wieder geloescht - nicht das gewollte
1123 rDoc.DeleteRedline( *pRedl, false, USHRT_MAX );
1125 RedlineMode_t eOld = rDoc.GetRedlineMode();
1126 rDoc.SetRedlineMode_intern((RedlineMode_t)(eOld | nsRedlineMode_t::REDLINE_DONTCOMBINE_REDLINES));
1127 //#i92154# let UI know about a new redline with comment
1128 if (rDoc.GetDocShell() && (pRedl->GetComment() != String(::rtl::OUString::createFromAscii(""))) )
1129 rDoc.GetDocShell()->Broadcast(SwRedlineHint(pRedl,SWREDLINE_INSERTED));
1131 rDoc.AppendRedline( pRedl, true );
1132 rDoc.SetRedlineMode_intern( eOld );
1135 BOOL SwUndo::FillSaveData( const SwPaM& rRange, SwRedlineSaveDatas& rSData,
1136 BOOL bDelRange, BOOL bCopyNext )
1138 if( rSData.Count() )
1139 rSData.DeleteAndDestroy( 0, rSData.Count() );
1141 SwRedlineSaveData* pNewData;
1142 const SwPosition *pStt = rRange.Start(), *pEnd = rRange.End();
1143 const SwRedlineTbl& rTbl = rRange.GetDoc()->GetRedlineTbl();
1144 USHORT n = 0;
1145 rRange.GetDoc()->GetRedline( *pStt, &n );
1146 for( ; n < rTbl.Count(); ++n )
1148 SwRedline* pRedl = rTbl[ n ];
1149 const SwPosition *pRStt = pRedl->Start(), *pREnd = pRedl->End();
1151 SwComparePosition eCmpPos = ComparePosition( *pStt, *pEnd, *pRStt, *pREnd );
1152 if( POS_BEFORE != eCmpPos && POS_BEHIND != eCmpPos &&
1153 POS_COLLIDE_END != eCmpPos && POS_COLLIDE_START != eCmpPos )
1155 pNewData = new SwRedlineSaveData( eCmpPos, *pStt, *pEnd,
1156 *pRedl, bCopyNext );
1157 rSData.Insert( pNewData, rSData.Count() );
1160 if( rSData.Count() && bDelRange )
1161 rRange.GetDoc()->DeleteRedline( rRange, false, USHRT_MAX );
1162 return 0 != rSData.Count();
1165 BOOL SwUndo::FillSaveDataForFmt( const SwPaM& rRange, SwRedlineSaveDatas& rSData )
1167 if( rSData.Count() )
1168 rSData.DeleteAndDestroy( 0, rSData.Count() );
1170 SwRedlineSaveData* pNewData;
1171 const SwPosition *pStt = rRange.Start(), *pEnd = rRange.End();
1172 const SwRedlineTbl& rTbl = rRange.GetDoc()->GetRedlineTbl();
1173 USHORT n = 0;
1174 rRange.GetDoc()->GetRedline( *pStt, &n );
1175 for( ; n < rTbl.Count(); ++n )
1177 SwRedline* pRedl = rTbl[ n ];
1178 if( nsRedlineType_t::REDLINE_FORMAT == pRedl->GetType() )
1180 const SwPosition *pRStt = pRedl->Start(), *pREnd = pRedl->End();
1182 SwComparePosition eCmpPos = ComparePosition( *pStt, *pEnd, *pRStt, *pREnd );
1183 if( POS_BEFORE != eCmpPos && POS_BEHIND != eCmpPos &&
1184 POS_COLLIDE_END != eCmpPos && POS_COLLIDE_START != eCmpPos )
1186 pNewData = new SwRedlineSaveData( eCmpPos, *pStt, *pEnd,
1187 *pRedl, TRUE );
1188 rSData.Insert( pNewData, rSData.Count() );
1194 return 0 != rSData.Count();
1197 void SwUndo::SetSaveData( SwDoc& rDoc, const SwRedlineSaveDatas& rSData )
1199 RedlineMode_t eOld = rDoc.GetRedlineMode();
1200 rDoc.SetRedlineMode_intern( (RedlineMode_t)(( eOld & ~nsRedlineMode_t::REDLINE_IGNORE) | nsRedlineMode_t::REDLINE_ON ));
1201 SwPaM aPam( rDoc.GetNodes().GetEndOfContent() );
1203 for( USHORT n = rSData.Count(); n; )
1204 rSData[ --n ]->RedlineToDoc( aPam );
1206 // check redline count against count saved in RedlineSaveData object
1207 DBG_ASSERT( (rSData.Count() == 0) ||
1208 (rSData[0]->nRedlineCount == rDoc.GetRedlineTbl().Count()),
1209 "redline count not restored properly" );
1211 rDoc.SetRedlineMode_intern( eOld );
1214 BOOL SwUndo::HasHiddenRedlines( const SwRedlineSaveDatas& rSData )
1216 for( USHORT n = rSData.Count(); n; )
1217 if( rSData[ --n ]->GetMvSttIdx() )
1218 return TRUE;
1219 return FALSE;
1222 BOOL SwUndo::CanRedlineGroup( SwRedlineSaveDatas& rCurr,
1223 const SwRedlineSaveDatas& rCheck, BOOL bCurrIsEnd )
1225 BOOL bRet = FALSE;
1226 USHORT n;
1228 if( rCurr.Count() == rCheck.Count() )
1230 bRet = TRUE;
1231 for( n = 0; n < rCurr.Count(); ++n )
1233 const SwRedlineSaveData& rSet = *rCurr[ n ];
1234 const SwRedlineSaveData& rGet = *rCheck[ n ];
1235 if( rSet.nSttNode != rGet.nSttNode ||
1236 rSet.GetMvSttIdx() || rGet.GetMvSttIdx() ||
1237 ( bCurrIsEnd ? rSet.nSttCntnt != rGet.nEndCntnt
1238 : rSet.nEndCntnt != rGet.nSttCntnt ) ||
1239 !rGet.CanCombine( rSet ) )
1241 bRet = FALSE;
1242 break;
1246 if( bRet )
1247 for( n = 0; n < rCurr.Count(); ++n )
1249 SwRedlineSaveData& rSet = *rCurr[ n ];
1250 const SwRedlineSaveData& rGet = *rCheck[ n ];
1251 if( bCurrIsEnd )
1252 rSet.nSttCntnt = rGet.nSttCntnt;
1253 else
1254 rSet.nEndCntnt = rGet.nEndCntnt;
1257 return bRet;
1260 // #111827#
1261 String ShortenString(const String & rStr, xub_StrLen nLength, const String & rFillStr)
1263 ASSERT( nLength - rFillStr.Len() >= 2, "improper arguments")
1265 String aResult;
1267 if (rStr.Len() <= nLength)
1268 aResult = rStr;
1269 else
1271 long nTmpLength = nLength - rFillStr.Len();
1272 if ( nTmpLength < 2 )
1273 nTmpLength = 2;
1275 nLength = static_cast<xub_StrLen>(nTmpLength);
1277 const xub_StrLen nFrontLen = nLength - nLength / 2;
1278 const xub_StrLen nBackLen = nLength - nFrontLen;
1280 aResult += rStr.Copy(0, nFrontLen);
1281 aResult += rFillStr;
1282 aResult += rStr.Copy(rStr.Len() - nBackLen, nBackLen);
1285 return aResult;
1288 static bool lcl_IsSpecialCharacter(sal_Unicode nChar)
1290 switch (nChar)
1292 case CH_TXTATR_BREAKWORD:
1293 case CH_TXTATR_INWORD:
1294 case CH_TXTATR_TAB:
1295 case CH_TXTATR_NEWLINE:
1296 return true;
1298 default:
1299 break;
1302 return false;
1305 static String lcl_DenotedPortion(String rStr, xub_StrLen nStart,
1306 xub_StrLen nEnd)
1308 String aResult;
1310 if (nEnd - nStart > 0)
1312 sal_Unicode cLast = rStr.GetChar(nEnd - 1);
1313 if (lcl_IsSpecialCharacter(cLast))
1315 switch(cLast)
1317 case CH_TXTATR_TAB:
1318 aResult += String(SW_RES(STR_UNDO_TABS));
1320 break;
1321 case CH_TXTATR_NEWLINE:
1322 aResult += String(SW_RES(STR_UNDO_NLS));
1324 break;
1326 case CH_TXTATR_INWORD:
1327 case CH_TXTATR_BREAKWORD:
1328 aResult += UNDO_ARG2;
1330 break;
1333 SwRewriter aRewriter;
1334 aRewriter.AddRule(UNDO_ARG1,
1335 String::CreateFromInt32(nEnd - nStart));
1336 aResult = aRewriter.Apply(aResult);
1338 else
1340 aResult = String(SW_RES(STR_START_QUOTE));
1341 aResult += rStr.Copy(nStart, nEnd - nStart);
1342 aResult += String(SW_RES(STR_END_QUOTE));
1346 return aResult;
1349 String DenoteSpecialCharacters(const String & rStr)
1351 String aResult;
1353 if (rStr.Len() > 0)
1355 bool bStart = false;
1356 xub_StrLen nStart = 0;
1357 sal_Unicode cLast = 0;
1359 for (xub_StrLen i = 0; i < rStr.Len(); i++)
1361 if (lcl_IsSpecialCharacter(rStr.GetChar(i)))
1363 if (cLast != rStr.GetChar(i))
1364 bStart = true;
1367 else
1369 if (lcl_IsSpecialCharacter(cLast))
1370 bStart = true;
1373 if (bStart)
1375 aResult += lcl_DenotedPortion(rStr, nStart, i);
1377 nStart = i;
1378 bStart = false;
1381 cLast = rStr.GetChar(i);
1384 aResult += lcl_DenotedPortion(rStr, nStart, rStr.Len());
1386 else
1387 aResult = UNDO_ARG2;
1389 return aResult;