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