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