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 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 // ------------------------------------------------------------------------- 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 67 TextCharAttrib::TextCharAttrib( const TextCharAttrib& rTextCharAttrib ) 68 { 69 mpAttr = rTextCharAttrib.GetAttr().Clone(); 70 mnStart = rTextCharAttrib.mnStart; 71 mnEnd = rTextCharAttrib.mnEnd; 72 } 73 74 TextCharAttrib::~TextCharAttrib() 75 { 76 delete mpAttr; 77 } 78 79 // ------------------------------------------------------------------------- 80 // (+) class TextCharAttribList 81 // ------------------------------------------------------------------------- 82 83 TextCharAttribList::TextCharAttribList() 84 { 85 mbHasEmptyAttribs = sal_False; 86 } 87 88 TextCharAttribList::~TextCharAttribList() 89 { 90 // PTRARR_DEL 91 } 92 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 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 124 void TextCharAttribList::ResortAttribs() 125 { 126 if ( Count() ) 127 qsort( (void*)GetData(), Count(), sizeof( TextCharAttrib* ), CompareStart ); 128 } 129 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 148 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 163 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 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 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 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 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 245 TextNode::TextNode( const String& rText ) : 246 maText( rText ) 247 { 248 } 249 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 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 388 void TextNode::InsertText( sal_uInt16 nPos, const String& rText ) 389 { 390 maText.Insert( rText, nPos ); 391 ExpandAttribs( nPos, rText.Len() ); 392 } 393 394 void TextNode::InsertText( sal_uInt16 nPos, sal_Unicode c ) 395 { 396 maText.Insert( c, nPos ); 397 ExpandAttribs( nPos, 1 ); 398 } 399 400 void TextNode::RemoveText( sal_uInt16 nPos, sal_uInt16 nChars ) 401 { 402 maText.Erase( nPos, nChars ); 403 CollapsAttribs( nPos, nChars ); 404 } 405 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 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 517 TextDoc::TextDoc() 518 { 519 mnLeftMargin = 0; 520 }; 521 522 TextDoc::~TextDoc() 523 { 524 DestroyTextNodes(); 525 } 526 527 void TextDoc::Clear() 528 { 529 DestroyTextNodes(); 530 } 531 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 539 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 564 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 575 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 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 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 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 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 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 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