1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include "PresenterTextView.hxx"
21 #include "PresenterCanvasHelper.hxx"
22 #include "PresenterGeometryHelper.hxx"
23 #include "PresenterTimer.hxx"
27 #include <com/sun/star/accessibility/AccessibleTextType.hpp>
28 #include <com/sun/star/container/XEnumerationAccess.hpp>
29 #include <com/sun/star/i18n/BreakIterator.hpp>
30 #include <com/sun/star/i18n/CharType.hpp>
31 #include <com/sun/star/i18n/CharacterIteratorMode.hpp>
32 #include <com/sun/star/i18n/CTLScriptType.hpp>
33 #include <com/sun/star/i18n/ScriptDirection.hpp>
34 #include <com/sun/star/i18n/WordType.hpp>
35 #include <com/sun/star/rendering/CompositeOperation.hpp>
36 #include <com/sun/star/rendering/TextDirection.hpp>
37 #include <com/sun/star/text/WritingMode2.hpp>
38 #include <boost/bind.hpp>
40 using namespace ::com::sun::star
;
41 using namespace ::com::sun::star::accessibility
;
42 using namespace ::com::sun::star::uno
;
44 const static sal_Int64 CaretBlinkIntervall
= 500 * 1000 * 1000;
46 //#define SHOW_CHARACTER_BOXES
49 sal_Int32
Signum (const sal_Int32 nValue
)
60 namespace sdext
{ namespace presenter
{
62 //===== PresenterTextView =====================================================
64 PresenterTextView::PresenterTextView (
65 const Reference
<XComponentContext
>& rxContext
,
66 const Reference
<rendering::XCanvas
>& rxCanvas
,
67 const ::boost::function
<void(const css::awt::Rectangle
&)>& rInvalidator
)
71 mxScriptTypeDetector(),
76 mpCaret(new PresenterTextCaret(
77 ::boost::bind(&PresenterTextView::GetCaretBounds
, this, _1
, _2
),
81 maInvalidator(rInvalidator
),
82 mbIsFormatPending(false),
84 maTextChangeBroadcaster()
86 Reference
<lang::XMultiComponentFactory
> xFactory (
87 rxContext
->getServiceManager(), UNO_QUERY
);
91 // Create the break iterator that we use to break text into lines.
92 mxBreakIterator
= i18n::BreakIterator::create(rxContext
);
94 // Create the script type detector that is used to split paragraphs into
95 // portions of the same text direction.
96 mxScriptTypeDetector
= Reference
<i18n::XScriptTypeDetector
>(
97 xFactory
->createInstanceWithContext(
98 "com.sun.star.i18n.ScriptTypeDetector",
103 void PresenterTextView::SetText (const Reference
<text::XText
>& rxText
)
105 maParagraphs
.clear();
106 mnCharacterCount
= -1;
108 Reference
<container::XEnumerationAccess
> xParagraphAccess (rxText
, UNO_QUERY
);
109 if ( ! xParagraphAccess
.is())
112 Reference
<container::XEnumeration
> xParagraphs (
113 xParagraphAccess
->createEnumeration() , UNO_QUERY
);
114 if ( ! xParagraphs
.is())
117 if ( ! mpFont
|| ! mpFont
->PrepareFont(mxCanvas
))
120 sal_Int32
nCharacterCount (0);
121 while (xParagraphs
->hasMoreElements())
123 SharedPresenterTextParagraph
pParagraph (new PresenterTextParagraph(
126 mxScriptTypeDetector
,
127 Reference
<text::XTextRange
>(xParagraphs
->nextElement(), UNO_QUERY
),
129 pParagraph
->SetupCellArray(mpFont
);
130 pParagraph
->SetCharacterOffset(nCharacterCount
);
131 nCharacterCount
+= pParagraph
->GetCharacterCount();
132 maParagraphs
.push_back(pParagraph
);
136 mpCaret
->HideCaret();
141 void PresenterTextView::SetTextChangeBroadcaster (
142 const ::boost::function
<void()>& rBroadcaster
)
144 maTextChangeBroadcaster
= rBroadcaster
;
147 void PresenterTextView::SetLocation (const css::geometry::RealPoint2D
& rLocation
)
149 maLocation
= rLocation
;
151 for (::std::vector
<SharedPresenterTextParagraph
>::iterator
152 iParagraph(maParagraphs
.begin()),
153 iEnd(maParagraphs
.end());
157 (*iParagraph
)->SetOrigin(
158 maLocation
.X
- mnLeftOffset
,
159 maLocation
.Y
- mnTopOffset
);
163 void PresenterTextView::SetSize (const css::geometry::RealSize2D
& rSize
)
169 double PresenterTextView::GetTotalTextHeight()
171 double nTotalHeight (0);
173 if (mbIsFormatPending
)
175 if ( ! mpFont
->PrepareFont(mxCanvas
))
180 for (::std::vector
<SharedPresenterTextParagraph
>::iterator
181 iParagraph(maParagraphs
.begin()),
182 iEnd(maParagraphs
.end());
186 nTotalHeight
+= (*iParagraph
)->GetTotalTextHeight();
192 void PresenterTextView::SetFont (const PresenterTheme::SharedFontDescriptor
& rpFont
)
198 void PresenterTextView::SetOffset(
202 mnLeftOffset
= nLeft
;
205 // Trigger an update of the text origin stored at the individual paragraphs.
206 SetLocation(maLocation
);
209 void PresenterTextView::MoveCaret (
210 const sal_Int32 nDistance
,
211 const sal_Int16 nTextType
)
216 // When the caret has not been visible yet then move it to the beginning
218 if (mpCaret
->GetParagraphIndex() < 0)
220 mpCaret
->SetPosition(0,0);
224 sal_Int32
nParagraphIndex (mpCaret
->GetParagraphIndex());
225 sal_Int32
nCharacterIndex (mpCaret
->GetCharacterIndex());
229 case AccessibleTextType::CHARACTER
:
230 nCharacterIndex
+= nDistance
;
233 case AccessibleTextType::WORD
:
235 sal_Int32
nRemainingDistance (nDistance
);
236 while (nRemainingDistance
!= 0)
238 SharedPresenterTextParagraph
pParagraph (GetParagraph(nParagraphIndex
));
241 const sal_Int32
nDelta (Signum(nDistance
));
242 nCharacterIndex
= pParagraph
->GetWordBoundary(nCharacterIndex
, nDelta
);
243 if (nCharacterIndex
< 0)
245 // Go to previous or next paragraph.
246 nParagraphIndex
+= nDelta
;
247 if (nParagraphIndex
< 0)
251 nRemainingDistance
= 0;
253 else if (sal_uInt32(nParagraphIndex
) >= maParagraphs
.size())
255 nParagraphIndex
= maParagraphs
.size()-1;
256 pParagraph
= GetParagraph(nParagraphIndex
);
258 nCharacterIndex
= pParagraph
->GetCharacterCount();
259 nRemainingDistance
= 0;
263 nRemainingDistance
-= nDelta
;
265 // Move caret one character to the end of
266 // the previous or the start of the next paragraph.
267 pParagraph
= GetParagraph(nParagraphIndex
);
271 nCharacterIndex
= pParagraph
->GetCharacterCount();
278 nRemainingDistance
-= nDelta
;
287 // Move the caret to the new position.
288 mpCaret
->SetPosition(nParagraphIndex
, nCharacterIndex
);
291 void PresenterTextView::Paint (
292 const css::awt::Rectangle
& rUpdateBox
)
296 if ( ! mxCanvas
.is())
298 if ( ! mpFont
->PrepareFont(mxCanvas
))
301 if (mbIsFormatPending
)
304 // Setup the clipping rectangle. Horizontally we make it a little
305 // larger to allow characters (and the caret) to stick out of their
306 // bounding boxes. This can happen on some characters (like the
307 // uppercase J) for typographical reasons.
308 const sal_Int32
nAdditionalLeftBorder (10);
309 const sal_Int32
nAdditionalRightBorder (5);
310 double nX (maLocation
.X
- mnLeftOffset
);
311 double nY (maLocation
.Y
- mnTopOffset
);
312 const sal_Int32
nClipLeft (::std::max(
313 PresenterGeometryHelper::Round(maLocation
.X
)-nAdditionalLeftBorder
, rUpdateBox
.X
));
314 const sal_Int32
nClipTop (::std::max(
315 PresenterGeometryHelper::Round(maLocation
.Y
), rUpdateBox
.Y
));
316 const sal_Int32
nClipRight (::std::min(
317 PresenterGeometryHelper::Round(maLocation
.X
+maSize
.Width
)+nAdditionalRightBorder
, rUpdateBox
.X
+rUpdateBox
.Width
));
318 const sal_Int32
nClipBottom (::std::min(
319 PresenterGeometryHelper::Round(maLocation
.Y
+maSize
.Height
), rUpdateBox
.Y
+rUpdateBox
.Height
));
320 if (nClipLeft
>=nClipRight
|| nClipTop
>=nClipBottom
)
323 const awt::Rectangle
aClipBox(
326 nClipRight
- nClipLeft
,
327 nClipBottom
- nClipTop
);
328 Reference
<rendering::XPolyPolygon2D
> xClipPolygon (
329 PresenterGeometryHelper::CreatePolygon(aClipBox
, mxCanvas
->getDevice()));
331 const rendering::ViewState
aViewState(
332 geometry::AffineMatrix2D(1,0,0, 0,1,0),
335 rendering::RenderState
aRenderState (
336 geometry::AffineMatrix2D(1,0,nX
, 0,1,nY
),
339 rendering::CompositeOperation::SOURCE
);
340 PresenterCanvasHelper::SetDeviceColor(aRenderState
, mpFont
->mnColor
);
342 for (::std::vector
<SharedPresenterTextParagraph
>::const_iterator
343 iParagraph(maParagraphs
.begin()),
344 iEnd(maParagraphs
.end());
348 (*iParagraph
)->Paint(
359 aRenderState
.AffineTransform
.m02
= 0;
360 aRenderState
.AffineTransform
.m12
= 0;
362 #ifdef SHOW_CHARACTER_BOXES
363 PresenterCanvasHelper::SetDeviceColor(aRenderState
, 0x00808080);
364 for (sal_Int32
nParagraphIndex(0), nParagraphCount(GetParagraphCount());
365 nParagraphIndex
<nParagraphCount
;
368 const SharedPresenterTextParagraph
pParagraph (GetParagraph(nParagraphIndex
));
371 for (sal_Int32
nCharacterIndex(0),nCharacterCount(pParagraph
->GetCharacterCount());
372 nCharacterIndex
<nCharacterCount
; ++nCharacterIndex
)
374 const awt::Rectangle
aBox (pParagraph
->GetCharacterBounds(nCharacterIndex
, false));
375 mxCanvas
->drawPolyPolygon (
376 PresenterGeometryHelper::CreatePolygon(
378 mxCanvas
->getDevice()),
383 PresenterCanvasHelper::SetDeviceColor(aRenderState
, mpFont
->mnColor
);
386 if (mpCaret
&& mpCaret
->IsVisible())
388 mxCanvas
->fillPolyPolygon (
389 PresenterGeometryHelper::CreatePolygon(
390 mpCaret
->GetBounds(),
391 mxCanvas
->getDevice()),
397 SharedPresenterTextCaret
PresenterTextView::GetCaret() const
402 awt::Rectangle
PresenterTextView::GetCaretBounds (
403 sal_Int32 nParagraphIndex
,
404 const sal_Int32 nCharacterIndex
) const
406 SharedPresenterTextParagraph
pParagraph (GetParagraph(nParagraphIndex
));
409 return pParagraph
->GetCharacterBounds(nCharacterIndex
, true);
411 return awt::Rectangle(0,0,0,0);
414 //----- private ---------------------------------------------------------------
416 void PresenterTextView::RequestFormat()
418 mbIsFormatPending
= true;
421 void PresenterTextView::Format()
423 mbIsFormatPending
= false;
426 for (::std::vector
<SharedPresenterTextParagraph
>::const_iterator
427 iParagraph(maParagraphs
.begin()),
428 iEnd(maParagraphs
.end());
432 (*iParagraph
)->Format(nY
, maSize
.Width
, mpFont
);
433 nY
+= (*iParagraph
)->GetTotalTextHeight();
436 if (maTextChangeBroadcaster
)
437 maTextChangeBroadcaster();
440 sal_Int32
PresenterTextView::GetParagraphCount() const
442 return maParagraphs
.size();
445 SharedPresenterTextParagraph
PresenterTextView::GetParagraph (
446 const sal_Int32 nParagraphIndex
) const
448 if (nParagraphIndex
< 0)
449 return SharedPresenterTextParagraph();
450 else if (nParagraphIndex
>=sal_Int32(maParagraphs
.size()))
451 return SharedPresenterTextParagraph();
453 return maParagraphs
[nParagraphIndex
];
456 //===== PresenterTextParagraph ================================================
458 PresenterTextParagraph::PresenterTextParagraph (
459 const sal_Int32 nParagraphIndex
,
460 const Reference
<i18n::XBreakIterator
>& rxBreakIterator
,
461 const Reference
<i18n::XScriptTypeDetector
>& rxScriptTypeDetector
,
462 const Reference
<text::XTextRange
>& rxTextRange
,
463 const SharedPresenterTextCaret
& rpCaret
)
465 mnParagraphIndex(nParagraphIndex
),
467 mxBreakIterator(rxBreakIterator
),
468 mxScriptTypeDetector(rxScriptTypeDetector
),
477 meAdjust(style::ParagraphAdjust_LEFT
),
478 mnWritingMode (text::WritingMode2::LR_TB
),
479 mnCharacterOffset(0),
482 if (rxTextRange
.is())
484 Reference
<beans::XPropertySet
> xProperties (rxTextRange
, UNO_QUERY
);
485 lang::Locale aLocale
;
488 xProperties
->getPropertyValue("CharLocale") >>= aLocale
;
490 catch(beans::UnknownPropertyException
&)
492 // Ignore the exception. Use the default value.
496 xProperties
->getPropertyValue("ParaAdjust") >>= meAdjust
;
498 catch(beans::UnknownPropertyException
&)
500 // Ignore the exception. Use the default value.
504 xProperties
->getPropertyValue("WritingMode") >>= mnWritingMode
;
506 catch(beans::UnknownPropertyException
&)
508 // Ignore the exception. Use the default value.
511 msParagraphText
= rxTextRange
->getString();
515 void PresenterTextParagraph::Paint (
516 const Reference
<rendering::XCanvas
>& rxCanvas
,
517 const geometry::RealSize2D
& rSize
,
518 const PresenterTheme::SharedFontDescriptor
& rpFont
,
519 const rendering::ViewState
& rViewState
,
520 rendering::RenderState
& rRenderState
,
521 const double nTopOffset
,
522 const double nClipTop
,
523 const double nClipBottom
)
525 if (mnLineHeight
<= 0)
528 sal_Int8
nTextDirection (GetTextDirection());
530 const double nSavedM12 (rRenderState
.AffineTransform
.m12
);
532 if ( ! IsTextReferencePointLeft())
533 rRenderState
.AffineTransform
.m02
+= rSize
.Width
;
535 #ifdef SHOW_CHARACTER_BOXES
536 for (sal_Int32 nIndex
=0,nCount
=maLines
.size();
540 Line
& rLine (maLines
[nIndex
]);
541 rLine
.ProvideLayoutedLine(msParagraphText
, rpFont
, nTextDirection
);
545 for (sal_Int32 nIndex
=0,nCount
=maLines
.size();
547 ++nIndex
, rRenderState
.AffineTransform
.m12
+= mnLineHeight
)
549 Line
& rLine (maLines
[nIndex
]);
551 // Paint only visible lines.
552 const double nLineTop
= rLine
.mnBaseLine
- mnAscent
- nTopOffset
;
553 if (nLineTop
+ mnLineHeight
< nClipTop
)
555 else if (nLineTop
> nClipBottom
)
557 rLine
.ProvideLayoutedLine(msParagraphText
, rpFont
, nTextDirection
);
559 rRenderState
.AffineTransform
.m12
= nSavedM12
+ rLine
.mnBaseLine
;
561 rxCanvas
->drawTextLayout (
562 rLine
.mxLayoutedLine
,
566 rRenderState
.AffineTransform
.m12
= nSavedM12
;
568 if ( ! IsTextReferencePointLeft())
569 rRenderState
.AffineTransform
.m02
-= rSize
.Width
;
572 void PresenterTextParagraph::Format (
575 const PresenterTheme::SharedFontDescriptor
& rpFont
)
577 // Make sure that the text view is in a valid and sane state.
578 if ( ! mxBreakIterator
.is() || ! mxScriptTypeDetector
.is())
582 if ( ! rpFont
|| ! rpFont
->mxFont
.is())
585 sal_Int32
nPosition (0);
592 mnVerticalOffset
= nY
;
593 maWordBoundaries
.clear();
594 maWordBoundaries
.push_back(0);
596 const rendering::FontMetrics
aMetrics (rpFont
->mxFont
->getFontMetrics());
597 mnAscent
= aMetrics
.Ascent
;
598 mnDescent
= aMetrics
.Descent
;
599 mnLineHeight
= aMetrics
.Ascent
+ aMetrics
.Descent
+ aMetrics
.ExternalLeading
;
601 i18n::Boundary
aCurrentLine(0,0);
604 const i18n::Boundary aWordBoundary
= mxBreakIterator
->nextWord(
608 i18n::WordType::ANYWORD_IGNOREWHITESPACES
);
609 AddWord(nWidth
, aCurrentLine
, aWordBoundary
.startPos
, rpFont
);
611 // Remember the new word boundary for caret travelling by words.
612 // Prevent duplicates.
613 if (aWordBoundary
.startPos
> maWordBoundaries
.back())
614 maWordBoundaries
.push_back(aWordBoundary
.startPos
);
616 if (aWordBoundary
.endPos
>aWordBoundary
.startPos
)
617 AddWord(nWidth
, aCurrentLine
, aWordBoundary
.endPos
, rpFont
);
619 if (aWordBoundary
.startPos
<0 || aWordBoundary
.endPos
<0)
621 if (nPosition
>= aWordBoundary
.endPos
)
623 nPosition
= aWordBoundary
.endPos
;
626 if (aCurrentLine
.endPos
>aCurrentLine
.startPos
)
627 AddLine(aCurrentLine
);
631 sal_Int32
PresenterTextParagraph::GetWordBoundary(
632 const sal_Int32 nLocalCharacterIndex
,
633 const sal_Int32 nDistance
)
635 OSL_ASSERT(nDistance
==-1 || nDistance
==+1);
637 if (nLocalCharacterIndex
< 0)
639 // The caller asked for the start or end position of the paragraph.
643 return GetCharacterCount();
646 sal_Int32
nIndex (0);
647 for (sal_Int32
nCount (maWordBoundaries
.size()); nIndex
<nCount
; ++nIndex
)
649 if (maWordBoundaries
[nIndex
] >= nLocalCharacterIndex
)
651 // When inside the word (not at its start or end) then
652 // first move to the start or end before going the previous or
654 if (maWordBoundaries
[nIndex
] > nLocalCharacterIndex
)
665 else if (sal_uInt32(nIndex
)>=maWordBoundaries
.size())
668 return maWordBoundaries
[nIndex
];
671 sal_Int32
PresenterTextParagraph::GetCaretPosition() const
673 if (mpCaret
&& mpCaret
->GetParagraphIndex()==mnParagraphIndex
)
674 return mpCaret
->GetCharacterIndex();
679 void PresenterTextParagraph::SetCaretPosition (const sal_Int32 nPosition
) const
681 if (mpCaret
&& mpCaret
->GetParagraphIndex()==mnParagraphIndex
)
682 return mpCaret
->SetPosition(mnParagraphIndex
, nPosition
);
685 void PresenterTextParagraph::SetOrigin (const double nXOrigin
, const double nYOrigin
)
687 mnXOrigin
= nXOrigin
;
688 mnYOrigin
= nYOrigin
;
691 awt::Point
PresenterTextParagraph::GetRelativeLocation() const
694 sal_Int32(mnXOrigin
),
695 sal_Int32(mnYOrigin
+ mnVerticalOffset
));
698 awt::Size
PresenterTextParagraph::GetSize()
702 sal_Int32(GetTotalTextHeight()));
705 void PresenterTextParagraph::AddWord (
707 i18n::Boundary
& rCurrentLine
,
708 const sal_Int32 nWordBoundary
,
709 const PresenterTheme::SharedFontDescriptor
& rpFont
)
711 sal_Int32
nLineStart (0);
712 if ( ! maLines
.empty())
713 nLineStart
= rCurrentLine
.startPos
;
715 const OUString
sLineCandidate (
716 msParagraphText
.copy(nLineStart
, nWordBoundary
-nLineStart
));
718 css::geometry::RealRectangle2D
aLineBox (
719 PresenterCanvasHelper::GetTextBoundingBox (
723 const double nLineWidth (aLineBox
.X2
- aLineBox
.X1
);
725 if (nLineWidth
>= nWidth
)
727 // Add new line with a single word (so far).
728 AddLine(rCurrentLine
);
730 rCurrentLine
.endPos
= nWordBoundary
;
733 void PresenterTextParagraph::AddLine (
734 i18n::Boundary
& rCurrentLine
)
736 Line
aLine (rCurrentLine
.startPos
, rCurrentLine
.endPos
);
738 // Find the start and end of the line with respect to cells.
739 if (maLines
.size() > 0)
741 aLine
.mnLineStartCellIndex
= maLines
.back().mnLineEndCellIndex
;
742 aLine
.mnBaseLine
= maLines
.back().mnBaseLine
+ mnLineHeight
;
746 aLine
.mnLineStartCellIndex
= 0;
747 aLine
.mnBaseLine
= mnVerticalOffset
+ mnAscent
;
749 sal_Int32
nCellIndex (aLine
.mnLineStartCellIndex
);
751 for ( ; nCellIndex
<sal_Int32(maCells
.size()); ++nCellIndex
)
753 const Cell
& rCell (maCells
[nCellIndex
]);
754 if (rCell
.mnCharacterIndex
+rCell
.mnCharacterCount
> aLine
.mnLineEndCharacterIndex
)
756 nWidth
+= rCell
.mnCellWidth
;
758 aLine
.mnLineEndCellIndex
= nCellIndex
;
759 aLine
.mnWidth
= nWidth
;
761 maLines
.push_back(aLine
);
763 rCurrentLine
.startPos
= rCurrentLine
.endPos
;
766 double PresenterTextParagraph::GetTotalTextHeight()
768 return maLines
.size() * mnLineHeight
;
771 void PresenterTextParagraph::SetCharacterOffset (const sal_Int32 nCharacterOffset
)
773 mnCharacterOffset
= nCharacterOffset
;
776 sal_Int32
PresenterTextParagraph::GetCharacterCount() const
778 return msParagraphText
.getLength();
781 sal_Unicode
PresenterTextParagraph::GetCharacter (
782 const sal_Int32 nGlobalCharacterIndex
) const
784 if (nGlobalCharacterIndex
<mnCharacterOffset
785 || nGlobalCharacterIndex
>=mnCharacterOffset
+msParagraphText
.getLength())
787 return sal_Unicode();
791 return msParagraphText
[nGlobalCharacterIndex
- mnCharacterOffset
];
795 OUString
PresenterTextParagraph::GetText() const
797 return msParagraphText
;
800 TextSegment
PresenterTextParagraph::GetTextSegment (
801 const sal_Int32 nOffset
,
802 const sal_Int32 nIndex
,
803 const sal_Int16 nTextType
) const
807 case AccessibleTextType::PARAGRAPH
:
811 mnCharacterOffset
+msParagraphText
.getLength());
813 case AccessibleTextType::SENTENCE
:
814 if (mxBreakIterator
.is())
816 const sal_Int32
nStart (mxBreakIterator
->beginOfSentence(
817 msParagraphText
, nIndex
-mnCharacterOffset
, lang::Locale()));
818 const sal_Int32
nEnd (mxBreakIterator
->endOfSentence(
819 msParagraphText
, nIndex
-mnCharacterOffset
, lang::Locale()));
822 msParagraphText
.copy(nStart
, nEnd
-nStart
),
823 nStart
+mnCharacterOffset
,
824 nEnd
+mnCharacterOffset
);
828 case AccessibleTextType::WORD
:
829 if (mxBreakIterator
.is())
830 return GetWordTextSegment(nOffset
, nIndex
);
833 case AccessibleTextType::LINE
:
835 for (::std::vector
<Line
>::const_iterator
836 iLine(maLines
.begin()),
841 if (nIndex
< iLine
->mnLineEndCharacterIndex
)
844 msParagraphText
.copy(
845 iLine
->mnLineStartCharacterIndex
,
846 iLine
->mnLineEndCharacterIndex
- iLine
->mnLineStartCharacterIndex
),
847 iLine
->mnLineStartCharacterIndex
,
848 iLine
->mnLineEndCharacterIndex
);
854 // Handle GLYPH and ATTRIBUTE_RUN like CHARACTER because we can not
855 // do better at the moment.
856 case AccessibleTextType::CHARACTER
:
857 case AccessibleTextType::GLYPH
:
858 case AccessibleTextType::ATTRIBUTE_RUN
:
859 return CreateTextSegment(nIndex
+nOffset
, nIndex
+nOffset
+1);
862 return TextSegment(OUString(), 0,0);
865 TextSegment
PresenterTextParagraph::GetWordTextSegment (
866 const sal_Int32 nOffset
,
867 const sal_Int32 nIndex
) const
869 sal_Int32
nCurrentOffset (nOffset
);
870 sal_Int32
nCurrentIndex (nIndex
);
872 i18n::Boundary aWordBoundary
;
873 if (nCurrentOffset
== 0)
874 aWordBoundary
= mxBreakIterator
->getWordBoundary(
878 i18n::WordType::ANYWORD_IGNOREWHITESPACES
,
880 else if (nCurrentOffset
< 0)
882 while (nCurrentOffset
<0 && nCurrentIndex
>0)
884 aWordBoundary
= mxBreakIterator
->previousWord(
888 i18n::WordType::ANYWORD_IGNOREWHITESPACES
);
889 nCurrentIndex
= aWordBoundary
.startPos
;
895 while (nCurrentOffset
>0 && nCurrentIndex
<=GetCharacterCount())
897 aWordBoundary
= mxBreakIterator
->nextWord(
901 i18n::WordType::ANYWORD_IGNOREWHITESPACES
);
902 nCurrentIndex
= aWordBoundary
.endPos
;
907 return CreateTextSegment(aWordBoundary
.startPos
, aWordBoundary
.endPos
);
910 TextSegment
PresenterTextParagraph::CreateTextSegment (
911 sal_Int32 nStartIndex
,
912 sal_Int32 nEndIndex
) const
914 if (nEndIndex
<= nStartIndex
)
921 msParagraphText
.copy(nStartIndex
, nEndIndex
-nStartIndex
),
926 awt::Rectangle
PresenterTextParagraph::GetCharacterBounds (
927 sal_Int32 nGlobalCharacterIndex
,
928 const bool bCaretBox
)
930 // Find the line that contains the requested character and accumulate
931 // the previous line heights.
932 double nX (mnXOrigin
);
933 double nY (mnYOrigin
+ mnVerticalOffset
+ mnAscent
);
934 const sal_Int8
nTextDirection (GetTextDirection());
935 for (sal_Int32 nLineIndex
=0,nLineCount
=maLines
.size();
936 nLineIndex
<nLineCount
;
937 ++nLineIndex
, nY
+=mnLineHeight
)
939 Line
& rLine (maLines
[nLineIndex
]);
940 // Skip lines before the indexed character.
941 if (nGlobalCharacterIndex
>= rLine
.mnLineEndCharacterIndex
)
942 // When in the last line then allow the index past the last char.
943 if (nLineIndex
<nLineCount
-1)
946 rLine
.ProvideCellBoxes();
948 const sal_Int32
nCellIndex (nGlobalCharacterIndex
- rLine
.mnLineStartCharacterIndex
);
950 // The cell bounding box is defined relative to the origin of
951 // the current line. Therefore we have to add the absolute
952 // position of the line.
953 geometry::RealRectangle2D
rCellBox (rLine
.maCellBoxes
[
954 ::std::min(nCellIndex
, rLine
.maCellBoxes
.getLength()-1)]);
956 double nLeft
= nX
+ rCellBox
.X1
;
957 double nRight
= nX
+ rCellBox
.X2
;
958 if (nTextDirection
== rendering::TextDirection::WEAK_RIGHT_TO_LEFT
)
960 const double nOldRight (nRight
);
961 nRight
= rLine
.mnWidth
- nLeft
;
962 nLeft
= rLine
.mnWidth
- nOldRight
;
964 double nTop (nY
+ rCellBox
.Y1
);
965 double nBottom (nY
+ rCellBox
.Y2
);
968 nTop
= nTop
- rCellBox
.Y1
- mnAscent
;
969 nBottom
= nTop
+ mnLineHeight
;
970 if (nCellIndex
>= rLine
.maCellBoxes
.getLength())
978 nTop
= nTop
- rCellBox
.Y1
- mnAscent
;
979 nBottom
= nTop
+ mnAscent
+ mnDescent
;
981 const sal_Int32 nX1
= sal_Int32(floor(nLeft
));
982 const sal_Int32 nY1
= sal_Int32(floor(nTop
));
983 const sal_Int32 nX2
= sal_Int32(ceil(nRight
));
984 const sal_Int32 nY2
= sal_Int32(ceil(nBottom
));
986 return awt::Rectangle(nX1
,nY1
,nX2
-nX1
+1,nY2
-nY1
+1);
989 // We are still here. That means that the given index lies past the
990 // last character in the paragraph.
991 // Return an empty box that lies past the last character. Better than nothing.
992 return awt::Rectangle(sal_Int32(nX
+0.5), sal_Int32(nY
+0.5), 0, 0);
995 sal_Int8
PresenterTextParagraph::GetTextDirection() const
997 // Find first portion that has a non-neutral text direction.
998 sal_Int32
nPosition (0);
999 sal_Int32
nTextLength (msParagraphText
.getLength());
1000 while (nPosition
< nTextLength
)
1002 const sal_Int16
nScriptDirection (
1003 mxScriptTypeDetector
->getScriptDirection(
1004 msParagraphText
, nPosition
, i18n::ScriptDirection::NEUTRAL
));
1005 switch (nScriptDirection
)
1007 case i18n::ScriptDirection::NEUTRAL
:
1008 // continue looping.
1010 case i18n::ScriptDirection::LEFT_TO_RIGHT
:
1011 return rendering::TextDirection::WEAK_LEFT_TO_RIGHT
;
1013 case i18n::ScriptDirection::RIGHT_TO_LEFT
:
1014 return rendering::TextDirection::WEAK_RIGHT_TO_LEFT
;
1017 nPosition
= mxScriptTypeDetector
->endOfScriptDirection(
1018 msParagraphText
, nPosition
, nScriptDirection
);
1021 // All text in paragraph is neutral. Fall back on writing mode taken
1022 // from the XText (which may not be properly initialized.)
1023 sal_Int8
nTextDirection(rendering::TextDirection::WEAK_LEFT_TO_RIGHT
);
1024 switch(mnWritingMode
)
1026 case text::WritingMode2::LR_TB
:
1027 nTextDirection
= rendering::TextDirection::WEAK_LEFT_TO_RIGHT
;
1030 case text::WritingMode2::RL_TB
:
1031 nTextDirection
= rendering::TextDirection::WEAK_RIGHT_TO_LEFT
;
1035 case text::WritingMode2::TB_RL
:
1036 case text::WritingMode2::TB_LR
:
1037 // Can not handle this. Use default and hope for the best.
1040 return nTextDirection
;
1043 bool PresenterTextParagraph::IsTextReferencePointLeft() const
1045 return mnWritingMode
!= text::WritingMode2::RL_TB
;
1048 void PresenterTextParagraph::SetupCellArray (
1049 const PresenterTheme::SharedFontDescriptor
& rpFont
)
1053 if ( ! rpFont
|| ! rpFont
->mxFont
.is())
1056 sal_Int32
nPosition (0);
1057 sal_Int32
nIndex (0);
1058 const sal_Int32
nTextLength (msParagraphText
.getLength());
1059 const sal_Int8
nTextDirection (GetTextDirection());
1060 while (nPosition
< nTextLength
)
1062 const sal_Int32
nNewPosition (mxBreakIterator
->nextCharacters(
1066 i18n::CharacterIteratorMode::SKIPCELL
,
1070 rendering::StringContext
aContext (msParagraphText
, nPosition
, nNewPosition
-nPosition
);
1071 Reference
<rendering::XTextLayout
> xLayout (
1072 rpFont
->mxFont
->createTextLayout(aContext
, nTextDirection
, 0));
1073 css::geometry::RealRectangle2D
aCharacterBox (xLayout
->queryTextBounds());
1075 maCells
.push_back(Cell(
1077 nNewPosition
-nPosition
,
1078 aCharacterBox
.X2
-aCharacterBox
.X1
));
1080 nPosition
= nNewPosition
;
1084 //===== PresenterTextCaret ================================================----
1086 PresenterTextCaret::PresenterTextCaret (
1087 const ::boost::function
<css::awt::Rectangle(const sal_Int32
,const sal_Int32
)>& rCharacterBoundsAccess
,
1088 const ::boost::function
<void(const css::awt::Rectangle
&)>& rInvalidator
)
1089 : mnParagraphIndex(-1),
1090 mnCharacterIndex(-1),
1091 mnCaretBlinkTaskId(0),
1092 mbIsCaretVisible(false),
1093 maCharacterBoundsAccess(rCharacterBoundsAccess
),
1094 maInvalidator(rInvalidator
),
1100 PresenterTextCaret::~PresenterTextCaret()
1105 void PresenterTextCaret::ShowCaret()
1107 if (mnCaretBlinkTaskId
== 0)
1109 mnCaretBlinkTaskId
= PresenterTimer::ScheduleRepeatedTask (
1110 ::boost::bind(&PresenterTextCaret::InvertCaret
, this),
1111 CaretBlinkIntervall
,
1112 CaretBlinkIntervall
);
1114 mbIsCaretVisible
= true;
1117 void PresenterTextCaret::HideCaret()
1119 if (mnCaretBlinkTaskId
!= 0)
1121 PresenterTimer::CancelTask(mnCaretBlinkTaskId
);
1122 mnCaretBlinkTaskId
= 0;
1124 mbIsCaretVisible
= false;
1125 // Reset the caret position.
1126 mnParagraphIndex
= -1;
1127 mnCharacterIndex
= -1;
1132 void PresenterTextCaret::SetPosition (
1133 const sal_Int32 nParagraphIndex
,
1134 const sal_Int32 nCharacterIndex
)
1136 if (mnParagraphIndex
!= nParagraphIndex
1137 || mnCharacterIndex
!= nCharacterIndex
)
1139 if (mnParagraphIndex
>= 0)
1140 maInvalidator(maCaretBounds
);
1142 const sal_Int32
nOldParagraphIndex (mnParagraphIndex
);
1143 const sal_Int32
nOldCharacterIndex (mnCharacterIndex
);
1144 mnParagraphIndex
= nParagraphIndex
;
1145 mnCharacterIndex
= nCharacterIndex
;
1146 maCaretBounds
= maCharacterBoundsAccess(mnParagraphIndex
, mnCharacterIndex
);
1147 if (mnParagraphIndex
>= 0)
1152 if (mnParagraphIndex
>= 0)
1153 maInvalidator(maCaretBounds
);
1166 void PresenterTextCaret::SetCaretMotionBroadcaster (
1167 const ::boost::function
<void(sal_Int32
,sal_Int32
,sal_Int32
,sal_Int32
)>& rBroadcaster
)
1169 maBroadcaster
= rBroadcaster
;
1172 css::awt::Rectangle
PresenterTextCaret::GetBounds() const
1174 return maCaretBounds
;
1177 void PresenterTextCaret::InvertCaret()
1179 mbIsCaretVisible
= !mbIsCaretVisible
;
1180 if (mnParagraphIndex
>= 0)
1181 maInvalidator(maCaretBounds
);
1184 //===== PresenterTextParagraph::Cell ==========================================
1186 PresenterTextParagraph::Cell::Cell (
1187 const sal_Int32 nCharacterIndex
,
1188 const sal_Int32 nCharacterCount
,
1189 const double nCellWidth
)
1190 : mnCharacterIndex(nCharacterIndex
),
1191 mnCharacterCount(nCharacterCount
),
1192 mnCellWidth(nCellWidth
)
1196 //===== PresenterTextParagraph::Line ==========================================
1198 PresenterTextParagraph::Line::Line (
1199 const sal_Int32 nLineStartCharacterIndex
,
1200 const sal_Int32 nLineEndCharacterIndex
)
1201 : mnLineStartCharacterIndex(nLineStartCharacterIndex
),
1202 mnLineEndCharacterIndex(nLineEndCharacterIndex
),
1203 mnLineStartCellIndex(-1), mnLineEndCellIndex(-1),
1205 mnBaseLine(0), mnWidth(0),
1210 void PresenterTextParagraph::Line::ProvideCellBoxes()
1212 if ( ! IsEmpty() && maCellBoxes
.getLength()==0)
1214 if (mxLayoutedLine
.is())
1215 maCellBoxes
= mxLayoutedLine
->queryInkMeasures();
1218 OSL_ASSERT(mxLayoutedLine
.is());
1223 void PresenterTextParagraph::Line::ProvideLayoutedLine (
1224 const OUString
& rsParagraphText
,
1225 const PresenterTheme::SharedFontDescriptor
& rpFont
,
1226 const sal_Int8 nTextDirection
)
1228 if ( ! mxLayoutedLine
.is())
1230 const rendering::StringContext
aContext (
1232 mnLineStartCharacterIndex
,
1233 mnLineEndCharacterIndex
- mnLineStartCharacterIndex
);
1235 mxLayoutedLine
= rpFont
->mxFont
->createTextLayout(
1242 bool PresenterTextParagraph::Line::IsEmpty() const
1244 return mnLineStartCharacterIndex
>= mnLineEndCharacterIndex
;
1247 } } // end of namespace ::sdext::presenter
1249 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */