Update ooo320-m1
[ooovba.git] / sdext / source / presenter / PresenterTextView.cxx
blobb2d96f6eba46b8667d2a70dac22bf33b7e9780c6
1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: PresenterNotesView.hxx,v $
11 * $Revision: 1.5 $
13 * This file is part of OpenOffice.org.
15 * OpenOffice.org is free software: you can redistribute it and/or modify
16 * it under the terms of the GNU Lesser General Public License version 3
17 * only, as published by the Free Software Foundation.
19 * OpenOffice.org is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU Lesser General Public License version 3 for more details
23 * (a copy is included in the LICENSE file that accompanied this code).
25 * You should have received a copy of the GNU Lesser General Public License
26 * version 3 along with OpenOffice.org. If not, see
27 * <http://www.openoffice.org/license.html>
28 * for a copy of the LGPLv3 License.
30 ************************************************************************/
32 #include "precompiled_sdext.hxx"
34 #include "PresenterTextView.hxx"
35 #include "PresenterCanvasHelper.hxx"
36 #include "PresenterGeometryHelper.hxx"
37 #include "PresenterTimer.hxx"
39 #include <cmath>
41 #include <com/sun/star/accessibility/AccessibleTextType.hpp>
42 #include <com/sun/star/container/XEnumerationAccess.hpp>
43 #include <com/sun/star/i18n/CharType.hpp>
44 #include <com/sun/star/i18n/CharacterIteratorMode.hpp>
45 #include <com/sun/star/i18n/CTLScriptType.hpp>
46 #include <com/sun/star/i18n/ScriptDirection.hpp>
47 #include <com/sun/star/i18n/WordType.hpp>
48 #include <com/sun/star/rendering/CompositeOperation.hpp>
49 #include <com/sun/star/rendering/TextDirection.hpp>
50 #include <com/sun/star/text/WritingMode2.hpp>
51 #include <boost/bind.hpp>
53 using namespace ::com::sun::star;
54 using namespace ::com::sun::star::accessibility;
55 using namespace ::com::sun::star::uno;
57 #define A2S(pString) (::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(pString)))
59 const static sal_Int64 CaretBlinkIntervall = 500 * 1000 * 1000;
61 //#define SHOW_CHARACTER_BOXES
63 namespace {
64 sal_Int32 Signum (const sal_Int32 nValue)
66 if (nValue < 0)
67 return -1;
68 else if (nValue > 0)
69 return +1;
70 else
71 return 0;
75 namespace sdext { namespace presenter {
78 //===== PresenterTextView =====================================================
80 PresenterTextView::PresenterTextView (
81 const Reference<XComponentContext>& rxContext,
82 const Reference<rendering::XCanvas>& rxCanvas,
83 const ::boost::function<void(const ::css::awt::Rectangle&)>& rInvalidator)
84 : mxCanvas(rxCanvas),
85 mbDoOuput(true),
86 mxBreakIterator(),
87 mxScriptTypeDetector(),
88 maLocation(0,0),
89 maSize(0,0),
90 mpFont(),
91 maParagraphs(),
92 mpCaret(new PresenterTextCaret(
93 ::boost::bind(&PresenterTextView::GetCaretBounds, this, _1, _2),
94 rInvalidator)),
95 mnLeftOffset(0),
96 mnTopOffset(0),
97 maInvalidator(rInvalidator),
98 mbIsFormatPending(false),
99 mnCharacterCount(-1),
100 maTextChangeBroadcaster()
102 Reference<lang::XMultiComponentFactory> xFactory (
103 rxContext->getServiceManager(), UNO_QUERY);
104 if ( ! xFactory.is())
105 return;
107 // Create the break iterator that we use to break text into lines.
108 mxBreakIterator = Reference<i18n::XBreakIterator>(
109 xFactory->createInstanceWithContext(
110 A2S("com.sun.star.i18n.BreakIterator"),
111 rxContext),
112 UNO_QUERY_THROW);
114 // Create the script type detector that is used to split paragraphs into
115 // portions of the same text direction.
116 mxScriptTypeDetector = Reference<i18n::XScriptTypeDetector>(
117 xFactory->createInstanceWithContext(
118 A2S("com.sun.star.i18n.ScriptTypeDetector"),
119 rxContext),
120 UNO_QUERY_THROW);
126 PresenterTextView::PresenterTextView (
127 const Reference<XComponentContext>& rxContext,
128 const Reference<rendering::XCanvas>& rxCanvas)
129 : mxCanvas(rxCanvas),
130 mbDoOuput(false),
131 mxBreakIterator(),
132 mxScriptTypeDetector(),
133 maLocation(0,0),
134 maSize(0,0),
135 mpFont(),
136 maParagraphs(),
137 mpCaret(new PresenterTextCaret(
138 ::boost::bind(&PresenterTextView::GetCaretBounds, this, _1, _2),
139 ::boost::function<void(const css::awt::Rectangle&)>())),
140 mnLeftOffset(0),
141 mnTopOffset(0),
142 maInvalidator(),
143 mbIsFormatPending(false),
144 mnCharacterCount(-1),
145 maTextChangeBroadcaster()
147 Reference<lang::XMultiComponentFactory> xFactory (
148 rxContext->getServiceManager(), UNO_QUERY);
149 if ( ! xFactory.is())
150 return;
152 // Create the break iterator that we use to break text into lines.
153 mxBreakIterator = Reference<i18n::XBreakIterator>(
154 xFactory->createInstanceWithContext(
155 A2S("com.sun.star.i18n.BreakIterator"),
156 rxContext),
157 UNO_QUERY_THROW);
159 // Create the script type detector that is used to split paragraphs into
160 // portions of the same text direction.
161 mxScriptTypeDetector = Reference<i18n::XScriptTypeDetector>(
162 xFactory->createInstanceWithContext(
163 A2S("com.sun.star.i18n.ScriptTypeDetector"),
164 rxContext),
165 UNO_QUERY_THROW);
171 void PresenterTextView::SetText (const Reference<text::XText>& rxText)
173 maParagraphs.clear();
174 mnCharacterCount = -1;
176 Reference<container::XEnumerationAccess> xParagraphAccess (rxText, UNO_QUERY);
177 if ( ! xParagraphAccess.is())
178 return;
180 Reference<container::XEnumeration> xParagraphs (
181 xParagraphAccess->createEnumeration() , UNO_QUERY);
182 if ( ! xParagraphs.is())
183 return;
185 if ( ! mpFont || ! mpFont->PrepareFont(mxCanvas))
186 return;
188 sal_Int32 nCharacterCount (0);
189 while (xParagraphs->hasMoreElements())
191 SharedPresenterTextParagraph pParagraph (new PresenterTextParagraph(
192 maParagraphs.size(),
193 mxBreakIterator,
194 mxScriptTypeDetector,
195 Reference<text::XTextRange>(xParagraphs->nextElement(), UNO_QUERY),
196 mpCaret));
197 pParagraph->SetupCellArray(mpFont);
198 pParagraph->SetCharacterOffset(nCharacterCount);
199 nCharacterCount += pParagraph->GetCharacterCount();
200 maParagraphs.push_back(pParagraph);
203 if (mpCaret)
204 mpCaret->HideCaret();
206 RequestFormat();
212 void PresenterTextView::SetText (const ::rtl::OUString& rsText)
214 maParagraphs.clear();
215 mnCharacterCount = -1;
217 if ( ! mpFont || ! mpFont->PrepareFont(mxCanvas))
218 return;
220 sal_Int32 nCharacterCount (0);
222 SharedPresenterTextParagraph pParagraph (new PresenterTextParagraph(
224 mxBreakIterator,
225 mxScriptTypeDetector,
226 rsText,
227 mpCaret));
228 pParagraph->SetupCellArray(mpFont);
229 pParagraph->SetCharacterOffset(nCharacterCount);
230 nCharacterCount += pParagraph->GetCharacterCount();
231 maParagraphs.push_back(pParagraph);
233 if (mpCaret)
234 mpCaret->HideCaret();
236 RequestFormat();
242 void PresenterTextView::SetTextChangeBroadcaster (
243 const ::boost::function<void(void)>& rBroadcaster)
245 maTextChangeBroadcaster = rBroadcaster;
251 void PresenterTextView::SetLocation (const css::geometry::RealPoint2D& rLocation)
253 maLocation = rLocation;
255 for (::std::vector<SharedPresenterTextParagraph>::iterator
256 iParagraph(maParagraphs.begin()),
257 iEnd(maParagraphs.end());
258 iParagraph!=iEnd;
259 ++iParagraph)
261 (*iParagraph)->SetOrigin(
262 maLocation.X - mnLeftOffset,
263 maLocation.Y - mnTopOffset);
270 void PresenterTextView::SetSize (const css::geometry::RealSize2D& rSize)
272 maSize = rSize;
273 RequestFormat();
279 double PresenterTextView::GetTotalTextHeight (void)
281 double nTotalHeight (0);
283 if (mbIsFormatPending)
285 if ( ! mpFont->PrepareFont(mxCanvas))
286 return 0;
287 Format();
290 for (::std::vector<SharedPresenterTextParagraph>::iterator
291 iParagraph(maParagraphs.begin()),
292 iEnd(maParagraphs.end());
293 iParagraph!=iEnd;
294 ++iParagraph)
296 nTotalHeight += (*iParagraph)->GetTotalTextHeight();
299 return nTotalHeight;
305 void PresenterTextView::SetFont (const PresenterTheme::SharedFontDescriptor& rpFont)
307 mpFont = rpFont;
308 RequestFormat();
314 void PresenterTextView::SetOffset(
315 const double nLeft,
316 const double nTop)
318 mnLeftOffset = nLeft;
319 mnTopOffset = nTop;
321 // Trigger an update of the text origin stored at the individual paragraphs.
322 SetLocation(maLocation);
327 void PresenterTextView::MoveCaret (
328 const sal_Int32 nDistance,
329 const sal_Int16 nTextType)
331 if ( ! mpCaret)
332 return;
334 // When the caret has not been visible yet then move it to the beginning
335 // of the text.
336 if (mpCaret->GetParagraphIndex() < 0)
338 mpCaret->SetPosition(0,0);
339 return;
342 sal_Int32 nParagraphIndex (mpCaret->GetParagraphIndex());
343 sal_Int32 nCharacterIndex (mpCaret->GetCharacterIndex());
344 switch (nTextType)
346 default:
347 case AccessibleTextType::CHARACTER:
348 nCharacterIndex += nDistance;
349 break;
351 case AccessibleTextType::WORD:
353 sal_Int32 nRemainingDistance (nDistance);
354 while (nRemainingDistance != 0)
356 SharedPresenterTextParagraph pParagraph (GetParagraph(nParagraphIndex));
357 if (pParagraph)
359 const sal_Int32 nDelta (Signum(nDistance));
360 nCharacterIndex = pParagraph->GetWordBoundary(nCharacterIndex, nDelta);
361 if (nCharacterIndex < 0)
363 // Go to previous or next paragraph.
364 nParagraphIndex += nDelta;
365 if (nParagraphIndex < 0)
367 nParagraphIndex = 0;
368 nCharacterIndex = 0;
369 nRemainingDistance = 0;
371 else if (sal_uInt32(nParagraphIndex) >= maParagraphs.size())
373 nParagraphIndex = maParagraphs.size()-1;
374 pParagraph = GetParagraph(nParagraphIndex);
375 if (pParagraph)
376 nCharacterIndex = pParagraph->GetCharacterCount();
377 nRemainingDistance = 0;
379 else
381 nRemainingDistance -= nDelta;
383 // Move caret one character to the end of
384 // the previous or the start of the next paragraph.
385 pParagraph = GetParagraph(nParagraphIndex);
386 if (pParagraph)
387 if (nDistance<0)
388 nCharacterIndex = pParagraph->GetCharacterCount();
389 else
390 nCharacterIndex = 0;
393 else
394 nRemainingDistance -= nDelta;
396 else
397 break;
399 break;
403 // Move the caret to the new position.
404 mpCaret->SetPosition(nParagraphIndex, nCharacterIndex);
410 void PresenterTextView::Paint (
411 const css::awt::Rectangle& rUpdateBox)
413 if ( ! mbDoOuput)
414 return;
415 if ( ! mxCanvas.is())
416 return;
417 if ( ! mpFont->PrepareFont(mxCanvas))
418 return;
420 if (mbIsFormatPending)
421 Format();
423 // Setup the clipping rectangle. Horizontally we make it a little
424 // larger to allow characters (and the caret) to stick out of their
425 // bounding boxes. This can happen on some characters (like the
426 // uppercase J) for typographical reasons.
427 const sal_Int32 nAdditionalLeftBorder (10);
428 const sal_Int32 nAdditionalRightBorder (5);
429 double nX (maLocation.X - mnLeftOffset);
430 double nY (maLocation.Y - mnTopOffset);
431 const sal_Int32 nClipLeft (::std::max(
432 PresenterGeometryHelper::Round(maLocation.X)-nAdditionalLeftBorder, rUpdateBox.X));
433 const sal_Int32 nClipTop (::std::max(
434 PresenterGeometryHelper::Round(maLocation.Y), rUpdateBox.Y));
435 const sal_Int32 nClipRight (::std::min(
436 PresenterGeometryHelper::Round(maLocation.X+maSize.Width)+nAdditionalRightBorder, rUpdateBox.X+rUpdateBox.Width));
437 const sal_Int32 nClipBottom (::std::min(
438 PresenterGeometryHelper::Round(maLocation.Y+maSize.Height), rUpdateBox.Y+rUpdateBox.Height));
439 if (nClipLeft>=nClipRight || nClipTop>=nClipBottom)
440 return;
442 const awt::Rectangle aClipBox(
443 nClipLeft,
444 nClipTop,
445 nClipRight - nClipLeft,
446 nClipBottom - nClipTop);
447 Reference<rendering::XPolyPolygon2D> xClipPolygon (
448 PresenterGeometryHelper::CreatePolygon(aClipBox, mxCanvas->getDevice()));
450 const rendering::ViewState aViewState(
451 geometry::AffineMatrix2D(1,0,0, 0,1,0),
452 xClipPolygon);
454 rendering::RenderState aRenderState (
455 geometry::AffineMatrix2D(1,0,nX, 0,1,nY),
456 NULL,
457 Sequence<double>(4),
458 rendering::CompositeOperation::SOURCE);
459 PresenterCanvasHelper::SetDeviceColor(aRenderState, mpFont->mnColor);
461 for (::std::vector<SharedPresenterTextParagraph>::const_iterator
462 iParagraph(maParagraphs.begin()),
463 iEnd(maParagraphs.end());
464 iParagraph!=iEnd;
465 ++iParagraph)
467 (*iParagraph)->Paint(
468 mxCanvas,
469 maSize,
470 mpFont,
471 aViewState,
472 aRenderState,
473 mnTopOffset,
474 nClipTop,
475 nClipBottom);
478 aRenderState.AffineTransform.m02 = 0;
479 aRenderState.AffineTransform.m12 = 0;
481 #ifdef SHOW_CHARACTER_BOXES
482 PresenterCanvasHelper::SetDeviceColor(aRenderState, 0x00808080);
483 for (sal_Int32 nParagraphIndex(0), nParagraphCount(GetParagraphCount());
484 nParagraphIndex<nParagraphCount;
485 ++nParagraphIndex)
487 const SharedPresenterTextParagraph pParagraph (GetParagraph(nParagraphIndex));
488 if ( ! pParagraph)
489 continue;
490 for (sal_Int32 nCharacterIndex(0),nCharacterCount(pParagraph->GetCharacterCount());
491 nCharacterIndex<nCharacterCount; ++nCharacterIndex)
493 const awt::Rectangle aBox (pParagraph->GetCharacterBounds(nCharacterIndex, false));
494 mxCanvas->drawPolyPolygon (
495 PresenterGeometryHelper::CreatePolygon(
496 aBox,
497 mxCanvas->getDevice()),
498 aViewState,
499 aRenderState);
502 PresenterCanvasHelper::SetDeviceColor(aRenderState, mpFont->mnColor);
503 #endif
505 if (mpCaret && mpCaret->IsVisible())
507 mxCanvas->fillPolyPolygon (
508 PresenterGeometryHelper::CreatePolygon(
509 mpCaret->GetBounds(),
510 mxCanvas->getDevice()),
511 aViewState,
512 aRenderState);
519 SharedPresenterTextCaret PresenterTextView::GetCaret (void) const
521 return mpCaret;
527 sal_Int32 PresenterTextView::GetCharacterOffset (const sal_Int32 nParagraphIndex) const
529 sal_Int32 nCharacterOffset (0);
530 for (sal_Int32 nIndex=0; nIndex<nParagraphIndex; ++nIndex)
531 nCharacterOffset += maParagraphs[nIndex]->GetCharacterCount();
532 return nCharacterOffset;
538 awt::Rectangle PresenterTextView::GetCaretBounds (
539 sal_Int32 nParagraphIndex,
540 const sal_Int32 nCharacterIndex) const
542 SharedPresenterTextParagraph pParagraph (GetParagraph(nParagraphIndex));
544 if (pParagraph)
545 return pParagraph->GetCharacterBounds(nCharacterIndex, true);
546 else
547 return awt::Rectangle(0,0,0,0);
553 //----- private ---------------------------------------------------------------
555 void PresenterTextView::RequestFormat (void)
557 mbIsFormatPending = true;
563 void PresenterTextView::Format (void)
565 mbIsFormatPending = false;
567 double nY (0);
568 for (::std::vector<SharedPresenterTextParagraph>::const_iterator
569 iParagraph(maParagraphs.begin()),
570 iEnd(maParagraphs.end());
571 iParagraph!=iEnd;
572 ++iParagraph)
574 (*iParagraph)->Format(nY, maSize.Width, mpFont);
575 nY += (*iParagraph)->GetTotalTextHeight();
578 if (maTextChangeBroadcaster)
579 maTextChangeBroadcaster();
585 sal_Int32 PresenterTextView::GetParagraphCount (void) const
587 return maParagraphs.size();
593 SharedPresenterTextParagraph PresenterTextView::GetParagraph (
594 const sal_Int32 nParagraphIndex) const
596 if (nParagraphIndex < 0)
597 return SharedPresenterTextParagraph();
598 else if (nParagraphIndex>=sal_Int32(maParagraphs.size()))
599 return SharedPresenterTextParagraph();
600 else
601 return maParagraphs[nParagraphIndex];
607 //===== PresenterTextParagraph ================================================
609 PresenterTextParagraph::PresenterTextParagraph (
610 const sal_Int32 nParagraphIndex,
611 const Reference<i18n::XBreakIterator>& rxBreakIterator,
612 const Reference<i18n::XScriptTypeDetector>& rxScriptTypeDetector,
613 const Reference<text::XTextRange>& rxTextRange,
614 const SharedPresenterTextCaret& rpCaret)
615 : msParagraphText(),
616 mnParagraphIndex(nParagraphIndex),
617 mpCaret(rpCaret),
618 mxBreakIterator(rxBreakIterator),
619 mxScriptTypeDetector(rxScriptTypeDetector),
620 maLines(),
621 mnVerticalOffset(0),
622 mnXOrigin(0),
623 mnYOrigin(0),
624 mnWidth(0),
625 mnAscent(0),
626 mnDescent(0),
627 mnLineHeight(-1),
628 meAdjust(style::ParagraphAdjust_LEFT),
629 mnWritingMode (text::WritingMode2::LR_TB),
630 mnCharacterOffset(0),
631 maCells()
633 if (rxTextRange.is())
635 Reference<beans::XPropertySet> xProperties (rxTextRange, UNO_QUERY);
636 lang::Locale aLocale;
639 xProperties->getPropertyValue(A2S("CharLocale")) >>= aLocale;
641 catch(beans::UnknownPropertyException&)
643 // Ignore the exception. Use the default value.
647 xProperties->getPropertyValue(A2S("ParaAdjust")) >>= meAdjust;
649 catch(beans::UnknownPropertyException&)
651 // Ignore the exception. Use the default value.
655 xProperties->getPropertyValue(A2S("WritingMode")) >>= mnWritingMode;
657 catch(beans::UnknownPropertyException&)
659 // Ignore the exception. Use the default value.
662 msParagraphText = rxTextRange->getString();
669 PresenterTextParagraph::PresenterTextParagraph (
670 const sal_Int32 nParagraphIndex,
671 const Reference<i18n::XBreakIterator>& rxBreakIterator,
672 const Reference<i18n::XScriptTypeDetector>& rxScriptTypeDetector,
673 const ::rtl::OUString& rsText,
674 const SharedPresenterTextCaret& rpCaret)
675 : msParagraphText(rsText),
676 mnParagraphIndex(nParagraphIndex),
677 mpCaret(rpCaret),
678 mxBreakIterator(rxBreakIterator),
679 mxScriptTypeDetector(rxScriptTypeDetector),
680 maLines(),
681 mnVerticalOffset(0),
682 mnXOrigin(0),
683 mnYOrigin(0),
684 mnWidth(0),
685 mnAscent(0),
686 mnDescent(0),
687 mnLineHeight(-1),
688 meAdjust(style::ParagraphAdjust_LEFT),
689 mnWritingMode (text::WritingMode2::LR_TB),
690 mnCharacterOffset(0),
691 maCells()
698 void PresenterTextParagraph::Paint (
699 const Reference<rendering::XCanvas>& rxCanvas,
700 const geometry::RealSize2D& rSize,
701 const PresenterTheme::SharedFontDescriptor& rpFont,
702 const rendering::ViewState& rViewState,
703 rendering::RenderState& rRenderState,
704 const double nTopOffset,
705 const double nClipTop,
706 const double nClipBottom)
708 if (mnLineHeight <= 0)
709 return;
711 sal_Int8 nTextDirection (GetTextDirection());
713 const double nSavedM12 (rRenderState.AffineTransform.m12);
715 if ( ! IsTextReferencePointLeft())
716 rRenderState.AffineTransform.m02 += rSize.Width;
719 #ifdef SHOW_CHARACTER_BOXES
720 for (sal_Int32 nIndex=0,nCount=maLines.size();
721 nIndex<nCount;
722 ++nIndex)
724 Line& rLine (maLines[nIndex]);
725 rLine.ProvideLayoutedLine(msParagraphText, rpFont, nTextDirection);
727 #endif
729 for (sal_Int32 nIndex=0,nCount=maLines.size();
730 nIndex<nCount;
731 ++nIndex, rRenderState.AffineTransform.m12 += mnLineHeight)
733 Line& rLine (maLines[nIndex]);
735 // Paint only visible lines.
736 const double nLineTop = rLine.mnBaseLine - mnAscent - nTopOffset;
737 if (nLineTop + mnLineHeight< nClipTop)
738 continue;
739 else if (nLineTop > nClipBottom)
740 break;
741 rLine.ProvideLayoutedLine(msParagraphText, rpFont, nTextDirection);
743 rRenderState.AffineTransform.m12 = nSavedM12 + rLine.mnBaseLine;
745 rxCanvas->drawTextLayout (
746 rLine.mxLayoutedLine,
747 rViewState,
748 rRenderState);
750 rRenderState.AffineTransform.m12 = nSavedM12;
752 if ( ! IsTextReferencePointLeft())
753 rRenderState.AffineTransform.m02 -= rSize.Width;
759 void PresenterTextParagraph::Format (
760 const double nY,
761 const double nWidth,
762 const PresenterTheme::SharedFontDescriptor& rpFont)
764 // Make sure that the text view is in a valid and sane state.
765 if ( ! mxBreakIterator.is() || ! mxScriptTypeDetector.is())
766 return;
767 if (nWidth<=0)
768 return;
769 if ( ! rpFont || ! rpFont->mxFont.is())
770 return;
772 sal_Int32 nPosition (0);
774 mnWidth = nWidth;
775 maLines.clear();
776 mnLineHeight = 0;
777 mnAscent = 0;
778 mnDescent = 0;
779 mnVerticalOffset = nY;
780 maWordBoundaries.clear();
781 maWordBoundaries.push_back(0);
783 const rendering::FontMetrics aMetrics (rpFont->mxFont->getFontMetrics());
784 mnAscent = aMetrics.Ascent;
785 mnDescent = aMetrics.Descent;
786 mnLineHeight = aMetrics.Ascent + aMetrics.Descent + aMetrics.ExternalLeading;
787 nPosition = 0;
788 i18n::Boundary aCurrentLine(0,0);
789 while (true)
791 const i18n::Boundary aWordBoundary = mxBreakIterator->nextWord(
792 msParagraphText,
793 nPosition,
794 lang::Locale(),
795 i18n::WordType::ANYWORD_IGNOREWHITESPACES);
796 AddWord(nWidth, aCurrentLine, aWordBoundary.startPos, rpFont);
798 // Remember the new word boundary for caret travelling by words.
799 // Prevent duplicates.
800 if (aWordBoundary.startPos > maWordBoundaries.back())
801 maWordBoundaries.push_back(aWordBoundary.startPos);
803 if (aWordBoundary.endPos>aWordBoundary.startPos)
804 AddWord(nWidth, aCurrentLine, aWordBoundary.endPos, rpFont);
806 if (aWordBoundary.startPos<0 || aWordBoundary.endPos<0)
807 break;
808 if (nPosition >= aWordBoundary.endPos)
809 break;
810 nPosition = aWordBoundary.endPos;
813 if (aCurrentLine.endPos>aCurrentLine.startPos)
814 AddLine(aCurrentLine);
821 sal_Int32 PresenterTextParagraph::GetWordBoundary(
822 const sal_Int32 nLocalCharacterIndex,
823 const sal_Int32 nDistance)
825 OSL_ASSERT(nDistance==-1 || nDistance==+1);
827 if (nLocalCharacterIndex < 0)
829 // The caller asked for the start or end position of the paragraph.
830 if (nDistance < 0)
831 return 0;
832 else
833 return GetCharacterCount();
836 sal_Int32 nIndex (0);
837 for (sal_Int32 nCount (maWordBoundaries.size()); nIndex<nCount; ++nIndex)
839 if (maWordBoundaries[nIndex] >= nLocalCharacterIndex)
841 // When inside the word (not at its start or end) then
842 // first move to the start or end before going the previous or
843 // next word.
844 if (maWordBoundaries[nIndex] > nLocalCharacterIndex)
845 if (nDistance > 0)
846 --nIndex;
847 break;
851 nIndex += nDistance;
853 if (nIndex < 0)
854 return -1;
855 else if (sal_uInt32(nIndex)>=maWordBoundaries.size())
856 return -1;
857 else
858 return maWordBoundaries[nIndex];
864 sal_Int32 PresenterTextParagraph::GetCaretPosition (void) const
866 if (mpCaret && mpCaret->GetParagraphIndex()==mnParagraphIndex)
867 return mpCaret->GetCharacterIndex();
868 else
869 return -1;
875 void PresenterTextParagraph::SetCaretPosition (const sal_Int32 nPosition) const
877 if (mpCaret && mpCaret->GetParagraphIndex()==mnParagraphIndex)
878 return mpCaret->SetPosition(mnParagraphIndex, nPosition);
884 void PresenterTextParagraph::SetOrigin (const double nXOrigin, const double nYOrigin)
886 mnXOrigin = nXOrigin;
887 mnYOrigin = nYOrigin;
893 awt::Point PresenterTextParagraph::GetRelativeLocation (void) const
895 return awt::Point(
896 sal_Int32(mnXOrigin),
897 sal_Int32(mnYOrigin + mnVerticalOffset));
903 awt::Size PresenterTextParagraph::GetSize (void)
905 return awt::Size(
906 sal_Int32(mnWidth),
907 sal_Int32(GetTotalTextHeight()));
913 void PresenterTextParagraph::AddWord (
914 const double nWidth,
915 i18n::Boundary& rCurrentLine,
916 const sal_Int32 nWordBoundary,
917 const PresenterTheme::SharedFontDescriptor& rpFont)
919 sal_Int32 nLineStart (0);
920 sal_Int32 nLineEnd (0);
921 if ( ! maLines.empty())
923 nLineStart = rCurrentLine.startPos;
924 nLineEnd = rCurrentLine.endPos;
927 const ::rtl::OUString sLineCandidate (
928 msParagraphText.copy(nLineStart, nWordBoundary-nLineStart));
930 css::geometry::RealRectangle2D aLineBox (
931 PresenterCanvasHelper::GetTextBoundingBox (
932 rpFont->mxFont,
933 sLineCandidate,
934 mnWritingMode));
935 const double nLineWidth (aLineBox.X2 - aLineBox.X1);
937 if (nLineWidth >= nWidth)
939 // Add new line with a single word (so far).
940 AddLine(rCurrentLine);
942 rCurrentLine.endPos = nWordBoundary;
948 void PresenterTextParagraph::AddLine (
949 i18n::Boundary& rCurrentLine)
951 Line aLine (rCurrentLine.startPos, rCurrentLine.endPos);
953 // Find the start and end of the line with respect to cells.
954 if (maLines.size() > 0)
956 aLine.mnLineStartCellIndex = maLines.back().mnLineEndCellIndex;
957 aLine.mnBaseLine = maLines.back().mnBaseLine + mnLineHeight;
959 else
961 aLine.mnLineStartCellIndex = 0;
962 aLine.mnBaseLine = mnVerticalOffset + mnAscent;
964 sal_Int32 nCellIndex (aLine.mnLineStartCellIndex);
965 double nWidth (0);
966 for ( ; nCellIndex<sal_Int32(maCells.size()); ++nCellIndex)
968 const Cell& rCell (maCells[nCellIndex]);
969 if (rCell.mnCharacterIndex+rCell.mnCharacterCount > aLine.mnLineEndCharacterIndex)
970 break;
971 nWidth += rCell.mnCellWidth;
973 aLine.mnLineEndCellIndex = nCellIndex;
974 aLine.mnWidth = nWidth;
976 maLines.push_back(aLine);
978 rCurrentLine.startPos = rCurrentLine.endPos;
984 sal_Int32 PresenterTextParagraph::GetParagraphIndex (void) const
986 return mnParagraphIndex;
992 double PresenterTextParagraph::GetTotalTextHeight (void)
994 return maLines.size() * mnLineHeight;
1000 sal_Int32 PresenterTextParagraph::GetCharacterOffset (void) const
1002 return mnCharacterOffset;
1008 void PresenterTextParagraph::SetCharacterOffset (const sal_Int32 nCharacterOffset)
1010 mnCharacterOffset = nCharacterOffset;
1016 sal_Int32 PresenterTextParagraph::GetCharacterCount (void) const
1018 return msParagraphText.getLength();
1024 sal_Unicode PresenterTextParagraph::GetCharacter (
1025 const sal_Int32 nGlobalCharacterIndex) const
1027 if (nGlobalCharacterIndex<mnCharacterOffset
1028 || nGlobalCharacterIndex>=mnCharacterOffset+msParagraphText.getLength())
1030 return sal_Unicode();
1032 else
1034 return msParagraphText.getStr()[nGlobalCharacterIndex - mnCharacterOffset];
1041 ::rtl::OUString PresenterTextParagraph::GetText (void) const
1043 return msParagraphText;
1049 TextSegment PresenterTextParagraph::GetTextSegment (
1050 const sal_Int32 nOffset,
1051 const sal_Int32 nIndex,
1052 const sal_Int16 nTextType) const
1054 switch(nTextType)
1056 case AccessibleTextType::PARAGRAPH:
1057 return TextSegment(
1058 msParagraphText,
1059 mnCharacterOffset,
1060 mnCharacterOffset+msParagraphText.getLength());
1062 case AccessibleTextType::SENTENCE:
1063 if (mxBreakIterator.is())
1065 const sal_Int32 nStart (mxBreakIterator->beginOfSentence(
1066 msParagraphText, nIndex-mnCharacterOffset, lang::Locale()));
1067 const sal_Int32 nEnd (mxBreakIterator->endOfSentence(
1068 msParagraphText, nIndex-mnCharacterOffset, lang::Locale()));
1069 if (nStart < nEnd)
1070 return TextSegment(
1071 msParagraphText.copy(nStart, nEnd-nStart),
1072 nStart+mnCharacterOffset,
1073 nEnd+mnCharacterOffset);
1075 break;
1077 case AccessibleTextType::WORD:
1078 if (mxBreakIterator.is())
1079 return GetWordTextSegment(nOffset, nIndex);
1080 break;
1082 case AccessibleTextType::LINE:
1084 for (::std::vector<Line>::const_iterator
1085 iLine(maLines.begin()),
1086 iEnd(maLines.end());
1087 iLine!=iEnd;
1088 ++iLine)
1090 if (nIndex < iLine->mnLineEndCharacterIndex)
1092 return TextSegment(
1093 msParagraphText.copy(
1094 iLine->mnLineStartCharacterIndex,
1095 iLine->mnLineEndCharacterIndex - iLine->mnLineStartCharacterIndex),
1096 iLine->mnLineStartCharacterIndex,
1097 iLine->mnLineEndCharacterIndex);
1101 break;
1103 // Handle GLYPH and ATTRIBUTE_RUN like CHARACTER because we can not
1104 // do better at the moment.
1105 case AccessibleTextType::CHARACTER:
1106 case AccessibleTextType::GLYPH:
1107 case AccessibleTextType::ATTRIBUTE_RUN:
1108 return CreateTextSegment(nIndex+nOffset, nIndex+nOffset+1);
1111 return TextSegment(::rtl::OUString(), 0,0);
1117 TextSegment PresenterTextParagraph::GetWordTextSegment (
1118 const sal_Int32 nOffset,
1119 const sal_Int32 nIndex) const
1121 sal_Int32 nCurrentOffset (nOffset);
1122 sal_Int32 nCurrentIndex (nIndex);
1124 i18n::Boundary aWordBoundary;
1125 if (nCurrentOffset == 0)
1126 aWordBoundary = mxBreakIterator->getWordBoundary(
1127 msParagraphText,
1128 nIndex,
1129 lang::Locale(),
1130 i18n::WordType::ANYWORD_IGNOREWHITESPACES,
1131 sal_True);
1132 else if (nCurrentOffset < 0)
1134 while (nCurrentOffset<0 && nCurrentIndex>0)
1136 aWordBoundary = mxBreakIterator->previousWord(
1137 msParagraphText,
1138 nCurrentIndex,
1139 lang::Locale(),
1140 i18n::WordType::ANYWORD_IGNOREWHITESPACES);
1141 nCurrentIndex = aWordBoundary.startPos;
1142 ++nCurrentOffset;
1145 else
1147 while (nCurrentOffset>0 && nCurrentIndex<=GetCharacterCount())
1149 aWordBoundary = mxBreakIterator->nextWord(
1150 msParagraphText,
1151 nCurrentIndex,
1152 lang::Locale(),
1153 i18n::WordType::ANYWORD_IGNOREWHITESPACES);
1154 nCurrentIndex = aWordBoundary.endPos;
1155 --nCurrentOffset;
1159 return CreateTextSegment(aWordBoundary.startPos, aWordBoundary.endPos);
1165 TextSegment PresenterTextParagraph::CreateTextSegment (
1166 sal_Int32 nStartIndex,
1167 sal_Int32 nEndIndex) const
1169 if (nEndIndex <= nStartIndex)
1170 return TextSegment(
1171 ::rtl::OUString(),
1172 nStartIndex,
1173 nEndIndex);
1174 else
1175 return TextSegment(
1176 msParagraphText.copy(nStartIndex, nEndIndex-nStartIndex),
1177 nStartIndex,
1178 nEndIndex);
1184 awt::Rectangle PresenterTextParagraph::GetCharacterBounds (
1185 sal_Int32 nGlobalCharacterIndex,
1186 const bool bCaretBox)
1188 // Find the line that contains the requested character and accumulate
1189 // the previous line heights.
1190 sal_Int32 nFirstCharacterIndex (0);
1191 sal_Int32 nEndCharacterIndex (0);
1192 double nX (mnXOrigin);
1193 double nY (mnYOrigin + mnVerticalOffset + mnAscent);
1194 const sal_Int8 nTextDirection (GetTextDirection());
1195 for (sal_Int32 nLineIndex=0,nLineCount=maLines.size();
1196 nLineIndex<nLineCount;
1197 ++nLineIndex, nFirstCharacterIndex=nEndCharacterIndex, nY+=mnLineHeight)
1199 Line& rLine (maLines[nLineIndex]);
1200 // Skip lines before the indexed character.
1201 if (nGlobalCharacterIndex >= rLine.mnLineEndCharacterIndex)
1202 // When in the last line then allow the index past the last char.
1203 if (nLineIndex<nLineCount-1)
1204 continue;
1206 rLine.ProvideCellBoxes();
1208 const sal_Int32 nCellIndex (nGlobalCharacterIndex - rLine.mnLineStartCharacterIndex);
1210 // The cell bounding box is defined relative to the origin of
1211 // the current line. Therefore we have to add the absolute
1212 // position of the line.
1213 geometry::RealRectangle2D rCellBox (rLine.maCellBoxes[
1214 ::std::min(nCellIndex, rLine.maCellBoxes.getLength()-1)]);
1216 double nLeft = nX + rCellBox.X1;
1217 double nRight = nX + rCellBox.X2;
1218 if (nTextDirection == rendering::TextDirection::WEAK_RIGHT_TO_LEFT)
1220 const double nOldRight (nRight);
1221 nRight = rLine.mnWidth - nLeft;
1222 nLeft = rLine.mnWidth - nOldRight;
1224 double nTop (nY + rCellBox.Y1);
1225 double nBottom (nY + rCellBox.Y2);
1226 if (bCaretBox)
1228 nTop = nTop - rCellBox.Y1 - mnAscent;
1229 nBottom = nTop + mnLineHeight;
1230 if (nCellIndex >= rLine.maCellBoxes.getLength())
1231 nLeft = nRight-2;
1232 if (nLeft < nX)
1233 nLeft = nX;
1234 nRight = nLeft+2;
1236 else
1238 nTop = nTop - rCellBox.Y1 - mnAscent;
1239 nBottom = nTop + mnAscent + mnDescent;
1241 const sal_Int32 nX1 = sal_Int32(floor(nLeft));
1242 const sal_Int32 nY1 = sal_Int32(floor(nTop));
1243 const sal_Int32 nX2 = sal_Int32(ceil(nRight));
1244 const sal_Int32 nY2 = sal_Int32(ceil(nBottom));
1246 return awt::Rectangle(nX1,nY1,nX2-nX1+1,nY2-nY1+1);
1249 // We are still here. That means that the given index lies past the
1250 // last character in the paragraph.
1251 // Return an empty box that lies past the last character. Better than nothing.
1252 return awt::Rectangle(sal_Int32(nX+0.5), sal_Int32(nY+0.5), 0, 0);
1258 sal_Int32 PresenterTextParagraph::GetIndexAtPoint (const awt::Point& rPoint) const
1260 (void)rPoint;
1261 return -1;
1267 sal_Int8 PresenterTextParagraph::GetTextDirection (void) const
1269 // Find first portion that has a non-neutral text direction.
1270 sal_Int32 nPosition (0);
1271 sal_Int32 nTextLength (msParagraphText.getLength());
1272 while (nPosition < nTextLength)
1274 const sal_Int16 nScriptDirection (
1275 mxScriptTypeDetector->getScriptDirection(
1276 msParagraphText, nPosition, i18n::ScriptDirection::NEUTRAL));
1277 switch (nScriptDirection)
1279 case i18n::ScriptDirection::NEUTRAL:
1280 // continue looping.
1281 break;
1282 case i18n::ScriptDirection::LEFT_TO_RIGHT:
1283 return rendering::TextDirection::WEAK_LEFT_TO_RIGHT;
1285 case i18n::ScriptDirection::RIGHT_TO_LEFT:
1286 return rendering::TextDirection::WEAK_RIGHT_TO_LEFT;
1289 nPosition = mxScriptTypeDetector->endOfScriptDirection(
1290 msParagraphText, nPosition, nScriptDirection);
1293 // All text in paragraph is neutral. Fall back on writing mode taken
1294 // from the XText (which may not be properly initialized.)
1295 sal_Int8 nTextDirection(rendering::TextDirection::WEAK_LEFT_TO_RIGHT);
1296 switch(mnWritingMode)
1298 case text::WritingMode2::LR_TB:
1299 nTextDirection = rendering::TextDirection::WEAK_LEFT_TO_RIGHT;
1300 break;
1302 case text::WritingMode2::RL_TB:
1303 nTextDirection = rendering::TextDirection::WEAK_RIGHT_TO_LEFT;
1304 break;
1306 default:
1307 case text::WritingMode2::TB_RL:
1308 case text::WritingMode2::TB_LR:
1309 // Can not handle this. Use default and hope for the best.
1310 break;
1312 return nTextDirection;
1318 bool PresenterTextParagraph::IsTextReferencePointLeft (void) const
1320 return mnWritingMode != text::WritingMode2::RL_TB;
1326 void PresenterTextParagraph::SetupCellArray (
1327 const PresenterTheme::SharedFontDescriptor& rpFont)
1329 maCells.clear();
1331 if ( ! rpFont || ! rpFont->mxFont.is())
1332 return;
1334 sal_Int32 nPosition (0);
1335 sal_Int32 nIndex (0);
1336 const sal_Int32 nTextLength (msParagraphText.getLength());
1337 const sal_Int8 nTextDirection (GetTextDirection());
1338 while (nPosition < nTextLength)
1340 const sal_Int32 nNewPosition (mxBreakIterator->nextCharacters(
1341 msParagraphText,
1342 nPosition,
1343 lang::Locale(),
1344 i18n::CharacterIteratorMode::SKIPCELL,
1346 nIndex));
1348 rendering::StringContext aContext (msParagraphText, nPosition, nNewPosition-nPosition);
1349 Reference<rendering::XTextLayout> xLayout (
1350 rpFont->mxFont->createTextLayout(aContext, nTextDirection, 0));
1351 css::geometry::RealRectangle2D aCharacterBox (xLayout->queryTextBounds());
1353 maCells.push_back(Cell(
1354 nPosition,
1355 nNewPosition-nPosition,
1356 aCharacterBox.X2-aCharacterBox.X1));
1358 nPosition = nNewPosition;
1365 //===== PresenterTextCaret ================================================----
1367 PresenterTextCaret::PresenterTextCaret (
1368 const ::boost::function<css::awt::Rectangle(const sal_Int32,const sal_Int32)>& rCharacterBoundsAccess,
1369 const ::boost::function<void(const css::awt::Rectangle&)>& rInvalidator)
1370 : mnParagraphIndex(-1),
1371 mnCharacterIndex(-1),
1372 mnCaretBlinkTaskId(0),
1373 mbIsCaretVisible(false),
1374 maCharacterBoundsAccess(rCharacterBoundsAccess),
1375 maInvalidator(rInvalidator),
1376 maBroadcaster(),
1377 maCaretBounds()
1384 PresenterTextCaret::~PresenterTextCaret (void)
1386 HideCaret();
1392 void PresenterTextCaret::ShowCaret (void)
1394 if (mnCaretBlinkTaskId == 0)
1396 mnCaretBlinkTaskId = PresenterTimer::ScheduleRepeatedTask (
1397 ::boost::bind(&PresenterTextCaret::InvertCaret, this),
1398 CaretBlinkIntervall,
1399 CaretBlinkIntervall);
1401 mbIsCaretVisible = true;
1407 void PresenterTextCaret::HideCaret (void)
1409 if (mnCaretBlinkTaskId != 0)
1411 PresenterTimer::CancelTask(mnCaretBlinkTaskId);
1412 mnCaretBlinkTaskId = 0;
1414 mbIsCaretVisible = false;
1415 // Reset the caret position.
1416 mnParagraphIndex = -1;
1417 mnCharacterIndex = -1;
1423 sal_Int32 PresenterTextCaret::GetParagraphIndex (void) const
1425 return mnParagraphIndex;
1431 sal_Int32 PresenterTextCaret::GetCharacterIndex (void) const
1433 return mnCharacterIndex;
1439 void PresenterTextCaret::SetPosition (
1440 const sal_Int32 nParagraphIndex,
1441 const sal_Int32 nCharacterIndex)
1443 if (mnParagraphIndex != nParagraphIndex
1444 || mnCharacterIndex != nCharacterIndex)
1446 if (mnParagraphIndex >= 0)
1447 maInvalidator(maCaretBounds);
1449 const sal_Int32 nOldParagraphIndex (mnParagraphIndex);
1450 const sal_Int32 nOldCharacterIndex (mnCharacterIndex);
1451 mnParagraphIndex = nParagraphIndex;
1452 mnCharacterIndex = nCharacterIndex;
1453 maCaretBounds = maCharacterBoundsAccess(mnParagraphIndex, mnCharacterIndex);
1454 if (mnParagraphIndex >= 0)
1455 ShowCaret();
1456 else
1457 HideCaret();
1459 if (mnParagraphIndex >= 0)
1460 maInvalidator(maCaretBounds);
1462 if (maBroadcaster)
1463 maBroadcaster(
1464 nOldParagraphIndex,
1465 nOldCharacterIndex,
1466 mnParagraphIndex,
1467 mnCharacterIndex);
1475 bool PresenterTextCaret::IsVisible (void) const
1477 return mbIsCaretVisible;
1483 void PresenterTextCaret::SetCaretMotionBroadcaster (
1484 const ::boost::function<void(sal_Int32,sal_Int32,sal_Int32,sal_Int32)>& rBroadcaster)
1486 maBroadcaster = rBroadcaster;
1492 css::awt::Rectangle PresenterTextCaret::GetBounds (void) const
1494 return maCaretBounds;
1500 void PresenterTextCaret::InvertCaret (void)
1502 mbIsCaretVisible = !mbIsCaretVisible;
1503 if (mnParagraphIndex >= 0)
1504 maInvalidator(maCaretBounds);
1513 //===== PresenterTextParagraph::Cell ==========================================
1515 PresenterTextParagraph::Cell::Cell (
1516 const sal_Int32 nCharacterIndex,
1517 const sal_Int32 nCharacterCount,
1518 const double nCellWidth)
1519 : mnCharacterIndex(nCharacterIndex),
1520 mnCharacterCount(nCharacterCount),
1521 mnCellWidth(nCellWidth)
1528 //===== PresenterTextParagraph::Line ==========================================
1530 PresenterTextParagraph::Line::Line (
1531 const sal_Int32 nLineStartCharacterIndex,
1532 const sal_Int32 nLineEndCharacterIndex)
1533 : mnLineStartCharacterIndex(nLineStartCharacterIndex),
1534 mnLineEndCharacterIndex(nLineEndCharacterIndex),
1535 mnLineStartCellIndex(-1), mnLineEndCellIndex(-1),
1536 mxLayoutedLine(),
1537 mnBaseLine(0), mnWidth(0),
1538 maCellBoxes()
1545 sal_Int32 PresenterTextParagraph::Line::GetLength (void) const
1547 return mnLineEndCharacterIndex-mnLineStartCharacterIndex;
1553 void PresenterTextParagraph::Line::ProvideCellBoxes (void)
1555 if ( ! IsEmpty() && maCellBoxes.getLength()==0)
1557 if (mxLayoutedLine.is())
1558 maCellBoxes = mxLayoutedLine->queryInkMeasures();
1559 else
1561 OSL_ASSERT(mxLayoutedLine.is());
1569 void PresenterTextParagraph::Line::ProvideLayoutedLine (
1570 const ::rtl::OUString& rsParagraphText,
1571 const PresenterTheme::SharedFontDescriptor& rpFont,
1572 const sal_Int8 nTextDirection)
1574 if ( ! mxLayoutedLine.is())
1576 const rendering::StringContext aContext (
1577 rsParagraphText,
1578 mnLineStartCharacterIndex,
1579 mnLineEndCharacterIndex - mnLineStartCharacterIndex);
1581 mxLayoutedLine = rpFont->mxFont->createTextLayout(
1582 aContext,
1583 nTextDirection,
1591 bool PresenterTextParagraph::Line::IsEmpty (void) const
1593 return mnLineStartCharacterIndex >= mnLineEndCharacterIndex;
1599 } } // end of namespace ::sdext::presenter