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