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 .
21 #include <vcl/settings.hxx>
22 #include "PresenterHelpView.hxx"
23 #include "PresenterButton.hxx"
24 #include "PresenterCanvasHelper.hxx"
25 #include "PresenterGeometryHelper.hxx"
26 #include <DrawController.hxx>
27 #include <com/sun/star/awt/XWindowPeer.hpp>
28 #include <com/sun/star/container/XNameAccess.hpp>
29 #include <com/sun/star/drawing/framework/XConfigurationController.hpp>
30 #include <com/sun/star/rendering/CompositeOperation.hpp>
31 #include <com/sun/star/rendering/TextDirection.hpp>
32 #include <com/sun/star/util/Color.hpp>
35 #include <string_view>
38 using namespace ::com::sun::star
;
39 using namespace ::com::sun::star::uno
;
40 using namespace ::com::sun::star::drawing::framework
;
43 namespace sdext::presenter
{
46 const sal_Int32
gnHorizontalGap (20);
47 const sal_Int32
gnVerticalBorder (30);
48 const sal_Int32
gnVerticalButtonPadding (12);
55 std::u16string_view rsLine
,
56 const css::uno::Reference
<css::rendering::XCanvasFont
>& rxFont
);
60 geometry::RealSize2D maSize
;
61 double mnVerticalOffset
;
63 void CalculateSize (const css::uno::Reference
<css::rendering::XCanvasFont
>& rxFont
);
66 class LineDescriptorList
71 const css::uno::Reference
<css::rendering::XCanvasFont
>& rxFont
,
72 const sal_Int32 nMaximalWidth
);
75 const css::uno::Reference
<css::rendering::XCanvasFont
>& rxFont
,
76 const sal_Int32 nMaximalWidth
);
79 const Reference
<rendering::XCanvas
>& rxCanvas
,
80 const geometry::RealRectangle2D
& rBBox
,
81 const bool bFlushLeft
,
82 const rendering::ViewState
& rViewState
,
83 rendering::RenderState
& rRenderState
,
84 const css::uno::Reference
<css::rendering::XCanvasFont
>& rxFont
) const;
85 double GetHeight() const;
88 const OUString msText
;
89 std::shared_ptr
<vector
<LineDescriptor
> > mpLineDescriptors
;
91 static void SplitText (std::u16string_view rsText
, vector
<OUString
>& rTextParts
);
93 const vector
<OUString
>& rTextParts
,
94 const css::uno::Reference
<css::rendering::XCanvasFont
>& rxFont
,
95 const sal_Int32 nMaximalWidth
);
102 const OUString
& rsLeftText
,
103 const OUString
& rsRightText
,
104 const css::uno::Reference
<css::rendering::XCanvasFont
>& rxFont
,
105 const sal_Int32 nMaximalWidth
);
106 Block(const Block
&) = delete;
107 Block
& operator=(const Block
&) = delete;
109 const css::uno::Reference
<css::rendering::XCanvasFont
>& rxFont
,
110 const sal_Int32 nMaximalWidth
);
112 LineDescriptorList maLeft
;
113 LineDescriptorList maRight
;
115 } // end of anonymous namespace
117 class PresenterHelpView::TextContainer
: public vector
<std::shared_ptr
<Block
> >
121 PresenterHelpView::PresenterHelpView (
122 const Reference
<uno::XComponentContext
>& rxContext
,
123 const Reference
<XResourceId
>& rxViewId
,
124 const rtl::Reference
<::sd::DrawController
>& rxController
,
125 ::rtl::Reference
<PresenterController
> xPresenterController
)
126 : PresenterHelpViewInterfaceBase(m_aMutex
),
127 mxComponentContext(rxContext
),
129 mpPresenterController(std::move(xPresenterController
)),
135 // Get the content window via the pane anchor.
136 Reference
<XConfigurationController
> xCC (
137 rxController
->getConfigurationController(), UNO_SET_THROW
);
138 mxPane
.set(xCC
->getResource(rxViewId
->getAnchor()), UNO_QUERY_THROW
);
140 mxWindow
= mxPane
->getWindow();
143 mxWindow
->addWindowListener(this);
144 mxWindow
->addPaintListener(this);
145 Reference
<awt::XWindowPeer
> xPeer (mxWindow
, UNO_QUERY
);
147 xPeer
->setBackground(util::Color(0xff000000));
148 mxWindow
->setVisible(true);
150 if (mpPresenterController
.is())
152 mpFont
= mpPresenterController
->GetViewFont(mxViewId
->getResourceURL());
155 mpFont
->PrepareFont(mxCanvas
);
159 // Create the close button.
160 mpCloseButton
= PresenterButton::Create(
162 mpPresenterController
,
163 mpPresenterController
->GetTheme(),
166 u
"HelpViewCloser"_ustr
);
171 catch (RuntimeException
&)
179 PresenterHelpView::~PresenterHelpView()
183 void SAL_CALL
PresenterHelpView::disposing()
187 if (mpCloseButton
.is())
189 Reference
<lang::XComponent
> xComponent
= mpCloseButton
;
190 mpCloseButton
= nullptr;
192 xComponent
->dispose();
197 mxWindow
->removeWindowListener(this);
198 mxWindow
->removePaintListener(this);
202 //----- lang::XEventListener --------------------------------------------------
204 void SAL_CALL
PresenterHelpView::disposing (const lang::EventObject
& rEventObject
)
206 if (rEventObject
.Source
== mxCanvas
)
210 else if (rEventObject
.Source
== mxWindow
)
217 //----- XWindowListener -------------------------------------------------------
219 void SAL_CALL
PresenterHelpView::windowResized (const awt::WindowEvent
&)
225 void SAL_CALL
PresenterHelpView::windowMoved (const awt::WindowEvent
&)
230 void SAL_CALL
PresenterHelpView::windowShown (const lang::EventObject
&)
236 void SAL_CALL
PresenterHelpView::windowHidden (const lang::EventObject
&)
241 //----- XPaintListener --------------------------------------------------------
243 void SAL_CALL
PresenterHelpView::windowPaint (const css::awt::PaintEvent
& rEvent
)
245 Paint(rEvent
.UpdateRect
);
248 void PresenterHelpView::Paint (const awt::Rectangle
& rUpdateBox
)
251 if ( ! mxCanvas
.is())
255 const awt::Rectangle
aWindowBox (mxWindow
->getPosSize());
256 mpPresenterController
->GetCanvasHelper()->Paint(
257 mpPresenterController
->GetViewBackground(mxViewId
->getResourceURL()),
260 awt::Rectangle(0,0,aWindowBox
.Width
,aWindowBox
.Height
),
263 // Paint vertical divider.
265 rendering::ViewState
aViewState(
266 geometry::AffineMatrix2D(1,0,0, 0,1,0),
267 PresenterGeometryHelper::CreatePolygon(rUpdateBox
, mxCanvas
->getDevice()));
269 rendering::RenderState
aRenderState (
270 geometry::AffineMatrix2D(1,0,0, 0,1,0),
273 rendering::CompositeOperation::SOURCE
);
274 PresenterCanvasHelper::SetDeviceColor(aRenderState
, mpFont
->mnColor
);
277 geometry::RealPoint2D((aWindowBox
.Width
/2.0), gnVerticalBorder
),
278 geometry::RealPoint2D((aWindowBox
.Width
/2.0), mnSeparatorY
- gnVerticalBorder
),
282 // Paint the horizontal separator.
284 geometry::RealPoint2D(0, mnSeparatorY
),
285 geometry::RealPoint2D(aWindowBox
.Width
, mnSeparatorY
),
290 double nY (gnVerticalBorder
);
291 for (const auto& rxBlock
: *mpTextContainer
)
293 sal_Int32 LeftX1
= gnHorizontalGap
;
294 sal_Int32 LeftX2
= aWindowBox
.Width
/2 - gnHorizontalGap
;
295 sal_Int32 RightX1
= aWindowBox
.Width
/2 + gnHorizontalGap
;
296 sal_Int32 RightX2
= aWindowBox
.Width
- gnHorizontalGap
;
297 /* check whether RTL interface or not
298 then replace the windowbox position */
299 if(AllSettings::GetLayoutRTL())
301 LeftX1
= aWindowBox
.Width
/2 + gnHorizontalGap
;
302 LeftX2
= aWindowBox
.Width
- gnHorizontalGap
;
303 RightX1
= gnHorizontalGap
;
304 RightX2
= aWindowBox
.Width
/2 - gnHorizontalGap
;
306 const double nLeftHeight (
307 rxBlock
->maLeft
.Paint(mxCanvas
,
308 geometry::RealRectangle2D(
312 aWindowBox
.Height
- gnVerticalBorder
),
317 const double nRightHeight (
318 rxBlock
->maRight
.Paint(mxCanvas
,
319 geometry::RealRectangle2D(
323 aWindowBox
.Height
- gnVerticalBorder
),
329 nY
+= ::std::max(nLeftHeight
,nRightHeight
);
332 Reference
<rendering::XSpriteCanvas
> xSpriteCanvas (mxCanvas
, UNO_QUERY
);
333 if (xSpriteCanvas
.is())
334 xSpriteCanvas
->updateScreen(false);
337 void PresenterHelpView::ReadHelpStrings()
339 mpTextContainer
.reset(new TextContainer
);
340 PresenterConfigurationAccess
aConfiguration (
342 u
"/org.openoffice.Office.PresenterScreen/"_ustr
,
343 PresenterConfigurationAccess::READ_ONLY
);
344 Reference
<container::XNameAccess
> xStrings (
345 aConfiguration
.GetConfigurationNode(u
"PresenterScreenSettings/HelpView/HelpStrings"_ustr
),
347 PresenterConfigurationAccess::ForAll(
349 [this](OUString
const&, uno::Reference
<beans::XPropertySet
> const& xProps
)
351 return this->ProcessString(xProps
);
355 void PresenterHelpView::ProcessString (
356 const Reference
<beans::XPropertySet
>& rsProperties
)
358 if ( ! rsProperties
.is())
362 PresenterConfigurationAccess::GetProperty(rsProperties
, u
"Left"_ustr
) >>= sLeftText
;
364 PresenterConfigurationAccess::GetProperty(rsProperties
, u
"Right"_ustr
) >>= sRightText
;
365 mpTextContainer
->push_back(
366 std::make_shared
<Block
>(
367 sLeftText
, sRightText
, mpFont
->mxFont
, mnMaximalWidth
));
370 void PresenterHelpView::CheckFontSize()
375 sal_Int32
nBestSize (6);
377 // Scaling down and then reformatting can cause the text to be too large
378 // still. So do this again and again until the text size is
379 // small enough. Restrict the number of loops.
380 for (int nLoopCount
=0; nLoopCount
<5; ++nLoopCount
)
382 double nY
= std::accumulate(mpTextContainer
->begin(), mpTextContainer
->end(), double(0),
383 [](const double& sum
, const std::shared_ptr
<Block
>& rxBlock
) {
384 return sum
+ std::max(
385 rxBlock
->maLeft
.GetHeight(),
386 rxBlock
->maRight
.GetHeight());
389 const double nHeightDifference (nY
- (mnSeparatorY
-gnVerticalBorder
));
390 if (nHeightDifference
<= 0 && nHeightDifference
> -50)
392 // We have found a good font size that is large and leaves not
393 // too much space below the help text.
397 // Use a simple linear transformation to calculate initial guess of
398 // a size that lets all help text to be shown inside the window.
399 const double nScale (double(mnSeparatorY
-gnVerticalBorder
) / nY
);
400 if (nScale
> 1.0 && nScale
< 1.05)
403 sal_Int32
nFontSizeGuess (sal_Int32(mpFont
->mnSize
* nScale
));
404 if (nHeightDifference
<=0 && mpFont
->mnSize
>nBestSize
)
405 nBestSize
= mpFont
->mnSize
;
406 mpFont
->mnSize
= nFontSizeGuess
;
407 mpFont
->mxFont
= nullptr;
408 mpFont
->PrepareFont(mxCanvas
);
411 for (auto& rxBlock
: *mpTextContainer
)
412 rxBlock
->Update(mpFont
->mxFont
, mnMaximalWidth
);
415 if (nBestSize
!= mpFont
->mnSize
)
417 mpFont
->mnSize
= nBestSize
;
418 mpFont
->mxFont
= nullptr;
419 mpFont
->PrepareFont(mxCanvas
);
422 for (auto& rxBlock
: *mpTextContainer
)
424 rxBlock
->Update(mpFont
->mxFont
, mnMaximalWidth
);
429 //----- XResourceId -----------------------------------------------------------
431 Reference
<XResourceId
> SAL_CALL
PresenterHelpView::getResourceId()
437 sal_Bool SAL_CALL
PresenterHelpView::isAnchorOnly()
443 void PresenterHelpView::ProvideCanvas()
445 if ( ! mxCanvas
.is() && mxPane
.is())
447 mxCanvas
= mxPane
->getCanvas();
448 if ( ! mxCanvas
.is())
450 Reference
<lang::XComponent
> xComponent (mxCanvas
, UNO_QUERY
);
452 xComponent
->addEventListener(static_cast<awt::XPaintListener
*>(this));
454 if (mpCloseButton
.is())
455 mpCloseButton
->SetCanvas(mxCanvas
, mxWindow
);
459 void PresenterHelpView::Resize()
461 if (!(mpCloseButton
&& mxWindow
.is()))
464 const awt::Rectangle
aWindowBox (mxWindow
->getPosSize());
465 mnMaximalWidth
= (mxWindow
->getPosSize().Width
- 4*gnHorizontalGap
) / 2;
467 // Place vertical separator.
468 mnSeparatorY
= aWindowBox
.Height
469 - mpCloseButton
->GetSize().Height
- gnVerticalButtonPadding
;
471 mpCloseButton
->SetCenter(geometry::RealPoint2D(
472 aWindowBox
.Width
/2.0,
473 aWindowBox
.Height
- mpCloseButton
->GetSize().Height
/2.0));
478 void PresenterHelpView::ThrowIfDisposed()
480 if (rBHelper
.bDisposed
|| rBHelper
.bInDispose
)
482 throw lang::DisposedException (
483 u
"PresenterHelpView has been already disposed"_ustr
,
484 static_cast<uno::XWeak
*>(this));
488 //===== LineDescriptor =========================================================
492 LineDescriptor::LineDescriptor()
498 void LineDescriptor::AddPart (
499 std::u16string_view rsLine
,
500 const css::uno::Reference
<css::rendering::XCanvasFont
>& rxFont
)
504 CalculateSize(rxFont
);
507 bool LineDescriptor::IsEmpty() const
509 return msLine
.isEmpty();
512 void LineDescriptor::CalculateSize (
513 const css::uno::Reference
<css::rendering::XCanvasFont
>& rxFont
)
515 OSL_ASSERT(rxFont
.is());
517 rendering::StringContext
aContext (msLine
, 0, msLine
.getLength());
518 Reference
<rendering::XTextLayout
> xLayout (
519 rxFont
->createTextLayout(aContext
, rendering::TextDirection::WEAK_LEFT_TO_RIGHT
, 0));
520 const geometry::RealRectangle2D
aTextBBox (xLayout
->queryTextBounds());
521 maSize
= css::geometry::RealSize2D(aTextBBox
.X2
- aTextBBox
.X1
, aTextBBox
.Y2
- aTextBBox
.Y1
);
522 mnVerticalOffset
= aTextBBox
.Y2
;
525 } // end of anonymous namespace
527 //===== LineDescriptorList ====================================================
531 LineDescriptorList::LineDescriptorList (
533 const css::uno::Reference
<css::rendering::XCanvasFont
>& rxFont
,
534 const sal_Int32 nMaximalWidth
)
535 : msText(std::move(sText
))
537 Update(rxFont
, nMaximalWidth
);
540 double LineDescriptorList::Paint(
541 const Reference
<rendering::XCanvas
>& rxCanvas
,
542 const geometry::RealRectangle2D
& rBBox
,
543 const bool bFlushLeft
,
544 const rendering::ViewState
& rViewState
,
545 rendering::RenderState
& rRenderState
,
546 const css::uno::Reference
<css::rendering::XCanvasFont
>& rxFont
) const
548 if ( ! rxCanvas
.is())
551 double nY (rBBox
.Y1
);
552 for (const auto& rLine
: *mpLineDescriptors
)
555 /// check whether RTL interface or not
556 if(!AllSettings::GetLayoutRTL())
560 nX
= rBBox
.X2
- rLine
.maSize
.Width
;
564 nX
=rBBox
.X2
- rLine
.maSize
.Width
;
568 rRenderState
.AffineTransform
.m02
= nX
;
569 rRenderState
.AffineTransform
.m12
= nY
+ rLine
.maSize
.Height
- rLine
.mnVerticalOffset
;
571 const rendering::StringContext
aContext (rLine
.msLine
, 0, rLine
.msLine
.getLength());
572 Reference
<rendering::XTextLayout
> xLayout (
573 rxFont
->createTextLayout(aContext
, rendering::TextDirection::WEAK_LEFT_TO_RIGHT
, 0));
574 rxCanvas
->drawTextLayout (
579 nY
+= rLine
.maSize
.Height
* 1.2;
582 return nY
- rBBox
.Y1
;
585 double LineDescriptorList::GetHeight() const
587 return std::accumulate(mpLineDescriptors
->begin(), mpLineDescriptors
->end(), double(0),
588 [](const double& nHeight
, const LineDescriptor
& rLine
) {
589 return nHeight
+ rLine
.maSize
.Height
* 1.2;
593 void LineDescriptorList::Update (
594 const css::uno::Reference
<css::rendering::XCanvasFont
>& rxFont
,
595 const sal_Int32 nMaximalWidth
)
597 vector
<OUString
> aTextParts
;
598 SplitText(msText
, aTextParts
);
599 FormatText(aTextParts
, rxFont
, nMaximalWidth
);
602 void LineDescriptorList::SplitText (
603 std::u16string_view rsText
,
604 vector
<OUString
>& rTextParts
)
606 const char cQuote ('\'');
607 const char cSeparator (',');
611 size_t nLength (rsText
.size());
612 bool bIsQuoted (false);
613 while (nIndex
< nLength
)
615 const size_t nQuoteIndex (rsText
.find(cQuote
, nIndex
));
616 const size_t nSeparatorIndex (rsText
.find(cSeparator
, nIndex
));
617 if (nQuoteIndex
!= std::u16string_view::npos
&&
618 (nSeparatorIndex
== std::u16string_view::npos
|| nQuoteIndex
<nSeparatorIndex
))
620 bIsQuoted
= !bIsQuoted
;
621 nIndex
= nQuoteIndex
+1;
625 const sal_Int32 nNextIndex
= nSeparatorIndex
;
630 else if ( ! bIsQuoted
)
632 rTextParts
.push_back(OUString(rsText
.substr(nStart
, nNextIndex
-nStart
)));
633 nStart
= nNextIndex
+ 1;
635 nIndex
= nNextIndex
+1;
637 if (nStart
< nLength
)
638 rTextParts
.push_back(OUString(rsText
.substr(nStart
, nLength
-nStart
)));
641 void LineDescriptorList::FormatText (
642 const vector
<OUString
>& rTextParts
,
643 const css::uno::Reference
<css::rendering::XCanvasFont
>& rxFont
,
644 const sal_Int32 nMaximalWidth
)
646 LineDescriptor aLineDescriptor
;
648 mpLineDescriptors
= std::make_shared
<vector
<LineDescriptor
>>();
650 vector
<OUString
>::const_iterator
iPart (rTextParts
.begin());
651 vector
<OUString
>::const_iterator
iEnd (rTextParts
.end());
654 if (aLineDescriptor
.IsEmpty())
656 // Avoid empty lines.
657 if (PresenterCanvasHelper::GetTextSize(
658 rxFont
, *iPart
).Width
> nMaximalWidth
)
660 const char cSpace (' ');
662 sal_Int32
nIndex (0);
663 sal_Int32
nStart (0);
664 sal_Int32
nLength (iPart
->getLength());
665 while (nIndex
< nLength
)
667 sal_Int32
nSpaceIndex (iPart
->indexOf(cSpace
, nIndex
));
668 while (nSpaceIndex
>= 0 && PresenterCanvasHelper::GetTextSize(
669 rxFont
, iPart
->copy(nStart
, nSpaceIndex
-nStart
)).Width
<= nMaximalWidth
)
671 nIndex
= nSpaceIndex
;
672 nSpaceIndex
= iPart
->indexOf(cSpace
, nIndex
+1);
675 if (nSpaceIndex
< 0 && PresenterCanvasHelper::GetTextSize(
676 rxFont
, iPart
->copy(nStart
, nLength
-nStart
)).Width
<= nMaximalWidth
)
681 if (nIndex
== nStart
)
686 aLineDescriptor
.AddPart(iPart
->subView(nStart
, nIndex
-nStart
), rxFont
);
687 if (nIndex
!= nLength
)
689 mpLineDescriptors
->push_back(aLineDescriptor
);
690 aLineDescriptor
= LineDescriptor();
697 aLineDescriptor
.AddPart(*iPart
, rxFont
);
700 else if (PresenterCanvasHelper::GetTextSize(
701 rxFont
, aLineDescriptor
.msLine
+", "+*iPart
).Width
> nMaximalWidth
)
703 aLineDescriptor
.AddPart(u
",", rxFont
);
704 mpLineDescriptors
->push_back(aLineDescriptor
);
705 aLineDescriptor
= LineDescriptor();
710 aLineDescriptor
.AddPart(Concat2View(", "+*iPart
), rxFont
);
714 if ( ! aLineDescriptor
.IsEmpty())
716 mpLineDescriptors
->push_back(aLineDescriptor
);
720 } // end of anonymous namespace
722 //===== Block =================================================================
727 const OUString
& rsLeftText
,
728 const OUString
& rsRightText
,
729 const css::uno::Reference
<css::rendering::XCanvasFont
>& rxFont
,
730 const sal_Int32 nMaximalWidth
)
731 : maLeft(rsLeftText
, rxFont
, nMaximalWidth
),
732 maRight(rsRightText
, rxFont
, nMaximalWidth
)
737 const css::uno::Reference
<css::rendering::XCanvasFont
>& rxFont
,
738 const sal_Int32 nMaximalWidth
)
740 maLeft
.Update(rxFont
, nMaximalWidth
);
741 maRight
.Update(rxFont
, nMaximalWidth
);
744 } // end of anonymous namespace
746 } // end of namespace ::sdext::presenter
748 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */