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