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 <vcl/settings.hxx>
21 
22 #include "PresenterSlideSorter.hxx"
23 #include "PresenterButton.hxx"
24 #include "PresenterCanvasHelper.hxx"
25 #include "PresenterGeometryHelper.hxx"
26 #include "PresenterPaintManager.hxx"
27 #include "PresenterPaneBase.hxx"
28 #include "PresenterScrollBar.hxx"
29 #include "PresenterUIPainter.hxx"
30 #include "PresenterWindowManager.hxx"
31 #include <com/sun/star/drawing/framework/XConfigurationController.hpp>
32 #include <com/sun/star/drawing/framework/XControllerManager.hpp>
33 #include <com/sun/star/rendering/XBitmapCanvas.hpp>
34 #include <com/sun/star/rendering/CompositeOperation.hpp>
35 #include <com/sun/star/rendering/TextDirection.hpp>
36 #include <algorithm>
37 #include <math.h>
38 
39 using namespace ::com::sun::star;
40 using namespace ::com::sun::star::uno;
41 using namespace ::com::sun::star::drawing::framework;
42 
43 namespace {
44     const sal_Int32 gnVerticalGap (10);
45     const sal_Int32 gnVerticalBorder (10);
46     const sal_Int32 gnHorizontalGap (10);
47     const sal_Int32 gnHorizontalBorder (10);
48 
49     const double gnMinimalPreviewWidth (200);
50     const double gnPreferredPreviewWidth (300);
51     const double gnMaximalPreviewWidth (400);
52     const sal_Int32 gnPreferredColumnCount (6);
53     const double gnMinimalHorizontalPreviewGap(15);
54     const double gnPreferredHorizontalPreviewGap(25);
55     const double gnMaximalHorizontalPreviewGap(50);
56     const double gnPreferredVerticalPreviewGap(25);
57 
58     const sal_Int32 gnHorizontalLabelBorder (3);
59     const sal_Int32 gnHorizontalLabelPadding (5);
60 
61     const sal_Int32 gnVerticalButtonPadding (gnVerticalGap);
62 }
63 
64 namespace sdext::presenter {
65 
66 namespace {
round(const double nValue)67     sal_Int32 round (const double nValue) { return sal::static_int_cast<sal_Int32>(0.5 + nValue); }
floor(const double nValue)68     sal_Int32 floor (const double nValue) { return sal::static_int_cast<sal_Int32>(nValue); }
69 }
70 
71 //===== PresenterSlideSorter::Layout ==========================================
72 
73 class PresenterSlideSorter::Layout
74 {
75 public:
76     explicit Layout (const ::rtl::Reference<PresenterScrollBar>& rpVerticalScrollBar);
77 
78     void Update (const geometry::RealRectangle2D& rBoundingBox, const double nSlideAspectRatio);
79     void SetupVisibleArea();
80     void UpdateScrollBars();
81     bool IsScrollBarNeeded (const sal_Int32 nSlideCount);
82     geometry::RealPoint2D GetLocalPosition (const geometry::RealPoint2D& rWindowPoint) const;
83     geometry::RealPoint2D GetWindowPosition(const geometry::RealPoint2D& rLocalPoint) const;
84     sal_Int32 GetColumn (const geometry::RealPoint2D& rLocalPoint) const;
85     sal_Int32 GetRow (const geometry::RealPoint2D& rLocalPoint,
86         const bool bReturnInvalidValue = false) const;
87     sal_Int32 GetSlideIndexForPosition (const css::geometry::RealPoint2D& rPoint) const;
88     css::geometry::RealPoint2D GetPoint (
89         const sal_Int32 nSlideIndex,
90         const sal_Int32 nRelativeHorizontalPosition,
91         const sal_Int32 nRelativeVerticalPosition) const;
92     css::awt::Rectangle GetBoundingBox (const sal_Int32 nSlideIndex) const;
93     void ForAllVisibleSlides (const ::std::function<void (sal_Int32)>& rAction);
94     sal_Int32 GetFirstVisibleSlideIndex() const;
95     sal_Int32 GetLastVisibleSlideIndex() const;
96     bool SetHorizontalOffset (const double nOffset);
97     bool SetVerticalOffset (const double nOffset);
98 
99     css::geometry::RealRectangle2D maBoundingBox;
100     css::geometry::IntegerSize2D maPreviewSize;
101     sal_Int32 mnHorizontalOffset;
102     sal_Int32 mnVerticalOffset;
103     sal_Int32 mnHorizontalGap;
104     sal_Int32 mnVerticalGap;
105     sal_Int32 mnHorizontalBorder;
106     sal_Int32 mnVerticalBorder;
107     sal_Int32 mnRowCount;
108     sal_Int32 mnColumnCount;
109     sal_Int32 mnSlideCount;
110     sal_Int32 mnFirstVisibleColumn;
111     sal_Int32 mnLastVisibleColumn;
112     sal_Int32 mnFirstVisibleRow;
113     sal_Int32 mnLastVisibleRow;
114 
115 private:
116     ::rtl::Reference<PresenterScrollBar> mpVerticalScrollBar;
117 
118     sal_Int32 GetIndex (const sal_Int32 nRow, const sal_Int32 nColumn) const;
119     sal_Int32 GetRow (const sal_Int32 nSlideIndex) const;
120     sal_Int32 GetColumn (const sal_Int32 nSlideIndex) const;
121 };
122 
123 //==== PresenterSlideSorter::MouseOverManager =================================
124 
125 class PresenterSlideSorter::MouseOverManager
126 {
127 public:
128     MouseOverManager (
129         const Reference<container::XIndexAccess>& rxSlides,
130         const std::shared_ptr<PresenterTheme>& rpTheme,
131         const Reference<awt::XWindow>& rxInvalidateTarget,
132         const std::shared_ptr<PresenterPaintManager>& rpPaintManager);
133     MouseOverManager(const MouseOverManager&) = delete;
134     MouseOverManager& operator=(const MouseOverManager&) = delete;
135 
136     void Paint (
137         const sal_Int32 nSlideIndex,
138         const Reference<rendering::XCanvas>& rxCanvas,
139         const Reference<rendering::XPolyPolygon2D>& rxClip);
140 
141     void SetSlide (
142         const sal_Int32 nSlideIndex,
143         const awt::Rectangle& rBox);
144 
145 private:
146     Reference<rendering::XCanvas> mxCanvas;
147     const Reference<container::XIndexAccess> mxSlides;
148     SharedBitmapDescriptor mpLeftLabelBitmap;
149     SharedBitmapDescriptor mpCenterLabelBitmap;
150     SharedBitmapDescriptor mpRightLabelBitmap;
151     PresenterTheme::SharedFontDescriptor mpFont;
152     sal_Int32 mnSlideIndex;
153     awt::Rectangle maSlideBoundingBox;
154     OUString msText;
155     Reference<rendering::XBitmap> mxBitmap;
156     Reference<awt::XWindow> mxInvalidateTarget;
157     std::shared_ptr<PresenterPaintManager> mpPaintManager;
158 
159     void SetCanvas (
160         const Reference<rendering::XCanvas>& rxCanvas);
161     /** Create a bitmap that shows the given text and is not wider than the
162         given maximal width.
163     */
164     Reference<rendering::XBitmap> CreateBitmap (
165         const OUString& rsText,
166         const sal_Int32 nMaximalWidth) const;
167     void Invalidate();
168     geometry::IntegerSize2D CalculateLabelSize (
169         const OUString& rsText) const;
170     OUString GetFittingText (const OUString& rsText, const double nMaximalWidth) const;
171     void PaintButtonBackground (
172         const Reference<rendering::XCanvas>& rxCanvas,
173         const geometry::IntegerSize2D& rSize) const;
174 };
175 
176 //==== PresenterSlideSorter::CurrentSlideFrameRenderer ========================
177 
178 class PresenterSlideSorter::CurrentSlideFrameRenderer
179 {
180 public:
181     CurrentSlideFrameRenderer (
182         const css::uno::Reference<css::uno::XComponentContext>& rxContext,
183         const css::uno::Reference<css::rendering::XCanvas>& rxCanvas);
184 
185     void PaintCurrentSlideFrame (
186         const awt::Rectangle& rSlideBoundingBox,
187         const Reference<rendering::XCanvas>& rxCanvas,
188         const geometry::RealRectangle2D& rClipBox);
189 
190     /** Enlarge the given rectangle to include the current slide indicator.
191     */
192     awt::Rectangle GetBoundingBox (
193         const awt::Rectangle& rSlideBoundingBox);
194 
195 private:
196     SharedBitmapDescriptor mpTopLeft;
197     SharedBitmapDescriptor mpTop;
198     SharedBitmapDescriptor mpTopRight;
199     SharedBitmapDescriptor mpLeft;
200     SharedBitmapDescriptor mpRight;
201     SharedBitmapDescriptor mpBottomLeft;
202     SharedBitmapDescriptor mpBottom;
203     SharedBitmapDescriptor mpBottomRight;
204     sal_Int32 mnTopFrameSize;
205     sal_Int32 mnLeftFrameSize;
206     sal_Int32 mnRightFrameSize;
207     sal_Int32 mnBottomFrameSize;
208 
209     static void PaintBitmapOnce(
210         const css::uno::Reference<css::rendering::XBitmap>& rxBitmap,
211         const css::uno::Reference<css::rendering::XCanvas>& rxCanvas,
212         const Reference<rendering::XPolyPolygon2D>& rxClip,
213         const double nX,
214         const double nY);
215     static void PaintBitmapTiled(
216         const css::uno::Reference<css::rendering::XBitmap>& rxBitmap,
217         const css::uno::Reference<css::rendering::XCanvas>& rxCanvas,
218         const geometry::RealRectangle2D& rClipBox,
219         const double nX,
220         const double nY,
221         const double nWidth,
222         const double nHeight);
223 };
224 
225 //===== PresenterSlideSorter ==================================================
226 
PresenterSlideSorter(const Reference<uno::XComponentContext> & rxContext,const Reference<XResourceId> & rxViewId,const Reference<frame::XController> & rxController,const::rtl::Reference<PresenterController> & rpPresenterController)227 PresenterSlideSorter::PresenterSlideSorter (
228     const Reference<uno::XComponentContext>& rxContext,
229     const Reference<XResourceId>& rxViewId,
230     const Reference<frame::XController>& rxController,
231     const ::rtl::Reference<PresenterController>& rpPresenterController)
232     : PresenterSlideSorterInterfaceBase(m_aMutex),
233       mxComponentContext(rxContext),
234       mxViewId(rxViewId),
235       mxPane(),
236       mxCanvas(),
237       mxWindow(),
238       mpPresenterController(rpPresenterController),
239       mxSlideShowController(mpPresenterController->GetSlideShowController()),
240       mxPreviewCache(),
241       mbIsLayoutPending(true),
242       mpLayout(),
243       mpVerticalScrollBar(),
244       mpCloseButton(),
245       mpMouseOverManager(),
246       mnSlideIndexMousePressed(-1),
247       mnCurrentSlideIndex(-1),
248       mnSeparatorY(0),
249       maSeparatorColor(0x00ffffff),
250       maCurrentSlideFrameBoundingBox(),
251       mpCurrentSlideFrameRenderer(),
252       mxPreviewFrame()
253 {
254     if ( ! rxContext.is()
255         || ! rxViewId.is()
256         || ! rxController.is()
257         || ! rpPresenterController)
258     {
259         throw lang::IllegalArgumentException();
260     }
261 
262     if ( ! mxSlideShowController.is())
263         throw RuntimeException();
264 
265     try
266     {
267         // Get pane and window.
268         Reference<XControllerManager> xCM (rxController, UNO_QUERY_THROW);
269         Reference<XConfigurationController> xCC (
270             xCM->getConfigurationController(), UNO_SET_THROW);
271         Reference<lang::XMultiComponentFactory> xFactory (
272             mxComponentContext->getServiceManager(), UNO_SET_THROW);
273 
274         mxPane.set(xCC->getResource(rxViewId->getAnchor()), UNO_QUERY_THROW);
275         mxWindow = mxPane->getWindow();
276 
277         // Add window listener.
278         mxWindow->addWindowListener(this);
279         mxWindow->addPaintListener(this);
280         mxWindow->addMouseListener(this);
281         mxWindow->addMouseMotionListener(this);
282         mxWindow->setVisible(true);
283 
284         // Remember the current slide.
285         mnCurrentSlideIndex = mxSlideShowController->getCurrentSlideIndex();
286 
287         // Create the scroll bar.
288         mpVerticalScrollBar.set(
289             new PresenterVerticalScrollBar(
290                 rxContext,
291                 mxWindow,
292                 mpPresenterController->GetPaintManager(),
293                 [this] (double const offset) { return this->SetVerticalOffset(offset); }));
294 
295         mpCloseButton = PresenterButton::Create(
296             rxContext,
297             mpPresenterController,
298             mpPresenterController->GetTheme(),
299             mxWindow,
300             mxCanvas,
301             "SlideSorterCloser");
302 
303         if (mpPresenterController->GetTheme() != nullptr)
304         {
305             PresenterTheme::SharedFontDescriptor pFont (
306                 mpPresenterController->GetTheme()->GetFont("ButtonFont"));
307             if (pFont)
308                 maSeparatorColor = pFont->mnColor;
309         }
310 
311         // Create the layout.
312         mpLayout = std::make_shared<Layout>(mpVerticalScrollBar);
313 
314         // Create the preview cache.
315         mxPreviewCache.set(
316             xFactory->createInstanceWithContext(
317                 "com.sun.star.drawing.PresenterPreviewCache",
318                 mxComponentContext),
319             UNO_QUERY_THROW);
320         Reference<container::XIndexAccess> xSlides (mxSlideShowController, UNO_QUERY);
321         mxPreviewCache->setDocumentSlides(xSlides, rxController->getModel());
322         mxPreviewCache->addPreviewCreationNotifyListener(this);
323         if (xSlides.is())
324         {
325             mpLayout->mnSlideCount = xSlides->getCount();
326         }
327 
328         // Create the mouse over manager.
329         mpMouseOverManager.reset(new MouseOverManager(
330             Reference<container::XIndexAccess>(mxSlideShowController, UNO_QUERY),
331             mpPresenterController->GetTheme(),
332             mxWindow,
333             mpPresenterController->GetPaintManager()));
334 
335         // Listen for changes of the current slide.
336         Reference<beans::XPropertySet> xControllerProperties (rxController, UNO_QUERY_THROW);
337         xControllerProperties->addPropertyChangeListener(
338             "CurrentPage",
339             this);
340 
341         // Move the current slide in the center of the window.
342         const awt::Rectangle aCurrentSlideBBox (mpLayout->GetBoundingBox(mnCurrentSlideIndex));
343         const awt::Rectangle aWindowBox (mxWindow->getPosSize());
344         SetHorizontalOffset(aCurrentSlideBBox.X - aWindowBox.Width/2.0);
345     }
346     catch (RuntimeException&)
347     {
348         disposing();
349         throw;
350     }
351 }
352 
~PresenterSlideSorter()353 PresenterSlideSorter::~PresenterSlideSorter()
354 {
355 }
356 
disposing()357 void SAL_CALL PresenterSlideSorter::disposing()
358 {
359     mxComponentContext = nullptr;
360     mxViewId = nullptr;
361     mxPane = nullptr;
362 
363     if (mpVerticalScrollBar.is())
364     {
365         Reference<lang::XComponent> xComponent (
366             static_cast<XWeak*>(mpVerticalScrollBar.get()), UNO_QUERY);
367         mpVerticalScrollBar = nullptr;
368         if (xComponent.is())
369             xComponent->dispose();
370     }
371     if (mpCloseButton.is())
372     {
373         Reference<lang::XComponent> xComponent (
374             static_cast<XWeak*>(mpCloseButton.get()), UNO_QUERY);
375         mpCloseButton = nullptr;
376         if (xComponent.is())
377             xComponent->dispose();
378     }
379 
380     if (mxCanvas.is())
381     {
382         Reference<lang::XComponent> xComponent (mxCanvas, UNO_QUERY);
383         if (xComponent.is())
384             xComponent->removeEventListener(static_cast<awt::XWindowListener*>(this));
385         mxCanvas = nullptr;
386     }
387     mpPresenterController = nullptr;
388     mxSlideShowController = nullptr;
389     mpLayout.reset();
390     mpMouseOverManager.reset();
391 
392     if (mxPreviewCache.is())
393     {
394         mxPreviewCache->removePreviewCreationNotifyListener(this);
395 
396         Reference<XComponent> xComponent (mxPreviewCache, UNO_QUERY);
397         mxPreviewCache = nullptr;
398         if (xComponent.is())
399             xComponent->dispose();
400     }
401 
402     if (mxWindow.is())
403     {
404         mxWindow->removeWindowListener(this);
405         mxWindow->removePaintListener(this);
406         mxWindow->removeMouseListener(this);
407         mxWindow->removeMouseMotionListener(this);
408     }
409 }
410 
411 //----- lang::XEventListener --------------------------------------------------
412 
disposing(const lang::EventObject & rEventObject)413 void SAL_CALL PresenterSlideSorter::disposing (const lang::EventObject& rEventObject)
414 {
415     if (rEventObject.Source == mxWindow)
416     {
417         mxWindow = nullptr;
418         dispose();
419     }
420     else if (rEventObject.Source == mxPreviewCache)
421     {
422         mxPreviewCache = nullptr;
423         dispose();
424     }
425     else if (rEventObject.Source == mxCanvas)
426     {
427         mxCanvas = nullptr;
428         mbIsLayoutPending = true;
429 
430         mpPresenterController->GetPaintManager()->Invalidate(mxWindow);
431     }
432 }
433 
434 //----- XWindowListener -------------------------------------------------------
435 
windowResized(const awt::WindowEvent &)436 void SAL_CALL PresenterSlideSorter::windowResized (const awt::WindowEvent&)
437 {
438     ThrowIfDisposed();
439     mbIsLayoutPending = true;
440     mpPresenterController->GetPaintManager()->Invalidate(mxWindow);
441 }
442 
windowMoved(const awt::WindowEvent &)443 void SAL_CALL PresenterSlideSorter::windowMoved (const awt::WindowEvent&)
444 {
445     ThrowIfDisposed();
446 }
447 
windowShown(const lang::EventObject &)448 void SAL_CALL PresenterSlideSorter::windowShown (const lang::EventObject&)
449 {
450     ThrowIfDisposed();
451     mbIsLayoutPending = true;
452     mpPresenterController->GetPaintManager()->Invalidate(mxWindow);
453 }
454 
windowHidden(const lang::EventObject &)455 void SAL_CALL PresenterSlideSorter::windowHidden (const lang::EventObject&)
456 {
457     ThrowIfDisposed();
458 }
459 
460 //----- XPaintListener --------------------------------------------------------
461 
windowPaint(const css::awt::PaintEvent & rEvent)462 void SAL_CALL PresenterSlideSorter::windowPaint (const css::awt::PaintEvent& rEvent)
463 {
464     // Deactivated views must not be painted.
465     if ( ! mbIsPresenterViewActive)
466         return;
467 
468     Paint(rEvent.UpdateRect);
469 
470     Reference<rendering::XSpriteCanvas> xSpriteCanvas (mxCanvas, UNO_QUERY);
471     if (xSpriteCanvas.is())
472         xSpriteCanvas->updateScreen(false);
473 }
474 
475 //----- XMouseListener --------------------------------------------------------
476 
mousePressed(const css::awt::MouseEvent & rEvent)477 void SAL_CALL PresenterSlideSorter::mousePressed (const css::awt::MouseEvent& rEvent)
478 {
479     css::awt::MouseEvent rTemp =rEvent;
480     /// check whether RTL interface or not
481     if(AllSettings::GetLayoutRTL()){
482         awt::Rectangle aBox = mxWindow->getPosSize();
483         rTemp.X=aBox.Width-rEvent.X;
484     }
485     const geometry::RealPoint2D aPosition(rTemp.X, rEvent.Y);
486     mnSlideIndexMousePressed = mpLayout->GetSlideIndexForPosition(aPosition);
487 }
488 
mouseReleased(const css::awt::MouseEvent & rEvent)489 void SAL_CALL PresenterSlideSorter::mouseReleased (const css::awt::MouseEvent& rEvent)
490 {
491     css::awt::MouseEvent rTemp =rEvent;
492     /// check whether RTL interface or not
493     if(AllSettings::GetLayoutRTL()){
494         awt::Rectangle aBox = mxWindow->getPosSize();
495         rTemp.X=aBox.Width-rEvent.X;
496     }
497     const geometry::RealPoint2D aPosition(rTemp.X, rEvent.Y);
498     const sal_Int32 nSlideIndex (mpLayout->GetSlideIndexForPosition(aPosition));
499 
500     if (nSlideIndex != mnSlideIndexMousePressed || mnSlideIndexMousePressed < 0)
501         return;
502 
503     switch (rEvent.ClickCount)
504     {
505         case 1:
506         default:
507             GotoSlide(nSlideIndex);
508             break;
509 
510         case 2:
511             OSL_ASSERT(mpPresenterController);
512             OSL_ASSERT(mpPresenterController->GetWindowManager());
513             mpPresenterController->GetWindowManager()->SetSlideSorterState(false);
514             GotoSlide(nSlideIndex);
515             break;
516     }
517 }
518 
mouseEntered(const css::awt::MouseEvent &)519 void SAL_CALL PresenterSlideSorter::mouseEntered (const css::awt::MouseEvent&) {}
520 
mouseExited(const css::awt::MouseEvent &)521 void SAL_CALL PresenterSlideSorter::mouseExited (const css::awt::MouseEvent&)
522 {
523     mnSlideIndexMousePressed = -1;
524     if (mpMouseOverManager != nullptr)
525         mpMouseOverManager->SetSlide(mnSlideIndexMousePressed, awt::Rectangle(0,0,0,0));
526 }
527 
528 //----- XMouseMotionListener --------------------------------------------------
529 
mouseMoved(const css::awt::MouseEvent & rEvent)530 void SAL_CALL PresenterSlideSorter::mouseMoved (const css::awt::MouseEvent& rEvent)
531 {
532     if (mpMouseOverManager == nullptr)
533         return;
534 
535     css::awt::MouseEvent rTemp =rEvent;
536     /// check whether RTL interface or not
537     if(AllSettings::GetLayoutRTL()){
538         awt::Rectangle aBox = mxWindow->getPosSize();
539         rTemp.X=aBox.Width-rEvent.X;
540     }
541     const geometry::RealPoint2D aPosition(rTemp.X, rEvent.Y);
542     sal_Int32 nSlideIndex (mpLayout->GetSlideIndexForPosition(aPosition));
543 
544     if (nSlideIndex < 0)
545     {
546         mnSlideIndexMousePressed = -1;
547         mpMouseOverManager->SetSlide(nSlideIndex, awt::Rectangle(0,0,0,0));
548     }
549     else
550     {
551         mpMouseOverManager->SetSlide(
552             nSlideIndex,
553             mpLayout->GetBoundingBox(nSlideIndex));
554     }
555 }
556 
mouseDragged(const css::awt::MouseEvent &)557 void SAL_CALL PresenterSlideSorter::mouseDragged (const css::awt::MouseEvent&) {}
558 
559 //----- XResourceId -----------------------------------------------------------
560 
getResourceId()561 Reference<XResourceId> SAL_CALL PresenterSlideSorter::getResourceId()
562 {
563     ThrowIfDisposed();
564     return mxViewId;
565 }
566 
isAnchorOnly()567 sal_Bool SAL_CALL PresenterSlideSorter::isAnchorOnly()
568 {
569     return false;
570 }
571 
572 //----- XPropertyChangeListener -----------------------------------------------
573 
propertyChange(const css::beans::PropertyChangeEvent &)574 void SAL_CALL PresenterSlideSorter::propertyChange (
575     const css::beans::PropertyChangeEvent&)
576 {}
577 
578 //----- XSlidePreviewCacheListener --------------------------------------------
579 
notifyPreviewCreation(sal_Int32 nSlideIndex)580 void SAL_CALL PresenterSlideSorter::notifyPreviewCreation (
581     sal_Int32 nSlideIndex)
582 {
583     OSL_ASSERT(mpLayout != nullptr);
584 
585     awt::Rectangle aBBox (mpLayout->GetBoundingBox(nSlideIndex));
586     mpPresenterController->GetPaintManager()->Invalidate(mxWindow, aBBox, true);
587 }
588 
589 //----- XDrawView -------------------------------------------------------------
590 
setCurrentPage(const Reference<drawing::XDrawPage> &)591 void SAL_CALL PresenterSlideSorter::setCurrentPage (const Reference<drawing::XDrawPage>&)
592 {
593     ThrowIfDisposed();
594     ::osl::MutexGuard aGuard (::osl::Mutex::getGlobalMutex());
595 
596     if (!mxSlideShowController.is())
597         return;
598 
599     const sal_Int32 nNewCurrentSlideIndex (mxSlideShowController->getCurrentSlideIndex());
600     if (nNewCurrentSlideIndex == mnCurrentSlideIndex)
601         return;
602 
603     mnCurrentSlideIndex = nNewCurrentSlideIndex;
604 
605     // Request a repaint of the previous current slide to hide its
606     // current slide indicator.
607     mpPresenterController->GetPaintManager()->Invalidate(
608         mxWindow,
609         maCurrentSlideFrameBoundingBox);
610 
611     // Request a repaint of the new current slide to show its
612     // current slide indicator.
613     maCurrentSlideFrameBoundingBox = mpCurrentSlideFrameRenderer->GetBoundingBox(
614         mpLayout->GetBoundingBox(mnCurrentSlideIndex));
615     mpPresenterController->GetPaintManager()->Invalidate(
616         mxWindow,
617         maCurrentSlideFrameBoundingBox);
618 }
619 
getCurrentPage()620 Reference<drawing::XDrawPage> SAL_CALL PresenterSlideSorter::getCurrentPage()
621 {
622     ThrowIfDisposed();
623     return nullptr;
624 }
625 
626 
UpdateLayout()627 void PresenterSlideSorter::UpdateLayout()
628 {
629     if ( ! mxWindow.is())
630         return;
631 
632     mbIsLayoutPending = false;
633 
634     const awt::Rectangle aWindowBox (mxWindow->getPosSize());
635     sal_Int32 nLeftBorderWidth (aWindowBox.X);
636 
637     // Get border width.
638     PresenterPaneContainer::SharedPaneDescriptor pPane (
639         mpPresenterController->GetPaneContainer()->FindViewURL(
640             mxViewId->getResourceURL()));
641     do
642     {
643         if (!pPane)
644             break;
645         if ( ! pPane->mxPane.is())
646             break;
647 
648         Reference<drawing::framework::XPaneBorderPainter> xBorderPainter (
649             pPane->mxPane->GetPaneBorderPainter());
650         if ( ! xBorderPainter.is())
651             break;
652         xBorderPainter->addBorder (
653             mxViewId->getAnchor()->getResourceURL(),
654             awt::Rectangle(0, 0, aWindowBox.Width, aWindowBox.Height),
655             drawing::framework::BorderType_INNER_BORDER);
656     }
657     while(false);
658 
659     // Place vertical separator.
660     mnSeparatorY = aWindowBox.Height - mpCloseButton->GetSize().Height - gnVerticalButtonPadding;
661 
662     PlaceCloseButton(pPane, aWindowBox, nLeftBorderWidth);
663 
664     geometry::RealRectangle2D aUpperBox(
665         gnHorizontalBorder,
666         gnVerticalBorder,
667         aWindowBox.Width - 2*gnHorizontalBorder,
668         mnSeparatorY - gnVerticalGap);
669 
670     // Determine whether the scroll bar has to be displayed.
671     aUpperBox = PlaceScrollBars(aUpperBox);
672 
673     mpLayout->Update(aUpperBox, GetSlideAspectRatio());
674     mpLayout->SetupVisibleArea();
675     mpLayout->UpdateScrollBars();
676 
677     // Tell the preview cache about some of the values.
678     mxPreviewCache->setPreviewSize(mpLayout->maPreviewSize);
679     mxPreviewCache->setVisibleRange(
680         mpLayout->GetFirstVisibleSlideIndex(),
681         mpLayout->GetLastVisibleSlideIndex());
682 
683     // Clear the frame polygon so that it is re-created on the next paint.
684     mxPreviewFrame = nullptr;
685 }
686 
PlaceScrollBars(const geometry::RealRectangle2D & rUpperBox)687 geometry::RealRectangle2D PresenterSlideSorter::PlaceScrollBars (
688     const geometry::RealRectangle2D& rUpperBox)
689 {
690     mpLayout->Update(rUpperBox, GetSlideAspectRatio());
691     bool bIsScrollBarNeeded (false);
692     Reference<container::XIndexAccess> xSlides (mxSlideShowController, UNO_QUERY_THROW);
693     bIsScrollBarNeeded = mpLayout->IsScrollBarNeeded(xSlides->getCount());
694     if (mpVerticalScrollBar)
695         {
696             if (bIsScrollBarNeeded)
697                 {
698                     if(AllSettings::GetLayoutRTL())
699                         {
700                             mpVerticalScrollBar->SetPosSize(geometry::RealRectangle2D(
701                                                                                       rUpperBox.X1,
702                                                                                       rUpperBox.Y1,
703                                                                                       rUpperBox.X1 +  mpVerticalScrollBar->GetSize(),
704                                                                                       rUpperBox.Y2));
705                             mpVerticalScrollBar->SetVisible(true);
706                             // Reduce area covered by the scroll bar from the available
707                             // space.
708                             return geometry::RealRectangle2D(
709                                                              rUpperBox.X1 + gnHorizontalGap + mpVerticalScrollBar->GetSize(),
710                                                              rUpperBox.Y1,
711                                                              rUpperBox.X2,
712                                                              rUpperBox.Y2);
713                         }
714                     else
715                         {
716                             // if it's not RTL place vertical scroll bar at right border.
717                             mpVerticalScrollBar->SetPosSize(geometry::RealRectangle2D(
718                                                                                       rUpperBox.X2 - mpVerticalScrollBar->GetSize(),
719                                                                                       rUpperBox.Y1,
720                                                                                       rUpperBox.X2,
721                                                                                       rUpperBox.Y2));
722                             mpVerticalScrollBar->SetVisible(true);
723                             // Reduce area covered by the scroll bar from the available
724                             // space.
725                             return geometry::RealRectangle2D(
726                                                              rUpperBox.X1,
727                                                              rUpperBox.Y1,
728                                                              rUpperBox.X2 - mpVerticalScrollBar->GetSize() - gnHorizontalGap,
729                                                              rUpperBox.Y2);
730                         }
731                 }
732             else
733                 mpVerticalScrollBar->SetVisible(false);
734         }
735     return rUpperBox;
736 }
737 
PlaceCloseButton(const PresenterPaneContainer::SharedPaneDescriptor & rpPane,const awt::Rectangle & rCenterBox,const sal_Int32 nLeftBorderWidth)738 void PresenterSlideSorter::PlaceCloseButton (
739     const PresenterPaneContainer::SharedPaneDescriptor& rpPane,
740     const awt::Rectangle& rCenterBox,
741     const sal_Int32 nLeftBorderWidth)
742 {
743     // Place button.  When the callout is near the center then the button is
744     // centered over the callout.  Otherwise it is centered with respect to
745     // the whole window.
746     sal_Int32 nCloseButtonCenter (rCenterBox.Width/2);
747     if (rpPane && rpPane->mxPane.is())
748     {
749         const sal_Int32 nCalloutCenter (-nLeftBorderWidth);
750         const sal_Int32 nDistanceFromWindowCenter (abs(nCalloutCenter - rCenterBox.Width/2));
751         const sal_Int32 nButtonWidth (mpCloseButton->GetSize().Width);
752         const static sal_Int32 nMaxDistanceForCalloutCentering (nButtonWidth * 2);
753         if (nDistanceFromWindowCenter < nMaxDistanceForCalloutCentering)
754         {
755             if (nCalloutCenter < nButtonWidth/2)
756                 nCloseButtonCenter = nButtonWidth/2;
757             else if (nCalloutCenter > rCenterBox.Width-nButtonWidth/2)
758                 nCloseButtonCenter = rCenterBox.Width-nButtonWidth/2;
759             else
760                 nCloseButtonCenter = nCalloutCenter;
761         }
762     }
763     mpCloseButton->SetCenter(geometry::RealPoint2D(
764         nCloseButtonCenter,
765         rCenterBox.Height - mpCloseButton->GetSize().Height/ 2));
766 }
767 
ClearBackground(const Reference<rendering::XCanvas> & rxCanvas,const awt::Rectangle & rUpdateBox)768 void PresenterSlideSorter::ClearBackground (
769     const Reference<rendering::XCanvas>& rxCanvas,
770     const awt::Rectangle& rUpdateBox)
771 {
772     OSL_ASSERT(rxCanvas.is());
773 
774     const awt::Rectangle aWindowBox (mxWindow->getPosSize());
775     mpPresenterController->GetCanvasHelper()->Paint(
776         mpPresenterController->GetViewBackground(mxViewId->getResourceURL()),
777         rxCanvas,
778         rUpdateBox,
779         awt::Rectangle(0,0,aWindowBox.Width,aWindowBox.Height),
780         awt::Rectangle());
781 }
782 
GetSlideAspectRatio() const783 double PresenterSlideSorter::GetSlideAspectRatio() const
784 {
785     double nSlideAspectRatio (28.0/21.0);
786 
787     try
788     {
789         Reference<container::XIndexAccess> xSlides(mxSlideShowController, UNO_QUERY_THROW);
790         if (mxSlideShowController.is() && xSlides->getCount()>0)
791         {
792             Reference<beans::XPropertySet> xProperties(xSlides->getByIndex(0),UNO_QUERY_THROW);
793             sal_Int32 nWidth (28000);
794             sal_Int32 nHeight (21000);
795             if ((xProperties->getPropertyValue("Width") >>= nWidth)
796                 && (xProperties->getPropertyValue("Height") >>= nHeight)
797                 && nHeight > 0)
798             {
799                 nSlideAspectRatio = double(nWidth) / double(nHeight);
800             }
801         }
802     }
803     catch (RuntimeException&)
804     {
805         OSL_ASSERT(false);
806     }
807 
808     return nSlideAspectRatio;
809 }
810 
GetPreview(const sal_Int32 nSlideIndex)811 Reference<rendering::XBitmap> PresenterSlideSorter::GetPreview (const sal_Int32 nSlideIndex)
812 {
813     if (nSlideIndex < 0 || nSlideIndex>=mpLayout->mnSlideCount)
814         return nullptr;
815     else if (mxPane.is())
816         return mxPreviewCache->getSlidePreview(nSlideIndex, mxPane->getCanvas());
817     else
818         return nullptr;
819 }
820 
PaintPreview(const Reference<rendering::XCanvas> & rxCanvas,const css::awt::Rectangle & rUpdateBox,const sal_Int32 nSlideIndex)821 void PresenterSlideSorter::PaintPreview (
822     const Reference<rendering::XCanvas>& rxCanvas,
823     const css::awt::Rectangle& rUpdateBox,
824     const sal_Int32 nSlideIndex)
825 {
826     OSL_ASSERT(rxCanvas.is());
827 
828     geometry::IntegerSize2D aSize (mpLayout->maPreviewSize);
829 
830     if (PresenterGeometryHelper::AreRectanglesDisjoint(
831         rUpdateBox,
832         mpLayout->GetBoundingBox(nSlideIndex)))
833     {
834         return;
835     }
836 
837     Reference<rendering::XBitmap> xPreview (GetPreview(nSlideIndex));
838     bool isRTL = AllSettings::GetLayoutRTL();
839 
840     const geometry::RealPoint2D aTopLeft (
841                                           mpLayout->GetWindowPosition(
842                                                                       mpLayout->GetPoint(nSlideIndex, isRTL?1:-1, -1)));
843 
844     PresenterBitmapContainer aContainer (
845         "PresenterScreenSettings/ScrollBar/Bitmaps",
846         std::shared_ptr<PresenterBitmapContainer>(),
847         mxComponentContext,
848         rxCanvas);
849     Reference<container::XIndexAccess> xIndexAccess(mxSlideShowController, UNO_QUERY);
850     Reference<drawing::XDrawPage> xPage( xIndexAccess->getByIndex(nSlideIndex), UNO_QUERY);
851     bool bTransition = PresenterController::HasTransition(xPage);
852     bool bCustomAnimation = PresenterController::HasCustomAnimation(xPage);
853 
854     // Create clip rectangle as intersection of the current update area and
855     // the bounding box of all previews.
856     geometry::RealRectangle2D aBoundingBox (mpLayout->maBoundingBox);
857     aBoundingBox.Y2 += 1;
858     const geometry::RealRectangle2D aClipBox (
859         PresenterGeometryHelper::Intersection(
860             PresenterGeometryHelper::ConvertRectangle(rUpdateBox),
861             aBoundingBox));
862     Reference<rendering::XPolyPolygon2D> xClip (
863         PresenterGeometryHelper::CreatePolygon(aClipBox, rxCanvas->getDevice()));
864 
865     const rendering::ViewState aViewState (geometry::AffineMatrix2D(1,0,0, 0,1,0), xClip);
866 
867     rendering::RenderState aRenderState (
868         geometry::AffineMatrix2D(
869             1, 0, aTopLeft.X,
870             0, 1, aTopLeft.Y),
871         nullptr,
872         Sequence<double>(4),
873         rendering::CompositeOperation::SOURCE);
874 
875     // Emphasize the current slide.
876     if (nSlideIndex == mnCurrentSlideIndex)
877     {
878         if (mpCurrentSlideFrameRenderer != nullptr)
879         {
880             const awt::Rectangle aSlideBoundingBox(
881                 sal::static_int_cast<sal_Int32>(0.5 + aTopLeft.X),
882                 sal::static_int_cast<sal_Int32>(0.5 + aTopLeft.Y),
883                 aSize.Width,
884                 aSize.Height);
885             maCurrentSlideFrameBoundingBox
886                 = mpCurrentSlideFrameRenderer->GetBoundingBox(aSlideBoundingBox);
887             mpCurrentSlideFrameRenderer->PaintCurrentSlideFrame (
888                 aSlideBoundingBox,
889                 mxCanvas,
890                 aClipBox);
891         }
892     }
893 
894     // Paint the preview.
895     if (xPreview.is())
896     {
897         aSize = xPreview->getSize();
898         if (aSize.Width > 0 && aSize.Height > 0)
899         {
900             rxCanvas->drawBitmap(xPreview, aViewState, aRenderState);
901             if( bCustomAnimation )
902             {
903                 const awt::Rectangle aAnimationPreviewBox(aTopLeft.X+3, aTopLeft.Y+aSize.Height-40, 0, 0);
904                 SharedBitmapDescriptor aAnimationDescriptor = aContainer.GetBitmap("Animation");
905                 Reference<rendering::XBitmap> xAnimationIcon (aAnimationDescriptor->GetNormalBitmap());
906                 rendering::RenderState aAnimationRenderState (
907                     geometry::AffineMatrix2D(
908                     1, 0, aAnimationPreviewBox.X,
909                     0, 1, aAnimationPreviewBox.Y),
910                     nullptr,
911                     Sequence<double>(4),
912                     rendering::CompositeOperation::SOURCE);
913                 rxCanvas->drawBitmap(xAnimationIcon, aViewState, aAnimationRenderState);
914             }
915             if( bTransition )
916             {
917                 const awt::Rectangle aTransitionPreviewBox(aTopLeft.X+3, aTopLeft.Y+aSize.Height-20, 0, 0);
918                 SharedBitmapDescriptor aTransitionDescriptor = aContainer.GetBitmap("Transition");
919                 Reference<rendering::XBitmap> xTransitionIcon (aTransitionDescriptor->GetNormalBitmap());
920                 rendering::RenderState aTransitionRenderState (
921                     geometry::AffineMatrix2D(
922                     1, 0, aTransitionPreviewBox.X,
923                     0, 1, aTransitionPreviewBox.Y),
924                     nullptr,
925                     Sequence<double>(4),
926                     rendering::CompositeOperation::SOURCE);
927                 rxCanvas->drawBitmap(xTransitionIcon, aViewState, aTransitionRenderState);
928             }
929         }
930     }
931 
932     // Create a polygon that is used to paint a frame around previews.  Its
933     // coordinates are chosen in the local coordinate system of a preview.
934     if ( ! mxPreviewFrame.is())
935         mxPreviewFrame = PresenterGeometryHelper::CreatePolygon(
936             awt::Rectangle(-1, -1, aSize.Width+2, aSize.Height+2),
937             rxCanvas->getDevice());
938 
939     // Paint a border around the preview.
940     if (mxPreviewFrame.is())
941     {
942         const util::Color aFrameColor (0x00000000);
943         PresenterCanvasHelper::SetDeviceColor(aRenderState, aFrameColor);
944         rxCanvas->drawPolyPolygon(mxPreviewFrame, aViewState, aRenderState);
945     }
946 
947     // Paint mouse over effect.
948     mpMouseOverManager->Paint(nSlideIndex, mxCanvas, xClip);
949 }
950 
Paint(const awt::Rectangle & rUpdateBox)951 void PresenterSlideSorter::Paint (const awt::Rectangle& rUpdateBox)
952 {
953     const bool bCanvasChanged ( ! mxCanvas.is());
954     if ( ! ProvideCanvas())
955         return;
956 
957     if (mpLayout->mnRowCount<=0 || mpLayout->mnColumnCount<=0)
958     {
959         OSL_ASSERT(mpLayout->mnRowCount>0 || mpLayout->mnColumnCount>0);
960         return;
961     }
962 
963     ClearBackground(mxCanvas, rUpdateBox);
964 
965     // Give the canvas to the controls.
966     if (bCanvasChanged)
967     {
968         if (mpVerticalScrollBar.is())
969             mpVerticalScrollBar->SetCanvas(mxCanvas);
970         if (mpCloseButton.is())
971             mpCloseButton->SetCanvas(mxCanvas, mxWindow);
972     }
973 
974     // Now that the controls have a canvas we can do the layouting.
975     if (mbIsLayoutPending)
976         UpdateLayout();
977 
978     // Paint the horizontal separator.
979     rendering::RenderState aRenderState (geometry::AffineMatrix2D(1,0,0, 0,1,0),
980             nullptr, Sequence<double>(4), rendering::CompositeOperation::SOURCE);
981     PresenterCanvasHelper::SetDeviceColor(aRenderState, maSeparatorColor);
982     mxCanvas->drawLine(
983         geometry::RealPoint2D(0, mnSeparatorY),
984         geometry::RealPoint2D(mxWindow->getPosSize().Width, mnSeparatorY),
985         rendering::ViewState(geometry::AffineMatrix2D(1,0,0, 0,1,0), nullptr),
986         aRenderState);
987 
988     // Paint the slides.
989     if ( ! PresenterGeometryHelper::AreRectanglesDisjoint(
990         rUpdateBox,
991         PresenterGeometryHelper::ConvertRectangle(mpLayout->maBoundingBox)))
992     {
993         mpLayout->ForAllVisibleSlides(
994             [this, &rUpdateBox] (sal_Int32 const nIndex) {
995                 return this->PaintPreview(this->mxCanvas, rUpdateBox, nIndex);
996             });
997     }
998 
999     Reference<rendering::XSpriteCanvas> xSpriteCanvas (mxCanvas, UNO_QUERY);
1000     if (xSpriteCanvas.is())
1001         xSpriteCanvas->updateScreen(false);
1002 }
1003 
SetHorizontalOffset(const double nXOffset)1004 void PresenterSlideSorter::SetHorizontalOffset (const double nXOffset)
1005 {
1006     if (mpLayout->SetHorizontalOffset(nXOffset))
1007     {
1008         mxPreviewCache->setVisibleRange(
1009             mpLayout->GetFirstVisibleSlideIndex(),
1010             mpLayout->GetLastVisibleSlideIndex());
1011 
1012         mpPresenterController->GetPaintManager()->Invalidate(mxWindow);
1013     }
1014 }
1015 
SetVerticalOffset(const double nYOffset)1016 void PresenterSlideSorter::SetVerticalOffset (const double nYOffset)
1017 {
1018     if (mpLayout->SetVerticalOffset(nYOffset))
1019     {
1020         mxPreviewCache->setVisibleRange(
1021             mpLayout->GetFirstVisibleSlideIndex(),
1022             mpLayout->GetLastVisibleSlideIndex());
1023 
1024         mpPresenterController->GetPaintManager()->Invalidate(mxWindow);
1025     }
1026 }
1027 
GotoSlide(const sal_Int32 nSlideIndex)1028 void PresenterSlideSorter::GotoSlide (const sal_Int32 nSlideIndex)
1029 {
1030     mxSlideShowController->gotoSlideIndex(nSlideIndex);
1031 }
1032 
ProvideCanvas()1033 bool PresenterSlideSorter::ProvideCanvas()
1034 {
1035     if ( ! mxCanvas.is())
1036     {
1037         if (mxPane.is())
1038             mxCanvas = mxPane->getCanvas();
1039 
1040         // Register as event listener so that we are informed when the
1041         // canvas is disposed (and we have to fetch another one).
1042         Reference<lang::XComponent> xComponent (mxCanvas, UNO_QUERY);
1043         if (xComponent.is())
1044             xComponent->addEventListener(static_cast<awt::XWindowListener*>(this));
1045 
1046         mpCurrentSlideFrameRenderer =
1047             std::make_shared<CurrentSlideFrameRenderer>(mxComponentContext, mxCanvas);
1048     }
1049     return mxCanvas.is();
1050 }
1051 
ThrowIfDisposed()1052 void PresenterSlideSorter::ThrowIfDisposed()
1053 {
1054     if (rBHelper.bDisposed || rBHelper.bInDispose)
1055     {
1056         throw lang::DisposedException (
1057             "PresenterSlideSorter has been already disposed",
1058             static_cast<uno::XWeak*>(this));
1059     }
1060 }
1061 
1062 //===== PresenterSlideSorter::Layout ==========================================
1063 
Layout(const::rtl::Reference<PresenterScrollBar> & rpVerticalScrollBar)1064 PresenterSlideSorter::Layout::Layout (
1065     const ::rtl::Reference<PresenterScrollBar>& rpVerticalScrollBar)
1066     : maBoundingBox(),
1067       maPreviewSize(),
1068       mnHorizontalOffset(0),
1069       mnVerticalOffset(0),
1070       mnHorizontalGap(0),
1071       mnVerticalGap(0),
1072       mnHorizontalBorder(0),
1073       mnVerticalBorder(0),
1074       mnRowCount(1),
1075       mnColumnCount(1),
1076       mnSlideCount(0),
1077       mnFirstVisibleColumn(-1),
1078       mnLastVisibleColumn(-1),
1079       mnFirstVisibleRow(-1),
1080       mnLastVisibleRow(-1),
1081       mpVerticalScrollBar(rpVerticalScrollBar)
1082 {
1083 }
1084 
Update(const geometry::RealRectangle2D & rBoundingBox,const double nSlideAspectRatio)1085 void PresenterSlideSorter::Layout::Update (
1086     const geometry::RealRectangle2D& rBoundingBox,
1087     const double nSlideAspectRatio)
1088 {
1089     maBoundingBox = rBoundingBox;
1090 
1091     mnHorizontalBorder = gnHorizontalBorder;
1092     mnVerticalBorder = gnVerticalBorder;
1093 
1094     const double nWidth (rBoundingBox.X2 - rBoundingBox.X1 - 2*mnHorizontalBorder);
1095     const double nHeight (rBoundingBox.Y2 - rBoundingBox.Y1 - 2*mnVerticalBorder);
1096     if (nWidth<=0 || nHeight<=0)
1097         return;
1098 
1099     double nPreviewWidth;
1100 
1101     // Determine column count, preview width, and horizontal gap (borders
1102     // are half the gap).  Try to use the preferred values.  Try more to
1103     // stay in the valid intervals.  This last constraint may be not
1104     // fulfilled in some cases.
1105     const double nElementWidth = nWidth / gnPreferredColumnCount;
1106     if (nElementWidth < gnMinimalPreviewWidth + gnMinimalHorizontalPreviewGap)
1107     {
1108         // The preferred column count is too large.
1109         // Can we use the preferred preview width?
1110         if (nWidth - gnMinimalHorizontalPreviewGap >= gnPreferredPreviewWidth)
1111         {
1112             // Yes.
1113             nPreviewWidth = gnPreferredPreviewWidth;
1114             mnColumnCount = floor((nWidth+gnPreferredHorizontalPreviewGap)
1115                 / (nPreviewWidth+gnPreferredHorizontalPreviewGap));
1116             mnHorizontalGap = round((nWidth - mnColumnCount*nPreviewWidth) / mnColumnCount);
1117         }
1118         else
1119         {
1120             // No.  Set the column count to 1 and adapt preview width and
1121             // gap.
1122             mnColumnCount = 1;
1123             mnHorizontalGap = floor(gnMinimalHorizontalPreviewGap);
1124             if (nWidth - gnMinimalHorizontalPreviewGap >= gnPreferredPreviewWidth)
1125                 nPreviewWidth = nWidth - gnMinimalHorizontalPreviewGap;
1126             else
1127                 nPreviewWidth = ::std::max(gnMinimalPreviewWidth, nWidth-mnHorizontalGap);
1128         }
1129     }
1130     else if (nElementWidth > gnMaximalPreviewWidth + gnMaximalHorizontalPreviewGap)
1131     {
1132         // The preferred column count is too small.
1133         nPreviewWidth = gnPreferredPreviewWidth;
1134         mnColumnCount = floor((nWidth+gnPreferredHorizontalPreviewGap)
1135             / (nPreviewWidth+gnPreferredHorizontalPreviewGap));
1136         mnHorizontalGap = round((nWidth - mnColumnCount*nPreviewWidth) / mnColumnCount);
1137     }
1138     else
1139     {
1140         // The preferred column count is possible.  Determine gap and
1141         // preview width.
1142         mnColumnCount = gnPreferredColumnCount;
1143         if (nElementWidth - gnPreferredPreviewWidth < gnMinimalHorizontalPreviewGap)
1144         {
1145             // Use the minimal gap and adapt the preview width.
1146             mnHorizontalGap = floor(gnMinimalHorizontalPreviewGap);
1147             nPreviewWidth = (nWidth - mnColumnCount*mnHorizontalGap) / mnColumnCount;
1148         }
1149         else if (nElementWidth - gnPreferredPreviewWidth <= gnMaximalHorizontalPreviewGap)
1150         {
1151             // Use the maximal gap and adapt the preview width.
1152             mnHorizontalGap = round(gnMaximalHorizontalPreviewGap);
1153             nPreviewWidth = (nWidth - mnColumnCount*mnHorizontalGap) / mnColumnCount;
1154         }
1155         else
1156         {
1157             // Use the preferred preview width and adapt the gap.
1158             nPreviewWidth = gnPreferredPreviewWidth;
1159             mnHorizontalGap = round((nWidth - mnColumnCount*nPreviewWidth) / mnColumnCount);
1160         }
1161     }
1162 
1163     // Now determine the row count, preview height, and vertical gap.
1164     const double nPreviewHeight = nPreviewWidth / nSlideAspectRatio;
1165     mnRowCount = ::std::max(
1166         sal_Int32(1),
1167         sal_Int32(ceil((nHeight+gnPreferredVerticalPreviewGap)
1168                 / (nPreviewHeight + gnPreferredVerticalPreviewGap))));
1169     mnVerticalGap = round(gnPreferredVerticalPreviewGap);
1170 
1171     maPreviewSize = geometry::IntegerSize2D(floor(nPreviewWidth), floor(nPreviewHeight));
1172 
1173     // Reset the offset.
1174     mnVerticalOffset = 0;
1175     mnHorizontalOffset = round(-(nWidth
1176         - mnColumnCount*maPreviewSize.Width
1177         - (mnColumnCount-1)*mnHorizontalGap)
1178         / 2);
1179 }
1180 
SetupVisibleArea()1181 void PresenterSlideSorter::Layout::SetupVisibleArea()
1182 {
1183     geometry::RealPoint2D aPoint (GetLocalPosition(
1184         geometry::RealPoint2D(maBoundingBox.X1, maBoundingBox.Y1)));
1185     mnFirstVisibleColumn = 0;
1186     mnFirstVisibleRow = ::std::max(sal_Int32(0), GetRow(aPoint));
1187 
1188     aPoint = GetLocalPosition(geometry::RealPoint2D( maBoundingBox.X2, maBoundingBox.Y2));
1189     mnLastVisibleColumn = mnColumnCount - 1;
1190     mnLastVisibleRow = GetRow(aPoint, true);
1191 }
1192 
IsScrollBarNeeded(const sal_Int32 nSlideCount)1193 bool PresenterSlideSorter::Layout::IsScrollBarNeeded (const sal_Int32 nSlideCount)
1194 {
1195     geometry::RealPoint2D aBottomRight = GetPoint(
1196         mnColumnCount * (GetRow(nSlideCount)+1) - 1, +1, +1);
1197     return aBottomRight.X > maBoundingBox.X2-maBoundingBox.X1
1198         || aBottomRight.Y > maBoundingBox.Y2-maBoundingBox.Y1;
1199 }
1200 
GetLocalPosition(const geometry::RealPoint2D & rWindowPoint) const1201 geometry::RealPoint2D PresenterSlideSorter::Layout::GetLocalPosition(
1202     const geometry::RealPoint2D& rWindowPoint) const
1203 {
1204     if(AllSettings::GetLayoutRTL())
1205         {
1206             return css::geometry::RealPoint2D(
1207                                               -rWindowPoint.X  + maBoundingBox.X2 + mnHorizontalOffset,
1208                                               rWindowPoint.Y - maBoundingBox.Y1 + mnVerticalOffset);
1209         }
1210     else
1211         {
1212             return css::geometry::RealPoint2D(
1213                                               rWindowPoint.X - maBoundingBox.X1 + mnHorizontalOffset,
1214                                               rWindowPoint.Y - maBoundingBox.Y1 + mnVerticalOffset);
1215         }
1216 }
1217 
GetWindowPosition(const geometry::RealPoint2D & rLocalPoint) const1218 geometry::RealPoint2D PresenterSlideSorter::Layout::GetWindowPosition(
1219     const geometry::RealPoint2D& rLocalPoint) const
1220 {
1221     if(AllSettings::GetLayoutRTL())
1222         {
1223             return css::geometry::RealPoint2D(
1224                                               -rLocalPoint.X + mnHorizontalOffset + maBoundingBox.X2,
1225                                               rLocalPoint.Y - mnVerticalOffset + maBoundingBox.Y1);
1226         }
1227     else
1228         {
1229             return css::geometry::RealPoint2D(
1230                                               rLocalPoint.X - mnHorizontalOffset + maBoundingBox.X1,
1231                                               rLocalPoint.Y - mnVerticalOffset + maBoundingBox.Y1);
1232         }
1233 }
1234 
GetColumn(const css::geometry::RealPoint2D & rLocalPoint) const1235 sal_Int32 PresenterSlideSorter::Layout::GetColumn (
1236     const css::geometry::RealPoint2D& rLocalPoint) const
1237 {
1238     const sal_Int32 nColumn(floor(
1239         (rLocalPoint.X + mnHorizontalGap/2.0) / (maPreviewSize.Width+mnHorizontalGap)));
1240     if (nColumn>=mnFirstVisibleColumn && nColumn<=mnLastVisibleColumn)
1241     {
1242         return nColumn;
1243     }
1244     else
1245         return -1;
1246 }
1247 
GetRow(const css::geometry::RealPoint2D & rLocalPoint,const bool bReturnInvalidValue) const1248 sal_Int32 PresenterSlideSorter::Layout::GetRow (
1249     const css::geometry::RealPoint2D& rLocalPoint,
1250     const bool bReturnInvalidValue) const
1251 {
1252     const sal_Int32 nRow (floor(
1253         (rLocalPoint.Y + mnVerticalGap/2.0) / (maPreviewSize.Height+mnVerticalGap)));
1254     if (bReturnInvalidValue
1255         || (nRow>=mnFirstVisibleRow && nRow<=mnLastVisibleRow))
1256     {
1257         return nRow;
1258     }
1259     else
1260         return -1;
1261 }
1262 
GetSlideIndexForPosition(const css::geometry::RealPoint2D & rWindowPoint) const1263 sal_Int32 PresenterSlideSorter::Layout::GetSlideIndexForPosition (
1264     const css::geometry::RealPoint2D& rWindowPoint) const
1265 {
1266     if ( ! PresenterGeometryHelper::IsInside(maBoundingBox, rWindowPoint))
1267         return -1;
1268 
1269     const css::geometry::RealPoint2D aLocalPosition (GetLocalPosition(rWindowPoint));
1270     const sal_Int32 nColumn (GetColumn(aLocalPosition));
1271     const sal_Int32 nRow (GetRow(aLocalPosition));
1272 
1273     if (nColumn < 0 || nRow < 0)
1274         return -1;
1275     else
1276     {
1277         sal_Int32 nIndex (GetIndex(nRow, nColumn));
1278         if (nIndex >= mnSlideCount)
1279             return -1;
1280         else
1281             return nIndex;
1282     }
1283 }
1284 
GetPoint(const sal_Int32 nSlideIndex,const sal_Int32 nRelativeHorizontalPosition,const sal_Int32 nRelativeVerticalPosition) const1285 geometry::RealPoint2D PresenterSlideSorter::Layout::GetPoint (
1286     const sal_Int32 nSlideIndex,
1287     const sal_Int32 nRelativeHorizontalPosition,
1288     const sal_Int32 nRelativeVerticalPosition) const
1289 {
1290     sal_Int32 nColumn (GetColumn(nSlideIndex));
1291     sal_Int32 nRow (GetRow(nSlideIndex));
1292 
1293     geometry::RealPoint2D aPosition (
1294         mnHorizontalBorder + nColumn*(maPreviewSize.Width+mnHorizontalGap),
1295         mnVerticalBorder + nRow*(maPreviewSize.Height+mnVerticalGap));
1296 
1297     if (nRelativeHorizontalPosition >= 0)
1298     {
1299         if (nRelativeHorizontalPosition > 0)
1300             aPosition.X += maPreviewSize.Width;
1301         else
1302             aPosition.X += maPreviewSize.Width / 2.0;
1303     }
1304     if (nRelativeVerticalPosition >= 0)
1305     {
1306         if (nRelativeVerticalPosition > 0)
1307             aPosition.Y += maPreviewSize.Height;
1308         else
1309             aPosition.Y += maPreviewSize.Height / 2.0;
1310     }
1311 
1312     return aPosition;
1313 }
1314 
GetBoundingBox(const sal_Int32 nSlideIndex) const1315 awt::Rectangle PresenterSlideSorter::Layout::GetBoundingBox (const sal_Int32 nSlideIndex) const
1316 {
1317     bool isRTL = AllSettings::GetLayoutRTL();
1318     const geometry::RealPoint2D aWindowPosition(GetWindowPosition(GetPoint(nSlideIndex, isRTL?1:-1, -1)));
1319     return PresenterGeometryHelper::ConvertRectangle(
1320                                                      geometry::RealRectangle2D(
1321                                                                                aWindowPosition.X,
1322                                                                                aWindowPosition.Y,
1323                                                                                aWindowPosition.X + maPreviewSize.Width,
1324                                                                                aWindowPosition.Y + maPreviewSize.Height));
1325 }
1326 
ForAllVisibleSlides(const::std::function<void (sal_Int32)> & rAction)1327 void PresenterSlideSorter::Layout::ForAllVisibleSlides(
1328         const ::std::function<void (sal_Int32)>& rAction)
1329 {
1330     for (sal_Int32 nRow=mnFirstVisibleRow; nRow<=mnLastVisibleRow; ++nRow)
1331     {
1332         for (sal_Int32 nColumn=mnFirstVisibleColumn; nColumn<=mnLastVisibleColumn; ++nColumn)
1333         {
1334             const sal_Int32 nSlideIndex (GetIndex(nRow, nColumn));
1335             if (nSlideIndex >= mnSlideCount)
1336                 return;
1337             rAction(nSlideIndex);
1338         }
1339     }
1340 }
1341 
GetFirstVisibleSlideIndex() const1342 sal_Int32 PresenterSlideSorter::Layout::GetFirstVisibleSlideIndex() const
1343 {
1344     return GetIndex(mnFirstVisibleRow, mnFirstVisibleColumn);
1345 }
1346 
GetLastVisibleSlideIndex() const1347 sal_Int32 PresenterSlideSorter::Layout::GetLastVisibleSlideIndex() const
1348 {
1349     return ::std::min(
1350         GetIndex(mnLastVisibleRow, mnLastVisibleColumn),
1351         mnSlideCount);
1352 }
1353 
SetHorizontalOffset(const double nOffset)1354 bool PresenterSlideSorter::Layout::SetHorizontalOffset (const double nOffset)
1355 {
1356     if (mnHorizontalOffset != nOffset)
1357     {
1358         mnHorizontalOffset = round(nOffset);
1359         SetupVisibleArea();
1360         UpdateScrollBars();
1361         return true;
1362     }
1363     else
1364         return false;
1365 }
1366 
SetVerticalOffset(const double nOffset)1367 bool PresenterSlideSorter::Layout::SetVerticalOffset (const double nOffset)
1368 {
1369     if (mnVerticalOffset != nOffset)
1370     {
1371         mnVerticalOffset = round(nOffset);
1372         SetupVisibleArea();
1373         UpdateScrollBars();
1374         return true;
1375     }
1376     else
1377         return false;
1378 }
1379 
UpdateScrollBars()1380 void PresenterSlideSorter::Layout::UpdateScrollBars()
1381 {
1382     sal_Int32 nTotalRowCount = sal_Int32(ceil(double(mnSlideCount) / double(mnColumnCount)));
1383 
1384     if (mpVerticalScrollBar)
1385     {
1386         mpVerticalScrollBar->SetTotalSize(
1387             nTotalRowCount * maPreviewSize.Height
1388                 + (nTotalRowCount-1) * mnVerticalGap
1389             + 2*mnVerticalGap);
1390         mpVerticalScrollBar->SetThumbPosition(mnVerticalOffset, false);
1391         mpVerticalScrollBar->SetThumbSize(maBoundingBox.Y2 - maBoundingBox.Y1 + 1);
1392         mpVerticalScrollBar->SetLineHeight(maPreviewSize.Height);
1393     }
1394 
1395     // No place yet for the vertical scroll bar.
1396 }
1397 
GetIndex(const sal_Int32 nRow,const sal_Int32 nColumn) const1398 sal_Int32 PresenterSlideSorter::Layout::GetIndex (
1399     const sal_Int32 nRow,
1400     const sal_Int32 nColumn) const
1401 {
1402     return nRow * mnColumnCount + nColumn;
1403 }
1404 
GetRow(const sal_Int32 nSlideIndex) const1405 sal_Int32 PresenterSlideSorter::Layout::GetRow (const sal_Int32 nSlideIndex) const
1406 {
1407     return nSlideIndex / mnColumnCount;
1408 }
1409 
GetColumn(const sal_Int32 nSlideIndex) const1410 sal_Int32 PresenterSlideSorter::Layout::GetColumn (const sal_Int32 nSlideIndex) const
1411 {
1412     return nSlideIndex % mnColumnCount;
1413 }
1414 
1415 //===== PresenterSlideSorter::MouseOverManager ================================
1416 
MouseOverManager(const Reference<container::XIndexAccess> & rxSlides,const std::shared_ptr<PresenterTheme> & rpTheme,const Reference<awt::XWindow> & rxInvalidateTarget,const std::shared_ptr<PresenterPaintManager> & rpPaintManager)1417 PresenterSlideSorter::MouseOverManager::MouseOverManager (
1418     const Reference<container::XIndexAccess>& rxSlides,
1419     const std::shared_ptr<PresenterTheme>& rpTheme,
1420     const Reference<awt::XWindow>& rxInvalidateTarget,
1421     const std::shared_ptr<PresenterPaintManager>& rpPaintManager)
1422     : mxCanvas(),
1423       mxSlides(rxSlides),
1424       mpLeftLabelBitmap(),
1425       mpCenterLabelBitmap(),
1426       mpRightLabelBitmap(),
1427       mpFont(),
1428       mnSlideIndex(-1),
1429       maSlideBoundingBox(),
1430       mxInvalidateTarget(rxInvalidateTarget),
1431       mpPaintManager(rpPaintManager)
1432 {
1433     if (rpTheme != nullptr)
1434     {
1435         std::shared_ptr<PresenterBitmapContainer> pBitmaps (rpTheme->GetBitmapContainer());
1436         if (pBitmaps != nullptr)
1437         {
1438             mpLeftLabelBitmap = pBitmaps->GetBitmap("LabelLeft");
1439             mpCenterLabelBitmap = pBitmaps->GetBitmap("LabelCenter");
1440             mpRightLabelBitmap = pBitmaps->GetBitmap("LabelRight");
1441         }
1442 
1443         mpFont = rpTheme->GetFont("SlideSorterLabelFont");
1444     }
1445 }
1446 
Paint(const sal_Int32 nSlideIndex,const Reference<rendering::XCanvas> & rxCanvas,const Reference<rendering::XPolyPolygon2D> & rxClip)1447 void PresenterSlideSorter::MouseOverManager::Paint (
1448     const sal_Int32 nSlideIndex,
1449     const Reference<rendering::XCanvas>& rxCanvas,
1450     const Reference<rendering::XPolyPolygon2D>& rxClip)
1451 {
1452     if (nSlideIndex != mnSlideIndex)
1453         return;
1454 
1455     if (mxCanvas != rxCanvas)
1456         SetCanvas(rxCanvas);
1457     if (rxCanvas == nullptr)
1458         return;
1459 
1460     if ( ! mxBitmap.is())
1461         mxBitmap = CreateBitmap(msText, maSlideBoundingBox.Width);
1462     if (!mxBitmap.is())
1463         return;
1464 
1465     geometry::IntegerSize2D aSize (mxBitmap->getSize());
1466     const double nXOffset (maSlideBoundingBox.X
1467         + (maSlideBoundingBox.Width - aSize.Width) / 2.0);
1468     const double nYOffset (maSlideBoundingBox.Y
1469         + (maSlideBoundingBox.Height - aSize.Height) / 2.0);
1470     rxCanvas->drawBitmap(
1471         mxBitmap,
1472         rendering::ViewState(
1473             geometry::AffineMatrix2D(1,0,0, 0,1,0),
1474             rxClip),
1475         rendering::RenderState(
1476             geometry::AffineMatrix2D(1,0,nXOffset, 0,1,nYOffset),
1477             nullptr,
1478             Sequence<double>(4),
1479             rendering::CompositeOperation::SOURCE));
1480 }
1481 
SetCanvas(const Reference<rendering::XCanvas> & rxCanvas)1482 void PresenterSlideSorter::MouseOverManager::SetCanvas (
1483     const Reference<rendering::XCanvas>& rxCanvas)
1484 {
1485     mxCanvas = rxCanvas;
1486     if (mpFont)
1487         mpFont->PrepareFont(mxCanvas);
1488 }
1489 
SetSlide(const sal_Int32 nSlideIndex,const awt::Rectangle & rBox)1490 void PresenterSlideSorter::MouseOverManager::SetSlide (
1491     const sal_Int32 nSlideIndex,
1492     const awt::Rectangle& rBox)
1493 {
1494     if (mnSlideIndex == nSlideIndex)
1495         return;
1496 
1497     mnSlideIndex = -1;
1498     Invalidate();
1499 
1500     maSlideBoundingBox = rBox;
1501     mnSlideIndex = nSlideIndex;
1502 
1503     if (nSlideIndex >= 0)
1504     {
1505         if (mxSlides)
1506         {
1507             msText.clear();
1508 
1509             Reference<beans::XPropertySet> xSlideProperties(mxSlides->getByIndex(nSlideIndex), UNO_QUERY);
1510             if (xSlideProperties.is())
1511                 xSlideProperties->getPropertyValue("LinkDisplayName") >>= msText;
1512 
1513             if (msText.isEmpty())
1514                 msText = "Slide " + OUString::number(nSlideIndex + 1);
1515         }
1516     }
1517     else
1518     {
1519         msText.clear();
1520     }
1521     mxBitmap = nullptr;
1522 
1523     Invalidate();
1524 }
1525 
CreateBitmap(const OUString & rsText,const sal_Int32 nMaximalWidth) const1526 Reference<rendering::XBitmap> PresenterSlideSorter::MouseOverManager::CreateBitmap (
1527     const OUString& rsText,
1528     const sal_Int32 nMaximalWidth) const
1529 {
1530     if ( ! mxCanvas.is())
1531         return nullptr;
1532 
1533     if (!mpFont || !mpFont->mxFont.is())
1534         return nullptr;
1535 
1536     // Long text has to be shortened.
1537     const OUString sText (GetFittingText(rsText, nMaximalWidth
1538             - 2*gnHorizontalLabelBorder
1539             - 2*gnHorizontalLabelPadding));
1540 
1541     // Determine the size of the label.  Its height is defined by the
1542     // bitmaps that are used to paints its background.  The width is defined
1543     // by the text.
1544     geometry::IntegerSize2D aLabelSize (CalculateLabelSize(sText));
1545 
1546     // Create a new bitmap that will contain the complete label.
1547     Reference<rendering::XBitmap> xBitmap (
1548         mxCanvas->getDevice()->createCompatibleAlphaBitmap(aLabelSize));
1549 
1550     if ( ! xBitmap.is())
1551         return nullptr;
1552 
1553     Reference<rendering::XBitmapCanvas> xBitmapCanvas (xBitmap, UNO_QUERY);
1554     if ( ! xBitmapCanvas.is())
1555         return nullptr;
1556 
1557     // Paint the background.
1558     PaintButtonBackground(xBitmapCanvas, aLabelSize);
1559 
1560     // Paint the text.
1561     if (!sText.isEmpty())
1562     {
1563 
1564         const rendering::StringContext aContext (sText, 0, sText.getLength());
1565         const Reference<rendering::XTextLayout> xLayout (mpFont->mxFont->createTextLayout(
1566             aContext, rendering::TextDirection::WEAK_LEFT_TO_RIGHT,0));
1567         const geometry::RealRectangle2D aTextBBox (xLayout->queryTextBounds());
1568 
1569         const double nXOffset = (aLabelSize.Width - aTextBBox.X2 + aTextBBox.X1) / 2;
1570         const double nYOffset = aLabelSize.Height
1571             - (aLabelSize.Height - aTextBBox.Y2 + aTextBBox.Y1)/2 - aTextBBox.Y2;
1572 
1573         const rendering::ViewState aViewState(
1574             geometry::AffineMatrix2D(1,0,0, 0,1,0),
1575             nullptr);
1576 
1577         rendering::RenderState aRenderState (
1578             geometry::AffineMatrix2D(1,0,nXOffset, 0,1,nYOffset),
1579             nullptr,
1580             Sequence<double>(4),
1581             rendering::CompositeOperation::SOURCE);
1582         PresenterCanvasHelper::SetDeviceColor(aRenderState, mpFont->mnColor);
1583 
1584         xBitmapCanvas->drawTextLayout (
1585             xLayout,
1586             aViewState,
1587             aRenderState);
1588     }
1589 
1590     return xBitmap;
1591 }
1592 
GetFittingText(const OUString & rsText,const double nMaximalWidth) const1593 OUString PresenterSlideSorter::MouseOverManager::GetFittingText (
1594     const OUString& rsText,
1595     const double nMaximalWidth) const
1596 {
1597     const double nTextWidth (
1598         PresenterCanvasHelper::GetTextSize(mpFont->mxFont, rsText).Width);
1599     if (nTextWidth > nMaximalWidth)
1600     {
1601         // Text is too wide.  Shorten it by removing characters from the end
1602         // and replacing them by ellipses.
1603 
1604         // Guess a start value of the final string length.
1605         double nBestWidth (0);
1606         OUString sBestCandidate;
1607         sal_Int32 nLength (round(rsText.getLength() * nMaximalWidth / nTextWidth));
1608         static const OUStringLiteral sEllipses (u"...");
1609         while (true)
1610         {
1611             const OUString sCandidate (rsText.subView(0,nLength) + sEllipses);
1612             const double nWidth (
1613                 PresenterCanvasHelper::GetTextSize(mpFont->mxFont, sCandidate).Width);
1614             if (nWidth > nMaximalWidth)
1615             {
1616                 // Candidate still too wide, shorten it.
1617                 nLength -= 1;
1618                 if (nLength <= 0)
1619                     break;
1620             }
1621             else if (nWidth < nMaximalWidth)
1622             {
1623                 // Candidate short enough.
1624                 if (nWidth > nBestWidth)
1625                 {
1626                     // Best length so far.
1627                     sBestCandidate = sCandidate;
1628                     nBestWidth = nWidth;
1629                     nLength += 1;
1630                     if (nLength >= rsText.getLength())
1631                         break;
1632                 }
1633                 else
1634                     break;
1635             }
1636             else
1637             {
1638                 // Candidate is exactly as long as it may be.  Use it
1639                 // without looking any further.
1640                 sBestCandidate = sCandidate;
1641                 break;
1642             }
1643         }
1644         return sBestCandidate;
1645     }
1646     else
1647         return rsText;
1648 }
1649 
CalculateLabelSize(const OUString & rsText) const1650 geometry::IntegerSize2D PresenterSlideSorter::MouseOverManager::CalculateLabelSize (
1651     const OUString& rsText) const
1652 {
1653     // Height is specified by the label bitmaps.
1654     sal_Int32 nHeight (32);
1655     if (mpCenterLabelBitmap)
1656     {
1657         Reference<rendering::XBitmap> xBitmap (mpCenterLabelBitmap->GetNormalBitmap());
1658         if (xBitmap.is())
1659             nHeight = xBitmap->getSize().Height;
1660     }
1661 
1662     // Width is specified by text width and maximal width.
1663     const geometry::RealSize2D aTextSize (
1664         PresenterCanvasHelper::GetTextSize(mpFont->mxFont, rsText));
1665 
1666     const sal_Int32 nWidth (round(aTextSize.Width + 2*gnHorizontalLabelPadding));
1667 
1668     return geometry::IntegerSize2D(nWidth, nHeight);
1669 }
1670 
PaintButtonBackground(const Reference<rendering::XCanvas> & rxCanvas,const geometry::IntegerSize2D & rSize) const1671 void PresenterSlideSorter::MouseOverManager::PaintButtonBackground (
1672     const Reference<rendering::XCanvas>& rxCanvas,
1673     const geometry::IntegerSize2D& rSize) const
1674 {
1675     // Get the bitmaps for painting the label background.
1676     Reference<rendering::XBitmap> xLeftLabelBitmap;
1677     if (mpLeftLabelBitmap)
1678         xLeftLabelBitmap = mpLeftLabelBitmap->GetNormalBitmap();
1679 
1680     Reference<rendering::XBitmap> xCenterLabelBitmap;
1681     if (mpCenterLabelBitmap)
1682         xCenterLabelBitmap = mpCenterLabelBitmap->GetNormalBitmap();
1683 
1684     Reference<rendering::XBitmap> xRightLabelBitmap;
1685     if (mpRightLabelBitmap)
1686         xRightLabelBitmap = mpRightLabelBitmap->GetNormalBitmap();
1687 
1688     PresenterUIPainter::PaintHorizontalBitmapComposite (
1689         rxCanvas,
1690         awt::Rectangle(0,0, rSize.Width,rSize.Height),
1691         awt::Rectangle(0,0, rSize.Width,rSize.Height),
1692         xLeftLabelBitmap,
1693         xCenterLabelBitmap,
1694         xRightLabelBitmap);
1695 }
1696 
Invalidate()1697 void PresenterSlideSorter::MouseOverManager::Invalidate()
1698 {
1699     if (mpPaintManager != nullptr)
1700         mpPaintManager->Invalidate(mxInvalidateTarget, maSlideBoundingBox, true);
1701 }
1702 
1703 //===== PresenterSlideSorter::CurrentSlideFrameRenderer =======================
1704 
CurrentSlideFrameRenderer(const css::uno::Reference<css::uno::XComponentContext> & rxContext,const css::uno::Reference<css::rendering::XCanvas> & rxCanvas)1705 PresenterSlideSorter::CurrentSlideFrameRenderer::CurrentSlideFrameRenderer (
1706     const css::uno::Reference<css::uno::XComponentContext>& rxContext,
1707     const css::uno::Reference<css::rendering::XCanvas>& rxCanvas)
1708     : mpTopLeft(),
1709       mpTop(),
1710       mpTopRight(),
1711       mpLeft(),
1712       mpRight(),
1713       mpBottomLeft(),
1714       mpBottom(),
1715       mpBottomRight(),
1716       mnTopFrameSize(0),
1717       mnLeftFrameSize(0),
1718       mnRightFrameSize(0),
1719       mnBottomFrameSize(0)
1720 {
1721     PresenterConfigurationAccess aConfiguration (
1722         rxContext,
1723         "/org.openoffice.Office.PresenterScreen/",
1724         PresenterConfigurationAccess::READ_ONLY);
1725     Reference<container::XHierarchicalNameAccess> xBitmaps (
1726         aConfiguration.GetConfigurationNode(
1727             "PresenterScreenSettings/SlideSorter/CurrentSlideBorderBitmaps"),
1728         UNO_QUERY);
1729     if ( ! xBitmaps.is())
1730         return;
1731 
1732     PresenterBitmapContainer aContainer (
1733         "PresenterScreenSettings/SlideSorter/CurrentSlideBorderBitmaps",
1734         std::shared_ptr<PresenterBitmapContainer>(),
1735         rxContext,
1736         rxCanvas);
1737 
1738     mpTopLeft = aContainer.GetBitmap("TopLeft");
1739     mpTop = aContainer.GetBitmap("Top");
1740     mpTopRight = aContainer.GetBitmap("TopRight");
1741     mpLeft = aContainer.GetBitmap("Left");
1742     mpRight = aContainer.GetBitmap("Right");
1743     mpBottomLeft = aContainer.GetBitmap("BottomLeft");
1744     mpBottom = aContainer.GetBitmap("Bottom");
1745     mpBottomRight = aContainer.GetBitmap("BottomRight");
1746 
1747     // Determine size of frame.
1748     if (mpTop)
1749         mnTopFrameSize = mpTop->mnHeight;
1750     if (mpLeft)
1751         mnLeftFrameSize = mpLeft->mnWidth;
1752     if (mpRight)
1753         mnRightFrameSize = mpRight->mnWidth;
1754     if (mpBottom)
1755         mnBottomFrameSize = mpBottom->mnHeight;
1756 
1757     if (mpTopLeft)
1758     {
1759         mnTopFrameSize = ::std::max(mnTopFrameSize, mpTopLeft->mnHeight);
1760         mnLeftFrameSize = ::std::max(mnLeftFrameSize, mpTopLeft->mnWidth);
1761     }
1762     if (mpTopRight)
1763     {
1764         mnTopFrameSize = ::std::max(mnTopFrameSize, mpTopRight->mnHeight);
1765         mnRightFrameSize = ::std::max(mnRightFrameSize, mpTopRight->mnWidth);
1766     }
1767     if (mpBottomLeft)
1768     {
1769         mnLeftFrameSize = ::std::max(mnLeftFrameSize, mpBottomLeft->mnWidth);
1770         mnBottomFrameSize = ::std::max(mnBottomFrameSize, mpBottomLeft->mnHeight);
1771     }
1772     if (mpBottomRight)
1773     {
1774         mnRightFrameSize = ::std::max(mnRightFrameSize, mpBottomRight->mnWidth);
1775         mnBottomFrameSize = ::std::max(mnBottomFrameSize, mpBottomRight->mnHeight);
1776     }
1777 }
1778 
PaintCurrentSlideFrame(const awt::Rectangle & rSlideBoundingBox,const Reference<rendering::XCanvas> & rxCanvas,const geometry::RealRectangle2D & rClipBox)1779 void PresenterSlideSorter::CurrentSlideFrameRenderer::PaintCurrentSlideFrame (
1780     const awt::Rectangle& rSlideBoundingBox,
1781     const Reference<rendering::XCanvas>& rxCanvas,
1782     const geometry::RealRectangle2D& rClipBox)
1783 {
1784     if ( ! rxCanvas.is())
1785         return;
1786 
1787     const Reference<rendering::XPolyPolygon2D> xClip (
1788         PresenterGeometryHelper::CreatePolygon(rClipBox, rxCanvas->getDevice()));
1789 
1790     if (mpTop)
1791     {
1792         PaintBitmapTiled(
1793             mpTop->GetNormalBitmap(),
1794             rxCanvas,
1795             rClipBox,
1796             rSlideBoundingBox.X,
1797             rSlideBoundingBox.Y - mpTop->mnHeight,
1798             rSlideBoundingBox.Width,
1799             mpTop->mnHeight);
1800     }
1801     if (mpLeft)
1802     {
1803         PaintBitmapTiled(
1804             mpLeft->GetNormalBitmap(),
1805             rxCanvas,
1806             rClipBox,
1807             rSlideBoundingBox.X - mpLeft->mnWidth,
1808             rSlideBoundingBox.Y,
1809             mpLeft->mnWidth,
1810             rSlideBoundingBox.Height);
1811     }
1812     if (mpRight)
1813     {
1814         PaintBitmapTiled(
1815             mpRight->GetNormalBitmap(),
1816             rxCanvas,
1817             rClipBox,
1818             rSlideBoundingBox.X + rSlideBoundingBox.Width,
1819             rSlideBoundingBox.Y,
1820             mpRight->mnWidth,
1821             rSlideBoundingBox.Height);
1822     }
1823     if (mpBottom)
1824     {
1825         PaintBitmapTiled(
1826             mpBottom->GetNormalBitmap(),
1827             rxCanvas,
1828             rClipBox,
1829             rSlideBoundingBox.X,
1830             rSlideBoundingBox.Y + rSlideBoundingBox.Height,
1831             rSlideBoundingBox.Width,
1832             mpBottom->mnHeight);
1833     }
1834     if (mpTopLeft)
1835     {
1836         PaintBitmapOnce(
1837             mpTopLeft->GetNormalBitmap(),
1838             rxCanvas,
1839             xClip,
1840             rSlideBoundingBox.X - mpTopLeft->mnWidth,
1841             rSlideBoundingBox.Y - mpTopLeft->mnHeight);
1842     }
1843     if (mpTopRight)
1844     {
1845         PaintBitmapOnce(
1846             mpTopRight->GetNormalBitmap(),
1847             rxCanvas,
1848             xClip,
1849             rSlideBoundingBox.X + rSlideBoundingBox.Width,
1850             rSlideBoundingBox.Y - mpTopLeft->mnHeight);
1851     }
1852     if (mpBottomLeft)
1853     {
1854         PaintBitmapOnce(
1855             mpBottomLeft->GetNormalBitmap(),
1856             rxCanvas,
1857             xClip,
1858             rSlideBoundingBox.X - mpBottomLeft->mnWidth,
1859             rSlideBoundingBox.Y + rSlideBoundingBox.Height);
1860     }
1861     if (mpBottomRight)
1862     {
1863         PaintBitmapOnce(
1864             mpBottomRight->GetNormalBitmap(),
1865             rxCanvas,
1866             xClip,
1867             rSlideBoundingBox.X + rSlideBoundingBox.Width,
1868             rSlideBoundingBox.Y + rSlideBoundingBox.Height);
1869     }
1870 }
1871 
GetBoundingBox(const awt::Rectangle & rSlideBoundingBox)1872 awt::Rectangle PresenterSlideSorter::CurrentSlideFrameRenderer::GetBoundingBox (
1873     const awt::Rectangle& rSlideBoundingBox)
1874 {
1875     return awt::Rectangle(
1876         rSlideBoundingBox.X - mnLeftFrameSize,
1877         rSlideBoundingBox.Y - mnTopFrameSize,
1878         rSlideBoundingBox.Width + mnLeftFrameSize + mnRightFrameSize,
1879         rSlideBoundingBox.Height + mnTopFrameSize + mnBottomFrameSize);
1880 }
1881 
PaintBitmapOnce(const css::uno::Reference<css::rendering::XBitmap> & rxBitmap,const css::uno::Reference<css::rendering::XCanvas> & rxCanvas,const Reference<rendering::XPolyPolygon2D> & rxClip,const double nX,const double nY)1882 void PresenterSlideSorter::CurrentSlideFrameRenderer::PaintBitmapOnce(
1883     const css::uno::Reference<css::rendering::XBitmap>& rxBitmap,
1884     const css::uno::Reference<css::rendering::XCanvas>& rxCanvas,
1885     const Reference<rendering::XPolyPolygon2D>& rxClip,
1886     const double nX,
1887     const double nY)
1888 {
1889     OSL_ASSERT(rxCanvas.is());
1890     if ( ! rxBitmap.is())
1891         return;
1892 
1893     const rendering::ViewState aViewState(
1894         geometry::AffineMatrix2D(1,0,0, 0,1,0),
1895         rxClip);
1896 
1897     const rendering::RenderState aRenderState (
1898         geometry::AffineMatrix2D(
1899             1, 0, nX,
1900             0, 1, nY),
1901         nullptr,
1902         Sequence<double>(4),
1903         rendering::CompositeOperation::SOURCE);
1904 
1905     rxCanvas->drawBitmap(
1906         rxBitmap,
1907         aViewState,
1908         aRenderState);
1909 }
1910 
PaintBitmapTiled(const css::uno::Reference<css::rendering::XBitmap> & rxBitmap,const css::uno::Reference<css::rendering::XCanvas> & rxCanvas,const geometry::RealRectangle2D & rClipBox,const double nX0,const double nY0,const double nWidth,const double nHeight)1911 void PresenterSlideSorter::CurrentSlideFrameRenderer::PaintBitmapTiled(
1912     const css::uno::Reference<css::rendering::XBitmap>& rxBitmap,
1913     const css::uno::Reference<css::rendering::XCanvas>& rxCanvas,
1914     const geometry::RealRectangle2D& rClipBox,
1915     const double nX0,
1916     const double nY0,
1917     const double nWidth,
1918     const double nHeight)
1919 {
1920     OSL_ASSERT(rxCanvas.is());
1921     if ( ! rxBitmap.is())
1922         return;
1923 
1924     geometry::IntegerSize2D aSize (rxBitmap->getSize());
1925 
1926     const rendering::ViewState aViewState(
1927         geometry::AffineMatrix2D(1,0,0, 0,1,0),
1928         PresenterGeometryHelper::CreatePolygon(
1929             PresenterGeometryHelper::Intersection(
1930                 rClipBox,
1931                 geometry::RealRectangle2D(nX0,nY0,nX0+nWidth,nY0+nHeight)),
1932             rxCanvas->getDevice()));
1933 
1934     rendering::RenderState aRenderState (
1935         geometry::AffineMatrix2D(
1936             1, 0, nX0,
1937             0, 1, nY0),
1938         nullptr,
1939         Sequence<double>(4),
1940         rendering::CompositeOperation::SOURCE);
1941 
1942     const double nX1 = nX0 + nWidth;
1943     const double nY1 = nY0 + nHeight;
1944     for (double nY=nY0; nY<nY1; nY+=aSize.Height)
1945         for (double nX=nX0; nX<nX1; nX+=aSize.Width)
1946         {
1947             aRenderState.AffineTransform.m02 = nX;
1948             aRenderState.AffineTransform.m12 = nY;
1949             rxCanvas->drawBitmap(
1950                 rxBitmap,
1951                 aViewState,
1952                 aRenderState);
1953         }
1954 }
1955 
1956 } // end of namespace ::sdext::presenter
1957 
1958 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
1959