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 #include "precompiled_sdext.hxx" 29 30 #include "PresenterTextView.hxx" 31 #include "PresenterCanvasHelper.hxx" 32 #include "PresenterGeometryHelper.hxx" 33 #include "PresenterTimer.hxx" 34 35 #include <cmath> 36 37 #include <com/sun/star/accessibility/AccessibleTextType.hpp> 38 #include <com/sun/star/container/XEnumerationAccess.hpp> 39 #include <com/sun/star/i18n/CharType.hpp> 40 #include <com/sun/star/i18n/CharacterIteratorMode.hpp> 41 #include <com/sun/star/i18n/CTLScriptType.hpp> 42 #include <com/sun/star/i18n/ScriptDirection.hpp> 43 #include <com/sun/star/i18n/WordType.hpp> 44 #include <com/sun/star/rendering/CompositeOperation.hpp> 45 #include <com/sun/star/rendering/TextDirection.hpp> 46 #include <com/sun/star/text/WritingMode2.hpp> 47 #include <boost/bind.hpp> 48 49 using namespace ::com::sun::star; 50 using namespace ::com::sun::star::accessibility; 51 using namespace ::com::sun::star::uno; 52 53 #define A2S(pString) (::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(pString))) 54 55 const static sal_Int64 CaretBlinkIntervall = 500 * 1000 * 1000; 56 57 //#define SHOW_CHARACTER_BOXES 58 59 namespace { 60 sal_Int32 Signum (const sal_Int32 nValue) 61 { 62 if (nValue < 0) 63 return -1; 64 else if (nValue > 0) 65 return +1; 66 else 67 return 0; 68 } 69 } 70 71 namespace sdext { namespace presenter { 72 73 74 //===== PresenterTextView ===================================================== 75 76 PresenterTextView::PresenterTextView ( 77 const Reference<XComponentContext>& rxContext, 78 const Reference<rendering::XCanvas>& rxCanvas, 79 const ::boost::function<void(const ::css::awt::Rectangle&)>& rInvalidator) 80 : mxCanvas(rxCanvas), 81 mbDoOuput(true), 82 mxBreakIterator(), 83 mxScriptTypeDetector(), 84 maLocation(0,0), 85 maSize(0,0), 86 mpFont(), 87 maParagraphs(), 88 mpCaret(new PresenterTextCaret( 89 ::boost::bind(&PresenterTextView::GetCaretBounds, this, _1, _2), 90 rInvalidator)), 91 mnLeftOffset(0), 92 mnTopOffset(0), 93 maInvalidator(rInvalidator), 94 mbIsFormatPending(false), 95 mnCharacterCount(-1), 96 maTextChangeBroadcaster() 97 { 98 Reference<lang::XMultiComponentFactory> xFactory ( 99 rxContext->getServiceManager(), UNO_QUERY); 100 if ( ! xFactory.is()) 101 return; 102 103 // Create the break iterator that we use to break text into lines. 104 mxBreakIterator = Reference<i18n::XBreakIterator>( 105 xFactory->createInstanceWithContext( 106 A2S("com.sun.star.i18n.BreakIterator"), 107 rxContext), 108 UNO_QUERY_THROW); 109 110 // Create the script type detector that is used to split paragraphs into 111 // portions of the same text direction. 112 mxScriptTypeDetector = Reference<i18n::XScriptTypeDetector>( 113 xFactory->createInstanceWithContext( 114 A2S("com.sun.star.i18n.ScriptTypeDetector"), 115 rxContext), 116 UNO_QUERY_THROW); 117 } 118 119 120 121 122 PresenterTextView::PresenterTextView ( 123 const Reference<XComponentContext>& rxContext, 124 const Reference<rendering::XCanvas>& rxCanvas) 125 : mxCanvas(rxCanvas), 126 mbDoOuput(false), 127 mxBreakIterator(), 128 mxScriptTypeDetector(), 129 maLocation(0,0), 130 maSize(0,0), 131 mpFont(), 132 maParagraphs(), 133 mpCaret(new PresenterTextCaret( 134 ::boost::bind(&PresenterTextView::GetCaretBounds, this, _1, _2), 135 ::boost::function<void(const css::awt::Rectangle&)>())), 136 mnLeftOffset(0), 137 mnTopOffset(0), 138 maInvalidator(), 139 mbIsFormatPending(false), 140 mnCharacterCount(-1), 141 maTextChangeBroadcaster() 142 { 143 Reference<lang::XMultiComponentFactory> xFactory ( 144 rxContext->getServiceManager(), UNO_QUERY); 145 if ( ! xFactory.is()) 146 return; 147 148 // Create the break iterator that we use to break text into lines. 149 mxBreakIterator = Reference<i18n::XBreakIterator>( 150 xFactory->createInstanceWithContext( 151 A2S("com.sun.star.i18n.BreakIterator"), 152 rxContext), 153 UNO_QUERY_THROW); 154 155 // Create the script type detector that is used to split paragraphs into 156 // portions of the same text direction. 157 mxScriptTypeDetector = Reference<i18n::XScriptTypeDetector>( 158 xFactory->createInstanceWithContext( 159 A2S("com.sun.star.i18n.ScriptTypeDetector"), 160 rxContext), 161 UNO_QUERY_THROW); 162 } 163 164 165 166 167 void PresenterTextView::SetText (const Reference<text::XText>& rxText) 168 { 169 maParagraphs.clear(); 170 mnCharacterCount = -1; 171 172 Reference<container::XEnumerationAccess> xParagraphAccess (rxText, UNO_QUERY); 173 if ( ! xParagraphAccess.is()) 174 return; 175 176 Reference<container::XEnumeration> xParagraphs ( 177 xParagraphAccess->createEnumeration() , UNO_QUERY); 178 if ( ! xParagraphs.is()) 179 return; 180 181 if ( ! mpFont || ! mpFont->PrepareFont(mxCanvas)) 182 return; 183 184 sal_Int32 nCharacterCount (0); 185 while (xParagraphs->hasMoreElements()) 186 { 187 SharedPresenterTextParagraph pParagraph (new PresenterTextParagraph( 188 maParagraphs.size(), 189 mxBreakIterator, 190 mxScriptTypeDetector, 191 Reference<text::XTextRange>(xParagraphs->nextElement(), UNO_QUERY), 192 mpCaret)); 193 pParagraph->SetupCellArray(mpFont); 194 pParagraph->SetCharacterOffset(nCharacterCount); 195 nCharacterCount += pParagraph->GetCharacterCount(); 196 maParagraphs.push_back(pParagraph); 197 } 198 199 if (mpCaret) 200 mpCaret->HideCaret(); 201 202 RequestFormat(); 203 } 204 205 206 207 208 void PresenterTextView::SetText (const ::rtl::OUString& rsText) 209 { 210 maParagraphs.clear(); 211 mnCharacterCount = -1; 212 213 if ( ! mpFont || ! mpFont->PrepareFont(mxCanvas)) 214 return; 215 216 sal_Int32 nCharacterCount (0); 217 218 SharedPresenterTextParagraph pParagraph (new PresenterTextParagraph( 219 0, 220 mxBreakIterator, 221 mxScriptTypeDetector, 222 rsText, 223 mpCaret)); 224 pParagraph->SetupCellArray(mpFont); 225 pParagraph->SetCharacterOffset(nCharacterCount); 226 nCharacterCount += pParagraph->GetCharacterCount(); 227 maParagraphs.push_back(pParagraph); 228 229 if (mpCaret) 230 mpCaret->HideCaret(); 231 232 RequestFormat(); 233 } 234 235 236 237 238 void PresenterTextView::SetTextChangeBroadcaster ( 239 const ::boost::function<void(void)>& rBroadcaster) 240 { 241 maTextChangeBroadcaster = rBroadcaster; 242 } 243 244 245 246 247 void PresenterTextView::SetLocation (const css::geometry::RealPoint2D& rLocation) 248 { 249 maLocation = rLocation; 250 251 for (::std::vector<SharedPresenterTextParagraph>::iterator 252 iParagraph(maParagraphs.begin()), 253 iEnd(maParagraphs.end()); 254 iParagraph!=iEnd; 255 ++iParagraph) 256 { 257 (*iParagraph)->SetOrigin( 258 maLocation.X - mnLeftOffset, 259 maLocation.Y - mnTopOffset); 260 } 261 } 262 263 264 265 266 void PresenterTextView::SetSize (const css::geometry::RealSize2D& rSize) 267 { 268 maSize = rSize; 269 RequestFormat(); 270 } 271 272 273 274 275 double PresenterTextView::GetTotalTextHeight (void) 276 { 277 double nTotalHeight (0); 278 279 if (mbIsFormatPending) 280 { 281 if ( ! mpFont->PrepareFont(mxCanvas)) 282 return 0; 283 Format(); 284 } 285 286 for (::std::vector<SharedPresenterTextParagraph>::iterator 287 iParagraph(maParagraphs.begin()), 288 iEnd(maParagraphs.end()); 289 iParagraph!=iEnd; 290 ++iParagraph) 291 { 292 nTotalHeight += (*iParagraph)->GetTotalTextHeight(); 293 } 294 295 return nTotalHeight; 296 } 297 298 299 300 301 void PresenterTextView::SetFont (const PresenterTheme::SharedFontDescriptor& rpFont) 302 { 303 mpFont = rpFont; 304 RequestFormat(); 305 } 306 307 308 309 310 void PresenterTextView::SetOffset( 311 const double nLeft, 312 const double nTop) 313 { 314 mnLeftOffset = nLeft; 315 mnTopOffset = nTop; 316 317 // Trigger an update of the text origin stored at the individual paragraphs. 318 SetLocation(maLocation); 319 } 320 321 322 323 void PresenterTextView::MoveCaret ( 324 const sal_Int32 nDistance, 325 const sal_Int16 nTextType) 326 { 327 if ( ! mpCaret) 328 return; 329 330 // When the caret has not been visible yet then move it to the beginning 331 // of the text. 332 if (mpCaret->GetParagraphIndex() < 0) 333 { 334 mpCaret->SetPosition(0,0); 335 return; 336 } 337 338 sal_Int32 nParagraphIndex (mpCaret->GetParagraphIndex()); 339 sal_Int32 nCharacterIndex (mpCaret->GetCharacterIndex()); 340 switch (nTextType) 341 { 342 default: 343 case AccessibleTextType::CHARACTER: 344 nCharacterIndex += nDistance; 345 break; 346 347 case AccessibleTextType::WORD: 348 { 349 sal_Int32 nRemainingDistance (nDistance); 350 while (nRemainingDistance != 0) 351 { 352 SharedPresenterTextParagraph pParagraph (GetParagraph(nParagraphIndex)); 353 if (pParagraph) 354 { 355 const sal_Int32 nDelta (Signum(nDistance)); 356 nCharacterIndex = pParagraph->GetWordBoundary(nCharacterIndex, nDelta); 357 if (nCharacterIndex < 0) 358 { 359 // Go to previous or next paragraph. 360 nParagraphIndex += nDelta; 361 if (nParagraphIndex < 0) 362 { 363 nParagraphIndex = 0; 364 nCharacterIndex = 0; 365 nRemainingDistance = 0; 366 } 367 else if (sal_uInt32(nParagraphIndex) >= maParagraphs.size()) 368 { 369 nParagraphIndex = maParagraphs.size()-1; 370 pParagraph = GetParagraph(nParagraphIndex); 371 if (pParagraph) 372 nCharacterIndex = pParagraph->GetCharacterCount(); 373 nRemainingDistance = 0; 374 } 375 else 376 { 377 nRemainingDistance -= nDelta; 378 379 // Move caret one character to the end of 380 // the previous or the start of the next paragraph. 381 pParagraph = GetParagraph(nParagraphIndex); 382 if (pParagraph) 383 { 384 if (nDistance<0) 385 nCharacterIndex = pParagraph->GetCharacterCount(); 386 else 387 nCharacterIndex = 0; 388 } 389 } 390 } 391 else 392 nRemainingDistance -= nDelta; 393 } 394 else 395 break; 396 } 397 break; 398 } 399 } 400 401 // Move the caret to the new position. 402 mpCaret->SetPosition(nParagraphIndex, nCharacterIndex); 403 } 404 405 406 407 408 void PresenterTextView::Paint ( 409 const css::awt::Rectangle& rUpdateBox) 410 { 411 if ( ! mbDoOuput) 412 return; 413 if ( ! mxCanvas.is()) 414 return; 415 if ( ! mpFont->PrepareFont(mxCanvas)) 416 return; 417 418 if (mbIsFormatPending) 419 Format(); 420 421 // Setup the clipping rectangle. Horizontally we make it a little 422 // larger to allow characters (and the caret) to stick out of their 423 // bounding boxes. This can happen on some characters (like the 424 // uppercase J) for typographical reasons. 425 const sal_Int32 nAdditionalLeftBorder (10); 426 const sal_Int32 nAdditionalRightBorder (5); 427 double nX (maLocation.X - mnLeftOffset); 428 double nY (maLocation.Y - mnTopOffset); 429 const sal_Int32 nClipLeft (::std::max( 430 PresenterGeometryHelper::Round(maLocation.X)-nAdditionalLeftBorder, rUpdateBox.X)); 431 const sal_Int32 nClipTop (::std::max( 432 PresenterGeometryHelper::Round(maLocation.Y), rUpdateBox.Y)); 433 const sal_Int32 nClipRight (::std::min( 434 PresenterGeometryHelper::Round(maLocation.X+maSize.Width)+nAdditionalRightBorder, rUpdateBox.X+rUpdateBox.Width)); 435 const sal_Int32 nClipBottom (::std::min( 436 PresenterGeometryHelper::Round(maLocation.Y+maSize.Height), rUpdateBox.Y+rUpdateBox.Height)); 437 if (nClipLeft>=nClipRight || nClipTop>=nClipBottom) 438 return; 439 440 const awt::Rectangle aClipBox( 441 nClipLeft, 442 nClipTop, 443 nClipRight - nClipLeft, 444 nClipBottom - nClipTop); 445 Reference<rendering::XPolyPolygon2D> xClipPolygon ( 446 PresenterGeometryHelper::CreatePolygon(aClipBox, mxCanvas->getDevice())); 447 448 const rendering::ViewState aViewState( 449 geometry::AffineMatrix2D(1,0,0, 0,1,0), 450 xClipPolygon); 451 452 rendering::RenderState aRenderState ( 453 geometry::AffineMatrix2D(1,0,nX, 0,1,nY), 454 NULL, 455 Sequence<double>(4), 456 rendering::CompositeOperation::SOURCE); 457 PresenterCanvasHelper::SetDeviceColor(aRenderState, mpFont->mnColor); 458 459 for (::std::vector<SharedPresenterTextParagraph>::const_iterator 460 iParagraph(maParagraphs.begin()), 461 iEnd(maParagraphs.end()); 462 iParagraph!=iEnd; 463 ++iParagraph) 464 { 465 (*iParagraph)->Paint( 466 mxCanvas, 467 maSize, 468 mpFont, 469 aViewState, 470 aRenderState, 471 mnTopOffset, 472 nClipTop, 473 nClipBottom); 474 } 475 476 aRenderState.AffineTransform.m02 = 0; 477 aRenderState.AffineTransform.m12 = 0; 478 479 #ifdef SHOW_CHARACTER_BOXES 480 PresenterCanvasHelper::SetDeviceColor(aRenderState, 0x00808080); 481 for (sal_Int32 nParagraphIndex(0), nParagraphCount(GetParagraphCount()); 482 nParagraphIndex<nParagraphCount; 483 ++nParagraphIndex) 484 { 485 const SharedPresenterTextParagraph pParagraph (GetParagraph(nParagraphIndex)); 486 if ( ! pParagraph) 487 continue; 488 for (sal_Int32 nCharacterIndex(0),nCharacterCount(pParagraph->GetCharacterCount()); 489 nCharacterIndex<nCharacterCount; ++nCharacterIndex) 490 { 491 const awt::Rectangle aBox (pParagraph->GetCharacterBounds(nCharacterIndex, false)); 492 mxCanvas->drawPolyPolygon ( 493 PresenterGeometryHelper::CreatePolygon( 494 aBox, 495 mxCanvas->getDevice()), 496 aViewState, 497 aRenderState); 498 } 499 } 500 PresenterCanvasHelper::SetDeviceColor(aRenderState, mpFont->mnColor); 501 #endif 502 503 if (mpCaret && mpCaret->IsVisible()) 504 { 505 mxCanvas->fillPolyPolygon ( 506 PresenterGeometryHelper::CreatePolygon( 507 mpCaret->GetBounds(), 508 mxCanvas->getDevice()), 509 aViewState, 510 aRenderState); 511 } 512 } 513 514 515 516 517 SharedPresenterTextCaret PresenterTextView::GetCaret (void) const 518 { 519 return mpCaret; 520 } 521 522 523 524 525 sal_Int32 PresenterTextView::GetCharacterOffset (const sal_Int32 nParagraphIndex) const 526 { 527 sal_Int32 nCharacterOffset (0); 528 for (sal_Int32 nIndex=0; nIndex<nParagraphIndex; ++nIndex) 529 nCharacterOffset += maParagraphs[nIndex]->GetCharacterCount(); 530 return nCharacterOffset; 531 } 532 533 534 535 536 awt::Rectangle PresenterTextView::GetCaretBounds ( 537 sal_Int32 nParagraphIndex, 538 const sal_Int32 nCharacterIndex) const 539 { 540 SharedPresenterTextParagraph pParagraph (GetParagraph(nParagraphIndex)); 541 542 if (pParagraph) 543 return pParagraph->GetCharacterBounds(nCharacterIndex, true); 544 else 545 return awt::Rectangle(0,0,0,0); 546 } 547 548 549 550 551 //----- private --------------------------------------------------------------- 552 553 void PresenterTextView::RequestFormat (void) 554 { 555 mbIsFormatPending = true; 556 } 557 558 559 560 561 void PresenterTextView::Format (void) 562 { 563 mbIsFormatPending = false; 564 565 double nY (0); 566 for (::std::vector<SharedPresenterTextParagraph>::const_iterator 567 iParagraph(maParagraphs.begin()), 568 iEnd(maParagraphs.end()); 569 iParagraph!=iEnd; 570 ++iParagraph) 571 { 572 (*iParagraph)->Format(nY, maSize.Width, mpFont); 573 nY += (*iParagraph)->GetTotalTextHeight(); 574 } 575 576 if (maTextChangeBroadcaster) 577 maTextChangeBroadcaster(); 578 } 579 580 581 582 583 sal_Int32 PresenterTextView::GetParagraphCount (void) const 584 { 585 return maParagraphs.size(); 586 } 587 588 589 590 591 SharedPresenterTextParagraph PresenterTextView::GetParagraph ( 592 const sal_Int32 nParagraphIndex) const 593 { 594 if (nParagraphIndex < 0) 595 return SharedPresenterTextParagraph(); 596 else if (nParagraphIndex>=sal_Int32(maParagraphs.size())) 597 return SharedPresenterTextParagraph(); 598 else 599 return maParagraphs[nParagraphIndex]; 600 } 601 602 603 604 605 //===== PresenterTextParagraph ================================================ 606 607 PresenterTextParagraph::PresenterTextParagraph ( 608 const sal_Int32 nParagraphIndex, 609 const Reference<i18n::XBreakIterator>& rxBreakIterator, 610 const Reference<i18n::XScriptTypeDetector>& rxScriptTypeDetector, 611 const Reference<text::XTextRange>& rxTextRange, 612 const SharedPresenterTextCaret& rpCaret) 613 : msParagraphText(), 614 mnParagraphIndex(nParagraphIndex), 615 mpCaret(rpCaret), 616 mxBreakIterator(rxBreakIterator), 617 mxScriptTypeDetector(rxScriptTypeDetector), 618 maLines(), 619 mnVerticalOffset(0), 620 mnXOrigin(0), 621 mnYOrigin(0), 622 mnWidth(0), 623 mnAscent(0), 624 mnDescent(0), 625 mnLineHeight(-1), 626 meAdjust(style::ParagraphAdjust_LEFT), 627 mnWritingMode (text::WritingMode2::LR_TB), 628 mnCharacterOffset(0), 629 maCells() 630 { 631 if (rxTextRange.is()) 632 { 633 Reference<beans::XPropertySet> xProperties (rxTextRange, UNO_QUERY); 634 lang::Locale aLocale; 635 try 636 { 637 xProperties->getPropertyValue(A2S("CharLocale")) >>= aLocale; 638 } 639 catch(beans::UnknownPropertyException&) 640 { 641 // Ignore the exception. Use the default value. 642 } 643 try 644 { 645 xProperties->getPropertyValue(A2S("ParaAdjust")) >>= meAdjust; 646 } 647 catch(beans::UnknownPropertyException&) 648 { 649 // Ignore the exception. Use the default value. 650 } 651 try 652 { 653 xProperties->getPropertyValue(A2S("WritingMode")) >>= mnWritingMode; 654 } 655 catch(beans::UnknownPropertyException&) 656 { 657 // Ignore the exception. Use the default value. 658 } 659 660 msParagraphText = rxTextRange->getString(); 661 } 662 } 663 664 665 666 667 PresenterTextParagraph::PresenterTextParagraph ( 668 const sal_Int32 nParagraphIndex, 669 const Reference<i18n::XBreakIterator>& rxBreakIterator, 670 const Reference<i18n::XScriptTypeDetector>& rxScriptTypeDetector, 671 const ::rtl::OUString& rsText, 672 const SharedPresenterTextCaret& rpCaret) 673 : msParagraphText(rsText), 674 mnParagraphIndex(nParagraphIndex), 675 mpCaret(rpCaret), 676 mxBreakIterator(rxBreakIterator), 677 mxScriptTypeDetector(rxScriptTypeDetector), 678 maLines(), 679 mnVerticalOffset(0), 680 mnXOrigin(0), 681 mnYOrigin(0), 682 mnWidth(0), 683 mnAscent(0), 684 mnDescent(0), 685 mnLineHeight(-1), 686 meAdjust(style::ParagraphAdjust_LEFT), 687 mnWritingMode (text::WritingMode2::LR_TB), 688 mnCharacterOffset(0), 689 maCells() 690 { 691 } 692 693 694 695 696 void PresenterTextParagraph::Paint ( 697 const Reference<rendering::XCanvas>& rxCanvas, 698 const geometry::RealSize2D& rSize, 699 const PresenterTheme::SharedFontDescriptor& rpFont, 700 const rendering::ViewState& rViewState, 701 rendering::RenderState& rRenderState, 702 const double nTopOffset, 703 const double nClipTop, 704 const double nClipBottom) 705 { 706 if (mnLineHeight <= 0) 707 return; 708 709 sal_Int8 nTextDirection (GetTextDirection()); 710 711 const double nSavedM12 (rRenderState.AffineTransform.m12); 712 713 if ( ! IsTextReferencePointLeft()) 714 rRenderState.AffineTransform.m02 += rSize.Width; 715 716 717 #ifdef SHOW_CHARACTER_BOXES 718 for (sal_Int32 nIndex=0,nCount=maLines.size(); 719 nIndex<nCount; 720 ++nIndex) 721 { 722 Line& rLine (maLines[nIndex]); 723 rLine.ProvideLayoutedLine(msParagraphText, rpFont, nTextDirection); 724 } 725 #endif 726 727 for (sal_Int32 nIndex=0,nCount=maLines.size(); 728 nIndex<nCount; 729 ++nIndex, rRenderState.AffineTransform.m12 += mnLineHeight) 730 { 731 Line& rLine (maLines[nIndex]); 732 733 // Paint only visible lines. 734 const double nLineTop = rLine.mnBaseLine - mnAscent - nTopOffset; 735 if (nLineTop + mnLineHeight< nClipTop) 736 continue; 737 else if (nLineTop > nClipBottom) 738 break; 739 rLine.ProvideLayoutedLine(msParagraphText, rpFont, nTextDirection); 740 741 rRenderState.AffineTransform.m12 = nSavedM12 + rLine.mnBaseLine; 742 743 rxCanvas->drawTextLayout ( 744 rLine.mxLayoutedLine, 745 rViewState, 746 rRenderState); 747 } 748 rRenderState.AffineTransform.m12 = nSavedM12; 749 750 if ( ! IsTextReferencePointLeft()) 751 rRenderState.AffineTransform.m02 -= rSize.Width; 752 } 753 754 755 756 757 void PresenterTextParagraph::Format ( 758 const double nY, 759 const double nWidth, 760 const PresenterTheme::SharedFontDescriptor& rpFont) 761 { 762 // Make sure that the text view is in a valid and sane state. 763 if ( ! mxBreakIterator.is() || ! mxScriptTypeDetector.is()) 764 return; 765 if (nWidth<=0) 766 return; 767 if ( ! rpFont || ! rpFont->mxFont.is()) 768 return; 769 770 sal_Int32 nPosition (0); 771 772 mnWidth = nWidth; 773 maLines.clear(); 774 mnLineHeight = 0; 775 mnAscent = 0; 776 mnDescent = 0; 777 mnVerticalOffset = nY; 778 maWordBoundaries.clear(); 779 maWordBoundaries.push_back(0); 780 781 const rendering::FontMetrics aMetrics (rpFont->mxFont->getFontMetrics()); 782 mnAscent = aMetrics.Ascent; 783 mnDescent = aMetrics.Descent; 784 mnLineHeight = aMetrics.Ascent + aMetrics.Descent + aMetrics.ExternalLeading; 785 nPosition = 0; 786 i18n::Boundary aCurrentLine(0,0); 787 while (true) 788 { 789 const i18n::Boundary aWordBoundary = mxBreakIterator->nextWord( 790 msParagraphText, 791 nPosition, 792 lang::Locale(), 793 i18n::WordType::ANYWORD_IGNOREWHITESPACES); 794 AddWord(nWidth, aCurrentLine, aWordBoundary.startPos, rpFont); 795 796 // Remember the new word boundary for caret travelling by words. 797 // Prevent duplicates. 798 if (aWordBoundary.startPos > maWordBoundaries.back()) 799 maWordBoundaries.push_back(aWordBoundary.startPos); 800 801 if (aWordBoundary.endPos>aWordBoundary.startPos) 802 AddWord(nWidth, aCurrentLine, aWordBoundary.endPos, rpFont); 803 804 if (aWordBoundary.startPos<0 || aWordBoundary.endPos<0) 805 break; 806 if (nPosition >= aWordBoundary.endPos) 807 break; 808 nPosition = aWordBoundary.endPos; 809 } 810 811 if (aCurrentLine.endPos>aCurrentLine.startPos) 812 AddLine(aCurrentLine); 813 814 } 815 816 817 818 819 sal_Int32 PresenterTextParagraph::GetWordBoundary( 820 const sal_Int32 nLocalCharacterIndex, 821 const sal_Int32 nDistance) 822 { 823 OSL_ASSERT(nDistance==-1 || nDistance==+1); 824 825 if (nLocalCharacterIndex < 0) 826 { 827 // The caller asked for the start or end position of the paragraph. 828 if (nDistance < 0) 829 return 0; 830 else 831 return GetCharacterCount(); 832 } 833 834 sal_Int32 nIndex (0); 835 for (sal_Int32 nCount (maWordBoundaries.size()); nIndex<nCount; ++nIndex) 836 { 837 if (maWordBoundaries[nIndex] >= nLocalCharacterIndex) 838 { 839 // When inside the word (not at its start or end) then 840 // first move to the start or end before going the previous or 841 // next word. 842 if (maWordBoundaries[nIndex] > nLocalCharacterIndex) 843 if (nDistance > 0) 844 --nIndex; 845 break; 846 } 847 } 848 849 nIndex += nDistance; 850 851 if (nIndex < 0) 852 return -1; 853 else if (sal_uInt32(nIndex)>=maWordBoundaries.size()) 854 return -1; 855 else 856 return maWordBoundaries[nIndex]; 857 } 858 859 860 861 862 sal_Int32 PresenterTextParagraph::GetCaretPosition (void) const 863 { 864 if (mpCaret && mpCaret->GetParagraphIndex()==mnParagraphIndex) 865 return mpCaret->GetCharacterIndex(); 866 else 867 return -1; 868 } 869 870 871 872 873 void PresenterTextParagraph::SetCaretPosition (const sal_Int32 nPosition) const 874 { 875 if (mpCaret && mpCaret->GetParagraphIndex()==mnParagraphIndex) 876 return mpCaret->SetPosition(mnParagraphIndex, nPosition); 877 } 878 879 880 881 882 void PresenterTextParagraph::SetOrigin (const double nXOrigin, const double nYOrigin) 883 { 884 mnXOrigin = nXOrigin; 885 mnYOrigin = nYOrigin; 886 } 887 888 889 890 891 awt::Point PresenterTextParagraph::GetRelativeLocation (void) const 892 { 893 return awt::Point( 894 sal_Int32(mnXOrigin), 895 sal_Int32(mnYOrigin + mnVerticalOffset)); 896 } 897 898 899 900 901 awt::Size PresenterTextParagraph::GetSize (void) 902 { 903 return awt::Size( 904 sal_Int32(mnWidth), 905 sal_Int32(GetTotalTextHeight())); 906 } 907 908 909 910 911 void PresenterTextParagraph::AddWord ( 912 const double nWidth, 913 i18n::Boundary& rCurrentLine, 914 const sal_Int32 nWordBoundary, 915 const PresenterTheme::SharedFontDescriptor& rpFont) 916 { 917 sal_Int32 nLineStart (0); 918 sal_Int32 nLineEnd (0); 919 if ( ! maLines.empty()) 920 { 921 nLineStart = rCurrentLine.startPos; 922 nLineEnd = rCurrentLine.endPos; 923 } 924 925 const ::rtl::OUString sLineCandidate ( 926 msParagraphText.copy(nLineStart, nWordBoundary-nLineStart)); 927 928 css::geometry::RealRectangle2D aLineBox ( 929 PresenterCanvasHelper::GetTextBoundingBox ( 930 rpFont->mxFont, 931 sLineCandidate, 932 mnWritingMode)); 933 const double nLineWidth (aLineBox.X2 - aLineBox.X1); 934 935 if (nLineWidth >= nWidth) 936 { 937 // Add new line with a single word (so far). 938 AddLine(rCurrentLine); 939 } 940 rCurrentLine.endPos = nWordBoundary; 941 } 942 943 944 945 946 void PresenterTextParagraph::AddLine ( 947 i18n::Boundary& rCurrentLine) 948 { 949 Line aLine (rCurrentLine.startPos, rCurrentLine.endPos); 950 951 // Find the start and end of the line with respect to cells. 952 if (maLines.size() > 0) 953 { 954 aLine.mnLineStartCellIndex = maLines.back().mnLineEndCellIndex; 955 aLine.mnBaseLine = maLines.back().mnBaseLine + mnLineHeight; 956 } 957 else 958 { 959 aLine.mnLineStartCellIndex = 0; 960 aLine.mnBaseLine = mnVerticalOffset + mnAscent; 961 } 962 sal_Int32 nCellIndex (aLine.mnLineStartCellIndex); 963 double nWidth (0); 964 for ( ; nCellIndex<sal_Int32(maCells.size()); ++nCellIndex) 965 { 966 const Cell& rCell (maCells[nCellIndex]); 967 if (rCell.mnCharacterIndex+rCell.mnCharacterCount > aLine.mnLineEndCharacterIndex) 968 break; 969 nWidth += rCell.mnCellWidth; 970 } 971 aLine.mnLineEndCellIndex = nCellIndex; 972 aLine.mnWidth = nWidth; 973 974 maLines.push_back(aLine); 975 976 rCurrentLine.startPos = rCurrentLine.endPos; 977 } 978 979 980 981 982 sal_Int32 PresenterTextParagraph::GetParagraphIndex (void) const 983 { 984 return mnParagraphIndex; 985 } 986 987 988 989 990 double PresenterTextParagraph::GetTotalTextHeight (void) 991 { 992 return maLines.size() * mnLineHeight; 993 } 994 995 996 997 998 sal_Int32 PresenterTextParagraph::GetCharacterOffset (void) const 999 { 1000 return mnCharacterOffset; 1001 } 1002 1003 1004 1005 1006 void PresenterTextParagraph::SetCharacterOffset (const sal_Int32 nCharacterOffset) 1007 { 1008 mnCharacterOffset = nCharacterOffset; 1009 } 1010 1011 1012 1013 1014 sal_Int32 PresenterTextParagraph::GetCharacterCount (void) const 1015 { 1016 return msParagraphText.getLength(); 1017 } 1018 1019 1020 1021 1022 sal_Unicode PresenterTextParagraph::GetCharacter ( 1023 const sal_Int32 nGlobalCharacterIndex) const 1024 { 1025 if (nGlobalCharacterIndex<mnCharacterOffset 1026 || nGlobalCharacterIndex>=mnCharacterOffset+msParagraphText.getLength()) 1027 { 1028 return sal_Unicode(); 1029 } 1030 else 1031 { 1032 return msParagraphText.getStr()[nGlobalCharacterIndex - mnCharacterOffset]; 1033 } 1034 } 1035 1036 1037 1038 1039 ::rtl::OUString PresenterTextParagraph::GetText (void) const 1040 { 1041 return msParagraphText; 1042 } 1043 1044 1045 1046 1047 TextSegment PresenterTextParagraph::GetTextSegment ( 1048 const sal_Int32 nOffset, 1049 const sal_Int32 nIndex, 1050 const sal_Int16 nTextType) const 1051 { 1052 switch(nTextType) 1053 { 1054 case AccessibleTextType::PARAGRAPH: 1055 return TextSegment( 1056 msParagraphText, 1057 mnCharacterOffset, 1058 mnCharacterOffset+msParagraphText.getLength()); 1059 1060 case AccessibleTextType::SENTENCE: 1061 if (mxBreakIterator.is()) 1062 { 1063 const sal_Int32 nStart (mxBreakIterator->beginOfSentence( 1064 msParagraphText, nIndex-mnCharacterOffset, lang::Locale())); 1065 const sal_Int32 nEnd (mxBreakIterator->endOfSentence( 1066 msParagraphText, nIndex-mnCharacterOffset, lang::Locale())); 1067 if (nStart < nEnd) 1068 return TextSegment( 1069 msParagraphText.copy(nStart, nEnd-nStart), 1070 nStart+mnCharacterOffset, 1071 nEnd+mnCharacterOffset); 1072 } 1073 break; 1074 1075 case AccessibleTextType::WORD: 1076 if (mxBreakIterator.is()) 1077 return GetWordTextSegment(nOffset, nIndex); 1078 break; 1079 1080 case AccessibleTextType::LINE: 1081 { 1082 for (::std::vector<Line>::const_iterator 1083 iLine(maLines.begin()), 1084 iEnd(maLines.end()); 1085 iLine!=iEnd; 1086 ++iLine) 1087 { 1088 if (nIndex < iLine->mnLineEndCharacterIndex) 1089 { 1090 return TextSegment( 1091 msParagraphText.copy( 1092 iLine->mnLineStartCharacterIndex, 1093 iLine->mnLineEndCharacterIndex - iLine->mnLineStartCharacterIndex), 1094 iLine->mnLineStartCharacterIndex, 1095 iLine->mnLineEndCharacterIndex); 1096 } 1097 } 1098 } 1099 break; 1100 1101 // Handle GLYPH and ATTRIBUTE_RUN like CHARACTER because we can not 1102 // do better at the moment. 1103 case AccessibleTextType::CHARACTER: 1104 case AccessibleTextType::GLYPH: 1105 case AccessibleTextType::ATTRIBUTE_RUN: 1106 return CreateTextSegment(nIndex+nOffset, nIndex+nOffset+1); 1107 } 1108 1109 return TextSegment(::rtl::OUString(), 0,0); 1110 } 1111 1112 1113 1114 1115 TextSegment PresenterTextParagraph::GetWordTextSegment ( 1116 const sal_Int32 nOffset, 1117 const sal_Int32 nIndex) const 1118 { 1119 sal_Int32 nCurrentOffset (nOffset); 1120 sal_Int32 nCurrentIndex (nIndex); 1121 1122 i18n::Boundary aWordBoundary; 1123 if (nCurrentOffset == 0) 1124 aWordBoundary = mxBreakIterator->getWordBoundary( 1125 msParagraphText, 1126 nIndex, 1127 lang::Locale(), 1128 i18n::WordType::ANYWORD_IGNOREWHITESPACES, 1129 sal_True); 1130 else if (nCurrentOffset < 0) 1131 { 1132 while (nCurrentOffset<0 && nCurrentIndex>0) 1133 { 1134 aWordBoundary = mxBreakIterator->previousWord( 1135 msParagraphText, 1136 nCurrentIndex, 1137 lang::Locale(), 1138 i18n::WordType::ANYWORD_IGNOREWHITESPACES); 1139 nCurrentIndex = aWordBoundary.startPos; 1140 ++nCurrentOffset; 1141 } 1142 } 1143 else 1144 { 1145 while (nCurrentOffset>0 && nCurrentIndex<=GetCharacterCount()) 1146 { 1147 aWordBoundary = mxBreakIterator->nextWord( 1148 msParagraphText, 1149 nCurrentIndex, 1150 lang::Locale(), 1151 i18n::WordType::ANYWORD_IGNOREWHITESPACES); 1152 nCurrentIndex = aWordBoundary.endPos; 1153 --nCurrentOffset; 1154 } 1155 } 1156 1157 return CreateTextSegment(aWordBoundary.startPos, aWordBoundary.endPos); 1158 } 1159 1160 1161 1162 1163 TextSegment PresenterTextParagraph::CreateTextSegment ( 1164 sal_Int32 nStartIndex, 1165 sal_Int32 nEndIndex) const 1166 { 1167 if (nEndIndex <= nStartIndex) 1168 return TextSegment( 1169 ::rtl::OUString(), 1170 nStartIndex, 1171 nEndIndex); 1172 else 1173 return TextSegment( 1174 msParagraphText.copy(nStartIndex, nEndIndex-nStartIndex), 1175 nStartIndex, 1176 nEndIndex); 1177 } 1178 1179 1180 1181 1182 awt::Rectangle PresenterTextParagraph::GetCharacterBounds ( 1183 sal_Int32 nGlobalCharacterIndex, 1184 const bool bCaretBox) 1185 { 1186 // Find the line that contains the requested character and accumulate 1187 // the previous line heights. 1188 sal_Int32 nFirstCharacterIndex (0); 1189 sal_Int32 nEndCharacterIndex (0); 1190 double nX (mnXOrigin); 1191 double nY (mnYOrigin + mnVerticalOffset + mnAscent); 1192 const sal_Int8 nTextDirection (GetTextDirection()); 1193 for (sal_Int32 nLineIndex=0,nLineCount=maLines.size(); 1194 nLineIndex<nLineCount; 1195 ++nLineIndex, nFirstCharacterIndex=nEndCharacterIndex, nY+=mnLineHeight) 1196 { 1197 Line& rLine (maLines[nLineIndex]); 1198 // Skip lines before the indexed character. 1199 if (nGlobalCharacterIndex >= rLine.mnLineEndCharacterIndex) 1200 // When in the last line then allow the index past the last char. 1201 if (nLineIndex<nLineCount-1) 1202 continue; 1203 1204 rLine.ProvideCellBoxes(); 1205 1206 const sal_Int32 nCellIndex (nGlobalCharacterIndex - rLine.mnLineStartCharacterIndex); 1207 1208 // The cell bounding box is defined relative to the origin of 1209 // the current line. Therefore we have to add the absolute 1210 // position of the line. 1211 geometry::RealRectangle2D rCellBox (rLine.maCellBoxes[ 1212 ::std::min(nCellIndex, rLine.maCellBoxes.getLength()-1)]); 1213 1214 double nLeft = nX + rCellBox.X1; 1215 double nRight = nX + rCellBox.X2; 1216 if (nTextDirection == rendering::TextDirection::WEAK_RIGHT_TO_LEFT) 1217 { 1218 const double nOldRight (nRight); 1219 nRight = rLine.mnWidth - nLeft; 1220 nLeft = rLine.mnWidth - nOldRight; 1221 } 1222 double nTop (nY + rCellBox.Y1); 1223 double nBottom (nY + rCellBox.Y2); 1224 if (bCaretBox) 1225 { 1226 nTop = nTop - rCellBox.Y1 - mnAscent; 1227 nBottom = nTop + mnLineHeight; 1228 if (nCellIndex >= rLine.maCellBoxes.getLength()) 1229 nLeft = nRight-2; 1230 if (nLeft < nX) 1231 nLeft = nX; 1232 nRight = nLeft+2; 1233 } 1234 else 1235 { 1236 nTop = nTop - rCellBox.Y1 - mnAscent; 1237 nBottom = nTop + mnAscent + mnDescent; 1238 } 1239 const sal_Int32 nX1 = sal_Int32(floor(nLeft)); 1240 const sal_Int32 nY1 = sal_Int32(floor(nTop)); 1241 const sal_Int32 nX2 = sal_Int32(ceil(nRight)); 1242 const sal_Int32 nY2 = sal_Int32(ceil(nBottom)); 1243 1244 return awt::Rectangle(nX1,nY1,nX2-nX1+1,nY2-nY1+1); 1245 } 1246 1247 // We are still here. That means that the given index lies past the 1248 // last character in the paragraph. 1249 // Return an empty box that lies past the last character. Better than nothing. 1250 return awt::Rectangle(sal_Int32(nX+0.5), sal_Int32(nY+0.5), 0, 0); 1251 } 1252 1253 1254 1255 1256 sal_Int32 PresenterTextParagraph::GetIndexAtPoint (const awt::Point& rPoint) const 1257 { 1258 (void)rPoint; 1259 return -1; 1260 } 1261 1262 1263 1264 1265 sal_Int8 PresenterTextParagraph::GetTextDirection (void) const 1266 { 1267 // Find first portion that has a non-neutral text direction. 1268 sal_Int32 nPosition (0); 1269 sal_Int32 nTextLength (msParagraphText.getLength()); 1270 while (nPosition < nTextLength) 1271 { 1272 const sal_Int16 nScriptDirection ( 1273 mxScriptTypeDetector->getScriptDirection( 1274 msParagraphText, nPosition, i18n::ScriptDirection::NEUTRAL)); 1275 switch (nScriptDirection) 1276 { 1277 case i18n::ScriptDirection::NEUTRAL: 1278 // continue looping. 1279 break; 1280 case i18n::ScriptDirection::LEFT_TO_RIGHT: 1281 return rendering::TextDirection::WEAK_LEFT_TO_RIGHT; 1282 1283 case i18n::ScriptDirection::RIGHT_TO_LEFT: 1284 return rendering::TextDirection::WEAK_RIGHT_TO_LEFT; 1285 } 1286 1287 nPosition = mxScriptTypeDetector->endOfScriptDirection( 1288 msParagraphText, nPosition, nScriptDirection); 1289 } 1290 1291 // All text in paragraph is neutral. Fall back on writing mode taken 1292 // from the XText (which may not be properly initialized.) 1293 sal_Int8 nTextDirection(rendering::TextDirection::WEAK_LEFT_TO_RIGHT); 1294 switch(mnWritingMode) 1295 { 1296 case text::WritingMode2::LR_TB: 1297 nTextDirection = rendering::TextDirection::WEAK_LEFT_TO_RIGHT; 1298 break; 1299 1300 case text::WritingMode2::RL_TB: 1301 nTextDirection = rendering::TextDirection::WEAK_RIGHT_TO_LEFT; 1302 break; 1303 1304 default: 1305 case text::WritingMode2::TB_RL: 1306 case text::WritingMode2::TB_LR: 1307 // Can not handle this. Use default and hope for the best. 1308 break; 1309 } 1310 return nTextDirection; 1311 } 1312 1313 1314 1315 1316 bool PresenterTextParagraph::IsTextReferencePointLeft (void) const 1317 { 1318 return mnWritingMode != text::WritingMode2::RL_TB; 1319 } 1320 1321 1322 1323 1324 void PresenterTextParagraph::SetupCellArray ( 1325 const PresenterTheme::SharedFontDescriptor& rpFont) 1326 { 1327 maCells.clear(); 1328 1329 if ( ! rpFont || ! rpFont->mxFont.is()) 1330 return; 1331 1332 sal_Int32 nPosition (0); 1333 sal_Int32 nIndex (0); 1334 const sal_Int32 nTextLength (msParagraphText.getLength()); 1335 const sal_Int8 nTextDirection (GetTextDirection()); 1336 while (nPosition < nTextLength) 1337 { 1338 const sal_Int32 nNewPosition (mxBreakIterator->nextCharacters( 1339 msParagraphText, 1340 nPosition, 1341 lang::Locale(), 1342 i18n::CharacterIteratorMode::SKIPCELL, 1343 1, 1344 nIndex)); 1345 1346 rendering::StringContext aContext (msParagraphText, nPosition, nNewPosition-nPosition); 1347 Reference<rendering::XTextLayout> xLayout ( 1348 rpFont->mxFont->createTextLayout(aContext, nTextDirection, 0)); 1349 css::geometry::RealRectangle2D aCharacterBox (xLayout->queryTextBounds()); 1350 1351 maCells.push_back(Cell( 1352 nPosition, 1353 nNewPosition-nPosition, 1354 aCharacterBox.X2-aCharacterBox.X1)); 1355 1356 nPosition = nNewPosition; 1357 } 1358 } 1359 1360 1361 1362 1363 //===== PresenterTextCaret ================================================---- 1364 1365 PresenterTextCaret::PresenterTextCaret ( 1366 const ::boost::function<css::awt::Rectangle(const sal_Int32,const sal_Int32)>& rCharacterBoundsAccess, 1367 const ::boost::function<void(const css::awt::Rectangle&)>& rInvalidator) 1368 : mnParagraphIndex(-1), 1369 mnCharacterIndex(-1), 1370 mnCaretBlinkTaskId(0), 1371 mbIsCaretVisible(false), 1372 maCharacterBoundsAccess(rCharacterBoundsAccess), 1373 maInvalidator(rInvalidator), 1374 maBroadcaster(), 1375 maCaretBounds() 1376 { 1377 } 1378 1379 1380 1381 1382 PresenterTextCaret::~PresenterTextCaret (void) 1383 { 1384 HideCaret(); 1385 } 1386 1387 1388 1389 1390 void PresenterTextCaret::ShowCaret (void) 1391 { 1392 if (mnCaretBlinkTaskId == 0) 1393 { 1394 mnCaretBlinkTaskId = PresenterTimer::ScheduleRepeatedTask ( 1395 ::boost::bind(&PresenterTextCaret::InvertCaret, this), 1396 CaretBlinkIntervall, 1397 CaretBlinkIntervall); 1398 } 1399 mbIsCaretVisible = true; 1400 } 1401 1402 1403 1404 1405 void PresenterTextCaret::HideCaret (void) 1406 { 1407 if (mnCaretBlinkTaskId != 0) 1408 { 1409 PresenterTimer::CancelTask(mnCaretBlinkTaskId); 1410 mnCaretBlinkTaskId = 0; 1411 } 1412 mbIsCaretVisible = false; 1413 // Reset the caret position. 1414 mnParagraphIndex = -1; 1415 mnCharacterIndex = -1; 1416 } 1417 1418 1419 1420 1421 sal_Int32 PresenterTextCaret::GetParagraphIndex (void) const 1422 { 1423 return mnParagraphIndex; 1424 } 1425 1426 1427 1428 1429 sal_Int32 PresenterTextCaret::GetCharacterIndex (void) const 1430 { 1431 return mnCharacterIndex; 1432 } 1433 1434 1435 1436 1437 void PresenterTextCaret::SetPosition ( 1438 const sal_Int32 nParagraphIndex, 1439 const sal_Int32 nCharacterIndex) 1440 { 1441 if (mnParagraphIndex != nParagraphIndex 1442 || mnCharacterIndex != nCharacterIndex) 1443 { 1444 if (mnParagraphIndex >= 0) 1445 maInvalidator(maCaretBounds); 1446 1447 const sal_Int32 nOldParagraphIndex (mnParagraphIndex); 1448 const sal_Int32 nOldCharacterIndex (mnCharacterIndex); 1449 mnParagraphIndex = nParagraphIndex; 1450 mnCharacterIndex = nCharacterIndex; 1451 maCaretBounds = maCharacterBoundsAccess(mnParagraphIndex, mnCharacterIndex); 1452 if (mnParagraphIndex >= 0) 1453 ShowCaret(); 1454 else 1455 HideCaret(); 1456 1457 if (mnParagraphIndex >= 0) 1458 maInvalidator(maCaretBounds); 1459 1460 if (maBroadcaster) 1461 maBroadcaster( 1462 nOldParagraphIndex, 1463 nOldCharacterIndex, 1464 mnParagraphIndex, 1465 mnCharacterIndex); 1466 1467 } 1468 } 1469 1470 1471 1472 1473 bool PresenterTextCaret::IsVisible (void) const 1474 { 1475 return mbIsCaretVisible; 1476 } 1477 1478 1479 1480 1481 void PresenterTextCaret::SetCaretMotionBroadcaster ( 1482 const ::boost::function<void(sal_Int32,sal_Int32,sal_Int32,sal_Int32)>& rBroadcaster) 1483 { 1484 maBroadcaster = rBroadcaster; 1485 } 1486 1487 1488 1489 1490 css::awt::Rectangle PresenterTextCaret::GetBounds (void) const 1491 { 1492 return maCaretBounds; 1493 } 1494 1495 1496 1497 1498 void PresenterTextCaret::InvertCaret (void) 1499 { 1500 mbIsCaretVisible = !mbIsCaretVisible; 1501 if (mnParagraphIndex >= 0) 1502 maInvalidator(maCaretBounds); 1503 } 1504 1505 1506 1507 1508 1509 1510 1511 //===== PresenterTextParagraph::Cell ========================================== 1512 1513 PresenterTextParagraph::Cell::Cell ( 1514 const sal_Int32 nCharacterIndex, 1515 const sal_Int32 nCharacterCount, 1516 const double nCellWidth) 1517 : mnCharacterIndex(nCharacterIndex), 1518 mnCharacterCount(nCharacterCount), 1519 mnCellWidth(nCellWidth) 1520 { 1521 } 1522 1523 1524 1525 1526 //===== PresenterTextParagraph::Line ========================================== 1527 1528 PresenterTextParagraph::Line::Line ( 1529 const sal_Int32 nLineStartCharacterIndex, 1530 const sal_Int32 nLineEndCharacterIndex) 1531 : mnLineStartCharacterIndex(nLineStartCharacterIndex), 1532 mnLineEndCharacterIndex(nLineEndCharacterIndex), 1533 mnLineStartCellIndex(-1), mnLineEndCellIndex(-1), 1534 mxLayoutedLine(), 1535 mnBaseLine(0), mnWidth(0), 1536 maCellBoxes() 1537 { 1538 } 1539 1540 1541 1542 1543 sal_Int32 PresenterTextParagraph::Line::GetLength (void) const 1544 { 1545 return mnLineEndCharacterIndex-mnLineStartCharacterIndex; 1546 } 1547 1548 1549 1550 1551 void PresenterTextParagraph::Line::ProvideCellBoxes (void) 1552 { 1553 if ( ! IsEmpty() && maCellBoxes.getLength()==0) 1554 { 1555 if (mxLayoutedLine.is()) 1556 maCellBoxes = mxLayoutedLine->queryInkMeasures(); 1557 else 1558 { 1559 OSL_ASSERT(mxLayoutedLine.is()); 1560 } 1561 } 1562 } 1563 1564 1565 1566 1567 void PresenterTextParagraph::Line::ProvideLayoutedLine ( 1568 const ::rtl::OUString& rsParagraphText, 1569 const PresenterTheme::SharedFontDescriptor& rpFont, 1570 const sal_Int8 nTextDirection) 1571 { 1572 if ( ! mxLayoutedLine.is()) 1573 { 1574 const rendering::StringContext aContext ( 1575 rsParagraphText, 1576 mnLineStartCharacterIndex, 1577 mnLineEndCharacterIndex - mnLineStartCharacterIndex); 1578 1579 mxLayoutedLine = rpFont->mxFont->createTextLayout( 1580 aContext, 1581 nTextDirection, 1582 0); 1583 } 1584 } 1585 1586 1587 1588 1589 bool PresenterTextParagraph::Line::IsEmpty (void) const 1590 { 1591 return mnLineStartCharacterIndex >= mnLineEndCharacterIndex; 1592 } 1593 1594 1595 1596 1597 } } // end of namespace ::sdext::presenter 1598