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 27 28 #include <tools/stream.hxx> 29 30 #include <svtools/texteng.hxx> 31 #include <svtools/textview.hxx> 32 #include <textdoc.hxx> 33 #include <textdat2.hxx> 34 #include <textundo.hxx> 35 #include <textund2.hxx> 36 #include <svl/ctloptions.hxx> 37 #include <vcl/window.hxx> 38 39 #include <vcl/edit.hxx> 40 #include <com/sun/star/lang/XMultiServiceFactory.hpp> 41 #include <com/sun/star/beans/PropertyValues.hpp> 42 43 #ifndef _COM_SUN_STAR_TEXT_XBREAKITERATOR_HPP_ 44 #include <com/sun/star/i18n/XBreakIterator.hpp> 45 #endif 46 47 #ifndef _COM_SUN_STAR_TEXT_CHARACTERITERATORMODE_HPP_ 48 #include <com/sun/star/i18n/CharacterIteratorMode.hpp> 49 #endif 50 51 #ifndef _COM_SUN_STAR_TEXT_WORDTYPE_HPP_ 52 #include <com/sun/star/i18n/WordType.hpp> 53 #endif 54 55 #ifndef _COM_SUN_STAR_I18N_XEXTENDEDINPUTSEQUENCECHECKER_HDL_ 56 #include <com/sun/star/i18n/XExtendedInputSequenceChecker.hpp> 57 #endif 58 #include <com/sun/star/i18n/InputSequenceCheckMode.hpp> 59 #include <com/sun/star/i18n/ScriptType.hpp> 60 61 #include <comphelper/processfactory.hxx> 62 63 #include <unotools/localedatawrapper.hxx> 64 #include <vcl/unohelp.hxx> 65 66 #include <vcl/svapp.hxx> 67 #include <vcl/unohelp.hxx> 68 #include <vcl/metric.hxx> 69 70 #include <unicode/ubidi.h> 71 72 using namespace ::com::sun::star; 73 using namespace ::com::sun::star::uno; 74 using namespace ::rtl; 75 76 typedef TextView* TextViewPtr; 77 SV_DECL_PTRARR( TextViews, TextViewPtr, 0, 1 ) 78 // SV_IMPL_PTRARR( TextViews, TextViewPtr ); 79 80 SV_DECL_VARARR_SORT( TESortedPositions, sal_uLong, 16, 8 ) 81 SV_IMPL_VARARR_SORT( TESortedPositions, sal_uLong ) 82 83 #define RESDIFF 10 84 #define SCRLRANGE 20 // 1/20 der Breite/Hoehe scrollen, wenn im QueryDrop 85 86 87 // ------------------------------------------------------------------------- 88 // (-) class TextEngine 89 // ------------------------------------------------------------------------- 90 TextEngine::TextEngine() 91 { 92 mpDoc = 0; 93 mpTEParaPortions = 0; 94 95 mpViews = new TextViews; 96 mpActiveView = NULL; 97 98 mbIsFormatting = sal_False; 99 mbFormatted = sal_False; 100 mbUpdate = sal_True; 101 mbModified = sal_False; 102 mbUndoEnabled = sal_False; 103 mbIsInUndo = sal_False; 104 mbDowning = sal_False; 105 mbRightToLeft = sal_False; 106 mbHasMultiLineParas = sal_False; 107 108 meAlign = TXTALIGN_LEFT; 109 110 mnMaxTextWidth = 0; 111 mnMaxTextLen = 0; 112 mnCurTextWidth = 0xFFFFFFFF; 113 mnCurTextHeight = 0; 114 115 mpUndoManager = NULL; 116 mpIMEInfos = NULL; 117 mpLocaleDataWrapper = NULL; 118 119 mpIdleFormatter = new IdleFormatter; 120 mpIdleFormatter->SetTimeoutHdl( LINK( this, TextEngine, IdleFormatHdl ) ); 121 122 mpRefDev = new VirtualDevice; 123 124 ImpInitLayoutMode( mpRefDev ); 125 126 ImpInitDoc(); 127 128 maTextColor = COL_BLACK; 129 Font aFont; 130 aFont.SetTransparent( sal_False ); 131 Color aFillColor( aFont.GetFillColor() ); 132 aFillColor.SetTransparency( 0 ); 133 aFont.SetFillColor( aFillColor ); 134 SetFont( aFont ); 135 } 136 137 TextEngine::~TextEngine() 138 { 139 mbDowning = sal_True; 140 141 delete mpIdleFormatter; 142 delete mpDoc; 143 delete mpTEParaPortions; 144 delete mpViews; // nur die Liste, nicht die Vies 145 delete mpRefDev; 146 delete mpUndoManager; 147 delete mpIMEInfos; 148 delete mpLocaleDataWrapper; 149 } 150 151 void TextEngine::InsertView( TextView* pTextView ) 152 { 153 mpViews->Insert( pTextView, mpViews->Count() ); 154 pTextView->SetSelection( TextSelection() ); 155 156 if ( !GetActiveView() ) 157 SetActiveView( pTextView ); 158 } 159 160 void TextEngine::RemoveView( TextView* pTextView ) 161 { 162 sal_uInt16 nPos = mpViews->GetPos( pTextView ); 163 if( nPos != USHRT_MAX ) 164 { 165 pTextView->HideCursor(); 166 mpViews->Remove( nPos, 1 ); 167 if ( pTextView == GetActiveView() ) 168 SetActiveView( 0 ); 169 } 170 } 171 172 sal_uInt16 TextEngine::GetViewCount() const 173 { 174 return mpViews->Count(); 175 } 176 177 TextView* TextEngine::GetView( sal_uInt16 nView ) const 178 { 179 return mpViews->GetObject( nView ); 180 } 181 182 TextView* TextEngine::GetActiveView() const 183 { 184 return mpActiveView; 185 } 186 187 void TextEngine::SetActiveView( TextView* pTextView ) 188 { 189 if ( pTextView != mpActiveView ) 190 { 191 if ( mpActiveView ) 192 mpActiveView->HideSelection(); 193 194 mpActiveView = pTextView; 195 196 if ( mpActiveView ) 197 mpActiveView->ShowSelection(); 198 } 199 } 200 201 void TextEngine::SetFont( const Font& rFont ) 202 { 203 if ( rFont != maFont ) 204 { 205 maFont = rFont; 206 // #i40221# As the font's color now defaults to transparent (since i35764) 207 // we have to choose a useful textcolor in this case. 208 // Otherwise maTextColor and maFont.GetColor() are both transparent.... 209 if( rFont.GetColor() == COL_TRANSPARENT ) 210 maTextColor = COL_BLACK; 211 else 212 maTextColor = rFont.GetColor(); 213 214 // Wegen Selektion keinen Transparenten Font zulassen... 215 // (Sonst spaeter in ImplPaint den Hintergrund anders loeschen...) 216 maFont.SetTransparent( sal_False ); 217 // Tell VCL not to use the font color, use text color from OutputDevice 218 maFont.SetColor( COL_TRANSPARENT ); 219 Color aFillColor( maFont.GetFillColor() ); 220 aFillColor.SetTransparency( 0 ); 221 maFont.SetFillColor( aFillColor ); 222 223 maFont.SetAlign( ALIGN_TOP ); 224 mpRefDev->SetFont( maFont); 225 Size aTextSize; 226 aTextSize.Width() = mpRefDev->GetTextWidth( String::CreateFromAscii( RTL_CONSTASCII_STRINGPARAM( " " ) ) ); 227 aTextSize.Height() = mpRefDev->GetTextHeight(); 228 if ( !aTextSize.Width() ) 229 aTextSize.Width() = mpRefDev->GetTextWidth( String::CreateFromAscii( RTL_CONSTASCII_STRINGPARAM( "XXXX" ) ) ); 230 231 mnDefTab = (sal_uInt16)aTextSize.Width(); 232 if ( !mnDefTab ) 233 mnDefTab = 1; 234 mnCharHeight = (sal_uInt16)aTextSize.Height(); 235 /* 236 // #93746# Doesn't work with CJK HalfWidth/FullWidth 237 FontMetric aRealFont( mpRefDev->GetFontMetric() ); 238 if ( aRealFont.GetPitch() == PITCH_FIXED ) 239 { 240 String aX100; 241 aX100.Fill( 100, 'X' ); 242 mnFixCharWidth100 = (sal_uInt16)mpRefDev->GetTextWidth( aX100 ); 243 } 244 else 245 */ 246 mnFixCharWidth100 = 0; 247 248 FormatFullDoc(); 249 UpdateViews(); 250 251 for ( sal_uInt16 nView = mpViews->Count(); nView; ) 252 { 253 TextView* pView = mpViews->GetObject( --nView ); 254 pView->GetWindow()->SetInputContext( InputContext( GetFont(), !pView->IsReadOnly() ? INPUTCONTEXT_TEXT|INPUTCONTEXT_EXTTEXTINPUT : 0 ) ); 255 } 256 } 257 } 258 259 void TextEngine::SetDefTab( sal_uInt16 nDefTab ) 260 { 261 mnDefTab = nDefTab; 262 // evtl neu setzen? 263 } 264 265 void TextEngine::SetMaxTextLen( sal_uLong nLen ) 266 { 267 mnMaxTextLen = nLen; 268 } 269 270 void TextEngine::SetMaxTextWidth( sal_uLong nMaxWidth ) 271 { 272 if ( nMaxWidth != mnMaxTextWidth ) 273 { 274 mnMaxTextWidth = Min( nMaxWidth, (sal_uLong)0x7FFFFFFF ); 275 FormatFullDoc(); 276 UpdateViews(); 277 } 278 } 279 280 static sal_Unicode static_aLFText[] = { '\n', 0 }; 281 static sal_Unicode static_aCRText[] = { '\r', 0 }; 282 static sal_Unicode static_aCRLFText[] = { '\r', '\n', 0 }; 283 284 static inline const sal_Unicode* static_getLineEndText( LineEnd aLineEnd ) 285 { 286 const sal_Unicode* pRet = NULL; 287 288 switch( aLineEnd ) 289 { 290 case LINEEND_LF: pRet = static_aLFText;break; 291 case LINEEND_CR: pRet = static_aCRText;break; 292 case LINEEND_CRLF: pRet = static_aCRLFText;break; 293 } 294 return pRet; 295 } 296 297 void TextEngine::ReplaceText(const TextSelection& rSel, const String& rText) 298 { 299 ImpInsertText( rSel, rText ); 300 } 301 302 String TextEngine::GetText( LineEnd aSeparator ) const 303 { 304 return mpDoc->GetText( static_getLineEndText( aSeparator ) ); 305 } 306 307 String TextEngine::GetTextLines( LineEnd aSeparator ) const 308 { 309 String aText; 310 sal_uLong nParas = mpTEParaPortions->Count(); 311 const sal_Unicode* pSep = static_getLineEndText( aSeparator ); 312 for ( sal_uLong nP = 0; nP < nParas; nP++ ) 313 { 314 TEParaPortion* pTEParaPortion = mpTEParaPortions->GetObject( nP ); 315 316 sal_uInt16 nLines = pTEParaPortion->GetLines().Count(); 317 for ( sal_uInt16 nL = 0; nL < nLines; nL++ ) 318 { 319 TextLine* pLine = pTEParaPortion->GetLines()[nL]; 320 aText += pTEParaPortion->GetNode()->GetText().Copy( pLine->GetStart(), pLine->GetEnd() - pLine->GetStart() ); 321 if ( pSep && ( ( (nP+1) < nParas ) || ( (nL+1) < nLines ) ) ) 322 aText += pSep; 323 } 324 } 325 return aText; 326 } 327 328 String TextEngine::GetText( sal_uLong nPara ) const 329 { 330 return mpDoc->GetText( nPara ); 331 } 332 333 sal_uLong TextEngine::GetTextLen( LineEnd aSeparator ) const 334 { 335 return mpDoc->GetTextLen( static_getLineEndText( aSeparator ) ); 336 } 337 338 sal_uLong TextEngine::GetTextLen( const TextSelection& rSel, LineEnd aSeparator ) const 339 { 340 TextSelection aSel( rSel ); 341 aSel.Justify(); 342 ValidateSelection( aSel ); 343 return mpDoc->GetTextLen( static_getLineEndText( aSeparator ), &aSel ); 344 } 345 346 sal_uInt16 TextEngine::GetTextLen( sal_uLong nPara ) const 347 { 348 return mpDoc->GetNodes().GetObject( nPara )->GetText().Len(); 349 } 350 351 void TextEngine::SetUpdateMode( sal_Bool bUpdate ) 352 { 353 if ( bUpdate != mbUpdate ) 354 { 355 mbUpdate = bUpdate; 356 if ( mbUpdate ) 357 { 358 FormatAndUpdate( GetActiveView() ); 359 if ( GetActiveView() ) 360 GetActiveView()->ShowCursor(); 361 } 362 } 363 } 364 365 sal_Bool TextEngine::DoesKeyMoveCursor( const KeyEvent& rKeyEvent ) 366 { 367 sal_Bool bDoesMove = sal_False; 368 369 switch ( rKeyEvent.GetKeyCode().GetCode() ) 370 { 371 case KEY_UP: 372 case KEY_DOWN: 373 case KEY_LEFT: 374 case KEY_RIGHT: 375 case KEY_HOME: 376 case KEY_END: 377 case KEY_PAGEUP: 378 case KEY_PAGEDOWN: 379 { 380 if ( !rKeyEvent.GetKeyCode().IsMod2() ) 381 bDoesMove = sal_True; 382 } 383 break; 384 } 385 return bDoesMove; 386 } 387 388 sal_Bool TextEngine::DoesKeyChangeText( const KeyEvent& rKeyEvent ) 389 { 390 sal_Bool bDoesChange = sal_False; 391 392 KeyFuncType eFunc = rKeyEvent.GetKeyCode().GetFunction(); 393 if ( eFunc != KEYFUNC_DONTKNOW ) 394 { 395 switch ( eFunc ) 396 { 397 case KEYFUNC_UNDO: 398 case KEYFUNC_REDO: 399 case KEYFUNC_CUT: 400 case KEYFUNC_PASTE: bDoesChange = sal_True; 401 break; 402 default: // wird dann evtl. unten bearbeitet. 403 eFunc = KEYFUNC_DONTKNOW; 404 } 405 } 406 if ( eFunc == KEYFUNC_DONTKNOW ) 407 { 408 switch ( rKeyEvent.GetKeyCode().GetCode() ) 409 { 410 case KEY_DELETE: 411 case KEY_BACKSPACE: 412 { 413 if ( !rKeyEvent.GetKeyCode().IsMod2() ) 414 bDoesChange = sal_True; 415 } 416 break; 417 case KEY_RETURN: 418 case KEY_TAB: 419 { 420 if ( !rKeyEvent.GetKeyCode().IsMod1() && !rKeyEvent.GetKeyCode().IsMod2() ) 421 bDoesChange = sal_True; 422 } 423 break; 424 default: 425 { 426 bDoesChange = TextEngine::IsSimpleCharInput( rKeyEvent ); 427 } 428 } 429 } 430 return bDoesChange; 431 } 432 433 sal_Bool TextEngine::IsSimpleCharInput( const KeyEvent& rKeyEvent ) 434 { 435 if( rKeyEvent.GetCharCode() >= 32 && rKeyEvent.GetCharCode() != 127 && 436 KEY_MOD1 != (rKeyEvent.GetKeyCode().GetModifier() & ~KEY_SHIFT) && // (ssa) #i45714#: 437 KEY_MOD2 != (rKeyEvent.GetKeyCode().GetModifier() & ~KEY_SHIFT) ) // check for Ctrl and Alt separately 438 { 439 return sal_True; 440 } 441 return sal_False; 442 } 443 444 void TextEngine::ImpInitDoc() 445 { 446 if ( mpDoc ) 447 mpDoc->Clear(); 448 else 449 mpDoc = new TextDoc; 450 451 delete mpTEParaPortions; 452 mpTEParaPortions = new TEParaPortions; 453 454 TextNode* pNode = new TextNode( String() ); 455 mpDoc->GetNodes().Insert( pNode, 0 ); 456 457 TEParaPortion* pIniPortion = new TEParaPortion( pNode ); 458 mpTEParaPortions->Insert( pIniPortion, (sal_uLong)0 ); 459 460 mbFormatted = sal_False; 461 462 ImpParagraphRemoved( TEXT_PARA_ALL ); 463 ImpParagraphInserted( 0 ); 464 } 465 466 String TextEngine::GetText( const TextSelection& rSel, LineEnd aSeparator ) const 467 { 468 String aText; 469 470 if ( !rSel.HasRange() ) 471 return aText; 472 473 TextSelection aSel( rSel ); 474 aSel.Justify(); 475 476 sal_uLong nStartPara = aSel.GetStart().GetPara(); 477 sal_uLong nEndPara = aSel.GetEnd().GetPara(); 478 const sal_Unicode* pSep = static_getLineEndText( aSeparator ); 479 for ( sal_uLong nNode = aSel.GetStart().GetPara(); nNode <= nEndPara; nNode++ ) 480 { 481 TextNode* pNode = mpDoc->GetNodes().GetObject( nNode ); 482 483 sal_uInt16 nStartPos = 0; 484 sal_uInt16 nEndPos = pNode->GetText().Len(); 485 if ( nNode == nStartPara ) 486 nStartPos = aSel.GetStart().GetIndex(); 487 if ( nNode == nEndPara ) // kann auch == nStart sein! 488 nEndPos = aSel.GetEnd().GetIndex(); 489 490 aText += pNode->GetText().Copy( nStartPos, nEndPos-nStartPos ); 491 if ( nNode < nEndPara ) 492 aText += pSep; 493 } 494 return aText; 495 } 496 497 void TextEngine::ImpRemoveText() 498 { 499 ImpInitDoc(); 500 501 TextPaM aStartPaM( 0, 0 ); 502 TextSelection aEmptySel( aStartPaM, aStartPaM ); 503 for ( sal_uInt16 nView = 0; nView < mpViews->Count(); nView++ ) 504 { 505 TextView* pView = mpViews->GetObject( nView ); 506 pView->ImpSetSelection( aEmptySel ); 507 } 508 ResetUndo(); 509 } 510 511 void TextEngine::SetText( const XubString& rText ) 512 { 513 ImpRemoveText(); 514 515 sal_Bool bUndoCurrentlyEnabled = IsUndoEnabled(); 516 // Der von Hand reingesteckte Text kann nicht vom Anwender rueckgaengig gemacht werden. 517 EnableUndo( sal_False ); 518 519 TextPaM aStartPaM( 0, 0 ); 520 TextSelection aEmptySel( aStartPaM, aStartPaM ); 521 522 TextPaM aPaM = aStartPaM; 523 if ( rText.Len() ) 524 aPaM = ImpInsertText( aEmptySel, rText ); 525 526 for ( sal_uInt16 nView = 0; nView < mpViews->Count(); nView++ ) 527 { 528 TextView* pView = mpViews->GetObject( nView ); 529 pView->ImpSetSelection( aEmptySel ); 530 531 // Wenn kein Text, dann auch Kein Format&Update 532 // => Der Text bleibt stehen. 533 if ( !rText.Len() && GetUpdateMode() ) 534 pView->Invalidate(); 535 } 536 537 if( !rText.Len() ) // sonst muss spaeter noch invalidiert werden, !bFormatted reicht. 538 mnCurTextHeight = 0; 539 540 FormatAndUpdate(); 541 542 EnableUndo( bUndoCurrentlyEnabled ); 543 DBG_ASSERT( !HasUndoManager() || !GetUndoManager().GetUndoActionCount(), "Undo nach SetText?" ); 544 } 545 546 547 void TextEngine::CursorMoved( sal_uLong nNode ) 548 { 549 // Leere Attribute loeschen, aber nur, wenn Absatz nicht leer! 550 TextNode* pNode = mpDoc->GetNodes().GetObject( nNode ); 551 if ( pNode && pNode->GetCharAttribs().HasEmptyAttribs() && pNode->GetText().Len() ) 552 pNode->GetCharAttribs().DeleteEmptyAttribs(); 553 } 554 555 void TextEngine::ImpRemoveChars( const TextPaM& rPaM, sal_uInt16 nChars, SfxUndoAction* ) 556 { 557 DBG_ASSERT( nChars, "ImpRemoveChars - 0 Chars?!" ); 558 if ( IsUndoEnabled() && !IsInUndo() ) 559 { 560 // Attribute muessen hier vorm RemoveChars fuer UNDO gesichert werden! 561 TextNode* pNode = mpDoc->GetNodes().GetObject( rPaM.GetPara() ); 562 XubString aStr( pNode->GetText().Copy( rPaM.GetIndex(), nChars ) ); 563 564 // Pruefen, ob Attribute geloescht oder geaendert werden: 565 sal_uInt16 nStart = rPaM.GetIndex(); 566 sal_uInt16 nEnd = nStart + nChars; 567 for ( sal_uInt16 nAttr = pNode->GetCharAttribs().Count(); nAttr; ) 568 { 569 TextCharAttrib* pAttr = pNode->GetCharAttribs().GetAttrib( --nAttr ); 570 if ( ( pAttr->GetEnd() >= nStart ) && ( pAttr->GetStart() < nEnd ) ) 571 { 572 // TextSelection aSel( rPaM ); 573 // aSel.GetEnd().GetIndex() += nChars; 574 // TextUndoSetAttribs* pAttrUndo = CreateAttribUndo( aSel ); 575 // InsertUndo( pAttrUndo ); 576 break; // for 577 } 578 } 579 // if ( pCurUndo && ( CreateTextPaM( pCurUndo->GetEPaM() ) == rPaM ) ) 580 // pCurUndo->GetStr() += aStr; 581 // else 582 InsertUndo( new TextUndoRemoveChars( this, rPaM, aStr ) ); 583 } 584 585 mpDoc->RemoveChars( rPaM, nChars ); 586 ImpCharsRemoved( rPaM.GetPara(), rPaM.GetIndex(), nChars ); 587 } 588 589 TextPaM TextEngine::ImpConnectParagraphs( sal_uLong nLeft, sal_uLong nRight ) 590 { 591 DBG_ASSERT( nLeft != nRight, "Den gleichen Absatz zusammenfuegen ?" ); 592 593 TextNode* pLeft = mpDoc->GetNodes().GetObject( nLeft ); 594 TextNode* pRight = mpDoc->GetNodes().GetObject( nRight ); 595 596 if ( IsUndoEnabled() && !IsInUndo() ) 597 InsertUndo( new TextUndoConnectParas( this, nLeft, pLeft->GetText().Len() ) ); 598 599 // Erstmal Portions suchen, da pRight nach ConnectParagraphs weg. 600 TEParaPortion* pLeftPortion = mpTEParaPortions->GetObject( nLeft ); 601 TEParaPortion* pRightPortion = mpTEParaPortions->GetObject( nRight ); 602 DBG_ASSERT( pLeft && pLeftPortion, "Blinde Portion in ImpConnectParagraphs(1)" ); 603 DBG_ASSERT( pRight && pRightPortion, "Blinde Portion in ImpConnectParagraphs(2)" ); 604 605 TextPaM aPaM = mpDoc->ConnectParagraphs( pLeft, pRight ); 606 ImpParagraphRemoved( nRight ); 607 608 pLeftPortion->MarkSelectionInvalid( aPaM.GetIndex(), pLeft->GetText().Len() ); 609 610 mpTEParaPortions->Remove( nRight ); 611 delete pRightPortion; 612 // der rechte Node wird von EditDoc::ConnectParagraphs() geloescht. 613 614 return aPaM; 615 } 616 617 TextPaM TextEngine::ImpDeleteText( const TextSelection& rSel ) 618 { 619 if ( !rSel.HasRange() ) 620 return rSel.GetStart(); 621 622 TextSelection aSel( rSel ); 623 aSel.Justify(); 624 TextPaM aStartPaM( aSel.GetStart() ); 625 TextPaM aEndPaM( aSel.GetEnd() ); 626 627 CursorMoved( aStartPaM.GetPara() ); // nur damit neu eingestellte Attribute verschwinden... 628 CursorMoved( aEndPaM.GetPara() ); // nur damit neu eingestellte Attribute verschwinden... 629 630 DBG_ASSERT( mpDoc->IsValidPaM( aStartPaM ), "Index im Wald in ImpDeleteText" ); 631 DBG_ASSERT( mpDoc->IsValidPaM( aEndPaM ), "Index im Wald in ImpDeleteText" ); 632 633 sal_uLong nStartNode = aStartPaM.GetPara(); 634 sal_uLong nEndNode = aEndPaM.GetPara(); 635 636 // Alle Nodes dazwischen entfernen.... 637 for ( sal_uLong z = nStartNode+1; z < nEndNode; z++ ) 638 { 639 // Immer nStartNode+1, wegen Remove()! 640 ImpRemoveParagraph( nStartNode+1 ); 641 } 642 643 if ( nStartNode != nEndNode ) 644 { 645 // Den Rest des StartNodes... 646 TextNode* pLeft = mpDoc->GetNodes().GetObject( nStartNode ); 647 sal_uInt16 nChars = pLeft->GetText().Len() - aStartPaM.GetIndex(); 648 if ( nChars ) 649 { 650 ImpRemoveChars( aStartPaM, nChars ); 651 TEParaPortion* pPortion = mpTEParaPortions->GetObject( nStartNode ); 652 DBG_ASSERT( pPortion, "Blinde Portion in ImpDeleteText(3)" ); 653 pPortion->MarkSelectionInvalid( aStartPaM.GetIndex(), pLeft->GetText().Len() ); 654 } 655 656 // Den Anfang des EndNodes.... 657 nEndNode = nStartNode+1; // Die anderen Absaetze wurden geloescht 658 nChars = aEndPaM.GetIndex(); 659 if ( nChars ) 660 { 661 aEndPaM.GetPara() = nEndNode; 662 aEndPaM.GetIndex() = 0; 663 ImpRemoveChars( aEndPaM, nChars ); 664 TEParaPortion* pPortion = mpTEParaPortions->GetObject( nEndNode ); 665 DBG_ASSERT( pPortion, "Blinde Portion in ImpDeleteText(4)" ); 666 pPortion->MarkSelectionInvalid( 0, pPortion->GetNode()->GetText().Len() ); 667 } 668 669 // Zusammenfuegen.... 670 aStartPaM = ImpConnectParagraphs( nStartNode, nEndNode ); 671 } 672 else 673 { 674 sal_uInt16 nChars; 675 nChars = aEndPaM.GetIndex() - aStartPaM.GetIndex(); 676 ImpRemoveChars( aStartPaM, nChars ); 677 TEParaPortion* pPortion = mpTEParaPortions->GetObject( nStartNode ); 678 DBG_ASSERT( pPortion, "Blinde Portion in ImpDeleteText(5)" ); 679 pPortion->MarkInvalid( aEndPaM.GetIndex(), aStartPaM.GetIndex() - aEndPaM.GetIndex() ); 680 } 681 682 // UpdateSelections(); 683 TextModified(); 684 return aStartPaM; 685 } 686 687 void TextEngine::ImpRemoveParagraph( sal_uLong nPara ) 688 { 689 TextNode* pNode = mpDoc->GetNodes().GetObject( nPara ); 690 TEParaPortion* pPortion = mpTEParaPortions->GetObject( nPara ); 691 692 // Der Node wird vom Undo verwaltet und ggf. zerstoert! 693 /* delete */ mpDoc->GetNodes().Remove( nPara ); 694 if ( IsUndoEnabled() && !IsInUndo() ) 695 InsertUndo( new TextUndoDelPara( this, pNode, nPara ) ); 696 else 697 delete pNode; 698 699 mpTEParaPortions->Remove( nPara ); 700 delete pPortion; 701 702 ImpParagraphRemoved( nPara ); 703 } 704 705 uno::Reference < i18n::XExtendedInputSequenceChecker > TextEngine::GetInputSequenceChecker() const 706 { 707 uno::Reference < i18n::XExtendedInputSequenceChecker > xISC; 708 // if ( !xISC.is() ) 709 { 710 uno::Reference< lang::XMultiServiceFactory > xMSF = ::comphelper::getProcessServiceFactory(); 711 uno::Reference< uno::XInterface > xI = xMSF->createInstance( OUString::createFromAscii( "com.sun.star.i18n.InputSequenceChecker" ) ); 712 if ( xI.is() ) 713 { 714 Any x = xI->queryInterface( ::getCppuType((const uno::Reference< i18n::XExtendedInputSequenceChecker >*)0) ); 715 x >>= xISC; 716 } 717 } 718 return xISC; 719 } 720 721 sal_Bool TextEngine::IsInputSequenceCheckingRequired( sal_Unicode c, const TextSelection& rCurSel ) const 722 { 723 uno::Reference< i18n::XBreakIterator > xBI = ((TextEngine *) this)->GetBreakIterator(); 724 SvtCTLOptions aCTLOptions; 725 726 // get the index that really is first 727 sal_uInt16 nFirstPos = rCurSel.GetStart().GetIndex(); 728 sal_uInt16 nMaxPos = rCurSel.GetEnd().GetIndex(); 729 if (nMaxPos < nFirstPos) 730 nFirstPos = nMaxPos; 731 732 sal_Bool bIsSequenceChecking = 733 aCTLOptions.IsCTLFontEnabled() && 734 aCTLOptions.IsCTLSequenceChecking() && 735 nFirstPos != 0 && /* first char needs not to be checked */ 736 xBI.is() && i18n::ScriptType::COMPLEX == xBI->getScriptType( rtl::OUString( c ), 0 ); 737 738 return bIsSequenceChecking; 739 } 740 741 TextPaM TextEngine::ImpInsertText( const TextSelection& rCurSel, sal_Unicode c, sal_Bool bOverwrite ) 742 { 743 return ImpInsertText( c, rCurSel, bOverwrite, sal_False ); 744 } 745 746 TextPaM TextEngine::ImpInsertText( sal_Unicode c, const TextSelection& rCurSel, sal_Bool bOverwrite, sal_Bool bIsUserInput ) 747 { 748 DBG_ASSERT( c != '\n', "Zeilenumbruch bei InsertText ?" ); 749 DBG_ASSERT( c != '\r', "Zeilenumbruch bei InsertText ?" ); 750 751 TextPaM aPaM( rCurSel.GetStart() ); 752 TextNode* pNode = mpDoc->GetNodes().GetObject( aPaM.GetPara() ); 753 754 if ( pNode->GetText().Len() < STRING_MAXLEN ) 755 { 756 sal_Bool bDoOverwrite = ( bOverwrite && 757 ( aPaM.GetIndex() < pNode->GetText().Len() ) ) ? sal_True : sal_False; 758 759 sal_Bool bUndoAction = ( rCurSel.HasRange() || bDoOverwrite ); 760 761 if ( bUndoAction ) 762 UndoActionStart(); 763 764 if ( rCurSel.HasRange() ) 765 { 766 aPaM = ImpDeleteText( rCurSel ); 767 } 768 else if ( bDoOverwrite ) 769 { 770 // Wenn Selektion, dann kein Zeichen ueberschreiben 771 TextSelection aTmpSel( aPaM ); 772 aTmpSel.GetEnd().GetIndex()++; 773 ImpDeleteText( aTmpSel ); 774 } 775 776 if (bIsUserInput && IsInputSequenceCheckingRequired( c, rCurSel )) 777 { 778 uno::Reference < i18n::XExtendedInputSequenceChecker > xISC = GetInputSequenceChecker(); 779 SvtCTLOptions aCTLOptions; 780 781 if (xISC.is()) 782 { 783 xub_StrLen nTmpPos = aPaM.GetIndex(); 784 sal_Int16 nCheckMode = aCTLOptions.IsCTLSequenceCheckingRestricted() ? 785 i18n::InputSequenceCheckMode::STRICT : i18n::InputSequenceCheckMode::BASIC; 786 787 // the text that needs to be checked is only the one 788 // before the current cursor position 789 rtl::OUString aOldText( mpDoc->GetText( aPaM.GetPara() ).Copy(0, nTmpPos) ); 790 rtl::OUString aNewText( aOldText ); 791 if (aCTLOptions.IsCTLSequenceCheckingTypeAndReplace()) 792 { 793 xISC->correctInputSequence( aNewText, nTmpPos - 1, c, nCheckMode ); 794 795 // find position of first character that has changed 796 sal_Int32 nOldLen = aOldText.getLength(); 797 sal_Int32 nNewLen = aNewText.getLength(); 798 const sal_Unicode *pOldTxt = aOldText.getStr(); 799 const sal_Unicode *pNewTxt = aNewText.getStr(); 800 sal_Int32 nChgPos = 0; 801 while ( nChgPos < nOldLen && nChgPos < nNewLen && 802 pOldTxt[nChgPos] == pNewTxt[nChgPos] ) 803 ++nChgPos; 804 805 xub_StrLen nChgLen = static_cast< xub_StrLen >(nNewLen - nChgPos); 806 String aChgText( aNewText.copy( nChgPos ).getStr(), nChgLen ); 807 808 // select text from first pos to be changed to current pos 809 TextSelection aSel( TextPaM( aPaM.GetPara(), (sal_uInt16) nChgPos ), aPaM ); 810 811 if (aChgText.Len()) 812 // ImpInsertText implicitly handles undo... 813 return ImpInsertText( aSel, aChgText ); 814 else 815 return aPaM; 816 } 817 else 818 { 819 // should the character be ignored (i.e. not get inserted) ? 820 if (!xISC->checkInputSequence( aOldText, nTmpPos - 1, c, nCheckMode )) 821 return aPaM; // nothing to be done -> no need for undo 822 } 823 } 824 825 // at this point now we will insert the character 'normally' some lines below... 826 } 827 828 829 if ( IsUndoEnabled() && !IsInUndo() ) 830 { 831 TextUndoInsertChars* pNewUndo = new TextUndoInsertChars( this, aPaM, c ); 832 sal_Bool bTryMerge = ( !bDoOverwrite && ( c != ' ' ) ) ? sal_True : sal_False; 833 InsertUndo( pNewUndo, bTryMerge ); 834 } 835 836 TEParaPortion* pPortion = mpTEParaPortions->GetObject( aPaM.GetPara() ); 837 pPortion->MarkInvalid( aPaM.GetIndex(), 1 ); 838 if ( c == '\t' ) 839 pPortion->SetNotSimpleInvalid(); 840 aPaM = mpDoc->InsertText( aPaM, c ); 841 ImpCharsInserted( aPaM.GetPara(), aPaM.GetIndex()-1, 1 ); 842 843 TextModified(); 844 845 if ( bUndoAction ) 846 UndoActionEnd(); 847 } 848 849 return aPaM; 850 } 851 852 853 TextPaM TextEngine::ImpInsertText( const TextSelection& rCurSel, const XubString& rStr ) 854 { 855 UndoActionStart(); 856 857 TextPaM aPaM; 858 859 if ( rCurSel.HasRange() ) 860 aPaM = ImpDeleteText( rCurSel ); 861 else 862 aPaM = rCurSel.GetEnd(); 863 864 XubString aText( rStr ); 865 aText.ConvertLineEnd( LINEEND_LF ); 866 867 sal_uInt16 nStart = 0; 868 while ( nStart < aText.Len() ) 869 { 870 sal_uInt16 nEnd = aText.Search( LINE_SEP, nStart ); 871 if ( nEnd == STRING_NOTFOUND ) 872 nEnd = aText.Len(); // nicht dereferenzieren! 873 874 // Start == End => Leerzeile 875 if ( nEnd > nStart ) 876 { 877 sal_uLong nL = aPaM.GetIndex(); 878 nL += ( nEnd-nStart ); 879 if ( nL > STRING_MAXLEN ) 880 { 881 sal_uInt16 nDiff = (sal_uInt16) (nL-STRING_MAXLEN); 882 nEnd = nEnd - nDiff; 883 } 884 885 XubString aLine( aText, nStart, nEnd-nStart ); 886 if ( IsUndoEnabled() && !IsInUndo() ) 887 InsertUndo( new TextUndoInsertChars( this, aPaM, aLine ) ); 888 889 TEParaPortion* pPortion = mpTEParaPortions->GetObject( aPaM.GetPara() ); 890 pPortion->MarkInvalid( aPaM.GetIndex(), aLine.Len() ); 891 if ( aLine.Search( '\t' ) != STRING_NOTFOUND ) 892 pPortion->SetNotSimpleInvalid(); 893 894 aPaM = mpDoc->InsertText( aPaM, aLine ); 895 ImpCharsInserted( aPaM.GetPara(), aPaM.GetIndex()-aLine.Len(), aLine.Len() ); 896 897 } 898 if ( nEnd < aText.Len() ) 899 aPaM = ImpInsertParaBreak( aPaM ); 900 901 nStart = nEnd+1; 902 903 if ( nStart < nEnd ) // #108611# overflow 904 break; 905 } 906 907 UndoActionEnd(); 908 909 TextModified(); 910 return aPaM; 911 } 912 913 TextPaM TextEngine::ImpInsertParaBreak( const TextSelection& rCurSel, sal_Bool bKeepEndingAttribs ) 914 { 915 TextPaM aPaM; 916 if ( rCurSel.HasRange() ) 917 aPaM = ImpDeleteText( rCurSel ); 918 else 919 aPaM = rCurSel.GetEnd(); 920 921 return ImpInsertParaBreak( aPaM, bKeepEndingAttribs ); 922 } 923 924 TextPaM TextEngine::ImpInsertParaBreak( const TextPaM& rPaM, sal_Bool bKeepEndingAttribs ) 925 { 926 if ( IsUndoEnabled() && !IsInUndo() ) 927 InsertUndo( new TextUndoSplitPara( this, rPaM.GetPara(), rPaM.GetIndex() ) ); 928 929 TextNode* pNode = mpDoc->GetNodes().GetObject( rPaM.GetPara() ); 930 sal_Bool bFirstParaContentChanged = rPaM.GetIndex() < pNode->GetText().Len(); 931 932 TextPaM aPaM( mpDoc->InsertParaBreak( rPaM, bKeepEndingAttribs ) ); 933 934 TEParaPortion* pPortion = mpTEParaPortions->GetObject( rPaM.GetPara() ); 935 DBG_ASSERT( pPortion, "Blinde Portion in ImpInsertParaBreak" ); 936 pPortion->MarkInvalid( rPaM.GetIndex(), 0 ); 937 938 TextNode* pNewNode = mpDoc->GetNodes().GetObject( aPaM.GetPara() ); 939 TEParaPortion* pNewPortion = new TEParaPortion( pNewNode ); 940 mpTEParaPortions->Insert( pNewPortion, aPaM.GetPara() ); 941 ImpParagraphInserted( aPaM.GetPara() ); 942 943 CursorMoved( rPaM.GetPara() ); // falls leeres Attribut entstanden. 944 TextModified(); 945 946 if ( bFirstParaContentChanged ) 947 Broadcast( TextHint( TEXT_HINT_PARACONTENTCHANGED, rPaM.GetPara() ) ); 948 949 return aPaM; 950 } 951 952 Rectangle TextEngine::PaMtoEditCursor( const TextPaM& rPaM, sal_Bool bSpecial ) 953 { 954 DBG_ASSERT( GetUpdateMode(), "Darf bei Update=sal_False nicht erreicht werden: PaMtoEditCursor" ); 955 956 Rectangle aEditCursor; 957 long nY = 0; 958 959 if ( !mbHasMultiLineParas ) 960 { 961 nY = rPaM.GetPara() * mnCharHeight; 962 } 963 else 964 { 965 for ( sal_uLong nPortion = 0; nPortion < rPaM.GetPara(); nPortion++ ) 966 { 967 TEParaPortion* pPortion = mpTEParaPortions->GetObject(nPortion); 968 nY += pPortion->GetLines().Count() * mnCharHeight; 969 } 970 } 971 972 aEditCursor = GetEditCursor( rPaM, bSpecial ); 973 aEditCursor.Top() += nY; 974 aEditCursor.Bottom() += nY; 975 return aEditCursor; 976 } 977 978 Rectangle TextEngine::GetEditCursor( const TextPaM& rPaM, sal_Bool bSpecial, sal_Bool bPreferPortionStart ) 979 { 980 if ( !IsFormatted() && !IsFormatting() ) 981 FormatAndUpdate(); 982 983 TEParaPortion* pPortion = mpTEParaPortions->GetObject( rPaM.GetPara() ); 984 //TextNode* pNode = mpDoc->GetNodes().GetObject( rPaM.GetPara() ); 985 986 /* 987 bSpecial: Wenn hinter dem letzten Zeichen einer umgebrochenen Zeile, 988 am Ende der Zeile bleiben, nicht am Anfang der naechsten. 989 Zweck: - END => wirklich hinter das letzte Zeichen 990 - Selektion.... 991 bSpecial: If behind the last character of a made up line, stay at the 992 end of the line, not at the start of the next line. 993 Purpose: - really END = > behind the last character 994 - to selection... 995 996 */ 997 998 long nY = 0; 999 sal_uInt16 nCurIndex = 0; 1000 TextLine* pLine = 0; 1001 for ( sal_uInt16 nLine = 0; nLine < pPortion->GetLines().Count(); nLine++ ) 1002 { 1003 TextLine* pTmpLine = pPortion->GetLines().GetObject( nLine ); 1004 if ( ( pTmpLine->GetStart() == rPaM.GetIndex() ) || ( pTmpLine->IsIn( rPaM.GetIndex(), bSpecial ) ) ) 1005 { 1006 pLine = pTmpLine; 1007 break; 1008 } 1009 1010 nCurIndex = nCurIndex + pTmpLine->GetLen(); 1011 nY += mnCharHeight; 1012 } 1013 if ( !pLine ) 1014 { 1015 // Cursor am Ende des Absatzes. 1016 DBG_ASSERT( rPaM.GetIndex() == nCurIndex, "Index voll daneben in GetEditCursor!" ); 1017 1018 pLine = pPortion->GetLines().GetObject( pPortion->GetLines().Count()-1 ); 1019 nY -= mnCharHeight; 1020 nCurIndex = nCurIndex - pLine->GetLen(); 1021 } 1022 1023 Rectangle aEditCursor; 1024 1025 aEditCursor.Top() = nY; 1026 nY += mnCharHeight; 1027 aEditCursor.Bottom() = nY-1; 1028 1029 // innerhalb der Zeile suchen.... 1030 long nX = ImpGetXPos( rPaM.GetPara(), pLine, rPaM.GetIndex(), bPreferPortionStart ); 1031 aEditCursor.Left() = aEditCursor.Right() = nX; 1032 return aEditCursor; 1033 } 1034 1035 long TextEngine::ImpGetXPos( sal_uLong nPara, TextLine* pLine, sal_uInt16 nIndex, sal_Bool bPreferPortionStart ) 1036 { 1037 DBG_ASSERT( ( nIndex >= pLine->GetStart() ) && ( nIndex <= pLine->GetEnd() ) , "ImpGetXPos muss richtig gerufen werden!" ); 1038 1039 sal_Bool bDoPreferPortionStart = bPreferPortionStart; 1040 // Assure that the portion belongs to this line: 1041 if ( nIndex == pLine->GetStart() ) 1042 bDoPreferPortionStart = sal_True; 1043 else if ( nIndex == pLine->GetEnd() ) 1044 bDoPreferPortionStart = sal_False; 1045 1046 TEParaPortion* pParaPortion = mpTEParaPortions->GetObject( nPara ); 1047 1048 sal_uInt16 nTextPortionStart = 0; 1049 sal_uInt16 nTextPortion = pParaPortion->GetTextPortions().FindPortion( nIndex, nTextPortionStart, bDoPreferPortionStart ); 1050 1051 DBG_ASSERT( ( nTextPortion >= pLine->GetStartPortion() ) && ( nTextPortion <= pLine->GetEndPortion() ), "GetXPos: Portion not in current line! " ); 1052 1053 TETextPortion* pPortion = pParaPortion->GetTextPortions().GetObject( nTextPortion ); 1054 1055 long nX = ImpGetPortionXOffset( nPara, pLine, nTextPortion ); 1056 1057 long nPortionTextWidth = pPortion->GetWidth(); 1058 1059 if ( nTextPortionStart != nIndex ) 1060 { 1061 // Search within portion... 1062 if ( nIndex == ( nTextPortionStart + pPortion->GetLen() ) ) 1063 { 1064 // End of Portion 1065 if ( ( pPortion->GetKind() == PORTIONKIND_TAB ) || 1066 ( !IsRightToLeft() && !pPortion->IsRightToLeft() ) || 1067 ( IsRightToLeft() && pPortion->IsRightToLeft() ) ) 1068 { 1069 nX += nPortionTextWidth; 1070 if ( ( pPortion->GetKind() == PORTIONKIND_TAB ) && ( (nTextPortion+1) < pParaPortion->GetTextPortions().Count() ) ) 1071 { 1072 TETextPortion* pNextPortion = pParaPortion->GetTextPortions().GetObject( nTextPortion+1 ); 1073 if ( ( pNextPortion->GetKind() != PORTIONKIND_TAB ) && ( 1074 ( !IsRightToLeft() && pNextPortion->IsRightToLeft() ) || 1075 ( IsRightToLeft() && !pNextPortion->IsRightToLeft() ) ) ) 1076 { 1077 // nX += pNextPortion->GetWidth(); 1078 // End of the tab portion, use start of next for cursor pos 1079 DBG_ASSERT( !bPreferPortionStart, "ImpGetXPos - How can we this tab portion here???" ); 1080 nX = ImpGetXPos( nPara, pLine, nIndex, sal_True ); 1081 } 1082 1083 } 1084 } 1085 } 1086 else if ( pPortion->GetKind() == PORTIONKIND_TEXT ) 1087 { 1088 DBG_ASSERT( nIndex != pLine->GetStart(), "Strange behavior in new ImpGetXPos()" ); 1089 1090 long nPosInPortion = (long)CalcTextWidth( nPara, nTextPortionStart, nIndex-nTextPortionStart ); 1091 1092 if ( ( !IsRightToLeft() && !pPortion->IsRightToLeft() ) || 1093 ( IsRightToLeft() && pPortion->IsRightToLeft() ) ) 1094 { 1095 nX += nPosInPortion; 1096 } 1097 else 1098 { 1099 nX += nPortionTextWidth - nPosInPortion; 1100 } 1101 } 1102 } 1103 else // if ( nIndex == pLine->GetStart() ) 1104 { 1105 if ( ( pPortion->GetKind() != PORTIONKIND_TAB ) && 1106 ( ( !IsRightToLeft() && pPortion->IsRightToLeft() ) || 1107 ( IsRightToLeft() && !pPortion->IsRightToLeft() ) ) ) 1108 { 1109 nX += nPortionTextWidth; 1110 } 1111 } 1112 1113 return nX; 1114 } 1115 1116 const TextAttrib* TextEngine::FindAttrib( const TextPaM& rPaM, sal_uInt16 nWhich ) const 1117 { 1118 const TextAttrib* pAttr = NULL; 1119 const TextCharAttrib* pCharAttr = FindCharAttrib( rPaM, nWhich ); 1120 if ( pCharAttr ) 1121 pAttr = &pCharAttr->GetAttr(); 1122 return pAttr; 1123 } 1124 1125 const TextCharAttrib* TextEngine::FindCharAttrib( const TextPaM& rPaM, sal_uInt16 nWhich ) const 1126 { 1127 const TextCharAttrib* pAttr = NULL; 1128 TextNode* pNode = mpDoc->GetNodes().GetObject( rPaM.GetPara() ); 1129 if ( pNode && ( rPaM.GetIndex() < pNode->GetText().Len() ) ) 1130 pAttr = pNode->GetCharAttribs().FindAttrib( nWhich, rPaM.GetIndex() ); 1131 return pAttr; 1132 } 1133 1134 sal_Bool TextEngine::HasAttrib( sal_uInt16 nWhich ) const 1135 { 1136 sal_Bool bAttr = sal_False; 1137 for ( sal_uLong n = mpDoc->GetNodes().Count(); --n && !bAttr; ) 1138 { 1139 TextNode* pNode = mpDoc->GetNodes().GetObject( n ); 1140 bAttr = pNode->GetCharAttribs().HasAttrib( nWhich ); 1141 } 1142 return bAttr; 1143 } 1144 1145 TextPaM TextEngine::GetPaM( const Point& rDocPos, sal_Bool bSmart ) 1146 { 1147 DBG_ASSERT( GetUpdateMode(), "Darf bei Update=sal_False nicht erreicht werden: GetPaM" ); 1148 1149 long nY = 0; 1150 for ( sal_uLong nPortion = 0; nPortion < mpTEParaPortions->Count(); nPortion++ ) 1151 { 1152 TEParaPortion* pPortion = mpTEParaPortions->GetObject( nPortion ); 1153 long nTmpHeight = pPortion->GetLines().Count() * mnCharHeight; 1154 nY += nTmpHeight; 1155 if ( nY > rDocPos.Y() ) 1156 { 1157 nY -= nTmpHeight; 1158 Point aPosInPara( rDocPos ); 1159 aPosInPara.Y() -= nY; 1160 1161 TextPaM aPaM( nPortion, 0 ); 1162 aPaM.GetIndex() = ImpFindIndex( nPortion, aPosInPara, bSmart ); 1163 return aPaM; 1164 } 1165 } 1166 1167 // Nicht gefunden - Dann den letzten sichtbare... 1168 sal_uLong nLastNode = mpDoc->GetNodes().Count() - 1; 1169 TextNode* pLast = mpDoc->GetNodes().GetObject( nLastNode ); 1170 return TextPaM( nLastNode, pLast->GetText().Len() ); 1171 } 1172 1173 sal_uInt16 TextEngine::ImpFindIndex( sal_uLong nPortion, const Point& rPosInPara, sal_Bool bSmart ) 1174 { 1175 DBG_ASSERT( IsFormatted(), "GetPaM: Nicht formatiert" ); 1176 TEParaPortion* pPortion = mpTEParaPortions->GetObject( nPortion ); 1177 1178 sal_uInt16 nCurIndex = 0; 1179 1180 long nY = 0; 1181 TextLine* pLine = 0; 1182 sal_uInt16 nLine; 1183 for ( nLine = 0; nLine < pPortion->GetLines().Count(); nLine++ ) 1184 { 1185 TextLine* pTmpLine = pPortion->GetLines().GetObject( nLine ); 1186 nY += mnCharHeight; 1187 if ( nY > rPosInPara.Y() ) // das war 'se 1188 { 1189 pLine = pTmpLine; 1190 break; // richtige Y-Position intressiert nicht 1191 } 1192 } 1193 DBG_ASSERT( pLine, "ImpFindIndex: pLine ?" ); 1194 1195 nCurIndex = GetCharPos( nPortion, nLine, rPosInPara.X(), bSmart ); 1196 1197 if ( nCurIndex && ( nCurIndex == pLine->GetEnd() ) && 1198 ( pLine != pPortion->GetLines().GetObject( pPortion->GetLines().Count()-1) ) ) 1199 { 1200 uno::Reference < i18n::XBreakIterator > xBI = GetBreakIterator(); 1201 sal_Int32 nCount = 1; 1202 nCurIndex = (sal_uInt16)xBI->previousCharacters( pPortion->GetNode()->GetText(), nCurIndex, GetLocale(), i18n::CharacterIteratorMode::SKIPCELL, nCount, nCount ); 1203 } 1204 return nCurIndex; 1205 } 1206 1207 sal_uInt16 TextEngine::GetCharPos( sal_uLong nPortion, sal_uInt16 nLine, long nXPos, sal_Bool ) 1208 { 1209 1210 TEParaPortion* pPortion = mpTEParaPortions->GetObject( nPortion ); 1211 TextLine* pLine = pPortion->GetLines().GetObject( nLine ); 1212 1213 sal_uInt16 nCurIndex = pLine->GetStart(); 1214 1215 long nTmpX = pLine->GetStartX(); 1216 if ( nXPos <= nTmpX ) 1217 return nCurIndex; 1218 1219 for ( sal_uInt16 i = pLine->GetStartPortion(); i <= pLine->GetEndPortion(); i++ ) 1220 { 1221 TETextPortion* pTextPortion = pPortion->GetTextPortions().GetObject( i ); 1222 nTmpX += pTextPortion->GetWidth(); 1223 1224 if ( nTmpX > nXPos ) 1225 { 1226 if( pTextPortion->GetLen() > 1 ) 1227 { 1228 nTmpX -= pTextPortion->GetWidth(); // vor die Portion stellen 1229 // Optimieren: Kein GetTextBreak, wenn feste Fontbreite... 1230 Font aFont; 1231 SeekCursor( nPortion, nCurIndex+1, aFont, NULL ); 1232 mpRefDev->SetFont( aFont); 1233 long nPosInPortion = nXPos-nTmpX; 1234 if ( IsRightToLeft() != pTextPortion->IsRightToLeft() ) 1235 nPosInPortion = pTextPortion->GetWidth() - nPosInPortion; 1236 nCurIndex = mpRefDev->GetTextBreak( pPortion->GetNode()->GetText(), nPosInPortion, nCurIndex ); 1237 // MT: GetTextBreak should assure that we are not withing a CTL cell... 1238 } 1239 return nCurIndex; 1240 } 1241 nCurIndex = nCurIndex + pTextPortion->GetLen(); 1242 } 1243 return nCurIndex; 1244 } 1245 1246 1247 sal_uLong TextEngine::GetTextHeight() const 1248 { 1249 DBG_ASSERT( GetUpdateMode(), "Sollte bei Update=sal_False nicht verwendet werden: GetTextHeight" ); 1250 1251 if ( !IsFormatted() && !IsFormatting() ) 1252 ((TextEngine*)this)->FormatAndUpdate(); 1253 1254 return mnCurTextHeight; 1255 } 1256 1257 sal_uLong TextEngine::GetTextHeight( sal_uLong nParagraph ) const 1258 { 1259 DBG_ASSERT( GetUpdateMode(), "Sollte bei Update=sal_False nicht verwendet werden: GetTextHeight" ); 1260 1261 if ( !IsFormatted() && !IsFormatting() ) 1262 ((TextEngine*)this)->FormatAndUpdate(); 1263 1264 return CalcParaHeight( nParagraph ); 1265 } 1266 1267 sal_uLong TextEngine::CalcTextWidth( sal_uLong nPara ) 1268 { 1269 sal_uLong nParaWidth = 0; 1270 TEParaPortion* pPortion = mpTEParaPortions->GetObject( nPara ); 1271 for ( sal_uInt16 nLine = pPortion->GetLines().Count(); nLine; ) 1272 { 1273 sal_uLong nLineWidth = 0; 1274 TextLine* pLine = pPortion->GetLines().GetObject( --nLine ); 1275 for ( sal_uInt16 nTP = pLine->GetStartPortion(); nTP <= pLine->GetEndPortion(); nTP++ ) 1276 { 1277 TETextPortion* pTextPortion = pPortion->GetTextPortions().GetObject( nTP ); 1278 nLineWidth += pTextPortion->GetWidth(); 1279 } 1280 if ( nLineWidth > nParaWidth ) 1281 nParaWidth = nLineWidth; 1282 } 1283 return nParaWidth; 1284 } 1285 1286 sal_uLong TextEngine::CalcTextWidth() 1287 { 1288 if ( !IsFormatted() && !IsFormatting() ) 1289 FormatAndUpdate(); 1290 1291 if ( mnCurTextWidth == 0xFFFFFFFF ) 1292 { 1293 mnCurTextWidth = 0; 1294 for ( sal_uLong nPara = mpTEParaPortions->Count(); nPara; ) 1295 { 1296 sal_uLong nParaWidth = CalcTextWidth( --nPara ); 1297 if ( nParaWidth > mnCurTextWidth ) 1298 mnCurTextWidth = nParaWidth; 1299 } 1300 } 1301 return mnCurTextWidth+1;// Ein breiter, da in CreateLines bei >= umgebrochen wird. 1302 } 1303 1304 sal_uLong TextEngine::CalcTextHeight() 1305 { 1306 DBG_ASSERT( GetUpdateMode(), "Sollte bei Update=sal_False nicht verwendet werden: CalcTextHeight" ); 1307 1308 sal_uLong nY = 0; 1309 for ( sal_uLong nPortion = mpTEParaPortions->Count(); nPortion; ) 1310 nY += CalcParaHeight( --nPortion ); 1311 return nY; 1312 } 1313 1314 sal_uLong TextEngine::CalcTextWidth( sal_uLong nPara, sal_uInt16 nPortionStart, sal_uInt16 nLen, const Font* pFont ) 1315 { 1316 // Innerhalb des Textes darf es keinen Portionwechsel (Attribut/Tab) geben! 1317 DBG_ASSERT( mpDoc->GetNodes().GetObject( nPara )->GetText().Search( '\t', nPortionStart ) >= (nPortionStart+nLen), "CalcTextWidth: Tab!" ); 1318 1319 sal_uLong nWidth; 1320 if ( mnFixCharWidth100 ) 1321 { 1322 nWidth = (sal_uLong)nLen*mnFixCharWidth100/100; 1323 } 1324 else 1325 { 1326 if ( pFont ) 1327 { 1328 if ( !mpRefDev->GetFont().IsSameInstance( *pFont ) ) 1329 mpRefDev->SetFont( *pFont ); 1330 } 1331 else 1332 { 1333 Font aFont; 1334 SeekCursor( nPara, nPortionStart+1, aFont, NULL ); 1335 mpRefDev->SetFont( aFont ); 1336 } 1337 TextNode* pNode = mpDoc->GetNodes().GetObject( nPara ); 1338 nWidth = (sal_uLong)mpRefDev->GetTextWidth( pNode->GetText(), nPortionStart, nLen ); 1339 1340 } 1341 return nWidth; 1342 } 1343 1344 1345 sal_uInt16 TextEngine::GetLineCount( sal_uLong nParagraph ) const 1346 { 1347 DBG_ASSERT( nParagraph < mpTEParaPortions->Count(), "GetLineCount: Out of range" ); 1348 1349 TEParaPortion* pPPortion = mpTEParaPortions->GetObject( nParagraph ); 1350 if ( pPPortion ) 1351 return pPPortion->GetLines().Count(); 1352 1353 return 0xFFFF; 1354 } 1355 1356 sal_uInt16 TextEngine::GetLineLen( sal_uLong nParagraph, sal_uInt16 nLine ) const 1357 { 1358 DBG_ASSERT( nParagraph < mpTEParaPortions->Count(), "GetLineCount: Out of range" ); 1359 1360 TEParaPortion* pPPortion = mpTEParaPortions->GetObject( nParagraph ); 1361 if ( pPPortion && ( nLine < pPPortion->GetLines().Count() ) ) 1362 { 1363 TextLine* pLine = pPPortion->GetLines().GetObject( nLine ); 1364 return pLine->GetLen(); 1365 } 1366 1367 return 0xFFFF; 1368 } 1369 1370 sal_uLong TextEngine::CalcParaHeight( sal_uLong nParagraph ) const 1371 { 1372 sal_uLong nHeight = 0; 1373 1374 TEParaPortion* pPPortion = mpTEParaPortions->GetObject( nParagraph ); 1375 DBG_ASSERT( pPPortion, "Absatz nicht gefunden: GetParaHeight" ); 1376 if ( pPPortion ) 1377 nHeight = pPPortion->GetLines().Count() * mnCharHeight; 1378 1379 return nHeight; 1380 } 1381 1382 void TextEngine::UpdateSelections() 1383 { 1384 } 1385 1386 Range TextEngine::GetInvalidYOffsets( sal_uLong nPortion ) 1387 { 1388 TEParaPortion* pTEParaPortion = mpTEParaPortions->GetObject( nPortion ); 1389 sal_uInt16 nLines = pTEParaPortion->GetLines().Count(); 1390 sal_uInt16 nLastInvalid, nFirstInvalid = 0; 1391 sal_uInt16 nLine; 1392 for ( nLine = 0; nLine < nLines; nLine++ ) 1393 { 1394 TextLine* pL = pTEParaPortion->GetLines().GetObject( nLine ); 1395 if ( pL->IsInvalid() ) 1396 { 1397 nFirstInvalid = nLine; 1398 break; 1399 } 1400 } 1401 1402 for ( nLastInvalid = nFirstInvalid; nLastInvalid < nLines; nLastInvalid++ ) 1403 { 1404 TextLine* pL = pTEParaPortion->GetLines().GetObject( nLine ); 1405 if ( pL->IsValid() ) 1406 break; 1407 } 1408 1409 if ( nLastInvalid >= nLines ) 1410 nLastInvalid = nLines-1; 1411 1412 return Range( nFirstInvalid*mnCharHeight, ((nLastInvalid+1)*mnCharHeight)-1 ); 1413 } 1414 1415 sal_uLong TextEngine::GetParagraphCount() const 1416 { 1417 return mpDoc->GetNodes().Count(); 1418 } 1419 1420 void TextEngine::EnableUndo( sal_Bool bEnable ) 1421 { 1422 // Beim Umschalten des Modus Liste loeschen: 1423 if ( bEnable != IsUndoEnabled() ) 1424 ResetUndo(); 1425 1426 mbUndoEnabled = bEnable; 1427 } 1428 1429 ::svl::IUndoManager& TextEngine::GetUndoManager() 1430 { 1431 if ( !mpUndoManager ) 1432 mpUndoManager = new TextUndoManager( this ); 1433 return *mpUndoManager; 1434 } 1435 1436 void TextEngine::UndoActionStart( sal_uInt16 nId ) 1437 { 1438 if ( IsUndoEnabled() && !IsInUndo() ) 1439 { 1440 String aComment; 1441 // ... 1442 GetUndoManager().EnterListAction( aComment, XubString(), nId ); 1443 } 1444 } 1445 1446 void TextEngine::UndoActionEnd() 1447 { 1448 if ( IsUndoEnabled() && !IsInUndo() ) 1449 GetUndoManager().LeaveListAction(); 1450 } 1451 1452 void TextEngine::InsertUndo( TextUndo* pUndo, sal_Bool bTryMerge ) 1453 { 1454 DBG_ASSERT( !IsInUndo(), "InsertUndo im Undomodus!" ); 1455 GetUndoManager().AddUndoAction( pUndo, bTryMerge ); 1456 } 1457 1458 void TextEngine::ResetUndo() 1459 { 1460 if ( mpUndoManager ) 1461 mpUndoManager->Clear(); 1462 } 1463 1464 void TextEngine::InsertContent( TextNode* pNode, sal_uLong nPara ) 1465 { 1466 DBG_ASSERT( pNode, "NULL-Pointer in InsertContent! " ); 1467 DBG_ASSERT( IsInUndo(), "InsertContent nur fuer Undo()!" ); 1468 TEParaPortion* pNew = new TEParaPortion( pNode ); 1469 mpTEParaPortions->Insert( pNew, nPara ); 1470 mpDoc->GetNodes().Insert( pNode, nPara ); 1471 ImpParagraphInserted( nPara ); 1472 } 1473 1474 TextPaM TextEngine::SplitContent( sal_uLong nNode, sal_uInt16 nSepPos ) 1475 { 1476 #ifdef DBG_UTIL 1477 TextNode* pNode = mpDoc->GetNodes().GetObject( nNode ); 1478 DBG_ASSERT( pNode, "Ungueltiger Node in SplitContent" ); 1479 DBG_ASSERT( IsInUndo(), "SplitContent nur fuer Undo()!" ); 1480 DBG_ASSERT( nSepPos <= pNode->GetText().Len(), "Index im Wald: SplitContent" ); 1481 #endif 1482 TextPaM aPaM( nNode, nSepPos ); 1483 return ImpInsertParaBreak( aPaM ); 1484 } 1485 1486 TextPaM TextEngine::ConnectContents( sal_uLong nLeftNode ) 1487 { 1488 DBG_ASSERT( IsInUndo(), "ConnectContent nur fuer Undo()!" ); 1489 return ImpConnectParagraphs( nLeftNode, nLeftNode+1 ); 1490 } 1491 1492 void TextEngine::SeekCursor( sal_uLong nPara, sal_uInt16 nPos, Font& rFont, OutputDevice* pOutDev ) 1493 { 1494 rFont = maFont; 1495 if ( pOutDev ) 1496 pOutDev->SetTextColor( maTextColor ); 1497 1498 TextNode* pNode = mpDoc->GetNodes().GetObject( nPara ); 1499 sal_uInt16 nAttribs = pNode->GetCharAttribs().Count(); 1500 for ( sal_uInt16 nAttr = 0; nAttr < nAttribs; nAttr++ ) 1501 { 1502 TextCharAttrib* pAttrib = pNode->GetCharAttribs().GetAttrib( nAttr ); 1503 if ( pAttrib->GetStart() > nPos ) 1504 break; 1505 1506 // Beim Seeken nicht die Attr beruecksichtigen, die dort beginnen! 1507 // Leere Attribute werden beruecksichtigt( verwendet), da diese 1508 // gerade eingestellt wurden. 1509 // 12.4.95: Doch keine Leeren Attribute verwenden: 1510 // - Wenn gerade eingestellt und leer => keine Auswirkung auf Font 1511 // In einem leeren Absatz eingestellte Zeichen werden sofort wirksam. 1512 if ( ( ( pAttrib->GetStart() < nPos ) && ( pAttrib->GetEnd() >= nPos ) ) 1513 || !pNode->GetText().Len() ) 1514 { 1515 if ( pAttrib->Which() != TEXTATTR_FONTCOLOR ) 1516 { 1517 pAttrib->GetAttr().SetFont( rFont ); 1518 } 1519 else 1520 { 1521 if ( pOutDev ) 1522 pOutDev->SetTextColor( ((TextAttribFontColor&)pAttrib->GetAttr()).GetColor() ); 1523 } 1524 } 1525 } 1526 1527 if ( mpIMEInfos && mpIMEInfos->pAttribs && ( mpIMEInfos->aPos.GetPara() == nPara ) && 1528 ( nPos > mpIMEInfos->aPos.GetIndex() ) && ( nPos <= ( mpIMEInfos->aPos.GetIndex() + mpIMEInfos->nLen ) ) ) 1529 { 1530 sal_uInt16 nAttr = mpIMEInfos->pAttribs[ nPos - mpIMEInfos->aPos.GetIndex() - 1 ]; 1531 if ( nAttr & EXTTEXTINPUT_ATTR_UNDERLINE ) 1532 rFont.SetUnderline( UNDERLINE_SINGLE ); 1533 else if ( nAttr & EXTTEXTINPUT_ATTR_BOLDUNDERLINE ) 1534 rFont.SetUnderline( UNDERLINE_BOLD ); 1535 else if ( nAttr & EXTTEXTINPUT_ATTR_DOTTEDUNDERLINE ) 1536 rFont.SetUnderline( UNDERLINE_DOTTED ); 1537 else if ( nAttr & EXTTEXTINPUT_ATTR_DASHDOTUNDERLINE ) 1538 rFont.SetUnderline( UNDERLINE_DOTTED ); 1539 if ( nAttr & EXTTEXTINPUT_ATTR_REDTEXT ) 1540 rFont.SetColor( Color( COL_RED ) ); 1541 else if ( nAttr & EXTTEXTINPUT_ATTR_HALFTONETEXT ) 1542 rFont.SetColor( Color( COL_LIGHTGRAY ) ); 1543 if ( nAttr & EXTTEXTINPUT_ATTR_HIGHLIGHT ) 1544 { 1545 const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings(); 1546 rFont.SetColor( rStyleSettings.GetHighlightTextColor() ); 1547 rFont.SetFillColor( rStyleSettings.GetHighlightColor() ); 1548 rFont.SetTransparent( sal_False ); 1549 } 1550 else if ( nAttr & EXTTEXTINPUT_ATTR_GRAYWAVELINE ) 1551 { 1552 rFont.SetUnderline( UNDERLINE_WAVE ); 1553 // if( pOut ) 1554 // pOut->SetTextLineColor( Color( COL_LIGHTGRAY ) ); 1555 } 1556 } 1557 } 1558 1559 void TextEngine::SetUpdateMode( sal_Bool bUp, TextView* pCurView, sal_Bool bForceUpdate ) 1560 { 1561 sal_Bool bChanged = ( GetUpdateMode() != bUp ); 1562 1563 mbUpdate = bUp; 1564 if ( mbUpdate && ( bChanged || bForceUpdate ) ) 1565 FormatAndUpdate( pCurView ); 1566 } 1567 1568 void TextEngine::FormatAndUpdate( TextView* pCurView ) 1569 { 1570 if ( mbDowning ) 1571 return ; 1572 1573 if ( IsInUndo() ) 1574 IdleFormatAndUpdate( pCurView ); 1575 else 1576 { 1577 FormatDoc(); 1578 UpdateViews( pCurView ); 1579 } 1580 } 1581 1582 1583 void TextEngine::IdleFormatAndUpdate( TextView* pCurView, sal_uInt16 nMaxTimerRestarts ) 1584 { 1585 mpIdleFormatter->DoIdleFormat( pCurView, nMaxTimerRestarts ); 1586 } 1587 1588 void TextEngine::TextModified() 1589 { 1590 mbFormatted = sal_False; 1591 mbModified = sal_True; 1592 } 1593 1594 void TextEngine::UpdateViews( TextView* pCurView ) 1595 { 1596 if ( !GetUpdateMode() || IsFormatting() || maInvalidRec.IsEmpty() ) 1597 return; 1598 1599 DBG_ASSERT( IsFormatted(), "UpdateViews: Doc nicht formatiert!" ); 1600 1601 for ( sal_uInt16 nView = 0; nView < mpViews->Count(); nView++ ) 1602 { 1603 TextView* pView = mpViews->GetObject( nView ); 1604 pView->HideCursor(); 1605 1606 Rectangle aClipRec( maInvalidRec ); 1607 Size aOutSz = pView->GetWindow()->GetOutputSizePixel(); 1608 Rectangle aVisArea( pView->GetStartDocPos(), aOutSz ); 1609 aClipRec.Intersection( aVisArea ); 1610 if ( !aClipRec.IsEmpty() ) 1611 { 1612 // in Fensterkoordinaten umwandeln.... 1613 Point aNewPos = pView->GetWindowPos( aClipRec.TopLeft() ); 1614 if ( IsRightToLeft() ) 1615 aNewPos.X() -= aOutSz.Width() - 1; 1616 aClipRec.SetPos( aNewPos ); 1617 1618 if ( pView == pCurView ) 1619 pView->ImpPaint( aClipRec, !pView->GetWindow()->IsPaintTransparent() ); 1620 else 1621 pView->GetWindow()->Invalidate( aClipRec ); 1622 } 1623 } 1624 1625 if ( pCurView ) 1626 { 1627 pCurView->ShowCursor( pCurView->IsAutoScroll() ); 1628 } 1629 1630 maInvalidRec = Rectangle(); 1631 } 1632 1633 IMPL_LINK( TextEngine, IdleFormatHdl, Timer *, EMPTYARG ) 1634 { 1635 FormatAndUpdate( mpIdleFormatter->GetView() ); 1636 return 0; 1637 } 1638 1639 void TextEngine::CheckIdleFormatter() 1640 { 1641 mpIdleFormatter->ForceTimeout(); 1642 } 1643 1644 void TextEngine::FormatFullDoc() 1645 { 1646 for ( sal_uLong nPortion = 0; nPortion < mpTEParaPortions->Count(); nPortion++ ) 1647 { 1648 TEParaPortion* pTEParaPortion = mpTEParaPortions->GetObject( nPortion ); sal_uInt16 nLen = pTEParaPortion->GetNode()->GetText().Len(); 1649 pTEParaPortion->MarkSelectionInvalid( 0, nLen ); 1650 } 1651 mbFormatted = sal_False; 1652 FormatDoc(); 1653 } 1654 1655 void TextEngine::FormatDoc() 1656 { 1657 if ( IsFormatted() || !GetUpdateMode() || IsFormatting() ) 1658 return; 1659 1660 mbIsFormatting = sal_True; 1661 mbHasMultiLineParas = sal_False; 1662 1663 long nY = 0; 1664 sal_Bool bGrow = sal_False; 1665 1666 maInvalidRec = Rectangle(); // leermachen 1667 for ( sal_uLong nPara = 0; nPara < mpTEParaPortions->Count(); nPara++ ) 1668 { 1669 TEParaPortion* pTEParaPortion = mpTEParaPortions->GetObject( nPara ); 1670 if ( pTEParaPortion->IsInvalid() ) 1671 { 1672 sal_uLong nOldParaWidth = 0xFFFFFFFF; 1673 if ( mnCurTextWidth != 0xFFFFFFFF ) 1674 nOldParaWidth = CalcTextWidth( nPara ); 1675 1676 ImpFormattingParagraph( nPara ); 1677 1678 if ( CreateLines( nPara ) ) 1679 bGrow = sal_True; 1680 1681 // InvalidRec nur einmal setzen... 1682 if ( maInvalidRec.IsEmpty() ) 1683 { 1684 // Bei Paperwidth 0 (AutoPageSize) bleibt es sonst Empty()... 1685 long nWidth = (long)mnMaxTextWidth; 1686 if ( !nWidth ) 1687 nWidth = 0x7FFFFFFF; 1688 Range aInvRange( GetInvalidYOffsets( nPara ) ); 1689 maInvalidRec = Rectangle( Point( 0, nY+aInvRange.Min() ), 1690 Size( nWidth, aInvRange.Len() ) ); 1691 } 1692 else 1693 { 1694 maInvalidRec.Bottom() = nY + CalcParaHeight( nPara ); 1695 } 1696 1697 if ( mnCurTextWidth != 0xFFFFFFFF ) 1698 { 1699 sal_uLong nNewParaWidth = CalcTextWidth( nPara ); 1700 if ( nNewParaWidth >= mnCurTextWidth ) 1701 mnCurTextWidth = nNewParaWidth; 1702 else if ( ( nOldParaWidth != 0xFFFFFFFF ) && ( nOldParaWidth >= mnCurTextWidth ) ) 1703 mnCurTextWidth = 0xFFFFFFFF; 1704 } 1705 } 1706 else if ( bGrow ) 1707 { 1708 maInvalidRec.Bottom() = nY + CalcParaHeight( nPara ); 1709 } 1710 nY += CalcParaHeight( nPara ); 1711 if ( !mbHasMultiLineParas && pTEParaPortion->GetLines().Count() > 1 ) 1712 mbHasMultiLineParas = sal_True; 1713 } 1714 1715 if ( !maInvalidRec.IsEmpty() ) 1716 { 1717 sal_uLong nNewHeight = CalcTextHeight(); 1718 long nDiff = nNewHeight - mnCurTextHeight; 1719 if ( nNewHeight < mnCurTextHeight ) 1720 { 1721 maInvalidRec.Bottom() = (long)Max( nNewHeight, mnCurTextHeight ); 1722 if ( maInvalidRec.IsEmpty() ) 1723 { 1724 maInvalidRec.Top() = 0; 1725 // Left und Right werden nicht ausgewertet, aber wegen IsEmpty gesetzt. 1726 maInvalidRec.Left() = 0; 1727 maInvalidRec.Right() = mnMaxTextWidth; 1728 } 1729 } 1730 1731 mnCurTextHeight = nNewHeight; 1732 if ( nDiff ) 1733 { 1734 mbFormatted = sal_True; 1735 ImpTextHeightChanged(); 1736 } 1737 } 1738 1739 mbIsFormatting = sal_False; 1740 mbFormatted = sal_True; 1741 1742 ImpTextFormatted(); 1743 } 1744 1745 void TextEngine::CreateAndInsertEmptyLine( sal_uLong nPara ) 1746 { 1747 TextNode* pNode = mpDoc->GetNodes().GetObject( nPara ); 1748 TEParaPortion* pTEParaPortion = mpTEParaPortions->GetObject( nPara ); 1749 1750 TextLine* pTmpLine = new TextLine; 1751 pTmpLine->SetStart( pNode->GetText().Len() ); 1752 pTmpLine->SetEnd( pTmpLine->GetStart() ); 1753 pTEParaPortion->GetLines().Insert( pTmpLine, pTEParaPortion->GetLines().Count() ); 1754 1755 if ( ImpGetAlign() == TXTALIGN_CENTER ) 1756 pTmpLine->SetStartX( (short)(mnMaxTextWidth / 2) ); 1757 else if ( ImpGetAlign() == TXTALIGN_RIGHT ) 1758 pTmpLine->SetStartX( (short)mnMaxTextWidth ); 1759 else 1760 pTmpLine->SetStartX( mpDoc->GetLeftMargin() ); 1761 1762 sal_Bool bLineBreak = pNode->GetText().Len() ? sal_True : sal_False; 1763 1764 TETextPortion* pDummyPortion = new TETextPortion( 0 ); 1765 pDummyPortion->GetWidth() = 0; 1766 pTEParaPortion->GetTextPortions().Insert( pDummyPortion, pTEParaPortion->GetTextPortions().Count() ); 1767 1768 if ( bLineBreak == sal_True ) 1769 { 1770 // -2: Die neue ist bereits eingefuegt. 1771 #ifdef DBG_UTIL 1772 TextLine* pLastLine = pTEParaPortion->GetLines().GetObject( pTEParaPortion->GetLines().Count()-2 ); 1773 DBG_ASSERT( pLastLine, "Weicher Umbruch, keine Zeile ?!" ); 1774 #endif 1775 sal_uInt16 nPos = (sal_uInt16) pTEParaPortion->GetTextPortions().Count() - 1 ; 1776 pTmpLine->SetStartPortion( nPos ); 1777 pTmpLine->SetEndPortion( nPos ); 1778 } 1779 } 1780 1781 void TextEngine::ImpBreakLine( sal_uLong nPara, TextLine* pLine, TETextPortion*, sal_uInt16 nPortionStart, long nRemainingWidth ) 1782 { 1783 TextNode* pNode = mpDoc->GetNodes().GetObject( nPara ); 1784 1785 // Font sollte noch eingestellt sein. 1786 sal_uInt16 nMaxBreakPos = mpRefDev->GetTextBreak( pNode->GetText(), nRemainingWidth, nPortionStart ); 1787 1788 DBG_ASSERT( nMaxBreakPos < pNode->GetText().Len(), "Break?!" ); 1789 1790 if ( nMaxBreakPos == STRING_LEN ) // GetTextBreak() ist anderer Auffassung als GetTextSize() 1791 nMaxBreakPos = pNode->GetText().Len() - 1; 1792 1793 uno::Reference < i18n::XBreakIterator > xBI = GetBreakIterator(); 1794 i18n::LineBreakHyphenationOptions aHyphOptions( NULL, uno::Sequence< beans::PropertyValue >(), 1 ); 1795 1796 i18n::LineBreakUserOptions aUserOptions; 1797 aUserOptions.forbiddenBeginCharacters = ImpGetLocaleDataWrapper()->getForbiddenCharacters().beginLine; 1798 aUserOptions.forbiddenEndCharacters = ImpGetLocaleDataWrapper()->getForbiddenCharacters().endLine; 1799 aUserOptions.applyForbiddenRules = sal_True; 1800 aUserOptions.allowPunctuationOutsideMargin = sal_False; 1801 aUserOptions.allowHyphenateEnglish = sal_False; 1802 1803 static const com::sun::star::lang::Locale aDefLocale; 1804 i18n::LineBreakResults aLBR = xBI->getLineBreak( pNode->GetText(), nMaxBreakPos, aDefLocale, pLine->GetStart(), aHyphOptions, aUserOptions ); 1805 sal_uInt16 nBreakPos = (sal_uInt16)aLBR.breakIndex; 1806 if ( nBreakPos <= pLine->GetStart() ) 1807 { 1808 nBreakPos = nMaxBreakPos; 1809 if ( nBreakPos <= pLine->GetStart() ) 1810 nBreakPos = pLine->GetStart() + 1; // Sonst Endlosschleife! 1811 } 1812 1813 1814 // die angeknackste Portion ist die End-Portion 1815 pLine->SetEnd( nBreakPos ); 1816 sal_uInt16 nEndPortion = SplitTextPortion( nPara, nBreakPos ); 1817 1818 sal_Bool bBlankSeparator = ( ( nBreakPos >= pLine->GetStart() ) && 1819 ( pNode->GetText().GetChar( nBreakPos ) == ' ' ) ) ? sal_True : sal_False; 1820 if ( bBlankSeparator ) 1821 { 1822 // Blanks am Zeilenende generell unterdruecken... 1823 TEParaPortion* pTEParaPortion = mpTEParaPortions->GetObject( nPara ); 1824 TETextPortion* pTP = pTEParaPortion->GetTextPortions().GetObject( nEndPortion ); 1825 DBG_ASSERT( nBreakPos > pLine->GetStart(), "SplitTextPortion am Anfang der Zeile?" ); 1826 pTP->GetWidth() = (long)CalcTextWidth( nPara, nBreakPos-pTP->GetLen(), pTP->GetLen()-1 ); 1827 } 1828 pLine->SetEndPortion( nEndPortion ); 1829 } 1830 1831 sal_uInt16 TextEngine::SplitTextPortion( sal_uLong nPara, sal_uInt16 nPos ) 1832 { 1833 1834 // Die Portion bei nPos wird geplittet, wenn bei nPos nicht 1835 // sowieso ein Wechsel ist 1836 if ( nPos == 0 ) 1837 return 0; 1838 1839 sal_uInt16 nSplitPortion; 1840 sal_uInt16 nTmpPos = 0; 1841 TETextPortion* pTextPortion = 0; 1842 TEParaPortion* pTEParaPortion = mpTEParaPortions->GetObject( nPara ); 1843 sal_uInt16 nPortions = pTEParaPortion->GetTextPortions().Count(); 1844 for ( nSplitPortion = 0; nSplitPortion < nPortions; nSplitPortion++ ) 1845 { 1846 TETextPortion* pTP = pTEParaPortion->GetTextPortions().GetObject(nSplitPortion); 1847 nTmpPos = nTmpPos + pTP->GetLen(); 1848 if ( nTmpPos >= nPos ) 1849 { 1850 if ( nTmpPos == nPos ) // dann braucht nichts geteilt werden 1851 return nSplitPortion; 1852 pTextPortion = pTP; 1853 break; 1854 } 1855 } 1856 1857 DBG_ASSERT( pTextPortion, "Position ausserhalb des Bereichs!" ); 1858 1859 sal_uInt16 nOverlapp = nTmpPos - nPos; 1860 pTextPortion->GetLen() = pTextPortion->GetLen() - nOverlapp; 1861 TETextPortion* pNewPortion = new TETextPortion( nOverlapp ); 1862 pTEParaPortion->GetTextPortions().Insert( pNewPortion, nSplitPortion+1 ); 1863 pTextPortion->GetWidth() = (long)CalcTextWidth( nPara, nPos-pTextPortion->GetLen(), pTextPortion->GetLen() ); 1864 1865 return nSplitPortion; 1866 } 1867 1868 void TextEngine::CreateTextPortions( sal_uLong nPara, sal_uInt16 nStartPos ) 1869 { 1870 TEParaPortion* pTEParaPortion = mpTEParaPortions->GetObject( nPara ); 1871 TextNode* pNode = pTEParaPortion->GetNode(); 1872 DBG_ASSERT( pNode->GetText().Len(), "CreateTextPortions sollte nicht fuer leere Absaetze verwendet werden!" ); 1873 1874 TESortedPositions aPositions; 1875 sal_uLong nZero = 0; 1876 aPositions.Insert( nZero ); 1877 1878 sal_uInt16 nAttribs = pNode->GetCharAttribs().Count(); 1879 for ( sal_uInt16 nAttr = 0; nAttr < nAttribs; nAttr++ ) 1880 { 1881 TextCharAttrib* pAttrib = pNode->GetCharAttribs().GetAttrib( nAttr ); 1882 1883 // Start und Ende in das Array eintragen... 1884 // Die InsertMethode laesst keine doppelten Werte zu.... 1885 aPositions.Insert( pAttrib->GetStart() ); 1886 aPositions.Insert( pAttrib->GetEnd() ); 1887 } 1888 aPositions.Insert( pNode->GetText().Len() ); 1889 1890 const TEWritingDirectionInfos& rWritingDirections = pTEParaPortion->GetWritingDirectionInfos(); 1891 for ( sal_uInt16 nD = 0; nD < rWritingDirections.Count(); nD++ ) 1892 aPositions.Insert( rWritingDirections[nD].nStartPos ); 1893 1894 if ( mpIMEInfos && mpIMEInfos->pAttribs && ( mpIMEInfos->aPos.GetPara() == nPara ) ) 1895 { 1896 sal_uInt16 nLastAttr = 0xFFFF; 1897 for( sal_uInt16 n = 0; n < mpIMEInfos->nLen; n++ ) 1898 { 1899 if ( mpIMEInfos->pAttribs[n] != nLastAttr ) 1900 { 1901 aPositions.Insert( mpIMEInfos->aPos.GetIndex() + n ); 1902 nLastAttr = mpIMEInfos->pAttribs[n]; 1903 } 1904 } 1905 } 1906 1907 sal_uInt16 nTabPos = pNode->GetText().Search( '\t', 0 ); 1908 while ( nTabPos != STRING_NOTFOUND ) 1909 { 1910 aPositions.Insert( nTabPos ); 1911 aPositions.Insert( nTabPos + 1 ); 1912 nTabPos = pNode->GetText().Search( '\t', nTabPos+1 ); 1913 } 1914 1915 // Ab ... loeschen: 1916 // Leider muss die Anzahl der TextPortions mit aPositions.Count() 1917 // nicht uebereinstimmen, da evtl. Zeilenumbrueche... 1918 sal_uInt16 nPortionStart = 0; 1919 sal_uInt16 nInvPortion = 0; 1920 sal_uInt16 nP; 1921 for ( nP = 0; nP < pTEParaPortion->GetTextPortions().Count(); nP++ ) 1922 { 1923 TETextPortion* pTmpPortion = pTEParaPortion->GetTextPortions().GetObject(nP); 1924 nPortionStart = nPortionStart + pTmpPortion->GetLen(); 1925 if ( nPortionStart >= nStartPos ) 1926 { 1927 nPortionStart = nPortionStart - pTmpPortion->GetLen(); 1928 nInvPortion = nP; 1929 break; 1930 } 1931 } 1932 DBG_ASSERT( nP < pTEParaPortion->GetTextPortions().Count() || !pTEParaPortion->GetTextPortions().Count(), "Nichts zum loeschen: CreateTextPortions" ); 1933 if ( nInvPortion && ( nPortionStart+pTEParaPortion->GetTextPortions().GetObject(nInvPortion)->GetLen() > nStartPos ) ) 1934 { 1935 // lieber eine davor... 1936 // Aber nur wenn es mitten in der Portion war, sonst ist es evtl. 1937 // die einzige in der Zeile davor ! 1938 nInvPortion--; 1939 nPortionStart = nPortionStart - pTEParaPortion->GetTextPortions().GetObject(nInvPortion)->GetLen(); 1940 } 1941 pTEParaPortion->GetTextPortions().DeleteFromPortion( nInvPortion ); 1942 1943 // Eine Portion kann auch durch einen Zeilenumbruch entstanden sein: 1944 aPositions.Insert( nPortionStart ); 1945 1946 sal_uInt16 nInvPos; 1947 #ifdef DBG_UTIL 1948 sal_Bool bFound = 1949 #endif 1950 aPositions.Seek_Entry( nPortionStart, &nInvPos ); 1951 DBG_ASSERT( bFound && ( nInvPos < (aPositions.Count()-1) ), "InvPos ?!" ); 1952 for ( sal_uInt16 i = nInvPos+1; i < aPositions.Count(); i++ ) 1953 { 1954 TETextPortion* pNew = new TETextPortion( (sal_uInt16)aPositions[i] - (sal_uInt16)aPositions[i-1] ); 1955 pTEParaPortion->GetTextPortions().Insert( pNew, pTEParaPortion->GetTextPortions().Count()); 1956 } 1957 1958 DBG_ASSERT( pTEParaPortion->GetTextPortions().Count(), "Keine Portions?!" ); 1959 #ifdef EDITDEBUG 1960 DBG_ASSERT( pTEParaPortion->DbgCheckTextPortions(), "Portions kaputt?" ); 1961 #endif 1962 } 1963 1964 void TextEngine::RecalcTextPortion( sal_uLong nPara, sal_uInt16 nStartPos, short nNewChars ) 1965 { 1966 TEParaPortion* pTEParaPortion = mpTEParaPortions->GetObject( nPara ); 1967 DBG_ASSERT( pTEParaPortion->GetTextPortions().Count(), "Keine Portions!" ); 1968 DBG_ASSERT( nNewChars, "RecalcTextPortion mit Diff == 0" ); 1969 1970 TextNode* const pNode = pTEParaPortion->GetNode(); 1971 if ( nNewChars > 0 ) 1972 { 1973 // Wenn an nStartPos ein Attribut beginnt/endet, oder vor nStartPos 1974 // ein Tab steht, faengt eine neue Portion an, 1975 // ansonsten wird die Portion an nStartPos erweitert. 1976 // Oder wenn ganz vorne ( StartPos 0 ) und dann ein Tab 1977 1978 if ( ( pNode->GetCharAttribs().HasBoundingAttrib( nStartPos ) ) || 1979 ( nStartPos && ( pNode->GetText().GetChar( nStartPos - 1 ) == '\t' ) ) || 1980 ( ( !nStartPos && ( nNewChars < pNode->GetText().Len() ) && pNode->GetText().GetChar( nNewChars ) == '\t' ) ) ) 1981 { 1982 sal_uInt16 nNewPortionPos = 0; 1983 if ( nStartPos ) 1984 nNewPortionPos = SplitTextPortion( nPara, nStartPos ) + 1; 1985 // else if ( ( pTEParaPortion->GetTextPortions().Count() == 1 ) && 1986 // !pTEParaPortion->GetTextPortions()[0]->GetLen() ) 1987 // pTEParaPortion->GetTextPortions().Reset(); // DummyPortion 1988 1989 // Eine leere Portion kann hier stehen, wenn der Absatz leer war, 1990 // oder eine Zeile durch einen harten Zeilenumbruch entstanden ist. 1991 if ( ( nNewPortionPos < pTEParaPortion->GetTextPortions().Count() ) && 1992 !pTEParaPortion->GetTextPortions()[nNewPortionPos]->GetLen() ) 1993 { 1994 // Dann die leere Portion verwenden. 1995 sal_uInt16 & r = 1996 pTEParaPortion->GetTextPortions()[nNewPortionPos]->GetLen(); 1997 r = r + nNewChars; 1998 } 1999 else 2000 { 2001 TETextPortion* pNewPortion = new TETextPortion( nNewChars ); 2002 pTEParaPortion->GetTextPortions().Insert( pNewPortion, nNewPortionPos ); 2003 } 2004 } 2005 else 2006 { 2007 sal_uInt16 nPortionStart; 2008 const sal_uInt16 nTP = pTEParaPortion->GetTextPortions(). 2009 FindPortion( nStartPos, nPortionStart ); 2010 TETextPortion* const pTP = pTEParaPortion->GetTextPortions()[ nTP ]; 2011 DBG_ASSERT( pTP, "RecalcTextPortion: Portion nicht gefunden" ); 2012 pTP->GetLen() = pTP->GetLen() + nNewChars; 2013 pTP->GetWidth() = (-1); 2014 } 2015 } 2016 else 2017 { 2018 // Portion schrumpfen oder ggf. entfernen. 2019 // Vor Aufruf dieser Methode muss sichergestellt sein, dass 2020 // keine Portions in dem geloeschten Bereich lagen! 2021 2022 // Es darf keine reinragende oder im Bereich startende Portion geben, 2023 // also muss nStartPos <= nPos <= nStartPos - nNewChars(neg.) sein 2024 sal_uInt16 nPortion = 0; 2025 sal_uInt16 nPos = 0; 2026 sal_uInt16 nEnd = nStartPos-nNewChars; 2027 sal_uInt16 nPortions = pTEParaPortion->GetTextPortions().Count(); 2028 TETextPortion* pTP = 0; 2029 for ( nPortion = 0; nPortion < nPortions; nPortion++ ) 2030 { 2031 pTP = pTEParaPortion->GetTextPortions()[ nPortion ]; 2032 if ( ( nPos+pTP->GetLen() ) > nStartPos ) 2033 { 2034 DBG_ASSERT( nPos <= nStartPos, "Start falsch!" ); 2035 DBG_ASSERT( nPos+pTP->GetLen() >= nEnd, "End falsch!" ); 2036 break; 2037 } 2038 nPos = nPos + pTP->GetLen(); 2039 } 2040 DBG_ASSERT( pTP, "RecalcTextPortion: Portion nicht gefunden" ); 2041 if ( ( nPos == nStartPos ) && ( (nPos+pTP->GetLen()) == nEnd ) ) 2042 { 2043 // Portion entfernen; 2044 pTEParaPortion->GetTextPortions().Remove( nPortion ); 2045 delete pTP; 2046 } 2047 else 2048 { 2049 DBG_ASSERT( pTP->GetLen() > (-nNewChars), "Portion zu klein zum schrumpfen!" ); 2050 pTP->GetLen() = pTP->GetLen() + nNewChars; 2051 } 2052 DBG_ASSERT( pTEParaPortion->GetTextPortions().Count(), "RecalcTextPortions: Keine mehr da!" ); 2053 } 2054 2055 #ifdef EDITDEBUG 2056 DBG_ASSERT( pTEParaPortion->DbgCheckTextPortions(), "Portions kaputt?" ); 2057 #endif 2058 } 2059 2060 void TextEngine::ImpPaint( OutputDevice* pOutDev, const Point& rStartPos, Rectangle const* pPaintArea, TextSelection const* pPaintRange, TextSelection const* pSelection ) 2061 { 2062 if ( !GetUpdateMode() ) 2063 return; 2064 2065 if ( !IsFormatted() ) 2066 FormatDoc(); 2067 2068 bool bTransparent = false; 2069 Window* pOutWin = dynamic_cast<Window*>(pOutDev); 2070 bTransparent = (pOutWin && pOutWin->IsPaintTransparent()); 2071 2072 long nY = rStartPos.Y(); 2073 2074 TextPaM const* pSelStart = 0; 2075 TextPaM const* pSelEnd = 0; 2076 if ( pSelection && pSelection->HasRange() ) 2077 { 2078 sal_Bool bInvers = pSelection->GetEnd() < pSelection->GetStart(); 2079 pSelStart = !bInvers ? &pSelection->GetStart() : &pSelection->GetEnd(); 2080 pSelEnd = bInvers ? &pSelection->GetStart() : &pSelection->GetEnd(); 2081 } 2082 DBG_ASSERT( !pPaintRange || ( pPaintRange->GetStart() < pPaintRange->GetEnd() ), "ImpPaint: Paint-Range?!" ); 2083 2084 const StyleSettings& rStyleSettings = pOutDev->GetSettings().GetStyleSettings(); 2085 2086 // -------------------------------------------------- 2087 // Ueber alle Absaetze... 2088 // -------------------------------------------------- 2089 for ( sal_uLong nPara = 0; nPara < mpTEParaPortions->Count(); nPara++ ) 2090 { 2091 TEParaPortion* pPortion = mpTEParaPortions->GetObject( nPara ); 2092 // falls beim Tippen Idle-Formatierung, asynchrones Paint. 2093 if ( pPortion->IsInvalid() ) 2094 return; 2095 2096 sal_uLong nParaHeight = CalcParaHeight( nPara ); 2097 sal_uInt16 nIndex = 0; 2098 if ( ( !pPaintArea || ( ( nY + (long)nParaHeight ) > pPaintArea->Top() ) ) 2099 && ( !pPaintRange || ( ( nPara >= pPaintRange->GetStart().GetPara() ) && ( nPara <= pPaintRange->GetEnd().GetPara() ) ) ) ) 2100 { 2101 // -------------------------------------------------- 2102 // Ueber die Zeilen des Absatzes... 2103 // -------------------------------------------------- 2104 sal_uInt16 nLines = pPortion->GetLines().Count(); 2105 for ( sal_uInt16 nLine = 0; nLine < nLines; nLine++ ) 2106 { 2107 TextLine* pLine = pPortion->GetLines().GetObject(nLine); 2108 Point aTmpPos( rStartPos.X() + pLine->GetStartX(), nY ); 2109 2110 if ( ( !pPaintArea || ( ( nY + mnCharHeight ) > pPaintArea->Top() ) ) 2111 && ( !pPaintRange || ( 2112 ( TextPaM( nPara, pLine->GetStart() ) < pPaintRange->GetEnd() ) && 2113 ( TextPaM( nPara, pLine->GetEnd() ) > pPaintRange->GetStart() ) ) ) ) 2114 { 2115 // -------------------------------------------------- 2116 // Ueber die Portions der Zeile... 2117 // -------------------------------------------------- 2118 nIndex = pLine->GetStart(); 2119 for ( sal_uInt16 y = pLine->GetStartPortion(); y <= pLine->GetEndPortion(); y++ ) 2120 { 2121 DBG_ASSERT( pPortion->GetTextPortions().Count(), "Zeile ohne Textportion im Paint!" ); 2122 TETextPortion* pTextPortion = pPortion->GetTextPortions().GetObject( y ); 2123 DBG_ASSERT( pTextPortion, "NULL-Pointer im Portioniterator in UpdateViews" ); 2124 2125 ImpInitLayoutMode( pOutDev /*, pTextPortion->IsRightToLeft() */); 2126 2127 long nTxtWidth = pTextPortion->GetWidth(); 2128 aTmpPos.X() = rStartPos.X() + ImpGetOutputOffset( nPara, pLine, nIndex, nIndex ); 2129 2130 // nur ausgeben, was im sichtbaren Bereich beginnt: 2131 if ( ( ( aTmpPos.X() + nTxtWidth ) >= 0 ) 2132 && ( !pPaintRange || ( 2133 ( TextPaM( nPara, nIndex ) < pPaintRange->GetEnd() ) && 2134 ( TextPaM( nPara, nIndex + pTextPortion->GetLen() ) > pPaintRange->GetStart() ) ) ) ) 2135 { 2136 switch ( pTextPortion->GetKind() ) 2137 { 2138 case PORTIONKIND_TEXT: 2139 { 2140 { 2141 Font aFont; 2142 SeekCursor( nPara, nIndex+1, aFont, pOutDev ); 2143 if( bTransparent ) 2144 aFont.SetTransparent( sal_True ); 2145 else if ( pSelection ) 2146 aFont.SetTransparent( sal_False ); 2147 pOutDev->SetFont( aFont ); 2148 2149 sal_uInt16 nTmpIndex = nIndex; 2150 sal_uInt16 nEnd = nTmpIndex + pTextPortion->GetLen(); 2151 Point aPos = aTmpPos; 2152 if ( pPaintRange ) 2153 { 2154 // evtl soll nicht alles ausgegeben werden... 2155 if ( ( pPaintRange->GetStart().GetPara() == nPara ) 2156 && ( nTmpIndex < pPaintRange->GetStart().GetIndex() ) ) 2157 { 2158 nTmpIndex = pPaintRange->GetStart().GetIndex(); 2159 } 2160 if ( ( pPaintRange->GetEnd().GetPara() == nPara ) 2161 && ( nEnd > pPaintRange->GetEnd().GetIndex() ) ) 2162 { 2163 nEnd = pPaintRange->GetEnd().GetIndex(); 2164 } 2165 } 2166 2167 sal_Bool bDone = sal_False; 2168 if ( pSelStart ) 2169 { 2170 // liegt ein Teil in der Selektion??? 2171 TextPaM aTextStart( nPara, nTmpIndex ); 2172 TextPaM aTextEnd( nPara, nEnd ); 2173 if ( ( aTextStart < *pSelEnd ) && ( aTextEnd > *pSelStart ) ) 2174 { 2175 sal_uInt16 nL; 2176 2177 // 1) Bereich vor Selektion 2178 if ( aTextStart < *pSelStart ) 2179 { 2180 nL = pSelStart->GetIndex() - nTmpIndex; 2181 pOutDev->SetFont( aFont); 2182 aPos.X() = rStartPos.X() + ImpGetOutputOffset( nPara, pLine, nTmpIndex, nTmpIndex+nL ); 2183 pOutDev->DrawText( aPos, pPortion->GetNode()->GetText(), nTmpIndex, nL ); 2184 nTmpIndex = nTmpIndex + nL; 2185 2186 } 2187 // 2) Bereich mit Selektion 2188 nL = nEnd-nTmpIndex; 2189 if ( aTextEnd > *pSelEnd ) 2190 nL = pSelEnd->GetIndex() - nTmpIndex; 2191 if ( nL ) 2192 { 2193 Color aOldTextColor = pOutDev->GetTextColor(); 2194 pOutDev->SetTextColor( rStyleSettings.GetHighlightTextColor() ); 2195 pOutDev->SetTextFillColor( rStyleSettings.GetHighlightColor() ); 2196 aPos.X() = rStartPos.X() + ImpGetOutputOffset( nPara, pLine, nTmpIndex, nTmpIndex+nL ); 2197 pOutDev->DrawText( aPos, pPortion->GetNode()->GetText(), nTmpIndex, nL ); 2198 pOutDev->SetTextColor( aOldTextColor ); 2199 pOutDev->SetTextFillColor(); 2200 nTmpIndex = nTmpIndex + nL; 2201 } 2202 2203 // 3) Bereich nach Selektion 2204 if ( nTmpIndex < nEnd ) 2205 { 2206 nL = nEnd-nTmpIndex; 2207 aPos.X() = rStartPos.X() + ImpGetOutputOffset( nPara, pLine, nTmpIndex, nTmpIndex+nL ); 2208 pOutDev->DrawText( aPos, pPortion->GetNode()->GetText(), nTmpIndex, nEnd-nTmpIndex ); 2209 } 2210 bDone = sal_True; 2211 } 2212 } 2213 if ( !bDone ) 2214 { 2215 aPos.X() = rStartPos.X() + ImpGetOutputOffset( nPara, pLine, nTmpIndex, nEnd ); 2216 pOutDev->DrawText( aPos, pPortion->GetNode()->GetText(), nTmpIndex, nEnd-nTmpIndex ); 2217 } 2218 } 2219 2220 } 2221 break; 2222 case PORTIONKIND_TAB: 2223 { 2224 // Bei HideSelection() nur Range, pSelection = 0. 2225 if ( pSelStart || pPaintRange ) 2226 { 2227 Rectangle aTabArea( aTmpPos, Point( aTmpPos.X()+nTxtWidth, aTmpPos.Y()+mnCharHeight-1 ) ); 2228 sal_Bool bDone = sal_False; 2229 if ( pSelStart ) 2230 { 2231 // liegt der Tab in der Selektion??? 2232 TextPaM aTextStart( nPara, nIndex ); 2233 TextPaM aTextEnd( nPara, nIndex+1 ); 2234 if ( ( aTextStart < *pSelEnd ) && ( aTextEnd > *pSelStart ) ) 2235 { 2236 Color aOldColor = pOutDev->GetFillColor(); 2237 pOutDev->SetFillColor( rStyleSettings.GetHighlightColor() ); 2238 pOutDev->DrawRect( aTabArea ); 2239 pOutDev->SetFillColor( aOldColor ); 2240 bDone = sal_True; 2241 } 2242 } 2243 if ( !bDone ) 2244 { 2245 pOutDev->Erase( aTabArea ); 2246 } 2247 } 2248 #ifdef EDITDEBUG 2249 Rectangle aTabArea( aTmpPos, Point( aTmpPos.X()+nTxtWidth, aTmpPos.Y()+mnCharHeight-1 ) ); 2250 Color aOldColor = pOutDev->GetFillColor(); 2251 pOutDev->SetFillColor( (y%2) ? COL_RED : COL_GREEN ); 2252 pOutDev->DrawRect( aTabArea ); 2253 pOutDev->SetFillColor( aOldColor ); 2254 #endif 2255 } 2256 break; 2257 default: DBG_ERROR( "ImpPaint: Unknown Portion-Type !" ); 2258 } 2259 } 2260 2261 nIndex = nIndex + pTextPortion->GetLen(); 2262 } 2263 } 2264 2265 nY += mnCharHeight; 2266 2267 if ( pPaintArea && ( nY >= pPaintArea->Bottom() ) ) 2268 break; // keine sichtbaren Aktionen mehr... 2269 } 2270 } 2271 else 2272 { 2273 nY += nParaHeight; 2274 } 2275 2276 if ( pPaintArea && ( nY > pPaintArea->Bottom() ) ) 2277 break; // keine sichtbaren Aktionen mehr... 2278 } 2279 } 2280 2281 sal_Bool TextEngine::CreateLines( sal_uLong nPara ) 2282 { 2283 // sal_Bool: Aenderung der Hoehe des Absatzes Ja/Nein - sal_True/sal_False 2284 2285 TextNode* pNode = mpDoc->GetNodes().GetObject( nPara ); 2286 TEParaPortion* pTEParaPortion = mpTEParaPortions->GetObject( nPara ); 2287 DBG_ASSERT( pTEParaPortion->IsInvalid(), "CreateLines: Portion nicht invalid!" ); 2288 2289 sal_uInt16 nOldLineCount = pTEParaPortion->GetLines().Count(); 2290 2291 // --------------------------------------------------------------- 2292 // Schnelle Sonderbehandlung fuer leere Absaetze... 2293 // --------------------------------------------------------------- 2294 if ( pTEParaPortion->GetNode()->GetText().Len() == 0 ) 2295 { 2296 // schnelle Sonderbehandlung... 2297 if ( pTEParaPortion->GetTextPortions().Count() ) 2298 pTEParaPortion->GetTextPortions().Reset(); 2299 if ( pTEParaPortion->GetLines().Count() ) 2300 pTEParaPortion->GetLines().DeleteAndDestroy( 0, pTEParaPortion->GetLines().Count() ); 2301 CreateAndInsertEmptyLine( nPara ); 2302 pTEParaPortion->SetValid(); 2303 return nOldLineCount != pTEParaPortion->GetLines().Count(); 2304 } 2305 2306 // --------------------------------------------------------------- 2307 // Initialisierung...... 2308 // --------------------------------------------------------------- 2309 2310 if ( pTEParaPortion->GetLines().Count() == 0 ) 2311 { 2312 TextLine* pL = new TextLine; 2313 pTEParaPortion->GetLines().Insert( pL, 0 ); 2314 } 2315 2316 const short nInvalidDiff = pTEParaPortion->GetInvalidDiff(); 2317 const sal_uInt16 nInvalidStart = pTEParaPortion->GetInvalidPosStart(); 2318 const sal_uInt16 nInvalidEnd = nInvalidStart + Abs( nInvalidDiff ); 2319 sal_Bool bQuickFormat = sal_False; 2320 2321 if ( !pTEParaPortion->GetWritingDirectionInfos().Count() ) 2322 ImpInitWritingDirections( nPara ); 2323 2324 if ( pTEParaPortion->GetWritingDirectionInfos().Count() == 1 ) 2325 { 2326 if ( pTEParaPortion->IsSimpleInvalid() && ( nInvalidDiff > 0 ) ) 2327 { 2328 bQuickFormat = sal_True; 2329 } 2330 else if ( ( pTEParaPortion->IsSimpleInvalid() ) && ( nInvalidDiff < 0 ) ) 2331 { 2332 // pruefen, ob loeschen ueber Portiongrenzen erfolgte... 2333 sal_uInt16 nStart = nInvalidStart; // DOPPELT !!!!!!!!!!!!!!! 2334 sal_uInt16 nEnd = nStart - nInvalidDiff; // neg. 2335 bQuickFormat = sal_True; 2336 sal_uInt16 nPos = 0; 2337 sal_uInt16 nPortions = pTEParaPortion->GetTextPortions().Count(); 2338 for ( sal_uInt16 nTP = 0; nTP < nPortions; nTP++ ) 2339 { 2340 // Es darf kein Start/Ende im geloeschten Bereich liegen. 2341 TETextPortion* const pTP = pTEParaPortion->GetTextPortions().GetObject( nTP ); 2342 nPos = nPos + pTP->GetLen(); 2343 if ( ( nPos > nStart ) && ( nPos < nEnd ) ) 2344 { 2345 bQuickFormat = sal_False; 2346 break; 2347 } 2348 } 2349 } 2350 } 2351 2352 if ( bQuickFormat ) 2353 RecalcTextPortion( nPara, nInvalidStart, nInvalidDiff ); 2354 else 2355 CreateTextPortions( nPara, nInvalidStart ); 2356 2357 // --------------------------------------------------------------- 2358 // Zeile mit InvalidPos suchen, eine Zeile davor beginnen... 2359 // Zeilen flaggen => nicht removen ! 2360 // --------------------------------------------------------------- 2361 2362 sal_uInt16 nLine = pTEParaPortion->GetLines().Count()-1; 2363 for ( sal_uInt16 nL = 0; nL <= nLine; nL++ ) 2364 { 2365 TextLine* pLine = pTEParaPortion->GetLines().GetObject( nL ); 2366 if ( pLine->GetEnd() > nInvalidStart ) 2367 { 2368 nLine = nL; 2369 break; 2370 } 2371 pLine->SetValid(); 2372 } 2373 // Eine Zeile davor beginnen... 2374 // Wenn ganz hinten getippt wird, kann sich die Zeile davor nicht aendern. 2375 if ( nLine && ( !pTEParaPortion->IsSimpleInvalid() || ( nInvalidEnd < pNode->GetText().Len() ) || ( nInvalidDiff <= 0 ) ) ) 2376 nLine--; 2377 2378 TextLine* pLine = pTEParaPortion->GetLines().GetObject( nLine ); 2379 2380 // --------------------------------------------------------------- 2381 // Ab hier alle Zeilen durchformatieren... 2382 // --------------------------------------------------------------- 2383 sal_uInt16 nDelFromLine = 0xFFFF; 2384 sal_Bool bLineBreak = sal_False; 2385 2386 sal_uInt16 nIndex = pLine->GetStart(); 2387 TextLine aSaveLine( *pLine ); 2388 2389 Font aFont; 2390 2391 sal_Bool bCalcPortion = sal_True; 2392 2393 while ( nIndex < pNode->GetText().Len() ) 2394 { 2395 sal_Bool bEOL = sal_False; 2396 sal_Bool bEOC = sal_False; 2397 sal_uInt16 nPortionStart = 0; 2398 sal_uInt16 nPortionEnd = 0; 2399 2400 sal_uInt16 nTmpPos = nIndex; 2401 sal_uInt16 nTmpPortion = pLine->GetStartPortion(); 2402 long nTmpWidth = mpDoc->GetLeftMargin(); 2403 // long nXWidth = mnMaxTextWidth ? ( mnMaxTextWidth - mpDoc->GetLeftMargin() ) : 0x7FFFFFFF; 2404 // Margin nicht abziehen, ist schon in TmpWidth enthalten. 2405 long nXWidth = mnMaxTextWidth ? mnMaxTextWidth : 0x7FFFFFFF; 2406 if ( nXWidth < nTmpWidth ) 2407 nXWidth = nTmpWidth; 2408 2409 // Portion suchen, die nicht mehr in Zeile passt.... 2410 TETextPortion* pPortion = 0; 2411 sal_Bool bBrokenLine = sal_False; 2412 bLineBreak = sal_False; 2413 2414 while ( ( nTmpWidth <= nXWidth ) && !bEOL && ( nTmpPortion < pTEParaPortion->GetTextPortions().Count() ) ) 2415 { 2416 nPortionStart = nTmpPos; 2417 pPortion = pTEParaPortion->GetTextPortions().GetObject( nTmpPortion ); 2418 DBG_ASSERT( pPortion->GetLen(), "Leere Portion in CreateLines ?!" ); 2419 if ( pNode->GetText().GetChar( nTmpPos ) == '\t' ) 2420 { 2421 long nCurPos = nTmpWidth-mpDoc->GetLeftMargin(); 2422 nTmpWidth = ((nCurPos/mnDefTab)+1)*mnDefTab+mpDoc->GetLeftMargin(); 2423 pPortion->GetWidth() = nTmpWidth - nCurPos - mpDoc->GetLeftMargin(); 2424 // Wenn dies das erste Token in der Zeile ist, und 2425 // nTmpWidth > aPaperSize.Width, habe ich eine Endlos-Schleife! 2426 if ( ( nTmpWidth >= nXWidth ) && ( nTmpPortion == pLine->GetStartPortion() ) ) 2427 { 2428 // Aber was jetzt ? Tab passend machen! 2429 pPortion->GetWidth() = nXWidth-1; 2430 nTmpWidth = pPortion->GetWidth(); 2431 bEOL = sal_True; 2432 bBrokenLine = sal_True; 2433 } 2434 pPortion->GetKind() = PORTIONKIND_TAB; 2435 } 2436 else 2437 { 2438 2439 if ( bCalcPortion || !pPortion->HasValidSize() ) 2440 pPortion->GetWidth() = (long)CalcTextWidth( nPara, nTmpPos, pPortion->GetLen() ); 2441 nTmpWidth += pPortion->GetWidth(); 2442 2443 pPortion->GetRightToLeft() = ImpGetRightToLeft( nPara, nTmpPos+1 ); 2444 pPortion->GetKind() = PORTIONKIND_TEXT; 2445 } 2446 2447 nTmpPos = nTmpPos + pPortion->GetLen(); 2448 nPortionEnd = nTmpPos; 2449 nTmpPortion++; 2450 } 2451 2452 // das war evtl. eine Portion zu weit: 2453 sal_Bool bFixedEnd = sal_False; 2454 if ( nTmpWidth > nXWidth ) 2455 { 2456 nPortionEnd = nTmpPos; 2457 nTmpPos = nTmpPos - pPortion->GetLen(); 2458 nPortionStart = nTmpPos; 2459 nTmpPortion--; 2460 bEOL = sal_False; 2461 bEOC = sal_False; 2462 2463 nTmpWidth -= pPortion->GetWidth(); 2464 if ( pPortion->GetKind() == PORTIONKIND_TAB ) 2465 { 2466 bEOL = sal_True; 2467 bFixedEnd = sal_True; 2468 } 2469 } 2470 else 2471 { 2472 bEOL = sal_True; 2473 bEOC = sal_True; 2474 pLine->SetEnd( nPortionEnd ); 2475 DBG_ASSERT( pTEParaPortion->GetTextPortions().Count(), "Keine TextPortions?" ); 2476 pLine->SetEndPortion( (sal_uInt16)pTEParaPortion->GetTextPortions().Count() - 1 ); 2477 } 2478 2479 if ( bFixedEnd ) 2480 { 2481 pLine->SetEnd( nPortionStart ); 2482 pLine->SetEndPortion( nTmpPortion-1 ); 2483 } 2484 else if ( bLineBreak || bBrokenLine ) 2485 { 2486 pLine->SetEnd( nPortionStart+1 ); 2487 pLine->SetEndPortion( nTmpPortion-1 ); 2488 bEOC = sal_False; // wurde oben gesetzt, vielleich mal die if's umstellen? 2489 } 2490 else if ( !bEOL ) 2491 { 2492 DBG_ASSERT( (nPortionEnd-nPortionStart) == pPortion->GetLen(), "Doch eine andere Portion?!" ); 2493 long nRemainingWidth = mnMaxTextWidth - nTmpWidth; 2494 ImpBreakLine( nPara, pLine, pPortion, nPortionStart, nRemainingWidth ); 2495 } 2496 2497 if ( ( ImpGetAlign() == TXTALIGN_CENTER ) || ( ImpGetAlign() == TXTALIGN_RIGHT ) ) 2498 { 2499 // Ausrichten... 2500 long nTextWidth = 0; 2501 for ( sal_uInt16 nTP = pLine->GetStartPortion(); nTP <= pLine->GetEndPortion(); nTP++ ) 2502 { 2503 TETextPortion* pTextPortion = pTEParaPortion->GetTextPortions().GetObject( nTP ); 2504 nTextWidth += pTextPortion->GetWidth(); 2505 } 2506 long nSpace = mnMaxTextWidth - nTextWidth; 2507 if ( nSpace > 0 ) 2508 { 2509 if ( ImpGetAlign() == TXTALIGN_CENTER ) 2510 pLine->SetStartX( (sal_uInt16)(nSpace / 2) ); 2511 else // TXTALIGN_RIGHT 2512 pLine->SetStartX( (sal_uInt16)nSpace ); 2513 } 2514 } 2515 else 2516 { 2517 pLine->SetStartX( mpDoc->GetLeftMargin() ); 2518 } 2519 2520 // ----------------------------------------------------------------- 2521 // pruefen, ob die Zeile neu ausgegeben werden muss... 2522 // ----------------------------------------------------------------- 2523 pLine->SetInvalid(); 2524 2525 if ( pTEParaPortion->IsSimpleInvalid() ) 2526 { 2527 // Aenderung durch einfache Textaenderung... 2528 // Formatierung nicht abbrechen, da Portions evtl. wieder 2529 // gesplittet werden muessen! 2530 // Wenn irgendwann mal abbrechbar, dann fogende Zeilen Validieren! 2531 // Aber ggf. als Valid markieren, damit weniger Ausgabe... 2532 if ( pLine->GetEnd() < nInvalidStart ) 2533 { 2534 if ( *pLine == aSaveLine ) 2535 { 2536 pLine->SetValid(); 2537 } 2538 } 2539 else 2540 { 2541 sal_uInt16 nStart = pLine->GetStart(); 2542 sal_uInt16 nEnd = pLine->GetEnd(); 2543 2544 if ( nStart > nInvalidEnd ) 2545 { 2546 if ( ( ( nStart-nInvalidDiff ) == aSaveLine.GetStart() ) && 2547 ( ( nEnd-nInvalidDiff ) == aSaveLine.GetEnd() ) ) 2548 { 2549 pLine->SetValid(); 2550 if ( bCalcPortion && bQuickFormat ) 2551 { 2552 bCalcPortion = sal_False; 2553 pTEParaPortion->CorrectValuesBehindLastFormattedLine( nLine ); 2554 break; 2555 } 2556 } 2557 } 2558 else if ( bQuickFormat && ( nEnd > nInvalidEnd) ) 2559 { 2560 // Wenn die ungueltige Zeile so endet, dass die naechste an 2561 // der 'gleichen' Textstelle wie vorher beginnt, also nicht 2562 // anders umgebrochen wird, brauche ich dort auch nicht die 2563 // textbreiten neu bestimmen: 2564 if ( nEnd == ( aSaveLine.GetEnd() + nInvalidDiff ) ) 2565 { 2566 bCalcPortion = sal_False; 2567 pTEParaPortion->CorrectValuesBehindLastFormattedLine( nLine ); 2568 break; 2569 } 2570 } 2571 } 2572 } 2573 2574 nIndex = pLine->GetEnd(); // naechste Zeile Start = letzte Zeile Ende 2575 // weil nEnd hinter das letzte Zeichen zeigt! 2576 2577 sal_uInt16 nEndPortion = pLine->GetEndPortion(); 2578 2579 // Naechste Zeile oder ggf. neue Zeile.... 2580 pLine = 0; 2581 if ( nLine < pTEParaPortion->GetLines().Count()-1 ) 2582 pLine = pTEParaPortion->GetLines().GetObject( ++nLine ); 2583 if ( pLine && ( nIndex >= pNode->GetText().Len() ) ) 2584 { 2585 nDelFromLine = nLine; 2586 break; 2587 } 2588 if ( !pLine && ( nIndex < pNode->GetText().Len() ) ) 2589 { 2590 pLine = new TextLine; 2591 pTEParaPortion->GetLines().Insert( pLine, ++nLine ); 2592 } 2593 if ( pLine ) 2594 { 2595 aSaveLine = *pLine; 2596 pLine->SetStart( nIndex ); 2597 pLine->SetEnd( nIndex ); 2598 pLine->SetStartPortion( nEndPortion+1 ); 2599 pLine->SetEndPortion( nEndPortion+1 ); 2600 } 2601 } // while ( Index < Len ) 2602 2603 if ( nDelFromLine != 0xFFFF ) 2604 pTEParaPortion->GetLines().DeleteAndDestroy( nDelFromLine, pTEParaPortion->GetLines().Count() - nDelFromLine ); 2605 2606 DBG_ASSERT( pTEParaPortion->GetLines().Count(), "Keine Zeile nach CreateLines!" ); 2607 2608 if ( bLineBreak == sal_True ) 2609 CreateAndInsertEmptyLine( nPara ); 2610 2611 pTEParaPortion->SetValid(); 2612 2613 return nOldLineCount != pTEParaPortion->GetLines().Count(); 2614 } 2615 2616 String TextEngine::GetWord( const TextPaM& rCursorPos, TextPaM* pStartOfWord ) 2617 { 2618 String aWord; 2619 if ( rCursorPos.GetPara() < mpDoc->GetNodes().Count() ) 2620 { 2621 TextSelection aSel( rCursorPos ); 2622 TextNode* pNode = mpDoc->GetNodes().GetObject( rCursorPos.GetPara() ); 2623 uno::Reference < i18n::XBreakIterator > xBI = GetBreakIterator(); 2624 i18n::Boundary aBoundary = xBI->getWordBoundary( pNode->GetText(), rCursorPos.GetIndex(), GetLocale(), i18n::WordType::ANYWORD_IGNOREWHITESPACES, sal_True ); 2625 aSel.GetStart().GetIndex() = (sal_uInt16)aBoundary.startPos; 2626 aSel.GetEnd().GetIndex() = (sal_uInt16)aBoundary.endPos; 2627 aWord = pNode->GetText().Copy( aSel.GetStart().GetIndex(), aSel.GetEnd().GetIndex() - aSel.GetStart().GetIndex() ); 2628 if ( pStartOfWord ) 2629 *pStartOfWord = aSel.GetStart(); 2630 } 2631 return aWord; 2632 } 2633 2634 sal_Bool TextEngine::Read( SvStream& rInput, const TextSelection* pSel ) 2635 { 2636 sal_Bool bUpdate = GetUpdateMode(); 2637 SetUpdateMode( sal_False ); 2638 2639 UndoActionStart(); 2640 TextSelection aSel; 2641 if ( pSel ) 2642 aSel = *pSel; 2643 else 2644 { 2645 sal_uLong nParas = mpDoc->GetNodes().Count(); 2646 TextNode* pNode = mpDoc->GetNodes().GetObject( nParas - 1 ); 2647 aSel = TextPaM( nParas-1 , pNode->GetText().Len() ); 2648 } 2649 2650 if ( aSel.HasRange() ) 2651 aSel = ImpDeleteText( aSel ); 2652 2653 ByteString aLine; 2654 sal_Bool bDone = rInput.ReadLine( aLine ); 2655 String aTmpStr( aLine, rInput.GetStreamCharSet() ), aStr; 2656 while ( bDone ) 2657 { 2658 aSel = ImpInsertText( aSel, aTmpStr ); 2659 bDone = rInput.ReadLine( aLine ); 2660 aTmpStr = String( aLine, rInput.GetStreamCharSet() ); 2661 if ( bDone ) 2662 aSel = ImpInsertParaBreak( aSel.GetEnd() ); 2663 } 2664 2665 UndoActionEnd(); 2666 2667 TextSelection aNewSel( aSel.GetEnd(), aSel.GetEnd() ); 2668 2669 // Damit bei FormatAndUpdate nicht auf die ungueltige Selektion zugegriffen wird. 2670 if ( GetActiveView() ) 2671 GetActiveView()->ImpSetSelection( aNewSel ); 2672 2673 SetUpdateMode( bUpdate ); 2674 FormatAndUpdate( GetActiveView() ); 2675 2676 return rInput.GetError() ? sal_False : sal_True; 2677 } 2678 2679 sal_Bool TextEngine::Write( SvStream& rOutput, const TextSelection* pSel, sal_Bool bHTML ) 2680 { 2681 TextSelection aSel; 2682 if ( pSel ) 2683 aSel = *pSel; 2684 else 2685 { 2686 sal_uLong nParas = mpDoc->GetNodes().Count(); 2687 TextNode* pNode = mpDoc->GetNodes().GetObject( nParas - 1 ); 2688 aSel.GetStart() = TextPaM( 0, 0 ); 2689 aSel.GetEnd() = TextPaM( nParas-1, pNode->GetText().Len() ); 2690 } 2691 2692 if ( bHTML ) 2693 { 2694 rOutput.WriteLine( "<HTML>" ); 2695 rOutput.WriteLine( "<BODY>" ); 2696 } 2697 2698 for ( sal_uLong nPara = aSel.GetStart().GetPara(); nPara <= aSel.GetEnd().GetPara(); nPara++ ) 2699 { 2700 TextNode* pNode = mpDoc->GetNodes().GetObject( nPara ); 2701 2702 sal_uInt16 nStartPos = 0; 2703 sal_uInt16 nEndPos = pNode->GetText().Len(); 2704 if ( nPara == aSel.GetStart().GetPara() ) 2705 nStartPos = aSel.GetStart().GetIndex(); 2706 if ( nPara == aSel.GetEnd().GetPara() ) 2707 nEndPos = aSel.GetEnd().GetIndex(); 2708 2709 String aText; 2710 if ( !bHTML ) 2711 { 2712 aText = pNode->GetText().Copy( nStartPos, nEndPos-nStartPos ); 2713 } 2714 else 2715 { 2716 aText.AssignAscii( RTL_CONSTASCII_STRINGPARAM( "<P STYLE=\"margin-bottom: 0cm\">" ) ); 2717 2718 if ( nStartPos == nEndPos ) 2719 { 2720 // Leerzeilen werden von Writer wegoptimiert 2721 aText.AppendAscii( RTL_CONSTASCII_STRINGPARAM( "<BR>" ) ); 2722 } 2723 else 2724 { 2725 sal_uInt16 nTmpStart = nStartPos; 2726 sal_uInt16 nTmpEnd = nEndPos; 2727 do 2728 { 2729 TextCharAttrib* pAttr = pNode->GetCharAttribs().FindNextAttrib( TEXTATTR_HYPERLINK, nTmpStart, nEndPos ); 2730 nTmpEnd = pAttr ? pAttr->GetStart() : nEndPos; 2731 2732 // Text vor dem Attribut 2733 aText += pNode->GetText().Copy( nTmpStart, nTmpEnd-nTmpStart ); 2734 2735 if ( pAttr ) 2736 { 2737 nTmpEnd = Min( pAttr->GetEnd(), nEndPos ); 2738 2739 // z.B. <A HREF="http://www.mopo.de/">Morgenpost</A> 2740 aText.AppendAscii( RTL_CONSTASCII_STRINGPARAM( "<A HREF=\"" ) ); 2741 aText += ((const TextAttribHyperLink&) pAttr->GetAttr() ).GetURL(); 2742 aText.AppendAscii( RTL_CONSTASCII_STRINGPARAM( "\">" ) ); 2743 nTmpStart = pAttr->GetStart(); 2744 aText += pNode->GetText().Copy( nTmpStart, nTmpEnd-nTmpStart ); 2745 aText.AppendAscii( RTL_CONSTASCII_STRINGPARAM( "</A>" ) ); 2746 2747 nTmpStart = pAttr->GetEnd(); 2748 } 2749 } while ( nTmpEnd < nEndPos ); 2750 } 2751 2752 aText.AppendAscii( RTL_CONSTASCII_STRINGPARAM( "</P>" ) ); 2753 } 2754 rOutput.WriteLine( ByteString( aText, rOutput.GetStreamCharSet() ) ); 2755 } 2756 2757 if ( bHTML ) 2758 { 2759 rOutput.WriteLine( "</BODY>" ); 2760 rOutput.WriteLine( "</HTML>" ); 2761 } 2762 2763 return rOutput.GetError() ? sal_False : sal_True; 2764 } 2765 2766 void TextEngine::RemoveAttribs( sal_uLong nPara, sal_Bool bIdleFormatAndUpdate ) 2767 { 2768 if ( nPara < mpDoc->GetNodes().Count() ) 2769 { 2770 TextNode* pNode = mpDoc->GetNodes().GetObject( nPara ); 2771 if ( pNode->GetCharAttribs().Count() ) 2772 { 2773 pNode->GetCharAttribs().Clear( sal_True ); 2774 2775 TEParaPortion* pTEParaPortion = mpTEParaPortions->GetObject( nPara ); 2776 pTEParaPortion->MarkSelectionInvalid( 0, pNode->GetText().Len() ); 2777 2778 mbFormatted = sal_False; 2779 2780 if ( bIdleFormatAndUpdate ) 2781 IdleFormatAndUpdate( NULL, 0xFFFF ); 2782 else 2783 FormatAndUpdate( NULL ); 2784 } 2785 } 2786 } 2787 void TextEngine::RemoveAttribs( sal_uLong nPara, sal_uInt16 nWhich, sal_Bool bIdleFormatAndUpdate ) 2788 { 2789 if ( nPara < mpDoc->GetNodes().Count() ) 2790 { 2791 TextNode* pNode = mpDoc->GetNodes().GetObject( nPara ); 2792 if ( pNode->GetCharAttribs().Count() ) 2793 { 2794 TextCharAttribList& rAttribs = pNode->GetCharAttribs(); 2795 sal_uInt16 nAttrCount = rAttribs.Count(); 2796 for(sal_uInt16 nAttr = nAttrCount; nAttr; --nAttr) 2797 { 2798 if(rAttribs.GetAttrib( nAttr - 1 )->Which() == nWhich) 2799 rAttribs.RemoveAttrib( nAttr -1 ); 2800 } 2801 TEParaPortion* pTEParaPortion = mpTEParaPortions->GetObject( nPara ); 2802 pTEParaPortion->MarkSelectionInvalid( 0, pNode->GetText().Len() ); 2803 mbFormatted = sal_False; 2804 if(bIdleFormatAndUpdate) 2805 IdleFormatAndUpdate( NULL, 0xFFFF ); 2806 else 2807 FormatAndUpdate( NULL ); 2808 } 2809 } 2810 } 2811 void TextEngine::RemoveAttrib( sal_uLong nPara, const TextCharAttrib& rAttrib ) 2812 { 2813 if ( nPara < mpDoc->GetNodes().Count() ) 2814 { 2815 TextNode* pNode = mpDoc->GetNodes().GetObject( nPara ); 2816 if ( pNode->GetCharAttribs().Count() ) 2817 { 2818 TextCharAttribList& rAttribs = pNode->GetCharAttribs(); 2819 sal_uInt16 nAttrCount = rAttribs.Count(); 2820 for(sal_uInt16 nAttr = nAttrCount; nAttr; --nAttr) 2821 { 2822 if(rAttribs.GetAttrib( nAttr - 1 ) == &rAttrib) 2823 { 2824 rAttribs.RemoveAttrib( nAttr -1 ); 2825 break; 2826 } 2827 } 2828 TEParaPortion* pTEParaPortion = mpTEParaPortions->GetObject( nPara ); 2829 pTEParaPortion->MarkSelectionInvalid( 0, pNode->GetText().Len() ); 2830 mbFormatted = sal_False; 2831 FormatAndUpdate( NULL ); 2832 } 2833 } 2834 } 2835 2836 void TextEngine::SetAttrib( const TextAttrib& rAttr, sal_uLong nPara, sal_uInt16 nStart, sal_uInt16 nEnd, sal_Bool bIdleFormatAndUpdate ) 2837 { 2838 // Es wird hier erstmal nicht geprueft, ob sich Attribute ueberlappen! 2839 // Diese Methode ist erstmal nur fuer einen Editor, der fuer eine Zeile 2840 // _schnell_ das Syntax-Highlight einstellen will. 2841 2842 // Da die TextEngine z.Zt fuer Editoren gedacht ist gibt es auch kein 2843 // Undo fuer Attribute! 2844 2845 if ( nPara < mpDoc->GetNodes().Count() ) 2846 { 2847 TextNode* pNode = mpDoc->GetNodes().GetObject( nPara ); 2848 TEParaPortion* pTEParaPortion = mpTEParaPortions->GetObject( nPara ); 2849 2850 sal_uInt16 nMax = pNode->GetText().Len(); 2851 if ( nStart > nMax ) 2852 nStart = nMax; 2853 if ( nEnd > nMax ) 2854 nEnd = nMax; 2855 2856 pNode->GetCharAttribs().InsertAttrib( new TextCharAttrib( rAttr, nStart, nEnd ) ); 2857 pTEParaPortion->MarkSelectionInvalid( nStart, nEnd ); 2858 2859 mbFormatted = sal_False; 2860 if ( bIdleFormatAndUpdate ) 2861 IdleFormatAndUpdate( NULL, 0xFFFF ); 2862 else 2863 FormatAndUpdate( NULL ); 2864 } 2865 } 2866 2867 void TextEngine::SetTextAlign( TxtAlign eAlign ) 2868 { 2869 if ( eAlign != meAlign ) 2870 { 2871 meAlign = eAlign; 2872 FormatFullDoc(); 2873 UpdateViews(); 2874 } 2875 } 2876 2877 2878 void TextEngine::ValidateSelection( TextSelection& rSel ) const 2879 { 2880 ValidatePaM( rSel.GetStart() ); 2881 ValidatePaM( rSel.GetEnd() ); 2882 } 2883 2884 void TextEngine::ValidatePaM( TextPaM& rPaM ) const 2885 { 2886 sal_uLong nMaxPara = mpDoc->GetNodes().Count() - 1; 2887 if ( rPaM.GetPara() > nMaxPara ) 2888 { 2889 rPaM.GetPara() = nMaxPara; 2890 rPaM.GetIndex() = 0xFFFF; 2891 } 2892 2893 sal_uInt16 nMaxIndex = GetTextLen( rPaM.GetPara() ); 2894 if ( rPaM.GetIndex() > nMaxIndex ) 2895 rPaM.GetIndex() = nMaxIndex; 2896 } 2897 2898 2899 // Status & Selektionsanpassung 2900 2901 void TextEngine::ImpParagraphInserted( sal_uLong nPara ) 2902 { 2903 // Die aktive View braucht nicht angepasst werden, aber bei allen 2904 // passiven muss die Selektion angepasst werden: 2905 if ( mpViews->Count() > 1 ) 2906 { 2907 for ( sal_uInt16 nView = mpViews->Count(); nView; ) 2908 { 2909 TextView* pView = mpViews->GetObject( --nView ); 2910 if ( pView != GetActiveView() ) 2911 { 2912 // sal_Bool bInvers = pView->maSelection.GetEnd() < pView->maSelection.GetStart(); 2913 // TextPaM& rMin = !bInvers ? pView->maSelection.GetStart(): pView->maSelection.GetEnd(); 2914 // TextPaM& rMax = bInvers ? pView->maSelection.GetStart() : pView->maSelection.GetEnd(); 2915 // 2916 // if ( rMin.GetPara() >= nPara ) 2917 // rMin.GetPara()++; 2918 // if ( rMax.GetPara() >= nPara ) 2919 // rMax.GetPara()++; 2920 for ( int n = 0; n <= 1; n++ ) 2921 { 2922 TextPaM& rPaM = n ? pView->GetSelection().GetStart(): pView->GetSelection().GetEnd(); 2923 if ( rPaM.GetPara() >= nPara ) 2924 rPaM.GetPara()++; 2925 } 2926 } 2927 } 2928 } 2929 Broadcast( TextHint( TEXT_HINT_PARAINSERTED, nPara ) ); 2930 } 2931 2932 void TextEngine::ImpParagraphRemoved( sal_uLong nPara ) 2933 { 2934 if ( mpViews->Count() > 1 ) 2935 { 2936 for ( sal_uInt16 nView = mpViews->Count(); nView; ) 2937 { 2938 TextView* pView = mpViews->GetObject( --nView ); 2939 if ( pView != GetActiveView() ) 2940 { 2941 sal_uLong nParas = mpDoc->GetNodes().Count(); 2942 for ( int n = 0; n <= 1; n++ ) 2943 { 2944 TextPaM& rPaM = n ? pView->GetSelection().GetStart(): pView->GetSelection().GetEnd(); 2945 if ( rPaM.GetPara() > nPara ) 2946 rPaM.GetPara()--; 2947 else if ( rPaM.GetPara() == nPara ) 2948 { 2949 rPaM.GetIndex() = 0; 2950 if ( rPaM.GetPara() >= nParas ) 2951 rPaM.GetPara()--; 2952 } 2953 } 2954 } 2955 } 2956 } 2957 Broadcast( TextHint( TEXT_HINT_PARAREMOVED, nPara ) ); 2958 } 2959 2960 void TextEngine::ImpCharsRemoved( sal_uLong nPara, sal_uInt16 nPos, sal_uInt16 nChars ) 2961 { 2962 if ( mpViews->Count() > 1 ) 2963 { 2964 for ( sal_uInt16 nView = mpViews->Count(); nView; ) 2965 { 2966 TextView* pView = mpViews->GetObject( --nView ); 2967 if ( pView != GetActiveView() ) 2968 { 2969 sal_uInt16 nEnd = nPos+nChars; 2970 for ( int n = 0; n <= 1; n++ ) 2971 { 2972 TextPaM& rPaM = n ? pView->GetSelection().GetStart(): pView->GetSelection().GetEnd(); 2973 if ( rPaM.GetPara() == nPara ) 2974 { 2975 if ( rPaM.GetIndex() > nEnd ) 2976 rPaM.GetIndex() = rPaM.GetIndex() - nChars; 2977 else if ( rPaM.GetIndex() > nPos ) 2978 rPaM.GetIndex() = nPos; 2979 } 2980 } 2981 } 2982 } 2983 } 2984 Broadcast( TextHint( TEXT_HINT_PARACONTENTCHANGED, nPara ) ); 2985 } 2986 2987 void TextEngine::ImpCharsInserted( sal_uLong nPara, sal_uInt16 nPos, sal_uInt16 nChars ) 2988 { 2989 if ( mpViews->Count() > 1 ) 2990 { 2991 for ( sal_uInt16 nView = mpViews->Count(); nView; ) 2992 { 2993 TextView* pView = mpViews->GetObject( --nView ); 2994 if ( pView != GetActiveView() ) 2995 { 2996 for ( int n = 0; n <= 1; n++ ) 2997 { 2998 TextPaM& rPaM = n ? pView->GetSelection().GetStart(): pView->GetSelection().GetEnd(); 2999 if ( rPaM.GetPara() == nPara ) 3000 { 3001 if ( rPaM.GetIndex() >= nPos ) 3002 rPaM.GetIndex() = rPaM.GetIndex() + nChars; 3003 } 3004 } 3005 } 3006 } 3007 } 3008 Broadcast( TextHint( TEXT_HINT_PARACONTENTCHANGED, nPara ) ); 3009 } 3010 3011 void TextEngine::ImpFormattingParagraph( sal_uLong nPara ) 3012 { 3013 Broadcast( TextHint( TEXT_HINT_FORMATPARA, nPara ) ); 3014 } 3015 3016 void TextEngine::ImpTextHeightChanged() 3017 { 3018 Broadcast( TextHint( TEXT_HINT_TEXTHEIGHTCHANGED ) ); 3019 } 3020 3021 void TextEngine::ImpTextFormatted() 3022 { 3023 Broadcast( TextHint( TEXT_HINT_TEXTFORMATTED ) ); 3024 } 3025 3026 void TextEngine::Draw( OutputDevice* pDev, const Point& rPos ) 3027 { 3028 ImpPaint( pDev, rPos, NULL ); 3029 } 3030 3031 void TextEngine::SetLeftMargin( sal_uInt16 n ) 3032 { 3033 mpDoc->SetLeftMargin( n ); 3034 } 3035 3036 sal_uInt16 TextEngine::GetLeftMargin() const 3037 { 3038 return mpDoc->GetLeftMargin(); 3039 } 3040 3041 uno::Reference< i18n::XBreakIterator > TextEngine::GetBreakIterator() 3042 { 3043 if ( !mxBreakIterator.is() ) 3044 mxBreakIterator = vcl::unohelper::CreateBreakIterator(); 3045 DBG_ASSERT( mxBreakIterator.is(), "Could not create BreakIterator" ); 3046 return mxBreakIterator; 3047 } 3048 3049 void TextEngine::SetLocale( const ::com::sun::star::lang::Locale& rLocale ) 3050 { 3051 maLocale = rLocale; 3052 delete mpLocaleDataWrapper; 3053 mpLocaleDataWrapper = NULL; 3054 } 3055 3056 ::com::sun::star::lang::Locale TextEngine::GetLocale() 3057 { 3058 if ( !maLocale.Language.getLength() ) 3059 { 3060 maLocale = Application::GetSettings().GetUILocale(); 3061 } 3062 return maLocale; 3063 } 3064 3065 LocaleDataWrapper* TextEngine::ImpGetLocaleDataWrapper() 3066 { 3067 if ( !mpLocaleDataWrapper ) 3068 mpLocaleDataWrapper = new LocaleDataWrapper( vcl::unohelper::GetMultiServiceFactory(), GetLocale() ); 3069 3070 return mpLocaleDataWrapper; 3071 } 3072 3073 void TextEngine::SetRightToLeft( sal_Bool bR2L ) 3074 { 3075 if ( mbRightToLeft != bR2L ) 3076 { 3077 mbRightToLeft = bR2L; 3078 meAlign = bR2L ? TXTALIGN_RIGHT : TXTALIGN_LEFT; 3079 FormatFullDoc(); 3080 UpdateViews(); 3081 } 3082 } 3083 3084 void TextEngine::ImpInitWritingDirections( sal_uLong nPara ) 3085 { 3086 TEParaPortion* pParaPortion = mpTEParaPortions->GetObject( nPara ); 3087 TEWritingDirectionInfos& rInfos = pParaPortion->GetWritingDirectionInfos(); 3088 rInfos.Remove( 0, rInfos.Count() ); 3089 3090 if ( pParaPortion->GetNode()->GetText().Len() ) 3091 { 3092 const UBiDiLevel nBidiLevel = IsRightToLeft() ? 1 /*RTL*/ : 0 /*LTR*/; 3093 String aText( pParaPortion->GetNode()->GetText() ); 3094 3095 // 3096 // Bidi functions from icu 2.0 3097 // 3098 UErrorCode nError = U_ZERO_ERROR; 3099 UBiDi* pBidi = ubidi_openSized( aText.Len(), 0, &nError ); 3100 nError = U_ZERO_ERROR; 3101 3102 ubidi_setPara( pBidi, reinterpret_cast<const UChar *>(aText.GetBuffer()), aText.Len(), nBidiLevel, NULL, &nError ); // UChar != sal_Unicode in MinGW 3103 nError = U_ZERO_ERROR; 3104 3105 long nCount = ubidi_countRuns( pBidi, &nError ); 3106 3107 int32_t nStart = 0; 3108 int32_t nEnd; 3109 UBiDiLevel nCurrDir; 3110 3111 for ( sal_uInt16 nIdx = 0; nIdx < nCount; ++nIdx ) 3112 { 3113 ubidi_getLogicalRun( pBidi, nStart, &nEnd, &nCurrDir ); 3114 rInfos.Insert( TEWritingDirectionInfo( nCurrDir, (sal_uInt16)nStart, (sal_uInt16)nEnd ), rInfos.Count() ); 3115 nStart = nEnd; 3116 } 3117 3118 ubidi_close( pBidi ); 3119 } 3120 3121 // No infos mean no CTL and default dir is L2R... 3122 if ( !rInfos.Count() ) 3123 rInfos.Insert( TEWritingDirectionInfo( 0, 0, (sal_uInt16)pParaPortion->GetNode()->GetText().Len() ), rInfos.Count() ); 3124 3125 } 3126 3127 sal_uInt8 TextEngine::ImpGetRightToLeft( sal_uLong nPara, sal_uInt16 nPos, sal_uInt16* pStart, sal_uInt16* pEnd ) 3128 { 3129 sal_uInt8 nRightToLeft = 0; 3130 3131 TextNode* pNode = mpDoc->GetNodes().GetObject( nPara ); 3132 if ( pNode && pNode->GetText().Len() ) 3133 { 3134 TEParaPortion* pParaPortion = mpTEParaPortions->GetObject( nPara ); 3135 if ( !pParaPortion->GetWritingDirectionInfos().Count() ) 3136 ImpInitWritingDirections( nPara ); 3137 3138 TEWritingDirectionInfos& rDirInfos = pParaPortion->GetWritingDirectionInfos(); 3139 for ( sal_uInt16 n = 0; n < rDirInfos.Count(); n++ ) 3140 { 3141 if ( ( rDirInfos[n].nStartPos <= nPos ) && ( rDirInfos[n].nEndPos >= nPos ) ) 3142 { 3143 nRightToLeft = rDirInfos[n].nType; 3144 if ( pStart ) 3145 *pStart = rDirInfos[n].nStartPos; 3146 if ( pEnd ) 3147 *pEnd = rDirInfos[n].nEndPos; 3148 break; 3149 } 3150 } 3151 } 3152 return nRightToLeft; 3153 } 3154 3155 long TextEngine::ImpGetPortionXOffset( sal_uLong nPara, TextLine* pLine, sal_uInt16 nTextPortion ) 3156 { 3157 long nX = pLine->GetStartX(); 3158 3159 TEParaPortion* pParaPortion = mpTEParaPortions->GetObject( nPara ); 3160 3161 for ( sal_uInt16 i = pLine->GetStartPortion(); i < nTextPortion; i++ ) 3162 { 3163 TETextPortion* pPortion = pParaPortion->GetTextPortions().GetObject( i ); 3164 nX += pPortion->GetWidth(); 3165 } 3166 3167 TETextPortion* pDestPortion = pParaPortion->GetTextPortions().GetObject( nTextPortion ); 3168 if ( pDestPortion->GetKind() != PORTIONKIND_TAB ) 3169 { 3170 if ( !IsRightToLeft() && pDestPortion->GetRightToLeft() ) 3171 { 3172 // Portions behind must be added, visual before this portion 3173 sal_uInt16 nTmpPortion = nTextPortion+1; 3174 while ( nTmpPortion <= pLine->GetEndPortion() ) 3175 { 3176 TETextPortion* pNextTextPortion = pParaPortion->GetTextPortions().GetObject( nTmpPortion ); 3177 if ( pNextTextPortion->GetRightToLeft() && ( pNextTextPortion->GetKind() != PORTIONKIND_TAB ) ) 3178 nX += pNextTextPortion->GetWidth(); 3179 else 3180 break; 3181 nTmpPortion++; 3182 } 3183 // Portions before must be removed, visual behind this portion 3184 nTmpPortion = nTextPortion; 3185 while ( nTmpPortion > pLine->GetStartPortion() ) 3186 { 3187 --nTmpPortion; 3188 TETextPortion* pPrevTextPortion = pParaPortion->GetTextPortions().GetObject( nTmpPortion ); 3189 if ( pPrevTextPortion->GetRightToLeft() && ( pPrevTextPortion->GetKind() != PORTIONKIND_TAB ) ) 3190 nX -= pPrevTextPortion->GetWidth(); 3191 else 3192 break; 3193 } 3194 } 3195 else if ( IsRightToLeft() && !pDestPortion->IsRightToLeft() ) 3196 { 3197 // Portions behind must be removed, visual behind this portion 3198 sal_uInt16 nTmpPortion = nTextPortion+1; 3199 while ( nTmpPortion <= pLine->GetEndPortion() ) 3200 { 3201 TETextPortion* pNextTextPortion = pParaPortion->GetTextPortions().GetObject( nTmpPortion ); 3202 if ( !pNextTextPortion->IsRightToLeft() && ( pNextTextPortion->GetKind() != PORTIONKIND_TAB ) ) 3203 nX += pNextTextPortion->GetWidth(); 3204 else 3205 break; 3206 nTmpPortion++; 3207 } 3208 // Portions before must be added, visual before this portion 3209 nTmpPortion = nTextPortion; 3210 while ( nTmpPortion > pLine->GetStartPortion() ) 3211 { 3212 --nTmpPortion; 3213 TETextPortion* pPrevTextPortion = pParaPortion->GetTextPortions().GetObject( nTmpPortion ); 3214 if ( !pPrevTextPortion->IsRightToLeft() && ( pPrevTextPortion->GetKind() != PORTIONKIND_TAB ) ) 3215 nX -= pPrevTextPortion->GetWidth(); 3216 else 3217 break; 3218 } 3219 } 3220 } 3221 /* 3222 if ( IsRightToLeft() ) 3223 { 3224 // Switch X postions... 3225 DBG_ASSERT( GetMaxTextWidth(), "GetPortionXOffset - max text width?!" ); 3226 DBG_ASSERT( nX <= (long)GetMaxTextWidth(), "GetPortionXOffset - position out of paper size!" ); 3227 nX = GetMaxTextWidth() - nX; 3228 nX -= pDestPortion->GetWidth(); 3229 } 3230 */ 3231 3232 return nX; 3233 } 3234 3235 void TextEngine::ImpInitLayoutMode( OutputDevice* pOutDev, sal_Bool bDrawingR2LPortion ) 3236 { 3237 sal_uLong nLayoutMode = pOutDev->GetLayoutMode(); 3238 3239 nLayoutMode &= ~(TEXT_LAYOUT_BIDI_RTL | TEXT_LAYOUT_COMPLEX_DISABLED | TEXT_LAYOUT_BIDI_STRONG ); 3240 if ( bDrawingR2LPortion ) 3241 nLayoutMode |= TEXT_LAYOUT_BIDI_RTL; 3242 3243 pOutDev->SetLayoutMode( nLayoutMode ); 3244 } 3245 3246 TxtAlign TextEngine::ImpGetAlign() const 3247 { 3248 TxtAlign eAlign = meAlign; 3249 if ( IsRightToLeft() ) 3250 { 3251 if ( eAlign == TXTALIGN_LEFT ) 3252 eAlign = TXTALIGN_RIGHT; 3253 else if ( eAlign == TXTALIGN_RIGHT ) 3254 eAlign = TXTALIGN_LEFT; 3255 } 3256 return eAlign; 3257 } 3258 3259 long TextEngine::ImpGetOutputOffset( sal_uLong nPara, TextLine* pLine, sal_uInt16 nIndex, sal_uInt16 nIndex2 ) 3260 { 3261 TEParaPortion* pPortion = mpTEParaPortions->GetObject( nPara ); 3262 3263 sal_uInt16 nPortionStart; 3264 sal_uInt16 nPortion = pPortion->GetTextPortions().FindPortion( nIndex, nPortionStart, sal_True ); 3265 3266 TETextPortion* pTextPortion = pPortion->GetTextPortions().GetObject( nPortion ); 3267 3268 long nX; 3269 3270 if ( ( nIndex == nPortionStart ) && ( nIndex == nIndex2 ) ) 3271 { 3272 // Output of full portion, so we need portion x offset. 3273 // Use ImpGetPortionXOffset, because GetXPos may deliver left or right position from portioon, depending on R2L, L2R 3274 nX = ImpGetPortionXOffset( nPara, pLine, nPortion ); 3275 if ( IsRightToLeft() ) 3276 { 3277 nX = -nX -pTextPortion->GetWidth(); 3278 } 3279 } 3280 else 3281 { 3282 nX = ImpGetXPos( nPara, pLine, nIndex, nIndex == nPortionStart ); 3283 if ( nIndex2 != nIndex ) 3284 { 3285 long nX2 = ImpGetXPos( nPara, pLine, nIndex2, sal_False ); 3286 if ( ( !IsRightToLeft() && ( nX2 < nX ) ) || 3287 ( IsRightToLeft() && ( nX2 > nX ) ) ) 3288 { 3289 nX = nX2; 3290 } 3291 } 3292 if ( IsRightToLeft() ) 3293 { 3294 nX = -nX; 3295 } 3296 } 3297 3298 return nX; 3299 } 3300