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 "PresenterButton.hxx"
21 #include "PresenterCanvasHelper.hxx"
22 #include "PresenterController.hxx"
23 #include "PresenterGeometryHelper.hxx"
24 #include "PresenterPaintManager.hxx"
25 #include "PresenterUIPainter.hxx"
26 #include <com/sun/star/awt/PosSize.hpp>
27 #include <com/sun/star/awt/XWindowPeer.hpp>
28 #include <com/sun/star/drawing/XPresenterHelper.hpp>
29 #include <com/sun/star/rendering/CompositeOperation.hpp>
30 #include <com/sun/star/rendering/TextDirection.hpp>
31 
32 using namespace ::com::sun::star;
33 using namespace ::com::sun::star::uno;
34 
35 namespace sdext::presenter {
36 
37 const double gnHorizontalBorder (15);
38 const double gnVerticalBorder (5);
39 
Create(const css::uno::Reference<css::uno::XComponentContext> & rxComponentContext,const::rtl::Reference<PresenterController> & rpPresenterController,const std::shared_ptr<PresenterTheme> & rpTheme,const css::uno::Reference<css::awt::XWindow> & rxParentWindow,const css::uno::Reference<css::rendering::XCanvas> & rxParentCanvas,const OUString & rsConfigurationName)40 ::rtl::Reference<PresenterButton> PresenterButton::Create (
41     const css::uno::Reference<css::uno::XComponentContext>& rxComponentContext,
42     const ::rtl::Reference<PresenterController>& rpPresenterController,
43     const std::shared_ptr<PresenterTheme>& rpTheme,
44     const css::uno::Reference<css::awt::XWindow>& rxParentWindow,
45     const css::uno::Reference<css::rendering::XCanvas>& rxParentCanvas,
46     const OUString& rsConfigurationName)
47 {
48     Reference<beans::XPropertySet> xProperties (GetConfigurationProperties(
49         rxComponentContext,
50         rsConfigurationName));
51     if (xProperties.is())
52     {
53         OUString sText;
54         OUString sAction;
55         PresenterConfigurationAccess::GetProperty(xProperties, "Text") >>= sText;
56         PresenterConfigurationAccess::GetProperty(xProperties, "Action") >>= sAction;
57 
58         PresenterTheme::SharedFontDescriptor pFont;
59         if (rpTheme != nullptr)
60             pFont = rpTheme->GetFont("ButtonFont");
61 
62         PresenterTheme::SharedFontDescriptor pMouseOverFont;
63         if (rpTheme != nullptr)
64             pMouseOverFont = rpTheme->GetFont("ButtonMouseOverFont");
65 
66         rtl::Reference<PresenterButton> pButton (
67             new PresenterButton(
68                 rxComponentContext,
69                 rpPresenterController,
70                 rpTheme,
71                 rxParentWindow,
72                 pFont,
73                 pMouseOverFont,
74                 sText,
75                 sAction));
76         pButton->SetCanvas(rxParentCanvas, rxParentWindow);
77         return pButton;
78     }
79     else
80         return nullptr;
81 }
82 
PresenterButton(const css::uno::Reference<css::uno::XComponentContext> & rxComponentContext,const::rtl::Reference<PresenterController> & rpPresenterController,const std::shared_ptr<PresenterTheme> & rpTheme,const css::uno::Reference<css::awt::XWindow> & rxParentWindow,const PresenterTheme::SharedFontDescriptor & rpFont,const PresenterTheme::SharedFontDescriptor & rpMouseOverFont,const OUString & rsText,const OUString & rsAction)83 PresenterButton::PresenterButton (
84     const css::uno::Reference<css::uno::XComponentContext>& rxComponentContext,
85     const ::rtl::Reference<PresenterController>& rpPresenterController,
86     const std::shared_ptr<PresenterTheme>& rpTheme,
87     const css::uno::Reference<css::awt::XWindow>& rxParentWindow,
88     const PresenterTheme::SharedFontDescriptor& rpFont,
89     const PresenterTheme::SharedFontDescriptor& rpMouseOverFont,
90     const OUString& rsText,
91     const OUString& rsAction)
92     : PresenterButtonInterfaceBase(m_aMutex),
93       mpPresenterController(rpPresenterController),
94       mpTheme(rpTheme),
95       mxWindow(),
96       mxCanvas(),
97       mxPresenterHelper(),
98       msText(rsText),
99       mpFont(rpFont),
100       mpMouseOverFont(rpMouseOverFont),
101       msAction(rsAction),
102       maCenter(),
103       maButtonSize(-1,-1),
104       meState(PresenterBitmapDescriptor::Normal),
105       mxNormalBitmap(),
106       mxMouseOverBitmap()
107 {
108     try
109     {
110         Reference<lang::XMultiComponentFactory> xFactory (rxComponentContext->getServiceManager());
111         if ( ! xFactory.is())
112             throw RuntimeException();
113 
114         mxPresenterHelper.set(
115             xFactory->createInstanceWithContext(
116                 "com.sun.star.comp.Draw.PresenterHelper",
117                 rxComponentContext),
118             UNO_QUERY_THROW);
119 
120         if (mxPresenterHelper.is())
121             mxWindow = mxPresenterHelper->createWindow(rxParentWindow,
122                 false,
123                 false,
124                 false,
125                 false);
126 
127         // Make the background transparent.
128         Reference<awt::XWindowPeer> xPeer (mxWindow, UNO_QUERY_THROW);
129         xPeer->setBackground(0xff000000);
130 
131         mxWindow->setVisible(true);
132         mxWindow->addPaintListener(this);
133         mxWindow->addMouseListener(this);
134     }
135     catch (RuntimeException&)
136     {
137     }
138 }
139 
~PresenterButton()140 PresenterButton::~PresenterButton()
141 {
142 }
143 
disposing()144 void SAL_CALL PresenterButton::disposing()
145 {
146     if (mxCanvas.is())
147     {
148         Reference<lang::XComponent> xComponent (mxCanvas, UNO_QUERY);
149         mxCanvas = nullptr;
150         if (xComponent.is())
151             xComponent->dispose();
152     }
153 
154     if (mxWindow.is())
155     {
156         mxWindow->removePaintListener(this);
157         mxWindow->removeMouseListener(this);
158         Reference<lang::XComponent> xComponent = mxWindow;
159         mxWindow = nullptr;
160         if (xComponent.is())
161             xComponent->dispose();
162     }
163 }
164 
SetCenter(const css::geometry::RealPoint2D & rLocation)165 void PresenterButton::SetCenter (const css::geometry::RealPoint2D& rLocation)
166 {
167     if (mxCanvas.is())
168     {
169         Invalidate();
170 
171         maCenter = rLocation;
172         mxWindow->setPosSize(
173             sal_Int32(0.5 + maCenter.X - maButtonSize.Width/2),
174             sal_Int32(0.5 + maCenter.Y - maButtonSize.Height/2),
175             maButtonSize.Width,
176             maButtonSize.Height,
177             awt::PosSize::POSSIZE);
178 
179         Invalidate();
180     }
181     else
182     {
183         // The button can not be painted but we can at least store the new center.
184         maCenter = rLocation;
185     }
186 }
187 
SetCanvas(const css::uno::Reference<css::rendering::XCanvas> & rxParentCanvas,const css::uno::Reference<css::awt::XWindow> & rxParentWindow)188 void PresenterButton::SetCanvas (
189     const css::uno::Reference<css::rendering::XCanvas>& rxParentCanvas,
190     const css::uno::Reference<css::awt::XWindow>& rxParentWindow)
191 {
192     if (mxCanvas.is())
193     {
194         Reference<lang::XComponent> xComponent (mxCanvas, UNO_QUERY);
195         mxCanvas = nullptr;
196         if (xComponent.is())
197             xComponent->dispose();
198     }
199 
200     if (!(mxPresenterHelper.is() && rxParentCanvas.is() && rxParentWindow.is()))
201         return;
202 
203     mxCanvas = mxPresenterHelper->createSharedCanvas (
204         Reference<rendering::XSpriteCanvas>(rxParentCanvas, UNO_QUERY),
205         rxParentWindow,
206         rxParentCanvas,
207         rxParentWindow,
208         mxWindow);
209     if (mxCanvas.is())
210     {
211         SetupButtonBitmaps();
212         SetCenter(maCenter);
213     }
214 }
215 
GetSize()216 css::geometry::IntegerSize2D const & PresenterButton::GetSize()
217 {
218     if (maButtonSize.Width < 0)
219         CalculateButtonSize();
220     return maButtonSize;
221 }
222 
223 //----- XPaintListener --------------------------------------------------------
224 
windowPaint(const css::awt::PaintEvent & rEvent)225 void SAL_CALL PresenterButton::windowPaint (const css::awt::PaintEvent& rEvent)
226 {
227     ThrowIfDisposed();
228     if (!(mxWindow.is() && mxCanvas.is()))
229         return;
230 
231     Reference<rendering::XBitmap> xBitmap;
232     if (meState == PresenterBitmapDescriptor::MouseOver)
233         xBitmap = mxMouseOverBitmap;
234     else
235         xBitmap = mxNormalBitmap;
236     if ( ! xBitmap.is())
237         return;
238 
239     rendering::ViewState aViewState(
240         geometry::AffineMatrix2D(1,0,0, 0,1,0),
241         nullptr);
242     rendering::RenderState aRenderState(
243         geometry::AffineMatrix2D(1,0,0, 0,1,0),
244         PresenterGeometryHelper::CreatePolygon(rEvent.UpdateRect, mxCanvas->getDevice()),
245         Sequence<double>(4),
246         rendering::CompositeOperation::SOURCE);
247 
248     mxCanvas->drawBitmap(xBitmap, aViewState, aRenderState);
249 
250     Reference<rendering::XSpriteCanvas> xSpriteCanvas (mxCanvas, UNO_QUERY);
251     if (xSpriteCanvas.is())
252         xSpriteCanvas->updateScreen(false);
253 }
254 
255 //----- XMouseListener --------------------------------------------------------
256 
mousePressed(const css::awt::MouseEvent &)257 void SAL_CALL PresenterButton::mousePressed (const css::awt::MouseEvent&)
258 {
259     ThrowIfDisposed();
260     meState = PresenterBitmapDescriptor::ButtonDown;
261 }
262 
mouseReleased(const css::awt::MouseEvent &)263 void SAL_CALL PresenterButton::mouseReleased (const css::awt::MouseEvent&)
264 {
265     ThrowIfDisposed();
266 
267     if (meState == PresenterBitmapDescriptor::ButtonDown)
268     {
269         OSL_ASSERT(mpPresenterController);
270         mpPresenterController->DispatchUnoCommand(msAction);
271 
272         meState = PresenterBitmapDescriptor::Normal;
273         Invalidate();
274     }
275 }
276 
mouseEntered(const css::awt::MouseEvent &)277 void SAL_CALL PresenterButton::mouseEntered (const css::awt::MouseEvent&)
278 {
279     ThrowIfDisposed();
280     meState = PresenterBitmapDescriptor::MouseOver;
281     Invalidate();
282 }
283 
mouseExited(const css::awt::MouseEvent &)284 void SAL_CALL PresenterButton::mouseExited (const css::awt::MouseEvent&)
285 {
286     ThrowIfDisposed();
287     meState = PresenterBitmapDescriptor::Normal;
288     Invalidate();
289 }
290 
291 //----- lang::XEventListener --------------------------------------------------
292 
disposing(const css::lang::EventObject & rEvent)293 void SAL_CALL PresenterButton::disposing (const css::lang::EventObject& rEvent)
294 {
295     if (rEvent.Source == mxWindow)
296         mxWindow = nullptr;
297 }
298 
299 
CalculateButtonSize()300 css::geometry::IntegerSize2D PresenterButton::CalculateButtonSize()
301 {
302     if (mpFont && !mpFont->mxFont.is() && mxCanvas.is())
303         mpFont->PrepareFont(mxCanvas);
304     if (!mpFont || !mpFont->mxFont.is())
305         return geometry::IntegerSize2D(-1,-1);
306 
307     geometry::RealSize2D aTextSize (PresenterCanvasHelper::GetTextSize(mpFont->mxFont,msText));
308 
309     return geometry::IntegerSize2D (
310         sal_Int32(0.5 + aTextSize.Width + 2*gnHorizontalBorder),
311         sal_Int32(0.5 + aTextSize.Height + 2*gnVerticalBorder));
312 }
313 
RenderButton(const Reference<rendering::XCanvas> & rxCanvas,const geometry::IntegerSize2D & rSize,const PresenterTheme::SharedFontDescriptor & rpFont,const PresenterBitmapDescriptor::Mode eMode,const SharedBitmapDescriptor & rpLeft,const SharedBitmapDescriptor & rpCenter,const SharedBitmapDescriptor & rpRight)314 void PresenterButton::RenderButton (
315     const Reference<rendering::XCanvas>& rxCanvas,
316     const geometry::IntegerSize2D& rSize,
317     const PresenterTheme::SharedFontDescriptor& rpFont,
318     const PresenterBitmapDescriptor::Mode eMode,
319     const SharedBitmapDescriptor& rpLeft,
320     const SharedBitmapDescriptor& rpCenter,
321     const SharedBitmapDescriptor& rpRight)
322 {
323     if ( ! rxCanvas.is())
324         return;
325 
326     const awt::Rectangle aBox(0,0, rSize.Width, rSize.Height);
327 
328     PresenterUIPainter::PaintHorizontalBitmapComposite (
329         rxCanvas,
330         aBox,
331         aBox,
332         GetBitmap(rpLeft, eMode),
333         GetBitmap(rpCenter, eMode),
334         GetBitmap(rpRight, eMode));
335 
336     if (!rpFont || ! rpFont->mxFont.is())
337         return;
338 
339     const rendering::StringContext aContext (msText, 0, msText.getLength());
340     const Reference<rendering::XTextLayout> xLayout (
341         rpFont->mxFont->createTextLayout(aContext,rendering::TextDirection::WEAK_LEFT_TO_RIGHT,0));
342     const geometry::RealRectangle2D aTextBBox (xLayout->queryTextBounds());
343 
344     rendering::RenderState aRenderState (geometry::AffineMatrix2D(1,0,0, 0,1,0), nullptr,
345         Sequence<double>(4), rendering::CompositeOperation::SOURCE);
346     PresenterCanvasHelper::SetDeviceColor(aRenderState, rpFont->mnColor);
347 
348     aRenderState.AffineTransform.m02 = (rSize.Width - aTextBBox.X2 + aTextBBox.X1)/2;
349     aRenderState.AffineTransform.m12 = (rSize.Height - aTextBBox.Y2 + aTextBBox.Y1)/2 - aTextBBox.Y1;
350 
351     /// this is responsible of the close button
352     rxCanvas->drawTextLayout(
353         xLayout,
354         rendering::ViewState(geometry::AffineMatrix2D(1,0,0, 0,1,0), nullptr),
355         aRenderState);
356 }
357 
Invalidate()358 void PresenterButton::Invalidate()
359 {
360     mpPresenterController->GetPaintManager()->Invalidate(mxWindow);
361 }
362 
GetBitmap(const SharedBitmapDescriptor & mpIcon,const PresenterBitmapDescriptor::Mode eMode)363 Reference<rendering::XBitmap> PresenterButton::GetBitmap (
364     const SharedBitmapDescriptor& mpIcon,
365     const PresenterBitmapDescriptor::Mode eMode)
366 {
367     if (mpIcon)
368         return mpIcon->GetBitmap(eMode);
369     else
370     {
371         OSL_ASSERT(mpIcon);
372         return nullptr;
373     }
374 }
375 
SetupButtonBitmaps()376 void PresenterButton::SetupButtonBitmaps()
377 {
378     if ( ! mxCanvas.is())
379         return;
380     if ( ! mxCanvas->getDevice().is())
381         return;
382 
383     // Get the bitmaps for the button border.
384     SharedBitmapDescriptor pLeftBitmap (mpTheme->GetBitmap("ButtonFrameLeft"));
385     SharedBitmapDescriptor pCenterBitmap(mpTheme->GetBitmap("ButtonFrameCenter"));
386     SharedBitmapDescriptor pRightBitmap(mpTheme->GetBitmap("ButtonFrameRight"));
387 
388     maButtonSize = CalculateButtonSize();
389 
390     if (maButtonSize.Height<=0 && maButtonSize.Width<= 0)
391         return;
392 
393     mxNormalBitmap = mxCanvas->getDevice()->createCompatibleAlphaBitmap(maButtonSize);
394     Reference<rendering::XCanvas> xCanvas (mxNormalBitmap, UNO_QUERY);
395     if (xCanvas.is())
396         RenderButton(
397             xCanvas,
398             maButtonSize,
399             mpFont,
400             PresenterBitmapDescriptor::Normal,
401             pLeftBitmap,
402             pCenterBitmap,
403             pRightBitmap);
404 
405     mxMouseOverBitmap = mxCanvas->getDevice()->createCompatibleAlphaBitmap(maButtonSize);
406     xCanvas.set(mxMouseOverBitmap, UNO_QUERY);
407     if (mpMouseOverFont && !mpMouseOverFont->mxFont.is() && mxCanvas.is())
408         mpMouseOverFont->PrepareFont(mxCanvas);
409     if (xCanvas.is())
410         RenderButton(
411             xCanvas,
412             maButtonSize,
413             mpMouseOverFont,
414             PresenterBitmapDescriptor::MouseOver,
415             pLeftBitmap,
416             pCenterBitmap,
417             pRightBitmap);
418 }
419 
GetConfigurationProperties(const css::uno::Reference<css::uno::XComponentContext> & rxComponentContext,const OUString & rsConfigurationName)420 Reference<beans::XPropertySet> PresenterButton::GetConfigurationProperties (
421     const css::uno::Reference<css::uno::XComponentContext>& rxComponentContext,
422     const OUString& rsConfigurationName)
423 {
424     PresenterConfigurationAccess aConfiguration (
425         rxComponentContext,
426         PresenterConfigurationAccess::msPresenterScreenRootName,
427         PresenterConfigurationAccess::READ_ONLY);
428     return Reference<beans::XPropertySet>(
429         PresenterConfigurationAccess::Find (
430             Reference<container::XNameAccess>(
431                 aConfiguration.GetConfigurationNode("PresenterScreenSettings/Buttons"),
432                 UNO_QUERY),
433             [&rsConfigurationName](OUString const&, uno::Reference<beans::XPropertySet> const& xProps) -> bool
434             {
435                 return PresenterConfigurationAccess::IsStringPropertyEqual(
436                         rsConfigurationName, "Name", xProps);
437             }),
438         UNO_QUERY);
439 }
440 
ThrowIfDisposed() const441 void PresenterButton::ThrowIfDisposed() const
442 {
443     if (rBHelper.bDisposed || rBHelper.bInDispose)
444     {
445         throw lang::DisposedException (
446             "PresenterButton object has already been disposed",
447             const_cast<uno::XWeak*>(static_cast<const uno::XWeak*>(this)));
448     }
449 }
450 
451 } // end of namespace sdext::presenter
452 
453 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
454