xref: /AOO41X/main/svtools/source/edit/textdoc.cxx (revision 7574bed8a3ab4efc1e595269f7730150df6b97dd)
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 
23 
24 // MARKER(update_precomp.py): autogen include statement, do not remove
25 #include "precompiled_svtools.hxx"
26 #include <textdoc.hxx>
27 
28 #include <stdlib.h>
29 
30 SV_IMPL_PTRARR( TextCharAttribs, TextCharAttribPtr );
31 
32 
33 
34 // Vergleichmethode wird von QuickSort gerufen...
35 
36 EXTERN_C
37 #ifdef WNT
38 #if _MSC_VER >= 1200
39 int __cdecl
40 #else
41 int _cdecl
42 #endif
43 #else
44 int
45 #endif
46 
CompareStart(const void * pFirst,const void * pSecond)47 CompareStart( const void* pFirst, const void* pSecond )
48 {
49     if ( (*((TextCharAttrib**)pFirst))->GetStart() < (*((TextCharAttrib**)pSecond))->GetStart() )
50         return (-1);
51     else if ( (*((TextCharAttrib**)pFirst))->GetStart() > (*((TextCharAttrib**)pSecond))->GetStart() )
52         return (1);
53     return 0;
54 }
55 
56 
57 // -------------------------------------------------------------------------
58 // (+) class TextCharAttrib
59 // -------------------------------------------------------------------------
TextCharAttrib(const TextAttrib & rAttr,sal_uInt16 nStart,sal_uInt16 nEnd)60 TextCharAttrib::TextCharAttrib( const TextAttrib& rAttr, sal_uInt16 nStart, sal_uInt16 nEnd )
61 {
62     mpAttr = rAttr.Clone();
63     mnStart = nStart,
64     mnEnd = nEnd;
65 }
66 
TextCharAttrib(const TextCharAttrib & rTextCharAttrib)67 TextCharAttrib::TextCharAttrib( const TextCharAttrib& rTextCharAttrib )
68 {
69     mpAttr = rTextCharAttrib.GetAttr().Clone();
70     mnStart = rTextCharAttrib.mnStart;
71     mnEnd = rTextCharAttrib.mnEnd;
72 }
73 
~TextCharAttrib()74 TextCharAttrib::~TextCharAttrib()
75 {
76     delete mpAttr;
77 }
78 
79 // -------------------------------------------------------------------------
80 // (+) class TextCharAttribList
81 // -------------------------------------------------------------------------
82 
TextCharAttribList()83 TextCharAttribList::TextCharAttribList()
84 {
85     mbHasEmptyAttribs = sal_False;
86 }
87 
~TextCharAttribList()88 TextCharAttribList::~TextCharAttribList()
89 {
90     // PTRARR_DEL
91 }
92 
Clear(sal_Bool bDestroyAttribs)93 void TextCharAttribList::Clear( sal_Bool bDestroyAttribs )
94 {
95     if ( bDestroyAttribs )
96         TextCharAttribs::DeleteAndDestroy( 0, Count() );
97     else
98         TextCharAttribs::Remove( 0, Count() );
99 }
100 
101 
InsertAttrib(TextCharAttrib * pAttrib)102 void TextCharAttribList::InsertAttrib( TextCharAttrib* pAttrib )
103 {
104     if ( pAttrib->IsEmpty() )
105         mbHasEmptyAttribs = sal_True;
106 
107     const sal_uInt16 nCount = Count();
108     const sal_uInt16 nStart = pAttrib->GetStart(); // vielleicht besser fuer Comp.Opt.
109     sal_Bool bInserted = sal_False;
110     for ( sal_uInt16 x = 0; x < nCount; x++ )
111     {
112         TextCharAttrib* pCurAttrib = GetObject( x );
113         if ( pCurAttrib->GetStart() > nStart )
114         {
115             Insert( pAttrib, x );
116             bInserted = sal_True;
117             break;
118         }
119     }
120     if ( !bInserted )
121         Insert( pAttrib, nCount );
122 }
123 
ResortAttribs()124 void TextCharAttribList::ResortAttribs()
125 {
126     if ( Count() )
127         qsort( (void*)GetData(), Count(), sizeof( TextCharAttrib* ), CompareStart );
128 }
129 
FindAttrib(sal_uInt16 nWhich,sal_uInt16 nPos)130 TextCharAttrib* TextCharAttribList::FindAttrib( sal_uInt16 nWhich, sal_uInt16 nPos )
131 {
132     // Rueckwaerts, falls eins dort endet, das naechste startet.
133     // => Das startende gilt...
134 
135     for ( sal_uInt16 nAttr = Count(); nAttr; )
136     {
137         TextCharAttrib* pAttr = GetObject( --nAttr );
138 
139         if ( pAttr->GetEnd() < nPos )
140             return 0;
141 
142         if ( ( pAttr->Which() == nWhich ) && pAttr->IsIn(nPos) )
143             return pAttr;
144     }
145     return NULL;
146 }
147 
FindNextAttrib(sal_uInt16 nWhich,sal_uInt16 nFromPos,sal_uInt16 nMaxPos) const148 TextCharAttrib* TextCharAttribList::FindNextAttrib( sal_uInt16 nWhich, sal_uInt16 nFromPos, sal_uInt16 nMaxPos ) const
149 {
150     DBG_ASSERT( nWhich, "FindNextAttrib: Which?" );
151     const sal_uInt16 nAttribs = Count();
152     for ( sal_uInt16 nAttr = 0; nAttr < nAttribs; nAttr++ )
153     {
154         TextCharAttrib* pAttr = GetObject( nAttr );
155         if ( ( pAttr->GetStart() >= nFromPos ) &&
156              ( pAttr->GetEnd() <= nMaxPos ) &&
157              ( pAttr->Which() == nWhich ) )
158             return pAttr;
159     }
160     return NULL;
161 }
162 
HasAttrib(sal_uInt16 nWhich) const163 sal_Bool TextCharAttribList::HasAttrib( sal_uInt16 nWhich ) const
164 {
165     for ( sal_uInt16 nAttr = Count(); nAttr; )
166     {
167         const TextCharAttrib* pAttr = GetObject( --nAttr );
168         if ( pAttr->Which() == nWhich )
169             return sal_True;
170     }
171     return sal_False;
172 }
173 
HasBoundingAttrib(sal_uInt16 nBound)174 sal_Bool TextCharAttribList::HasBoundingAttrib( sal_uInt16 nBound )
175 {
176     // Rueckwaerts, falls eins dort endet, das naechste startet.
177     // => Das startende gilt...
178     for ( sal_uInt16 nAttr = Count(); nAttr; )
179     {
180         TextCharAttrib* pAttr = GetObject( --nAttr );
181 
182         if ( pAttr->GetEnd() < nBound )
183             return sal_False;
184 
185         if ( ( pAttr->GetStart() == nBound ) || ( pAttr->GetEnd() == nBound ) )
186             return sal_True;
187     }
188     return sal_False;
189 }
190 
FindEmptyAttrib(sal_uInt16 nWhich,sal_uInt16 nPos)191 TextCharAttrib* TextCharAttribList::FindEmptyAttrib( sal_uInt16 nWhich, sal_uInt16 nPos )
192 {
193     if ( !mbHasEmptyAttribs )
194         return 0;
195 
196     const sal_uInt16 nAttribs = Count();
197     for ( sal_uInt16 nAttr = 0; nAttr < nAttribs; nAttr++ )
198     {
199         TextCharAttrib* pAttr = GetObject( nAttr );
200         if ( pAttr->GetStart() > nPos )
201             return 0;
202 
203         if ( ( pAttr->GetStart() == nPos ) && ( pAttr->GetEnd() == nPos ) && ( pAttr->Which() == nWhich ) )
204             return pAttr;
205     }
206     return 0;
207 }
208 
DeleteEmptyAttribs()209 void TextCharAttribList::DeleteEmptyAttribs()
210 {
211     for ( sal_uInt16 nAttr = 0; nAttr < Count(); nAttr++ )
212     {
213         TextCharAttrib* pAttr = GetObject( nAttr );
214         if ( pAttr->IsEmpty() )
215         {
216             Remove( nAttr );
217             delete pAttr;
218             nAttr--;
219         }
220     }
221     mbHasEmptyAttribs = sal_False;
222 }
223 
224 #ifdef  DBG_UTIL
DbgCheckAttribs()225 sal_Bool TextCharAttribList::DbgCheckAttribs()
226 {
227     sal_Bool bOK = sal_True;
228     for ( sal_uInt16 nAttr = 0; nAttr < Count(); nAttr++ )
229     {
230         TextCharAttrib* pAttr = GetObject( nAttr );
231         if ( pAttr->GetStart() > pAttr->GetEnd() )
232         {
233             bOK = sal_False;
234             DBG_ERROR( "Attr verdreht" );
235         }
236     }
237     return bOK;
238 }
239 #endif
240 
241 // -------------------------------------------------------------------------
242 // (+) class TextNode
243 // -------------------------------------------------------------------------
244 
TextNode(const String & rText)245 TextNode::TextNode( const String& rText ) :
246     maText( rText )
247 {
248 }
249 
ExpandAttribs(sal_uInt16 nIndex,sal_uInt16 nNew)250 void TextNode::ExpandAttribs( sal_uInt16 nIndex, sal_uInt16 nNew )
251 {
252     if ( !nNew )
253         return;
254 
255     sal_Bool bResort = sal_False;
256     sal_uInt16 nAttribs = maCharAttribs.Count();
257     for ( sal_uInt16 nAttr = 0; nAttr < nAttribs; nAttr++ )
258     {
259         TextCharAttrib* pAttrib = maCharAttribs.GetAttrib( nAttr );
260         if ( pAttrib->GetEnd() >= nIndex )
261         {
262             // Alle Attribute hinter der Einfuegeposition verschieben...
263             if ( pAttrib->GetStart() > nIndex )
264             {
265                 pAttrib->MoveForward( nNew );
266             }
267             // 0: Leeres Attribut expandieren, wenn an Einfuegestelle
268             else if ( pAttrib->IsEmpty() )
269             {
270                 // Index nicht pruefen, leeres durfte nur dort liegen.
271                 // Wenn spaeter doch Ueberpruefung:
272                 //   Spezialfall: Start == 0; AbsLen == 1, nNew = 1 => Expand, weil durch Absatzumbruch!
273                 // Start <= nIndex, End >= nIndex => Start=End=nIndex!
274 //              if ( pAttrib->GetStart() == nIndex )
275                     pAttrib->Expand( nNew );
276             }
277             // 1: Attribut startet davor, geht bis Index...
278             else if ( pAttrib->GetEnd() == nIndex ) // Start muss davor liegen
279             {
280                 // Nur expandieren, wenn kein Feature,
281                 // und wenn nicht in ExcludeListe!
282                 // Sonst geht z.B. ein UL bis zum neuen ULDB, beide expandieren
283                 if ( !maCharAttribs.FindEmptyAttrib( pAttrib->Which(), nIndex ) )
284                 {
285                     pAttrib->Expand( nNew );
286                 }
287                 else
288                     bResort = sal_True;
289             }
290             // 2: Attribut startet davor, geht hinter Index...
291             else if ( ( pAttrib->GetStart() < nIndex ) && ( pAttrib->GetEnd() > nIndex ) )
292             {
293                 pAttrib->Expand( nNew );
294             }
295             // 3: Attribut startet auf Index...
296             else if ( pAttrib->GetStart() == nIndex )
297             {
298                 if ( nIndex == 0 )
299                 {
300                     pAttrib->Expand( nNew );
301 //                  bResort = sal_True;     // es gibt ja keine Features mehr...
302                 }
303                 else
304                     pAttrib->MoveForward( nNew );
305             }
306         }
307 
308         DBG_ASSERT( pAttrib->GetStart() <= pAttrib->GetEnd(), "Expand: Attribut verdreht!" );
309         DBG_ASSERT( ( pAttrib->GetEnd() <= maText.Len() ), "Expand: Attrib groesser als Absatz!" );
310         DBG_ASSERT( !pAttrib->IsEmpty(), "Leeres Attribut nach ExpandAttribs?" );
311     }
312 
313     if ( bResort )
314         maCharAttribs.ResortAttribs();
315 
316 #ifdef EDITDEBUG
317     DBG_ASSERT( CheckOrderedList( (TextCharAttribs*)&maCharAttribs ), "Expand: Start-Liste verdreht" );
318 #endif
319 }
320 
CollapsAttribs(sal_uInt16 nIndex,sal_uInt16 nDeleted)321 void TextNode::CollapsAttribs( sal_uInt16 nIndex, sal_uInt16 nDeleted )
322 {
323     if ( !nDeleted )
324         return;
325 
326     sal_Bool bResort = sal_False;
327     sal_uInt16 nEndChanges = nIndex+nDeleted;
328 
329     for ( sal_uInt16 nAttr = 0; nAttr < maCharAttribs.Count(); nAttr++ )
330     {
331         TextCharAttrib* pAttrib = maCharAttribs.GetAttrib( nAttr );
332         sal_Bool bDelAttr = sal_False;
333         if ( pAttrib->GetEnd() >= nIndex )
334         {
335             // Alles Attribute hinter der Einfuegeposition verschieben...
336             if ( pAttrib->GetStart() >= nEndChanges )
337             {
338                 pAttrib->MoveBackward( nDeleted );
339             }
340             // 1. Innenliegende Attribute loeschen...
341             else if ( ( pAttrib->GetStart() >= nIndex ) && ( pAttrib->GetEnd() <= nEndChanges ) )
342             {
343                 // Spezialfall: Attrubt deckt genau den Bereich ab
344                 // => als leeres Attribut behalten.
345                 if ( ( pAttrib->GetStart() == nIndex ) && ( pAttrib->GetEnd() == nEndChanges ) )
346                     pAttrib->GetEnd() = nIndex; // leer
347                 else
348                     bDelAttr = sal_True;
349             }
350             // 2. Attribut beginnt davor, endet drinnen oder dahinter...
351             else if ( ( pAttrib->GetStart() <= nIndex ) && ( pAttrib->GetEnd() > nIndex ) )
352             {
353                 if ( pAttrib->GetEnd() <= nEndChanges ) // endet drinnen
354                     pAttrib->GetEnd() = nIndex;
355                 else
356                     pAttrib->Collaps( nDeleted );       // endet dahinter
357             }
358             // 3. Attribut beginnt drinnen, endet dahinter...
359             else if ( ( pAttrib->GetStart() >= nIndex ) && ( pAttrib->GetEnd() > nEndChanges ) )
360             {
361                 // Features duerfen nicht expandieren!
362                 pAttrib->GetStart() = nEndChanges;
363                 pAttrib->MoveBackward( nDeleted );
364             }
365         }
366 
367         DBG_ASSERT( pAttrib->GetStart() <= pAttrib->GetEnd(), "Collaps: Attribut verdreht!" );
368         DBG_ASSERT( ( pAttrib->GetEnd() <= maText.Len()) || bDelAttr, "Collaps: Attrib groesser als Absatz!" );
369         if ( bDelAttr /* || pAttrib->IsEmpty() */ )
370         {
371             bResort = sal_True;
372             maCharAttribs.RemoveAttrib( nAttr );
373             delete pAttrib;
374             nAttr--;
375         }
376         else if ( pAttrib->IsEmpty() )
377             maCharAttribs.HasEmptyAttribs() = sal_True;
378     }
379 
380     if ( bResort )
381         maCharAttribs.ResortAttribs();
382 
383 #ifdef EDITDEBUG
384     DBG_ASSERT( CheckOrderedList( (TextCharAttribs)&maCharAttribs ), "Collaps: Start-Liste verdreht" );
385 #endif
386 }
387 
InsertText(sal_uInt16 nPos,const String & rText)388 void TextNode::InsertText( sal_uInt16 nPos, const String& rText )
389 {
390     maText.Insert( rText, nPos );
391     ExpandAttribs( nPos, rText.Len() );
392 }
393 
InsertText(sal_uInt16 nPos,sal_Unicode c)394 void TextNode::InsertText( sal_uInt16 nPos, sal_Unicode c )
395 {
396     maText.Insert( c, nPos );
397     ExpandAttribs( nPos, 1 );
398 }
399 
RemoveText(sal_uInt16 nPos,sal_uInt16 nChars)400 void TextNode::RemoveText( sal_uInt16 nPos, sal_uInt16 nChars )
401 {
402     maText.Erase( nPos, nChars );
403     CollapsAttribs( nPos, nChars );
404 }
405 
Split(sal_uInt16 nPos,sal_Bool bKeepEndingAttribs)406 TextNode* TextNode::Split( sal_uInt16 nPos, sal_Bool bKeepEndingAttribs )
407 {
408     String aNewText;
409     if ( nPos < maText.Len() )
410     {
411         aNewText = maText.Copy( nPos );
412         maText.Erase( nPos );
413     }
414     TextNode* pNew = new TextNode( aNewText );
415 
416     for ( sal_uInt16 nAttr = 0; nAttr < maCharAttribs.Count(); nAttr++ )
417     {
418         TextCharAttrib* pAttrib = maCharAttribs.GetAttrib( nAttr );
419         if ( pAttrib->GetEnd() < nPos )
420         {
421             // bleiben unveraendert....
422             ;
423         }
424         else if ( pAttrib->GetEnd() == nPos )
425         {
426             // muessen als leeres Attribut kopiert werden.
427             // !FindAttrib nur sinnvoll, wenn Rueckwaerts durch Liste!
428             if ( bKeepEndingAttribs && !pNew->maCharAttribs.FindAttrib( pAttrib->Which(), 0 ) )
429             {
430                 TextCharAttrib* pNewAttrib = new TextCharAttrib( *pAttrib );
431                 pNewAttrib->GetStart() = 0;
432                 pNewAttrib->GetEnd() = 0;
433                 pNew->maCharAttribs.InsertAttrib( pNewAttrib );
434             }
435         }
436         else if ( pAttrib->IsInside( nPos ) || ( !nPos && !pAttrib->GetStart() ) )
437         {
438             // Wenn ganz vorne gecuttet wird, muss das Attribut erhalten bleiben!
439             // muessen kopiert und geaendert werden
440             TextCharAttrib* pNewAttrib = new TextCharAttrib( *pAttrib );
441             pNewAttrib->GetStart() = 0;
442             pNewAttrib->GetEnd() = pAttrib->GetEnd()-nPos;
443             pNew->maCharAttribs.InsertAttrib( pNewAttrib );
444             // stutzen:
445             pAttrib->GetEnd() = nPos;
446         }
447         else
448         {
449             DBG_ASSERT( pAttrib->GetStart() >= nPos, "Start < nPos!" );
450             DBG_ASSERT( pAttrib->GetEnd() >= nPos, "End < nPos!" );
451             // alle dahinter verschieben in den neuen Node (this)
452             maCharAttribs.RemoveAttrib( nAttr );
453             pNew->maCharAttribs.InsertAttrib( pAttrib );
454             pAttrib->GetStart() = pAttrib->GetStart() - nPos;
455             pAttrib->GetEnd() = pAttrib->GetEnd() - nPos;
456             nAttr--;
457         }
458     }
459     return pNew;
460 }
461 
Append(const TextNode & rNode)462 void TextNode::Append( const TextNode& rNode )
463 {
464     sal_uInt16 nOldLen = maText.Len();
465 
466     maText += rNode.GetText();
467 
468 #ifdef EDITDEBUG
469     DBG_ASSERT( maCharAttribs.DbgCheckAttribs(), "Attribute VOR AppendAttribs kaputt" );
470 #endif
471 
472     const sal_uInt16 nAttribs = rNode.GetCharAttribs().Count();
473     for ( sal_uInt16 nAttr = 0; nAttr < nAttribs; nAttr++ )
474     {
475         TextCharAttrib* pAttrib = rNode.GetCharAttribs().GetAttrib( nAttr );
476         sal_Bool bMelted = sal_False;
477         if ( pAttrib->GetStart() == 0 )
478         {
479             // Evtl koennen Attribute zusammengefasst werden:
480             sal_uInt16 nTmpAttribs = maCharAttribs.Count();
481             for ( sal_uInt16 nTmpAttr = 0; nTmpAttr < nTmpAttribs; nTmpAttr++ )
482             {
483                 TextCharAttrib* pTmpAttrib = maCharAttribs.GetAttrib( nTmpAttr );
484 
485                 if ( pTmpAttrib->GetEnd() == nOldLen )
486                 {
487                     if ( ( pTmpAttrib->Which() == pAttrib->Which() ) &&
488                          ( pTmpAttrib->GetAttr() == pAttrib->GetAttr() ) )
489                     {
490                         pTmpAttrib->GetEnd() =
491                             pTmpAttrib->GetEnd() + pAttrib->GetLen();
492                         bMelted = sal_True;
493                         break;  // es kann nur eins von der Sorte an der Stelle geben
494                     }
495                 }
496             }
497         }
498 
499         if ( !bMelted )
500         {
501             TextCharAttrib* pNewAttrib = new TextCharAttrib( *pAttrib );
502             pNewAttrib->GetStart() = pNewAttrib->GetStart() + nOldLen;
503             pNewAttrib->GetEnd() = pNewAttrib->GetEnd() + nOldLen;
504             maCharAttribs.InsertAttrib( pNewAttrib );
505         }
506     }
507 
508 #ifdef EDITDEBUG
509     DBG_ASSERT( maCharAttribs.DbgCheckAttribs(), "Attribute NACH AppendAttribs kaputt" );
510 #endif
511 }
512 
513 // -------------------------------------------------------------------------
514 // (+) class TextDoc
515 // -------------------------------------------------------------------------
516 
TextDoc()517 TextDoc::TextDoc()
518 {
519     mnLeftMargin = 0;
520 };
521 
~TextDoc()522 TextDoc::~TextDoc()
523 {
524     DestroyTextNodes();
525 }
526 
Clear()527 void TextDoc::Clear()
528 {
529     DestroyTextNodes();
530 }
531 
DestroyTextNodes()532 void TextDoc::DestroyTextNodes()
533 {
534     for ( sal_uLong nNode = 0; nNode < maTextNodes.Count(); nNode++ )
535         delete maTextNodes.GetObject( nNode );
536     maTextNodes.clear();
537 }
538 
GetText(const sal_Unicode * pSep) const539 String TextDoc::GetText( const sal_Unicode* pSep ) const
540 {
541     sal_uLong nLen = GetTextLen( pSep );
542     sal_uLong nNodes = maTextNodes.Count();
543 
544     if ( nLen > STRING_MAXLEN )
545     {
546         DBG_ERROR( "Text zu gross fuer String" );
547         return String();
548     }
549 
550     String aASCIIText;
551     sal_uLong nLastNode = nNodes-1;
552     for ( sal_uLong nNode = 0; nNode < nNodes; nNode++ )
553     {
554         TextNode* pNode = maTextNodes.GetObject( nNode );
555         String aTmp( pNode->GetText() );
556         aASCIIText += aTmp;
557         if ( pSep && ( nNode != nLastNode ) )
558             aASCIIText += pSep;
559     }
560 
561     return aASCIIText;
562 }
563 
GetText(sal_uLong nPara) const564 XubString TextDoc::GetText( sal_uLong nPara ) const
565 {
566     XubString aText;
567     TextNode* pNode = ( nPara < maTextNodes.Count() ) ? maTextNodes.GetObject( nPara ) : 0;
568     if ( pNode )
569         aText = pNode->GetText();
570 
571     return aText;
572 }
573 
574 
GetTextLen(const xub_Unicode * pSep,const TextSelection * pSel) const575 sal_uLong TextDoc::GetTextLen( const xub_Unicode* pSep, const TextSelection* pSel ) const
576 {
577     sal_uLong nLen = 0;
578     sal_uLong nNodes = maTextNodes.Count();
579     if ( nNodes )
580     {
581         sal_uLong nStartNode = 0;
582         sal_uLong nEndNode = nNodes-1;
583         if ( pSel )
584         {
585             nStartNode = pSel->GetStart().GetPara();
586             nEndNode = pSel->GetEnd().GetPara();
587         }
588 
589         for ( sal_uLong nNode = nStartNode; nNode <= nEndNode; nNode++ )
590         {
591             TextNode* pNode = maTextNodes.GetObject( nNode );
592 
593             sal_uInt16 nS = 0;
594             sal_uLong nE = pNode->GetText().Len();
595             if ( pSel && ( nNode == pSel->GetStart().GetPara() ) )
596                 nS = pSel->GetStart().GetIndex();
597             if ( pSel && ( nNode == pSel->GetEnd().GetPara() ) )
598                 nE = pSel->GetEnd().GetIndex();
599 
600             nLen += ( nE - nS );
601         }
602 
603         if ( pSep )
604             nLen += (nEndNode-nStartNode) * String( pSep ).Len();
605     }
606 
607     return nLen;
608 }
609 
InsertText(const TextPaM & rPaM,xub_Unicode c)610 TextPaM TextDoc::InsertText( const TextPaM& rPaM, xub_Unicode c )
611 {
612     DBG_ASSERT( c != 0x0A, "TextDoc::InsertText: Zeilentrenner in Absatz nicht erlaubt!" );
613     DBG_ASSERT( c != 0x0D, "TextDoc::InsertText: Zeilentrenner in Absatz nicht erlaubt!" );
614 
615     TextNode* pNode = maTextNodes.GetObject( rPaM.GetPara() );
616     pNode->InsertText( rPaM.GetIndex(), c );
617 
618     TextPaM aPaM( rPaM.GetPara(), rPaM.GetIndex()+1 );
619     return aPaM;
620 }
621 
InsertText(const TextPaM & rPaM,const XubString & rStr)622 TextPaM TextDoc::InsertText( const TextPaM& rPaM, const XubString& rStr )
623 {
624     DBG_ASSERT( rStr.Search( 0x0A ) == STRING_NOTFOUND, "TextDoc::InsertText: Zeilentrenner in Absatz nicht erlaubt!" );
625     DBG_ASSERT( rStr.Search( 0x0D ) == STRING_NOTFOUND, "TextDoc::InsertText: Zeilentrenner in Absatz nicht erlaubt!" );
626 
627     TextNode* pNode = maTextNodes.GetObject( rPaM.GetPara() );
628     pNode->InsertText( rPaM.GetIndex(), rStr );
629 
630     TextPaM aPaM( rPaM.GetPara(), rPaM.GetIndex()+rStr.Len() );
631     return aPaM;
632 }
633 
InsertParaBreak(const TextPaM & rPaM,sal_Bool bKeepEndingAttribs)634 TextPaM TextDoc::InsertParaBreak( const TextPaM& rPaM, sal_Bool bKeepEndingAttribs )
635 {
636     TextNode* pNode = maTextNodes.GetObject( rPaM.GetPara() );
637     TextNode* pNew = pNode->Split( rPaM.GetIndex(), bKeepEndingAttribs );
638 
639     maTextNodes.Insert( pNew, rPaM.GetPara()+1 );
640 
641     TextPaM aPaM( rPaM.GetPara()+1, 0 );
642     return aPaM;
643 }
644 
ConnectParagraphs(TextNode * pLeft,TextNode * pRight)645 TextPaM TextDoc::ConnectParagraphs( TextNode* pLeft, TextNode* pRight )
646 {
647     sal_uInt16 nPrevLen = pLeft->GetText().Len();
648     pLeft->Append( *pRight );
649 
650     // der rechte verschwindet.
651     sal_uLong nRight = maTextNodes.GetPos( pRight );
652     maTextNodes.Remove( nRight );
653     delete pRight;
654 
655     sal_uLong nLeft = maTextNodes.GetPos( pLeft );
656     TextPaM aPaM( nLeft, nPrevLen );
657     return aPaM;
658 }
659 
RemoveChars(const TextPaM & rPaM,sal_uInt16 nChars)660 TextPaM TextDoc::RemoveChars( const TextPaM& rPaM, sal_uInt16 nChars )
661 {
662     TextNode* pNode = maTextNodes.GetObject( rPaM.GetPara() );
663     pNode->RemoveText( rPaM.GetIndex(), nChars );
664 
665     return rPaM;
666 }
667 
IsValidPaM(const TextPaM & rPaM)668 sal_Bool TextDoc::IsValidPaM( const TextPaM& rPaM )
669 {
670     if ( rPaM.GetPara() >= maTextNodes.Count() )
671     {
672         DBG_ERROR( "PaM: Para out of range" );
673         return sal_False;
674     }
675     TextNode * pNode = maTextNodes.GetObject( rPaM.GetPara() );
676     if ( rPaM.GetIndex() > pNode->GetText().Len() )
677     {
678         DBG_ERROR( "PaM: Index out of range" );
679         return sal_False;
680     }
681     return sal_True;
682 }
683 
684 /*
685 
686 void TextDoc::InsertAttribInSelection( TextNode* pNode, sal_uInt16 nStart, sal_uInt16 nEnd, const SfxPoolItem& rPoolItem )
687 {
688     DBG_ASSERT( pNode, "Wohin mit dem Attribut?" );
689     DBG_ASSERT( nEnd <= pNode->Len(), "InsertAttrib: Attribut zu gross!" );
690 
691     // fuer Optimierung:
692     // dieses endet am Anfang der Selektion => kann erweitert werden
693     TextCharAttrib* pEndingAttrib = 0;
694     // dieses startet am Ende der Selektion => kann erweitert werden
695     TextCharAttrib* pStartingAttrib = 0;
696 
697     DBG_ASSERT( nStart <= nEnd, "Kleiner Rechenfehler in InsertAttribInSelection" );
698 
699     RemoveAttribs( pNode, nStart, nEnd, pStartingAttrib, pEndingAttrib, rPoolItem.Which() );
700 
701     if ( pStartingAttrib && pEndingAttrib &&
702          ( *(pStartingAttrib->GetItem()) == rPoolItem ) &&
703          ( *(pEndingAttrib->GetItem()) == rPoolItem ) )
704     {
705         // wird ein groesses Attribut.
706         pEndingAttrib->GetEnd() = pStartingAttrib->GetEnd();
707         pCurPool->Remove( *(pStartingAttrib->GetItem()) );
708         pNode->GetCharAttribs().GetAttribs().Remove( pNode->GetCharAttribs().GetAttribs().GetPos( pStartingAttrib ) );
709         delete pStartingAttrib;
710     }
711     else if ( pStartingAttrib && ( *(pStartingAttrib->GetItem()) == rPoolItem ) )
712         pStartingAttrib->GetStart() = nStart;
713     else if ( pEndingAttrib && ( *(pEndingAttrib->GetItem()) == rPoolItem ) )
714         pEndingAttrib->GetEnd() = nEnd;
715     else
716         InsertAttrib( rPoolItem, pNode, nStart, nEnd );
717 
718     if ( pStartingAttrib )
719         pNode->GetCharAttribs().ResortAttribs();
720 }
721 
722 sal_Bool TextDoc::RemoveAttribs( TextNode* pNode, sal_uInt16 nStart, sal_uInt16 nEnd, sal_uInt16 nWhich )
723 {
724     TextCharAttrib* pStarting;
725     TextCharAttrib* pEnding;
726     return RemoveAttribs( pNode, nStart, nEnd, pStarting, pEnding, nWhich );
727 }
728 
729 sal_Bool TextDoc::RemoveAttribs( TextNode* pNode, sal_uInt16 nStart, sal_uInt16 nEnd, TextCharAttrib*& rpStarting, TextCharAttrib*& rpEnding, sal_uInt16 nWhich )
730 {
731     DBG_ASSERT( pNode, "Wohin mit dem Attribut?" );
732     DBG_ASSERT( nEnd <= pNode->Len(), "InsertAttrib: Attribut zu gross!" );
733 
734     // dieses endet am Anfang der Selektion => kann erweitert werden
735     rpEnding = 0;
736     // dieses startet am Ende der Selektion => kann erweitert werden
737     rpStarting = 0;
738 
739     sal_Bool bChanged = sal_False;
740 
741     DBG_ASSERT( nStart <= nEnd, "Kleiner Rechenfehler in InsertAttribInSelection" );
742 
743     // ueber die Attribute iterieren...
744     sal_uInt16 nAttr = 0;
745     TextCharAttrib* pAttr = GetAttrib( pNode->GetCharAttribs().GetAttribs(), nAttr );
746     while ( pAttr )
747     {
748         sal_Bool bRemoveAttrib = sal_False;
749         if ( !nWhich || ( pAttr->Which() == nWhich ) )
750         {
751             // Attribut beginnt in Selection
752             if ( ( pAttr->GetStart() >= nStart ) && ( pAttr->GetStart() <= nEnd ) )
753             {
754                 bChanged = sal_True;
755                 if ( pAttr->GetEnd() > nEnd )
756                 {
757                     pAttr->GetStart() = nEnd;   // dann faengt es dahinter an
758                     rpStarting = pAttr;
759                     break;  // es kann kein weiteres Attrib hier liegen
760                 }
761                 else if ( !pAttr->IsFeature() || ( pAttr->GetStart() == nStart ) )
762                 {
763                     // Feature nur loeschen, wenn genau an der Stelle
764                     bRemoveAttrib = sal_True;
765                 }
766             }
767 
768             // Attribut endet in Selection
769             else if ( ( pAttr->GetEnd() >= nStart ) && ( pAttr->GetEnd() <= nEnd ) )
770             {
771                 bChanged = sal_True;
772                 if ( ( pAttr->GetStart() < nStart ) && !pAttr->IsFeature() )
773                 {
774                     pAttr->GetEnd() = nStart;   // dann hoert es hier auf
775                     rpEnding = pAttr;
776                 }
777                 else if ( !pAttr->IsFeature() || ( pAttr->GetStart() == nStart ) )
778                 {
779                     // Feature nur loeschen, wenn genau an der Stelle
780                     bRemoveAttrib = sal_True;
781                 }
782             }
783             // Attribut ueberlappt die Selektion
784             else if ( ( pAttr->GetStart() <= nStart ) && ( pAttr->GetEnd() >= nEnd ) )
785             {
786                 bChanged = sal_True;
787                 if ( pAttr->GetStart() == nStart )
788                 {
789                     pAttr->GetStart() = nEnd;
790                     rpStarting = pAttr;
791                     break;  // es kann weitere Attribute geben!
792                 }
793                 else if ( pAttr->GetEnd() == nEnd )
794                 {
795                     pAttr->GetEnd() = nStart;
796                     rpEnding = pAttr;
797                     break;  // es kann weitere Attribute geben!
798                 }
799                 else // Attribut muss gesplittet werden...
800                 {
801                     sal_uInt16 nOldEnd = pAttr->GetEnd();
802                     pAttr->GetEnd() = nStart;
803                     rpEnding = pAttr;
804 //                  sal_uLong nSavePos = pNode->GetCharAttribs().GetStartList().GetCurPos();
805                     InsertAttrib( *pAttr->GetItem(), pNode, nEnd, nOldEnd );
806 //                  pNode->GetCharAttribs().GetStartList().Seek( nSavePos );
807                     break;  // es kann weitere Attribute geben!
808                 }
809             }
810         }
811         if ( bRemoveAttrib )
812         {
813             DBG_ASSERT( ( pAttr != rpStarting ) && ( pAttr != rpEnding ), "Loeschen und behalten des gleichen Attributs ?" );
814             pNode->GetCharAttribs().GetAttribs().Remove(nAttr);
815             pCurPool->Remove( *pAttr->GetItem() );
816             delete pAttr;
817             nAttr--;
818         }
819         nAttr++;
820         pAttr = GetAttrib( pNode->GetCharAttribs().GetAttribs(), nAttr );
821     }
822     return bChanged;
823 }
824 
825 #pragma SEG_FUNCDEF(editdoc_3f)
826 
827 void TextDoc::InsertAttrib( const SfxPoolItem& rPoolItem, TextNode* pNode, sal_uInt16 nStart, sal_uInt16 nEnd )
828 {
829     // Diese Methode prueft nicht mehr, ob ein entspr. Attribut
830     // schon an der Stelle existiert!
831 
832     // pruefen, ob neues Attrib oder einfach nur Ende eines Attribs...
833 //  const SfxPoolItem& rDefItem = pNode->GetContentAttribs().GetItem( rPoolItem.Which() );
834 //  sal_Bool bCreateAttrib = ( rDefItem != rPoolItem );
835 
836     // Durch den Verlust der Exclude-Liste geht es nicht mehr, dass ich
837     // kein neues Attribut benoetige und nur das alte nicht expandiere...
838 //  if ( !bCreateAttrib )
839     {
840         // => Wenn schon Default-Item, dann wenigstens nur dann einstellen,
841         // wenn davor wirklich ein entsprechendes Attribut.
842 //      if ( pNode->GetCharAttribs().FindAttrib( rPoolItem.Which(), nStart ) )
843 //          bCreateAttrib = sal_True;
844         // Aber kleiner Trost:
845         // Die wenigsten schreiben, aendern das Attr, schreiben, und
846         // stellen dann wieder das Default-Attr ein.
847     }
848 
849     // 22.9.95:
850     // Die Uberlegung, einfach das andere Attribut nicht zu expandieren, war
851     // sowieso falsch, da das DefAttr aus einer Vorlage kommen kann,
852     // die irgendwann verschwindet!
853 //  if ( bCreateAttrib )
854 //  {
855         TextCharAttrib* pAttrib = MakeCharAttrib( *pCurPool, rPoolItem, nStart, nEnd );
856         DBG_ASSERT( pAttrib, "MakeCharAttrib fehlgeschlagen!" );
857         pNode->GetCharAttribs().InsertAttrib( pAttrib );
858 //  }
859 //  else
860 //  {
861 //      TextCharAttrib* pTmpAttrib =
862 //          pNode->GetCharAttribs().FindAnyAttrib( rPoolItem.Which() );
863 //      if ( pTmpAttrib )   // sonst benoetige ich es sowieso nicht....
864 //      {
865 //          aExcludeList.Insert( pTmpAttrib->GetItem() );
866 //      }
867 //  }
868 }
869 
870 #pragma SEG_FUNCDEF(editdoc_40)
871 
872 void TextDoc::InsertAttrib( TextNode* pNode, sal_uInt16 nStart, sal_uInt16 nEnd, const SfxPoolItem& rPoolItem )
873 {
874     if ( nStart != nEnd )
875     {
876         InsertAttribInSelection( pNode, nStart, nEnd, rPoolItem );
877     }
878     else
879     {
880         // Pruefen, ob schon ein neues Attribut mit der WhichId an der Stelle:
881         TextCharAttrib* pAttr = pNode->GetCharAttribs().FindEmptyAttrib( rPoolItem.Which(), nStart );
882         if ( pAttr )
883         {
884             // Attribut entfernen....
885             pNode->GetCharAttribs().GetAttribs().Remove(
886                 pNode->GetCharAttribs().GetAttribs().GetPos( pAttr ) );
887         }
888 
889         // pruefen, ob ein 'gleiches' Attribut an der Stelle liegt.
890         pAttr = pNode->GetCharAttribs().FindAttrib( rPoolItem.Which(), nStart );
891         if ( pAttr )
892         {
893             if ( pAttr->IsInside( nStart ) )    // splitten
894             {
895                 // ???????????????????????????????
896                 // eigentlich noch pruefen, ob wirklich splittet, oder return !
897                 // ???????????????????????????????
898                 sal_uInt16 nOldEnd = pAttr->GetEnd();
899                 pAttr->GetEnd() = nStart;
900                 pAttr = MakeCharAttrib( *pCurPool, *(pAttr->GetItem()), nStart, nOldEnd );
901                 pNode->GetCharAttribs().InsertAttrib( pAttr );
902             }
903             else if ( pAttr->GetEnd() == nStart )
904             {
905                 DBG_ASSERT( !pAttr->IsEmpty(), "Doch noch ein leeres Attribut?" );
906                 // pruefen, ob genau das gleiche Attribut
907                 if ( *(pAttr->GetItem()) == rPoolItem )
908                     return;
909             }
910         }
911         InsertAttrib( rPoolItem, pNode, nStart, nStart );
912     }
913 }
914 
915 #pragma SEG_FUNCDEF(editdoc_41)
916 
917 void TextDoc::FindAttribs( TextNode* pNode, sal_uInt16 nStartPos, sal_uInt16 nEndPos, SfxItemSet& rCurSet )
918 {
919     DBG_ASSERT( pNode, "Wo soll ich suchen ?" );
920     DBG_ASSERT( nStartPos <= nEndPos, "Ungueltiger Bereich!" );
921 
922     sal_uInt16 nAttr = 0;
923     TextCharAttrib* pAttr = GetAttrib( pNode->GetCharAttribs().GetAttribs(), nAttr );
924     // keine Selection...
925     if ( nStartPos == nEndPos )
926     {
927         while ( pAttr && ( pAttr->GetStart() <= nEndPos) )
928         {
929             const SfxPoolItem* pItem = 0;
930             // Attribut liegt dadrueber...
931             if ( ( pAttr->GetStart() < nStartPos ) && ( pAttr->GetEnd() > nStartPos ) )
932                 pItem = pAttr->GetItem();
933             // Attribut endet hier, ist nicht leer
934             else if ( ( pAttr->GetStart() < nStartPos ) && ( pAttr->GetEnd() == nStartPos ) )
935             {
936                 if ( !pNode->GetCharAttribs().FindEmptyAttrib( pAttr->GetItem()->Which(), nStartPos ) )
937                     pItem = pAttr->GetItem();
938             }
939             // Attribut endet hier, ist leer
940             else if ( ( pAttr->GetStart() == nStartPos ) && ( pAttr->GetEnd() == nStartPos ) )
941             {
942 //              if ( aExcludeList.FindAttrib( pAttr->GetItem()->Which() ) )
943                     pItem = pAttr->GetItem();
944 //              else if ( pNode->Len() == 0 )   // Sonderfall
945 //                  pItem = pAttr->GetItem();
946             }
947             // Attribut beginnt hier
948             else if ( ( pAttr->GetStart() == nStartPos ) && ( pAttr->GetEnd() > nStartPos ) )
949             {
950                 if ( nStartPos == 0 )   // Sonderfall
951                     pItem = pAttr->GetItem();
952             }
953 
954             if ( pItem )
955             {
956                 sal_uInt16 nWhich = pItem->Which();
957                 if ( rCurSet.GetItemState( nWhich ) == SFX_ITEM_OFF )
958                 {
959                     rCurSet.Put( *pItem );
960                 }
961                 else if ( rCurSet.GetItemState( nWhich ) == SFX_ITEM_ON )
962                 {
963                     const SfxPoolItem& rItem = rCurSet.Get( nWhich );
964                     if ( rItem != *pItem )
965                     {
966                         rCurSet.InvalidateItem( nWhich );
967                     }
968                 }
969             }
970             nAttr++;
971             pAttr = GetAttrib( pNode->GetCharAttribs().GetAttribs(), nAttr );
972         }
973     }
974     else    // Selektion
975     {
976         while ( pAttr && ( pAttr->GetStart() < nEndPos) )
977         {
978             const SfxPoolItem* pItem = 0;
979             // Attribut liegt dadrueber...
980             if ( ( pAttr->GetStart() <= nStartPos ) && ( pAttr->GetEnd() >= nEndPos ) )
981                 pItem = pAttr->GetItem();
982             // Attribut startet mitten drin...
983             else if ( pAttr->GetStart() >= nStartPos )
984             {
985                 // !!! pItem = pAttr->GetItem();
986                 // einfach nur pItem reicht nicht, da ich z.B. bei Shadow
987                 // niemals ein ungleiches Item finden wuerde, da ein solche
988                 // seine Anwesenheit durch Abwesenheit repraesentiert!
989                 // if ( ... )
990                 // Es muesste geprueft werden, on genau das gleiche Attribut
991                 // an der Bruchstelle aufsetzt, was recht aufwendig ist.
992                 // Da ich beim Einfuegen von Attributen aber etwas optimiere
993                 // tritt der Fall nicht so schnell auf...
994                 // Also aus Geschwindigkeitsgruenden:
995                 rCurSet.InvalidateItem( pAttr->GetItem()->Which() );
996 
997             }
998             // Attribut endet mitten drin...
999             else if ( pAttr->GetEnd() > nStartPos )
1000             {
1001                 // pItem = pAttr->GetItem();
1002                 // s.o.
1003 
1004                 // -----------------31.05.95 16:01-------------------
1005                 //  Ist falsch, wenn das gleiche Attribut sofort wieder
1006                 //  eingestellt wird!
1007                 //  => Sollte am besten nicht vorkommen, also gleich beim
1008                 //      Setzen von Attributen richtig machen!
1009                 // --------------------------------------------------
1010                 rCurSet.InvalidateItem( pAttr->GetItem()->Which() );
1011             }
1012 
1013             if ( pItem )
1014             {
1015                 sal_uInt16 nWhich = pItem->Which();
1016                 if ( rCurSet.GetItemState( nWhich ) == SFX_ITEM_OFF )
1017                 {
1018                     rCurSet.Put( *pItem );
1019                 }
1020                 else if ( rCurSet.GetItemState( nWhich ) == SFX_ITEM_ON )
1021                 {
1022                     const SfxPoolItem& rItem = rCurSet.Get( nWhich );
1023                     if ( rItem != *pItem )
1024                     {
1025                         rCurSet.InvalidateItem( nWhich );
1026                     }
1027                 }
1028             }
1029             nAttr++;
1030             pAttr = GetAttrib( pNode->GetCharAttribs().GetAttribs(), nAttr );
1031         }
1032     }
1033 }
1034 
1035 
1036 */
1037 
1038 
1039