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