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