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