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