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