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