Version 7.6.3.2-android, tag libreoffice-7.6.3.2-android
[LibreOffice.git] / sd / source / console / PresenterHelpView.cxx
blobb4f3477e33be6cff3134762524d397025b324d5f
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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 <utility>
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>
33 #include <algorithm>
34 #include <numeric>
35 #include <string_view>
36 #include <vector>
38 using namespace ::com::sun::star;
39 using namespace ::com::sun::star::uno;
40 using namespace ::com::sun::star::drawing::framework;
41 using ::std::vector;
43 namespace sdext::presenter {
45 namespace {
46 const sal_Int32 gnHorizontalGap (20);
47 const sal_Int32 gnVerticalBorder (30);
48 const sal_Int32 gnVerticalButtonPadding (12);
50 class LineDescriptor
52 public:
53 LineDescriptor();
54 void AddPart (
55 std::u16string_view rsLine,
56 const css::uno::Reference<css::rendering::XCanvasFont>& rxFont);
57 bool IsEmpty() const;
59 OUString msLine;
60 geometry::RealSize2D maSize;
61 double mnVerticalOffset;
63 void CalculateSize (const css::uno::Reference<css::rendering::XCanvasFont>& rxFont);
66 class LineDescriptorList
68 public:
69 LineDescriptorList (
70 OUString sText,
71 const css::uno::Reference<css::rendering::XCanvasFont>& rxFont,
72 const sal_Int32 nMaximalWidth);
74 void Update (
75 const css::uno::Reference<css::rendering::XCanvasFont>& rxFont,
76 const sal_Int32 nMaximalWidth);
78 double Paint(
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;
87 private:
88 const OUString msText;
89 std::shared_ptr<vector<LineDescriptor> > mpLineDescriptors;
91 static void SplitText (std::u16string_view rsText, vector<OUString>& rTextParts);
92 void FormatText (
93 const vector<OUString>& rTextParts,
94 const css::uno::Reference<css::rendering::XCanvasFont>& rxFont,
95 const sal_Int32 nMaximalWidth);
98 class Block
100 public:
101 Block (
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;
108 void Update (
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),
128 mxViewId(rxViewId),
129 mpPresenterController(std::move(xPresenterController)),
130 mnSeparatorY(0),
131 mnMaximalWidth(0)
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();
141 ProvideCanvas();
143 mxWindow->addWindowListener(this);
144 mxWindow->addPaintListener(this);
145 Reference<awt::XWindowPeer> xPeer (mxWindow, UNO_QUERY);
146 if (xPeer.is())
147 xPeer->setBackground(util::Color(0xff000000));
148 mxWindow->setVisible(true);
150 if (mpPresenterController.is())
152 mpFont = mpPresenterController->GetViewFont(mxViewId->getResourceURL());
153 if (mpFont)
155 mpFont->PrepareFont(mxCanvas);
159 // Create the close button.
160 mpCloseButton = PresenterButton::Create(
161 mxComponentContext,
162 mpPresenterController,
163 mpPresenterController->GetTheme(),
164 mxWindow,
165 mxCanvas,
166 "HelpViewCloser");
168 ReadHelpStrings();
169 Resize();
171 catch (RuntimeException&)
173 mxViewId = nullptr;
174 mxWindow = nullptr;
175 throw;
179 PresenterHelpView::~PresenterHelpView()
183 void SAL_CALL PresenterHelpView::disposing()
185 mxViewId = nullptr;
187 if (mpCloseButton.is())
189 Reference<lang::XComponent> xComponent = mpCloseButton;
190 mpCloseButton = nullptr;
191 if (xComponent.is())
192 xComponent->dispose();
195 if (mxWindow.is())
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)
208 mxCanvas = nullptr;
210 else if (rEventObject.Source == mxWindow)
212 mxWindow = nullptr;
213 dispose();
217 //----- XWindowListener -------------------------------------------------------
219 void SAL_CALL PresenterHelpView::windowResized (const awt::WindowEvent&)
221 ThrowIfDisposed();
222 Resize();
225 void SAL_CALL PresenterHelpView::windowMoved (const awt::WindowEvent&)
227 ThrowIfDisposed();
230 void SAL_CALL PresenterHelpView::windowShown (const lang::EventObject&)
232 ThrowIfDisposed();
233 Resize();
236 void SAL_CALL PresenterHelpView::windowHidden (const lang::EventObject&)
238 ThrowIfDisposed();
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)
250 ProvideCanvas();
251 if ( ! mxCanvas.is())
252 return;
254 // Clear background.
255 const awt::Rectangle aWindowBox (mxWindow->getPosSize());
256 mpPresenterController->GetCanvasHelper()->Paint(
257 mpPresenterController->GetViewBackground(mxViewId->getResourceURL()),
258 mxCanvas,
259 rUpdateBox,
260 awt::Rectangle(0,0,aWindowBox.Width,aWindowBox.Height),
261 awt::Rectangle());
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),
271 nullptr,
272 Sequence<double>(4),
273 rendering::CompositeOperation::SOURCE);
274 PresenterCanvasHelper::SetDeviceColor(aRenderState, mpFont->mnColor);
276 mxCanvas->drawLine(
277 geometry::RealPoint2D((aWindowBox.Width/2.0), gnVerticalBorder),
278 geometry::RealPoint2D((aWindowBox.Width/2.0), mnSeparatorY - gnVerticalBorder),
279 aViewState,
280 aRenderState);
282 // Paint the horizontal separator.
283 mxCanvas->drawLine(
284 geometry::RealPoint2D(0, mnSeparatorY),
285 geometry::RealPoint2D(aWindowBox.Width, mnSeparatorY),
286 aViewState,
287 aRenderState);
289 // Paint text.
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(
309 LeftX1,
311 LeftX2,
312 aWindowBox.Height - gnVerticalBorder),
313 false,
314 aViewState,
315 aRenderState,
316 mpFont->mxFont));
317 const double nRightHeight (
318 rxBlock->maRight.Paint(mxCanvas,
319 geometry::RealRectangle2D(
320 RightX1,
322 RightX2,
323 aWindowBox.Height - gnVerticalBorder),
324 true,
325 aViewState,
326 aRenderState,
327 mpFont->mxFont));
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 (
341 mxComponentContext,
342 "/org.openoffice.Office.PresenterScreen/",
343 PresenterConfigurationAccess::READ_ONLY);
344 Reference<container::XNameAccess> xStrings (
345 aConfiguration.GetConfigurationNode("PresenterScreenSettings/HelpView/HelpStrings"),
346 UNO_QUERY);
347 PresenterConfigurationAccess::ForAll(
348 xStrings,
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())
359 return;
361 OUString sLeftText;
362 PresenterConfigurationAccess::GetProperty(rsProperties, "Left") >>= sLeftText;
363 OUString sRightText;
364 PresenterConfigurationAccess::GetProperty(rsProperties, "Right") >>= sRightText;
365 mpTextContainer->push_back(
366 std::make_shared<Block>(
367 sLeftText, sRightText, mpFont->mxFont, mnMaximalWidth));
370 void PresenterHelpView::CheckFontSize()
372 if (!mpFont)
373 return;
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.
394 return;
397 // Use a simple linear transformation to calculate initial guess of
398 // a size that lets all help text be shown inside the window.
399 const double nScale (double(mnSeparatorY-gnVerticalBorder) / nY);
400 if (nScale > 1.0 && nScale < 1.05)
401 break;
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);
410 // Reformat blocks.
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);
421 // Reformat blocks.
422 for (auto& rxBlock : *mpTextContainer)
424 rxBlock->Update(mpFont->mxFont, mnMaximalWidth);
429 //----- XResourceId -----------------------------------------------------------
431 Reference<XResourceId> SAL_CALL PresenterHelpView::getResourceId()
433 ThrowIfDisposed();
434 return mxViewId;
437 sal_Bool SAL_CALL PresenterHelpView::isAnchorOnly()
439 return false;
443 void PresenterHelpView::ProvideCanvas()
445 if ( ! mxCanvas.is() && mxPane.is())
447 mxCanvas = mxPane->getCanvas();
448 if ( ! mxCanvas.is())
449 return;
450 Reference<lang::XComponent> xComponent (mxCanvas, UNO_QUERY);
451 if (xComponent.is())
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()))
462 return;
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));
475 CheckFontSize();
478 void PresenterHelpView::ThrowIfDisposed()
480 if (rBHelper.bDisposed || rBHelper.bInDispose)
482 throw lang::DisposedException (
483 "PresenterHelpView has been already disposed",
484 static_cast<uno::XWeak*>(this));
488 //===== LineDescriptor =========================================================
490 namespace {
492 LineDescriptor::LineDescriptor()
493 : maSize(0,0),
494 mnVerticalOffset(0)
498 void LineDescriptor::AddPart (
499 std::u16string_view rsLine,
500 const css::uno::Reference<css::rendering::XCanvasFont>& rxFont)
502 msLine += rsLine;
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 ====================================================
529 namespace {
531 LineDescriptorList::LineDescriptorList (
532 OUString sText,
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())
549 return 0;
551 double nY (rBBox.Y1);
552 for (const auto& rLine : *mpLineDescriptors)
554 double nX;
555 /// check whether RTL interface or not
556 if(!AllSettings::GetLayoutRTL())
558 nX = rBBox.X1;
559 if ( ! bFlushLeft)
560 nX = rBBox.X2 - rLine.maSize.Width;
562 else
564 nX=rBBox.X2 - rLine.maSize.Width;
565 if ( ! bFlushLeft)
566 nX = rBBox.X1;
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 (
575 xLayout,
576 rViewState,
577 rRenderState);
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 (',');
609 size_t nIndex (0);
610 size_t nStart (0);
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;
622 continue;
625 const sal_Int32 nNextIndex = nSeparatorIndex;
626 if (nNextIndex < 0)
628 break;
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());
652 while (iPart!=iEnd)
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)
678 nIndex = nLength;
681 if (nIndex == nStart)
683 nIndex = nLength;
686 aLineDescriptor.AddPart(iPart->subView(nStart, nIndex-nStart), rxFont);
687 if (nIndex != nLength)
689 mpLineDescriptors->push_back(aLineDescriptor);
690 aLineDescriptor = LineDescriptor();
692 nStart = nIndex;
695 else
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();
706 continue;
708 else
710 aLineDescriptor.AddPart(Concat2View(", "+*iPart), rxFont);
712 ++iPart;
714 if ( ! aLineDescriptor.IsEmpty())
716 mpLineDescriptors->push_back(aLineDescriptor);
720 } // end of anonymous namespace
722 //===== Block =================================================================
724 namespace {
726 Block::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)
736 void Block::Update (
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: */