xref: /trunk/main/sw/source/core/doc/docedt.cxx (revision 78190a370f7d7129fed9a7e70ca122eaae71ce1d)
1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 // MARKER(update_precomp.py): autogen include statement, do not remove
23 #include "precompiled_sw.hxx"
24 
25 #include <string.h>         // fuer strchr()
26 #include <hintids.hxx>
27 
28 #include <vcl/sound.hxx>
29 #include <editeng/cscoitem.hxx>
30 #include <editeng/brkitem.hxx>
31 #include <linguistic/lngprops.hxx>
32 #include <com/sun/star/beans/XPropertySet.hpp>
33 #include <com/sun/star/i18n/WordType.hdl>
34 #include <unotools/charclass.hxx>
35 #include <unotools/transliterationwrapper.hxx>
36 #include <fmtanchr.hxx>
37 #include <fmtcntnt.hxx>
38 #include <fmtpdsc.hxx>
39 #include <txtftn.hxx>
40 #include <acorrect.hxx>     // Autokorrektur
41 #include <IMark.hxx>        // fuer SwBookmark
42 #include <cntfrm.hxx>       // fuers Spell
43 #include <crsrsh.hxx>
44 #include <doc.hxx>
45 #include <UndoManager.hxx>
46 #include <docsh.hxx>
47 #include <docary.hxx>
48 #include <doctxm.hxx>       // beim Move: Verzeichnisse korrigieren
49 #include <ftnidx.hxx>
50 #include <ftninfo.hxx>
51 #include <mdiexp.hxx>       // Statusanzeige
52 #include <mvsave.hxx>       // Strukturen zum Sichern beim Move/Delete
53 #include <ndtxt.hxx>
54 #include <pam.hxx>
55 #include <redline.hxx>
56 #include <rootfrm.hxx>      // fuers UpdateFtn
57 #include <splargs.hxx>      // fuer Spell
58 #include <swtable.hxx>
59 #include <swundo.hxx>       // fuer die UndoIds
60 #include <txtfrm.hxx>
61 #include <hints.hxx>
62 #include <UndoSplitMove.hxx>
63 #include <UndoRedline.hxx>
64 #include <UndoOverwrite.hxx>
65 #include <UndoInsert.hxx>
66 #include <UndoDelete.hxx>
67 #include <breakit.hxx>
68 #include <hhcwrp.hxx>
69 #include <breakit.hxx>
70 #include <vcl/msgbox.hxx>
71 #include "comcore.hrc"
72 #include "editsh.hxx"
73 #include <fldbas.hxx>
74 #include <fmtfld.hxx>
75 #include <docufld.hxx>
76 #include <unoflatpara.hxx>
77 #include <SwGrammarMarkUp.hxx>
78 
79 #include <vector>
80 
81 using ::rtl::OUString;
82 using namespace ::com::sun::star;
83 using namespace ::com::sun::star::linguistic2;
84 using namespace ::com::sun::star::i18n;
85 
86 //using namespace ::utl;
87 #ifndef S2U
88 #define S2U(rString) OUString::createFromAscii(rString)
89 #endif
90 
91 struct _SaveRedline
92 {
93     SwRedline* pRedl;
94     sal_uInt32 nStt, nEnd;
95     xub_StrLen nSttCnt, nEndCnt;
96 
97     _SaveRedline( SwRedline* pR, const SwNodeIndex& rSttIdx )
98         : pRedl( pR )
99     {
100         const SwPosition* pStt = pR->Start(),
101             * pEnd = pR->GetMark() == pStt ? pR->GetPoint() : pR->GetMark();
102         sal_uInt32 nSttIdx = rSttIdx.GetIndex();
103         nStt = pStt->nNode.GetIndex() - nSttIdx;
104         nSttCnt = pStt->nContent.GetIndex();
105         if( pR->HasMark() )
106         {
107             nEnd = pEnd->nNode.GetIndex() - nSttIdx;
108             nEndCnt = pEnd->nContent.GetIndex();
109         }
110 
111         pRedl->GetPoint()->nNode = 0;
112         pRedl->GetPoint()->nContent.Assign( 0, 0 );
113         pRedl->GetMark()->nNode = 0;
114         pRedl->GetMark()->nContent.Assign( 0, 0 );
115     }
116 
117     _SaveRedline( SwRedline* pR, const SwPosition& rPos )
118         : pRedl( pR )
119     {
120         const SwPosition* pStt = pR->Start(),
121             * pEnd = pR->GetMark() == pStt ? pR->GetPoint() : pR->GetMark();
122         sal_uInt32 nSttIdx = rPos.nNode.GetIndex();
123         nStt = pStt->nNode.GetIndex() - nSttIdx;
124         nSttCnt = pStt->nContent.GetIndex();
125         if( nStt == 0 )
126             nSttCnt = nSttCnt - rPos.nContent.GetIndex();
127         if( pR->HasMark() )
128         {
129             nEnd = pEnd->nNode.GetIndex() - nSttIdx;
130             nEndCnt = pEnd->nContent.GetIndex();
131             if( nEnd == 0 )
132                 nEndCnt = nEndCnt - rPos.nContent.GetIndex();
133         }
134 
135         pRedl->GetPoint()->nNode = 0;
136         pRedl->GetPoint()->nContent.Assign( 0, 0 );
137         pRedl->GetMark()->nNode = 0;
138         pRedl->GetMark()->nContent.Assign( 0, 0 );
139     }
140 
141     void SetPos( sal_uInt32 nInsPos )
142     {
143         pRedl->GetPoint()->nNode = nInsPos + nStt;
144         pRedl->GetPoint()->nContent.Assign( pRedl->GetCntntNode(), nSttCnt );
145         if( pRedl->HasMark() )
146         {
147             pRedl->GetMark()->nNode = nInsPos + nEnd;
148             pRedl->GetMark()->nContent.Assign( pRedl->GetCntntNode(sal_False), nEndCnt );
149         }
150     }
151 
152     void SetPos( const SwPosition& aPos )
153     {
154         pRedl->GetPoint()->nNode = aPos.nNode.GetIndex() + nStt;
155         pRedl->GetPoint()->nContent.Assign( pRedl->GetCntntNode(), nSttCnt + ( nStt == 0 ? aPos.nContent.GetIndex() : 0 ) );
156         if( pRedl->HasMark() )
157         {
158             pRedl->GetMark()->nNode = aPos.nNode.GetIndex() + nEnd;
159             pRedl->GetMark()->nContent.Assign( pRedl->GetCntntNode(sal_False), nEndCnt + ( nEnd == 0 ? aPos.nContent.GetIndex() : 0 ) );
160         }
161     }
162 };
163 
164 SV_DECL_PTRARR_DEL( _SaveRedlines, _SaveRedline*, 0, 4 )
165 
166 SV_IMPL_VARARR( _SaveFlyArr, _SaveFly )
167 SV_IMPL_PTRARR( _SaveRedlines, _SaveRedline* )
168 
169 bool lcl_MayOverwrite( const SwTxtNode *pNode, const xub_StrLen nPos )
170 {
171     sal_Unicode cChr = pNode->GetTxt().GetChar( nPos );
172     return !( ( CH_TXTATR_BREAKWORD == cChr || CH_TXTATR_INWORD == cChr ) &&
173               (0 != pNode->GetTxtAttrForCharAt( nPos ) ) );
174 }
175 
176 void lcl_SkipAttr( const SwTxtNode *pNode, SwIndex &rIdx, xub_StrLen &rStart )
177 {
178     if( !lcl_MayOverwrite( pNode, rStart ) )
179     {
180         // ueberspringe alle SonderAttribute
181         do {
182             // "Beep" bei jedem ausgelassenen
183             Sound::Beep(SOUND_ERROR);
184             rIdx++;
185         } while( (rStart = rIdx.GetIndex()) < pNode->GetTxt().Len()
186                && !lcl_MayOverwrite(pNode, rStart) );
187     }
188 }
189 
190 // -----------------------------------------------------------------
191 
192 void _RestFlyInRange( _SaveFlyArr & rArr, const SwNodeIndex& rSttIdx,
193                       const SwNodeIndex* pInsertPos )
194 {
195     SwPosition aPos( rSttIdx );
196     for( sal_uInt16 n = 0; n < rArr.Count(); ++n )
197     {
198         // neuen Anker anlegen
199         _SaveFly& rSave = rArr[n];
200         SwFrmFmt* pFmt = rSave.pFrmFmt;
201 
202         if( rSave.bInsertPosition )
203         {
204             if( pInsertPos != NULL )
205                 aPos.nNode = *pInsertPos;
206             else
207                 aPos.nNode = rSttIdx.GetIndex();
208         }
209         else
210             aPos.nNode = rSttIdx.GetIndex() + rSave.nNdDiff;
211 
212         aPos.nContent.Assign( 0, 0 );
213         SwFmtAnchor aAnchor( pFmt->GetAnchor() );
214         aAnchor.SetAnchor( &aPos );
215         pFmt->GetDoc()->GetSpzFrmFmts()->Insert(
216                 pFmt, pFmt->GetDoc()->GetSpzFrmFmts()->Count() );
217         pFmt->SetFmtAttr( aAnchor );
218         SwCntntNode* pCNd = aPos.nNode.GetNode().GetCntntNode();
219         if( pCNd && pCNd->getLayoutFrm( pFmt->GetDoc()->GetCurrentLayout(), 0, 0, sal_False ) )
220             pFmt->MakeFrms();
221     }
222 }
223 
224 void _SaveFlyInRange( const SwNodeRange& rRg, _SaveFlyArr& rArr )
225 {
226     SwSpzFrmFmts& rFmts = *rRg.aStart.GetNode().GetDoc()->GetSpzFrmFmts();
227     for( sal_uInt16 n = 0; n < rFmts.Count(); ++n )
228     {
229         SwFrmFmt *const pFmt = static_cast<SwFrmFmt*>(rFmts[n]);
230         SwFmtAnchor const*const pAnchor = &pFmt->GetAnchor();
231         SwPosition const*const pAPos = pAnchor->GetCntntAnchor();
232         if (pAPos &&
233             ((FLY_AT_PARA == pAnchor->GetAnchorId()) ||
234              (FLY_AT_CHAR == pAnchor->GetAnchorId())) &&
235             rRg.aStart <= pAPos->nNode && pAPos->nNode < rRg.aEnd )
236         {
237             _SaveFly aSave( pAPos->nNode.GetIndex() - rRg.aStart.GetIndex(),
238                             pFmt, sal_False );
239             rArr.Insert( aSave, rArr.Count());
240             pFmt->DelFrms();
241             rFmts.Remove( n--, 1 );
242         }
243     }
244 }
245 
246 void _SaveFlyInRange( const SwPaM& rPam, const SwNodeIndex& rInsPos,
247                        _SaveFlyArr& rArr, bool bMoveAllFlys )
248 {
249     SwSpzFrmFmts& rFmts = *rPam.GetPoint()->nNode.GetNode().GetDoc()->GetSpzFrmFmts();
250     SwFrmFmt* pFmt;
251     const SwFmtAnchor* pAnchor;
252 
253     const SwPosition* pPos = rPam.Start();
254     const SwNodeIndex& rSttNdIdx = pPos->nNode;
255     short nSttOff = (!bMoveAllFlys && rSttNdIdx.GetNode().IsCntntNode() &&
256                     pPos->nContent.GetIndex()) ? 1 : 0;
257 
258     pPos = rPam.GetPoint() == pPos ? rPam.GetMark() : rPam.GetPoint();
259     const SwNodeIndex& rEndNdIdx = pPos->nNode;
260     short nOff = ( bMoveAllFlys || ( rEndNdIdx.GetNode().IsCntntNode() &&
261                 pPos->nContent == rEndNdIdx.GetNode().GetCntntNode()->Len() ))
262                     ? 0 : 1;
263 
264     const SwNodeIndex* pCntntIdx;
265 
266     for( sal_uInt16 n = 0; n < rFmts.Count(); ++n )
267     {
268         sal_Bool bInsPos = sal_False;
269         pFmt = (SwFrmFmt*)rFmts[n];
270         pAnchor = &pFmt->GetAnchor();
271         const SwPosition* pAPos = pAnchor->GetCntntAnchor();
272         if (pAPos &&
273             ((FLY_AT_PARA == pAnchor->GetAnchorId()) ||
274              (FLY_AT_CHAR == pAnchor->GetAnchorId())) &&
275             // nicht verschieben, wenn die InsPos im CntntBereich vom Fly ist
276             ( 0 == ( pCntntIdx = pFmt->GetCntnt().GetCntntIdx() ) ||
277               !( *pCntntIdx < rInsPos &&
278                 rInsPos < pCntntIdx->GetNode().EndOfSectionIndex() )) )
279         {
280             if( !bMoveAllFlys && rEndNdIdx == pAPos->nNode )
281             {
282                 // wenn nur teil vom EndNode oder der EndNode und SttNode
283                 // identisch sind, chaos::Anchor nicht anfassen
284                 if( rSttNdIdx != pAPos->nNode )
285                 {
286                     // Anker nur an Anfang/Ende haengen
287                     SwPosition aPos( rSttNdIdx );
288                     SwFmtAnchor aAnchor( *pAnchor );
289                     aAnchor.SetAnchor( &aPos );
290                     pFmt->SetFmtAttr( aAnchor );
291 //                  ((SwFmtAnchor*)pAnchor)->SetAnchor( &aPos );
292                 }
293             }
294             else if( ( rSttNdIdx.GetIndex() + nSttOff <= pAPos->nNode.GetIndex()
295                     && pAPos->nNode.GetIndex() <= rEndNdIdx.GetIndex() - nOff ) ||
296                         0 != ( bInsPos = rInsPos == pAPos->nNode ))
297 
298             {
299                 _SaveFly aSave( pAPos->nNode.GetIndex() - rSttNdIdx.GetIndex(),
300                                 pFmt, bInsPos );
301                 rArr.Insert( aSave, rArr.Count());
302                 pFmt->DelFrms();
303                 rFmts.Remove( n--, 1 );
304             }
305         }
306     }
307 }
308 
309 // -----------------------------------------------------------------
310 
311 // loesche und verschiebe alle "Fly's am Absatz", die in der SSelection
312 // liegen. Steht am SPoint ein Fly, wird dieser auf den Mark verschoben.
313 
314 void DelFlyInRange( const SwNodeIndex& rMkNdIdx,
315                     const SwNodeIndex& rPtNdIdx )
316 {
317     const sal_Bool bDelFwrd = rMkNdIdx.GetIndex() <= rPtNdIdx.GetIndex();
318 
319     SwDoc* pDoc = rMkNdIdx.GetNode().GetDoc();
320     SwSpzFrmFmts& rTbl = *pDoc->GetSpzFrmFmts();
321     for ( sal_uInt16 i = rTbl.Count(); i; )
322     {
323         SwFrmFmt *pFmt = rTbl[--i];
324         const SwFmtAnchor &rAnch = pFmt->GetAnchor();
325         SwPosition const*const pAPos = rAnch.GetCntntAnchor();
326         if (pAPos &&
327             ((rAnch.GetAnchorId() == FLY_AT_PARA) ||
328              (rAnch.GetAnchorId() == FLY_AT_CHAR)) &&
329             ( bDelFwrd
330                 ? rMkNdIdx < pAPos->nNode && pAPos->nNode <= rPtNdIdx
331                 : rPtNdIdx <= pAPos->nNode && pAPos->nNode < rMkNdIdx ))
332         {
333             // nur den Anker verschieben ??
334             if( rPtNdIdx == pAPos->nNode )
335             {
336                 SwFmtAnchor aAnch( pFmt->GetAnchor() );
337                 SwPosition aPos( rMkNdIdx );
338                 aAnch.SetAnchor( &aPos );
339                 pFmt->SetFmtAttr( aAnch );
340             }
341             else
342             {
343                 // wird der Fly geloescht muss auch im seinem Inhalt alle
344                 // Flys geloescht werden !!
345                 const SwFmtCntnt &rCntnt = pFmt->GetCntnt();
346                 if( rCntnt.GetCntntIdx() )
347                 {
348                     DelFlyInRange( *rCntnt.GetCntntIdx(),
349                                     SwNodeIndex( *rCntnt.GetCntntIdx()->
350                                             GetNode().EndOfSectionNode() ));
351                     // Position kann sich verschoben haben !
352                     if( i > rTbl.Count() )
353                         i = rTbl.Count();
354                     else if( pFmt != rTbl[i] )
355                         i = rTbl.GetPos( pFmt );
356                 }
357 
358                 pDoc->DelLayoutFmt( pFmt );
359 
360                 // --> FME 2004-10-06 #117913# DelLayoutFmt can also
361                 // trigger the deletion of objects.
362                 if( i > rTbl.Count() )
363                     i = rTbl.Count();
364                 // <--
365             }
366         }
367     }
368 }
369 
370 
371 bool lcl_SaveFtn( const SwNodeIndex& rSttNd, const SwNodeIndex& rEndNd,
372                  const SwNodeIndex& rInsPos,
373                  SwFtnIdxs& rFtnArr, SwFtnIdxs& rSaveArr,
374                  const SwIndex* pSttCnt = 0, const SwIndex* pEndCnt = 0 )
375 {
376     bool bUpdateFtn = sal_False;
377     const SwNodes& rNds = rInsPos.GetNodes();
378     const bool bDelFtn = rInsPos.GetIndex() < rNds.GetEndOfAutotext().GetIndex() &&
379                 rSttNd.GetIndex() >= rNds.GetEndOfAutotext().GetIndex();
380     const bool bSaveFtn = !bDelFtn &&
381                     rInsPos.GetIndex() >= rNds.GetEndOfExtras().GetIndex();
382     if( rFtnArr.Count() )
383     {
384 
385         sal_uInt16 nPos;
386         rFtnArr.SeekEntry( rSttNd, &nPos );
387         SwTxtFtn* pSrch;
388         const SwNode* pFtnNd;
389 
390         // loesche/sicher erstmal alle, die dahinter stehen
391         while( nPos < rFtnArr.Count() && ( pFtnNd =
392             &( pSrch = rFtnArr[ nPos ] )->GetTxtNode())->GetIndex()
393                     <= rEndNd.GetIndex() )
394         {
395             xub_StrLen nFtnSttIdx = *pSrch->GetStart();
396             if( ( pEndCnt && pSttCnt )
397                 ? (( &rSttNd.GetNode() == pFtnNd &&
398                      pSttCnt->GetIndex() > nFtnSttIdx) ||
399                    ( &rEndNd.GetNode() == pFtnNd &&
400                     nFtnSttIdx >= pEndCnt->GetIndex() ))
401                 : ( &rEndNd.GetNode() == pFtnNd ))
402             {
403                 ++nPos;     // weiter suchen
404             }
405             else
406             {
407                 // dann weg damit
408                 if( bDelFtn )
409                 {
410                     SwTxtNode& rTxtNd = (SwTxtNode&)pSrch->GetTxtNode();
411                     SwIndex aIdx( &rTxtNd, nFtnSttIdx );
412                     rTxtNd.EraseText( aIdx, 1 );
413                 }
414                 else
415                 {
416                     pSrch->DelFrms(0);
417                     rFtnArr.Remove( nPos );
418                     if( bSaveFtn )
419                         rSaveArr.Insert( pSrch );
420                 }
421                 bUpdateFtn = sal_True;
422             }
423         }
424 
425         while( nPos-- && ( pFtnNd = &( pSrch = rFtnArr[ nPos ] )->
426                 GetTxtNode())->GetIndex() >= rSttNd.GetIndex() )
427         {
428             xub_StrLen nFtnSttIdx = *pSrch->GetStart();
429             if( !pEndCnt || !pSttCnt ||
430                 !( (( &rSttNd.GetNode() == pFtnNd &&
431                     pSttCnt->GetIndex() > nFtnSttIdx ) ||
432                    ( &rEndNd.GetNode() == pFtnNd &&
433                     nFtnSttIdx >= pEndCnt->GetIndex() )) ))
434             {
435                 if( bDelFtn )
436                 {
437                     // dann weg damit
438                     SwTxtNode& rTxtNd = (SwTxtNode&)pSrch->GetTxtNode();
439                     SwIndex aIdx( &rTxtNd, nFtnSttIdx );
440                     rTxtNd.EraseText( aIdx, 1 );
441                 }
442                 else
443                 {
444                     pSrch->DelFrms(0);
445                     rFtnArr.Remove( nPos );
446                     if( bSaveFtn )
447                         rSaveArr.Insert( pSrch );
448                 }
449                 bUpdateFtn = sal_True;
450             }
451         }
452     }
453     // When moving from redline section into document content section, e.g.
454     // after loading a document with (delete-)redlines, the footnote array
455     // has to be adjusted... (#i70572)
456     if( bSaveFtn )
457     {
458         SwNodeIndex aIdx( rSttNd );
459         while( aIdx < rEndNd ) // Check the moved section
460         {
461             SwNode* pNode = &aIdx.GetNode();
462             if( pNode->IsTxtNode() ) // Looking for text nodes...
463             {
464                 SwpHints *pHints =
465                     static_cast<SwTxtNode*>(pNode)->GetpSwpHints();
466                 if( pHints && pHints->HasFtn() ) //...with footnotes
467                 {
468                     bUpdateFtn = sal_True; // Heureka
469                     sal_uInt16 nCount = pHints->Count();
470                     for( sal_uInt16 i = 0; i < nCount; ++i )
471                     {
472                         SwTxtAttr *pAttr = pHints->GetTextHint( i );
473                         if ( pAttr->Which() == RES_TXTATR_FTN )
474                         {
475                             rSaveArr.Insert( static_cast<SwTxtFtn*>(pAttr) );
476                         }
477                     }
478                 }
479             }
480             ++aIdx;
481         }
482     }
483     return bUpdateFtn;
484 }
485 
486 void lcl_SaveRedlines( const SwPaM& aPam, _SaveRedlines& rArr )
487 {
488     SwDoc* pDoc = aPam.GetNode()->GetDoc();
489 
490     const SwPosition* pStart = aPam.Start();
491     const SwPosition* pEnd = aPam.End();
492 
493     // get first relevant redline
494     sal_uInt16 nCurrentRedline;
495     pDoc->GetRedline( *pStart, &nCurrentRedline );
496     if( nCurrentRedline > 0)
497         nCurrentRedline--;
498 
499     // redline mode REDLINE_IGNORE|REDLINE_ON; save old mode
500     RedlineMode_t eOld = pDoc->GetRedlineMode();
501     pDoc->SetRedlineMode_intern( (RedlineMode_t)(( eOld & ~nsRedlineMode_t::REDLINE_IGNORE) | nsRedlineMode_t::REDLINE_ON ));
502 
503     // iterate over relevant redlines and decide for each whether it should
504     // be saved, or split + saved
505     SwRedlineTbl& rRedlineTable = const_cast<SwRedlineTbl&>( pDoc->GetRedlineTbl() );
506     for( ; nCurrentRedline < rRedlineTable.Count(); nCurrentRedline++ )
507     {
508         SwRedline* pCurrent = rRedlineTable[ nCurrentRedline ];
509         SwComparePosition eCompare =
510             ComparePosition( *pCurrent->Start(), *pCurrent->End(),
511                              *pStart, *pEnd);
512 
513         // we must save this redline if it overlaps aPam
514         // (we may have to split it, too)
515         if( eCompare == POS_OVERLAP_BEHIND ||
516             eCompare == POS_OVERLAP_BEFORE ||
517             eCompare == POS_OUTSIDE ||
518             eCompare == POS_INSIDE ||
519             eCompare == POS_EQUAL )
520         {
521             rRedlineTable.Remove( nCurrentRedline-- );
522 
523             // split beginning, if necessary
524             if( eCompare == POS_OVERLAP_BEFORE ||
525                 eCompare == POS_OUTSIDE )
526             {
527 
528                 SwRedline* pNewRedline = new SwRedline( *pCurrent );
529                 *pNewRedline->End() = *pStart;
530                 *pCurrent->Start() = *pStart;
531                 pDoc->AppendRedline( pNewRedline, true );
532             }
533 
534             // split end, if necessary
535             if( eCompare == POS_OVERLAP_BEHIND ||
536                 eCompare == POS_OUTSIDE )
537             {
538                 SwRedline* pNewRedline = new SwRedline( *pCurrent );
539                 *pNewRedline->Start() = *pEnd;
540                 *pCurrent->End() = *pEnd;
541                 pDoc->AppendRedline( pNewRedline, true );
542             }
543 
544             // save the current redline
545             _SaveRedline* pSave = new _SaveRedline( pCurrent, *pStart );
546             rArr.C40_INSERT( _SaveRedline, pSave, rArr.Count() );
547         }
548     }
549 
550     // restore old redline mode
551     pDoc->SetRedlineMode_intern( eOld );
552 }
553 
554 void lcl_RestoreRedlines( SwDoc* pDoc, const SwPosition& rPos, _SaveRedlines& rArr )
555 {
556     RedlineMode_t eOld = pDoc->GetRedlineMode();
557     pDoc->SetRedlineMode_intern( (RedlineMode_t)(( eOld & ~nsRedlineMode_t::REDLINE_IGNORE) | nsRedlineMode_t::REDLINE_ON ));
558 
559     for( sal_uInt16 n = 0; n < rArr.Count(); ++n )
560     {
561         _SaveRedline* pSave = rArr[ n ];
562         pSave->SetPos( rPos );
563         pDoc->AppendRedline( pSave->pRedl, true );
564     }
565 
566     pDoc->SetRedlineMode_intern( eOld );
567 }
568 
569 
570 void lcl_SaveRedlines( const SwNodeRange& rRg, _SaveRedlines& rArr )
571 {
572     SwDoc* pDoc = rRg.aStart.GetNode().GetDoc();
573     sal_uInt16 nRedlPos;
574     SwPosition aSrchPos( rRg.aStart ); aSrchPos.nNode--;
575     aSrchPos.nContent.Assign( aSrchPos.nNode.GetNode().GetCntntNode(), 0 );
576     if( pDoc->GetRedline( aSrchPos, &nRedlPos ) && nRedlPos )
577         --nRedlPos;
578     else if( nRedlPos >= pDoc->GetRedlineTbl().Count() )
579         return ;
580 
581     RedlineMode_t eOld = pDoc->GetRedlineMode();
582     pDoc->SetRedlineMode_intern( (RedlineMode_t)(( eOld & ~nsRedlineMode_t::REDLINE_IGNORE) | nsRedlineMode_t::REDLINE_ON ));
583     SwRedlineTbl& rRedlTbl = (SwRedlineTbl&)pDoc->GetRedlineTbl();
584 
585     do {
586         SwRedline* pTmp = rRedlTbl[ nRedlPos ];
587 
588         const SwPosition* pRStt = pTmp->Start(),
589                         * pREnd = pTmp->GetMark() == pRStt
590                             ? pTmp->GetPoint() : pTmp->GetMark();
591 
592         if( pRStt->nNode < rRg.aStart )
593         {
594             if( pREnd->nNode > rRg.aStart && pREnd->nNode < rRg.aEnd )
595             {
596                 // Kopie erzeugen und Ende vom Original ans Ende des
597                 // MoveBereiches setzen. Die Kopie wird mit verschoben
598                 SwRedline* pNewRedl = new SwRedline( *pTmp );
599                 SwPosition* pTmpPos = pNewRedl->Start();
600                 pTmpPos->nNode = rRg.aStart;
601                 pTmpPos->nContent.Assign(
602                             pTmpPos->nNode.GetNode().GetCntntNode(), 0 );
603 
604                 _SaveRedline* pSave = new _SaveRedline( pNewRedl, rRg.aStart );
605 //              rArr.Insert( pSave, rArr.Count() );
606                 rArr.C40_INSERT( _SaveRedline, pSave, rArr.Count() );
607 
608                 pTmpPos = pTmp->End();
609                 pTmpPos->nNode = rRg.aEnd;
610                 pTmpPos->nContent.Assign(
611                             pTmpPos->nNode.GetNode().GetCntntNode(), 0 );
612             }
613             else if( pREnd->nNode == rRg.aStart )
614             {
615                 SwPosition* pTmpPos = pTmp->End();
616                 pTmpPos->nNode = rRg.aEnd;
617                 pTmpPos->nContent.Assign(
618                             pTmpPos->nNode.GetNode().GetCntntNode(), 0 );
619             }
620         }
621         else if( pRStt->nNode < rRg.aEnd )
622         {
623             rRedlTbl.Remove( nRedlPos-- );
624             if( pREnd->nNode < rRg.aEnd ||
625                 ( pREnd->nNode == rRg.aEnd && !pREnd->nContent.GetIndex()) )
626             {
627                 // gesamt verschieben
628                 _SaveRedline* pSave = new _SaveRedline( pTmp, rRg.aStart );
629 //              rArr.Insert( pSave, rArr.Count() );
630                 rArr.C40_INSERT( _SaveRedline, pSave, rArr.Count() );
631             }
632             else
633             {
634                 // aufsplitten
635                 SwRedline* pNewRedl = new SwRedline( *pTmp );
636                 SwPosition* pTmpPos = pNewRedl->End();
637                 pTmpPos->nNode = rRg.aEnd;
638                 pTmpPos->nContent.Assign(
639                             pTmpPos->nNode.GetNode().GetCntntNode(), 0 );
640 
641                 _SaveRedline* pSave = new _SaveRedline( pNewRedl, rRg.aStart );
642 //              rArr.Insert( pSave, rArr.Count() );
643                 rArr.C40_INSERT( _SaveRedline, pSave, rArr.Count() );
644 
645                 pTmpPos = pTmp->Start();
646                 pTmpPos->nNode = rRg.aEnd;
647                 pTmpPos->nContent.Assign(
648                             pTmpPos->nNode.GetNode().GetCntntNode(), 0 );
649                 pDoc->AppendRedline( pTmp, true );
650             }
651         }
652         else
653             break;
654 
655     } while( ++nRedlPos < pDoc->GetRedlineTbl().Count() );
656     pDoc->SetRedlineMode_intern( eOld );
657 }
658 
659 void lcl_RestoreRedlines( SwDoc* pDoc, sal_uInt32 nInsPos, _SaveRedlines& rArr )
660 {
661     RedlineMode_t eOld = pDoc->GetRedlineMode();
662     pDoc->SetRedlineMode_intern( (RedlineMode_t)(( eOld & ~nsRedlineMode_t::REDLINE_IGNORE) | nsRedlineMode_t::REDLINE_ON ));
663 
664     for( sal_uInt16 n = 0; n < rArr.Count(); ++n )
665     {
666         _SaveRedline* pSave = rArr[ n ];
667         pSave->SetPos( nInsPos );
668         pDoc->AppendRedline( pSave->pRedl, true );
669     }
670 
671     pDoc->SetRedlineMode_intern( eOld );
672 }
673 
674 // ------------------------------------------------------------------------
675 // #i59534: Redo of insertion of multiple text nodes runs into trouble
676 // because of unnecessary expanded redlines
677 // From now on this class saves the redline positions of all redlines which ends exact at the
678 // insert position (node _and_ content index)
679 
680 _SaveRedlEndPosForRestore::_SaveRedlEndPosForRestore( const SwNodeIndex& rInsIdx, xub_StrLen nCnt )
681     : pSavArr( 0 ), pSavIdx( 0 ), nSavCntnt( nCnt )
682 {
683     SwNode& rNd = rInsIdx.GetNode();
684     SwDoc* pDest = rNd.GetDoc();
685     if( pDest->GetRedlineTbl().Count() )
686     {
687         sal_uInt16 nFndPos;
688         const SwPosition* pEnd;
689         SwPosition aSrcPos( rInsIdx, SwIndex( rNd.GetCntntNode(), nCnt ));
690         const SwRedline* pRedl = pDest->GetRedline( aSrcPos, &nFndPos );
691         while( nFndPos-- && *( pEnd = ( pRedl =
692             pDest->GetRedlineTbl()[ nFndPos ] )->End() ) == aSrcPos && *pRedl->Start() < aSrcPos )
693         {
694             if( !pSavArr )
695             {
696                 pSavArr = new SvPtrarr( 2, 2 );
697                 pSavIdx = new SwNodeIndex( rInsIdx, -1 );
698             }
699             void* p = (void*)pEnd;
700             pSavArr->Insert( p, pSavArr->Count() );
701         }
702     }
703 }
704 
705 _SaveRedlEndPosForRestore::~_SaveRedlEndPosForRestore()
706 {
707     if( pSavArr )
708     {
709         delete pSavArr;
710         delete pSavIdx;
711     }
712 }
713 
714 void _SaveRedlEndPosForRestore::_Restore()
715 {
716     (*pSavIdx)++;
717     SwCntntNode* pNode = pSavIdx->GetNode().GetCntntNode();
718     // If there's no content node at the remembered position, we will not restore the old position
719     // This may happen if a table (or section?) will be inserted.
720     if( pNode )
721     {
722         SwPosition aPos( *pSavIdx, SwIndex( pNode, nSavCntnt ));
723         for( sal_uInt16 n = pSavArr->Count(); n; )
724             *((SwPosition*)pSavArr->GetObject( --n )) = aPos;
725     }
726 }
727 
728 
729 // ------------------------------------------------------------------------
730 
731 // Loeschen einer vollstaendigen Section des NodesArray.
732 // Der uebergebene Node steht irgendwo in der gewuenschten Section
733 void SwDoc::DeleteSection( SwNode *pNode )
734 {
735     ASSERT( pNode, "Kein Node uebergeben." );
736     SwStartNode* pSttNd = pNode->IsStartNode() ? (SwStartNode*)pNode
737                                                : pNode->StartOfSectionNode();
738     SwNodeIndex aSttIdx( *pSttNd ), aEndIdx( *pNode->EndOfSectionNode() );
739 
740     // dann loesche mal alle Fly's, text::Bookmarks, ...
741     DelFlyInRange( aSttIdx, aEndIdx );
742     DeleteRedline( *pSttNd, true, USHRT_MAX );
743     _DelBookmarks(aSttIdx, aEndIdx);
744 
745     {
746         // alle Crsr/StkCrsr/UnoCrsr aus dem Loeschbereich verschieben
747         SwNodeIndex aMvStt( aSttIdx, 1 );
748         CorrAbs( aMvStt, aEndIdx, SwPosition( aSttIdx ), sal_True );
749     }
750 
751     GetNodes().DelNodes( aSttIdx, aEndIdx.GetIndex() - aSttIdx.GetIndex() + 1 );
752 }
753 
754 
755 void SwDoc::SetModified(SwPaM &rPaM)
756 {
757     SwDataChanged aTmp( rPaM, 0 );
758     SetModified();
759 }
760 
761 /*************************************************************************
762  *                SwDoc::Overwrite()
763  ************************************************************************/
764 
765 bool SwDoc::Overwrite( const SwPaM &rRg, const String &rStr )
766 {
767     SwPosition& rPt = *(SwPosition*)rRg.GetPoint();
768     if( pACEWord )                  // Aufnahme in die Autokorrektur
769     {
770         if( 1 == rStr.Len() )
771             pACEWord->CheckChar( rPt, rStr.GetChar( 0 ) );
772         delete pACEWord, pACEWord = 0;
773     }
774 
775     SwTxtNode *pNode = rPt.nNode.GetNode().GetTxtNode();
776     if(!pNode)
777         return sal_False;
778 
779     if (GetIDocumentUndoRedo().DoesUndo())
780     {
781         GetIDocumentUndoRedo().ClearRedo(); // AppendUndo not always called
782     }
783 
784     sal_uInt16 nOldAttrCnt = pNode->GetpSwpHints()
785                                 ? pNode->GetpSwpHints()->Count() : 0;
786     SwDataChanged aTmp( rRg, 0 );
787     SwIndex& rIdx = rPt.nContent;
788     xub_StrLen nStart = 0;
789 
790     sal_Unicode c;
791     String aStr;
792 
793     sal_Bool bOldExpFlg = pNode->IsIgnoreDontExpand();
794     pNode->SetIgnoreDontExpand( sal_True );
795 
796     for( xub_StrLen nCnt = 0; nCnt < rStr.Len(); ++nCnt )
797     {
798         // hinter das Zeichen (zum aufspannen der Attribute !!)
799         nStart = rIdx.GetIndex();
800         if ( nStart < pNode->GetTxt().Len() )
801         {
802             lcl_SkipAttr( pNode, rIdx, nStart );
803         }
804         c = rStr.GetChar( nCnt );
805         if (GetIDocumentUndoRedo().DoesUndo())
806         {
807             bool bMerged(false);
808             if (GetIDocumentUndoRedo().DoesGroupUndo())
809             {
810                 SwUndo *const pUndo = GetUndoManager().GetLastUndo();
811                 SwUndoOverwrite *const pUndoOW(
812                     dynamic_cast<SwUndoOverwrite *>(pUndo) );
813                 if (pUndoOW)
814                 {
815                     // if CanGrouping() returns true it's already merged
816                     bMerged = pUndoOW->CanGrouping( this, rPt, c );
817                 }
818             }
819             if (!bMerged)
820             {
821                 SwUndo *const pUndoOW( new SwUndoOverwrite(this, rPt, c) );
822                 GetIDocumentUndoRedo().AppendUndo(pUndoOW);
823             }
824         }
825         else
826         {
827             // hinter das Zeichen (zum Aufspannen der Attribute !!)
828             if( nStart < pNode->GetTxt().Len() )
829                 rIdx++;
830             pNode->InsertText( c, rIdx, INS_EMPTYEXPAND );
831             if( nStart+1 < rIdx.GetIndex() )
832             {
833                 rIdx = nStart;
834                 pNode->EraseText( rIdx, 1 );
835                 rIdx++;
836             }
837         }
838     }
839     pNode->SetIgnoreDontExpand( bOldExpFlg );
840 
841     sal_uInt16 nNewAttrCnt = pNode->GetpSwpHints()
842                                 ? pNode->GetpSwpHints()->Count() : 0;
843     if( nOldAttrCnt != nNewAttrCnt )
844     {
845         SwUpdateAttr aHint(
846             0,
847             0,
848             0);
849 
850         pNode->ModifyBroadcast( 0, &aHint, TYPE( SwCrsrShell ) );
851     }
852 
853     if (!GetIDocumentUndoRedo().DoesUndo() &&
854         !IsIgnoreRedline() && GetRedlineTbl().Count())
855     {
856         SwPaM aPam( rPt.nNode, nStart, rPt.nNode, rPt.nContent.GetIndex() );
857         DeleteRedline( aPam, true, USHRT_MAX );
858     }
859     else if( IsRedlineOn() )
860     {
861         // FIXME: this redline is WRONG: there is no DELETE, and the skipped
862         // characters are also included in aPam
863         SwPaM aPam( rPt.nNode, nStart, rPt.nNode, rPt.nContent.GetIndex() );
864         AppendRedline( new SwRedline( nsRedlineType_t::REDLINE_INSERT, aPam ), true);
865     }
866 
867     SetModified();
868     return sal_True;
869 }
870 
871 
872 bool SwDoc::MoveAndJoin( SwPaM& rPaM, SwPosition& rPos, SwMoveFlags eMvFlags )
873 {
874     SwNodeIndex aIdx( rPaM.Start()->nNode );
875     sal_Bool bJoinTxt = aIdx.GetNode().IsTxtNode();
876     sal_Bool bOneNode = rPaM.GetPoint()->nNode == rPaM.GetMark()->nNode;
877     aIdx--;             // vor den Move Bereich !!
878 
879     bool bRet = MoveRange( rPaM, rPos, eMvFlags );
880     if( bRet && !bOneNode )
881     {
882         if( bJoinTxt )
883             aIdx++;
884         SwTxtNode * pTxtNd = aIdx.GetNode().GetTxtNode();
885         SwNodeIndex aNxtIdx( aIdx );
886         if( pTxtNd && pTxtNd->CanJoinNext( &aNxtIdx ) )
887         {
888             {   // Block wegen SwIndex in den Node !!
889                 CorrRel( aNxtIdx, SwPosition( aIdx, SwIndex( pTxtNd,
890                             pTxtNd->GetTxt().Len() ) ), 0, sal_True );
891             }
892             pTxtNd->JoinNext();
893         }
894     }
895     return bRet;
896 }
897 
898 // mst: it seems that this is mostly used by SwDoc internals; the only
899 // way to call this from the outside seems to be the special case in
900 // SwDoc::CopyRange (but i have not managed to actually hit that case)
901 bool SwDoc::MoveRange( SwPaM& rPaM, SwPosition& rPos, SwMoveFlags eMvFlags )
902 {
903     // keine Moves-Abfangen
904     const SwPosition *pStt = rPaM.Start(), *pEnd = rPaM.End();
905     if( !rPaM.HasMark() || *pStt >= *pEnd || (*pStt <= rPos && rPos < *pEnd))
906         return false;
907 
908     // sicher die absatzgebundenen Flys, damit sie verschoben werden koennen.
909     _SaveFlyArr aSaveFlyArr;
910     _SaveFlyInRange( rPaM, rPos.nNode, aSaveFlyArr, 0 != ( DOC_MOVEALLFLYS & eMvFlags ) );
911 
912     // save redlines (if DOC_MOVEREDLINES is used)
913     _SaveRedlines aSaveRedl( 0, 4 );
914     if( DOC_MOVEREDLINES & eMvFlags && GetRedlineTbl().Count() )
915     {
916         lcl_SaveRedlines( rPaM, aSaveRedl );
917 
918         // #i17764# unfortunately, code below relies on undos being
919         //          in a particular order, and presence of bookmarks
920         //          will change this order. Hence, we delete bookmarks
921         //          here without undo.
922         ::sw::UndoGuard const undoGuard(GetIDocumentUndoRedo());
923         _DelBookmarks(
924             pStt->nNode,
925             pEnd->nNode,
926             NULL,
927             &pStt->nContent,
928             &pEnd->nContent);
929     }
930 
931 
932     int bUpdateFtn = sal_False;
933     SwFtnIdxs aTmpFntIdx;
934 
935     // falls Undo eingeschaltet, erzeuge das UndoMove-Objekt
936     SwUndoMove * pUndoMove = 0;
937     if (GetIDocumentUndoRedo().DoesUndo())
938     {
939         GetIDocumentUndoRedo().ClearRedo();
940         pUndoMove = new SwUndoMove( rPaM, rPos );
941         pUndoMove->SetMoveRedlines( eMvFlags == DOC_MOVEREDLINES );
942     }
943     else
944     {
945         bUpdateFtn = lcl_SaveFtn( pStt->nNode, pEnd->nNode, rPos.nNode,
946                                     GetFtnIdxs(), aTmpFntIdx,
947                                     &pStt->nContent, &pEnd->nContent );
948     }
949 
950     sal_Bool bSplit = sal_False;
951     SwPaM aSavePam( rPos, rPos );
952 
953     // stelle den SPoint an den Anfang vom Bereich (Definition)
954     if( rPaM.GetPoint() == pEnd )
955         rPaM.Exchange();
956 
957     // in der EditShell wird nach dem Move ein JoinNext erzeugt, wenn
958     // vor und nach dem Move ein Text-Node steht.
959     SwTxtNode* pSrcNd = rPaM.GetPoint()->nNode.GetNode().GetTxtNode();
960     sal_Bool bCorrSavePam = pSrcNd && pStt->nNode != pEnd->nNode;
961 
962     // werden ein oder mehr TextNodes bewegt, so wird
963     // im SwNodes::Move ein SplitNode erzeugt. Dieser Updated aber nicht
964     // den Cursor. Um das zu verhindern, wird hier ein TextNode angelegt,
965     // um die Updaterei der Indizies zu erhalten. Nach dem Move wird
966     // evt. der Node geloescht.
967 
968     SwTxtNode * pTNd = rPos.nNode.GetNode().GetTxtNode();
969     if( pTNd && rPaM.GetPoint()->nNode != rPaM.GetMark()->nNode &&
970         ( rPos.nContent.GetIndex() || ( pTNd->Len() && bCorrSavePam  )) )
971     {
972         bSplit = sal_True;
973         xub_StrLen nMkCntnt = rPaM.GetMark()->nContent.GetIndex();
974 
975         SvULongs aBkmkArr( 15, 15 );
976         _SaveCntntIdx( this, rPos.nNode.GetIndex(), rPos.nContent.GetIndex(),
977                         aBkmkArr, SAVEFLY_SPLIT );
978 
979         pTNd = static_cast<SwTxtNode*>(pTNd->SplitCntntNode( rPos ));
980 
981         if( aBkmkArr.Count() )
982             _RestoreCntntIdx( this, aBkmkArr, rPos.nNode.GetIndex()-1, 0, sal_True );
983 
984         // jetzt noch den Pam berichtigen !!
985         if( rPos.nNode == rPaM.GetMark()->nNode )
986         {
987             rPaM.GetMark()->nNode = rPos.nNode.GetIndex()-1;
988             rPaM.GetMark()->nContent.Assign( pTNd, nMkCntnt );
989         }
990     }
991 
992     // setze den Pam um einen "Inhalt" zurueck; dadurch steht er immer
993     // ausserhalb des manipulierten Bereiches. Falls kein Inhalt mehr vor-
994     // handen, dann auf den StartNode (es ist immer einer vorhanden !!!)
995     sal_Bool bNullCntnt = !aSavePam.Move( fnMoveBackward, fnGoCntnt );
996     if( bNullCntnt )
997     {
998         aSavePam.GetPoint()->nNode--;
999     }
1000 
1001     // kopiere alle Bookmarks, die im Move Bereich stehen in ein
1002     // Array, das alle Angaben auf die Position als Offset speichert.
1003     // Die neue Zuordung erfolgt nach dem Moven.
1004     ::std::vector< ::sw::mark::SaveBookmark> aSaveBkmks;
1005     _DelBookmarks(
1006         pStt->nNode,
1007         pEnd->nNode,
1008         &aSaveBkmks,
1009         &pStt->nContent,
1010         &pEnd->nContent);
1011 
1012     // falls durch die vorherigen Loeschungen (z.B. der Fussnoten) kein
1013     // Bereich mehr existiert, ist das immernoch ein gueltiger Move!
1014     if( *rPaM.GetPoint() != *rPaM.GetMark() )
1015     {
1016         // now do the actual move
1017         GetNodes().MoveRange( rPaM, rPos, GetNodes() );
1018 
1019         // after a MoveRange() the Mark is deleted
1020         if ( rPaM.HasMark() ) // => no Move occurred!
1021         {
1022             delete pUndoMove;
1023             return false;
1024         }
1025     }
1026     else
1027         rPaM.DeleteMark();
1028 
1029     ASSERT( *aSavePam.GetMark() == rPos ||
1030             ( aSavePam.GetMark()->nNode.GetNode().GetCntntNode() == NULL ),
1031             "PaM wurde nicht verschoben, am Anfang/Ende keine ContentNodes?" );
1032     *aSavePam.GetMark() = rPos;
1033 
1034     rPaM.SetMark();         // um den neuen Bereich eine Sel. aufspannen
1035     pTNd = aSavePam.GetNode()->GetTxtNode();
1036     if (GetIDocumentUndoRedo().DoesUndo())
1037     {
1038         // korrigiere erstmal den Content vom SavePam
1039         if( bNullCntnt )
1040         {
1041             aSavePam.GetPoint()->nContent = 0;
1042         }
1043 
1044         // die Methode SwEditShell::Move() fuegt nach dem Move den Text-Node
1045         // zusammen, in dem der rPaM steht. Wurde der Inhalt nach hinten
1046         // geschoben und liegt der SPoint vom SavePam im naechsten Node, so
1047         // muss beim Speichern vom Undo-Object das beachtet werden !!
1048         SwTxtNode * pPamTxtNd = 0;
1049 
1050         // wird ans SwUndoMove weitergegeben, das dann beim Undo JoinNext
1051         // aufruft. (falls es hier nicht moeglich ist).
1052         sal_Bool bJoin = bSplit && pTNd;
1053         bCorrSavePam = bCorrSavePam &&
1054                         0 != ( pPamTxtNd = rPaM.GetNode()->GetTxtNode() )
1055                         && pPamTxtNd->CanJoinNext()
1056                         && (*rPaM.GetPoint() <= *aSavePam.GetPoint());
1057 
1058         // muessen am SavePam 2 Nodes zusammengefasst werden ??
1059         if( bJoin && pTNd->CanJoinNext() )
1060         {
1061             pTNd->JoinNext();
1062             // kein temp. sdbcx::Index bei &&
1063             // es sollten wohl nur die Indexwerte verglichen werden.
1064             if( bCorrSavePam && rPaM.GetPoint()->nNode.GetIndex()+1 ==
1065                                 aSavePam.GetPoint()->nNode.GetIndex() )
1066             {
1067                 aSavePam.GetPoint()->nContent += pPamTxtNd->Len();
1068             }
1069             bJoin = sal_False;
1070         }
1071 //      else if( !bCorrSavePam && !pSavePam->Move( fnMoveForward, fnGoCntnt ))
1072         else if ( !aSavePam.Move( fnMoveForward, fnGoCntnt ) )
1073         {
1074             aSavePam.GetPoint()->nNode++;
1075         }
1076 
1077         // zwischen SPoint und GetMark steht jetzt der neu eingefuegte Bereich
1078         pUndoMove->SetDestRange( aSavePam, *rPaM.GetPoint(),
1079                                     bJoin, bCorrSavePam );
1080         GetIDocumentUndoRedo().AppendUndo( pUndoMove );
1081     }
1082     else
1083     {
1084         bool bRemove = true;
1085         // muessen am SavePam 2 Nodes zusammengefasst werden ??
1086         if( bSplit && pTNd )
1087         {
1088             if( pTNd->CanJoinNext())
1089             {
1090                 // --> OD 2009-08-20 #i100466#
1091                 // Always join next, because <pTNd> has to stay as it is.
1092                 // A join previous from its next would more or less delete <pTNd>
1093                 pTNd->JoinNext();
1094                 // <--
1095                 bRemove = false;
1096             }
1097         }
1098         if( bNullCntnt )
1099         {
1100             aSavePam.GetPoint()->nNode++;
1101             aSavePam.GetPoint()->nContent.Assign( aSavePam.GetCntntNode(), 0 );
1102         }
1103         else if( bRemove ) // No move forward after joining with next paragraph
1104         {
1105             aSavePam.Move( fnMoveForward, fnGoCntnt );
1106         }
1107     }
1108 
1109     // setze jetzt wieder die text::Bookmarks in das Dokument
1110     *rPaM.GetMark() = *aSavePam.Start();
1111     for(
1112         ::std::vector< ::sw::mark::SaveBookmark>::iterator pBkmk = aSaveBkmks.begin();
1113         pBkmk != aSaveBkmks.end();
1114         ++pBkmk)
1115         pBkmk->SetInDoc(
1116             this,
1117             rPaM.GetMark()->nNode,
1118             &rPaM.GetMark()->nContent);
1119     *rPaM.GetPoint() = *aSavePam.End();
1120 
1121     // verschiebe die Flys an die neue Position
1122     _RestFlyInRange( aSaveFlyArr, rPaM.Start()->nNode, &(rPos.nNode) );
1123 
1124     // restore redlines (if DOC_MOVEREDLINES is used)
1125     if( aSaveRedl.Count() )
1126     {
1127         lcl_RestoreRedlines( this, *aSavePam.Start(), aSaveRedl );
1128     }
1129 
1130     if( bUpdateFtn )
1131     {
1132         if( aTmpFntIdx.Count() )
1133         {
1134             GetFtnIdxs().Insert( &aTmpFntIdx );
1135             aTmpFntIdx.Remove( sal_uInt16( 0 ), aTmpFntIdx.Count() );
1136         }
1137 
1138         GetFtnIdxs().UpdateAllFtn();
1139     }
1140 
1141     SetModified();
1142     return true;
1143 }
1144 
1145 bool SwDoc::MoveNodeRange( SwNodeRange& rRange, SwNodeIndex& rPos,
1146         SwMoveFlags eMvFlags )
1147 {
1148     // bewegt alle Nodes an die neue Position. Dabei werden die
1149     // text::Bookmarks mit verschoben !! (zur Zeit ohne Undo)
1150 
1151     // falls durchs Move Fussnoten in den Sonderbereich kommen sollten,
1152     // dann entferne sie jetzt.
1153     //JP 13.07.95:
1154     // ansonsten bei allen Fussnoten, die verschoben werden, die Frames
1155     // loeschen und nach dem Move wieder aufbauen lassen (Fussnoten koennen
1156     // die Seite wechseln). Zusaetzlich muss natuerlich die Sortierung
1157     // der FtnIdx-Array wieder korrigiert werden.
1158 
1159     int bUpdateFtn = sal_False;
1160     SwFtnIdxs aTmpFntIdx;
1161 
1162     SwUndoMove* pUndo = 0;
1163     if ((DOC_CREATEUNDOOBJ & eMvFlags ) && GetIDocumentUndoRedo().DoesUndo())
1164     {
1165         pUndo = new SwUndoMove( this, rRange, rPos );
1166     }
1167     else
1168     {
1169         bUpdateFtn = lcl_SaveFtn( rRange.aStart, rRange.aEnd, rPos,
1170                                     GetFtnIdxs(), aTmpFntIdx );
1171     }
1172 
1173     _SaveRedlines aSaveRedl( 0, 4 );
1174     SvPtrarr aSavRedlInsPosArr( 0, 4 );
1175     if( DOC_MOVEREDLINES & eMvFlags && GetRedlineTbl().Count() )
1176     {
1177         lcl_SaveRedlines( rRange, aSaveRedl );
1178 
1179         // suche alle Redlines, die an der InsPos aufhoeren. Diese muessen
1180         // nach dem Move wieder an die "alte" Position verschoben werden
1181         sal_uInt16 nRedlPos = GetRedlinePos( rPos.GetNode(), USHRT_MAX );
1182         if( USHRT_MAX != nRedlPos )
1183         {
1184             const SwPosition *pRStt, *pREnd;
1185             do {
1186                 SwRedline* pTmp = GetRedlineTbl()[ nRedlPos ];
1187                 pRStt = pTmp->Start();
1188                 pREnd = pTmp->End();
1189                 if( pREnd->nNode == rPos && pRStt->nNode < rPos )
1190                 {
1191                     void* p = pTmp;
1192                     aSavRedlInsPosArr.Insert( p, aSavRedlInsPosArr.Count() );
1193                 }
1194             } while( pRStt->nNode < rPos && ++nRedlPos < GetRedlineTbl().Count());
1195         }
1196     }
1197 
1198     // kopiere alle Bookmarks, die im Move Bereich stehen in ein
1199     // Array, das alle Angaben auf die Position als Offset speichert.
1200     // Die neue Zuordung erfolgt nach dem Moven.
1201     ::std::vector< ::sw::mark::SaveBookmark> aSaveBkmks;
1202     _DelBookmarks(rRange.aStart, rRange.aEnd, &aSaveBkmks);
1203 
1204     // sicher die absatzgebundenen Flys, damit verschoben werden koennen.
1205     _SaveFlyArr aSaveFlyArr;
1206     if( GetSpzFrmFmts()->Count() )
1207         _SaveFlyInRange( rRange, aSaveFlyArr );
1208 
1209     // vor die Position setzen, damit er nicht weitergeschoben wird
1210     SwNodeIndex aIdx( rPos, -1 );
1211 
1212     SwNodeIndex* pSaveInsPos = 0;
1213     if( pUndo )
1214         pSaveInsPos = new SwNodeIndex( rRange.aStart, -1 );
1215 
1216     // verschiebe die Nodes
1217     sal_Bool bNoDelFrms = 0 != (DOC_NO_DELFRMS & eMvFlags);
1218     if( GetNodes()._MoveNodes( rRange, GetNodes(), rPos, !bNoDelFrms ) )
1219     {
1220         aIdx++;     // wieder auf alte Position
1221         if( pSaveInsPos )
1222             (*pSaveInsPos)++;
1223     }
1224     else
1225     {
1226         aIdx = rRange.aStart;
1227         delete pUndo, pUndo = 0;
1228     }
1229 
1230     // verschiebe die Flys an die neue Position
1231     if( aSaveFlyArr.Count() )
1232         _RestFlyInRange( aSaveFlyArr, aIdx, NULL );
1233 
1234     // setze jetzt wieder die text::Bookmarks in das Dokument
1235     for(
1236         ::std::vector< ::sw::mark::SaveBookmark>::iterator pBkmk = aSaveBkmks.begin();
1237         pBkmk != aSaveBkmks.end();
1238         ++pBkmk)
1239         pBkmk->SetInDoc(this, aIdx);
1240 
1241     if( aSavRedlInsPosArr.Count() )
1242     {
1243         SwNode* pNewNd = &aIdx.GetNode();
1244         for( sal_uInt16 n = 0; n < aSavRedlInsPosArr.Count(); ++n )
1245         {
1246             SwRedline* pTmp = (SwRedline*)aSavRedlInsPosArr[ n ];
1247             if( USHRT_MAX != GetRedlineTbl().GetPos( pTmp ) )
1248             {
1249                 SwPosition* pEnd = pTmp->End();
1250                 pEnd->nNode = aIdx;
1251                 pEnd->nContent.Assign( pNewNd->GetCntntNode(), 0 );
1252             }
1253         }
1254     }
1255 
1256     if( aSaveRedl.Count() )
1257         lcl_RestoreRedlines( this, aIdx.GetIndex(), aSaveRedl );
1258 
1259     if( pUndo )
1260     {
1261         pUndo->SetDestRange( aIdx, rPos, *pSaveInsPos );
1262         GetIDocumentUndoRedo().AppendUndo(pUndo);
1263     }
1264 
1265     if( pSaveInsPos )
1266         delete pSaveInsPos;
1267 
1268     if( bUpdateFtn )
1269     {
1270         if( aTmpFntIdx.Count() )
1271         {
1272             GetFtnIdxs().Insert( &aTmpFntIdx );
1273             aTmpFntIdx.Remove( sal_uInt16( 0 ), aTmpFntIdx.Count() );
1274         }
1275 
1276         GetFtnIdxs().UpdateAllFtn();
1277     }
1278 
1279     SetModified();
1280     return sal_True;
1281 }
1282 
1283 /* #107318# Convert list of ranges of whichIds to a corresponding list
1284     of whichIds*/
1285 SvUShorts * lcl_RangesToUShorts(sal_uInt16 * pRanges)
1286 {
1287     SvUShorts * pResult = new SvUShorts();
1288 
1289     int i = 0;
1290     while (pRanges[i] != 0)
1291     {
1292         ASSERT(pRanges[i+1] != 0, "malformed ranges");
1293 
1294         for (sal_uInt16 j = pRanges[i]; j < pRanges[i+1]; j++)
1295             pResult->Insert(j, pResult->Count());
1296 
1297         i += 2;
1298     }
1299 
1300     return pResult;
1301 }
1302 
1303 bool lcl_StrLenOverFlow( const SwPaM& rPam )
1304 {
1305     // If we try to merge two paragraph we have to test if afterwards
1306     // the string doesn't exceed the allowed string length
1307     bool bRet = false;
1308     if( rPam.GetPoint()->nNode != rPam.GetMark()->nNode )
1309     {
1310         const SwPosition* pStt = rPam.Start(), *pEnd = rPam.End();
1311         const SwTxtNode* pEndNd = pEnd->nNode.GetNode().GetTxtNode();
1312         if( (0 != pEndNd) && pStt->nNode.GetNode().IsTxtNode() )
1313         {
1314             sal_uInt64 nSum = pStt->nContent.GetIndex() +
1315                 pEndNd->GetTxt().Len() - pEnd->nContent.GetIndex();
1316             if( nSum > STRING_LEN )
1317                 bRet = true;
1318         }
1319     }
1320     return bRet;
1321 }
1322 
1323 void lcl_GetJoinFlags( SwPaM& rPam, sal_Bool& rJoinTxt, sal_Bool& rJoinPrev )
1324 {
1325     rJoinTxt = sal_False;
1326     rJoinPrev = sal_False;
1327     if( rPam.GetPoint()->nNode != rPam.GetMark()->nNode )
1328     {
1329         const SwPosition* pStt = rPam.Start(), *pEnd = rPam.End();
1330         SwTxtNode *pSttNd = pStt->nNode.GetNode().GetTxtNode();
1331         if( pSttNd )
1332         {
1333             SwTxtNode *pEndNd = pEnd->nNode.GetNode().GetTxtNode();
1334             rJoinTxt = 0 != pEndNd;
1335             if( rJoinTxt )
1336             {
1337                 bool bExchange = pStt == rPam.GetPoint();
1338                 if( !pStt->nContent.GetIndex() &&
1339                     pEndNd->GetTxt().Len() != pEnd->nContent.GetIndex() )
1340                     bExchange = !bExchange;
1341                 if( bExchange )
1342                     rPam.Exchange();
1343                 rJoinPrev = rPam.GetPoint() == pStt;
1344                 ASSERT( !pStt->nContent.GetIndex() &&
1345                     pEndNd->GetTxt().Len() != pEnd->nContent.GetIndex()
1346                     ? rPam.GetPoint()->nNode < rPam.GetMark()->nNode
1347                     : rPam.GetPoint()->nNode > rPam.GetMark()->nNode,
1348                     "lcl_GetJoinFlags");
1349             }
1350         }
1351     }
1352 }
1353 
1354 void lcl_JoinText( SwPaM& rPam, sal_Bool bJoinPrev )
1355 {
1356     SwNodeIndex aIdx( rPam.GetPoint()->nNode );
1357     SwTxtNode *pTxtNd = aIdx.GetNode().GetTxtNode();
1358     SwNodeIndex aOldIdx( aIdx );
1359     SwTxtNode *pOldTxtNd = pTxtNd;
1360 
1361     if( pTxtNd && pTxtNd->CanJoinNext( &aIdx ) )
1362     {
1363         SwDoc* pDoc = rPam.GetDoc();
1364         if( bJoinPrev )
1365         {
1366             // N.B.: we do not need to handle xmlids in this case, because
1367             // it is only invoked if one paragraph is completely empty
1368             // (see lcl_GetJoinFlags)
1369             {
1370                 // falls PageBreaks geloescht / gesetzt werden, darf das
1371                 // nicht in die Undo-History aufgenommen werden !!
1372                 // (das loeschen vom Node geht auch am Undo vorbei !!!)
1373                 ::sw::UndoGuard const undoGuard(pDoc->GetIDocumentUndoRedo());
1374 
1375                 /* PageBreaks, PageDesc, ColumnBreaks */
1376                 // Sollte an der Logik zum Kopieren der PageBreak's ...
1377                 // etwas geaendert werden, muss es auch im SwUndoDelete
1378                 // geandert werden. Dort wird sich das AUTO-PageBreak
1379                 // aus dem GetMarkNode kopiert.!!!
1380 
1381                 /* Der GetMarkNode */
1382                 if( ( pTxtNd = aIdx.GetNode().GetTxtNode())->HasSwAttrSet() )
1383                 {
1384                     const SfxPoolItem* pItem;
1385                     if( SFX_ITEM_SET == pTxtNd->GetpSwAttrSet()->GetItemState(
1386                         RES_BREAK, sal_False, &pItem ) )
1387                         pTxtNd->ResetAttr( RES_BREAK );
1388                     if( pTxtNd->HasSwAttrSet() &&
1389                         SFX_ITEM_SET == pTxtNd->GetpSwAttrSet()->GetItemState(
1390                         RES_PAGEDESC, sal_False, &pItem ) )
1391                         pTxtNd->ResetAttr( RES_PAGEDESC );
1392                 }
1393 
1394                 /* Der PointNode */
1395                 if( pOldTxtNd->HasSwAttrSet() )
1396                 {
1397                     const SfxPoolItem* pItem;
1398                     SfxItemSet aSet( pDoc->GetAttrPool(), aBreakSetRange );
1399                     const SfxItemSet* pSet = pOldTxtNd->GetpSwAttrSet();
1400                     if( SFX_ITEM_SET == pSet->GetItemState( RES_BREAK,
1401                         sal_False, &pItem ) )
1402                         aSet.Put( *pItem );
1403                     if( SFX_ITEM_SET == pSet->GetItemState( RES_PAGEDESC,
1404                         sal_False, &pItem ) )
1405                         aSet.Put( *pItem );
1406                     if( aSet.Count() )
1407                         pTxtNd->SetAttr( aSet );
1408                 }
1409                 pOldTxtNd->FmtToTxtAttr( pTxtNd );
1410 
1411                 SvULongs aBkmkArr( 15, 15 );
1412                 ::_SaveCntntIdx( pDoc, aOldIdx.GetIndex(),
1413                                     pOldTxtNd->Len(), aBkmkArr );
1414 
1415                 SwIndex aAlphaIdx(pTxtNd);
1416                 pOldTxtNd->CutText( pTxtNd, aAlphaIdx, SwIndex(pOldTxtNd),
1417                                     pOldTxtNd->Len() );
1418                 SwPosition aAlphaPos( aIdx, aAlphaIdx );
1419                 pDoc->CorrRel( rPam.GetPoint()->nNode, aAlphaPos, 0, sal_True );
1420 
1421                 // verschiebe noch alle Bookmarks/TOXMarks
1422                 if( aBkmkArr.Count() )
1423                     ::_RestoreCntntIdx( pDoc, aBkmkArr, aIdx.GetIndex() );
1424 
1425                 // falls der uebergebene PaM nicht im Crsr-Ring steht,
1426                 // gesondert behandeln (z.B. Aufruf aus dem Auto-Format)
1427                 if( pOldTxtNd == rPam.GetBound( sal_True ).nContent.GetIdxReg() )
1428                     rPam.GetBound( sal_True ) = aAlphaPos;
1429                 if( pOldTxtNd == rPam.GetBound( sal_False ).nContent.GetIdxReg() )
1430                     rPam.GetBound( sal_False ) = aAlphaPos;
1431             }
1432             // jetzt nur noch den Node loeschen
1433             pDoc->GetNodes().Delete( aOldIdx, 1 );
1434         }
1435         else
1436         {
1437             SwTxtNode* pDelNd = aIdx.GetNode().GetTxtNode();
1438             if( pTxtNd->Len() )
1439                 pDelNd->FmtToTxtAttr( pTxtNd );
1440             else
1441             {
1442                 /* #107318# This case was missed:
1443 
1444                    <something></something>   <-- pTxtNd
1445                    <other>ccc</other>        <-- pDelNd
1446 
1447                    <something> and <other> are paragraph
1448                    attributes. The attribute <something> stayed if not
1449                    overwritten by an attribute in "ccc". Fixed by
1450                    first resetting all character attributes in first
1451                    paragraph (pTxtNd).
1452                 */
1453                 SvUShorts * pShorts =
1454                     lcl_RangesToUShorts(aCharFmtSetRange);
1455                 pTxtNd->ResetAttr(*pShorts);
1456                 delete pShorts;
1457 
1458                 if( pDelNd->HasSwAttrSet() )
1459                 {
1460                     // nur die Zeichenattribute kopieren
1461                     SfxItemSet aTmpSet( pDoc->GetAttrPool(), aCharFmtSetRange );
1462                     aTmpSet.Put( *pDelNd->GetpSwAttrSet() );
1463                     pTxtNd->SetAttr( aTmpSet );
1464                 }
1465             }
1466 
1467             pDoc->CorrRel( aIdx, *rPam.GetPoint(), 0, sal_True );
1468             // --> OD 2009-08-20 #i100466#
1469             // adjust given <rPam>, if it does not belong to the cursors
1470             if ( pDelNd == rPam.GetBound( sal_True ).nContent.GetIdxReg() )
1471             {
1472                 rPam.GetBound( sal_True ) = SwPosition( SwNodeIndex( *pTxtNd ), SwIndex( pTxtNd ) );
1473             }
1474             if( pDelNd == rPam.GetBound( sal_False ).nContent.GetIdxReg() )
1475             {
1476                 rPam.GetBound( sal_False ) = SwPosition( SwNodeIndex( *pTxtNd ), SwIndex( pTxtNd ) );
1477             }
1478             // <--
1479             pTxtNd->JoinNext();
1480         }
1481     }
1482 }
1483 
1484 static void
1485 lcl_CalcBreaks( ::std::vector<xub_StrLen> & rBreaks, SwPaM const & rPam )
1486 {
1487     SwTxtNode const * const pTxtNode(
1488             rPam.End()->nNode.GetNode().GetTxtNode() );
1489     if (!pTxtNode)
1490         return; // left-overlap only possible at end of selection...
1491 
1492     const xub_StrLen nStart(rPam.Start()->nContent.GetIndex());
1493     const xub_StrLen nEnd  (rPam.End  ()->nContent.GetIndex());
1494     if (nEnd == pTxtNode->Len())
1495         return; // paragraph selected until the end
1496 
1497     for (xub_StrLen i = nStart; i < nEnd; ++i)
1498     {
1499         const sal_Unicode c(pTxtNode->GetTxt().GetChar(i));
1500         if ((CH_TXTATR_INWORD == c) || (CH_TXTATR_BREAKWORD == c))
1501         {
1502             SwTxtAttr const * const pAttr( pTxtNode->GetTxtAttrForCharAt(i) );
1503             if (pAttr && pAttr->End() && (*pAttr->End() > nEnd))
1504             {
1505                 ASSERT(pAttr->HasDummyChar(), "GetTxtAttrForCharAt broken?");
1506                 rBreaks.push_back(i);
1507             }
1508         }
1509     }
1510 }
1511 
1512 bool lcl_DoWithBreaks(SwDoc & rDoc, SwPaM & rPam,
1513         bool (SwDoc::*pFunc)(SwPaM&, bool), const bool bForceJoinNext = false)
1514 {
1515     ::std::vector<xub_StrLen> Breaks;
1516 
1517     lcl_CalcBreaks(Breaks, rPam);
1518 
1519     if (!Breaks.size())
1520     {
1521         return (rDoc.*pFunc)(rPam, bForceJoinNext);
1522     }
1523 
1524     // N.B.: deletion must be split into several parts if the text node
1525     // contains a text attribute with end and with dummy character
1526     // and the selection does not contain the text attribute completely,
1527     // but overlaps its start (left), where the dummy character is.
1528 
1529     SwPosition const & rSelectionEnd( *rPam.End() );
1530 
1531     bool bRet( true );
1532     // iterate from end to start, to avoid invalidating the offsets!
1533     ::std::vector<xub_StrLen>::reverse_iterator iter( Breaks.rbegin() );
1534     SwPaM aPam( rSelectionEnd, rSelectionEnd ); // end node!
1535     SwPosition & rEnd( *aPam.End() );
1536     SwPosition & rStart( *aPam.Start() );
1537 
1538     while (iter != Breaks.rend())
1539     {
1540         rStart.nContent = *iter + 1;
1541         if (rEnd.nContent > rStart.nContent) // check if part is empty
1542         {
1543             bRet &= (rDoc.*pFunc)(aPam, bForceJoinNext);
1544         }
1545         rEnd.nContent = *iter;
1546         ++iter;
1547     }
1548 
1549     rStart = *rPam.Start(); // set to original start
1550     if (rEnd.nContent > rStart.nContent) // check if part is empty
1551     {
1552         bRet &= (rDoc.*pFunc)(aPam, bForceJoinNext);
1553     }
1554 
1555     return bRet;
1556 }
1557 
1558 
1559 bool SwDoc::DeleteAndJoinWithRedlineImpl( SwPaM & rPam, const bool )
1560 {
1561     ASSERT( IsRedlineOn(), "DeleteAndJoinWithRedline: redline off" );
1562 
1563     {
1564         SwUndoRedlineDelete* pUndo = 0;
1565         RedlineMode_t eOld = GetRedlineMode();
1566         checkRedlining( eOld );
1567         if (GetIDocumentUndoRedo().DoesUndo())
1568         {
1569 
1570             //JP 06.01.98: MUSS noch optimiert werden!!!
1571             SetRedlineMode(
1572                 (RedlineMode_t) ( nsRedlineMode_t::REDLINE_ON | nsRedlineMode_t::REDLINE_SHOW_INSERT | nsRedlineMode_t::REDLINE_SHOW_DELETE ) );
1573 
1574             GetIDocumentUndoRedo().StartUndo( UNDO_DELETE, NULL );
1575             pUndo = new SwUndoRedlineDelete( rPam, UNDO_DELETE );
1576             GetIDocumentUndoRedo().AppendUndo( pUndo );
1577         }
1578 
1579         if ( *rPam.GetPoint() != *rPam.GetMark() )
1580             AppendRedline( new SwRedline( nsRedlineType_t::REDLINE_DELETE, rPam ), true );
1581         SetModified();
1582 
1583         if ( pUndo )
1584         {
1585             GetIDocumentUndoRedo().EndUndo( UNDO_EMPTY, NULL );
1586             // ??? why the hell is the AppendUndo not below the
1587             // CanGrouping, so this hideous cleanup wouldn't be necessary?
1588             // bah, this is redlining, probably changing this would break it...
1589             if ( GetIDocumentUndoRedo().DoesGroupUndo() )
1590             {
1591                 SwUndo * const pLastUndo( GetUndoManager().GetLastUndo() );
1592                 SwUndoRedlineDelete * const pUndoRedlineDel( dynamic_cast< SwUndoRedlineDelete* >( pLastUndo ) );
1593                 if ( pUndoRedlineDel )
1594                 {
1595                     bool const bMerged = pUndoRedlineDel->CanGrouping( *pUndo );
1596                     if ( bMerged )
1597                     {
1598                         ::sw::UndoGuard const undoGuard( GetIDocumentUndoRedo() );
1599                         SwUndo const* const pDeleted = GetUndoManager().RemoveLastUndo();
1600                         OSL_ENSURE( pDeleted == pUndo, "DeleteAndJoinWithRedlineImpl: "
1601                             "undo removed is not undo inserted?" );
1602                         delete pDeleted;
1603                     }
1604                 }
1605             }
1606             //JP 06.01.98: MUSS noch optimiert werden!!!
1607             SetRedlineMode( eOld );
1608         }
1609         return true;
1610     }
1611 }
1612 
1613 bool SwDoc::DeleteAndJoinImpl( SwPaM & rPam,
1614                                const bool bForceJoinNext )
1615 {
1616     sal_Bool bJoinTxt, bJoinPrev;
1617     lcl_GetJoinFlags( rPam, bJoinTxt, bJoinPrev );
1618     // --> OD 2009-08-20 #i100466#
1619     if ( bForceJoinNext )
1620     {
1621         bJoinPrev = sal_False;
1622     }
1623     // <--
1624     {
1625         bool const bSuccess( DeleteRangeImpl( rPam ) );
1626         if (!bSuccess)
1627             return false;
1628     }
1629 
1630     if( bJoinTxt )
1631     {
1632         lcl_JoinText( rPam, bJoinPrev );
1633     }
1634 
1635     return true;
1636 }
1637 
1638 bool SwDoc::DeleteRangeImpl(SwPaM & rPam, const bool)
1639 {
1640     // move all cursors out of the deleted range.
1641     // but first copy the given PaM, because it could be a cursor that
1642     // would be moved!
1643     SwPaM aDelPam( *rPam.GetMark(), *rPam.GetPoint() );
1644     ::PaMCorrAbs( aDelPam, *aDelPam.GetPoint() );
1645 
1646     bool const bSuccess( DeleteRangeImplImpl( aDelPam ) );
1647     if (bSuccess)
1648     {   // now copy position from temp copy to given PaM
1649         *rPam.GetPoint() = *aDelPam.GetPoint();
1650     }
1651 
1652     return bSuccess;
1653 }
1654 
1655 bool SwDoc::DeleteRangeImplImpl(SwPaM & rPam)
1656 {
1657     SwPosition *pStt = (SwPosition*)rPam.Start(), *pEnd = (SwPosition*)rPam.End();
1658 
1659     if( !rPam.HasMark() || *pStt >= *pEnd )
1660         return false;
1661 
1662     if( pACEWord )
1663     {
1664         // ggfs. das gesicherte Word fuer die Ausnahme
1665         if( pACEWord->IsDeleted() ||  pStt->nNode != pEnd->nNode ||
1666             pStt->nContent.GetIndex() + 1 != pEnd->nContent.GetIndex() ||
1667             !pACEWord->CheckDelChar( *pStt ))
1668             delete pACEWord, pACEWord = 0;
1669     }
1670 
1671     {
1672         // loesche alle leeren TextHints an der Mark-Position
1673         SwTxtNode* pTxtNd = rPam.GetMark()->nNode.GetNode().GetTxtNode();
1674         SwpHints* pHts;
1675         if( pTxtNd &&  0 != ( pHts = pTxtNd->GetpSwpHints()) && pHts->Count() )
1676         {
1677             const xub_StrLen *pEndIdx;
1678             xub_StrLen nMkCntPos = rPam.GetMark()->nContent.GetIndex();
1679             for( sal_uInt16 n = pHts->Count(); n; )
1680             {
1681                 const SwTxtAttr* pAttr = (*pHts)[ --n ];
1682                 if( nMkCntPos > *pAttr->GetStart() )
1683                     break;
1684 
1685                 if( nMkCntPos == *pAttr->GetStart() &&
1686                     0 != (pEndIdx = pAttr->End()) &&
1687                     *pEndIdx == *pAttr->GetStart() )
1688                     pTxtNd->DestroyAttr( pHts->Cut( n ) );
1689             }
1690         }
1691     }
1692 
1693     {
1694         // Bug 26675:   DataChanged vorm loeschen verschicken, dann bekommt
1695         //          man noch mit, welche Objecte sich im Bereich befinden.
1696         //          Danach koennen sie vor/hinter der Position befinden.
1697         SwDataChanged aTmp( rPam, 0 );
1698     }
1699 
1700 
1701     if (GetIDocumentUndoRedo().DoesUndo())
1702     {
1703         GetIDocumentUndoRedo().ClearRedo();
1704         bool bMerged(false);
1705         if (GetIDocumentUndoRedo().DoesGroupUndo())
1706         {
1707             SwUndo *const pLastUndo( GetUndoManager().GetLastUndo() );
1708             SwUndoDelete *const pUndoDelete(
1709                     dynamic_cast<SwUndoDelete *>(pLastUndo) );
1710             if (pUndoDelete)
1711             {
1712                 bMerged = pUndoDelete->CanGrouping( this, rPam );
1713                 // if CanGrouping() returns true it's already merged
1714             }
1715         }
1716         if (!bMerged)
1717         {
1718             GetIDocumentUndoRedo().AppendUndo( new SwUndoDelete( rPam ) );
1719         }
1720 
1721         SetModified();
1722 
1723         return true;
1724     }
1725 
1726     if( !IsIgnoreRedline() && GetRedlineTbl().Count() )
1727         DeleteRedline( rPam, true, USHRT_MAX );
1728 
1729     // loesche und verschiebe erstmal alle "Fly's am Absatz", die in der
1730     // Selection liegen
1731     DelFlyInRange(rPam.GetMark()->nNode, rPam.GetPoint()->nNode);
1732     _DelBookmarks(
1733         pStt->nNode,
1734         pEnd->nNode,
1735         NULL,
1736         &pStt->nContent,
1737         &pEnd->nContent);
1738 
1739     SwNodeIndex aSttIdx( pStt->nNode );
1740     SwCntntNode * pCNd = aSttIdx.GetNode().GetCntntNode();
1741 
1742     do {        // middle checked loop!
1743         if( pCNd )
1744         {
1745             SwTxtNode * pStartTxtNode( pCNd->GetTxtNode() );
1746             if ( pStartTxtNode )
1747             {
1748                 // verschiebe jetzt noch den Inhalt in den neuen Node
1749                 sal_Bool bOneNd = pStt->nNode == pEnd->nNode;
1750                 xub_StrLen nLen = ( bOneNd ? pEnd->nContent.GetIndex()
1751                                            : pCNd->Len() )
1752                                         - pStt->nContent.GetIndex();
1753 
1754                 // falls schon leer, dann nicht noch aufrufen
1755                 if( nLen )
1756                 {
1757                     pStartTxtNode->EraseText( pStt->nContent, nLen );
1758 
1759                     if( !pStartTxtNode->Len() )
1760                     {
1761                 // METADATA: remove reference if empty (consider node deleted)
1762                         pStartTxtNode->RemoveMetadataReference();
1763                     }
1764                 }
1765 
1766                 if( bOneNd )        // das wars schon
1767                     break;
1768 
1769                 aSttIdx++;
1770             }
1771             else
1772             {
1773                 // damit beim loeschen keine Indizies mehr angemeldet sind,
1774                 // wird hier der SwPaM aus dem Content entfernt !!
1775                 pStt->nContent.Assign( 0, 0 );
1776             }
1777         }
1778 
1779         pCNd = pEnd->nNode.GetNode().GetCntntNode();
1780         if( pCNd )
1781         {
1782             SwTxtNode * pEndTxtNode( pCNd->GetTxtNode() );
1783             if( pEndTxtNode )
1784             {
1785                 // falls schon leer, dann nicht noch aufrufen
1786                 if( pEnd->nContent.GetIndex() )
1787                 {
1788                     SwIndex aIdx( pCNd, 0 );
1789                     pEndTxtNode->EraseText( aIdx, pEnd->nContent.GetIndex() );
1790 
1791                     if( !pEndTxtNode->Len() )
1792                     {
1793                 // METADATA: remove reference if empty (consider node deleted)
1794                         pEndTxtNode->RemoveMetadataReference();
1795                     }
1796                 }
1797             }
1798             else
1799             {
1800                 // damit beim Loeschen keine Indizies mehr angemeldet sind,
1801                 // wird hier der SwPaM aus dem Content entfernt !!
1802                 pEnd->nContent.Assign( 0, 0 );
1803             }
1804         }
1805 
1806         // if the end is not a content node, delete it as well
1807         sal_uInt32 nEnde = pEnd->nNode.GetIndex();
1808         if( pCNd == NULL )
1809             nEnde++;
1810 
1811         if( aSttIdx != nEnde )
1812         {
1813             // loesche jetzt die Nodes in das NodesArary
1814             GetNodes().Delete( aSttIdx, nEnde - aSttIdx.GetIndex() );
1815         }
1816 
1817         // falls der Node geloescht wurde, in dem der Cursor stand, so
1818         // muss der Content im akt. Content angemeldet werden !!!
1819         pStt->nContent.Assign( pStt->nNode.GetNode().GetCntntNode(),
1820                                 pStt->nContent.GetIndex() );
1821 
1822         // der PaM wird korrigiert, denn falls ueber Nodegrenzen geloescht
1823         // wurde, so stehen sie in unterschieden Nodes. Auch die Selektion
1824         // wird aufgehoben !
1825         *pEnd = *pStt;
1826         rPam.DeleteMark();
1827 
1828     } while( sal_False );
1829 
1830     if( !IsIgnoreRedline() && GetRedlineTbl().Count() )
1831         CompressRedlines();
1832     SetModified();
1833 
1834     return true;
1835 }
1836 
1837 // OD 2009-08-20 #i100466#
1838 // Add handling of new optional parameter <bForceJoinNext>
1839 bool SwDoc::DeleteAndJoin( SwPaM & rPam,
1840                            const bool bForceJoinNext )
1841 {
1842     if ( lcl_StrLenOverFlow( rPam ) )
1843         return false;
1844 
1845     return lcl_DoWithBreaks( *this, rPam, (IsRedlineOn())
1846                 ? &SwDoc::DeleteAndJoinWithRedlineImpl
1847                 : &SwDoc::DeleteAndJoinImpl,
1848                 bForceJoinNext );
1849 }
1850 
1851 bool SwDoc::DeleteRange( SwPaM & rPam )
1852 {
1853     return lcl_DoWithBreaks( *this, rPam, &SwDoc::DeleteRangeImpl );
1854 }
1855 
1856 
1857 void lcl_syncGrammarError( SwTxtNode &rTxtNode, linguistic2::ProofreadingResult& rResult,
1858     xub_StrLen /*nBeginGrammarCheck*/, const ModelToViewHelper::ConversionMap* pConversionMap )
1859 {
1860     if( rTxtNode.IsGrammarCheckDirty() )
1861         return;
1862     SwGrammarMarkUp* pWrong = rTxtNode.GetGrammarCheck();
1863     linguistic2::SingleProofreadingError* pArray = rResult.aErrors.getArray();
1864     sal_uInt16 i, j = 0;
1865     if( pWrong )
1866     {
1867         for( i = 0; i < rResult.aErrors.getLength(); ++i )
1868         {
1869             const linguistic2::SingleProofreadingError &rError = rResult.aErrors[i];
1870             xub_StrLen nStart = (xub_StrLen)ModelToViewHelper::ConvertToModelPosition( pConversionMap, rError.nErrorStart ).mnPos;
1871             xub_StrLen nEnd = (xub_StrLen)ModelToViewHelper::ConvertToModelPosition( pConversionMap, rError.nErrorStart + rError.nErrorLength ).mnPos;
1872             if( i != j )
1873                 pArray[j] = pArray[i];
1874             if( pWrong->LookForEntry( nStart, nEnd ) )
1875                 ++j;
1876         }
1877     }
1878     if( rResult.aErrors.getLength() > j )
1879         rResult.aErrors.realloc( j );
1880 }
1881 
1882 
1883 uno::Any SwDoc::Spell( SwPaM& rPaM,
1884                     uno::Reference< XSpellChecker1 >  &xSpeller,
1885                     sal_uInt16* pPageCnt, sal_uInt16* pPageSt,
1886                     bool bGrammarCheck,
1887                     SwConversionArgs *pConvArgs  ) const
1888 {
1889     SwPosition* pSttPos = rPaM.Start(), *pEndPos = rPaM.End();
1890     uno::Reference< beans::XPropertySet >  xProp( ::GetLinguPropertySet() );
1891 
1892     SwSpellArgs      *pSpellArgs = 0;
1893     //SwConversionArgs *pConvArgs  = 0;
1894     if (pConvArgs)
1895     {
1896         pConvArgs->SetStart(pSttPos->nNode.GetNode().GetTxtNode(), pSttPos->nContent);
1897         pConvArgs->SetEnd(  pEndPos->nNode.GetNode().GetTxtNode(), pEndPos->nContent );
1898     }
1899     else
1900         pSpellArgs = new SwSpellArgs( xSpeller,
1901                             pSttPos->nNode.GetNode().GetTxtNode(), pSttPos->nContent,
1902                             pEndPos->nNode.GetNode().GetTxtNode(), pEndPos->nContent,
1903                             bGrammarCheck );
1904 
1905     sal_uLong nCurrNd = pSttPos->nNode.GetIndex();
1906     sal_uLong nEndNd = pEndPos->nNode.GetIndex();
1907 
1908     uno::Any aRet;
1909     if( nCurrNd <= nEndNd )
1910     {
1911         SwCntntFrm* pCntFrm;
1912         sal_Bool bGoOn = sal_True;
1913         while( bGoOn )
1914         {
1915             SwNode* pNd = GetNodes()[ nCurrNd ];
1916             switch( pNd->GetNodeType() )
1917             {
1918             case ND_TEXTNODE:
1919                 if( 0 != ( pCntFrm = ((SwTxtNode*)pNd)->getLayoutFrm( GetCurrentLayout() )) )
1920                 {
1921                     // geschutze Cellen/Flys ueberspringen, ausgeblendete
1922                     //ebenfalls
1923                     if( pCntFrm->IsProtected() )
1924                     {
1925                         nCurrNd = pNd->EndOfSectionIndex();
1926                     }
1927                     else if( !((SwTxtFrm*)pCntFrm)->IsHiddenNow() )
1928                     {
1929                         if( pPageCnt && *pPageCnt && pPageSt )
1930                         {
1931                             sal_uInt16 nPageNr = pCntFrm->GetPhyPageNum();
1932                             if( !*pPageSt )
1933                             {
1934                                 *pPageSt = nPageNr;
1935                                 if( *pPageCnt < *pPageSt )
1936                                     *pPageCnt = *pPageSt;
1937                             }
1938                             long nStat;
1939                             if( nPageNr >= *pPageSt )
1940                                 nStat = nPageNr - *pPageSt + 1;
1941                             else
1942                                 nStat = nPageNr + *pPageCnt - *pPageSt + 1;
1943                             ::SetProgressState( nStat, (SwDocShell*)GetDocShell() );
1944                         }
1945                         //Spell() changes the pSpellArgs in case an error is found
1946                         xub_StrLen nBeginGrammarCheck = 0;
1947                         xub_StrLen nEndGrammarCheck = 0;
1948                         if( pSpellArgs && pSpellArgs->bIsGrammarCheck)
1949                         {
1950                             nBeginGrammarCheck = pSpellArgs->pStartNode == pNd ?  pSpellArgs->pStartIdx->GetIndex() : 0;
1951                             // if grammar checking starts inside of a sentence the start position has to be adjusted
1952                             if( nBeginGrammarCheck )
1953                             {
1954                                 SwIndex aStartIndex( dynamic_cast< SwTxtNode* >( pNd ), nBeginGrammarCheck );
1955                                 SwPosition aStart( *pNd, aStartIndex );
1956                                 SwCursor aCrsr(aStart, 0, false);
1957                                 SwPosition aOrigPos = *aCrsr.GetPoint();
1958                                 aCrsr.GoSentence( SwCursor::START_SENT );
1959                                 if( aOrigPos != *aCrsr.GetPoint() )
1960                                 {
1961                                     nBeginGrammarCheck = aCrsr.GetPoint()->nContent.GetIndex();
1962                                 }
1963                             }
1964                             nEndGrammarCheck = pSpellArgs->pEndNode == pNd ? pSpellArgs->pEndIdx->GetIndex() : ((SwTxtNode*)pNd)->GetTxt().Len();
1965                         }
1966 
1967                         xub_StrLen nSpellErrorPosition = ((SwTxtNode*)pNd)->GetTxt().Len();
1968                         if( (!pConvArgs &&
1969                                 ((SwTxtNode*)pNd)->Spell( pSpellArgs )) ||
1970                             ( pConvArgs &&
1971                                 ((SwTxtNode*)pNd)->Convert( *pConvArgs )))
1972                         {
1973                             // Abbrechen und Position merken
1974                             pSttPos->nNode = nCurrNd;
1975                             pEndPos->nNode = nCurrNd;
1976                             nCurrNd = nEndNd;
1977                             if( pSpellArgs )
1978                                 nSpellErrorPosition = pSpellArgs->pStartIdx->GetIndex() > pSpellArgs->pEndIdx->GetIndex() ?
1979                                             pSpellArgs->pEndIdx->GetIndex() :
1980                                             pSpellArgs->pStartIdx->GetIndex();
1981                         }
1982 
1983 
1984                         if( pSpellArgs && pSpellArgs->bIsGrammarCheck )
1985                         {
1986                             uno::Reference< linguistic2::XProofreadingIterator >  xGCIterator( GetGCIterator() );
1987                             if (xGCIterator.is())
1988                             {
1989                                 String aText( ((SwTxtNode*)pNd)->GetTxt().Copy( nBeginGrammarCheck, nEndGrammarCheck - nBeginGrammarCheck ) );
1990                                 uno::Reference< lang::XComponent > xDoc( ((SwDocShell*)GetDocShell())->GetBaseModel(), uno::UNO_QUERY );
1991                                 // Expand the string:
1992                                 rtl::OUString aExpandText;
1993                                 const ModelToViewHelper::ConversionMap* pConversionMap =
1994                                         ((SwTxtNode*)pNd)->BuildConversionMap( aExpandText );
1995                                 // get XFlatParagraph to use...
1996                                 uno::Reference< text::XFlatParagraph > xFlatPara = new SwXFlatParagraph( *((SwTxtNode*)pNd), aExpandText, pConversionMap );
1997 
1998                                 // get error position of cursor in XFlatParagraph
1999                                 sal_Int32 nGrammarErrorPosInText;
2000                                 linguistic2::ProofreadingResult aResult;
2001                                 sal_Int32 nGrammarErrors;
2002                                 do
2003                                 {
2004                                     nGrammarErrorPosInText = ModelToViewHelper::ConvertToViewPosition( pConversionMap, nBeginGrammarCheck );
2005                                     aResult = xGCIterator->checkSentenceAtPosition(
2006                                             xDoc, xFlatPara, aExpandText, lang::Locale(), nBeginGrammarCheck, -1, -1 );
2007 
2008                                     lcl_syncGrammarError( *((SwTxtNode*)pNd), aResult, nBeginGrammarCheck, pConversionMap );
2009 
2010                                     // get suggestions to use for the specific error position
2011                                     nGrammarErrors = aResult.aErrors.getLength();
2012                                     // if grammar checking doesn't have any progress then quit
2013                                     if( aResult.nStartOfNextSentencePosition <= nBeginGrammarCheck )
2014                                         break;
2015                                     // prepare next iteration
2016                                     nBeginGrammarCheck = (xub_StrLen)aResult.nStartOfNextSentencePosition;
2017                                 }
2018                                 while( nSpellErrorPosition > aResult.nBehindEndOfSentencePosition && !nGrammarErrors && aResult.nBehindEndOfSentencePosition < nEndGrammarCheck );
2019 
2020                                 if( nGrammarErrors > 0 && nSpellErrorPosition >= aResult.nBehindEndOfSentencePosition )
2021                                 {
2022                                     aRet <<= aResult;
2023                                     //put the cursor to the current error
2024                                     const linguistic2::SingleProofreadingError &rError = aResult.aErrors[0];
2025                                     nCurrNd = pNd->GetIndex();
2026                                     pSttPos->nNode = nCurrNd;
2027                                     pEndPos->nNode = nCurrNd;
2028                                     pSpellArgs->pStartNode = ((SwTxtNode*)pNd);
2029                                     pSpellArgs->pEndNode = ((SwTxtNode*)pNd);
2030                                     pSpellArgs->pStartIdx->Assign(((SwTxtNode*)pNd), (xub_StrLen)ModelToViewHelper::ConvertToModelPosition( pConversionMap, rError.nErrorStart ).mnPos );
2031                                     pSpellArgs->pEndIdx->Assign(((SwTxtNode*)pNd), (xub_StrLen)ModelToViewHelper::ConvertToModelPosition( pConversionMap, rError.nErrorStart + rError.nErrorLength ).mnPos );
2032                                     nCurrNd = nEndNd;
2033                                 }
2034                             }
2035                         }
2036                     }
2037                 }
2038                 break;
2039             case ND_SECTIONNODE:
2040                 if( ( ((SwSectionNode*)pNd)->GetSection().IsProtect() ||
2041                     ((SwSectionNode*)pNd)->GetSection().IsHidden() ) )
2042                     nCurrNd = pNd->EndOfSectionIndex();
2043                 break;
2044             case ND_ENDNODE:
2045                 {
2046                     break;
2047                 }
2048             }
2049 
2050             bGoOn = nCurrNd < nEndNd;
2051             ++nCurrNd;
2052         }
2053     }
2054 
2055     if( !aRet.hasValue() )
2056     {
2057         if (pConvArgs)
2058             aRet <<= pConvArgs->aConvText;
2059         else
2060             aRet <<= pSpellArgs->xSpellAlt;
2061     }
2062     delete pSpellArgs;
2063 
2064     return aRet;
2065 }
2066 
2067 class SwHyphArgs : public SwInterHyphInfo
2068 {
2069     const SwNode *pStart;
2070     const SwNode *pEnd;
2071           SwNode *pNode;
2072     sal_uInt16 *pPageCnt;
2073     sal_uInt16 *pPageSt;
2074 
2075     sal_uInt32 nNode;
2076     xub_StrLen nPamStart;
2077     xub_StrLen nPamLen;
2078 
2079 public:
2080          SwHyphArgs( const SwPaM *pPam, const Point &rPoint,
2081                          sal_uInt16* pPageCount, sal_uInt16* pPageStart );
2082     void SetPam( SwPaM *pPam ) const;
2083     inline void SetNode( SwNode *pNew ) { pNode = pNew; }
2084     inline const SwNode *GetNode() const { return pNode; }
2085     inline void SetRange( const SwNode *pNew );
2086     inline void NextNode() { ++nNode; }
2087     inline sal_uInt16 *GetPageCnt() { return pPageCnt; }
2088     inline sal_uInt16 *GetPageSt() { return pPageSt; }
2089 };
2090 
2091 SwHyphArgs::SwHyphArgs( const SwPaM *pPam, const Point &rCrsrPos,
2092                          sal_uInt16* pPageCount, sal_uInt16* pPageStart )
2093      : SwInterHyphInfo( rCrsrPos ), pNode(0),
2094      pPageCnt( pPageCount ), pPageSt( pPageStart )
2095 {
2096     // Folgende Bedingungen muessen eingehalten werden:
2097     // 1) es gibt mindestens eine Selektion
2098     // 2) SPoint() == Start()
2099     ASSERT( pPam->HasMark(), "SwDoc::Hyphenate: blowing in the wind");
2100     ASSERT( *pPam->GetPoint() <= *pPam->GetMark(),
2101             "SwDoc::Hyphenate: New York, New York");
2102 
2103     const SwPosition *pPoint = pPam->GetPoint();
2104     nNode = pPoint->nNode.GetIndex();
2105 
2106     // Start einstellen
2107     pStart = pPoint->nNode.GetNode().GetTxtNode();
2108     nPamStart = pPoint->nContent.GetIndex();
2109 
2110     // Ende und Laenge einstellen.
2111     const SwPosition *pMark = pPam->GetMark();
2112     pEnd = pMark->nNode.GetNode().GetTxtNode();
2113     nPamLen = pMark->nContent.GetIndex();
2114     if( pPoint->nNode == pMark->nNode )
2115         nPamLen = nPamLen - pPoint->nContent.GetIndex();
2116 }
2117 
2118 inline void SwHyphArgs::SetRange( const SwNode *pNew )
2119 {
2120     nStart = pStart == pNew ? nPamStart : 0;
2121     nLen   = pEnd   == pNew ? nPamLen : STRING_NOTFOUND;
2122 }
2123 
2124 void SwHyphArgs::SetPam( SwPaM *pPam ) const
2125 {
2126     if( !pNode )
2127         *pPam->GetPoint() = *pPam->GetMark();
2128     else
2129     {
2130         pPam->GetPoint()->nNode = nNode;
2131         pPam->GetPoint()->nContent.Assign( pNode->GetCntntNode(), nWordStart );
2132         pPam->GetMark()->nNode = nNode;
2133         pPam->GetMark()->nContent.Assign( pNode->GetCntntNode(),
2134                                           nWordStart + nWordLen );
2135         ASSERT( nNode == pNode->GetIndex(),
2136                 "SwHyphArgs::SetPam: Pam desaster" );
2137     }
2138 }
2139 
2140 // liefert sal_True zurueck, wenn es weitergehen soll.
2141 sal_Bool lcl_HyphenateNode( const SwNodePtr& rpNd, void* pArgs )
2142 {
2143     // Hyphenate liefert sal_True zurueck, wenn eine Trennstelle anliegt
2144     // und stellt pPam ein.
2145     SwTxtNode *pNode = rpNd->GetTxtNode();
2146     SwHyphArgs *pHyphArgs = (SwHyphArgs*)pArgs;
2147     if( pNode )
2148     {
2149         SwCntntFrm* pCntFrm = pNode->getLayoutFrm( pNode->GetDoc()->GetCurrentLayout() );
2150         if( pCntFrm && !((SwTxtFrm*)pCntFrm)->IsHiddenNow() )
2151         {
2152             sal_uInt16 *pPageSt = pHyphArgs->GetPageSt();
2153             sal_uInt16 *pPageCnt = pHyphArgs->GetPageCnt();
2154             if( pPageCnt && *pPageCnt && pPageSt )
2155             {
2156                 sal_uInt16 nPageNr = pCntFrm->GetPhyPageNum();
2157                 if( !*pPageSt )
2158                 {
2159                     *pPageSt = nPageNr;
2160                     if( *pPageCnt < *pPageSt )
2161                         *pPageCnt = *pPageSt;
2162                 }
2163                 long nStat = nPageNr >= *pPageSt ? nPageNr - *pPageSt + 1
2164                                          : nPageNr + *pPageCnt - *pPageSt + 1;
2165                 ::SetProgressState( nStat, (SwDocShell*)pNode->GetDoc()->GetDocShell() );
2166             }
2167             pHyphArgs->SetRange( rpNd );
2168             if( pNode->Hyphenate( *pHyphArgs ) )
2169             {
2170                 pHyphArgs->SetNode( rpNd );
2171                 return sal_False;
2172             }
2173         }
2174     }
2175     pHyphArgs->NextNode();
2176     return sal_True;
2177 }
2178 
2179 uno::Reference< XHyphenatedWord >  SwDoc::Hyphenate(
2180                             SwPaM *pPam, const Point &rCrsrPos,
2181                             sal_uInt16* pPageCnt, sal_uInt16* pPageSt )
2182 {
2183     ASSERT(this == pPam->GetDoc(), "SwDoc::Hyphenate: strangers in the night");
2184 
2185     if( *pPam->GetPoint() > *pPam->GetMark() )
2186         pPam->Exchange();
2187 
2188     SwHyphArgs aHyphArg( pPam, rCrsrPos, pPageCnt, pPageSt );
2189     SwNodeIndex aTmpIdx( pPam->GetMark()->nNode, 1 );
2190     GetNodes().ForEach( pPam->GetPoint()->nNode, aTmpIdx,
2191                     lcl_HyphenateNode, &aHyphArg );
2192     aHyphArg.SetPam( pPam );
2193     return aHyphArg.GetHyphWord();  // will be set by lcl_HyphenateNode
2194 }
2195 
2196 
2197 sal_Bool lcl_GetTokenToParaBreak( String& rStr, String& rRet, sal_Bool bRegExpRplc )
2198 {
2199     sal_Bool bRet = sal_False;
2200     if( bRegExpRplc )
2201     {
2202         xub_StrLen nPos = 0;
2203         String sPara( String::CreateFromAscii(
2204                                     RTL_CONSTASCII_STRINGPARAM( "\\n" )));
2205         while( STRING_NOTFOUND != ( nPos = rStr.Search( sPara, nPos )) )
2206         {
2207             // wurde das escaped?
2208             if( nPos && '\\' == rStr.GetChar( nPos-1 ))
2209             {
2210                 if( ++nPos >= rStr.Len() )
2211                     break;
2212             }
2213             else
2214             {
2215                 rRet = rStr.Copy( 0, nPos );
2216                 rStr.Erase( 0, nPos + sPara.Len() );
2217                 bRet = sal_True;
2218                 break;
2219             }
2220         }
2221     }
2222     if( !bRet )
2223     {
2224         rRet = rStr;
2225         rStr.Erase();
2226     }
2227     return bRet;
2228 }
2229 
2230 bool SwDoc::ReplaceRange( SwPaM& rPam, const String& rStr,
2231         const bool bRegExReplace )
2232 {
2233     // unfortunately replace works slightly differently from delete,
2234     // so we cannot use lcl_DoWithBreaks here...
2235 
2236     ::std::vector<xub_StrLen> Breaks;
2237 
2238     SwPaM aPam( *rPam.GetMark(), *rPam.GetPoint() );
2239     aPam.Normalize(sal_False);
2240     if (aPam.GetPoint()->nNode != aPam.GetMark()->nNode)
2241     {
2242         aPam.Move(fnMoveBackward);
2243     }
2244     ASSERT((aPam.GetPoint()->nNode == aPam.GetMark()->nNode), "invalid pam?");
2245 
2246     lcl_CalcBreaks(Breaks, aPam);
2247 
2248     while (!Breaks.empty() // skip over prefix of dummy chars
2249             && (aPam.GetMark()->nContent.GetIndex() == *Breaks.begin()) )
2250     {
2251         // skip!
2252         ++aPam.GetMark()->nContent; // always in bounds if Breaks valid
2253         Breaks.erase(Breaks.begin());
2254     }
2255     *rPam.Start() = *aPam.GetMark(); // update start of original pam w/ prefix
2256 
2257     if (!Breaks.size())
2258     {
2259         return ReplaceRangeImpl(rPam, rStr, bRegExReplace); // original pam!
2260     }
2261 
2262     // N.B.: deletion must be split into several parts if the text node
2263     // contains a text attribute with end and with dummy character
2264     // and the selection does not contain the text attribute completely,
2265     // but overlaps its start (left), where the dummy character is.
2266 
2267     bool bRet( true );
2268     // iterate from end to start, to avoid invalidating the offsets!
2269     ::std::vector<xub_StrLen>::reverse_iterator iter( Breaks.rbegin() );
2270     ASSERT(aPam.GetPoint() == aPam.End(), "wrong!");
2271     SwPosition & rEnd( *aPam.End() );
2272     SwPosition & rStart( *aPam.Start() );
2273 
2274     // set end of temp pam to original end (undo Move backward above)
2275     rEnd = *rPam.End();
2276     // after first deletion, rEnd will point into the original text node again!
2277 
2278     while (iter != Breaks.rend())
2279     {
2280         rStart.nContent = *iter + 1;
2281         if (rEnd.nContent != rStart.nContent) // check if part is empty
2282         {
2283             bRet &= (IsRedlineOn())
2284                 ? DeleteAndJoinWithRedlineImpl(aPam)
2285                 : DeleteAndJoinImpl(aPam, false);
2286         }
2287         rEnd.nContent = *iter;
2288         ++iter;
2289     }
2290 
2291     rStart = *rPam.Start(); // set to original start
2292     ASSERT(rEnd.nContent > rStart.nContent, "replace part empty!");
2293     if (rEnd.nContent > rStart.nContent) // check if part is empty
2294     {
2295         bRet &= ReplaceRangeImpl(aPam, rStr, bRegExReplace);
2296     }
2297 
2298     rPam = aPam; // update original pam (is this required?)
2299 
2300     return bRet;
2301 }
2302 
2303 // N.B.: it is possible to call Replace with a PaM that spans 2 paragraphs:
2304 // search with regex for "$", then replace _all_
2305 bool SwDoc::ReplaceRangeImpl( SwPaM& rPam, const String& rStr,
2306         const bool bRegExReplace )
2307 {
2308     if( !rPam.HasMark() || *rPam.GetPoint() == *rPam.GetMark() )
2309         return false;
2310 
2311     sal_Bool bJoinTxt, bJoinPrev;
2312     lcl_GetJoinFlags( rPam, bJoinTxt, bJoinPrev );
2313 
2314     {
2315         // dann eine Kopie vom Cursor erzeugen um alle Pams aus den
2316         // anderen Sichten aus dem Loeschbereich zu verschieben
2317         // ABER NICHT SICH SELBST !!
2318         SwPaM aDelPam( *rPam.GetMark(), *rPam.GetPoint() );
2319         ::PaMCorrAbs( aDelPam, *aDelPam.GetPoint() );
2320 
2321         SwPosition *pStt = (SwPosition*)aDelPam.Start(),
2322                    *pEnd = (SwPosition*)aDelPam.End();
2323         ASSERT( pStt->nNode == pEnd->nNode ||
2324                 ( pStt->nNode.GetIndex() + 1 == pEnd->nNode.GetIndex() &&
2325                     !pEnd->nContent.GetIndex() ),
2326                 "invalid range: Point and Mark on different nodes" );
2327         sal_Bool bOneNode = pStt->nNode == pEnd->nNode;
2328 
2329         // eigenes Undo ????
2330         String sRepl( rStr );
2331         SwTxtNode* pTxtNd = pStt->nNode.GetNode().GetTxtNode();
2332         xub_StrLen nStt = pStt->nContent.GetIndex(),
2333                 nEnd = bOneNode ? pEnd->nContent.GetIndex()
2334                                 : pTxtNd->GetTxt().Len();
2335 
2336         SwDataChanged aTmp( aDelPam, 0 );
2337 
2338         if( IsRedlineOn() )
2339         {
2340             RedlineMode_t eOld = GetRedlineMode();
2341             checkRedlining(eOld);
2342             if (GetIDocumentUndoRedo().DoesUndo())
2343             {
2344                 GetIDocumentUndoRedo().StartUndo(UNDO_EMPTY, NULL);
2345 
2346                 // Bug 68584 - if any Redline will change (split!) the node
2347                 const ::sw::mark::IMark* pBkmk = getIDocumentMarkAccess()->makeMark( aDelPam, ::rtl::OUString(), IDocumentMarkAccess::UNO_BOOKMARK );
2348 
2349                 //JP 06.01.98: MUSS noch optimiert werden!!!
2350                 SetRedlineMode(
2351                     (RedlineMode_t)(nsRedlineMode_t::REDLINE_ON | nsRedlineMode_t::REDLINE_SHOW_INSERT | nsRedlineMode_t::REDLINE_SHOW_DELETE ));
2352 
2353                 *aDelPam.GetPoint() = pBkmk->GetMarkPos();
2354                 if(pBkmk->IsExpanded())
2355                     *aDelPam.GetMark() = pBkmk->GetOtherMarkPos();
2356                 getIDocumentMarkAccess()->deleteMark(pBkmk);
2357                 pStt = aDelPam.Start();
2358                 pTxtNd = pStt->nNode.GetNode().GetTxtNode();
2359                 nStt = pStt->nContent.GetIndex();
2360             }
2361 
2362             if( sRepl.Len() )
2363             {
2364                 // Attribute des 1. Zeichens ueber den ReplaceText setzen
2365                 SfxItemSet aSet( GetAttrPool(),
2366                             RES_CHRATR_BEGIN,     RES_TXTATR_WITHEND_END - 1,
2367                             RES_UNKNOWNATR_BEGIN, RES_UNKNOWNATR_END-1,
2368                             0 );
2369                 pTxtNd->GetAttr( aSet, nStt+1, nStt+1 );
2370 
2371                 aSet.ClearItem( RES_TXTATR_REFMARK );
2372                 aSet.ClearItem( RES_TXTATR_TOXMARK );
2373                 aSet.ClearItem( RES_TXTATR_CJK_RUBY );
2374                 aSet.ClearItem( RES_TXTATR_INETFMT );
2375                 aSet.ClearItem( RES_TXTATR_META );
2376                 aSet.ClearItem( RES_TXTATR_METAFIELD );
2377 
2378                 if( aDelPam.GetPoint() != aDelPam.End() )
2379                     aDelPam.Exchange();
2380 
2381                 // das Ende merken
2382                 SwNodeIndex aPtNd( aDelPam.GetPoint()->nNode, -1 );
2383                 xub_StrLen nPtCnt = aDelPam.GetPoint()->nContent.GetIndex();
2384 
2385                 sal_Bool bFirst = sal_True;
2386                 String sIns;
2387                 while ( lcl_GetTokenToParaBreak( sRepl, sIns, bRegExReplace ) )
2388                 {
2389                     InsertString( aDelPam, sIns );
2390                     if( bFirst )
2391                     {
2392                         SwNodeIndex aMkNd( aDelPam.GetMark()->nNode, -1 );
2393                         xub_StrLen nMkCnt = aDelPam.GetMark()->nContent.GetIndex();
2394 
2395                         SplitNode( *aDelPam.GetPoint(), false );
2396 
2397                         aMkNd++;
2398                         aDelPam.GetMark()->nNode = aMkNd;
2399                         aDelPam.GetMark()->nContent.Assign(
2400                                     aMkNd.GetNode().GetCntntNode(), nMkCnt );
2401                         bFirst = sal_False;
2402                     }
2403                     else
2404                         SplitNode( *aDelPam.GetPoint(), false );
2405                 }
2406                 if( sIns.Len() )
2407                 {
2408                     InsertString( aDelPam, sIns );
2409                 }
2410 
2411                 SwPaM aTmpRange( *aDelPam.GetPoint() );
2412                 aTmpRange.SetMark();
2413 
2414                 aPtNd++;
2415                 aDelPam.GetPoint()->nNode = aPtNd;
2416                 aDelPam.GetPoint()->nContent.Assign( aPtNd.GetNode().GetCntntNode(),
2417                                                     nPtCnt);
2418                 *aTmpRange.GetMark() = *aDelPam.GetPoint();
2419 
2420                 RstTxtAttrs( aTmpRange );
2421                 InsertItemSet( aTmpRange, aSet, 0 );
2422             }
2423 
2424             if (GetIDocumentUndoRedo().DoesUndo())
2425             {
2426                 SwUndo *const pUndoRD =
2427                     new SwUndoRedlineDelete( aDelPam, UNDO_REPLACE );
2428                 GetIDocumentUndoRedo().AppendUndo(pUndoRD);
2429             }
2430             AppendRedline( new SwRedline( nsRedlineType_t::REDLINE_DELETE, aDelPam ), true);
2431 
2432             *rPam.GetMark() = *aDelPam.GetMark();
2433             if (GetIDocumentUndoRedo().DoesUndo())
2434             {
2435                 *aDelPam.GetPoint() = *rPam.GetPoint();
2436                 GetIDocumentUndoRedo().EndUndo(UNDO_EMPTY, NULL);
2437 
2438                 // Bug 68584 - if any Redline will change (split!) the node
2439                 const ::sw::mark::IMark* pBkmk = getIDocumentMarkAccess()->makeMark( aDelPam, ::rtl::OUString(), IDocumentMarkAccess::UNO_BOOKMARK );
2440 
2441                 SwIndex& rIdx = aDelPam.GetPoint()->nContent;
2442                 rIdx.Assign( 0, 0 );
2443                 aDelPam.GetMark()->nContent = rIdx;
2444                 rPam.GetPoint()->nNode = 0;
2445                 rPam.GetPoint()->nContent = rIdx;
2446                 *rPam.GetMark() = *rPam.GetPoint();
2447 //JP 06.01.98: MUSS noch optimiert werden!!!
2448 SetRedlineMode( eOld );
2449 
2450                 *rPam.GetPoint() = pBkmk->GetMarkPos();
2451                 if(pBkmk->IsExpanded())
2452                     *rPam.GetMark() = pBkmk->GetOtherMarkPos();
2453                 getIDocumentMarkAccess()->deleteMark(pBkmk);
2454             }
2455             bJoinTxt = sal_False;
2456         }
2457         else
2458         {
2459             if( !IsIgnoreRedline() && GetRedlineTbl().Count() )
2460                 DeleteRedline( aDelPam, true, USHRT_MAX );
2461 
2462             SwUndoReplace* pUndoRpl = 0;
2463             bool const bDoesUndo = GetIDocumentUndoRedo().DoesUndo();
2464             if (bDoesUndo)
2465             {
2466                 pUndoRpl = new SwUndoReplace(aDelPam, sRepl, bRegExReplace);
2467                 GetIDocumentUndoRedo().AppendUndo(pUndoRpl);
2468             }
2469             ::sw::UndoGuard const undoGuard(GetIDocumentUndoRedo());
2470 
2471             if( aDelPam.GetPoint() != pStt )
2472                 aDelPam.Exchange();
2473 
2474             SwNodeIndex aPtNd( pStt->nNode, -1 );
2475             xub_StrLen nPtCnt = pStt->nContent.GetIndex();
2476 
2477             // die Werte nochmal setzen, falls schohn Rahmen oder Fussnoten
2478             // auf dem Text entfernt wurden!
2479             nStt = nPtCnt;
2480             nEnd = bOneNode ? pEnd->nContent.GetIndex()
2481                             : pTxtNd->GetTxt().Len();
2482 
2483             sal_Bool bFirst = sal_True;
2484             String sIns;
2485             while ( lcl_GetTokenToParaBreak( sRepl, sIns, bRegExReplace ) )
2486             {
2487                 if( !bFirst || nStt == pTxtNd->GetTxt().Len() )
2488                 {
2489                     InsertString( aDelPam, sIns );
2490                 }
2491                 else if( nStt < nEnd || sIns.Len() )
2492                 {
2493                     pTxtNd->ReplaceText( pStt->nContent, nEnd - nStt, sIns );
2494                 }
2495                 SplitNode( *pStt, false);
2496                 bFirst = sal_False;
2497             }
2498 
2499             if( bFirst || sIns.Len() )
2500             {
2501                 if( !bFirst || nStt == pTxtNd->GetTxt().Len() )
2502                 {
2503                     InsertString( aDelPam, sIns );
2504                 }
2505                 else if( nStt < nEnd || sIns.Len() )
2506                 {
2507                     pTxtNd->ReplaceText( pStt->nContent, nEnd - nStt, sIns );
2508                 }
2509             }
2510 
2511             *rPam.GetMark() = *aDelPam.GetMark();
2512 
2513             aPtNd++;
2514             rPam.GetMark()->nNode = aPtNd;
2515             rPam.GetMark()->nContent.Assign( aPtNd.GetNode().GetCntntNode(),
2516                                                 nPtCnt );
2517 
2518             if ( bJoinTxt && !bJoinPrev )
2519             {
2520                 rPam.Move( fnMoveBackward );
2521             }
2522 
2523             if( pUndoRpl )
2524             {
2525                 pUndoRpl->SetEnd(rPam);
2526             }
2527         }
2528     }
2529 
2530     if( bJoinTxt )
2531         lcl_JoinText( rPam, bJoinPrev );
2532 
2533     SetModified();
2534     return true;
2535 }
2536 
2537     // speicher die akt. Werte fuer die automatische Aufnahme von Ausnahmen
2538     // in die Autokorrektur
2539 void SwDoc::SetAutoCorrExceptWord( SwAutoCorrExceptWord* pNew )
2540 {
2541     if( pACEWord && pNew != pACEWord )
2542         delete pACEWord;
2543     pACEWord = pNew;
2544 }
2545 
2546 bool SwDoc::DelFullPara( SwPaM& rPam )
2547 {
2548     const SwPosition &rStt = *rPam.Start(), &rEnd = *rPam.End();
2549     const SwNode* pNd = &rStt.nNode.GetNode();
2550     sal_uInt32 nSectDiff = pNd->StartOfSectionNode()->EndOfSectionIndex() -
2551                         pNd->StartOfSectionIndex();
2552     sal_uInt32 nNodeDiff = rEnd.nNode.GetIndex() - rStt.nNode.GetIndex();
2553 
2554     if ( nSectDiff-2 <= nNodeDiff || IsRedlineOn() ||
2555          /* #i9185# Prevent getting the node after the end node (see below) */
2556         rEnd.nNode.GetIndex() + 1 == GetNodes().Count() )
2557     {
2558         return sal_False;
2559     }
2560 
2561     // harte SeitenUmbrueche am nachfolgenden Node verschieben
2562     sal_Bool bSavePageBreak = sal_False, bSavePageDesc = sal_False;
2563 
2564     /* #i9185# This would lead to a segmentation fault if not caught
2565        above. */
2566     sal_uLong nNextNd = rEnd.nNode.GetIndex() + 1;
2567     SwTableNode *const pTblNd = GetNodes()[ nNextNd ]->GetTableNode();
2568 
2569     if( pTblNd && pNd->IsCntntNode() )
2570     {
2571         SwFrmFmt* pTableFmt = pTblNd->GetTable().GetFrmFmt();
2572 //JP 24.08.98: will man wirklich den PageDesc/Break vom
2573 //              nachfolgen Absatz ueberbuegeln?
2574 //      const SwAttrSet& rAttrSet = pTableFmt->GetAttrSet();
2575 //      if( SFX_ITEM_SET != rAttrSet.GetItemState( RES_PAGEDESC ) &&
2576 //          SFX_ITEM_SET != rAttrSet.GetItemState( RES_BREAK ))
2577         {
2578             const SfxPoolItem *pItem;
2579             const SfxItemSet* pSet = ((SwCntntNode*)pNd)->GetpSwAttrSet();
2580             if( pSet && SFX_ITEM_SET == pSet->GetItemState( RES_PAGEDESC,
2581                 sal_False, &pItem ) )
2582             {
2583                 pTableFmt->SetFmtAttr( *pItem );
2584                 bSavePageDesc = sal_True;
2585             }
2586 
2587             if( pSet && SFX_ITEM_SET == pSet->GetItemState( RES_BREAK,
2588                 sal_False, &pItem ) )
2589             {
2590                 pTableFmt->SetFmtAttr( *pItem );
2591                 bSavePageBreak = sal_True;
2592             }
2593         }
2594     }
2595 
2596     bool const bDoesUndo = GetIDocumentUndoRedo().DoesUndo();
2597     if( bDoesUndo )
2598     {
2599         if( !rPam.HasMark() )
2600             rPam.SetMark();
2601         else if( rPam.GetPoint() == &rStt )
2602             rPam.Exchange();
2603         rPam.GetPoint()->nNode++;
2604 
2605         SwCntntNode *pTmpNode = rPam.GetPoint()->nNode.GetNode().GetCntntNode();
2606         rPam.GetPoint()->nContent.Assign( pTmpNode, 0 );
2607         bool bGoNext = (0 == pTmpNode);
2608         pTmpNode = rPam.GetMark()->nNode.GetNode().GetCntntNode();
2609         rPam.GetMark()->nContent.Assign( pTmpNode, 0 );
2610 
2611         GetIDocumentUndoRedo().ClearRedo();
2612 
2613         SwPaM aDelPam( *rPam.GetMark(), *rPam.GetPoint() );
2614         {
2615             SwPosition aTmpPos( *aDelPam.GetPoint() );
2616             if( bGoNext )
2617             {
2618                 pTmpNode = GetNodes().GoNext( &aTmpPos.nNode );
2619                 aTmpPos.nContent.Assign( pTmpNode, 0 );
2620             }
2621             ::PaMCorrAbs( aDelPam, aTmpPos );
2622         }
2623 
2624         SwUndoDelete* pUndo = new SwUndoDelete( aDelPam, sal_True );
2625 
2626         *rPam.GetPoint() = *aDelPam.GetPoint();
2627         pUndo->SetPgBrkFlags( bSavePageBreak, bSavePageDesc );
2628         GetIDocumentUndoRedo().AppendUndo(pUndo);
2629     }
2630     else
2631     {
2632         SwNodeRange aRg( rStt.nNode, rEnd.nNode );
2633         if( rPam.GetPoint() != &rEnd )
2634             rPam.Exchange();
2635 
2636         // versuche hinters Ende zu verschieben
2637         if( !rPam.Move( fnMoveForward, fnGoNode ) )
2638         {
2639             // na gut, dann an den Anfang
2640             rPam.Exchange();
2641             if( !rPam.Move( fnMoveBackward, fnGoNode ))
2642             {
2643                 ASSERT( sal_False, "kein Node mehr vorhanden" );
2644                 return sal_False;
2645             }
2646         }
2647         // move bookmarks, redlines etc.
2648         if (aRg.aStart == aRg.aEnd) // only first CorrAbs variant handles this
2649         {
2650             CorrAbs( aRg.aStart, *rPam.GetPoint(), 0, sal_True );
2651         }
2652         else
2653         {
2654             CorrAbs( aRg.aStart, aRg.aEnd, *rPam.GetPoint(), sal_True );
2655         }
2656 
2657             // was ist mit Fly's ??
2658         {
2659             // stehen noch FlyFrames rum, loesche auch diese
2660             for( sal_uInt16 n = 0; n < GetSpzFrmFmts()->Count(); ++n )
2661             {
2662                 SwFrmFmt* pFly = (*GetSpzFrmFmts())[n];
2663                 const SwFmtAnchor* pAnchor = &pFly->GetAnchor();
2664                 SwPosition const*const pAPos = pAnchor->GetCntntAnchor();
2665                 if (pAPos &&
2666                     ((FLY_AT_PARA == pAnchor->GetAnchorId()) ||
2667                      (FLY_AT_CHAR == pAnchor->GetAnchorId())) &&
2668                     aRg.aStart <= pAPos->nNode && pAPos->nNode <= aRg.aEnd )
2669                 {
2670                     DelLayoutFmt( pFly );
2671                     --n;
2672                 }
2673             }
2674         }
2675 
2676         SwCntntNode *pTmpNode = rPam.GetBound( sal_True ).nNode.GetNode().GetCntntNode();
2677         rPam.GetBound( sal_True ).nContent.Assign( pTmpNode, 0 );
2678         pTmpNode = rPam.GetBound( sal_False ).nNode.GetNode().GetCntntNode();
2679         rPam.GetBound( sal_False ).nContent.Assign( pTmpNode, 0 );
2680         GetNodes().Delete( aRg.aStart, nNodeDiff+1 );
2681     }
2682     rPam.DeleteMark();
2683     SetModified();
2684 
2685     return sal_True;
2686 }
2687 
2688 
2689 void SwDoc::TransliterateText(
2690     const SwPaM& rPaM,
2691     utl::TransliterationWrapper& rTrans )
2692 {
2693     SwUndoTransliterate *const pUndo = (GetIDocumentUndoRedo().DoesUndo())
2694         ?   new SwUndoTransliterate( rPaM, rTrans )
2695         :   0;
2696 
2697     const SwPosition* pStt = rPaM.Start(),
2698                     * pEnd = rPaM.End();
2699     sal_uLong nSttNd = pStt->nNode.GetIndex(),
2700           nEndNd = pEnd->nNode.GetIndex();
2701     xub_StrLen nSttCnt = pStt->nContent.GetIndex(),
2702                nEndCnt = pEnd->nContent.GetIndex();
2703 
2704     SwTxtNode* pTNd = pStt->nNode.GetNode().GetTxtNode();
2705     if( pStt == pEnd && pTNd ) // no selection?
2706     {
2707         // set current word as 'area of effect'
2708 
2709         Boundary aBndry;
2710         if( pBreakIt->GetBreakIter().is() )
2711             aBndry = pBreakIt->GetBreakIter()->getWordBoundary(
2712                         pTNd->GetTxt(), nSttCnt,
2713                         pBreakIt->GetLocale( pTNd->GetLang( nSttCnt ) ),
2714                         WordType::ANY_WORD /*ANYWORD_IGNOREWHITESPACES*/,
2715                         sal_True );
2716 
2717         if( aBndry.startPos < nSttCnt && nSttCnt < aBndry.endPos )
2718         {
2719             nSttCnt = (xub_StrLen)aBndry.startPos;
2720             nEndCnt = (xub_StrLen)aBndry.endPos;
2721         }
2722     }
2723 
2724     if( nSttNd != nEndNd ) // is more than one text node involved?
2725     {
2726         // iterate over all effected text nodes, the first and the last one
2727         // may be incomplete because the selection starts and/or ends there
2728 
2729         SwNodeIndex aIdx( pStt->nNode );
2730         if( nSttCnt )
2731         {
2732             aIdx++;
2733             if( pTNd )
2734                 pTNd->TransliterateText( rTrans, nSttCnt, pTNd->GetTxt().Len(), pUndo );
2735         }
2736 
2737         for( ; aIdx.GetIndex() < nEndNd; aIdx++ )
2738         {
2739             if( 0 != ( pTNd = aIdx.GetNode().GetTxtNode() ))
2740                 pTNd->TransliterateText( rTrans, 0, pTNd->GetTxt().Len(), pUndo );
2741         }
2742 
2743         if( nEndCnt && 0 != ( pTNd = pEnd->nNode.GetNode().GetTxtNode() ))
2744             pTNd->TransliterateText( rTrans, 0, nEndCnt, pUndo );
2745     }
2746     else if( pTNd && nSttCnt < nEndCnt )
2747         pTNd->TransliterateText( rTrans, nSttCnt, nEndCnt, pUndo );
2748 
2749     if( pUndo )
2750     {
2751         if( pUndo->HasData() )
2752         {
2753             GetIDocumentUndoRedo().AppendUndo(pUndo);
2754         }
2755         else
2756             delete pUndo;
2757     }
2758     SetModified();
2759 }
2760 
2761 
2762 #define MAX_REDLINE_COUNT   250
2763 // -----------------------------------------------------------------------------
2764 void SwDoc::checkRedlining(RedlineMode_t& _rReadlineMode)
2765 {
2766     const SwRedlineTbl& rRedlineTbl = GetRedlineTbl();
2767     SwEditShell* pEditShell = GetEditShell();
2768     Window* pParent = pEditShell ? pEditShell->GetWin() : NULL;
2769     if ( pParent && !mbReadlineChecked && rRedlineTbl.Count() > MAX_REDLINE_COUNT
2770         && !((_rReadlineMode & nsRedlineMode_t::REDLINE_SHOW_DELETE) == nsRedlineMode_t::REDLINE_SHOW_DELETE) )
2771     {
2772         WarningBox aWarning( pParent,SW_RES(MSG_DISABLE_READLINE_QUESTION));
2773         sal_uInt16 nResult = aWarning.Execute();
2774         mbReadlineChecked = sal_True;
2775         if ( nResult == RET_YES )
2776         {
2777             sal_Int32 nMode = (sal_Int32)_rReadlineMode;
2778             nMode |= nsRedlineMode_t::REDLINE_SHOW_INSERT | nsRedlineMode_t::REDLINE_SHOW_DELETE;
2779             _rReadlineMode = (RedlineMode_t)nMode;
2780         }
2781     }
2782 }
2783 // -----------------------------------------------------------------------------
2784 
2785 void SwDoc::CountWords( const SwPaM& rPaM, SwDocStat& rStat ) const
2786 {
2787     // This is a modified version of SwDoc::TransliterateText
2788     const SwPosition* pStt = rPaM.Start();
2789     const SwPosition* pEnd = pStt == rPaM.GetPoint() ? rPaM.GetMark()
2790                                                      : rPaM.GetPoint();
2791 
2792     const sal_uLong nSttNd = pStt->nNode.GetIndex();
2793     const sal_uLong nEndNd = pEnd->nNode.GetIndex();
2794 
2795     const xub_StrLen nSttCnt = pStt->nContent.GetIndex();
2796     const xub_StrLen nEndCnt = pEnd->nContent.GetIndex();
2797 
2798     const SwTxtNode* pTNd = pStt->nNode.GetNode().GetTxtNode();
2799     if( pStt == pEnd && pTNd ) // no region ?
2800     {
2801         // do nothing
2802         return;
2803     }
2804 
2805     if( nSttNd != nEndNd )
2806     {
2807         SwNodeIndex aIdx( pStt->nNode );
2808         if( nSttCnt )
2809         {
2810             aIdx++;
2811             if( pTNd )
2812                 pTNd->CountWords( rStat, nSttCnt, pTNd->GetTxt().Len() );
2813         }
2814 
2815         for( ; aIdx.GetIndex() < nEndNd; aIdx++ )
2816             if( 0 != ( pTNd = aIdx.GetNode().GetTxtNode() ))
2817                 pTNd->CountWords( rStat, 0, pTNd->GetTxt().Len() );
2818 
2819         if( nEndCnt && 0 != ( pTNd = pEnd->nNode.GetNode().GetTxtNode() ))
2820             pTNd->CountWords( rStat, 0, nEndCnt );
2821     }
2822     else if( pTNd && nSttCnt < nEndCnt )
2823         pTNd->CountWords( rStat, nSttCnt, nEndCnt );
2824 }
2825 
2826 void SwDoc::RemoveLeadingWhiteSpace(const SwPosition & rPos )
2827 {
2828     const SwTxtNode* pTNd = rPos.nNode.GetNode().GetTxtNode();
2829     if ( pTNd )
2830     {
2831         const String& rTxt = pTNd->GetTxt();
2832         xub_StrLen nIdx = 0;
2833         sal_Unicode cCh;
2834         while( nIdx < rTxt.Len() &&
2835                 ( '\t' == ( cCh = rTxt.GetChar( nIdx ) ) ||
2836                 (  ' ' == cCh ) ) )
2837             ++nIdx;
2838 
2839         if ( nIdx > 0 )
2840         {
2841             SwPaM aPam(rPos);
2842             aPam.GetPoint()->nContent = 0;
2843             aPam.SetMark();
2844             aPam.GetMark()->nContent = nIdx;
2845             DeleteRange( aPam );
2846         }
2847     }
2848 }
2849 
2850 /* vim: set noet sw=4 ts=4: */
2851