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