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