1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include <vcl/settings.hxx>
21 #include "PresenterHelpView.hxx"
22 #include "PresenterButton.hxx"
23 #include "PresenterCanvasHelper.hxx"
24 #include "PresenterGeometryHelper.hxx"
25 #include <com/sun/star/awt/XWindowPeer.hpp>
26 #include <com/sun/star/container/XNameAccess.hpp>
27 #include <com/sun/star/drawing/framework/XConfigurationController.hpp>
28 #include <com/sun/star/drawing/framework/XControllerManager.hpp>
29 #include <com/sun/star/rendering/CompositeOperation.hpp>
30 #include <com/sun/star/rendering/TextDirection.hpp>
31 #include <com/sun/star/util/Color.hpp>
36 using namespace ::com::sun::star
;
37 using namespace ::com::sun::star::uno
;
38 using namespace ::com::sun::star::drawing::framework
;
41 namespace sdext
{ namespace presenter
{
44 const static sal_Int32
gnHorizontalGap (20);
45 const static sal_Int32
gnVerticalBorder (30);
46 const static sal_Int32
gnVerticalButtonPadding (12);
53 const OUString
& rsLine
,
54 const css::uno::Reference
<css::rendering::XCanvasFont
>& rxFont
);
58 geometry::RealSize2D maSize
;
59 double mnVerticalOffset
;
61 void CalculateSize (const css::uno::Reference
<css::rendering::XCanvasFont
>& rxFont
);
64 class LineDescriptorList
68 const OUString
& rsText
,
69 const css::uno::Reference
<css::rendering::XCanvasFont
>& rxFont
,
70 const sal_Int32 nMaximalWidth
);
73 const css::uno::Reference
<css::rendering::XCanvasFont
>& rxFont
,
74 const sal_Int32 nMaximalWidth
);
77 const Reference
<rendering::XCanvas
>& rxCanvas
,
78 const geometry::RealRectangle2D
& rBBox
,
79 const bool bFlushLeft
,
80 const rendering::ViewState
& rViewState
,
81 rendering::RenderState
& rRenderState
,
82 const css::uno::Reference
<css::rendering::XCanvasFont
>& rxFont
) const;
83 double GetHeight() const;
86 const OUString msText
;
87 std::shared_ptr
<vector
<LineDescriptor
> > mpLineDescriptors
;
89 static void SplitText (const OUString
& rsText
, vector
<OUString
>& rTextParts
);
91 const vector
<OUString
>& rTextParts
,
92 const css::uno::Reference
<css::rendering::XCanvasFont
>& rxFont
,
93 const sal_Int32 nMaximalWidth
);
100 const OUString
& rsLeftText
,
101 const OUString
& rsRightText
,
102 const css::uno::Reference
<css::rendering::XCanvasFont
>& rxFont
,
103 const sal_Int32 nMaximalWidth
);
104 Block(const Block
&) = delete;
105 Block
& operator=(const Block
&) = delete;
107 const css::uno::Reference
<css::rendering::XCanvasFont
>& rxFont
,
108 const sal_Int32 nMaximalWidth
);
110 LineDescriptorList maLeft
;
111 LineDescriptorList maRight
;
113 } // end of anonymous namespace
115 class PresenterHelpView::TextContainer
: public vector
<std::shared_ptr
<Block
> >
119 PresenterHelpView::PresenterHelpView (
120 const Reference
<uno::XComponentContext
>& rxContext
,
121 const Reference
<XResourceId
>& rxViewId
,
122 const Reference
<frame::XController
>& rxController
,
123 const ::rtl::Reference
<PresenterController
>& rpPresenterController
)
124 : PresenterHelpViewInterfaceBase(m_aMutex
),
125 mxComponentContext(rxContext
),
130 mpPresenterController(rpPresenterController
),
139 // Get the content window via the pane anchor.
140 Reference
<XControllerManager
> xCM (rxController
, UNO_QUERY_THROW
);
141 Reference
<XConfigurationController
> xCC (
142 xCM
->getConfigurationController(), UNO_SET_THROW
);
143 mxPane
.set(xCC
->getResource(rxViewId
->getAnchor()), UNO_QUERY_THROW
);
145 mxWindow
= mxPane
->getWindow();
148 mxWindow
->addWindowListener(this);
149 mxWindow
->addPaintListener(this);
150 Reference
<awt::XWindowPeer
> xPeer (mxWindow
, UNO_QUERY
);
152 xPeer
->setBackground(util::Color(0xff000000));
153 mxWindow
->setVisible(true);
155 if (mpPresenterController
.is())
157 mpFont
= mpPresenterController
->GetViewFont(mxViewId
->getResourceURL());
158 if (mpFont
.get() != nullptr)
160 mpFont
->PrepareFont(mxCanvas
);
164 // Create the close button.
165 mpCloseButton
= PresenterButton::Create(
167 mpPresenterController
,
168 mpPresenterController
->GetTheme(),
176 catch (RuntimeException
&)
184 PresenterHelpView::~PresenterHelpView()
188 void SAL_CALL
PresenterHelpView::disposing()
192 if (mpCloseButton
.is())
194 Reference
<lang::XComponent
> xComponent (
195 static_cast<XWeak
*>(mpCloseButton
.get()), UNO_QUERY
);
196 mpCloseButton
= nullptr;
198 xComponent
->dispose();
203 mxWindow
->removeWindowListener(this);
204 mxWindow
->removePaintListener(this);
208 //----- lang::XEventListener --------------------------------------------------
210 void SAL_CALL
PresenterHelpView::disposing (const lang::EventObject
& rEventObject
)
212 if (rEventObject
.Source
== mxCanvas
)
216 else if (rEventObject
.Source
== mxWindow
)
223 //----- XWindowListener -------------------------------------------------------
225 void SAL_CALL
PresenterHelpView::windowResized (const awt::WindowEvent
&)
231 void SAL_CALL
PresenterHelpView::windowMoved (const awt::WindowEvent
&)
236 void SAL_CALL
PresenterHelpView::windowShown (const lang::EventObject
&)
242 void SAL_CALL
PresenterHelpView::windowHidden (const lang::EventObject
&)
247 //----- XPaintListener --------------------------------------------------------
249 void SAL_CALL
PresenterHelpView::windowPaint (const css::awt::PaintEvent
& rEvent
)
251 Paint(rEvent
.UpdateRect
);
254 void PresenterHelpView::Paint (const awt::Rectangle
& rUpdateBox
)
257 if ( ! mxCanvas
.is())
261 const awt::Rectangle
aWindowBox (mxWindow
->getPosSize());
262 mpPresenterController
->GetCanvasHelper()->Paint(
263 mpPresenterController
->GetViewBackground(mxViewId
->getResourceURL()),
266 awt::Rectangle(0,0,aWindowBox
.Width
,aWindowBox
.Height
),
269 // Paint vertical divider.
271 rendering::ViewState
aViewState(
272 geometry::AffineMatrix2D(1,0,0, 0,1,0),
273 PresenterGeometryHelper::CreatePolygon(rUpdateBox
, mxCanvas
->getDevice()));
275 rendering::RenderState
aRenderState (
276 geometry::AffineMatrix2D(1,0,0, 0,1,0),
279 rendering::CompositeOperation::SOURCE
);
280 PresenterCanvasHelper::SetDeviceColor(aRenderState
, mpFont
->mnColor
);
283 geometry::RealPoint2D((aWindowBox
.Width
/2.0), gnVerticalBorder
),
284 geometry::RealPoint2D((aWindowBox
.Width
/2.0), mnSeparatorY
- gnVerticalBorder
),
288 // Paint the horizontal separator.
290 geometry::RealPoint2D(0, mnSeparatorY
),
291 geometry::RealPoint2D(aWindowBox
.Width
, mnSeparatorY
),
296 double nY (gnVerticalBorder
);
297 for (const auto& rxBlock
: *mpTextContainer
)
299 sal_Int32 LeftX1
= gnHorizontalGap
;
300 sal_Int32 LeftX2
= aWindowBox
.Width
/2 - gnHorizontalGap
;
301 sal_Int32 RightX1
= aWindowBox
.Width
/2 + gnHorizontalGap
;
302 sal_Int32 RightX2
= aWindowBox
.Width
- gnHorizontalGap
;
303 /* check whether RTL interface or not
304 then replace the windowbox position */
305 if(AllSettings::GetLayoutRTL())
307 LeftX1
= aWindowBox
.Width
/2 + gnHorizontalGap
;
308 LeftX2
= aWindowBox
.Width
- gnHorizontalGap
;
309 RightX1
= gnHorizontalGap
;
310 RightX2
= aWindowBox
.Width
/2 - gnHorizontalGap
;
312 const double nLeftHeight (
313 rxBlock
->maLeft
.Paint(mxCanvas
,
314 geometry::RealRectangle2D(
318 aWindowBox
.Height
- gnVerticalBorder
),
323 const double nRightHeight (
324 rxBlock
->maRight
.Paint(mxCanvas
,
325 geometry::RealRectangle2D(
329 aWindowBox
.Height
- gnVerticalBorder
),
335 nY
+= ::std::max(nLeftHeight
,nRightHeight
);
338 Reference
<rendering::XSpriteCanvas
> xSpriteCanvas (mxCanvas
, UNO_QUERY
);
339 if (xSpriteCanvas
.is())
340 xSpriteCanvas
->updateScreen(false);
343 void PresenterHelpView::ReadHelpStrings()
345 mpTextContainer
.reset(new TextContainer
);
346 PresenterConfigurationAccess
aConfiguration (
348 "/org.openoffice.Office.PresenterScreen/",
349 PresenterConfigurationAccess::READ_ONLY
);
350 Reference
<container::XNameAccess
> xStrings (
351 aConfiguration
.GetConfigurationNode("PresenterScreenSettings/HelpView/HelpStrings"),
353 PresenterConfigurationAccess::ForAll(
355 [this](OUString
const&, uno::Reference
<beans::XPropertySet
> const& xProps
)
357 return this->ProcessString(xProps
);
361 void PresenterHelpView::ProcessString (
362 const Reference
<beans::XPropertySet
>& rsProperties
)
364 if ( ! rsProperties
.is())
368 PresenterConfigurationAccess::GetProperty(rsProperties
, "Left") >>= sLeftText
;
370 PresenterConfigurationAccess::GetProperty(rsProperties
, "Right") >>= sRightText
;
371 mpTextContainer
->push_back(
372 std::make_shared
<Block
>(
373 sLeftText
, sRightText
, mpFont
->mxFont
, mnMaximalWidth
));
376 void PresenterHelpView::CheckFontSize()
378 if (mpFont
.get() == nullptr)
381 sal_Int32
nBestSize (6);
383 // Scaling down and then reformatting can cause the text to be too large
384 // still. So do this again and again until the text size is
385 // small enough. Restrict the number of loops.
386 for (int nLoopCount
=0; nLoopCount
<5; ++nLoopCount
)
388 double nY
= std::accumulate(mpTextContainer
->begin(), mpTextContainer
->end(), double(0),
389 [](const double& sum
, const std::shared_ptr
<Block
>& rxBlock
) {
390 return sum
+ std::max(
391 rxBlock
->maLeft
.GetHeight(),
392 rxBlock
->maRight
.GetHeight());
395 const double nHeightDifference (nY
- (mnSeparatorY
-gnVerticalBorder
));
396 if (nHeightDifference
<= 0 && nHeightDifference
> -50)
398 // We have found a good font size that is large and leaves not
399 // too much space below the help text.
403 // Use a simple linear transformation to calculate initial guess of
404 // a size that lets all help text be shown inside the window.
405 const double nScale (double(mnSeparatorY
-gnVerticalBorder
) / nY
);
406 if (nScale
> 1.0 && nScale
< 1.05)
409 sal_Int32
nFontSizeGuess (sal_Int32(mpFont
->mnSize
* nScale
));
410 if (nHeightDifference
<=0 && mpFont
->mnSize
>nBestSize
)
411 nBestSize
= mpFont
->mnSize
;
412 mpFont
->mnSize
= nFontSizeGuess
;
413 mpFont
->mxFont
= nullptr;
414 mpFont
->PrepareFont(mxCanvas
);
417 for (auto& rxBlock
: *mpTextContainer
)
418 rxBlock
->Update(mpFont
->mxFont
, mnMaximalWidth
);
421 if (nBestSize
!= mpFont
->mnSize
)
423 mpFont
->mnSize
= nBestSize
;
424 mpFont
->mxFont
= nullptr;
425 mpFont
->PrepareFont(mxCanvas
);
428 for (auto& rxBlock
: *mpTextContainer
)
430 rxBlock
->Update(mpFont
->mxFont
, mnMaximalWidth
);
435 //----- XResourceId -----------------------------------------------------------
437 Reference
<XResourceId
> SAL_CALL
PresenterHelpView::getResourceId()
443 sal_Bool SAL_CALL
PresenterHelpView::isAnchorOnly()
449 void PresenterHelpView::ProvideCanvas()
451 if ( ! mxCanvas
.is() && mxPane
.is())
453 mxCanvas
= mxPane
->getCanvas();
454 if ( ! mxCanvas
.is())
456 Reference
<lang::XComponent
> xComponent (mxCanvas
, UNO_QUERY
);
458 xComponent
->addEventListener(static_cast<awt::XPaintListener
*>(this));
460 if (mpCloseButton
.is())
461 mpCloseButton
->SetCanvas(mxCanvas
, mxWindow
);
465 void PresenterHelpView::Resize()
467 if (!(mpCloseButton
.get() != nullptr && mxWindow
.is()))
470 const awt::Rectangle
aWindowBox (mxWindow
->getPosSize());
471 mnMaximalWidth
= (mxWindow
->getPosSize().Width
- 4*gnHorizontalGap
) / 2;
473 // Place vertical separator.
474 mnSeparatorY
= aWindowBox
.Height
475 - mpCloseButton
->GetSize().Height
- gnVerticalButtonPadding
;
477 mpCloseButton
->SetCenter(geometry::RealPoint2D(
478 aWindowBox
.Width
/2.0,
479 aWindowBox
.Height
- mpCloseButton
->GetSize().Height
/2.0));
484 void PresenterHelpView::ThrowIfDisposed()
486 if (rBHelper
.bDisposed
|| rBHelper
.bInDispose
)
488 throw lang::DisposedException (
489 "PresenterHelpView has been already disposed",
490 static_cast<uno::XWeak
*>(this));
494 //===== LineDescriptor =========================================================
498 LineDescriptor::LineDescriptor()
505 void LineDescriptor::AddPart (
506 const OUString
& rsLine
,
507 const css::uno::Reference
<css::rendering::XCanvasFont
>& rxFont
)
511 CalculateSize(rxFont
);
514 bool LineDescriptor::IsEmpty() const
516 return msLine
.isEmpty();
519 void LineDescriptor::CalculateSize (
520 const css::uno::Reference
<css::rendering::XCanvasFont
>& rxFont
)
522 OSL_ASSERT(rxFont
.is());
524 rendering::StringContext
aContext (msLine
, 0, msLine
.getLength());
525 Reference
<rendering::XTextLayout
> xLayout (
526 rxFont
->createTextLayout(aContext
, rendering::TextDirection::WEAK_LEFT_TO_RIGHT
, 0));
527 const geometry::RealRectangle2D
aTextBBox (xLayout
->queryTextBounds());
528 maSize
= css::geometry::RealSize2D(aTextBBox
.X2
- aTextBBox
.X1
, aTextBBox
.Y2
- aTextBBox
.Y1
);
529 mnVerticalOffset
= aTextBBox
.Y2
;
532 } // end of anonymous namespace
534 //===== LineDescriptorList ====================================================
538 LineDescriptorList::LineDescriptorList (
539 const OUString
& rsText
,
540 const css::uno::Reference
<css::rendering::XCanvasFont
>& rxFont
,
541 const sal_Int32 nMaximalWidth
)
544 Update(rxFont
, nMaximalWidth
);
547 double LineDescriptorList::Paint(
548 const Reference
<rendering::XCanvas
>& rxCanvas
,
549 const geometry::RealRectangle2D
& rBBox
,
550 const bool bFlushLeft
,
551 const rendering::ViewState
& rViewState
,
552 rendering::RenderState
& rRenderState
,
553 const css::uno::Reference
<css::rendering::XCanvasFont
>& rxFont
) const
555 if ( ! rxCanvas
.is())
558 double nY (rBBox
.Y1
);
559 for (const auto& rLine
: *mpLineDescriptors
)
562 /// check whether RTL interface or not
563 if(!AllSettings::GetLayoutRTL())
567 nX
= rBBox
.X2
- rLine
.maSize
.Width
;
571 nX
=rBBox
.X2
- rLine
.maSize
.Width
;
575 rRenderState
.AffineTransform
.m02
= nX
;
576 rRenderState
.AffineTransform
.m12
= nY
+ rLine
.maSize
.Height
- rLine
.mnVerticalOffset
;
578 const rendering::StringContext
aContext (rLine
.msLine
, 0, rLine
.msLine
.getLength());
579 Reference
<rendering::XTextLayout
> xLayout (
580 rxFont
->createTextLayout(aContext
, rendering::TextDirection::WEAK_LEFT_TO_RIGHT
, 0));
581 rxCanvas
->drawTextLayout (
586 nY
+= rLine
.maSize
.Height
* 1.2;
589 return nY
- rBBox
.Y1
;
592 double LineDescriptorList::GetHeight() const
594 return std::accumulate(mpLineDescriptors
->begin(), mpLineDescriptors
->end(), double(0),
595 [](const double& nHeight
, const LineDescriptor
& rLine
) {
596 return nHeight
+ rLine
.maSize
.Height
* 1.2;
600 void LineDescriptorList::Update (
601 const css::uno::Reference
<css::rendering::XCanvasFont
>& rxFont
,
602 const sal_Int32 nMaximalWidth
)
604 vector
<OUString
> aTextParts
;
605 SplitText(msText
, aTextParts
);
606 FormatText(aTextParts
, rxFont
, nMaximalWidth
);
609 void LineDescriptorList::SplitText (
610 const OUString
& rsText
,
611 vector
<OUString
>& rTextParts
)
613 const sal_Char
cQuote ('\'');
614 const sal_Char
cSeparator (',');
616 sal_Int32
nIndex (0);
617 sal_Int32
nStart (0);
618 sal_Int32
nLength (rsText
.getLength());
619 bool bIsQuoted (false);
620 while (nIndex
< nLength
)
622 const sal_Int32
nQuoteIndex (rsText
.indexOf(cQuote
, nIndex
));
623 const sal_Int32
nSeparatorIndex (rsText
.indexOf(cSeparator
, nIndex
));
624 if (nQuoteIndex
>=0 && (nSeparatorIndex
==-1 || nQuoteIndex
<nSeparatorIndex
))
626 bIsQuoted
= !bIsQuoted
;
627 nIndex
= nQuoteIndex
+1;
631 const sal_Int32 nNextIndex
= nSeparatorIndex
;
636 else if ( ! bIsQuoted
)
638 rTextParts
.push_back(rsText
.copy(nStart
, nNextIndex
-nStart
));
639 nStart
= nNextIndex
+ 1;
641 nIndex
= nNextIndex
+1;
643 if (nStart
< nLength
)
644 rTextParts
.push_back(rsText
.copy(nStart
, nLength
-nStart
));
647 void LineDescriptorList::FormatText (
648 const vector
<OUString
>& rTextParts
,
649 const css::uno::Reference
<css::rendering::XCanvasFont
>& rxFont
,
650 const sal_Int32 nMaximalWidth
)
652 LineDescriptor aLineDescriptor
;
654 mpLineDescriptors
.reset(new vector
<LineDescriptor
>);
656 vector
<OUString
>::const_iterator
iPart (rTextParts
.begin());
657 vector
<OUString
>::const_iterator
iEnd (rTextParts
.end());
660 if (aLineDescriptor
.IsEmpty())
662 // Avoid empty lines.
663 if (PresenterCanvasHelper::GetTextSize(
664 rxFont
, *iPart
).Width
> nMaximalWidth
)
666 const sal_Char
cSpace (' ');
668 sal_Int32
nIndex (0);
669 sal_Int32
nStart (0);
670 sal_Int32
nLength (iPart
->getLength());
671 while (nIndex
< nLength
)
673 sal_Int32
nSpaceIndex (iPart
->indexOf(cSpace
, nIndex
));
674 while (nSpaceIndex
>= 0 && PresenterCanvasHelper::GetTextSize(
675 rxFont
, iPart
->copy(nStart
, nSpaceIndex
-nStart
)).Width
<= nMaximalWidth
)
677 nIndex
= nSpaceIndex
;
678 nSpaceIndex
= iPart
->indexOf(cSpace
, nIndex
+1);
681 if (nSpaceIndex
< 0 && PresenterCanvasHelper::GetTextSize(
682 rxFont
, iPart
->copy(nStart
, nLength
-nStart
)).Width
<= nMaximalWidth
)
687 if (nIndex
== nStart
)
692 aLineDescriptor
.AddPart(iPart
->copy(nStart
, nIndex
-nStart
), rxFont
);
693 if (nIndex
!= nLength
)
695 mpLineDescriptors
->push_back(aLineDescriptor
);
696 aLineDescriptor
= LineDescriptor();
703 aLineDescriptor
.AddPart(*iPart
, rxFont
);
706 else if (PresenterCanvasHelper::GetTextSize(
707 rxFont
, aLineDescriptor
.msLine
+", "+*iPart
).Width
> nMaximalWidth
)
709 aLineDescriptor
.AddPart(",", rxFont
);
710 mpLineDescriptors
->push_back(aLineDescriptor
);
711 aLineDescriptor
= LineDescriptor();
716 aLineDescriptor
.AddPart(", "+*iPart
, rxFont
);
720 if ( ! aLineDescriptor
.IsEmpty())
722 mpLineDescriptors
->push_back(aLineDescriptor
);
726 } // end of anonymous namespace
728 //===== Block =================================================================
733 const OUString
& rsLeftText
,
734 const OUString
& rsRightText
,
735 const css::uno::Reference
<css::rendering::XCanvasFont
>& rxFont
,
736 const sal_Int32 nMaximalWidth
)
737 : maLeft(rsLeftText
, rxFont
, nMaximalWidth
),
738 maRight(rsRightText
, rxFont
, nMaximalWidth
)
743 const css::uno::Reference
<css::rendering::XCanvasFont
>& rxFont
,
744 const sal_Int32 nMaximalWidth
)
746 maLeft
.Update(rxFont
, nMaximalWidth
);
747 maRight
.Update(rxFont
, nMaximalWidth
);
750 } // end of anonymous namespace
752 } } // end of namespace ::sdext::presenter
754 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */