1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: PresenterNotesView.hxx,v $
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"
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
64 sal_Int32
Signum (const sal_Int32 nValue
)
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
)
87 mxScriptTypeDetector(),
92 mpCaret(new PresenterTextCaret(
93 ::boost::bind(&PresenterTextView::GetCaretBounds
, this, _1
, _2
),
97 maInvalidator(rInvalidator
),
98 mbIsFormatPending(false),
100 maTextChangeBroadcaster()
102 Reference
<lang::XMultiComponentFactory
> xFactory (
103 rxContext
->getServiceManager(), UNO_QUERY
);
104 if ( ! xFactory
.is())
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"),
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"),
126 PresenterTextView::PresenterTextView (
127 const Reference
<XComponentContext
>& rxContext
,
128 const Reference
<rendering::XCanvas
>& rxCanvas
)
129 : mxCanvas(rxCanvas
),
132 mxScriptTypeDetector(),
137 mpCaret(new PresenterTextCaret(
138 ::boost::bind(&PresenterTextView::GetCaretBounds
, this, _1
, _2
),
139 ::boost::function
<void(const css::awt::Rectangle
&)>())),
143 mbIsFormatPending(false),
144 mnCharacterCount(-1),
145 maTextChangeBroadcaster()
147 Reference
<lang::XMultiComponentFactory
> xFactory (
148 rxContext
->getServiceManager(), UNO_QUERY
);
149 if ( ! xFactory
.is())
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"),
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"),
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())
180 Reference
<container::XEnumeration
> xParagraphs (
181 xParagraphAccess
->createEnumeration() , UNO_QUERY
);
182 if ( ! xParagraphs
.is())
185 if ( ! mpFont
|| ! mpFont
->PrepareFont(mxCanvas
))
188 sal_Int32
nCharacterCount (0);
189 while (xParagraphs
->hasMoreElements())
191 SharedPresenterTextParagraph
pParagraph (new PresenterTextParagraph(
194 mxScriptTypeDetector
,
195 Reference
<text::XTextRange
>(xParagraphs
->nextElement(), UNO_QUERY
),
197 pParagraph
->SetupCellArray(mpFont
);
198 pParagraph
->SetCharacterOffset(nCharacterCount
);
199 nCharacterCount
+= pParagraph
->GetCharacterCount();
200 maParagraphs
.push_back(pParagraph
);
204 mpCaret
->HideCaret();
212 void PresenterTextView::SetText (const ::rtl::OUString
& rsText
)
214 maParagraphs
.clear();
215 mnCharacterCount
= -1;
217 if ( ! mpFont
|| ! mpFont
->PrepareFont(mxCanvas
))
220 sal_Int32
nCharacterCount (0);
222 SharedPresenterTextParagraph
pParagraph (new PresenterTextParagraph(
225 mxScriptTypeDetector
,
228 pParagraph
->SetupCellArray(mpFont
);
229 pParagraph
->SetCharacterOffset(nCharacterCount
);
230 nCharacterCount
+= pParagraph
->GetCharacterCount();
231 maParagraphs
.push_back(pParagraph
);
234 mpCaret
->HideCaret();
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());
261 (*iParagraph
)->SetOrigin(
262 maLocation
.X
- mnLeftOffset
,
263 maLocation
.Y
- mnTopOffset
);
270 void PresenterTextView::SetSize (const css::geometry::RealSize2D
& rSize
)
279 double PresenterTextView::GetTotalTextHeight (void)
281 double nTotalHeight (0);
283 if (mbIsFormatPending
)
285 if ( ! mpFont
->PrepareFont(mxCanvas
))
290 for (::std::vector
<SharedPresenterTextParagraph
>::iterator
291 iParagraph(maParagraphs
.begin()),
292 iEnd(maParagraphs
.end());
296 nTotalHeight
+= (*iParagraph
)->GetTotalTextHeight();
305 void PresenterTextView::SetFont (const PresenterTheme::SharedFontDescriptor
& rpFont
)
314 void PresenterTextView::SetOffset(
318 mnLeftOffset
= nLeft
;
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
)
334 // When the caret has not been visible yet then move it to the beginning
336 if (mpCaret
->GetParagraphIndex() < 0)
338 mpCaret
->SetPosition(0,0);
342 sal_Int32
nParagraphIndex (mpCaret
->GetParagraphIndex());
343 sal_Int32
nCharacterIndex (mpCaret
->GetCharacterIndex());
347 case AccessibleTextType::CHARACTER
:
348 nCharacterIndex
+= nDistance
;
351 case AccessibleTextType::WORD
:
353 sal_Int32
nRemainingDistance (nDistance
);
354 while (nRemainingDistance
!= 0)
356 SharedPresenterTextParagraph
pParagraph (GetParagraph(nParagraphIndex
));
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)
369 nRemainingDistance
= 0;
371 else if (sal_uInt32(nParagraphIndex
) >= maParagraphs
.size())
373 nParagraphIndex
= maParagraphs
.size()-1;
374 pParagraph
= GetParagraph(nParagraphIndex
);
376 nCharacterIndex
= pParagraph
->GetCharacterCount();
377 nRemainingDistance
= 0;
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
);
388 nCharacterIndex
= pParagraph
->GetCharacterCount();
394 nRemainingDistance
-= nDelta
;
403 // Move the caret to the new position.
404 mpCaret
->SetPosition(nParagraphIndex
, nCharacterIndex
);
410 void PresenterTextView::Paint (
411 const css::awt::Rectangle
& rUpdateBox
)
415 if ( ! mxCanvas
.is())
417 if ( ! mpFont
->PrepareFont(mxCanvas
))
420 if (mbIsFormatPending
)
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
)
442 const awt::Rectangle
aClipBox(
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),
454 rendering::RenderState
aRenderState (
455 geometry::AffineMatrix2D(1,0,nX
, 0,1,nY
),
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());
467 (*iParagraph
)->Paint(
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
;
487 const SharedPresenterTextParagraph
pParagraph (GetParagraph(nParagraphIndex
));
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(
497 mxCanvas
->getDevice()),
502 PresenterCanvasHelper::SetDeviceColor(aRenderState
, mpFont
->mnColor
);
505 if (mpCaret
&& mpCaret
->IsVisible())
507 mxCanvas
->fillPolyPolygon (
508 PresenterGeometryHelper::CreatePolygon(
509 mpCaret
->GetBounds(),
510 mxCanvas
->getDevice()),
519 SharedPresenterTextCaret
PresenterTextView::GetCaret (void) const
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
));
545 return pParagraph
->GetCharacterBounds(nCharacterIndex
, true);
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;
568 for (::std::vector
<SharedPresenterTextParagraph
>::const_iterator
569 iParagraph(maParagraphs
.begin()),
570 iEnd(maParagraphs
.end());
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();
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
)
616 mnParagraphIndex(nParagraphIndex
),
618 mxBreakIterator(rxBreakIterator
),
619 mxScriptTypeDetector(rxScriptTypeDetector
),
628 meAdjust(style::ParagraphAdjust_LEFT
),
629 mnWritingMode (text::WritingMode2::LR_TB
),
630 mnCharacterOffset(0),
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
),
678 mxBreakIterator(rxBreakIterator
),
679 mxScriptTypeDetector(rxScriptTypeDetector
),
688 meAdjust(style::ParagraphAdjust_LEFT
),
689 mnWritingMode (text::WritingMode2::LR_TB
),
690 mnCharacterOffset(0),
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)
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();
724 Line
& rLine (maLines
[nIndex
]);
725 rLine
.ProvideLayoutedLine(msParagraphText
, rpFont
, nTextDirection
);
729 for (sal_Int32 nIndex
=0,nCount
=maLines
.size();
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
)
739 else if (nLineTop
> nClipBottom
)
741 rLine
.ProvideLayoutedLine(msParagraphText
, rpFont
, nTextDirection
);
743 rRenderState
.AffineTransform
.m12
= nSavedM12
+ rLine
.mnBaseLine
;
745 rxCanvas
->drawTextLayout (
746 rLine
.mxLayoutedLine
,
750 rRenderState
.AffineTransform
.m12
= nSavedM12
;
752 if ( ! IsTextReferencePointLeft())
753 rRenderState
.AffineTransform
.m02
-= rSize
.Width
;
759 void PresenterTextParagraph::Format (
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())
769 if ( ! rpFont
|| ! rpFont
->mxFont
.is())
772 sal_Int32
nPosition (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
;
788 i18n::Boundary
aCurrentLine(0,0);
791 const i18n::Boundary aWordBoundary
= mxBreakIterator
->nextWord(
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)
808 if (nPosition
>= aWordBoundary
.endPos
)
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.
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
844 if (maWordBoundaries
[nIndex
] > nLocalCharacterIndex
)
855 else if (sal_uInt32(nIndex
)>=maWordBoundaries
.size())
858 return maWordBoundaries
[nIndex
];
864 sal_Int32
PresenterTextParagraph::GetCaretPosition (void) const
866 if (mpCaret
&& mpCaret
->GetParagraphIndex()==mnParagraphIndex
)
867 return mpCaret
->GetCharacterIndex();
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
896 sal_Int32(mnXOrigin
),
897 sal_Int32(mnYOrigin
+ mnVerticalOffset
));
903 awt::Size
PresenterTextParagraph::GetSize (void)
907 sal_Int32(GetTotalTextHeight()));
913 void PresenterTextParagraph::AddWord (
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 (
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
;
961 aLine
.mnLineStartCellIndex
= 0;
962 aLine
.mnBaseLine
= mnVerticalOffset
+ mnAscent
;
964 sal_Int32
nCellIndex (aLine
.mnLineStartCellIndex
);
966 for ( ; nCellIndex
<sal_Int32(maCells
.size()); ++nCellIndex
)
968 const Cell
& rCell (maCells
[nCellIndex
]);
969 if (rCell
.mnCharacterIndex
+rCell
.mnCharacterCount
> aLine
.mnLineEndCharacterIndex
)
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();
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
1056 case AccessibleTextType::PARAGRAPH
:
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()));
1071 msParagraphText
.copy(nStart
, nEnd
-nStart
),
1072 nStart
+mnCharacterOffset
,
1073 nEnd
+mnCharacterOffset
);
1077 case AccessibleTextType::WORD
:
1078 if (mxBreakIterator
.is())
1079 return GetWordTextSegment(nOffset
, nIndex
);
1082 case AccessibleTextType::LINE
:
1084 for (::std::vector
<Line
>::const_iterator
1085 iLine(maLines
.begin()),
1086 iEnd(maLines
.end());
1090 if (nIndex
< iLine
->mnLineEndCharacterIndex
)
1093 msParagraphText
.copy(
1094 iLine
->mnLineStartCharacterIndex
,
1095 iLine
->mnLineEndCharacterIndex
- iLine
->mnLineStartCharacterIndex
),
1096 iLine
->mnLineStartCharacterIndex
,
1097 iLine
->mnLineEndCharacterIndex
);
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(
1130 i18n::WordType::ANYWORD_IGNOREWHITESPACES
,
1132 else if (nCurrentOffset
< 0)
1134 while (nCurrentOffset
<0 && nCurrentIndex
>0)
1136 aWordBoundary
= mxBreakIterator
->previousWord(
1140 i18n::WordType::ANYWORD_IGNOREWHITESPACES
);
1141 nCurrentIndex
= aWordBoundary
.startPos
;
1147 while (nCurrentOffset
>0 && nCurrentIndex
<=GetCharacterCount())
1149 aWordBoundary
= mxBreakIterator
->nextWord(
1153 i18n::WordType::ANYWORD_IGNOREWHITESPACES
);
1154 nCurrentIndex
= aWordBoundary
.endPos
;
1159 return CreateTextSegment(aWordBoundary
.startPos
, aWordBoundary
.endPos
);
1165 TextSegment
PresenterTextParagraph::CreateTextSegment (
1166 sal_Int32 nStartIndex
,
1167 sal_Int32 nEndIndex
) const
1169 if (nEndIndex
<= nStartIndex
)
1176 msParagraphText
.copy(nStartIndex
, nEndIndex
-nStartIndex
),
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)
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
);
1228 nTop
= nTop
- rCellBox
.Y1
- mnAscent
;
1229 nBottom
= nTop
+ mnLineHeight
;
1230 if (nCellIndex
>= rLine
.maCellBoxes
.getLength())
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
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.
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
;
1302 case text::WritingMode2::RL_TB
:
1303 nTextDirection
= rendering::TextDirection::WEAK_RIGHT_TO_LEFT
;
1307 case text::WritingMode2::TB_RL
:
1308 case text::WritingMode2::TB_LR
:
1309 // Can not handle this. Use default and hope for the best.
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
)
1331 if ( ! rpFont
|| ! rpFont
->mxFont
.is())
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(
1344 i18n::CharacterIteratorMode::SKIPCELL
,
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(
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
),
1384 PresenterTextCaret::~PresenterTextCaret (void)
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)
1459 if (mnParagraphIndex
>= 0)
1460 maInvalidator(maCaretBounds
);
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),
1537 mnBaseLine(0), mnWidth(0),
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();
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 (
1578 mnLineStartCharacterIndex
,
1579 mnLineEndCharacterIndex
- mnLineStartCharacterIndex
);
1581 mxLayoutedLine
= rpFont
->mxFont
->createTextLayout(
1591 bool PresenterTextParagraph::Line::IsEmpty (void) const
1593 return mnLineStartCharacterIndex
>= mnLineEndCharacterIndex
;
1599 } } // end of namespace ::sdext::presenter