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