xref: /aoo42x/main/svtools/source/edit/textdoc.cxx (revision cdf0e10c)
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