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